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