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