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