ar5416_ani.c revision 227377
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 227377 2011-11-09 05:39:17Z 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 if (ANI_ENA_RSSI(ah)) { 400 int32_t rssi = BEACON_RSSI(ahp); 401 if (rssi > params->rssiThrHigh) { 402 /* 403 * Beacon rssi is high, can turn off ofdm 404 * weak sig detect. 405 */ 406 if (!aniState->ofdmWeakSigDetectOff) { 407 ar5416AniControl(ah, 408 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 409 AH_FALSE); 410 ar5416AniControl(ah, 411 HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); 412 return; 413 } 414 /* 415 * If weak sig detect is already off, as last resort, 416 * raise firstep level 417 */ 418 if (aniState->firstepLevel+1 < params->maxFirstepLevel) { 419 if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 420 aniState->firstepLevel + 1)) 421 return; 422 } 423 } else if (rssi > params->rssiThrLow) { 424 /* 425 * Beacon rssi in mid range, need ofdm weak signal 426 * detect, but we can raise firststepLevel. 427 */ 428 if (aniState->ofdmWeakSigDetectOff) 429 ar5416AniControl(ah, 430 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 431 AH_TRUE); 432 if (aniState->firstepLevel+1 < params->maxFirstepLevel) 433 if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 434 aniState->firstepLevel + 1)) 435 return; 436 } else { 437 /* 438 * Beacon rssi is low, if in 11b/g mode, turn off ofdm 439 * weak signal detection and zero firstepLevel to 440 * maximize CCK sensitivity 441 */ 442 if (IEEE80211_IS_CHAN_CCK(chan)) { 443 if (!aniState->ofdmWeakSigDetectOff) 444 ar5416AniControl(ah, 445 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 446 AH_FALSE); 447 if (aniState->firstepLevel > 0) 448 if (ar5416AniControl(ah, 449 HAL_ANI_FIRSTEP_LEVEL, 0)) 450 return; 451 } 452 } 453 } 454} 455 456static void 457ar5416AniCckErrTrigger(struct ath_hal *ah) 458{ 459 struct ath_hal_5212 *ahp = AH5212(ah); 460 const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; 461 struct ar5212AniState *aniState; 462 const struct ar5212AniParams *params; 463 464 HALASSERT(chan != AH_NULL); 465 466 if (!ANI_ENA(ah)) 467 return; 468 469 /* first, raise noise immunity level, up to max */ 470 aniState = ahp->ah_curani; 471 params = aniState->params; 472 if ((AH5416(ah)->ah_ani_function & (1 << HAL_ANI_NOISE_IMMUNITY_LEVEL) && 473 aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel)) { 474 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 475 aniState->noiseImmunityLevel + 1); 476 return; 477 } 478 479 if (ANI_ENA_RSSI(ah)) { 480 int32_t rssi = BEACON_RSSI(ahp); 481 if (rssi > params->rssiThrLow) { 482 /* 483 * Beacon signal in mid and high range, 484 * raise firstep level. 485 */ 486 if (aniState->firstepLevel+1 < params->maxFirstepLevel) 487 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 488 aniState->firstepLevel + 1); 489 } else { 490 /* 491 * Beacon rssi is low, zero firstep level to maximize 492 * CCK sensitivity in 11b/g mode. 493 */ 494 if (IEEE80211_IS_CHAN_CCK(chan)) { 495 if (aniState->firstepLevel > 0) 496 ar5416AniControl(ah, 497 HAL_ANI_FIRSTEP_LEVEL, 0); 498 } 499 } 500 } 501} 502 503static void 504ar5416AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState) 505{ 506 struct ath_hal_5212 *ahp = AH5212(ah); 507 const struct ar5212AniParams *params = aniState->params; 508 509 aniState->listenTime = 0; 510 /* 511 * NB: these are written on reset based on the 512 * ini so we must re-write them! 513 */ 514 HALDEBUG(ah, HAL_DEBUG_ANI, 515 "%s: Writing ofdmbase=%u cckbase=%u\n", __func__, 516 params->ofdmPhyErrBase, params->cckPhyErrBase); 517 OS_REG_WRITE(ah, AR_PHY_ERR_1, params->ofdmPhyErrBase); 518 OS_REG_WRITE(ah, AR_PHY_ERR_2, params->cckPhyErrBase); 519 OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); 520 OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); 521 522 /* Clear the mib counters and save them in the stats */ 523 ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 524 aniState->ofdmPhyErrCount = 0; 525 aniState->cckPhyErrCount = 0; 526} 527 528/* 529 * Restore/reset the ANI parameters and reset the statistics. 530 * This routine must be called for every channel change. 531 * 532 * NOTE: This is where ah_curani is set; other ani code assumes 533 * it is setup to reflect the current channel. 534 */ 535void 536ar5416AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan, 537 HAL_OPMODE opmode, int restore) 538{ 539 struct ath_hal_5212 *ahp = AH5212(ah); 540 HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); 541 /* XXX bounds check ic_devdata */ 542 struct ar5212AniState *aniState = &ahp->ah_ani[chan->ic_devdata]; 543 uint32_t rxfilter; 544 545 if ((ichan->privFlags & CHANNEL_ANI_INIT) == 0) { 546 OS_MEMZERO(aniState, sizeof(*aniState)); 547 if (IEEE80211_IS_CHAN_2GHZ(chan)) 548 aniState->params = &ahp->ah_aniParams24; 549 else 550 aniState->params = &ahp->ah_aniParams5; 551 ichan->privFlags |= CHANNEL_ANI_INIT; 552 HALASSERT((ichan->privFlags & CHANNEL_ANI_SETUP) == 0); 553 } 554 ahp->ah_curani = aniState; 555#if 0 556 ath_hal_printf(ah,"%s: chan %u/0x%x restore %d opmode %u%s\n", 557 __func__, chan->ic_freq, chan->ic_flags, restore, opmode, 558 ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); 559#else 560 HALDEBUG(ah, HAL_DEBUG_ANI, "%s: chan %u/0x%x restore %d opmode %u%s\n", 561 __func__, chan->ic_freq, chan->ic_flags, restore, opmode, 562 ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); 563#endif 564 OS_MARK(ah, AH_MARK_ANI_RESET, opmode); 565 566 /* 567 * Turn off PHY error frame delivery while we futz with settings. 568 */ 569 rxfilter = ah->ah_getRxFilter(ah); 570 ah->ah_setRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR); 571 572 /* 573 * If ANI is disabled at this point, don't set the default 574 * ANI parameter settings - leave the HAL settings there. 575 * This is (currently) needed for reliable radar detection. 576 */ 577 if (! ANI_ENA(ah)) { 578 HALDEBUG(ah, HAL_DEBUG_ANI, "%s: ANI disabled\n", 579 __func__); 580 goto finish; 581 } 582 583 584 /* 585 * Automatic processing is done only in station mode right now. 586 */ 587 if (opmode == HAL_M_STA) 588 ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA; 589 else 590 ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA; 591 /* 592 * Set all ani parameters. We either set them to initial 593 * values or restore the previous ones for the channel. 594 * XXX if ANI follows hardware, we don't care what mode we're 595 * XXX in, we should keep the ani parameters 596 */ 597 if (restore && (ichan->privFlags & CHANNEL_ANI_SETUP)) { 598 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 599 aniState->noiseImmunityLevel); 600 ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 601 aniState->spurImmunityLevel); 602 ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 603 !aniState->ofdmWeakSigDetectOff); 604 ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, 605 aniState->cckWeakSigThreshold); 606 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 607 aniState->firstepLevel); 608 } else { 609 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0); 610 ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); 611 ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 612 AH_TRUE); 613 ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE); 614 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); 615 ichan->privFlags |= CHANNEL_ANI_SETUP; 616 } 617 618 /* 619 * In case the counters haven't yet been setup; set them up. 620 */ 621 enableAniMIBCounters(ah, aniState->params); 622 ar5416AniRestart(ah, aniState); 623 624finish: 625 /* restore RX filter mask */ 626 ah->ah_setRxFilter(ah, rxfilter); 627} 628 629/* 630 * Process a MIB interrupt. We may potentially be invoked because 631 * any of the MIB counters overflow/trigger so don't assume we're 632 * here because a PHY error counter triggered. 633 */ 634void 635ar5416ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats) 636{ 637 struct ath_hal_5212 *ahp = AH5212(ah); 638 uint32_t phyCnt1, phyCnt2; 639 640 HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x " 641 "filtofdm 0x%x filtcck 0x%x\n", 642 __func__, OS_REG_READ(ah, AR_MIBC), 643 OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2), 644 OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK)); 645 646 /* 647 * First order of business is to clear whatever caused 648 * the interrupt so we don't keep getting interrupted. 649 * We have the usual mib counters that are reset-on-read 650 * and the additional counters that appeared starting in 651 * Hainan. We collect the mib counters and explicitly 652 * zero additional counters we are not using. Anything 653 * else is reset only if it caused the interrupt. 654 */ 655 /* NB: these are not reset-on-read */ 656 phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1); 657 phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2); 658 /* not used, always reset them in case they are the cause */ 659 OS_REG_WRITE(ah, AR_FILTOFDM, 0); 660 OS_REG_WRITE(ah, AR_FILTCCK, 0); 661 if ((OS_REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING) == 0) 662 OS_REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR); 663 664 /* Clear the mib counters and save them in the stats */ 665 ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 666 ahp->ah_stats.ast_nodestats = *stats; 667 668 /* 669 * Check for an ani stat hitting the trigger threshold. 670 * When this happens we get a MIB interrupt and the top 671 * 2 bits of the counter register will be 0b11, hence 672 * the mask check of phyCnt?. 673 */ 674 if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || 675 ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { 676 struct ar5212AniState *aniState = ahp->ah_curani; 677 const struct ar5212AniParams *params = aniState->params; 678 uint32_t ofdmPhyErrCnt, cckPhyErrCnt; 679 680 ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; 681 ahp->ah_stats.ast_ani_ofdmerrs += 682 ofdmPhyErrCnt - aniState->ofdmPhyErrCount; 683 aniState->ofdmPhyErrCount = ofdmPhyErrCnt; 684 685 cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; 686 ahp->ah_stats.ast_ani_cckerrs += 687 cckPhyErrCnt - aniState->cckPhyErrCount; 688 aniState->cckPhyErrCount = cckPhyErrCnt; 689 690 /* 691 * NB: figure out which counter triggered. If both 692 * trigger we'll only deal with one as the processing 693 * clobbers the error counter so the trigger threshold 694 * check will never be true. 695 */ 696 if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh) 697 ar5416AniOfdmErrTrigger(ah); 698 if (aniState->cckPhyErrCount > params->cckTrigHigh) 699 ar5416AniCckErrTrigger(ah); 700 /* NB: always restart to insure the h/w counters are reset */ 701 ar5416AniRestart(ah, aniState); 702 } 703} 704 705static void 706ar5416AniLowerImmunity(struct ath_hal *ah) 707{ 708 struct ath_hal_5212 *ahp = AH5212(ah); 709 struct ar5212AniState *aniState; 710 const struct ar5212AniParams *params; 711 712 HALASSERT(ANI_ENA(ah)); 713 714 aniState = ahp->ah_curani; 715 params = aniState->params; 716 if (ANI_ENA_RSSI(ah)) { 717 int32_t rssi = BEACON_RSSI(ahp); 718 if (rssi > params->rssiThrHigh) { 719 /* 720 * Beacon signal is high, leave ofdm weak signal 721 * detection off or it may oscillate. Let it fall 722 * through. 723 */ 724 } else if (rssi > params->rssiThrLow) { 725 /* 726 * Beacon rssi in mid range, turn on ofdm weak signal 727 * detection or lower firstep level. 728 */ 729 if (aniState->ofdmWeakSigDetectOff) { 730 if (ar5416AniControl(ah, 731 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 732 AH_TRUE)) 733 return; 734 } 735 if (aniState->firstepLevel > 0) { 736 if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 737 aniState->firstepLevel - 1)) 738 return; 739 } 740 } else { 741 /* 742 * Beacon rssi is low, reduce firstep level. 743 */ 744 if (aniState->firstepLevel > 0) { 745 if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 746 aniState->firstepLevel - 1)) 747 return; 748 } 749 } 750 } 751 /* then lower spur immunity level, down to zero */ 752 if (aniState->spurImmunityLevel > 0) { 753 if (ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 754 aniState->spurImmunityLevel - 1)) 755 return; 756 } 757 /* 758 * if all else fails, lower noise immunity level down to a min value 759 * zero for now 760 */ 761 if (aniState->noiseImmunityLevel > 0) { 762 if (ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 763 aniState->noiseImmunityLevel - 1)) 764 return; 765 } 766} 767 768#define CLOCK_RATE 44000 /* XXX use mac_usec or similar */ 769/* convert HW counter values to ms using 11g clock rate, goo9d enough 770 for 11a and Turbo */ 771 772/* 773 * Return an approximation of the time spent ``listening'' by 774 * deducting the cycles spent tx'ing and rx'ing from the total 775 * cycle count since our last call. A return value <0 indicates 776 * an invalid/inconsistent time. 777 */ 778static int32_t 779ar5416AniGetListenTime(struct ath_hal *ah) 780{ 781 struct ath_hal_5212 *ahp = AH5212(ah); 782 struct ar5212AniState *aniState; 783 uint32_t txFrameCount, rxFrameCount, cycleCount; 784 int32_t listenTime; 785 786 txFrameCount = OS_REG_READ(ah, AR_TFCNT); 787 rxFrameCount = OS_REG_READ(ah, AR_RFCNT); 788 cycleCount = OS_REG_READ(ah, AR_CCCNT); 789 790 aniState = ahp->ah_curani; 791 if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) { 792 /* 793 * Cycle counter wrap (or initial call); it's not possible 794 * to accurately calculate a value because the registers 795 * right shift rather than wrap--so punt and return 0. 796 */ 797 listenTime = 0; 798 ahp->ah_stats.ast_ani_lzero++; 799 } else { 800 int32_t ccdelta = cycleCount - aniState->cycleCount; 801 int32_t rfdelta = rxFrameCount - aniState->rxFrameCount; 802 int32_t tfdelta = txFrameCount - aniState->txFrameCount; 803 listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE; 804 } 805 aniState->cycleCount = cycleCount; 806 aniState->txFrameCount = txFrameCount; 807 aniState->rxFrameCount = rxFrameCount; 808 return listenTime; 809} 810 811/* 812 * Update ani stats in preparation for listen time processing. 813 */ 814static void 815updateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState) 816{ 817 struct ath_hal_5212 *ahp = AH5212(ah); 818 const struct ar5212AniParams *params = aniState->params; 819 uint32_t phyCnt1, phyCnt2; 820 int32_t ofdmPhyErrCnt, cckPhyErrCnt; 821 822 /* Clear the mib counters and save them in the stats */ 823 ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 824 825 /* NB: these are not reset-on-read */ 826 phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1); 827 phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2); 828 829 /* NB: these are spec'd to never roll-over */ 830 ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; 831 if (ofdmPhyErrCnt < 0) { 832 HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n", 833 ofdmPhyErrCnt, phyCnt1); 834 ofdmPhyErrCnt = AR_PHY_COUNTMAX; 835 } 836 ahp->ah_stats.ast_ani_ofdmerrs += 837 ofdmPhyErrCnt - aniState->ofdmPhyErrCount; 838 aniState->ofdmPhyErrCount = ofdmPhyErrCnt; 839 840 cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; 841 if (cckPhyErrCnt < 0) { 842 HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n", 843 cckPhyErrCnt, phyCnt2); 844 cckPhyErrCnt = AR_PHY_COUNTMAX; 845 } 846 ahp->ah_stats.ast_ani_cckerrs += 847 cckPhyErrCnt - aniState->cckPhyErrCount; 848 aniState->cckPhyErrCount = cckPhyErrCnt; 849} 850 851void 852ar5416RxMonitor(struct ath_hal *ah, const HAL_NODE_STATS *stats, 853 const struct ieee80211_channel *chan) 854{ 855 struct ath_hal_5212 *ahp = AH5212(ah); 856 ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi; 857} 858 859/* 860 * Do periodic processing. This routine is called from the 861 * driver's rx interrupt handler after processing frames. 862 */ 863void 864ar5416AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan) 865{ 866 struct ath_hal_5212 *ahp = AH5212(ah); 867 struct ar5212AniState *aniState = ahp->ah_curani; 868 const struct ar5212AniParams *params; 869 int32_t listenTime; 870 871 /* XXX can aniState be null? */ 872 if (aniState == AH_NULL) 873 return; 874 if (!ANI_ENA(ah)) 875 return; 876 877 listenTime = ar5416AniGetListenTime(ah); 878 if (listenTime < 0) { 879 ahp->ah_stats.ast_ani_lneg++; 880 /* restart ANI period if listenTime is invalid */ 881 ar5416AniRestart(ah, aniState); 882 } 883 /* XXX beware of overflow? */ 884 aniState->listenTime += listenTime; 885 886 OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime); 887 888 params = aniState->params; 889 if (aniState->listenTime > 5*params->period) { 890 /* 891 * Check to see if need to lower immunity if 892 * 5 aniPeriods have passed 893 */ 894 updateMIBStats(ah, aniState); 895 if (aniState->ofdmPhyErrCount <= aniState->listenTime * 896 params->ofdmTrigLow/1000 && 897 aniState->cckPhyErrCount <= aniState->listenTime * 898 params->cckTrigLow/1000) 899 ar5416AniLowerImmunity(ah); 900 ar5416AniRestart(ah, aniState); 901 } else if (aniState->listenTime > params->period) { 902 updateMIBStats(ah, aniState); 903 /* check to see if need to raise immunity */ 904 if (aniState->ofdmPhyErrCount > aniState->listenTime * 905 params->ofdmTrigHigh / 1000) { 906 HALDEBUG(ah, HAL_DEBUG_ANI, 907 "%s: OFDM err %u listenTime %u\n", __func__, 908 aniState->ofdmPhyErrCount, aniState->listenTime); 909 ar5416AniOfdmErrTrigger(ah); 910 ar5416AniRestart(ah, aniState); 911 } else if (aniState->cckPhyErrCount > aniState->listenTime * 912 params->cckTrigHigh / 1000) { 913 HALDEBUG(ah, HAL_DEBUG_ANI, 914 "%s: CCK err %u listenTime %u\n", __func__, 915 aniState->ofdmPhyErrCount, aniState->listenTime); 916 ar5416AniCckErrTrigger(ah); 917 ar5416AniRestart(ah, aniState); 918 } 919 } 920} 921