ar5416_ani.c revision 218763
1193323Sed/* 2193323Sed * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting 3193323Sed * Copyright (c) 2002-2008 Atheros Communications, Inc. 4193323Sed * 5193323Sed * Permission to use, copy, modify, and/or distribute this software for any 6193323Sed * purpose with or without fee is hereby granted, provided that the above 7193323Sed * copyright notice and this permission notice appear in all copies. 8193323Sed * 9193323Sed * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10193323Sed * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11193323Sed * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12195340Sed * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13193323Sed * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14193323Sed * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15193323Sed * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16193323Sed * 17193323Sed * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c 218763 2011-02-17 05:52:53Z adrian $ 18193323Sed */ 19193323Sed#include "opt_ah.h" 20193323Sed 21193323Sed/* 22193323Sed * XXX this is virtually the same code as for 5212; we reuse 23193323Sed * storage in the 5212 state block; need to refactor. 24193323Sed */ 25193323Sed#include "ah.h" 26195340Sed#include "ah_internal.h" 27195340Sed#include "ah_desc.h" 28193323Sed 29193323Sed#include "ar5416/ar5416.h" 30193323Sed#include "ar5416/ar5416reg.h" 31193323Sed#include "ar5416/ar5416phy.h" 32193323Sed 33193323Sed/* 34193323Sed * Anti noise immunity support. We track phy errors and react 35193323Sed * to excessive errors by adjusting the noise immunity parameters. 36193323Sed */ 37195340Sed 38195340Sed#define HAL_EP_RND(x, mul) \ 39195340Sed ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) 40195340Sed#define BEACON_RSSI(ahp) \ 41195340Sed HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \ 42195340Sed HAL_RSSI_EP_MULTIPLIER) 43195340Sed 44195340Sed/* 45195340Sed * ANI processing tunes radio parameters according to PHY errors 46195340Sed * and related information. This is done for for noise and spur 47195340Sed * immunity in all operating modes if the device indicates it's 48195340Sed * capable at attach time. In addition, when there is a reference 49195340Sed * rssi value (e.g. beacon frames from an ap in station mode) 50195340Sed * further tuning is done. 51195340Sed * 52195340Sed * ANI_ENA indicates whether any ANI processing should be done; 53193323Sed * this is specified at attach time. 54193323Sed * 55193323Sed * ANI_ENA_RSSI indicates whether rssi-based processing should 56193323Sed * done, this is enabled based on operating mode and is meaningful 57193323Sed * only if ANI_ENA is true. 58195340Sed * 59193323Sed * ANI parameters are typically controlled only by the hal. The 60195340Sed * AniControl interface however permits manual tuning through the 61195340Sed * diagnostic api. 62195340Sed */ 63195340Sed#define ANI_ENA(ah) \ 64195340Sed (AH5212(ah)->ah_procPhyErr & HAL_ANI_ENA) 65195340Sed#define ANI_ENA_RSSI(ah) \ 66195340Sed (AH5212(ah)->ah_procPhyErr & HAL_RSSI_ANI_ENA) 67195340Sed 68195340Sed#define ah_mibStats ah_stats.ast_mibstats 69195340Sed 70195340Sedstatic void 71195340SedenableAniMIBCounters(struct ath_hal *ah, const struct ar5212AniParams *params) 72195340Sed{ 73195340Sed struct ath_hal_5212 *ahp = AH5212(ah); 74195340Sed 75195340Sed HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Enable mib counters: " 76195340Sed "OfdmPhyErrBase 0x%x cckPhyErrBase 0x%x\n", 77195340Sed __func__, params->ofdmPhyErrBase, params->cckPhyErrBase); 78193323Sed 79195340Sed OS_REG_WRITE(ah, AR_FILTOFDM, 0); 80195340Sed OS_REG_WRITE(ah, AR_FILTCCK, 0); 81193323Sed 82193323Sed OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase); 83193323Sed OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase); 84193323Sed OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); 85193323Sed OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); 86193323Sed 87193323Sed ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save+clear counters*/ 88193323Sed 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 & AH5416(ah)->ah_ani_function) { 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 ((AH5416(ah)->ah_ani_function & HAL_ANI_NOISE_IMMUNITY_LEVEL) && 358 (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel)) { 359 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 360 aniState->noiseImmunityLevel + 1); 361 return; 362 } 363 /* then, raise spur immunity level, up to max */ 364 if ((AH5416(ah)->ah_ani_function & HAL_ANI_SPUR_IMMUNITY_LEVEL) && 365 (aniState->spurImmunityLevel+1 < params->maxSpurImmunityLevel)) { 366 ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 367 aniState->spurImmunityLevel + 1); 368 return; 369 } 370 371 if (ANI_ENA_RSSI(ah)) { 372 int32_t rssi = BEACON_RSSI(ahp); 373 if (rssi > params->rssiThrHigh) { 374 /* 375 * Beacon rssi is high, can turn off ofdm 376 * weak sig detect. 377 */ 378 if (!aniState->ofdmWeakSigDetectOff) { 379 ar5416AniControl(ah, 380 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 381 AH_FALSE); 382 ar5416AniControl(ah, 383 HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); 384 return; 385 } 386 /* 387 * If weak sig detect is already off, as last resort, 388 * raise firstep level 389 */ 390 if (aniState->firstepLevel+1 < params->maxFirstepLevel) { 391 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 392 aniState->firstepLevel + 1); 393 return; 394 } 395 } else if (rssi > params->rssiThrLow) { 396 /* 397 * Beacon rssi in mid range, need ofdm weak signal 398 * detect, but we can raise firststepLevel. 399 */ 400 if (aniState->ofdmWeakSigDetectOff) 401 ar5416AniControl(ah, 402 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 403 AH_TRUE); 404 if (aniState->firstepLevel+1 < params->maxFirstepLevel) 405 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 406 aniState->firstepLevel + 1); 407 return; 408 } else { 409 /* 410 * Beacon rssi is low, if in 11b/g mode, turn off ofdm 411 * weak signal detection and zero firstepLevel to 412 * maximize CCK sensitivity 413 */ 414 if (IEEE80211_IS_CHAN_CCK(chan)) { 415 if (!aniState->ofdmWeakSigDetectOff) 416 ar5416AniControl(ah, 417 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 418 AH_FALSE); 419 if (aniState->firstepLevel > 0) 420 ar5416AniControl(ah, 421 HAL_ANI_FIRSTEP_LEVEL, 0); 422 return; 423 } 424 } 425 } 426} 427 428static void 429ar5416AniCckErrTrigger(struct ath_hal *ah) 430{ 431 struct ath_hal_5212 *ahp = AH5212(ah); 432 const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; 433 struct ar5212AniState *aniState; 434 const struct ar5212AniParams *params; 435 436 HALASSERT(chan != AH_NULL); 437 438 if (!ANI_ENA(ah)) 439 return; 440 441 /* first, raise noise immunity level, up to max */ 442 aniState = ahp->ah_curani; 443 params = aniState->params; 444 if (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel) { 445 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 446 aniState->noiseImmunityLevel + 1); 447 return; 448 } 449 450 if (ANI_ENA_RSSI(ah)) { 451 int32_t rssi = BEACON_RSSI(ahp); 452 if (rssi > params->rssiThrLow) { 453 /* 454 * Beacon signal in mid and high range, 455 * raise firstep level. 456 */ 457 if (aniState->firstepLevel+1 < params->maxFirstepLevel) 458 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 459 aniState->firstepLevel + 1); 460 } else { 461 /* 462 * Beacon rssi is low, zero firstep level to maximize 463 * CCK sensitivity in 11b/g mode. 464 */ 465 if (IEEE80211_IS_CHAN_CCK(chan)) { 466 if (aniState->firstepLevel > 0) 467 ar5416AniControl(ah, 468 HAL_ANI_FIRSTEP_LEVEL, 0); 469 } 470 } 471 } 472} 473 474static void 475ar5416AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState) 476{ 477 struct ath_hal_5212 *ahp = AH5212(ah); 478 const struct ar5212AniParams *params = aniState->params; 479 480 aniState->listenTime = 0; 481 /* 482 * NB: these are written on reset based on the 483 * ini so we must re-write them! 484 */ 485 HALDEBUG(ah, HAL_DEBUG_ANI, 486 "%s: Writing ofdmbase=%u cckbase=%u\n", __func__, 487 params->ofdmPhyErrBase, params->cckPhyErrBase); 488 OS_REG_WRITE(ah, AR_PHY_ERR_1, params->ofdmPhyErrBase); 489 OS_REG_WRITE(ah, AR_PHY_ERR_2, params->cckPhyErrBase); 490 OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); 491 OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_CCK_TIMING); 492 493 /* Clear the mib counters and save them in the stats */ 494 ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 495 aniState->ofdmPhyErrCount = 0; 496 aniState->cckPhyErrCount = 0; 497} 498 499/* 500 * Restore/reset the ANI parameters and reset the statistics. 501 * This routine must be called for every channel change. 502 * 503 * NOTE: This is where ah_curani is set; other ani code assumes 504 * it is setup to reflect the current channel. 505 */ 506void 507ar5416AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan, 508 HAL_OPMODE opmode, int restore) 509{ 510 struct ath_hal_5212 *ahp = AH5212(ah); 511 HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); 512 /* XXX bounds check ic_devdata */ 513 struct ar5212AniState *aniState = &ahp->ah_ani[chan->ic_devdata]; 514 uint32_t rxfilter; 515 516 if ((ichan->privFlags & CHANNEL_ANI_INIT) == 0) { 517 OS_MEMZERO(aniState, sizeof(*aniState)); 518 if (IEEE80211_IS_CHAN_2GHZ(chan)) 519 aniState->params = &ahp->ah_aniParams24; 520 else 521 aniState->params = &ahp->ah_aniParams5; 522 ichan->privFlags |= CHANNEL_ANI_INIT; 523 HALASSERT((ichan->privFlags & CHANNEL_ANI_SETUP) == 0); 524 } 525 ahp->ah_curani = aniState; 526#if 0 527 ath_hal_printf(ah,"%s: chan %u/0x%x restore %d opmode %u%s\n", 528 __func__, chan->ic_freq, chan->ic_flags, restore, opmode, 529 ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); 530#else 531 HALDEBUG(ah, HAL_DEBUG_ANI, "%s: chan %u/0x%x restore %d opmode %u%s\n", 532 __func__, chan->ic_freq, chan->ic_flags, restore, opmode, 533 ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); 534#endif 535 OS_MARK(ah, AH_MARK_ANI_RESET, opmode); 536 537 /* 538 * Turn off PHY error frame delivery while we futz with settings. 539 */ 540 rxfilter = ar5212GetRxFilter(ah); 541 ar5212SetRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR); 542 /* 543 * Automatic processing is done only in station mode right now. 544 */ 545 if (opmode == HAL_M_STA) 546 ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA; 547 else 548 ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA; 549 /* 550 * Set all ani parameters. We either set them to initial 551 * values or restore the previous ones for the channel. 552 * XXX if ANI follows hardware, we don't care what mode we're 553 * XXX in, we should keep the ani parameters 554 */ 555 if (restore && (ichan->privFlags & CHANNEL_ANI_SETUP)) { 556 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 557 aniState->noiseImmunityLevel); 558 ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 559 aniState->spurImmunityLevel); 560 ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 561 !aniState->ofdmWeakSigDetectOff); 562 ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, 563 aniState->cckWeakSigThreshold); 564 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 565 aniState->firstepLevel); 566 } else { 567 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0); 568 ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); 569 ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 570 AH_TRUE); 571 ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE); 572 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); 573 ichan->privFlags |= CHANNEL_ANI_SETUP; 574 } 575 ar5416AniRestart(ah, aniState); 576 577 /* restore RX filter mask */ 578 ar5212SetRxFilter(ah, rxfilter); 579} 580 581/* 582 * Process a MIB interrupt. We may potentially be invoked because 583 * any of the MIB counters overflow/trigger so don't assume we're 584 * here because a PHY error counter triggered. 585 */ 586void 587ar5416ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats) 588{ 589 struct ath_hal_5212 *ahp = AH5212(ah); 590 uint32_t phyCnt1, phyCnt2; 591 592 HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x " 593 "filtofdm 0x%x filtcck 0x%x\n", 594 __func__, OS_REG_READ(ah, AR_MIBC), 595 OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2), 596 OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK)); 597 598 /* 599 * First order of business is to clear whatever caused 600 * the interrupt so we don't keep getting interrupted. 601 * We have the usual mib counters that are reset-on-read 602 * and the additional counters that appeared starting in 603 * Hainan. We collect the mib counters and explicitly 604 * zero additional counters we are not using. Anything 605 * else is reset only if it caused the interrupt. 606 */ 607 /* NB: these are not reset-on-read */ 608 phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1); 609 phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2); 610 /* not used, always reset them in case they are the cause */ 611 OS_REG_WRITE(ah, AR_FILTOFDM, 0); 612 OS_REG_WRITE(ah, AR_FILTCCK, 0); 613 if ((OS_REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING) == 0) 614 OS_REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR); 615 616 /* Clear the mib counters and save them in the stats */ 617 ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 618 ahp->ah_stats.ast_nodestats = *stats; 619 620 /* 621 * Check for an ani stat hitting the trigger threshold. 622 * When this happens we get a MIB interrupt and the top 623 * 2 bits of the counter register will be 0b11, hence 624 * the mask check of phyCnt?. 625 */ 626 if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || 627 ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { 628 struct ar5212AniState *aniState = ahp->ah_curani; 629 const struct ar5212AniParams *params = aniState->params; 630 uint32_t ofdmPhyErrCnt, cckPhyErrCnt; 631 632 ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; 633 ahp->ah_stats.ast_ani_ofdmerrs += 634 ofdmPhyErrCnt - aniState->ofdmPhyErrCount; 635 aniState->ofdmPhyErrCount = ofdmPhyErrCnt; 636 637 cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; 638 ahp->ah_stats.ast_ani_cckerrs += 639 cckPhyErrCnt - aniState->cckPhyErrCount; 640 aniState->cckPhyErrCount = cckPhyErrCnt; 641 642 /* 643 * NB: figure out which counter triggered. If both 644 * trigger we'll only deal with one as the processing 645 * clobbers the error counter so the trigger threshold 646 * check will never be true. 647 */ 648 if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh) 649 ar5416AniOfdmErrTrigger(ah); 650 if (aniState->cckPhyErrCount > params->cckTrigHigh) 651 ar5416AniCckErrTrigger(ah); 652 /* NB: always restart to insure the h/w counters are reset */ 653 ar5416AniRestart(ah, aniState); 654 } 655} 656 657static void 658ar5416AniLowerImmunity(struct ath_hal *ah) 659{ 660 struct ath_hal_5212 *ahp = AH5212(ah); 661 struct ar5212AniState *aniState; 662 const struct ar5212AniParams *params; 663 664 HALASSERT(ANI_ENA(ah)); 665 666 aniState = ahp->ah_curani; 667 params = aniState->params; 668 if (ANI_ENA_RSSI(ah)) { 669 int32_t rssi = BEACON_RSSI(ahp); 670 if (rssi > params->rssiThrHigh) { 671 /* 672 * Beacon signal is high, leave ofdm weak signal 673 * detection off or it may oscillate. Let it fall 674 * through. 675 */ 676 } else if (rssi > params->rssiThrLow) { 677 /* 678 * Beacon rssi in mid range, turn on ofdm weak signal 679 * detection or lower firstep level. 680 */ 681 if (aniState->ofdmWeakSigDetectOff) { 682 ar5416AniControl(ah, 683 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 684 AH_TRUE); 685 return; 686 } 687 if (aniState->firstepLevel > 0) { 688 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 689 aniState->firstepLevel - 1); 690 return; 691 } 692 } else { 693 /* 694 * Beacon rssi is low, reduce firstep level. 695 */ 696 if (aniState->firstepLevel > 0) { 697 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 698 aniState->firstepLevel - 1); 699 return; 700 } 701 } 702 } 703 /* then lower spur immunity level, down to zero */ 704 if (aniState->spurImmunityLevel > 0) { 705 ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 706 aniState->spurImmunityLevel - 1); 707 return; 708 } 709 /* 710 * if all else fails, lower noise immunity level down to a min value 711 * zero for now 712 */ 713 if (aniState->noiseImmunityLevel > 0) { 714 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 715 aniState->noiseImmunityLevel - 1); 716 return; 717 } 718} 719 720#define CLOCK_RATE 44000 /* XXX use mac_usec or similar */ 721/* convert HW counter values to ms using 11g clock rate, goo9d enough 722 for 11a and Turbo */ 723 724/* 725 * Return an approximation of the time spent ``listening'' by 726 * deducting the cycles spent tx'ing and rx'ing from the total 727 * cycle count since our last call. A return value <0 indicates 728 * an invalid/inconsistent time. 729 */ 730static int32_t 731ar5416AniGetListenTime(struct ath_hal *ah) 732{ 733 struct ath_hal_5212 *ahp = AH5212(ah); 734 struct ar5212AniState *aniState; 735 uint32_t txFrameCount, rxFrameCount, cycleCount; 736 int32_t listenTime; 737 738 txFrameCount = OS_REG_READ(ah, AR_TFCNT); 739 rxFrameCount = OS_REG_READ(ah, AR_RFCNT); 740 cycleCount = OS_REG_READ(ah, AR_CCCNT); 741 742 aniState = ahp->ah_curani; 743 if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) { 744 /* 745 * Cycle counter wrap (or initial call); it's not possible 746 * to accurately calculate a value because the registers 747 * right shift rather than wrap--so punt and return 0. 748 */ 749 listenTime = 0; 750 ahp->ah_stats.ast_ani_lzero++; 751 } else { 752 int32_t ccdelta = cycleCount - aniState->cycleCount; 753 int32_t rfdelta = rxFrameCount - aniState->rxFrameCount; 754 int32_t tfdelta = txFrameCount - aniState->txFrameCount; 755 listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE; 756 } 757 aniState->cycleCount = cycleCount; 758 aniState->txFrameCount = txFrameCount; 759 aniState->rxFrameCount = rxFrameCount; 760 return listenTime; 761} 762 763/* 764 * Update ani stats in preparation for listen time processing. 765 */ 766static void 767updateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState) 768{ 769 struct ath_hal_5212 *ahp = AH5212(ah); 770 const struct ar5212AniParams *params = aniState->params; 771 uint32_t phyCnt1, phyCnt2; 772 int32_t ofdmPhyErrCnt, cckPhyErrCnt; 773 774 /* Clear the mib counters and save them in the stats */ 775 ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 776 777 /* NB: these are not reset-on-read */ 778 phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1); 779 phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2); 780 781 /* NB: these are spec'd to never roll-over */ 782 ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; 783 if (ofdmPhyErrCnt < 0) { 784 HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n", 785 ofdmPhyErrCnt, phyCnt1); 786 ofdmPhyErrCnt = AR_PHY_COUNTMAX; 787 } 788 ahp->ah_stats.ast_ani_ofdmerrs += 789 ofdmPhyErrCnt - aniState->ofdmPhyErrCount; 790 aniState->ofdmPhyErrCount = ofdmPhyErrCnt; 791 792 cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; 793 if (cckPhyErrCnt < 0) { 794 HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n", 795 cckPhyErrCnt, phyCnt2); 796 cckPhyErrCnt = AR_PHY_COUNTMAX; 797 } 798 ahp->ah_stats.ast_ani_cckerrs += 799 cckPhyErrCnt - aniState->cckPhyErrCount; 800 aniState->cckPhyErrCount = cckPhyErrCnt; 801} 802 803void 804ar5416RxMonitor(struct ath_hal *ah, const HAL_NODE_STATS *stats, 805 const struct ieee80211_channel *chan) 806{ 807 struct ath_hal_5212 *ahp = AH5212(ah); 808 ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi; 809} 810 811/* 812 * Do periodic processing. This routine is called from the 813 * driver's rx interrupt handler after processing frames. 814 */ 815void 816ar5416AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan) 817{ 818 struct ath_hal_5212 *ahp = AH5212(ah); 819 struct ar5212AniState *aniState = ahp->ah_curani; 820 const struct ar5212AniParams *params; 821 int32_t listenTime; 822 823 /* XXX can aniState be null? */ 824 if (aniState == AH_NULL) 825 return; 826 if (!ANI_ENA(ah)) 827 return; 828 829 listenTime = ar5416AniGetListenTime(ah); 830 if (listenTime < 0) { 831 ahp->ah_stats.ast_ani_lneg++; 832 /* restart ANI period if listenTime is invalid */ 833 ar5416AniRestart(ah, aniState); 834 } 835 /* XXX beware of overflow? */ 836 aniState->listenTime += listenTime; 837 838 OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime); 839 840 params = aniState->params; 841 if (aniState->listenTime > 5*params->period) { 842 /* 843 * Check to see if need to lower immunity if 844 * 5 aniPeriods have passed 845 */ 846 updateMIBStats(ah, aniState); 847 if (aniState->ofdmPhyErrCount <= aniState->listenTime * 848 params->ofdmTrigLow/1000 && 849 aniState->cckPhyErrCount <= aniState->listenTime * 850 params->cckTrigLow/1000) 851 ar5416AniLowerImmunity(ah); 852 ar5416AniRestart(ah, aniState); 853 } else if (aniState->listenTime > params->period) { 854 updateMIBStats(ah, aniState); 855 /* check to see if need to raise immunity */ 856 if (aniState->ofdmPhyErrCount > aniState->listenTime * 857 params->ofdmTrigHigh / 1000) { 858 ar5416AniOfdmErrTrigger(ah); 859 ar5416AniRestart(ah, aniState); 860 } else if (aniState->cckPhyErrCount > aniState->listenTime * 861 params->cckTrigHigh / 1000) { 862 ar5416AniCckErrTrigger(ah); 863 ar5416AniRestart(ah, aniState); 864 } 865 } 866} 867