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