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