1/* 2 * Copyright (c) 2002-2008 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 * $Id: ar5416_ani.c,v 1.1 2008/11/11 20:46:06 sam Exp $ 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 105/* 106 * This routine returns the index into the aniState array that 107 * corresponds to the channel in *chan. If no match is found and the 108 * array is still not fully utilized, a new entry is created for the 109 * channel. We assume the attach function has already initialized the 110 * ah_ani values and only the channel field needs to be set. 111 */ 112static int 113ar5416GetAniChannelIndex(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan) 114{ 115#define N(a) (sizeof(a) / sizeof(a[0])) 116 struct ath_hal_5212 *ahp = AH5212(ah); 117 int i; 118 119 for (i = 0; i < N(ahp->ah_ani); i++) { 120 struct ar5212AniState *asp = &ahp->ah_ani[i]; 121 if (asp->c.channel == chan->channel) 122 return i; 123 if (asp->c.channel == 0) { 124 asp->c.channel = chan->channel; 125 asp->c.channelFlags = chan->channelFlags; 126 asp->c.privFlags = chan->privFlags; 127 asp->isSetup = AH_FALSE; 128 if (IS_CHAN_2GHZ(chan)) 129 asp->params = &ahp->ah_aniParams24; 130 else 131 asp->params = &ahp->ah_aniParams5; 132 return i; 133 } 134 } 135 /* XXX statistic */ 136 HALDEBUG(ah, HAL_DEBUG_ANY, 137 "No more channel states left. Using channel 0\n"); 138 return 0; /* XXX gotta return something valid */ 139#undef N 140} 141 142static void 143setPhyErrBase(struct ath_hal *ah, struct ar5212AniParams *params) 144{ 145 if (params->ofdmTrigHigh >= AR_PHY_COUNTMAX) { 146 HALDEBUG(ah, HAL_DEBUG_ANY, 147 "OFDM Trigger %d is too high for hw counters, using max\n", 148 params->ofdmTrigHigh); 149 params->ofdmPhyErrBase = 0; 150 } else 151 params->ofdmPhyErrBase = AR_PHY_COUNTMAX - params->ofdmTrigHigh; 152 if (params->cckTrigHigh >= AR_PHY_COUNTMAX) { 153 HALDEBUG(ah, HAL_DEBUG_ANY, 154 "CCK Trigger %d is too high for hw counters, using max\n", 155 params->cckTrigHigh); 156 params->cckPhyErrBase = 0; 157 } else 158 params->cckPhyErrBase = AR_PHY_COUNTMAX - params->cckTrigHigh; 159} 160 161/* 162 * Setup ANI handling. Sets all thresholds and reset the 163 * channel statistics. Note that ar5416AniReset should be 164 * called by ar5416Reset before anything else happens and 165 * that's where we force initial settings. 166 */ 167void 168ar5416AniAttach(struct ath_hal *ah, const struct ar5212AniParams *params24, 169 const struct ar5212AniParams *params5, HAL_BOOL enable) 170{ 171 struct ath_hal_5212 *ahp = AH5212(ah); 172 173 if (params24 != AH_NULL) { 174 OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24)); 175 setPhyErrBase(ah, &ahp->ah_aniParams24); 176 } 177 if (params5 != AH_NULL) { 178 OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5)); 179 setPhyErrBase(ah, &ahp->ah_aniParams5); 180 } 181 182 OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani)); 183 /* Enable MIB Counters */ 184 enableAniMIBCounters(ah, &ahp->ah_aniParams24 /*XXX*/); 185 186 if (enable) { /* Enable ani now */ 187 HALASSERT(params24 != AH_NULL && params5 != AH_NULL); 188 ahp->ah_procPhyErr |= HAL_ANI_ENA; 189 } else { 190 ahp->ah_procPhyErr &= ~HAL_ANI_ENA; 191 } 192} 193 194/* 195 * Cleanup any ANI state setup. 196 */ 197void 198ar5416AniDetach(struct ath_hal *ah) 199{ 200 HALDEBUG(ah, HAL_DEBUG_ANI, "Detaching Ani\n"); 201 disableAniMIBCounters(ah); 202} 203 204/* 205 * Control Adaptive Noise Immunity Parameters 206 */ 207HAL_BOOL 208ar5416AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param) 209{ 210 typedef int TABLE[]; 211 struct ath_hal_5212 *ahp = AH5212(ah); 212 struct ar5212AniState *aniState = ahp->ah_curani; 213 const struct ar5212AniParams *params = aniState->params; 214 215 OS_MARK(ah, AH_MARK_ANI_CONTROL, cmd); 216 217 switch (cmd) { 218 case HAL_ANI_NOISE_IMMUNITY_LEVEL: { 219 u_int level = param; 220 221 if (level >= params->maxNoiseImmunityLevel) { 222 HALDEBUG(ah, HAL_DEBUG_ANY, 223 "%s: level out of range (%u > %u)\n", 224 __func__, level, params->maxNoiseImmunityLevel); 225 return AH_FALSE; 226 } 227 228 OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, 229 AR_PHY_DESIRED_SZ_TOT_DES, params->totalSizeDesired[level]); 230 OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, 231 AR_PHY_AGC_CTL1_COARSE_LOW, params->coarseLow[level]); 232 OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, 233 AR_PHY_AGC_CTL1_COARSE_HIGH, params->coarseHigh[level]); 234 OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, 235 AR_PHY_FIND_SIG_FIRPWR, params->firpwr[level]); 236 237 if (level > aniState->noiseImmunityLevel) 238 ahp->ah_stats.ast_ani_niup++; 239 else if (level < aniState->noiseImmunityLevel) 240 ahp->ah_stats.ast_ani_nidown++; 241 aniState->noiseImmunityLevel = level; 242 break; 243 } 244 case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: { 245 static const TABLE m1ThreshLow = { 127, 50 }; 246 static const TABLE m2ThreshLow = { 127, 40 }; 247 static const TABLE m1Thresh = { 127, 0x4d }; 248 static const TABLE m2Thresh = { 127, 0x40 }; 249 static const TABLE m2CountThr = { 31, 16 }; 250 static const TABLE m2CountThrLow = { 63, 48 }; 251 u_int on = param ? 1 : 0; 252 253 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, 254 AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1ThreshLow[on]); 255 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, 256 AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2ThreshLow[on]); 257 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, 258 AR_PHY_SFCORR_M1_THRESH, m1Thresh[on]); 259 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, 260 AR_PHY_SFCORR_M2_THRESH, m2Thresh[on]); 261 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, 262 AR_PHY_SFCORR_M2COUNT_THR, m2CountThr[on]); 263 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, 264 AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2CountThrLow[on]); 265 266 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, 267 AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLow[on]); 268 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, 269 AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLow[on]); 270 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, 271 AR_PHY_SFCORR_EXT_M1_THRESH, m1Thresh[on]); 272 OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, 273 AR_PHY_SFCORR_EXT_M2_THRESH, m2Thresh[on]); 274 275 if (on) { 276 OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, 277 AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); 278 } else { 279 OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, 280 AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); 281 } 282 if (on) 283 ahp->ah_stats.ast_ani_ofdmon++; 284 else 285 ahp->ah_stats.ast_ani_ofdmoff++; 286 aniState->ofdmWeakSigDetectOff = !on; 287 break; 288 } 289 case HAL_ANI_CCK_WEAK_SIGNAL_THR: { 290 static const TABLE weakSigThrCck = { 8, 6 }; 291 u_int high = param ? 1 : 0; 292 293 OS_REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT, 294 AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, weakSigThrCck[high]); 295 if (high) 296 ahp->ah_stats.ast_ani_cckhigh++; 297 else 298 ahp->ah_stats.ast_ani_ccklow++; 299 aniState->cckWeakSigThreshold = high; 300 break; 301 } 302 case HAL_ANI_FIRSTEP_LEVEL: { 303 u_int level = param; 304 305 if (level >= params->maxFirstepLevel) { 306 HALDEBUG(ah, HAL_DEBUG_ANY, 307 "%s: level out of range (%u > %u)\n", 308 __func__, level, params->maxFirstepLevel); 309 return AH_FALSE; 310 } 311 OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, 312 AR_PHY_FIND_SIG_FIRSTEP, params->firstep[level]); 313 if (level > aniState->firstepLevel) 314 ahp->ah_stats.ast_ani_stepup++; 315 else if (level < aniState->firstepLevel) 316 ahp->ah_stats.ast_ani_stepdown++; 317 aniState->firstepLevel = level; 318 break; 319 } 320 case HAL_ANI_SPUR_IMMUNITY_LEVEL: { 321 u_int level = param; 322 323 if (level >= params->maxSpurImmunityLevel) { 324 HALDEBUG(ah, HAL_DEBUG_ANY, 325 "%s: level out of range (%u > %u)\n", 326 __func__, level, params->maxSpurImmunityLevel); 327 return AH_FALSE; 328 } 329 OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5, 330 AR_PHY_TIMING5_CYCPWR_THR1, params->cycPwrThr1[level]); 331 if (level > aniState->spurImmunityLevel) 332 ahp->ah_stats.ast_ani_spurup++; 333 else if (level < aniState->spurImmunityLevel) 334 ahp->ah_stats.ast_ani_spurdown++; 335 aniState->spurImmunityLevel = level; 336 break; 337 } 338 case HAL_ANI_PRESENT: 339 break; 340 case HAL_ANI_MODE: 341 if (param == 0) { 342 ahp->ah_procPhyErr &= ~HAL_ANI_ENA; 343 /* Turn off HW counters if we have them */ 344 ar5416AniDetach(ah); 345 ar5212SetRxFilter(ah, 346 ar5212GetRxFilter(ah) &~ HAL_RX_FILTER_PHYERR); 347 } else { /* normal/auto mode */ 348 /* don't mess with state if already enabled */ 349 if (ahp->ah_procPhyErr & HAL_ANI_ENA) 350 break; 351 ar5212SetRxFilter(ah, 352 ar5212GetRxFilter(ah) &~ HAL_RX_FILTER_PHYERR); 353 /* Enable MIB Counters */ 354 enableAniMIBCounters(ah, ahp->ah_curani != AH_NULL ? 355 ahp->ah_curani->params: &ahp->ah_aniParams24 /*XXX*/); 356 ahp->ah_procPhyErr |= HAL_ANI_ENA; 357 } 358 break; 359#ifdef AH_PRIVATE_DIAG 360 case HAL_ANI_PHYERR_RESET: 361 ahp->ah_stats.ast_ani_ofdmerrs = 0; 362 ahp->ah_stats.ast_ani_cckerrs = 0; 363 break; 364#endif /* AH_PRIVATE_DIAG */ 365 default: 366 HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid cmd %u\n", 367 __func__, cmd); 368 return AH_FALSE; 369 } 370 return AH_TRUE; 371} 372 373static void 374ar5416AniOfdmErrTrigger(struct ath_hal *ah) 375{ 376 struct ath_hal_5212 *ahp = AH5212(ah); 377 HAL_CHANNEL_INTERNAL *chan = AH_PRIVATE(ah)->ah_curchan; 378 struct ar5212AniState *aniState; 379 const struct ar5212AniParams *params; 380 381 HALASSERT(chan != AH_NULL); 382 383 if (!ANI_ENA(ah)) 384 return; 385 386 aniState = ahp->ah_curani; 387 params = aniState->params; 388 /* First, raise noise immunity level, up to max */ 389 if (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel) { 390 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 391 aniState->noiseImmunityLevel + 1); 392 return; 393 } 394 /* then, raise spur immunity level, up to max */ 395 if (aniState->spurImmunityLevel+1 < params->maxSpurImmunityLevel) { 396 ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 397 aniState->spurImmunityLevel + 1); 398 return; 399 } 400 401 if (ANI_ENA_RSSI(ah)) { 402 int32_t rssi = BEACON_RSSI(ahp); 403 if (rssi > params->rssiThrHigh) { 404 /* 405 * Beacon rssi is high, can turn off ofdm 406 * weak sig detect. 407 */ 408 if (!aniState->ofdmWeakSigDetectOff) { 409 ar5416AniControl(ah, 410 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 411 AH_FALSE); 412 ar5416AniControl(ah, 413 HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); 414 return; 415 } 416 /* 417 * If weak sig detect is already off, as last resort, 418 * raise firstep level 419 */ 420 if (aniState->firstepLevel+1 < params->maxFirstepLevel) { 421 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 422 aniState->firstepLevel + 1); 423 return; 424 } 425 } else if (rssi > params->rssiThrLow) { 426 /* 427 * Beacon rssi in mid range, need ofdm weak signal 428 * detect, but we can raise firststepLevel. 429 */ 430 if (aniState->ofdmWeakSigDetectOff) 431 ar5416AniControl(ah, 432 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 433 AH_TRUE); 434 if (aniState->firstepLevel+1 < params->maxFirstepLevel) 435 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 436 aniState->firstepLevel + 1); 437 return; 438 } else { 439 /* 440 * Beacon rssi is low, if in 11b/g mode, turn off ofdm 441 * weak signal detection and zero firstepLevel to 442 * maximize CCK sensitivity 443 */ 444 /* XXX can optimize */ 445 if (IS_CHAN_B(chan) || IS_CHAN_G(chan)) { 446 if (!aniState->ofdmWeakSigDetectOff) 447 ar5416AniControl(ah, 448 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 449 AH_FALSE); 450 if (aniState->firstepLevel > 0) 451 ar5416AniControl(ah, 452 HAL_ANI_FIRSTEP_LEVEL, 0); 453 return; 454 } 455 } 456 } 457} 458 459static void 460ar5416AniCckErrTrigger(struct ath_hal *ah) 461{ 462 struct ath_hal_5212 *ahp = AH5212(ah); 463 HAL_CHANNEL_INTERNAL *chan = AH_PRIVATE(ah)->ah_curchan; 464 struct ar5212AniState *aniState; 465 const struct ar5212AniParams *params; 466 467 HALASSERT(chan != AH_NULL); 468 469 if (!ANI_ENA(ah)) 470 return; 471 472 /* first, raise noise immunity level, up to max */ 473 aniState = ahp->ah_curani; 474 params = aniState->params; 475 if (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel) { 476 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 477 aniState->noiseImmunityLevel + 1); 478 return; 479 } 480 481 if (ANI_ENA_RSSI(ah)) { 482 int32_t rssi = BEACON_RSSI(ahp); 483 if (rssi > params->rssiThrLow) { 484 /* 485 * Beacon signal in mid and high range, 486 * raise firstep level. 487 */ 488 if (aniState->firstepLevel+1 < params->maxFirstepLevel) 489 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 490 aniState->firstepLevel + 1); 491 } else { 492 /* 493 * Beacon rssi is low, zero firstep level to maximize 494 * CCK sensitivity in 11b/g mode. 495 */ 496 /* XXX can optimize */ 497 if (IS_CHAN_B(chan) || IS_CHAN_G(chan)) { 498 if (aniState->firstepLevel > 0) 499 ar5416AniControl(ah, 500 HAL_ANI_FIRSTEP_LEVEL, 0); 501 } 502 } 503 } 504} 505 506static void 507ar5416AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState) 508{ 509 struct ath_hal_5212 *ahp = AH5212(ah); 510 const struct ar5212AniParams *params = aniState->params; 511 512 aniState->listenTime = 0; 513 /* 514 * NB: these are written on reset based on the 515 * ini so we must re-write them! 516 */ 517 HALDEBUG(ah, HAL_DEBUG_ANI, 518 "%s: Writing ofdmbase=%u cckbase=%u\n", __func__, 519 params->ofdmPhyErrBase, params->cckPhyErrBase); 520 OS_REG_WRITE(ah, AR_PHY_ERR_1, params->ofdmPhyErrBase); 521 OS_REG_WRITE(ah, AR_PHY_ERR_2, params->cckPhyErrBase); 522 OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); 523 OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_CCK_TIMING); 524 525 /* Clear the mib counters and save them in the stats */ 526 ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 527 aniState->ofdmPhyErrCount = 0; 528 aniState->cckPhyErrCount = 0; 529} 530 531/* 532 * Restore/reset the ANI parameters and reset the statistics. 533 * This routine must be called for every channel change. 534 * 535 * NOTE: This is where ah_curani is set; other ani code assumes 536 * it is setup to reflect the current channel. 537 */ 538void 539ar5416AniReset(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan, 540 HAL_OPMODE opmode, int restore) 541{ 542 struct ath_hal_5212 *ahp = AH5212(ah); 543 struct ar5212AniState *aniState; 544 uint32_t rxfilter; 545 int index; 546 547 index = ar5416GetAniChannelIndex(ah, chan); 548 aniState = &ahp->ah_ani[index]; 549 ahp->ah_curani = aniState; 550#if 0 551 ath_hal_printf(ah,"%s: chan %u/0x%x restore %d setup %d opmode %u\n", 552 __func__, chan->channel, chan->channelFlags, restore, 553 aniState->isSetup, opmode); 554#else 555 HALDEBUG(ah, HAL_DEBUG_ANI, 556 "%s: chan %u/0x%x restore %d setup %d opmode %u\n", 557 __func__, chan->channel, chan->channelFlags, restore, 558 aniState->isSetup, opmode); 559#endif 560 OS_MARK(ah, AH_MARK_ANI_RESET, opmode); 561 562 /* 563 * Turn off PHY error frame delivery while we futz with settings. 564 */ 565 rxfilter = ar5212GetRxFilter(ah); 566 ar5212SetRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR); 567 /* 568 * Automatic processing is done only in station mode right now. 569 */ 570 if (opmode == HAL_M_STA) 571 ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA; 572 else 573 ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA; 574 /* 575 * Set all ani parameters. We either set them to initial 576 * values or restore the previous ones for the channel. 577 * XXX if ANI follows hardware, we don't care what mode we're 578 * XXX in, we should keep the ani parameters 579 */ 580 if (restore && aniState->isSetup) { 581 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 582 aniState->noiseImmunityLevel); 583 ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 584 aniState->spurImmunityLevel); 585 ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 586 !aniState->ofdmWeakSigDetectOff); 587 ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, 588 aniState->cckWeakSigThreshold); 589 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 590 aniState->firstepLevel); 591 } else { 592 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0); 593 ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); 594 ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 595 AH_TRUE); 596 ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE); 597 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); 598 aniState->isSetup = AH_TRUE; 599 } 600 ar5416AniRestart(ah, aniState); 601 602 /* restore RX filter mask */ 603 ar5212SetRxFilter(ah, rxfilter); 604} 605 606/* 607 * Process a MIB interrupt. We may potentially be invoked because 608 * any of the MIB counters overflow/trigger so don't assume we're 609 * here because a PHY error counter triggered. 610 */ 611void 612ar5416ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats) 613{ 614 struct ath_hal_5212 *ahp = AH5212(ah); 615 uint32_t phyCnt1, phyCnt2; 616 617 HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x " 618 "filtofdm 0x%x filtcck 0x%x\n", 619 __func__, OS_REG_READ(ah, AR_MIBC), 620 OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2), 621 OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK)); 622 623 /* 624 * First order of business is to clear whatever caused 625 * the interrupt so we don't keep getting interrupted. 626 * We have the usual mib counters that are reset-on-read 627 * and the additional counters that appeared starting in 628 * Hainan. We collect the mib counters and explicitly 629 * zero additional counters we are not using. Anything 630 * else is reset only if it caused the interrupt. 631 */ 632 /* NB: these are not reset-on-read */ 633 phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1); 634 phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2); 635 /* not used, always reset them in case they are the cause */ 636 OS_REG_WRITE(ah, AR_FILTOFDM, 0); 637 OS_REG_WRITE(ah, AR_FILTCCK, 0); 638 if ((OS_REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING) == 0) 639 OS_REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR); 640 641 /* Clear the mib counters and save them in the stats */ 642 ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 643 ahp->ah_stats.ast_nodestats = *stats; 644 645 /* 646 * Check for an ani stat hitting the trigger threshold. 647 * When this happens we get a MIB interrupt and the top 648 * 2 bits of the counter register will be 0b11, hence 649 * the mask check of phyCnt?. 650 */ 651 if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || 652 ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { 653 struct ar5212AniState *aniState = ahp->ah_curani; 654 const struct ar5212AniParams *params = aniState->params; 655 uint32_t ofdmPhyErrCnt, cckPhyErrCnt; 656 657 ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; 658 ahp->ah_stats.ast_ani_ofdmerrs += 659 ofdmPhyErrCnt - aniState->ofdmPhyErrCount; 660 aniState->ofdmPhyErrCount = ofdmPhyErrCnt; 661 662 cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; 663 ahp->ah_stats.ast_ani_cckerrs += 664 cckPhyErrCnt - aniState->cckPhyErrCount; 665 aniState->cckPhyErrCount = cckPhyErrCnt; 666 667 /* 668 * NB: figure out which counter triggered. If both 669 * trigger we'll only deal with one as the processing 670 * clobbers the error counter so the trigger threshold 671 * check will never be true. 672 */ 673 if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh) 674 ar5416AniOfdmErrTrigger(ah); 675 if (aniState->cckPhyErrCount > params->cckTrigHigh) 676 ar5416AniCckErrTrigger(ah); 677 /* NB: always restart to insure the h/w counters are reset */ 678 ar5416AniRestart(ah, aniState); 679 } 680} 681 682static void 683ar5416AniLowerImmunity(struct ath_hal *ah) 684{ 685 struct ath_hal_5212 *ahp = AH5212(ah); 686 struct ar5212AniState *aniState; 687 const struct ar5212AniParams *params; 688 689 HALASSERT(ANI_ENA(ah)); 690 691 aniState = ahp->ah_curani; 692 params = aniState->params; 693 if (ANI_ENA_RSSI(ah)) { 694 int32_t rssi = BEACON_RSSI(ahp); 695 if (rssi > params->rssiThrHigh) { 696 /* 697 * Beacon signal is high, leave ofdm weak signal 698 * detection off or it may oscillate. Let it fall 699 * through. 700 */ 701 } else if (rssi > params->rssiThrLow) { 702 /* 703 * Beacon rssi in mid range, turn on ofdm weak signal 704 * detection or lower firstep level. 705 */ 706 if (aniState->ofdmWeakSigDetectOff) { 707 ar5416AniControl(ah, 708 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 709 AH_TRUE); 710 return; 711 } 712 if (aniState->firstepLevel > 0) { 713 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 714 aniState->firstepLevel - 1); 715 return; 716 } 717 } else { 718 /* 719 * Beacon rssi is low, reduce firstep level. 720 */ 721 if (aniState->firstepLevel > 0) { 722 ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 723 aniState->firstepLevel - 1); 724 return; 725 } 726 } 727 } 728 /* then lower spur immunity level, down to zero */ 729 if (aniState->spurImmunityLevel > 0) { 730 ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 731 aniState->spurImmunityLevel - 1); 732 return; 733 } 734 /* 735 * if all else fails, lower noise immunity level down to a min value 736 * zero for now 737 */ 738 if (aniState->noiseImmunityLevel > 0) { 739 ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 740 aniState->noiseImmunityLevel - 1); 741 return; 742 } 743} 744 745#define CLOCK_RATE 44000 /* XXX use mac_usec or similar */ 746/* convert HW counter values to ms using 11g clock rate, goo9d enough 747 for 11a and Turbo */ 748 749/* 750 * Return an approximation of the time spent ``listening'' by 751 * deducting the cycles spent tx'ing and rx'ing from the total 752 * cycle count since our last call. A return value <0 indicates 753 * an invalid/inconsistent time. 754 */ 755static int32_t 756ar5416AniGetListenTime(struct ath_hal *ah) 757{ 758 struct ath_hal_5212 *ahp = AH5212(ah); 759 struct ar5212AniState *aniState; 760 uint32_t txFrameCount, rxFrameCount, cycleCount; 761 int32_t listenTime; 762 763 txFrameCount = OS_REG_READ(ah, AR_TFCNT); 764 rxFrameCount = OS_REG_READ(ah, AR_RFCNT); 765 cycleCount = OS_REG_READ(ah, AR_CCCNT); 766 767 aniState = ahp->ah_curani; 768 if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) { 769 /* 770 * Cycle counter wrap (or initial call); it's not possible 771 * to accurately calculate a value because the registers 772 * right shift rather than wrap--so punt and return 0. 773 */ 774 listenTime = 0; 775 ahp->ah_stats.ast_ani_lzero++; 776 } else { 777 int32_t ccdelta = cycleCount - aniState->cycleCount; 778 int32_t rfdelta = rxFrameCount - aniState->rxFrameCount; 779 int32_t tfdelta = txFrameCount - aniState->txFrameCount; 780 listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE; 781 } 782 aniState->cycleCount = cycleCount; 783 aniState->txFrameCount = txFrameCount; 784 aniState->rxFrameCount = rxFrameCount; 785 return listenTime; 786} 787 788/* 789 * Update ani stats in preparation for listen time processing. 790 */ 791static void 792updateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState) 793{ 794 struct ath_hal_5212 *ahp = AH5212(ah); 795 const struct ar5212AniParams *params = aniState->params; 796 uint32_t phyCnt1, phyCnt2; 797 int32_t ofdmPhyErrCnt, cckPhyErrCnt; 798 799 /* Clear the mib counters and save them in the stats */ 800 ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 801 802 /* NB: these are not reset-on-read */ 803 phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1); 804 phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2); 805 806 /* NB: these are spec'd to never roll-over */ 807 ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; 808 if (ofdmPhyErrCnt < 0) { 809 HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n", 810 ofdmPhyErrCnt, phyCnt1); 811 ofdmPhyErrCnt = AR_PHY_COUNTMAX; 812 } 813 ahp->ah_stats.ast_ani_ofdmerrs += 814 ofdmPhyErrCnt - aniState->ofdmPhyErrCount; 815 aniState->ofdmPhyErrCount = ofdmPhyErrCnt; 816 817 cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; 818 if (cckPhyErrCnt < 0) { 819 HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n", 820 cckPhyErrCnt, phyCnt2); 821 cckPhyErrCnt = AR_PHY_COUNTMAX; 822 } 823 ahp->ah_stats.ast_ani_cckerrs += 824 cckPhyErrCnt - aniState->cckPhyErrCount; 825 aniState->cckPhyErrCount = cckPhyErrCnt; 826} 827 828/* 829 * Do periodic processing. This routine is called from the 830 * driver's rx interrupt handler after processing frames. 831 */ 832void 833ar5416AniPoll(struct ath_hal *ah, const HAL_NODE_STATS *stats, 834 HAL_CHANNEL *chan) 835{ 836 struct ath_hal_5212 *ahp = AH5212(ah); 837 struct ar5212AniState *aniState = ahp->ah_curani; 838 const struct ar5212AniParams *params; 839 int32_t listenTime; 840 841 ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi; 842 843 /* XXX can aniState be null? */ 844 if (aniState == AH_NULL) 845 return; 846 if (!ANI_ENA(ah)) 847 return; 848 849 listenTime = ar5416AniGetListenTime(ah); 850 if (listenTime < 0) { 851 ahp->ah_stats.ast_ani_lneg++; 852 /* restart ANI period if listenTime is invalid */ 853 ar5416AniRestart(ah, aniState); 854 } 855 /* XXX beware of overflow? */ 856 aniState->listenTime += listenTime; 857 858 OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime); 859 860 params = aniState->params; 861 if (aniState->listenTime > 5*params->period) { 862 /* 863 * Check to see if need to lower immunity if 864 * 5 aniPeriods have passed 865 */ 866 updateMIBStats(ah, aniState); 867 if (aniState->ofdmPhyErrCount <= aniState->listenTime * 868 params->ofdmTrigLow/1000 && 869 aniState->cckPhyErrCount <= aniState->listenTime * 870 params->cckTrigLow/1000) 871 ar5416AniLowerImmunity(ah); 872 ar5416AniRestart(ah, aniState); 873 } else if (aniState->listenTime > params->period) { 874 updateMIBStats(ah, aniState); 875 /* check to see if need to raise immunity */ 876 if (aniState->ofdmPhyErrCount > aniState->listenTime * 877 params->ofdmTrigHigh / 1000) { 878 ar5416AniOfdmErrTrigger(ah); 879 ar5416AniRestart(ah, aniState); 880 } else if (aniState->cckPhyErrCount > aniState->listenTime * 881 params->cckTrigHigh / 1000) { 882 ar5416AniCckErrTrigger(ah); 883 ar5416AniRestart(ah, aniState); 884 } 885 } 886} |