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