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