1185377Ssam/* 2187831Ssam * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting 3185377Ssam * Copyright (c) 2002-2008 Atheros Communications, Inc. 4185377Ssam * 5185377Ssam * Permission to use, copy, modify, and/or distribute this software for any 6185377Ssam * purpose with or without fee is hereby granted, provided that the above 7185377Ssam * copyright notice and this permission notice appear in all copies. 8185377Ssam * 9185377Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10185377Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11185377Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12185377Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13185377Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14185377Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15185377Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16185377Ssam * 17186332Ssam * $FreeBSD$ 18185377Ssam */ 19185377Ssam#include "opt_ah.h" 20185377Ssam 21185377Ssam#include "ah.h" 22185377Ssam#include "ah_internal.h" 23185377Ssam#include "ah_desc.h" 24185377Ssam 25185377Ssam#include "ar5212/ar5212.h" 26185377Ssam#include "ar5212/ar5212reg.h" 27185377Ssam#include "ar5212/ar5212phy.h" 28185377Ssam 29185377Ssam/* 30185377Ssam * Anti noise immunity support. We track phy errors and react 31185377Ssam * to excessive errors by adjusting the noise immunity parameters. 32185377Ssam */ 33185377Ssam 34185377Ssam#define HAL_EP_RND(x, mul) \ 35185377Ssam ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) 36185377Ssam#define BEACON_RSSI(ahp) \ 37185377Ssam HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \ 38185377Ssam HAL_RSSI_EP_MULTIPLIER) 39185377Ssam 40185377Ssam/* 41185377Ssam * ANI processing tunes radio parameters according to PHY errors 42185377Ssam * and related information. This is done for for noise and spur 43185377Ssam * immunity in all operating modes if the device indicates it's 44185377Ssam * capable at attach time. In addition, when there is a reference 45185377Ssam * rssi value (e.g. beacon frames from an ap in station mode) 46185377Ssam * further tuning is done. 47185377Ssam * 48185377Ssam * ANI_ENA indicates whether any ANI processing should be done; 49185377Ssam * this is specified at attach time. 50185377Ssam * 51185377Ssam * ANI_ENA_RSSI indicates whether rssi-based processing should 52185377Ssam * done, this is enabled based on operating mode and is meaningful 53185377Ssam * only if ANI_ENA is true. 54185377Ssam * 55185377Ssam * ANI parameters are typically controlled only by the hal. The 56185377Ssam * AniControl interface however permits manual tuning through the 57185377Ssam * diagnostic api. 58185377Ssam */ 59185377Ssam#define ANI_ENA(ah) \ 60185377Ssam (AH5212(ah)->ah_procPhyErr & HAL_ANI_ENA) 61185377Ssam#define ANI_ENA_RSSI(ah) \ 62185377Ssam (AH5212(ah)->ah_procPhyErr & HAL_RSSI_ANI_ENA) 63185377Ssam 64185377Ssam#define ah_mibStats ah_stats.ast_mibstats 65185377Ssam 66185377Ssamstatic void 67185377SsamenableAniMIBCounters(struct ath_hal *ah, const struct ar5212AniParams *params) 68185377Ssam{ 69185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 70185377Ssam 71185377Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Enable mib counters: " 72185377Ssam "OfdmPhyErrBase 0x%x cckPhyErrBase 0x%x\n", 73185377Ssam __func__, params->ofdmPhyErrBase, params->cckPhyErrBase); 74185377Ssam 75185377Ssam OS_REG_WRITE(ah, AR_FILTOFDM, 0); 76185377Ssam OS_REG_WRITE(ah, AR_FILTCCK, 0); 77185377Ssam 78185377Ssam OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase); 79185377Ssam OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase); 80185377Ssam OS_REG_WRITE(ah, AR_PHYCNTMASK1, AR_PHY_ERR_OFDM_TIMING); 81185377Ssam OS_REG_WRITE(ah, AR_PHYCNTMASK2, AR_PHY_ERR_CCK_TIMING); 82185377Ssam 83185377Ssam ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save+clear counters*/ 84185377Ssam ar5212EnableMibCounters(ah); /* enable everything */ 85185377Ssam} 86185377Ssam 87185377Ssamstatic void 88185377SsamdisableAniMIBCounters(struct ath_hal *ah) 89185377Ssam{ 90185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 91185377Ssam 92185377Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "Disable MIB counters\n"); 93185377Ssam 94185377Ssam ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save stats */ 95185377Ssam ar5212DisableMibCounters(ah); /* disable everything */ 96185377Ssam 97185377Ssam OS_REG_WRITE(ah, AR_PHYCNTMASK1, 0); 98185377Ssam OS_REG_WRITE(ah, AR_PHYCNTMASK2, 0); 99185377Ssam} 100185377Ssam 101185377Ssam/* 102185377Ssam * Return the current ANI state of the channel we're on 103185377Ssam */ 104185377Ssamstruct ar5212AniState * 105185377Ssamar5212AniGetCurrentState(struct ath_hal *ah) 106185377Ssam{ 107185377Ssam return AH5212(ah)->ah_curani; 108185377Ssam} 109185377Ssam 110185377Ssam/* 111185377Ssam * Return the current statistics. 112185377Ssam */ 113280940SadrianHAL_ANI_STATS * 114185377Ssamar5212AniGetCurrentStats(struct ath_hal *ah) 115185377Ssam{ 116185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 117185377Ssam 118185377Ssam /* update mib stats so we return current data */ 119185377Ssam /* XXX? side-effects to doing this here? */ 120185377Ssam ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 121185377Ssam return &ahp->ah_stats; 122185377Ssam} 123185377Ssam 124185377Ssamstatic void 125185377SsamsetPhyErrBase(struct ath_hal *ah, struct ar5212AniParams *params) 126185377Ssam{ 127185377Ssam if (params->ofdmTrigHigh >= AR_PHY_COUNTMAX) { 128185377Ssam HALDEBUG(ah, HAL_DEBUG_ANY, 129185377Ssam "OFDM Trigger %d is too high for hw counters, using max\n", 130185377Ssam params->ofdmTrigHigh); 131185377Ssam params->ofdmPhyErrBase = 0; 132185377Ssam } else 133185377Ssam params->ofdmPhyErrBase = AR_PHY_COUNTMAX - params->ofdmTrigHigh; 134185377Ssam if (params->cckTrigHigh >= AR_PHY_COUNTMAX) { 135185377Ssam HALDEBUG(ah, HAL_DEBUG_ANY, 136185377Ssam "CCK Trigger %d is too high for hw counters, using max\n", 137185377Ssam params->cckTrigHigh); 138185377Ssam params->cckPhyErrBase = 0; 139185377Ssam } else 140185377Ssam params->cckPhyErrBase = AR_PHY_COUNTMAX - params->cckTrigHigh; 141185377Ssam} 142185377Ssam 143185377Ssam/* 144185377Ssam * Setup ANI handling. Sets all thresholds and reset the 145185377Ssam * channel statistics. Note that ar5212AniReset should be 146185377Ssam * called by ar5212Reset before anything else happens and 147185377Ssam * that's where we force initial settings. 148185377Ssam */ 149185377Ssamvoid 150185377Ssamar5212AniAttach(struct ath_hal *ah, const struct ar5212AniParams *params24, 151185377Ssam const struct ar5212AniParams *params5, HAL_BOOL enable) 152185377Ssam{ 153185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 154185377Ssam 155185377Ssam ahp->ah_hasHwPhyCounters = 156185377Ssam AH_PRIVATE(ah)->ah_caps.halHwPhyCounterSupport; 157185377Ssam 158185377Ssam if (params24 != AH_NULL) { 159185377Ssam OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24)); 160185377Ssam setPhyErrBase(ah, &ahp->ah_aniParams24); 161185377Ssam } 162185377Ssam if (params5 != AH_NULL) { 163185377Ssam OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5)); 164185377Ssam setPhyErrBase(ah, &ahp->ah_aniParams5); 165185377Ssam } 166185377Ssam 167185377Ssam OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani)); 168185377Ssam if (ahp->ah_hasHwPhyCounters) { 169185377Ssam /* Enable MIB Counters */ 170185377Ssam enableAniMIBCounters(ah, &ahp->ah_aniParams24 /*XXX*/); 171185377Ssam } 172185377Ssam if (enable) { /* Enable ani now */ 173185377Ssam HALASSERT(params24 != AH_NULL && params5 != AH_NULL); 174185377Ssam ahp->ah_procPhyErr |= HAL_ANI_ENA; 175185377Ssam } else { 176185377Ssam ahp->ah_procPhyErr &= ~HAL_ANI_ENA; 177185377Ssam } 178185377Ssam} 179185377Ssam 180185377SsamHAL_BOOL 181185377Ssamar5212AniSetParams(struct ath_hal *ah, const struct ar5212AniParams *params24, 182185377Ssam const struct ar5212AniParams *params5) 183185377Ssam{ 184185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 185185377Ssam HAL_BOOL ena = (ahp->ah_procPhyErr & HAL_ANI_ENA) != 0; 186185377Ssam 187185377Ssam ar5212AniControl(ah, HAL_ANI_MODE, AH_FALSE); 188185377Ssam 189185377Ssam OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24)); 190185377Ssam setPhyErrBase(ah, &ahp->ah_aniParams24); 191185377Ssam OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5)); 192185377Ssam setPhyErrBase(ah, &ahp->ah_aniParams5); 193185377Ssam 194185377Ssam OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani)); 195185377Ssam ar5212AniReset(ah, AH_PRIVATE(ah)->ah_curchan, 196185377Ssam AH_PRIVATE(ah)->ah_opmode, AH_FALSE); 197185377Ssam 198185377Ssam ar5212AniControl(ah, HAL_ANI_MODE, ena); 199185377Ssam 200185377Ssam return AH_TRUE; 201185377Ssam} 202185377Ssam 203185377Ssam/* 204185377Ssam * Cleanup any ANI state setup. 205185377Ssam */ 206185377Ssamvoid 207185377Ssamar5212AniDetach(struct ath_hal *ah) 208185377Ssam{ 209185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 210185377Ssam 211185377Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "Detaching Ani\n"); 212185377Ssam if (ahp->ah_hasHwPhyCounters) 213185377Ssam disableAniMIBCounters(ah); 214185377Ssam} 215185377Ssam 216185377Ssam/* 217185377Ssam * Control Adaptive Noise Immunity Parameters 218185377Ssam */ 219185377SsamHAL_BOOL 220185377Ssamar5212AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param) 221185377Ssam{ 222185377Ssam typedef int TABLE[]; 223185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 224185377Ssam struct ar5212AniState *aniState = ahp->ah_curani; 225224514Sadrian const struct ar5212AniParams *params = AH_NULL; 226224514Sadrian 227224514Sadrian /* 228224514Sadrian * This function may be called before there's a current 229224514Sadrian * channel (eg to disable ANI.) 230224514Sadrian */ 231224514Sadrian if (aniState != AH_NULL) 232224514Sadrian params = aniState->params; 233185377Ssam 234185377Ssam OS_MARK(ah, AH_MARK_ANI_CONTROL, cmd); 235185377Ssam 236185377Ssam switch (cmd) { 237185377Ssam case HAL_ANI_NOISE_IMMUNITY_LEVEL: { 238185377Ssam u_int level = param; 239185377Ssam 240186332Ssam if (level > params->maxNoiseImmunityLevel) { 241185377Ssam HALDEBUG(ah, HAL_DEBUG_ANY, 242185377Ssam "%s: level out of range (%u > %u)\n", 243185377Ssam __func__, level, params->maxNoiseImmunityLevel); 244185377Ssam return AH_FALSE; 245185377Ssam } 246185377Ssam 247185377Ssam OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, 248185377Ssam AR_PHY_DESIRED_SZ_TOT_DES, params->totalSizeDesired[level]); 249185377Ssam OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, 250185377Ssam AR_PHY_AGC_CTL1_COARSE_LOW, params->coarseLow[level]); 251185377Ssam OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, 252185377Ssam AR_PHY_AGC_CTL1_COARSE_HIGH, params->coarseHigh[level]); 253185377Ssam OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, 254185377Ssam AR_PHY_FIND_SIG_FIRPWR, params->firpwr[level]); 255185377Ssam 256185377Ssam if (level > aniState->noiseImmunityLevel) 257185377Ssam ahp->ah_stats.ast_ani_niup++; 258185377Ssam else if (level < aniState->noiseImmunityLevel) 259185377Ssam ahp->ah_stats.ast_ani_nidown++; 260185377Ssam aniState->noiseImmunityLevel = level; 261185377Ssam break; 262185377Ssam } 263185377Ssam case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: { 264185377Ssam static const TABLE m1ThreshLow = { 127, 50 }; 265185377Ssam static const TABLE m2ThreshLow = { 127, 40 }; 266185377Ssam static const TABLE m1Thresh = { 127, 0x4d }; 267185377Ssam static const TABLE m2Thresh = { 127, 0x40 }; 268185377Ssam static const TABLE m2CountThr = { 31, 16 }; 269185377Ssam static const TABLE m2CountThrLow = { 63, 48 }; 270185377Ssam u_int on = param ? 1 : 0; 271185377Ssam 272185377Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, 273185377Ssam AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1ThreshLow[on]); 274185377Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, 275185377Ssam AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2ThreshLow[on]); 276185377Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, 277185377Ssam AR_PHY_SFCORR_M1_THRESH, m1Thresh[on]); 278185377Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, 279185377Ssam AR_PHY_SFCORR_M2_THRESH, m2Thresh[on]); 280185377Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, 281185377Ssam AR_PHY_SFCORR_M2COUNT_THR, m2CountThr[on]); 282185377Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, 283185377Ssam AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2CountThrLow[on]); 284185377Ssam 285185377Ssam if (on) { 286185377Ssam OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, 287185377Ssam AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); 288188444Ssam ahp->ah_stats.ast_ani_ofdmon++; 289185377Ssam } else { 290185377Ssam OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, 291185377Ssam AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); 292188444Ssam ahp->ah_stats.ast_ani_ofdmoff++; 293185377Ssam } 294185377Ssam aniState->ofdmWeakSigDetectOff = !on; 295185377Ssam break; 296185377Ssam } 297185377Ssam case HAL_ANI_CCK_WEAK_SIGNAL_THR: { 298185377Ssam static const TABLE weakSigThrCck = { 8, 6 }; 299185377Ssam u_int high = param ? 1 : 0; 300185377Ssam 301185377Ssam OS_REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT, 302185377Ssam AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, weakSigThrCck[high]); 303185377Ssam if (high) 304185377Ssam ahp->ah_stats.ast_ani_cckhigh++; 305185377Ssam else 306185377Ssam ahp->ah_stats.ast_ani_ccklow++; 307185377Ssam aniState->cckWeakSigThreshold = high; 308185377Ssam break; 309185377Ssam } 310185377Ssam case HAL_ANI_FIRSTEP_LEVEL: { 311185377Ssam u_int level = param; 312185377Ssam 313186332Ssam if (level > params->maxFirstepLevel) { 314185377Ssam HALDEBUG(ah, HAL_DEBUG_ANY, 315185377Ssam "%s: level out of range (%u > %u)\n", 316185377Ssam __func__, level, params->maxFirstepLevel); 317185377Ssam return AH_FALSE; 318185377Ssam } 319185377Ssam OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, 320185377Ssam AR_PHY_FIND_SIG_FIRSTEP, params->firstep[level]); 321185377Ssam if (level > aniState->firstepLevel) 322185377Ssam ahp->ah_stats.ast_ani_stepup++; 323185377Ssam else if (level < aniState->firstepLevel) 324185377Ssam ahp->ah_stats.ast_ani_stepdown++; 325185377Ssam aniState->firstepLevel = level; 326185377Ssam break; 327185377Ssam } 328185377Ssam case HAL_ANI_SPUR_IMMUNITY_LEVEL: { 329185377Ssam u_int level = param; 330185377Ssam 331186332Ssam if (level > params->maxSpurImmunityLevel) { 332185377Ssam HALDEBUG(ah, HAL_DEBUG_ANY, 333185377Ssam "%s: level out of range (%u > %u)\n", 334185377Ssam __func__, level, params->maxSpurImmunityLevel); 335185377Ssam return AH_FALSE; 336185377Ssam } 337185377Ssam OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5, 338185377Ssam AR_PHY_TIMING5_CYCPWR_THR1, params->cycPwrThr1[level]); 339185377Ssam if (level > aniState->spurImmunityLevel) 340185377Ssam ahp->ah_stats.ast_ani_spurup++; 341185377Ssam else if (level < aniState->spurImmunityLevel) 342185377Ssam ahp->ah_stats.ast_ani_spurdown++; 343185377Ssam aniState->spurImmunityLevel = level; 344185377Ssam break; 345185377Ssam } 346185377Ssam case HAL_ANI_PRESENT: 347185377Ssam break; 348185377Ssam case HAL_ANI_MODE: 349185377Ssam if (param == 0) { 350185377Ssam ahp->ah_procPhyErr &= ~HAL_ANI_ENA; 351185377Ssam /* Turn off HW counters if we have them */ 352185377Ssam ar5212AniDetach(ah); 353224514Sadrian ah->ah_setRxFilter(ah, 354224514Sadrian ah->ah_getRxFilter(ah) &~ HAL_RX_FILTER_PHYERR); 355185377Ssam } else { /* normal/auto mode */ 356185377Ssam /* don't mess with state if already enabled */ 357185377Ssam if (ahp->ah_procPhyErr & HAL_ANI_ENA) 358185377Ssam break; 359185377Ssam if (ahp->ah_hasHwPhyCounters) { 360185377Ssam ar5212SetRxFilter(ah, 361185377Ssam ar5212GetRxFilter(ah) &~ HAL_RX_FILTER_PHYERR); 362185377Ssam /* Enable MIB Counters */ 363185377Ssam enableAniMIBCounters(ah, 364185377Ssam ahp->ah_curani != AH_NULL ? 365185377Ssam ahp->ah_curani->params: 366185377Ssam &ahp->ah_aniParams24 /*XXX*/); 367185377Ssam } else { 368224514Sadrian ah->ah_setRxFilter(ah, 369224514Sadrian ah->ah_getRxFilter(ah) | HAL_RX_FILTER_PHYERR); 370185377Ssam } 371185377Ssam ahp->ah_procPhyErr |= HAL_ANI_ENA; 372185377Ssam } 373185377Ssam break; 374185377Ssam#ifdef AH_PRIVATE_DIAG 375185377Ssam case HAL_ANI_PHYERR_RESET: 376185377Ssam ahp->ah_stats.ast_ani_ofdmerrs = 0; 377185377Ssam ahp->ah_stats.ast_ani_cckerrs = 0; 378185377Ssam break; 379185377Ssam#endif /* AH_PRIVATE_DIAG */ 380185377Ssam default: 381185377Ssam HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid cmd %u\n", 382185377Ssam __func__, cmd); 383185377Ssam return AH_FALSE; 384185377Ssam } 385185377Ssam return AH_TRUE; 386185377Ssam} 387185377Ssam 388185377Ssamstatic void 389185377Ssamar5212AniOfdmErrTrigger(struct ath_hal *ah) 390185377Ssam{ 391185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 392187831Ssam const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; 393185377Ssam struct ar5212AniState *aniState; 394185377Ssam const struct ar5212AniParams *params; 395185377Ssam 396185377Ssam HALASSERT(chan != AH_NULL); 397185377Ssam 398185377Ssam if (!ANI_ENA(ah)) 399185377Ssam return; 400185377Ssam 401185377Ssam aniState = ahp->ah_curani; 402185377Ssam params = aniState->params; 403185377Ssam /* First, raise noise immunity level, up to max */ 404186332Ssam if (aniState->noiseImmunityLevel+1 <= params->maxNoiseImmunityLevel) { 405185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise NI to %u\n", __func__, 406185380Ssam aniState->noiseImmunityLevel + 1); 407185377Ssam ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 408185377Ssam aniState->noiseImmunityLevel + 1); 409185377Ssam return; 410185377Ssam } 411185377Ssam /* then, raise spur immunity level, up to max */ 412186332Ssam if (aniState->spurImmunityLevel+1 <= params->maxSpurImmunityLevel) { 413185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise SI to %u\n", __func__, 414185380Ssam aniState->spurImmunityLevel + 1); 415185377Ssam ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 416185377Ssam aniState->spurImmunityLevel + 1); 417185377Ssam return; 418185377Ssam } 419185377Ssam 420185377Ssam if (ANI_ENA_RSSI(ah)) { 421185377Ssam int32_t rssi = BEACON_RSSI(ahp); 422185377Ssam if (rssi > params->rssiThrHigh) { 423185377Ssam /* 424185377Ssam * Beacon rssi is high, can turn off ofdm 425185377Ssam * weak sig detect. 426185377Ssam */ 427185377Ssam if (!aniState->ofdmWeakSigDetectOff) { 428185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, 429185380Ssam "%s: rssi %d OWSD off\n", __func__, rssi); 430185377Ssam ar5212AniControl(ah, 431185377Ssam HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 432185377Ssam AH_FALSE); 433185377Ssam ar5212AniControl(ah, 434185377Ssam HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); 435185377Ssam return; 436185377Ssam } 437185377Ssam /* 438185377Ssam * If weak sig detect is already off, as last resort, 439185377Ssam * raise firstep level 440185377Ssam */ 441186332Ssam if (aniState->firstepLevel+1 <= params->maxFirstepLevel) { 442185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, 443185380Ssam "%s: rssi %d raise ST %u\n", __func__, rssi, 444185380Ssam aniState->firstepLevel+1); 445185377Ssam ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 446185377Ssam aniState->firstepLevel + 1); 447185377Ssam return; 448185377Ssam } 449185377Ssam } else if (rssi > params->rssiThrLow) { 450185377Ssam /* 451185377Ssam * Beacon rssi in mid range, need ofdm weak signal 452185377Ssam * detect, but we can raise firststepLevel. 453185377Ssam */ 454185380Ssam if (aniState->ofdmWeakSigDetectOff) { 455185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, 456185380Ssam "%s: rssi %d OWSD on\n", __func__, rssi); 457185377Ssam ar5212AniControl(ah, 458185377Ssam HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 459185377Ssam AH_TRUE); 460185380Ssam } 461186332Ssam if (aniState->firstepLevel+1 <= params->maxFirstepLevel) { 462185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, 463185380Ssam "%s: rssi %d raise ST %u\n", __func__, rssi, 464185380Ssam aniState->firstepLevel+1); 465185377Ssam ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 466185377Ssam aniState->firstepLevel + 1); 467185380Ssam } 468185377Ssam return; 469185377Ssam } else { 470185377Ssam /* 471185377Ssam * Beacon rssi is low, if in 11b/g mode, turn off ofdm 472185377Ssam * weak signal detection and zero firstepLevel to 473185377Ssam * maximize CCK sensitivity 474185377Ssam */ 475187831Ssam if (IEEE80211_IS_CHAN_CCK(chan)) { 476185380Ssam if (!aniState->ofdmWeakSigDetectOff) { 477185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, 478185380Ssam "%s: rssi %d OWSD off\n", 479185380Ssam __func__, rssi); 480185377Ssam ar5212AniControl(ah, 481185377Ssam HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 482185377Ssam AH_FALSE); 483185380Ssam } 484185380Ssam if (aniState->firstepLevel > 0) { 485185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, 486185380Ssam "%s: rssi %d zero ST (was %u)\n", 487185380Ssam __func__, rssi, 488185380Ssam aniState->firstepLevel); 489185377Ssam ar5212AniControl(ah, 490185377Ssam HAL_ANI_FIRSTEP_LEVEL, 0); 491185380Ssam } 492185377Ssam return; 493185377Ssam } 494185377Ssam } 495185377Ssam } 496185377Ssam} 497185377Ssam 498185377Ssamstatic void 499185377Ssamar5212AniCckErrTrigger(struct ath_hal *ah) 500185377Ssam{ 501185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 502187831Ssam const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; 503185377Ssam struct ar5212AniState *aniState; 504185377Ssam const struct ar5212AniParams *params; 505185377Ssam 506185377Ssam HALASSERT(chan != AH_NULL); 507185377Ssam 508185377Ssam if (!ANI_ENA(ah)) 509185377Ssam return; 510185377Ssam 511185377Ssam /* first, raise noise immunity level, up to max */ 512185377Ssam aniState = ahp->ah_curani; 513185377Ssam params = aniState->params; 514186332Ssam if (aniState->noiseImmunityLevel+1 <= params->maxNoiseImmunityLevel) { 515185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise NI to %u\n", __func__, 516185380Ssam aniState->noiseImmunityLevel + 1); 517185377Ssam ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 518185377Ssam aniState->noiseImmunityLevel + 1); 519185377Ssam return; 520185377Ssam } 521185377Ssam 522185377Ssam if (ANI_ENA_RSSI(ah)) { 523185377Ssam int32_t rssi = BEACON_RSSI(ahp); 524185377Ssam if (rssi > params->rssiThrLow) { 525185377Ssam /* 526185377Ssam * Beacon signal in mid and high range, 527185377Ssam * raise firstep level. 528185377Ssam */ 529186332Ssam if (aniState->firstepLevel+1 <= params->maxFirstepLevel) { 530185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, 531185380Ssam "%s: rssi %d raise ST %u\n", __func__, rssi, 532185380Ssam aniState->firstepLevel+1); 533185377Ssam ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 534185377Ssam aniState->firstepLevel + 1); 535185380Ssam } 536185377Ssam } else { 537185377Ssam /* 538185377Ssam * Beacon rssi is low, zero firstep level to maximize 539185377Ssam * CCK sensitivity in 11b/g mode. 540185377Ssam */ 541185377Ssam /* XXX can optimize */ 542187831Ssam if (IEEE80211_IS_CHAN_B(chan) || 543187831Ssam IEEE80211_IS_CHAN_G(chan)) { 544185380Ssam if (aniState->firstepLevel > 0) { 545185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, 546185380Ssam "%s: rssi %d zero ST (was %u)\n", 547185380Ssam __func__, rssi, 548185380Ssam aniState->firstepLevel); 549185377Ssam ar5212AniControl(ah, 550185377Ssam HAL_ANI_FIRSTEP_LEVEL, 0); 551185380Ssam } 552185377Ssam } 553185377Ssam } 554185377Ssam } 555185377Ssam} 556185377Ssam 557185377Ssamstatic void 558185377Ssamar5212AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState) 559185377Ssam{ 560185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 561185377Ssam 562185377Ssam aniState->listenTime = 0; 563185377Ssam if (ahp->ah_hasHwPhyCounters) { 564185377Ssam const struct ar5212AniParams *params = aniState->params; 565185377Ssam /* 566185377Ssam * NB: these are written on reset based on the 567185377Ssam * ini so we must re-write them! 568185377Ssam */ 569185377Ssam OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase); 570185377Ssam OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase); 571185377Ssam OS_REG_WRITE(ah, AR_PHYCNTMASK1, AR_PHY_ERR_OFDM_TIMING); 572185377Ssam OS_REG_WRITE(ah, AR_PHYCNTMASK2, AR_PHY_ERR_CCK_TIMING); 573185377Ssam 574185377Ssam /* Clear the mib counters and save them in the stats */ 575185377Ssam ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 576185377Ssam } 577185377Ssam aniState->ofdmPhyErrCount = 0; 578185377Ssam aniState->cckPhyErrCount = 0; 579185377Ssam} 580185377Ssam 581185377Ssam/* 582185377Ssam * Restore/reset the ANI parameters and reset the statistics. 583185377Ssam * This routine must be called for every channel change. 584185377Ssam */ 585185377Ssamvoid 586187831Ssamar5212AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan, 587185377Ssam HAL_OPMODE opmode, int restore) 588185377Ssam{ 589185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 590187831Ssam HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); 591187831Ssam /* XXX bounds check ic_devdata */ 592187831Ssam struct ar5212AniState *aniState = &ahp->ah_ani[chan->ic_devdata]; 593185377Ssam uint32_t rxfilter; 594185377Ssam 595187831Ssam if ((ichan->privFlags & CHANNEL_ANI_INIT) == 0) { 596187831Ssam OS_MEMZERO(aniState, sizeof(*aniState)); 597187831Ssam if (IEEE80211_IS_CHAN_2GHZ(chan)) 598187831Ssam aniState->params = &ahp->ah_aniParams24; 599187831Ssam else 600187831Ssam aniState->params = &ahp->ah_aniParams5; 601187831Ssam ichan->privFlags |= CHANNEL_ANI_INIT; 602187831Ssam HALASSERT((ichan->privFlags & CHANNEL_ANI_SETUP) == 0); 603187831Ssam } 604185377Ssam ahp->ah_curani = aniState; 605185377Ssam#if 0 606187831Ssam ath_hal_printf(ah,"%s: chan %u/0x%x restore %d opmode %u%s\n", 607187831Ssam __func__, chan->ic_freq, chan->ic_flags, restore, opmode, 608187831Ssam ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); 609185377Ssam#else 610187831Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "%s: chan %u/0x%x restore %d opmode %u%s\n", 611187831Ssam __func__, chan->ic_freq, chan->ic_flags, restore, opmode, 612187831Ssam ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); 613185377Ssam#endif 614185377Ssam OS_MARK(ah, AH_MARK_ANI_RESET, opmode); 615185377Ssam 616185377Ssam /* 617185377Ssam * Turn off PHY error frame delivery while we futz with settings. 618185377Ssam */ 619224514Sadrian rxfilter = ah->ah_getRxFilter(ah); 620224514Sadrian ah->ah_setRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR); 621224514Sadrian 622185377Ssam /* 623224514Sadrian * If ANI is disabled at this point, don't set the default 624224514Sadrian * ANI parameter settings - leave the HAL settings there. 625224514Sadrian * This is (currently) needed for reliable radar detection. 626224514Sadrian */ 627224514Sadrian if (! ANI_ENA(ah)) { 628224514Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: ANI disabled\n", 629224514Sadrian __func__); 630224514Sadrian goto finish; 631224514Sadrian } 632224514Sadrian 633224514Sadrian /* 634185377Ssam * Automatic processing is done only in station mode right now. 635185377Ssam */ 636185377Ssam if (opmode == HAL_M_STA) 637185377Ssam ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA; 638185377Ssam else 639185377Ssam ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA; 640185377Ssam /* 641185377Ssam * Set all ani parameters. We either set them to initial 642185377Ssam * values or restore the previous ones for the channel. 643185377Ssam * XXX if ANI follows hardware, we don't care what mode we're 644185377Ssam * XXX in, we should keep the ani parameters 645185377Ssam */ 646187831Ssam if (restore && (ichan->privFlags & CHANNEL_ANI_SETUP)) { 647185377Ssam ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 648185377Ssam aniState->noiseImmunityLevel); 649185377Ssam ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 650185377Ssam aniState->spurImmunityLevel); 651185377Ssam ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 652185377Ssam !aniState->ofdmWeakSigDetectOff); 653185377Ssam ar5212AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, 654185377Ssam aniState->cckWeakSigThreshold); 655185377Ssam ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 656185377Ssam aniState->firstepLevel); 657185377Ssam } else { 658185377Ssam ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0); 659185377Ssam ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); 660185377Ssam ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 661185377Ssam AH_TRUE); 662185377Ssam ar5212AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE); 663185377Ssam ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); 664187831Ssam ichan->privFlags |= CHANNEL_ANI_SETUP; 665185377Ssam } 666224514Sadrian /* 667224514Sadrian * In case the counters haven't yet been setup; set them up. 668224514Sadrian */ 669224514Sadrian enableAniMIBCounters(ah, ahp->ah_curani->params); 670185377Ssam ar5212AniRestart(ah, aniState); 671185377Ssam 672224514Sadrianfinish: 673185377Ssam /* restore RX filter mask */ 674224514Sadrian ah->ah_setRxFilter(ah, rxfilter); 675185377Ssam} 676185377Ssam 677185377Ssam/* 678185377Ssam * Process a MIB interrupt. We may potentially be invoked because 679185377Ssam * any of the MIB counters overflow/trigger so don't assume we're 680185377Ssam * here because a PHY error counter triggered. 681185377Ssam */ 682185377Ssamvoid 683185377Ssamar5212ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats) 684185377Ssam{ 685185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 686185377Ssam uint32_t phyCnt1, phyCnt2; 687185377Ssam 688185377Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x " 689185377Ssam "filtofdm 0x%x filtcck 0x%x\n", 690185377Ssam __func__, OS_REG_READ(ah, AR_MIBC), 691185377Ssam OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2), 692185377Ssam OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK)); 693185377Ssam 694185377Ssam /* 695185377Ssam * First order of business is to clear whatever caused 696185377Ssam * the interrupt so we don't keep getting interrupted. 697185377Ssam * We have the usual mib counters that are reset-on-read 698185377Ssam * and the additional counters that appeared starting in 699185377Ssam * Hainan. We collect the mib counters and explicitly 700185377Ssam * zero additional counters we are not using. Anything 701185377Ssam * else is reset only if it caused the interrupt. 702185377Ssam */ 703185377Ssam /* NB: these are not reset-on-read */ 704185377Ssam phyCnt1 = OS_REG_READ(ah, AR_PHYCNT1); 705185377Ssam phyCnt2 = OS_REG_READ(ah, AR_PHYCNT2); 706185377Ssam /* not used, always reset them in case they are the cause */ 707185377Ssam OS_REG_WRITE(ah, AR_FILTOFDM, 0); 708185377Ssam OS_REG_WRITE(ah, AR_FILTCCK, 0); 709185377Ssam 710185377Ssam /* Clear the mib counters and save them in the stats */ 711185377Ssam ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 712185377Ssam ahp->ah_stats.ast_nodestats = *stats; 713185377Ssam 714185377Ssam /* 715185377Ssam * Check for an ani stat hitting the trigger threshold. 716185377Ssam * When this happens we get a MIB interrupt and the top 717185377Ssam * 2 bits of the counter register will be 0b11, hence 718185377Ssam * the mask check of phyCnt?. 719185377Ssam */ 720185377Ssam if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || 721185377Ssam ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { 722185377Ssam struct ar5212AniState *aniState = ahp->ah_curani; 723185377Ssam const struct ar5212AniParams *params = aniState->params; 724185377Ssam uint32_t ofdmPhyErrCnt, cckPhyErrCnt; 725185377Ssam 726185377Ssam ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; 727185377Ssam ahp->ah_stats.ast_ani_ofdmerrs += 728185377Ssam ofdmPhyErrCnt - aniState->ofdmPhyErrCount; 729185377Ssam aniState->ofdmPhyErrCount = ofdmPhyErrCnt; 730185377Ssam 731185377Ssam cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; 732185377Ssam ahp->ah_stats.ast_ani_cckerrs += 733185377Ssam cckPhyErrCnt - aniState->cckPhyErrCount; 734185377Ssam aniState->cckPhyErrCount = cckPhyErrCnt; 735185377Ssam 736185377Ssam /* 737185377Ssam * NB: figure out which counter triggered. If both 738185377Ssam * trigger we'll only deal with one as the processing 739185377Ssam * clobbers the error counter so the trigger threshold 740185377Ssam * check will never be true. 741185377Ssam */ 742185377Ssam if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh) 743185377Ssam ar5212AniOfdmErrTrigger(ah); 744185377Ssam if (aniState->cckPhyErrCount > params->cckTrigHigh) 745185377Ssam ar5212AniCckErrTrigger(ah); 746185377Ssam /* NB: always restart to insure the h/w counters are reset */ 747185377Ssam ar5212AniRestart(ah, aniState); 748185377Ssam } 749185377Ssam} 750185377Ssam 751185377Ssamvoid 752185377Ssamar5212AniPhyErrReport(struct ath_hal *ah, const struct ath_rx_status *rs) 753185377Ssam{ 754185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 755185377Ssam struct ar5212AniState *aniState; 756185377Ssam const struct ar5212AniParams *params; 757185377Ssam 758185377Ssam HALASSERT(!ahp->ah_hasHwPhyCounters && rs != AH_NULL); 759185377Ssam 760185377Ssam aniState = ahp->ah_curani; 761185377Ssam params = aniState->params; 762185377Ssam if (rs->rs_phyerr == HAL_PHYERR_OFDM_TIMING) { 763185377Ssam aniState->ofdmPhyErrCount++; 764185377Ssam ahp->ah_stats.ast_ani_ofdmerrs++; 765185377Ssam if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh) { 766185377Ssam ar5212AniOfdmErrTrigger(ah); 767185377Ssam ar5212AniRestart(ah, aniState); 768185377Ssam } 769185377Ssam } else if (rs->rs_phyerr == HAL_PHYERR_CCK_TIMING) { 770185377Ssam aniState->cckPhyErrCount++; 771185377Ssam ahp->ah_stats.ast_ani_cckerrs++; 772185377Ssam if (aniState->cckPhyErrCount > params->cckTrigHigh) { 773185377Ssam ar5212AniCckErrTrigger(ah); 774185377Ssam ar5212AniRestart(ah, aniState); 775185377Ssam } 776185377Ssam } 777185377Ssam} 778185377Ssam 779185377Ssamstatic void 780185377Ssamar5212AniLowerImmunity(struct ath_hal *ah) 781185377Ssam{ 782185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 783185377Ssam struct ar5212AniState *aniState; 784185377Ssam const struct ar5212AniParams *params; 785185377Ssam 786185377Ssam HALASSERT(ANI_ENA(ah)); 787185377Ssam 788185377Ssam aniState = ahp->ah_curani; 789185377Ssam params = aniState->params; 790185377Ssam if (ANI_ENA_RSSI(ah)) { 791185377Ssam int32_t rssi = BEACON_RSSI(ahp); 792185377Ssam if (rssi > params->rssiThrHigh) { 793185377Ssam /* 794185377Ssam * Beacon signal is high, leave ofdm weak signal 795185377Ssam * detection off or it may oscillate. Let it fall 796185377Ssam * through. 797185377Ssam */ 798185377Ssam } else if (rssi > params->rssiThrLow) { 799185377Ssam /* 800185377Ssam * Beacon rssi in mid range, turn on ofdm weak signal 801185377Ssam * detection or lower firstep level. 802185377Ssam */ 803185377Ssam if (aniState->ofdmWeakSigDetectOff) { 804185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, 805185380Ssam "%s: rssi %d OWSD on\n", __func__, rssi); 806185377Ssam ar5212AniControl(ah, 807185377Ssam HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 808185377Ssam AH_TRUE); 809185377Ssam return; 810185377Ssam } 811185377Ssam if (aniState->firstepLevel > 0) { 812185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, 813185380Ssam "%s: rssi %d lower ST %u\n", __func__, rssi, 814185380Ssam aniState->firstepLevel-1); 815185377Ssam ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 816185377Ssam aniState->firstepLevel - 1); 817185377Ssam return; 818185377Ssam } 819185377Ssam } else { 820185377Ssam /* 821185377Ssam * Beacon rssi is low, reduce firstep level. 822185377Ssam */ 823185377Ssam if (aniState->firstepLevel > 0) { 824185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, 825185380Ssam "%s: rssi %d lower ST %u\n", __func__, rssi, 826185380Ssam aniState->firstepLevel-1); 827185377Ssam ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 828185377Ssam aniState->firstepLevel - 1); 829185377Ssam return; 830185377Ssam } 831185377Ssam } 832185377Ssam } 833185377Ssam /* then lower spur immunity level, down to zero */ 834185377Ssam if (aniState->spurImmunityLevel > 0) { 835185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "%s: lower SI %u\n", 836185380Ssam __func__, aniState->spurImmunityLevel-1); 837185377Ssam ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 838185377Ssam aniState->spurImmunityLevel - 1); 839185377Ssam return; 840185377Ssam } 841185377Ssam /* 842185377Ssam * if all else fails, lower noise immunity level down to a min value 843185377Ssam * zero for now 844185377Ssam */ 845185377Ssam if (aniState->noiseImmunityLevel > 0) { 846185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "%s: lower NI %u\n", 847185380Ssam __func__, aniState->noiseImmunityLevel-1); 848185377Ssam ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 849185377Ssam aniState->noiseImmunityLevel - 1); 850185377Ssam return; 851185377Ssam } 852185377Ssam} 853185377Ssam 854185377Ssam#define CLOCK_RATE 44000 /* XXX use mac_usec or similar */ 855185377Ssam/* convert HW counter values to ms using 11g clock rate, goo9d enough 856185377Ssam for 11a and Turbo */ 857185377Ssam 858185377Ssam/* 859185377Ssam * Return an approximation of the time spent ``listening'' by 860185377Ssam * deducting the cycles spent tx'ing and rx'ing from the total 861185377Ssam * cycle count since our last call. A return value <0 indicates 862185377Ssam * an invalid/inconsistent time. 863185377Ssam */ 864185377Ssamstatic int32_t 865185377Ssamar5212AniGetListenTime(struct ath_hal *ah) 866185377Ssam{ 867185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 868256139Sadrian struct ar5212AniState *aniState = NULL; 869256139Sadrian int32_t listenTime = 0; 870256139Sadrian int good; 871256139Sadrian HAL_SURVEY_SAMPLE hs; 872185377Ssam 873256139Sadrian /* 874256139Sadrian * We shouldn't see ah_curchan be NULL, but just in case.. 875256139Sadrian */ 876256139Sadrian if (AH_PRIVATE(ah)->ah_curchan == AH_NULL) { 877256139Sadrian ath_hal_printf(ah, "%s: ah_curchan = NULL?\n", __func__); 878256139Sadrian return (0); 879256139Sadrian } 880185377Ssam 881256139Sadrian /* 882256139Sadrian * Fetch the current statistics, squirrel away the current 883256139Sadrian * sample, bump the sequence/sample counter. 884256139Sadrian */ 885256139Sadrian OS_MEMZERO(&hs, sizeof(hs)); 886256139Sadrian good = ar5212GetMibCycleCounts(ah, &hs); 887280828Sadrian ath_hal_survey_add_sample(ah, &hs); 888256139Sadrian 889256139Sadrian if (ANI_ENA(ah)) 890256139Sadrian aniState = ahp->ah_curani; 891256139Sadrian 892256139Sadrian if (good == AH_FALSE) { 893185377Ssam /* 894185377Ssam * Cycle counter wrap (or initial call); it's not possible 895185377Ssam * to accurately calculate a value because the registers 896185377Ssam * right shift rather than wrap--so punt and return 0. 897185377Ssam */ 898185377Ssam listenTime = 0; 899185377Ssam ahp->ah_stats.ast_ani_lzero++; 900256139Sadrian } else if (ANI_ENA(ah)) { 901256139Sadrian /* 902256139Sadrian * Only calculate and update the cycle count if we have 903256139Sadrian * an ANI state. 904256139Sadrian */ 905256139Sadrian int32_t ccdelta = 906256139Sadrian AH5212(ah)->ah_cycleCount - aniState->cycleCount; 907256139Sadrian int32_t rfdelta = 908256139Sadrian AH5212(ah)->ah_rxBusy - aniState->rxFrameCount; 909256139Sadrian int32_t tfdelta = 910256139Sadrian AH5212(ah)->ah_txBusy - aniState->txFrameCount; 911185377Ssam listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE; 912185377Ssam } 913256139Sadrian 914256139Sadrian /* 915256139Sadrian * Again, only update ANI state if we have it. 916256139Sadrian */ 917256139Sadrian if (ANI_ENA(ah)) { 918256139Sadrian aniState->cycleCount = AH5212(ah)->ah_cycleCount; 919256139Sadrian aniState->rxFrameCount = AH5212(ah)->ah_rxBusy; 920256139Sadrian aniState->txFrameCount = AH5212(ah)->ah_txBusy; 921256139Sadrian } 922256139Sadrian 923185377Ssam return listenTime; 924185377Ssam} 925185377Ssam 926185377Ssam/* 927185377Ssam * Update ani stats in preparation for listen time processing. 928185377Ssam */ 929185377Ssamstatic void 930185377SsamupdateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState) 931185377Ssam{ 932185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 933185377Ssam const struct ar5212AniParams *params = aniState->params; 934185377Ssam uint32_t phyCnt1, phyCnt2; 935185377Ssam int32_t ofdmPhyErrCnt, cckPhyErrCnt; 936185377Ssam 937185377Ssam HALASSERT(ahp->ah_hasHwPhyCounters); 938185377Ssam 939185377Ssam /* Clear the mib counters and save them in the stats */ 940185377Ssam ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 941185377Ssam 942185377Ssam /* NB: these are not reset-on-read */ 943185377Ssam phyCnt1 = OS_REG_READ(ah, AR_PHYCNT1); 944185377Ssam phyCnt2 = OS_REG_READ(ah, AR_PHYCNT2); 945185377Ssam 946185377Ssam /* NB: these are spec'd to never roll-over */ 947185377Ssam ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; 948185377Ssam if (ofdmPhyErrCnt < 0) { 949185377Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n", 950185377Ssam ofdmPhyErrCnt, phyCnt1); 951185377Ssam ofdmPhyErrCnt = AR_PHY_COUNTMAX; 952185377Ssam } 953185377Ssam ahp->ah_stats.ast_ani_ofdmerrs += 954185377Ssam ofdmPhyErrCnt - aniState->ofdmPhyErrCount; 955185377Ssam aniState->ofdmPhyErrCount = ofdmPhyErrCnt; 956185377Ssam 957185377Ssam cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; 958185377Ssam if (cckPhyErrCnt < 0) { 959185377Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n", 960185377Ssam cckPhyErrCnt, phyCnt2); 961185377Ssam cckPhyErrCnt = AR_PHY_COUNTMAX; 962185377Ssam } 963185377Ssam ahp->ah_stats.ast_ani_cckerrs += 964185377Ssam cckPhyErrCnt - aniState->cckPhyErrCount; 965185377Ssam aniState->cckPhyErrCount = cckPhyErrCnt; 966185377Ssam} 967185377Ssam 968217684Sadrianvoid 969217684Sadrianar5212RxMonitor(struct ath_hal *ah, const HAL_NODE_STATS *stats, 970217684Sadrian const struct ieee80211_channel *chan) 971217684Sadrian{ 972217684Sadrian struct ath_hal_5212 *ahp = AH5212(ah); 973217684Sadrian ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi; 974217684Sadrian} 975217684Sadrian 976185377Ssam/* 977185377Ssam * Do periodic processing. This routine is called from the 978185377Ssam * driver's rx interrupt handler after processing frames. 979185377Ssam */ 980185377Ssamvoid 981217684Sadrianar5212AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan) 982185377Ssam{ 983185377Ssam struct ath_hal_5212 *ahp = AH5212(ah); 984185377Ssam struct ar5212AniState *aniState = ahp->ah_curani; 985185377Ssam const struct ar5212AniParams *params; 986185377Ssam int32_t listenTime; 987185377Ssam 988256139Sadrian /* Always update from the MIB, for statistics gathering */ 989256139Sadrian listenTime = ar5212AniGetListenTime(ah); 990256139Sadrian 991185377Ssam /* XXX can aniState be null? */ 992185377Ssam if (aniState == AH_NULL) 993185377Ssam return; 994185377Ssam if (!ANI_ENA(ah)) 995185377Ssam return; 996185377Ssam 997185377Ssam if (listenTime < 0) { 998185377Ssam ahp->ah_stats.ast_ani_lneg++; 999185377Ssam /* restart ANI period if listenTime is invalid */ 1000185377Ssam ar5212AniRestart(ah, aniState); 1001185377Ssam } 1002185377Ssam /* XXX beware of overflow? */ 1003185377Ssam aniState->listenTime += listenTime; 1004185377Ssam 1005185377Ssam OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime); 1006185377Ssam 1007185377Ssam params = aniState->params; 1008185377Ssam if (aniState->listenTime > 5*params->period) { 1009185377Ssam /* 1010185377Ssam * Check to see if need to lower immunity if 1011185377Ssam * 5 aniPeriods have passed 1012185377Ssam */ 1013185377Ssam if (ahp->ah_hasHwPhyCounters) 1014185377Ssam updateMIBStats(ah, aniState); 1015185377Ssam if (aniState->ofdmPhyErrCount <= aniState->listenTime * 1016185377Ssam params->ofdmTrigLow/1000 && 1017185377Ssam aniState->cckPhyErrCount <= aniState->listenTime * 1018185377Ssam params->cckTrigLow/1000) 1019185377Ssam ar5212AniLowerImmunity(ah); 1020185377Ssam ar5212AniRestart(ah, aniState); 1021185377Ssam } else if (aniState->listenTime > params->period) { 1022185377Ssam if (ahp->ah_hasHwPhyCounters) 1023185377Ssam updateMIBStats(ah, aniState); 1024185377Ssam /* check to see if need to raise immunity */ 1025185377Ssam if (aniState->ofdmPhyErrCount > aniState->listenTime * 1026185377Ssam params->ofdmTrigHigh / 1000) { 1027185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, 1028185380Ssam "%s: OFDM err %u listenTime %u\n", __func__, 1029185380Ssam aniState->ofdmPhyErrCount, aniState->listenTime); 1030185377Ssam ar5212AniOfdmErrTrigger(ah); 1031185377Ssam ar5212AniRestart(ah, aniState); 1032185377Ssam } else if (aniState->cckPhyErrCount > aniState->listenTime * 1033185377Ssam params->cckTrigHigh / 1000) { 1034185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, 1035185380Ssam "%s: CCK err %u listenTime %u\n", __func__, 1036185380Ssam aniState->cckPhyErrCount, aniState->listenTime); 1037185377Ssam ar5212AniCckErrTrigger(ah); 1038185377Ssam ar5212AniRestart(ah, aniState); 1039185377Ssam } 1040185377Ssam } 1041185377Ssam} 1042