ar5416_ani.c revision 217925
1/* 2 * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting 3 * Copyright (c) 2002-2008 Atheros Communications, Inc. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 * 17 * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c 217925 2011-01-27 08:42:50Z adrian $ 18 */ 19#include "opt_ah.h" 20 21/* 22 * XXX this is virtually the same code as for 5212; we reuse 23 * storage in the 5212 state block; need to refactor. 24 */ 25#include "ah.h" 26#include "ah_internal.h" 27#include "ah_desc.h" 28 29#include "ar5416/ar5416.h" 30#include "ar5416/ar5416reg.h" 31#include "ar5416/ar5416phy.h" 32 33/* 34 * Anti noise immunity support. We track phy errors and react 35 * to excessive errors by adjusting the noise immunity parameters. 36 */ 37 38#define HAL_EP_RND(x, mul) \ 39 ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) 40#define BEACON_RSSI(ahp) \ 41 HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \ 42 HAL_RSSI_EP_MULTIPLIER) 43 44/* 45 * ANI processing tunes radio parameters according to PHY errors 46 * and related information. This is done for for noise and spur 47 * immunity in all operating modes if the device indicates it's 48 * capable at attach time. In addition, when there is a reference 49 * rssi value (e.g. beacon frames from an ap in station mode) 50 * further tuning is done. 51 * 52 * ANI_ENA indicates whether any ANI processing should be done; 53 * this is specified at attach time. 54 * 55 * ANI_ENA_RSSI indicates whether rssi-based processing should 56 * done, this is enabled based on operating mode and is meaningful 57 * only if ANI_ENA is true. 58 * 59 * ANI parameters are typically controlled only by the hal. The 60 * AniControl interface however permits manual tuning through the 61 * diagnostic api. 62 */ 63#define ANI_ENA(ah) \ 64 (AH5212(ah)->ah_procPhyErr & HAL_ANI_ENA) 65#define ANI_ENA_RSSI(ah) \ 66 (AH5212(ah)->ah_procPhyErr & HAL_RSSI_ANI_ENA) 67 68#define ah_mibStats ah_stats.ast_mibstats 69 70static void 71enableAniMIBCounters(struct ath_hal *ah, const struct ar5212AniParams *params) 72{ 73 struct ath_hal_5212 *ahp = AH5212(ah); 74 75 HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Enable mib counters: " 76 "OfdmPhyErrBase 0x%x cckPhyErrBase 0x%x\n", 77 __func__, params->ofdmPhyErrBase, params->cckPhyErrBase); 78 79 OS_REG_WRITE(ah, AR_FILTOFDM, 0); 80 OS_REG_WRITE(ah, AR_FILTCCK, 0); 81 82 OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase); 83 OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase); 84 OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); 85 OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); 86 87 ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save+clear counters*/ 88 ar5212EnableMibCounters(ah); /* enable everything */ 89} 90 91static void 92disableAniMIBCounters(struct ath_hal *ah) 93{ 94 struct ath_hal_5212 *ahp = AH5212(ah); 95 96 HALDEBUG(ah, HAL_DEBUG_ANI, "Disable MIB counters\n"); 97 98 ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save stats */ 99 ar5212DisableMibCounters(ah); /* disable everything */ 100 101 OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, 0); 102 OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, 0); 103} 104 105static void 106setPhyErrBase(struct ath_hal *ah, struct ar5212AniParams *params) 107{ 108 if (params->ofdmTrigHigh >= AR_PHY_COUNTMAX) { 109 HALDEBUG(ah, HAL_DEBUG_ANY, 110 "OFDM Trigger %d is too high for hw counters, using max\n", 111 params->ofdmTrigHigh); 112 params->ofdmPhyErrBase = 0; 113 } else 114 params->ofdmPhyErrBase = AR_PHY_COUNTMAX - params->ofdmTrigHigh; 115 if (params->cckTrigHigh >= AR_PHY_COUNTMAX) { 116 HALDEBUG(ah, HAL_DEBUG_ANY, 117 "CCK Trigger %d is too high for hw counters, using max\n", 118 params->cckTrigHigh); 119 params->cckPhyErrBase = 0; 120 } else 121 params->cckPhyErrBase = AR_PHY_COUNTMAX - params->cckTrigHigh; 122} 123 124/* 125 * Setup ANI handling. Sets all thresholds and reset the 126 * channel statistics. Note that ar5416AniReset should be 127 * called by ar5416Reset before anything else happens and 128 * that's where we force initial settings. 129 */ 130void 131ar5416AniAttach(struct ath_hal *ah, const struct ar5212AniParams *params24, 132 const struct ar5212AniParams *params5, HAL_BOOL enable) 133{ 134 struct ath_hal_5212 *ahp = AH5212(ah); 135 136 if (params24 != AH_NULL) { 137 OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24)); 138 setPhyErrBase(ah, &ahp->ah_aniParams24); 139 } 140 if (params5 != AH_NULL) { 141 OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5)); 142 setPhyErrBase(ah, &ahp->ah_aniParams5); 143 } 144 145 OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani)); 146 /* Enable MIB Counters */ 147 enableAniMIBCounters(ah, &ahp->ah_aniParams24 /*XXX*/); 148 149 if (enable) { /* Enable ani now */ 150 HALASSERT(params24 != AH_NULL && params5 != AH_NULL); 151 ahp->ah_procPhyErr |= HAL_ANI_ENA; 152 } else { 153 ahp->ah_procPhyErr &= ~HAL_ANI_ENA; 154 } 155} 156 157/* 158 * Cleanup any ANI state setup. 159 */ 160void 161ar5416AniDetach(struct ath_hal *ah) 162{ 163 HALDEBUG(ah, HAL_DEBUG_ANI, "Detaching Ani\n"); 164 disableAniMIBCounters(ah); 165} 166 167/* 168 * Control Adaptive Noise Immunity Parameters 169 */ 170HAL_BOOL 171ar5416AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param) 172{ 173 typedef int TABLE[]; 174 struct ath_hal_5212 *ahp = AH5212(ah); 175 struct ar5212AniState *aniState = ahp->ah_curani; 176 const struct ar5212AniParams *params = aniState->params; 177 178 OS_MARK(ah, AH_MARK_ANI_CONTROL, cmd); 179 180 switch (cmd) { 181 case HAL_ANI_NOISE_IMMUNITY_LEVEL: { 182 u_int level = param; 183 184 HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_NOISE_IMMUNITY_LEVEL: set level = %d\n", __func__, level); 185 if (level >= params->maxNoiseImmunityLevel) { 186 HALDEBUG(ah, HAL_DEBUG_ANI, 187 "%s: immunity level out of range (%u > %u)\n", 188 __func__, level, params->maxNoiseImmunityLevel); 189 return AH_FALSE; 190 } 191 192 OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, 193 AR_PHY_DESIRED_SZ_TOT_DES, params->totalSizeDesired[level]); 194 OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, 195 AR_PHY_AGC_CTL1_COARSE_LOW, params->coarseLow[level]); 196 OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, 197 AR_PHY_AGC_CTL1_COARSE_HIGH, params->coarseHigh[level]); 198 OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, 199 AR_PHY_FIND_SIG_FIRPWR, params->firpwr[level]); 200 201 if (level > aniState->noiseImmunityLevel) 202 ahp->ah_stats.ast_ani_niup++; 203 else if (level < aniState->noiseImmunityLevel) 204 ahp->ah_stats.ast_ani_nidown++; 205 aniState->noiseImmunityLevel = level; 206 break; 207 } 208 case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: { 209 static const TABLE m1ThreshLow = { 127, 50 }; 210 static const TABLE m2ThreshLow = { 127, 40 }; 211 static const TABLE m1Thresh = { 127, 0x4d }; 212 static const TABLE m2Thresh = { 127, 0x40 }; 213 static const TABLE m2CountThr = { 31, 16 }; 214 static const TABLE m2CountThrLow = { 63, 48 }; 215 u_int on = param ? 1 : 0; 216 217 HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: %s\n", __func__, on ? "enabled" : "disabled"); 218 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, 219 AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1ThreshLow[on]); 220 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, 221 AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2ThreshLow[on]); 222 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, 223 AR_PHY_SFCORR_M1_THRESH, m1Thresh[on]); 224 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, 225 AR_PHY_SFCORR_M2_THRESH, m2Thresh[on]); 226 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, 227 AR_PHY_SFCORR_M2COUNT_THR, m2CountThr[on]); 228 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, 229 AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2CountThrLow[on]); 230 231 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, 232 AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLow[on]); 233 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, 234 AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLow[on]); 235 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, 236 AR_PHY_SFCORR_EXT_M1_THRESH, m1Thresh[on]); 237 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, 238 AR_PHY_SFCORR_EXT_M2_THRESH, m2Thresh[on]); 239 240 if (on) { 241 OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, 242 AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); 243 } else { 244 OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, 245 AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); 246 } 247 if (on) 248 ahp->ah_stats.ast_ani_ofdmon++; 249 else 250 ahp->ah_stats.ast_ani_ofdmoff++; 251 aniState->ofdmWeakSigDetectOff = !on; 252 break; 253 } 254 case HAL_ANI_CCK_WEAK_SIGNAL_THR: { 255 static const TABLE weakSigThrCck = { 8, 6 }; 256 u_int high = param ? 1 : 0; 257 258 HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_CCK_WEAK_SIGNAL_THR: %s\n", __func__, high ? "high" : "low"); 259 OS_REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT, 260 AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, weakSigThrCck[high]); 261 if (high) 262 ahp->ah_stats.ast_ani_cckhigh++; 263 else 264 ahp->ah_stats.ast_ani_ccklow++; 265 aniState->cckWeakSigThreshold = high; 266 break; 267 } 268 case HAL_ANI_FIRSTEP_LEVEL: { 269 u_int level = param; 270 271 HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_FIRSTEP_LEVEL: level = %d\n", __func__, level); 272 if (level >= params->maxFirstepLevel) { 273 HALDEBUG(ah, HAL_DEBUG_ANI, 274 "%s: firstep level out of range (%u > %u)\n", 275 __func__, level, params->maxFirstepLevel); 276 return AH_FALSE; 277 } 278 OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, 279 AR_PHY_FIND_SIG_FIRSTEP, params->firstep[level]); 280 if (level > aniState->firstepLevel) 281 ahp->ah_stats.ast_ani_stepup++; 282 else if (level < aniState->firstepLevel) 283 ahp->ah_stats.ast_ani_stepdown++; 284 aniState->firstepLevel = level; 285 break; 286 } 287 case HAL_ANI_SPUR_IMMUNITY_LEVEL: { 288 u_int level = param; 289 290 HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_SPUR_IMMUNITY_LEVEL: level = %d\n", __func__, level); 291 if (level >= params->maxSpurImmunityLevel) { 292 HALDEBUG(ah, HAL_DEBUG_ANI, 293 "%s: spur immunity level out of range (%u > %u)\n", 294 __func__, level, params->maxSpurImmunityLevel); 295 return AH_FALSE; 296 } 297 OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5, 298 AR_PHY_TIMING5_CYCPWR_THR1, params->cycPwrThr1[level]); 299 if (level > aniState->spurImmunityLevel) 300 ahp->ah_stats.ast_ani_spurup++; 301 else if (level < aniState->spurImmunityLevel) 302 ahp->ah_stats.ast_ani_spurdown++; 303 aniState->spurImmunityLevel = level; 304 break; 305 } 306 case HAL_ANI_PRESENT: 307 break; 308 case HAL_ANI_MODE: 309 if (param == 0) { 310 ahp->ah_procPhyErr &= ~HAL_ANI_ENA; 311 /* Turn off HW counters if we have them */ 312 ar5416AniDetach(ah); 313 ar5212SetRxFilter(ah, 314 ar5212GetRxFilter(ah) &~ HAL_RX_FILTER_PHYERR); 315 } else { /* normal/auto mode */ 316 /* don't mess with state if already enabled */ 317 if (ahp->ah_procPhyErr & HAL_ANI_ENA) 318 break; 319 ar5212SetRxFilter(ah, 320 ar5212GetRxFilter(ah) &~ HAL_RX_FILTER_PHYERR); 321 /* Enable MIB Counters */ 322 enableAniMIBCounters(ah, ahp->ah_curani != AH_NULL ? 323 ahp->ah_curani->params: &ahp->ah_aniParams24 /*XXX*/); 324 ahp->ah_procPhyErr |= HAL_ANI_ENA; 325 } 326 break; 327#ifdef AH_PRIVATE_DIAG 328 case HAL_ANI_PHYERR_RESET: 329 ahp->ah_stats.ast_ani_ofdmerrs = 0; 330 ahp->ah_stats.ast_ani_cckerrs = 0; 331 break; 332#endif /* AH_PRIVATE_DIAG */ 333 default: 334 HALDEBUG(ah, HAL_DEBUG_ANI, "%s: invalid cmd %u\n", 335 __func__, cmd); 336 return AH_FALSE; 337 } 338 return AH_TRUE; 339} 340 341static void 342ar5416AniOfdmErrTrigger(struct ath_hal *ah) 343{ 344 struct ath_hal_5212 *ahp = AH5212(ah); 345 const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; 346 struct ar5212AniState *aniState; 347 const struct ar5212AniParams *params; 348 349 HALASSERT(chan != AH_NULL); 350 351 if (!ANI_ENA(ah)) 352 return; 353 354 aniState = ahp->ah_curani; 355 params = aniState->params; 356 /* First, raise noise immunity level, up to max */ 357 if (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel) { 358 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 359 aniState->noiseImmunityLevel + 1); 360 return; 361 } 362 /* then, raise spur immunity level, up to max */ 363 if (aniState->spurImmunityLevel+1 < params->maxSpurImmunityLevel) { 364 ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 365 aniState->spurImmunityLevel + 1); 366 return; 367 } 368 369 if (ANI_ENA_RSSI(ah)) { 370 int32_t rssi = BEACON_RSSI(ahp); 371 if (rssi > params->rssiThrHigh) { 372 /* 373 * Beacon rssi is high, can turn off ofdm 374 * weak sig detect. 375 */ 376 if (!aniState->ofdmWeakSigDetectOff) { 377 ar5416AniControl(ah, 378 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 379 AH_FALSE); 380 ar5416AniControl(ah, 381 HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); 382 return; 383 } 384 /* 385 * If weak sig detect is already off, as last resort, 386 * raise firstep level 387 */ 388 if (aniState->firstepLevel+1 < params->maxFirstepLevel) { 389 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 390 aniState->firstepLevel + 1); 391 return; 392 } 393 } else if (rssi > params->rssiThrLow) { 394 /* 395 * Beacon rssi in mid range, need ofdm weak signal 396 * detect, but we can raise firststepLevel. 397 */ 398 if (aniState->ofdmWeakSigDetectOff) 399 ar5416AniControl(ah, 400 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 401 AH_TRUE); 402 if (aniState->firstepLevel+1 < params->maxFirstepLevel) 403 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 404 aniState->firstepLevel + 1); 405 return; 406 } else { 407 /* 408 * Beacon rssi is low, if in 11b/g mode, turn off ofdm 409 * weak signal detection and zero firstepLevel to 410 * maximize CCK sensitivity 411 */ 412 if (IEEE80211_IS_CHAN_CCK(chan)) { 413 if (!aniState->ofdmWeakSigDetectOff) 414 ar5416AniControl(ah, 415 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 416 AH_FALSE); 417 if (aniState->firstepLevel > 0) 418 ar5416AniControl(ah, 419 HAL_ANI_FIRSTEP_LEVEL, 0); 420 return; 421 } 422 } 423 } 424} 425 426static void 427ar5416AniCckErrTrigger(struct ath_hal *ah) 428{ 429 struct ath_hal_5212 *ahp = AH5212(ah); 430 const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; 431 struct ar5212AniState *aniState; 432 const struct ar5212AniParams *params; 433 434 HALASSERT(chan != AH_NULL); 435 436 if (!ANI_ENA(ah)) 437 return; 438 439 /* first, raise noise immunity level, up to max */ 440 aniState = ahp->ah_curani; 441 params = aniState->params; 442 if (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel) { 443 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 444 aniState->noiseImmunityLevel + 1); 445 return; 446 } 447 448 if (ANI_ENA_RSSI(ah)) { 449 int32_t rssi = BEACON_RSSI(ahp); 450 if (rssi > params->rssiThrLow) { 451 /* 452 * Beacon signal in mid and high range, 453 * raise firstep level. 454 */ 455 if (aniState->firstepLevel+1 < params->maxFirstepLevel) 456 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 457 aniState->firstepLevel + 1); 458 } else { 459 /* 460 * Beacon rssi is low, zero firstep level to maximize 461 * CCK sensitivity in 11b/g mode. 462 */ 463 if (IEEE80211_IS_CHAN_CCK(chan)) { 464 if (aniState->firstepLevel > 0) 465 ar5416AniControl(ah, 466 HAL_ANI_FIRSTEP_LEVEL, 0); 467 } 468 } 469 } 470} 471 472static void 473ar5416AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState) 474{ 475 struct ath_hal_5212 *ahp = AH5212(ah); 476 const struct ar5212AniParams *params = aniState->params; 477 478 aniState->listenTime = 0; 479 /* 480 * NB: these are written on reset based on the 481 * ini so we must re-write them! 482 */ 483 HALDEBUG(ah, HAL_DEBUG_ANI, 484 "%s: Writing ofdmbase=%u cckbase=%u\n", __func__, 485 params->ofdmPhyErrBase, params->cckPhyErrBase); 486 OS_REG_WRITE(ah, AR_PHY_ERR_1, params->ofdmPhyErrBase); 487 OS_REG_WRITE(ah, AR_PHY_ERR_2, params->cckPhyErrBase); 488 OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); 489 OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_CCK_TIMING); 490 491 /* Clear the mib counters and save them in the stats */ 492 ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 493 aniState->ofdmPhyErrCount = 0; 494 aniState->cckPhyErrCount = 0; 495} 496 497/* 498 * Restore/reset the ANI parameters and reset the statistics. 499 * This routine must be called for every channel change. 500 * 501 * NOTE: This is where ah_curani is set; other ani code assumes 502 * it is setup to reflect the current channel. 503 */ 504void 505ar5416AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan, 506 HAL_OPMODE opmode, int restore) 507{ 508 struct ath_hal_5212 *ahp = AH5212(ah); 509 HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); 510 /* XXX bounds check ic_devdata */ 511 struct ar5212AniState *aniState = &ahp->ah_ani[chan->ic_devdata]; 512 uint32_t rxfilter; 513 514 if ((ichan->privFlags & CHANNEL_ANI_INIT) == 0) { 515 OS_MEMZERO(aniState, sizeof(*aniState)); 516 if (IEEE80211_IS_CHAN_2GHZ(chan)) 517 aniState->params = &ahp->ah_aniParams24; 518 else 519 aniState->params = &ahp->ah_aniParams5; 520 ichan->privFlags |= CHANNEL_ANI_INIT; 521 HALASSERT((ichan->privFlags & CHANNEL_ANI_SETUP) == 0); 522 } 523 ahp->ah_curani = aniState; 524#if 0 525 ath_hal_printf(ah,"%s: chan %u/0x%x restore %d opmode %u%s\n", 526 __func__, chan->ic_freq, chan->ic_flags, restore, opmode, 527 ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); 528#else 529 HALDEBUG(ah, HAL_DEBUG_ANI, "%s: chan %u/0x%x restore %d opmode %u%s\n", 530 __func__, chan->ic_freq, chan->ic_flags, restore, opmode, 531 ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); 532#endif 533 OS_MARK(ah, AH_MARK_ANI_RESET, opmode); 534 535 /* 536 * Turn off PHY error frame delivery while we futz with settings. 537 */ 538 rxfilter = ar5212GetRxFilter(ah); 539 ar5212SetRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR); 540 /* 541 * Automatic processing is done only in station mode right now. 542 */ 543 if (opmode == HAL_M_STA) 544 ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA; 545 else 546 ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA; 547 /* 548 * Set all ani parameters. We either set them to initial 549 * values or restore the previous ones for the channel. 550 * XXX if ANI follows hardware, we don't care what mode we're 551 * XXX in, we should keep the ani parameters 552 */ 553 if (restore && (ichan->privFlags & CHANNEL_ANI_SETUP)) { 554 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 555 aniState->noiseImmunityLevel); 556 ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 557 aniState->spurImmunityLevel); 558 ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 559 !aniState->ofdmWeakSigDetectOff); 560 ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, 561 aniState->cckWeakSigThreshold); 562 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 563 aniState->firstepLevel); 564 } else { 565 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0); 566 ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); 567 ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 568 AH_TRUE); 569 ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE); 570 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); 571 ichan->privFlags |= CHANNEL_ANI_SETUP; 572 } 573 ar5416AniRestart(ah, aniState); 574 575 /* restore RX filter mask */ 576 ar5212SetRxFilter(ah, rxfilter); 577} 578 579/* 580 * Process a MIB interrupt. We may potentially be invoked because 581 * any of the MIB counters overflow/trigger so don't assume we're 582 * here because a PHY error counter triggered. 583 */ 584void 585ar5416ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats) 586{ 587 struct ath_hal_5212 *ahp = AH5212(ah); 588 uint32_t phyCnt1, phyCnt2; 589 590 HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x " 591 "filtofdm 0x%x filtcck 0x%x\n", 592 __func__, OS_REG_READ(ah, AR_MIBC), 593 OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2), 594 OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK)); 595 596 /* 597 * First order of business is to clear whatever caused 598 * the interrupt so we don't keep getting interrupted. 599 * We have the usual mib counters that are reset-on-read 600 * and the additional counters that appeared starting in 601 * Hainan. We collect the mib counters and explicitly 602 * zero additional counters we are not using. Anything 603 * else is reset only if it caused the interrupt. 604 */ 605 /* NB: these are not reset-on-read */ 606 phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1); 607 phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2); 608 /* not used, always reset them in case they are the cause */ 609 OS_REG_WRITE(ah, AR_FILTOFDM, 0); 610 OS_REG_WRITE(ah, AR_FILTCCK, 0); 611 if ((OS_REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING) == 0) 612 OS_REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR); 613 614 /* Clear the mib counters and save them in the stats */ 615 ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 616 ahp->ah_stats.ast_nodestats = *stats; 617 618 /* 619 * Check for an ani stat hitting the trigger threshold. 620 * When this happens we get a MIB interrupt and the top 621 * 2 bits of the counter register will be 0b11, hence 622 * the mask check of phyCnt?. 623 */ 624 if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || 625 ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { 626 struct ar5212AniState *aniState = ahp->ah_curani; 627 const struct ar5212AniParams *params = aniState->params; 628 uint32_t ofdmPhyErrCnt, cckPhyErrCnt; 629 630 ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; 631 ahp->ah_stats.ast_ani_ofdmerrs += 632 ofdmPhyErrCnt - aniState->ofdmPhyErrCount; 633 aniState->ofdmPhyErrCount = ofdmPhyErrCnt; 634 635 cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; 636 ahp->ah_stats.ast_ani_cckerrs += 637 cckPhyErrCnt - aniState->cckPhyErrCount; 638 aniState->cckPhyErrCount = cckPhyErrCnt; 639 640 /* 641 * NB: figure out which counter triggered. If both 642 * trigger we'll only deal with one as the processing 643 * clobbers the error counter so the trigger threshold 644 * check will never be true. 645 */ 646 if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh) 647 ar5416AniOfdmErrTrigger(ah); 648 if (aniState->cckPhyErrCount > params->cckTrigHigh) 649 ar5416AniCckErrTrigger(ah); 650 /* NB: always restart to insure the h/w counters are reset */ 651 ar5416AniRestart(ah, aniState); 652 } 653} 654 655static void 656ar5416AniLowerImmunity(struct ath_hal *ah) 657{ 658 struct ath_hal_5212 *ahp = AH5212(ah); 659 struct ar5212AniState *aniState; 660 const struct ar5212AniParams *params; 661 662 HALASSERT(ANI_ENA(ah)); 663 664 aniState = ahp->ah_curani; 665 params = aniState->params; 666 if (ANI_ENA_RSSI(ah)) { 667 int32_t rssi = BEACON_RSSI(ahp); 668 if (rssi > params->rssiThrHigh) { 669 /* 670 * Beacon signal is high, leave ofdm weak signal 671 * detection off or it may oscillate. Let it fall 672 * through. 673 */ 674 } else if (rssi > params->rssiThrLow) { 675 /* 676 * Beacon rssi in mid range, turn on ofdm weak signal 677 * detection or lower firstep level. 678 */ 679 if (aniState->ofdmWeakSigDetectOff) { 680 ar5416AniControl(ah, 681 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 682 AH_TRUE); 683 return; 684 } 685 if (aniState->firstepLevel > 0) { 686 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 687 aniState->firstepLevel - 1); 688 return; 689 } 690 } else { 691 /* 692 * Beacon rssi is low, reduce firstep level. 693 */ 694 if (aniState->firstepLevel > 0) { 695 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 696 aniState->firstepLevel - 1); 697 return; 698 } 699 } 700 } 701 /* then lower spur immunity level, down to zero */ 702 if (aniState->spurImmunityLevel > 0) { 703 ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 704 aniState->spurImmunityLevel - 1); 705 return; 706 } 707 /* 708 * if all else fails, lower noise immunity level down to a min value 709 * zero for now 710 */ 711 if (aniState->noiseImmunityLevel > 0) { 712 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 713 aniState->noiseImmunityLevel - 1); 714 return; 715 } 716} 717 718#define CLOCK_RATE 44000 /* XXX use mac_usec or similar */ 719/* convert HW counter values to ms using 11g clock rate, goo9d enough 720 for 11a and Turbo */ 721 722/* 723 * Return an approximation of the time spent ``listening'' by 724 * deducting the cycles spent tx'ing and rx'ing from the total 725 * cycle count since our last call. A return value <0 indicates 726 * an invalid/inconsistent time. 727 */ 728static int32_t 729ar5416AniGetListenTime(struct ath_hal *ah) 730{ 731 struct ath_hal_5212 *ahp = AH5212(ah); 732 struct ar5212AniState *aniState; 733 uint32_t txFrameCount, rxFrameCount, cycleCount; 734 int32_t listenTime; 735 736 txFrameCount = OS_REG_READ(ah, AR_TFCNT); 737 rxFrameCount = OS_REG_READ(ah, AR_RFCNT); 738 cycleCount = OS_REG_READ(ah, AR_CCCNT); 739 740 aniState = ahp->ah_curani; 741 if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) { 742 /* 743 * Cycle counter wrap (or initial call); it's not possible 744 * to accurately calculate a value because the registers 745 * right shift rather than wrap--so punt and return 0. 746 */ 747 listenTime = 0; 748 ahp->ah_stats.ast_ani_lzero++; 749 } else { 750 int32_t ccdelta = cycleCount - aniState->cycleCount; 751 int32_t rfdelta = rxFrameCount - aniState->rxFrameCount; 752 int32_t tfdelta = txFrameCount - aniState->txFrameCount; 753 listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE; 754 } 755 aniState->cycleCount = cycleCount; 756 aniState->txFrameCount = txFrameCount; 757 aniState->rxFrameCount = rxFrameCount; 758 return listenTime; 759} 760 761/* 762 * Update ani stats in preparation for listen time processing. 763 */ 764static void 765updateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState) 766{ 767 struct ath_hal_5212 *ahp = AH5212(ah); 768 const struct ar5212AniParams *params = aniState->params; 769 uint32_t phyCnt1, phyCnt2; 770 int32_t ofdmPhyErrCnt, cckPhyErrCnt; 771 772 /* Clear the mib counters and save them in the stats */ 773 ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 774 775 /* NB: these are not reset-on-read */ 776 phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1); 777 phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2); 778 779 /* NB: these are spec'd to never roll-over */ 780 ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; 781 if (ofdmPhyErrCnt < 0) { 782 HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n", 783 ofdmPhyErrCnt, phyCnt1); 784 ofdmPhyErrCnt = AR_PHY_COUNTMAX; 785 } 786 ahp->ah_stats.ast_ani_ofdmerrs += 787 ofdmPhyErrCnt - aniState->ofdmPhyErrCount; 788 aniState->ofdmPhyErrCount = ofdmPhyErrCnt; 789 790 cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; 791 if (cckPhyErrCnt < 0) { 792 HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n", 793 cckPhyErrCnt, phyCnt2); 794 cckPhyErrCnt = AR_PHY_COUNTMAX; 795 } 796 ahp->ah_stats.ast_ani_cckerrs += 797 cckPhyErrCnt - aniState->cckPhyErrCount; 798 aniState->cckPhyErrCount = cckPhyErrCnt; 799} 800 801void 802ar5416RxMonitor(struct ath_hal *ah, const HAL_NODE_STATS *stats, 803 const struct ieee80211_channel *chan) 804{ 805 struct ath_hal_5212 *ahp = AH5212(ah); 806 ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi; 807} 808 809/* 810 * Do periodic processing. This routine is called from the 811 * driver's rx interrupt handler after processing frames. 812 */ 813void 814ar5416AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan) 815{ 816 struct ath_hal_5212 *ahp = AH5212(ah); 817 struct ar5212AniState *aniState = ahp->ah_curani; 818 const struct ar5212AniParams *params; 819 int32_t listenTime; 820 821 /* XXX can aniState be null? */ 822 if (aniState == AH_NULL) 823 return; 824 if (!ANI_ENA(ah)) 825 return; 826 827 listenTime = ar5416AniGetListenTime(ah); 828 if (listenTime < 0) { 829 ahp->ah_stats.ast_ani_lneg++; 830 /* restart ANI period if listenTime is invalid */ 831 ar5416AniRestart(ah, aniState); 832 } 833 /* XXX beware of overflow? */ 834 aniState->listenTime += listenTime; 835 836 OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime); 837 838 params = aniState->params; 839 if (aniState->listenTime > 5*params->period) { 840 /* 841 * Check to see if need to lower immunity if 842 * 5 aniPeriods have passed 843 */ 844 updateMIBStats(ah, aniState); 845 if (aniState->ofdmPhyErrCount <= aniState->listenTime * 846 params->ofdmTrigLow/1000 && 847 aniState->cckPhyErrCount <= aniState->listenTime * 848 params->cckTrigLow/1000) 849 ar5416AniLowerImmunity(ah); 850 ar5416AniRestart(ah, aniState); 851 } else if (aniState->listenTime > params->period) { 852 updateMIBStats(ah, aniState); 853 /* check to see if need to raise immunity */ 854 if (aniState->ofdmPhyErrCount > aniState->listenTime * 855 params->ofdmTrigHigh / 1000) { 856 ar5416AniOfdmErrTrigger(ah); 857 ar5416AniRestart(ah, aniState); 858 } else if (aniState->cckPhyErrCount > aniState->listenTime * 859 params->cckTrigHigh / 1000) { 860 ar5416AniCckErrTrigger(ah); 861 ar5416AniRestart(ah, aniState); 862 } 863 } 864} 865