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