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