514 lines · c
1/*2 * Copyright (c) 2008-2011 Atheros Communications Inc.3 *4 * Permission to use, copy, modify, and/or distribute this software for any5 * purpose with or without fee is hereby granted, provided that the above6 * copyright notice and this permission notice appear in all copies.7 *8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.15 */16 17#include <linux/kernel.h>18#include <linux/export.h>19#include "hw.h"20#include "hw-ops.h"21 22struct ani_ofdm_level_entry {23 int spur_immunity_level;24 int fir_step_level;25 int ofdm_weak_signal_on;26};27 28/* values here are relative to the INI */29 30/*31 * Legend:32 *33 * SI: Spur immunity34 * FS: FIR Step35 * WS: OFDM / CCK Weak Signal detection36 * MRC-CCK: Maximal Ratio Combining for CCK37 */38 39static const struct ani_ofdm_level_entry ofdm_level_table[] = {40 /* SI FS WS */41 { 0, 0, 1 }, /* lvl 0 */42 { 1, 1, 1 }, /* lvl 1 */43 { 2, 2, 1 }, /* lvl 2 */44 { 3, 2, 1 }, /* lvl 3 (default) */45 { 4, 3, 1 }, /* lvl 4 */46 { 5, 4, 1 }, /* lvl 5 */47 { 6, 5, 1 }, /* lvl 6 */48 { 7, 6, 1 }, /* lvl 7 */49 { 7, 7, 1 }, /* lvl 8 */50 { 7, 8, 0 } /* lvl 9 */51};52#define ATH9K_ANI_OFDM_NUM_LEVEL \53 ARRAY_SIZE(ofdm_level_table)54#define ATH9K_ANI_OFDM_MAX_LEVEL \55 (ATH9K_ANI_OFDM_NUM_LEVEL-1)56#define ATH9K_ANI_OFDM_DEF_LEVEL \57 3 /* default level - matches the INI settings */58 59/*60 * MRC (Maximal Ratio Combining) has always been used with multi-antenna ofdm.61 * With OFDM for single stream you just add up all antenna inputs, you're62 * only interested in what you get after FFT. Signal alignment is also not63 * required for OFDM because any phase difference adds up in the frequency64 * domain.65 *66 * MRC requires extra work for use with CCK. You need to align the antenna67 * signals from the different antenna before you can add the signals together.68 * You need alignment of signals as CCK is in time domain, so addition can cancel69 * your signal completely if phase is 180 degrees (think of adding sine waves).70 * You also need to remove noise before the addition and this is where ANI71 * MRC CCK comes into play. One of the antenna inputs may be stronger but72 * lower SNR, so just adding after alignment can be dangerous.73 *74 * Regardless of alignment in time, the antenna signals add constructively after75 * FFT and improve your reception. For more information:76 *77 * https://en.wikipedia.org/wiki/Maximal-ratio_combining78 */79 80struct ani_cck_level_entry {81 int fir_step_level;82 int mrc_cck_on;83};84 85static const struct ani_cck_level_entry cck_level_table[] = {86 /* FS MRC-CCK */87 { 0, 1 }, /* lvl 0 */88 { 1, 1 }, /* lvl 1 */89 { 2, 1 }, /* lvl 2 (default) */90 { 3, 1 }, /* lvl 3 */91 { 4, 0 }, /* lvl 4 */92 { 5, 0 }, /* lvl 5 */93 { 6, 0 }, /* lvl 6 */94 { 7, 0 }, /* lvl 7 (only for high rssi) */95 { 8, 0 } /* lvl 8 (only for high rssi) */96};97 98#define ATH9K_ANI_CCK_NUM_LEVEL \99 ARRAY_SIZE(cck_level_table)100#define ATH9K_ANI_CCK_MAX_LEVEL \101 (ATH9K_ANI_CCK_NUM_LEVEL-1)102#define ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI \103 (ATH9K_ANI_CCK_NUM_LEVEL-3)104#define ATH9K_ANI_CCK_DEF_LEVEL \105 2 /* default level - matches the INI settings */106 107static void ath9k_hw_update_mibstats(struct ath_hw *ah,108 struct ath9k_mib_stats *stats)109{110 u32 addr[5] = {AR_RTS_OK, AR_RTS_FAIL, AR_ACK_FAIL,111 AR_FCS_FAIL, AR_BEACON_CNT};112 u32 data[5];113 114 REG_READ_MULTI(ah, &addr[0], &data[0], 5);115 /* AR_RTS_OK */116 stats->rts_good += data[0];117 /* AR_RTS_FAIL */118 stats->rts_bad += data[1];119 /* AR_ACK_FAIL */120 stats->ackrcv_bad += data[2];121 /* AR_FCS_FAIL */122 stats->fcs_bad += data[3];123 /* AR_BEACON_CNT */124 stats->beacons += data[4];125}126 127static void ath9k_ani_restart(struct ath_hw *ah)128{129 struct ar5416AniState *aniState = &ah->ani;130 131 aniState->listenTime = 0;132 133 ENABLE_REGWRITE_BUFFER(ah);134 135 REG_WRITE(ah, AR_PHY_ERR_1, 0);136 REG_WRITE(ah, AR_PHY_ERR_2, 0);137 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);138 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);139 140 REGWRITE_BUFFER_FLUSH(ah);141 142 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);143 144 aniState->ofdmPhyErrCount = 0;145 aniState->cckPhyErrCount = 0;146}147 148/* Adjust the OFDM Noise Immunity Level */149static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel,150 bool scan)151{152 struct ar5416AniState *aniState = &ah->ani;153 struct ath_common *common = ath9k_hw_common(ah);154 const struct ani_ofdm_level_entry *entry_ofdm;155 const struct ani_cck_level_entry *entry_cck;156 bool weak_sig;157 158 ath_dbg(common, ANI, "**** ofdmlevel %d=>%d, rssi=%d[lo=%d hi=%d]\n",159 aniState->ofdmNoiseImmunityLevel,160 immunityLevel, BEACON_RSSI(ah),161 ATH9K_ANI_RSSI_THR_LOW,162 ATH9K_ANI_RSSI_THR_HIGH);163 164 if (AR_SREV_9100(ah) && immunityLevel < ATH9K_ANI_OFDM_DEF_LEVEL)165 immunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL;166 167 if (!scan)168 aniState->ofdmNoiseImmunityLevel = immunityLevel;169 170 entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel];171 entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel];172 173 if (aniState->spurImmunityLevel != entry_ofdm->spur_immunity_level)174 ath9k_hw_ani_control(ah,175 ATH9K_ANI_SPUR_IMMUNITY_LEVEL,176 entry_ofdm->spur_immunity_level);177 178 if (aniState->firstepLevel != entry_ofdm->fir_step_level &&179 entry_ofdm->fir_step_level >= entry_cck->fir_step_level)180 ath9k_hw_ani_control(ah,181 ATH9K_ANI_FIRSTEP_LEVEL,182 entry_ofdm->fir_step_level);183 184 weak_sig = entry_ofdm->ofdm_weak_signal_on;185 if (ah->opmode == NL80211_IFTYPE_STATION &&186 BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_HIGH)187 weak_sig = true;188 /*189 * Newer chipsets are better at dealing with high PHY error counts -190 * keep weak signal detection enabled when no RSSI threshold is191 * available to determine if it is needed (mode != STA)192 */193 else if (AR_SREV_9300_20_OR_LATER(ah) &&194 ah->opmode != NL80211_IFTYPE_STATION)195 weak_sig = true;196 197 /* Older chipsets are more sensitive to high PHY error counts */198 else if (!AR_SREV_9300_20_OR_LATER(ah) &&199 aniState->ofdmNoiseImmunityLevel >= 8)200 weak_sig = false;201 202 if (aniState->ofdmWeakSigDetect != weak_sig)203 ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,204 weak_sig);205 206 if (!AR_SREV_9300_20_OR_LATER(ah))207 return;208 209 if (aniState->ofdmNoiseImmunityLevel >= ATH9K_ANI_OFDM_DEF_LEVEL) {210 ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;211 ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI;212 } else {213 ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI;214 ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW;215 }216}217 218static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)219{220 struct ar5416AniState *aniState = &ah->ani;221 222 if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL)223 ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1, false);224}225 226/*227 * Set the ANI settings to match an CCK level.228 */229static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel,230 bool scan)231{232 struct ar5416AniState *aniState = &ah->ani;233 struct ath_common *common = ath9k_hw_common(ah);234 const struct ani_ofdm_level_entry *entry_ofdm;235 const struct ani_cck_level_entry *entry_cck;236 237 ath_dbg(common, ANI, "**** ccklevel %d=>%d, rssi=%d[lo=%d hi=%d]\n",238 aniState->cckNoiseImmunityLevel, immunityLevel,239 BEACON_RSSI(ah), ATH9K_ANI_RSSI_THR_LOW,240 ATH9K_ANI_RSSI_THR_HIGH);241 242 if (AR_SREV_9100(ah) && immunityLevel < ATH9K_ANI_CCK_DEF_LEVEL)243 immunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;244 245 if (ah->opmode == NL80211_IFTYPE_STATION &&246 BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_LOW &&247 immunityLevel > ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI)248 immunityLevel = ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI;249 250 if (!scan)251 aniState->cckNoiseImmunityLevel = immunityLevel;252 253 entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel];254 entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel];255 256 if (aniState->firstepLevel != entry_cck->fir_step_level &&257 entry_cck->fir_step_level >= entry_ofdm->fir_step_level)258 ath9k_hw_ani_control(ah,259 ATH9K_ANI_FIRSTEP_LEVEL,260 entry_cck->fir_step_level);261 262 /* Skip MRC CCK for pre AR9003 families */263 if (!AR_SREV_9300_20_OR_LATER(ah) || AR_SREV_9485(ah) ||264 AR_SREV_9565(ah) || AR_SREV_9561(ah))265 return;266 267 if (aniState->mrcCCK != entry_cck->mrc_cck_on)268 ath9k_hw_ani_control(ah,269 ATH9K_ANI_MRC_CCK,270 entry_cck->mrc_cck_on);271}272 273static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah)274{275 struct ar5416AniState *aniState = &ah->ani;276 277 if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL)278 ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1,279 false);280}281 282/*283 * only lower either OFDM or CCK errors per turn284 * we lower the other one next time285 */286static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)287{288 struct ar5416AniState *aniState = &ah->ani;289 290 /* lower OFDM noise immunity */291 if (aniState->ofdmNoiseImmunityLevel > 0 &&292 (aniState->ofdmsTurn || aniState->cckNoiseImmunityLevel == 0)) {293 ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel - 1,294 false);295 return;296 }297 298 /* lower CCK noise immunity */299 if (aniState->cckNoiseImmunityLevel > 0)300 ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel - 1,301 false);302}303 304/*305 * Restore the ANI parameters in the HAL and reset the statistics.306 * This routine should be called for every hardware reset and for307 * every channel change.308 */309void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)310{311 struct ar5416AniState *aniState = &ah->ani;312 struct ath9k_channel *chan = ah->curchan;313 struct ath_common *common = ath9k_hw_common(ah);314 int ofdm_nil, cck_nil;315 316 if (!chan)317 return;318 319 BUG_ON(aniState == NULL);320 ah->stats.ast_ani_reset++;321 322 ofdm_nil = max_t(int, ATH9K_ANI_OFDM_DEF_LEVEL,323 aniState->ofdmNoiseImmunityLevel);324 cck_nil = max_t(int, ATH9K_ANI_CCK_DEF_LEVEL,325 aniState->cckNoiseImmunityLevel);326 327 if (is_scanning ||328 (ah->opmode != NL80211_IFTYPE_STATION &&329 ah->opmode != NL80211_IFTYPE_ADHOC)) {330 /*331 * If we're scanning or in AP mode, the defaults (ini)332 * should be in place. For an AP we assume the historical333 * levels for this channel are probably outdated so start334 * from defaults instead.335 */336 if (aniState->ofdmNoiseImmunityLevel !=337 ATH9K_ANI_OFDM_DEF_LEVEL ||338 aniState->cckNoiseImmunityLevel !=339 ATH9K_ANI_CCK_DEF_LEVEL) {340 ath_dbg(common, ANI,341 "Restore defaults: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n",342 ah->opmode,343 chan->channel,344 is_scanning,345 aniState->ofdmNoiseImmunityLevel,346 aniState->cckNoiseImmunityLevel);347 348 ofdm_nil = ATH9K_ANI_OFDM_DEF_LEVEL;349 cck_nil = ATH9K_ANI_CCK_DEF_LEVEL;350 }351 } else {352 /*353 * restore historical levels for this channel354 */355 ath_dbg(common, ANI,356 "Restore history: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n",357 ah->opmode,358 chan->channel,359 is_scanning,360 aniState->ofdmNoiseImmunityLevel,361 aniState->cckNoiseImmunityLevel);362 }363 ath9k_hw_set_ofdm_nil(ah, ofdm_nil, is_scanning);364 ath9k_hw_set_cck_nil(ah, cck_nil, is_scanning);365 366 ath9k_ani_restart(ah);367}368 369static bool ath9k_hw_ani_read_counters(struct ath_hw *ah)370{371 struct ath_common *common = ath9k_hw_common(ah);372 struct ar5416AniState *aniState = &ah->ani;373 u32 phyCnt1, phyCnt2;374 int32_t listenTime;375 376 ath_hw_cycle_counters_update(common);377 listenTime = ath_hw_get_listen_time(common);378 379 if (listenTime <= 0) {380 ah->stats.ast_ani_lneg_or_lzero++;381 ath9k_ani_restart(ah);382 return false;383 }384 385 aniState->listenTime += listenTime;386 387 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);388 389 phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);390 phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);391 392 ah->stats.ast_ani_ofdmerrs += phyCnt1 - aniState->ofdmPhyErrCount;393 aniState->ofdmPhyErrCount = phyCnt1;394 395 ah->stats.ast_ani_cckerrs += phyCnt2 - aniState->cckPhyErrCount;396 aniState->cckPhyErrCount = phyCnt2;397 398 return true;399}400 401void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan)402{403 struct ar5416AniState *aniState = &ah->ani;404 struct ath_common *common = ath9k_hw_common(ah);405 u32 ofdmPhyErrRate, cckPhyErrRate;406 407 if (!ath9k_hw_ani_read_counters(ah))408 return;409 410 ofdmPhyErrRate = aniState->ofdmPhyErrCount * 1000 /411 aniState->listenTime;412 cckPhyErrRate = aniState->cckPhyErrCount * 1000 /413 aniState->listenTime;414 415 ath_dbg(common, ANI,416 "listenTime=%d OFDM:%d errs=%d/s CCK:%d errs=%d/s ofdm_turn=%d\n",417 aniState->listenTime,418 aniState->ofdmNoiseImmunityLevel,419 ofdmPhyErrRate, aniState->cckNoiseImmunityLevel,420 cckPhyErrRate, aniState->ofdmsTurn);421 422 if (aniState->listenTime > ah->aniperiod) {423 if (cckPhyErrRate < ah->config.cck_trig_low &&424 ofdmPhyErrRate < ah->config.ofdm_trig_low) {425 ath9k_hw_ani_lower_immunity(ah);426 aniState->ofdmsTurn = !aniState->ofdmsTurn;427 } else if (ofdmPhyErrRate > ah->config.ofdm_trig_high) {428 ath9k_hw_ani_ofdm_err_trigger(ah);429 aniState->ofdmsTurn = false;430 } else if (cckPhyErrRate > ah->config.cck_trig_high) {431 ath9k_hw_ani_cck_err_trigger(ah);432 aniState->ofdmsTurn = true;433 } else434 return;435 436 ath9k_ani_restart(ah);437 }438}439EXPORT_SYMBOL(ath9k_hw_ani_monitor);440 441void ath9k_enable_mib_counters(struct ath_hw *ah)442{443 struct ath_common *common = ath9k_hw_common(ah);444 445 ath_dbg(common, ANI, "Enable MIB counters\n");446 447 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);448 449 ENABLE_REGWRITE_BUFFER(ah);450 451 REG_WRITE(ah, AR_FILT_OFDM, 0);452 REG_WRITE(ah, AR_FILT_CCK, 0);453 REG_WRITE(ah, AR_MIBC,454 ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS)455 & 0x0f);456 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);457 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);458 459 REGWRITE_BUFFER_FLUSH(ah);460}461 462/* Freeze the MIB counters, get the stats and then clear them */463void ath9k_hw_disable_mib_counters(struct ath_hw *ah)464{465 struct ath_common *common = ath9k_hw_common(ah);466 467 ath_dbg(common, ANI, "Disable MIB counters\n");468 469 REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC);470 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);471 REG_WRITE(ah, AR_MIBC, AR_MIBC_CMC);472 REG_WRITE(ah, AR_FILT_OFDM, 0);473 REG_WRITE(ah, AR_FILT_CCK, 0);474}475EXPORT_SYMBOL(ath9k_hw_disable_mib_counters);476 477void ath9k_hw_ani_init(struct ath_hw *ah)478{479 struct ath_common *common = ath9k_hw_common(ah);480 struct ar5416AniState *ani = &ah->ani;481 482 ath_dbg(common, ANI, "Initialize ANI\n");483 484 if (AR_SREV_9300_20_OR_LATER(ah)) {485 ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;486 ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW;487 ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH;488 ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW;489 } else {490 ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_OLD;491 ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_OLD;492 ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH_OLD;493 ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW_OLD;494 }495 496 ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;497 ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;498 ani->mrcCCK = AR_SREV_9300_20_OR_LATER(ah) ? true : false;499 ani->ofdmsTurn = true;500 ani->ofdmWeakSigDetect = true;501 ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;502 ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL;503 504 /*505 * since we expect some ongoing maintenance on the tables, let's sanity506 * check here default level should not modify INI setting.507 */508 ah->aniperiod = ATH9K_ANI_PERIOD;509 ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL;510 511 ath9k_ani_restart(ah);512 ath9k_enable_mib_counters(ah);513}514