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