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