linux/drivers/net/wireless/ath/ath9k/ani.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2008-2010 Atheros Communications Inc.
   3 *
   4 * Permission to use, copy, modify, and/or distribute this software for any
   5 * purpose with or without fee is hereby granted, provided that the above
   6 * copyright notice and this permission notice appear in all copies.
   7 *
   8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15 */
  16
  17#include <linux/kernel.h>
  18#include "hw.h"
  19#include "hw-ops.h"
  20
  21struct ani_ofdm_level_entry {
  22        int spur_immunity_level;
  23        int fir_step_level;
  24        int ofdm_weak_signal_on;
  25};
  26
  27/* values here are relative to the INI */
  28
  29/*
  30 * Legend:
  31 *
  32 * SI: Spur immunity
  33 * FS: FIR Step
  34 * WS: OFDM / CCK Weak Signal detection
  35 * MRC-CCK: Maximal Ratio Combining for CCK
  36 */
  37
  38static const struct ani_ofdm_level_entry ofdm_level_table[] = {
  39        /* SI  FS  WS */
  40        {  0,  0,  1  }, /* lvl 0 */
  41        {  1,  1,  1  }, /* lvl 1 */
  42        {  2,  2,  1  }, /* lvl 2 */
  43        {  3,  2,  1  }, /* lvl 3  (default) */
  44        {  4,  3,  1  }, /* lvl 4 */
  45        {  5,  4,  1  }, /* lvl 5 */
  46        {  6,  5,  1  }, /* lvl 6 */
  47        {  7,  6,  1  }, /* lvl 7 */
  48        {  7,  7,  1  }, /* lvl 8 */
  49        {  7,  8,  0  }  /* lvl 9 */
  50};
  51#define ATH9K_ANI_OFDM_NUM_LEVEL \
  52        ARRAY_SIZE(ofdm_level_table)
  53#define ATH9K_ANI_OFDM_MAX_LEVEL \
  54        (ATH9K_ANI_OFDM_NUM_LEVEL-1)
  55#define ATH9K_ANI_OFDM_DEF_LEVEL \
  56        3 /* default level - matches the INI settings */
  57
  58/*
  59 * MRC (Maximal Ratio Combining) has always been used with multi-antenna ofdm.
  60 * With OFDM for single stream you just add up all antenna inputs, you're
  61 * only interested in what you get after FFT. Signal aligment is also not
  62 * required for OFDM because any phase difference adds up in the frequency
  63 * domain.
  64 *
  65 * MRC requires extra work for use with CCK. You need to align the antenna
  66 * signals from the different antenna before you can add the signals together.
  67 * You need aligment of signals as CCK is in time domain, so addition can cancel
  68 * your signal completely if phase is 180 degrees (think of adding sine waves).
  69 * You also need to remove noise before the addition and this is where ANI
  70 * MRC CCK comes into play. One of the antenna inputs may be stronger but
  71 * lower SNR, so just adding after alignment can be dangerous.
  72 *
  73 * Regardless of alignment in time, the antenna signals add constructively after
  74 * FFT and improve your reception. For more information:
  75 *
  76 * http://en.wikipedia.org/wiki/Maximal-ratio_combining
  77 */
  78
  79struct ani_cck_level_entry {
  80        int fir_step_level;
  81        int mrc_cck_on;
  82};
  83
  84static const struct ani_cck_level_entry cck_level_table[] = {
  85        /* FS  MRC-CCK  */
  86        {  0,  1  }, /* lvl 0 */
  87        {  1,  1  }, /* lvl 1 */
  88        {  2,  1  }, /* lvl 2  (default) */
  89        {  3,  1  }, /* lvl 3 */
  90        {  4,  0  }, /* lvl 4 */
  91        {  5,  0  }, /* lvl 5 */
  92        {  6,  0  }, /* lvl 6 */
  93        {  7,  0  }, /* lvl 7 (only for high rssi) */
  94        {  8,  0  }  /* lvl 8 (only for high rssi) */
  95};
  96
  97#define ATH9K_ANI_CCK_NUM_LEVEL \
  98        ARRAY_SIZE(cck_level_table)
  99#define ATH9K_ANI_CCK_MAX_LEVEL \
 100        (ATH9K_ANI_CCK_NUM_LEVEL-1)
 101#define ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI \
 102        (ATH9K_ANI_CCK_NUM_LEVEL-3)
 103#define ATH9K_ANI_CCK_DEF_LEVEL \
 104        2 /* default level - matches the INI settings */
 105
 106static bool use_new_ani(struct ath_hw *ah)
 107{
 108        return AR_SREV_9300_20_OR_LATER(ah) || modparam_force_new_ani;
 109}
 110
 111static void ath9k_hw_update_mibstats(struct ath_hw *ah,
 112                                     struct ath9k_mib_stats *stats)
 113{
 114        stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL);
 115        stats->rts_bad += REG_READ(ah, AR_RTS_FAIL);
 116        stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL);
 117        stats->rts_good += REG_READ(ah, AR_RTS_OK);
 118        stats->beacons += REG_READ(ah, AR_BEACON_CNT);
 119}
 120
 121static void ath9k_ani_restart(struct ath_hw *ah)
 122{
 123        struct ar5416AniState *aniState;
 124        struct ath_common *common = ath9k_hw_common(ah);
 125        u32 ofdm_base = 0, cck_base = 0;
 126
 127        if (!DO_ANI(ah))
 128                return;
 129
 130        aniState = &ah->curchan->ani;
 131        aniState->listenTime = 0;
 132
 133        if (!use_new_ani(ah)) {
 134                ofdm_base = AR_PHY_COUNTMAX - ah->config.ofdm_trig_high;
 135                cck_base = AR_PHY_COUNTMAX - ah->config.cck_trig_high;
 136        }
 137
 138        ath_print(common, ATH_DBG_ANI,
 139                  "Writing ofdmbase=%u   cckbase=%u\n", ofdm_base, cck_base);
 140
 141        ENABLE_REGWRITE_BUFFER(ah);
 142
 143        REG_WRITE(ah, AR_PHY_ERR_1, ofdm_base);
 144        REG_WRITE(ah, AR_PHY_ERR_2, cck_base);
 145        REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
 146        REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
 147
 148        REGWRITE_BUFFER_FLUSH(ah);
 149
 150        ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
 151
 152        aniState->ofdmPhyErrCount = 0;
 153        aniState->cckPhyErrCount = 0;
 154}
 155
 156static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah)
 157{
 158        struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
 159        struct ar5416AniState *aniState;
 160        int32_t rssi;
 161
 162        aniState = &ah->curchan->ani;
 163
 164        if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
 165                if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
 166                                         aniState->noiseImmunityLevel + 1)) {
 167                        return;
 168                }
 169        }
 170
 171        if (aniState->spurImmunityLevel < HAL_SPUR_IMMUNE_MAX) {
 172                if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
 173                                         aniState->spurImmunityLevel + 1)) {
 174                        return;
 175                }
 176        }
 177
 178        if (ah->opmode == NL80211_IFTYPE_AP) {
 179                if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
 180                        ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
 181                                             aniState->firstepLevel + 1);
 182                }
 183                return;
 184        }
 185        rssi = BEACON_RSSI(ah);
 186        if (rssi > aniState->rssiThrHigh) {
 187                if (!aniState->ofdmWeakSigDetectOff) {
 188                        if (ath9k_hw_ani_control(ah,
 189                                         ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
 190                                         false)) {
 191                                ath9k_hw_ani_control(ah,
 192                                        ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
 193                                return;
 194                        }
 195                }
 196                if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
 197                        ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
 198                                             aniState->firstepLevel + 1);
 199                        return;
 200                }
 201        } else if (rssi > aniState->rssiThrLow) {
 202                if (aniState->ofdmWeakSigDetectOff)
 203                        ath9k_hw_ani_control(ah,
 204                                     ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
 205                                     true);
 206                if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
 207                        ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
 208                                             aniState->firstepLevel + 1);
 209                return;
 210        } else {
 211                if ((conf->channel->band == IEEE80211_BAND_2GHZ) &&
 212                    !conf_is_ht(conf)) {
 213                        if (!aniState->ofdmWeakSigDetectOff)
 214                                ath9k_hw_ani_control(ah,
 215                                     ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
 216                                     false);
 217                        if (aniState->firstepLevel > 0)
 218                                ath9k_hw_ani_control(ah,
 219                                             ATH9K_ANI_FIRSTEP_LEVEL, 0);
 220                        return;
 221                }
 222        }
 223}
 224
 225static void ath9k_hw_ani_cck_err_trigger_old(struct ath_hw *ah)
 226{
 227        struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
 228        struct ar5416AniState *aniState;
 229        int32_t rssi;
 230
 231        aniState = &ah->curchan->ani;
 232        if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
 233                if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
 234                                         aniState->noiseImmunityLevel + 1)) {
 235                        return;
 236                }
 237        }
 238        if (ah->opmode == NL80211_IFTYPE_AP) {
 239                if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
 240                        ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
 241                                             aniState->firstepLevel + 1);
 242                }
 243                return;
 244        }
 245        rssi = BEACON_RSSI(ah);
 246        if (rssi > aniState->rssiThrLow) {
 247                if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
 248                        ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
 249                                             aniState->firstepLevel + 1);
 250        } else {
 251                if ((conf->channel->band == IEEE80211_BAND_2GHZ) &&
 252                    !conf_is_ht(conf)) {
 253                        if (aniState->firstepLevel > 0)
 254                                ath9k_hw_ani_control(ah,
 255                                             ATH9K_ANI_FIRSTEP_LEVEL, 0);
 256                }
 257        }
 258}
 259
 260/* Adjust the OFDM Noise Immunity Level */
 261static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel)
 262{
 263        struct ar5416AniState *aniState = &ah->curchan->ani;
 264        struct ath_common *common = ath9k_hw_common(ah);
 265        const struct ani_ofdm_level_entry *entry_ofdm;
 266        const struct ani_cck_level_entry *entry_cck;
 267
 268        aniState->noiseFloor = BEACON_RSSI(ah);
 269
 270        ath_print(common, ATH_DBG_ANI,
 271                  "**** ofdmlevel %d=>%d, rssi=%d[lo=%d hi=%d]\n",
 272                  aniState->ofdmNoiseImmunityLevel,
 273                  immunityLevel, aniState->noiseFloor,
 274                  aniState->rssiThrLow, aniState->rssiThrHigh);
 275
 276        aniState->ofdmNoiseImmunityLevel = immunityLevel;
 277
 278        entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel];
 279        entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel];
 280
 281        if (aniState->spurImmunityLevel != entry_ofdm->spur_immunity_level)
 282                ath9k_hw_ani_control(ah,
 283                                     ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
 284                                     entry_ofdm->spur_immunity_level);
 285
 286        if (aniState->firstepLevel != entry_ofdm->fir_step_level &&
 287            entry_ofdm->fir_step_level >= entry_cck->fir_step_level)
 288                ath9k_hw_ani_control(ah,
 289                                     ATH9K_ANI_FIRSTEP_LEVEL,
 290                                     entry_ofdm->fir_step_level);
 291
 292        if ((ah->opmode != NL80211_IFTYPE_STATION &&
 293             ah->opmode != NL80211_IFTYPE_ADHOC) ||
 294            aniState->noiseFloor <= aniState->rssiThrHigh) {
 295                if (aniState->ofdmWeakSigDetectOff)
 296                        /* force on ofdm weak sig detect */
 297                        ath9k_hw_ani_control(ah,
 298                                ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
 299                                             true);
 300                else if (aniState->ofdmWeakSigDetectOff ==
 301                         entry_ofdm->ofdm_weak_signal_on)
 302                        ath9k_hw_ani_control(ah,
 303                                ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
 304                                entry_ofdm->ofdm_weak_signal_on);
 305        }
 306}
 307
 308static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
 309{
 310        struct ar5416AniState *aniState;
 311
 312        if (!DO_ANI(ah))
 313                return;
 314
 315        if (!use_new_ani(ah)) {
 316                ath9k_hw_ani_ofdm_err_trigger_old(ah);
 317                return;
 318        }
 319
 320        aniState = &ah->curchan->ani;
 321
 322        if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL)
 323                ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1);
 324}
 325
 326/*
 327 * Set the ANI settings to match an CCK level.
 328 */
 329static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel)
 330{
 331        struct ar5416AniState *aniState = &ah->curchan->ani;
 332        struct ath_common *common = ath9k_hw_common(ah);
 333        const struct ani_ofdm_level_entry *entry_ofdm;
 334        const struct ani_cck_level_entry *entry_cck;
 335
 336        aniState->noiseFloor = BEACON_RSSI(ah);
 337        ath_print(common, ATH_DBG_ANI,
 338                  "**** ccklevel %d=>%d, rssi=%d[lo=%d hi=%d]\n",
 339                  aniState->cckNoiseImmunityLevel, immunityLevel,
 340                  aniState->noiseFloor, aniState->rssiThrLow,
 341                  aniState->rssiThrHigh);
 342
 343        if ((ah->opmode == NL80211_IFTYPE_STATION ||
 344             ah->opmode == NL80211_IFTYPE_ADHOC) &&
 345            aniState->noiseFloor <= aniState->rssiThrLow &&
 346            immunityLevel > ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI)
 347                immunityLevel = ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI;
 348
 349        aniState->cckNoiseImmunityLevel = immunityLevel;
 350
 351        entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel];
 352        entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel];
 353
 354        if (aniState->firstepLevel != entry_cck->fir_step_level &&
 355            entry_cck->fir_step_level >= entry_ofdm->fir_step_level)
 356                ath9k_hw_ani_control(ah,
 357                                     ATH9K_ANI_FIRSTEP_LEVEL,
 358                                     entry_cck->fir_step_level);
 359
 360        /* Skip MRC CCK for pre AR9003 families */
 361        if (!AR_SREV_9300_20_OR_LATER(ah))
 362                return;
 363
 364        if (aniState->mrcCCKOff == entry_cck->mrc_cck_on)
 365                ath9k_hw_ani_control(ah,
 366                                     ATH9K_ANI_MRC_CCK,
 367                                     entry_cck->mrc_cck_on);
 368}
 369
 370static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah)
 371{
 372        struct ar5416AniState *aniState;
 373
 374        if (!DO_ANI(ah))
 375                return;
 376
 377        if (!use_new_ani(ah)) {
 378                ath9k_hw_ani_cck_err_trigger_old(ah);
 379                return;
 380        }
 381
 382        aniState = &ah->curchan->ani;
 383
 384        if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL)
 385                ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1);
 386}
 387
 388static void ath9k_hw_ani_lower_immunity_old(struct ath_hw *ah)
 389{
 390        struct ar5416AniState *aniState;
 391        int32_t rssi;
 392
 393        aniState = &ah->curchan->ani;
 394
 395        if (ah->opmode == NL80211_IFTYPE_AP) {
 396                if (aniState->firstepLevel > 0) {
 397                        if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
 398                                                 aniState->firstepLevel - 1))
 399                                return;
 400                }
 401        } else {
 402                rssi = BEACON_RSSI(ah);
 403                if (rssi > aniState->rssiThrHigh) {
 404                        /* XXX: Handle me */
 405                } else if (rssi > aniState->rssiThrLow) {
 406                        if (aniState->ofdmWeakSigDetectOff) {
 407                                if (ath9k_hw_ani_control(ah,
 408                                         ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
 409                                         true) == true)
 410                                        return;
 411                        }
 412                        if (aniState->firstepLevel > 0) {
 413                                if (ath9k_hw_ani_control(ah,
 414                                         ATH9K_ANI_FIRSTEP_LEVEL,
 415                                         aniState->firstepLevel - 1) == true)
 416                                        return;
 417                        }
 418                } else {
 419                        if (aniState->firstepLevel > 0) {
 420                                if (ath9k_hw_ani_control(ah,
 421                                         ATH9K_ANI_FIRSTEP_LEVEL,
 422                                         aniState->firstepLevel - 1) == true)
 423                                        return;
 424                        }
 425                }
 426        }
 427
 428        if (aniState->spurImmunityLevel > 0) {
 429                if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
 430                                         aniState->spurImmunityLevel - 1))
 431                        return;
 432        }
 433
 434        if (aniState->noiseImmunityLevel > 0) {
 435                ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
 436                                     aniState->noiseImmunityLevel - 1);
 437                return;
 438        }
 439}
 440
 441/*
 442 * only lower either OFDM or CCK errors per turn
 443 * we lower the other one next time
 444 */
 445static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
 446{
 447        struct ar5416AniState *aniState;
 448
 449        aniState = &ah->curchan->ani;
 450
 451        if (!use_new_ani(ah)) {
 452                ath9k_hw_ani_lower_immunity_old(ah);
 453                return;
 454        }
 455
 456        /* lower OFDM noise immunity */
 457        if (aniState->ofdmNoiseImmunityLevel > 0 &&
 458            (aniState->ofdmsTurn || aniState->cckNoiseImmunityLevel == 0)) {
 459                ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel - 1);
 460                return;
 461        }
 462
 463        /* lower CCK noise immunity */
 464        if (aniState->cckNoiseImmunityLevel > 0)
 465                ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel - 1);
 466}
 467
 468static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning)
 469{
 470        struct ar5416AniState *aniState;
 471        struct ath9k_channel *chan = ah->curchan;
 472        struct ath_common *common = ath9k_hw_common(ah);
 473
 474        if (!DO_ANI(ah))
 475                return;
 476
 477        aniState = &ah->curchan->ani;
 478
 479        if (ah->opmode != NL80211_IFTYPE_STATION
 480            && ah->opmode != NL80211_IFTYPE_ADHOC) {
 481                ath_print(common, ATH_DBG_ANI,
 482                          "Reset ANI state opmode %u\n", ah->opmode);
 483                ah->stats.ast_ani_reset++;
 484
 485                if (ah->opmode == NL80211_IFTYPE_AP) {
 486                        /*
 487                         * ath9k_hw_ani_control() will only process items set on
 488                         * ah->ani_function
 489                         */
 490                        if (IS_CHAN_2GHZ(chan))
 491                                ah->ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL |
 492                                                    ATH9K_ANI_FIRSTEP_LEVEL);
 493                        else
 494                                ah->ani_function = 0;
 495                }
 496
 497                ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0);
 498                ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
 499                ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0);
 500                ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
 501                                     !ATH9K_ANI_USE_OFDM_WEAK_SIG);
 502                ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
 503                                     ATH9K_ANI_CCK_WEAK_SIG_THR);
 504
 505                ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) |
 506                                     ATH9K_RX_FILTER_PHYERR);
 507
 508                ath9k_ani_restart(ah);
 509                return;
 510        }
 511
 512        if (aniState->noiseImmunityLevel != 0)
 513                ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
 514                                     aniState->noiseImmunityLevel);
 515        if (aniState->spurImmunityLevel != 0)
 516                ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
 517                                     aniState->spurImmunityLevel);
 518        if (aniState->ofdmWeakSigDetectOff)
 519                ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
 520                                     !aniState->ofdmWeakSigDetectOff);
 521        if (aniState->cckWeakSigThreshold)
 522                ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
 523                                     aniState->cckWeakSigThreshold);
 524        if (aniState->firstepLevel != 0)
 525                ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
 526                                     aniState->firstepLevel);
 527
 528        ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) &
 529                             ~ATH9K_RX_FILTER_PHYERR);
 530        ath9k_ani_restart(ah);
 531
 532        ENABLE_REGWRITE_BUFFER(ah);
 533
 534        REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
 535        REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
 536
 537        REGWRITE_BUFFER_FLUSH(ah);
 538}
 539
 540/*
 541 * Restore the ANI parameters in the HAL and reset the statistics.
 542 * This routine should be called for every hardware reset and for
 543 * every channel change.
 544 */
 545void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
 546{
 547        struct ar5416AniState *aniState = &ah->curchan->ani;
 548        struct ath9k_channel *chan = ah->curchan;
 549        struct ath_common *common = ath9k_hw_common(ah);
 550
 551        if (!DO_ANI(ah))
 552                return;
 553
 554        if (!use_new_ani(ah))
 555                return ath9k_ani_reset_old(ah, is_scanning);
 556
 557        BUG_ON(aniState == NULL);
 558        ah->stats.ast_ani_reset++;
 559
 560        /* only allow a subset of functions in AP mode */
 561        if (ah->opmode == NL80211_IFTYPE_AP) {
 562                if (IS_CHAN_2GHZ(chan)) {
 563                        ah->ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL |
 564                                            ATH9K_ANI_FIRSTEP_LEVEL);
 565                        if (AR_SREV_9300_20_OR_LATER(ah))
 566                                ah->ani_function |= ATH9K_ANI_MRC_CCK;
 567                } else
 568                        ah->ani_function = 0;
 569        }
 570
 571        /* always allow mode (on/off) to be controlled */
 572        ah->ani_function |= ATH9K_ANI_MODE;
 573
 574        if (is_scanning ||
 575            (ah->opmode != NL80211_IFTYPE_STATION &&
 576             ah->opmode != NL80211_IFTYPE_ADHOC)) {
 577                /*
 578                 * If we're scanning or in AP mode, the defaults (ini)
 579                 * should be in place. For an AP we assume the historical
 580                 * levels for this channel are probably outdated so start
 581                 * from defaults instead.
 582                 */
 583                if (aniState->ofdmNoiseImmunityLevel !=
 584                    ATH9K_ANI_OFDM_DEF_LEVEL ||
 585                    aniState->cckNoiseImmunityLevel !=
 586                    ATH9K_ANI_CCK_DEF_LEVEL) {
 587                        ath_print(common, ATH_DBG_ANI,
 588                                  "Restore defaults: opmode %u "
 589                                  "chan %d Mhz/0x%x is_scanning=%d "
 590                                  "ofdm:%d cck:%d\n",
 591                                  ah->opmode,
 592                                  chan->channel,
 593                                  chan->channelFlags,
 594                                  is_scanning,
 595                                  aniState->ofdmNoiseImmunityLevel,
 596                                  aniState->cckNoiseImmunityLevel);
 597
 598                        ath9k_hw_set_ofdm_nil(ah, ATH9K_ANI_OFDM_DEF_LEVEL);
 599                        ath9k_hw_set_cck_nil(ah, ATH9K_ANI_CCK_DEF_LEVEL);
 600                }
 601        } else {
 602                /*
 603                 * restore historical levels for this channel
 604                 */
 605                ath_print(common, ATH_DBG_ANI,
 606                          "Restore history: opmode %u "
 607                          "chan %d Mhz/0x%x is_scanning=%d "
 608                          "ofdm:%d cck:%d\n",
 609                          ah->opmode,
 610                          chan->channel,
 611                          chan->channelFlags,
 612                          is_scanning,
 613                          aniState->ofdmNoiseImmunityLevel,
 614                          aniState->cckNoiseImmunityLevel);
 615
 616                        ath9k_hw_set_ofdm_nil(ah,
 617                                              aniState->ofdmNoiseImmunityLevel);
 618                        ath9k_hw_set_cck_nil(ah,
 619                                             aniState->cckNoiseImmunityLevel);
 620        }
 621
 622        /*
 623         * enable phy counters if hw supports or if not, enable phy
 624         * interrupts (so we can count each one)
 625         */
 626        ath9k_ani_restart(ah);
 627
 628        ENABLE_REGWRITE_BUFFER(ah);
 629
 630        REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
 631        REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
 632
 633        REGWRITE_BUFFER_FLUSH(ah);
 634}
 635
 636static bool ath9k_hw_ani_read_counters(struct ath_hw *ah)
 637{
 638        struct ath_common *common = ath9k_hw_common(ah);
 639        struct ar5416AniState *aniState = &ah->curchan->ani;
 640        u32 ofdm_base = 0;
 641        u32 cck_base = 0;
 642        u32 ofdmPhyErrCnt, cckPhyErrCnt;
 643        u32 phyCnt1, phyCnt2;
 644        int32_t listenTime;
 645
 646        ath_hw_cycle_counters_update(common);
 647        listenTime = ath_hw_get_listen_time(common);
 648
 649        if (listenTime <= 0) {
 650                ah->stats.ast_ani_lneg++;
 651                ath9k_ani_restart(ah);
 652                return false;
 653        }
 654
 655        if (!use_new_ani(ah)) {
 656                ofdm_base = AR_PHY_COUNTMAX - ah->config.ofdm_trig_high;
 657                cck_base = AR_PHY_COUNTMAX - ah->config.cck_trig_high;
 658        }
 659
 660        aniState->listenTime += listenTime;
 661
 662        ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
 663
 664        phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
 665        phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
 666
 667        if (!use_new_ani(ah) && (phyCnt1 < ofdm_base || phyCnt2 < cck_base)) {
 668                if (phyCnt1 < ofdm_base) {
 669                        ath_print(common, ATH_DBG_ANI,
 670                                  "phyCnt1 0x%x, resetting "
 671                                  "counter value to 0x%x\n",
 672                                  phyCnt1, ofdm_base);
 673                        REG_WRITE(ah, AR_PHY_ERR_1, ofdm_base);
 674                        REG_WRITE(ah, AR_PHY_ERR_MASK_1,
 675                                  AR_PHY_ERR_OFDM_TIMING);
 676                }
 677                if (phyCnt2 < cck_base) {
 678                        ath_print(common, ATH_DBG_ANI,
 679                                  "phyCnt2 0x%x, resetting "
 680                                  "counter value to 0x%x\n",
 681                                  phyCnt2, cck_base);
 682                        REG_WRITE(ah, AR_PHY_ERR_2, cck_base);
 683                        REG_WRITE(ah, AR_PHY_ERR_MASK_2,
 684                                  AR_PHY_ERR_CCK_TIMING);
 685                }
 686                return false;
 687        }
 688
 689        ofdmPhyErrCnt = phyCnt1 - ofdm_base;
 690        ah->stats.ast_ani_ofdmerrs +=
 691                ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
 692        aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
 693
 694        cckPhyErrCnt = phyCnt2 - cck_base;
 695        ah->stats.ast_ani_cckerrs +=
 696                cckPhyErrCnt - aniState->cckPhyErrCount;
 697        aniState->cckPhyErrCount = cckPhyErrCnt;
 698        return true;
 699}
 700
 701void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan)
 702{
 703        struct ar5416AniState *aniState;
 704        struct ath_common *common = ath9k_hw_common(ah);
 705        u32 ofdmPhyErrRate, cckPhyErrRate;
 706
 707        if (!DO_ANI(ah))
 708                return;
 709
 710        aniState = &ah->curchan->ani;
 711        if (WARN_ON(!aniState))
 712                return;
 713
 714        if (!ath9k_hw_ani_read_counters(ah))
 715                return;
 716
 717        ofdmPhyErrRate = aniState->ofdmPhyErrCount * 1000 /
 718                         aniState->listenTime;
 719        cckPhyErrRate =  aniState->cckPhyErrCount * 1000 /
 720                         aniState->listenTime;
 721
 722        ath_print(common, ATH_DBG_ANI,
 723                  "listenTime=%d OFDM:%d errs=%d/s CCK:%d "
 724                  "errs=%d/s ofdm_turn=%d\n",
 725                  aniState->listenTime,
 726                  aniState->ofdmNoiseImmunityLevel,
 727                  ofdmPhyErrRate, aniState->cckNoiseImmunityLevel,
 728                  cckPhyErrRate, aniState->ofdmsTurn);
 729
 730        if (aniState->listenTime > 5 * ah->aniperiod) {
 731                if (ofdmPhyErrRate <= ah->config.ofdm_trig_low &&
 732                    cckPhyErrRate <= ah->config.cck_trig_low) {
 733                        ath9k_hw_ani_lower_immunity(ah);
 734                        aniState->ofdmsTurn = !aniState->ofdmsTurn;
 735                }
 736                ath9k_ani_restart(ah);
 737        } else if (aniState->listenTime > ah->aniperiod) {
 738                /* check to see if need to raise immunity */
 739                if (ofdmPhyErrRate > ah->config.ofdm_trig_high &&
 740                    (cckPhyErrRate <= ah->config.cck_trig_high ||
 741                     aniState->ofdmsTurn)) {
 742                        ath9k_hw_ani_ofdm_err_trigger(ah);
 743                        ath9k_ani_restart(ah);
 744                        aniState->ofdmsTurn = false;
 745                } else if (cckPhyErrRate > ah->config.cck_trig_high) {
 746                        ath9k_hw_ani_cck_err_trigger(ah);
 747                        ath9k_ani_restart(ah);
 748                        aniState->ofdmsTurn = true;
 749                }
 750        }
 751}
 752EXPORT_SYMBOL(ath9k_hw_ani_monitor);
 753
 754void ath9k_enable_mib_counters(struct ath_hw *ah)
 755{
 756        struct ath_common *common = ath9k_hw_common(ah);
 757
 758        ath_print(common, ATH_DBG_ANI, "Enable MIB counters\n");
 759
 760        ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
 761
 762        ENABLE_REGWRITE_BUFFER(ah);
 763
 764        REG_WRITE(ah, AR_FILT_OFDM, 0);
 765        REG_WRITE(ah, AR_FILT_CCK, 0);
 766        REG_WRITE(ah, AR_MIBC,
 767                  ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS)
 768                  & 0x0f);
 769        REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
 770        REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
 771
 772        REGWRITE_BUFFER_FLUSH(ah);
 773}
 774
 775/* Freeze the MIB counters, get the stats and then clear them */
 776void ath9k_hw_disable_mib_counters(struct ath_hw *ah)
 777{
 778        struct ath_common *common = ath9k_hw_common(ah);
 779
 780        ath_print(common, ATH_DBG_ANI, "Disable MIB counters\n");
 781
 782        REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC);
 783        ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
 784        REG_WRITE(ah, AR_MIBC, AR_MIBC_CMC);
 785        REG_WRITE(ah, AR_FILT_OFDM, 0);
 786        REG_WRITE(ah, AR_FILT_CCK, 0);
 787}
 788EXPORT_SYMBOL(ath9k_hw_disable_mib_counters);
 789
 790/*
 791 * Process a MIB interrupt.  We may potentially be invoked because
 792 * any of the MIB counters overflow/trigger so don't assume we're
 793 * here because a PHY error counter triggered.
 794 */
 795void ath9k_hw_proc_mib_event(struct ath_hw *ah)
 796{
 797        u32 phyCnt1, phyCnt2;
 798
 799        /* Reset these counters regardless */
 800        REG_WRITE(ah, AR_FILT_OFDM, 0);
 801        REG_WRITE(ah, AR_FILT_CCK, 0);
 802        if (!(REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING))
 803                REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);
 804
 805        /* Clear the mib counters and save them in the stats */
 806        ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
 807
 808        if (!DO_ANI(ah)) {
 809                /*
 810                 * We must always clear the interrupt cause by
 811                 * resetting the phy error regs.
 812                 */
 813                REG_WRITE(ah, AR_PHY_ERR_1, 0);
 814                REG_WRITE(ah, AR_PHY_ERR_2, 0);
 815                return;
 816        }
 817
 818        /* NB: these are not reset-on-read */
 819        phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
 820        phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
 821        if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
 822            ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
 823
 824                if (!use_new_ani(ah))
 825                        ath9k_hw_ani_read_counters(ah);
 826
 827                /* NB: always restart to insure the h/w counters are reset */
 828                ath9k_ani_restart(ah);
 829        }
 830}
 831EXPORT_SYMBOL(ath9k_hw_proc_mib_event);
 832
 833void ath9k_hw_ani_setup(struct ath_hw *ah)
 834{
 835        int i;
 836
 837        const int totalSizeDesired[] = { -55, -55, -55, -55, -62 };
 838        const int coarseHigh[] = { -14, -14, -14, -14, -12 };
 839        const int coarseLow[] = { -64, -64, -64, -64, -70 };
 840        const int firpwr[] = { -78, -78, -78, -78, -80 };
 841
 842        for (i = 0; i < 5; i++) {
 843                ah->totalSizeDesired[i] = totalSizeDesired[i];
 844                ah->coarse_high[i] = coarseHigh[i];
 845                ah->coarse_low[i] = coarseLow[i];
 846                ah->firpwr[i] = firpwr[i];
 847        }
 848}
 849
 850void ath9k_hw_ani_init(struct ath_hw *ah)
 851{
 852        struct ath_common *common = ath9k_hw_common(ah);
 853        int i;
 854
 855        ath_print(common, ATH_DBG_ANI, "Initialize ANI\n");
 856
 857        if (use_new_ani(ah)) {
 858                ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_NEW;
 859                ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_NEW;
 860
 861                ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH_NEW;
 862                ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW_NEW;
 863        } else {
 864                ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_OLD;
 865                ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_OLD;
 866
 867                ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH_OLD;
 868                ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW_OLD;
 869        }
 870
 871        for (i = 0; i < ARRAY_SIZE(ah->channels); i++) {
 872                struct ath9k_channel *chan = &ah->channels[i];
 873                struct ar5416AniState *ani = &chan->ani;
 874
 875                if (use_new_ani(ah)) {
 876                        ani->spurImmunityLevel =
 877                                ATH9K_ANI_SPUR_IMMUNE_LVL_NEW;
 878
 879                        ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW;
 880
 881                        if (AR_SREV_9300_20_OR_LATER(ah))
 882                                ani->mrcCCKOff =
 883                                        !ATH9K_ANI_ENABLE_MRC_CCK;
 884                        else
 885                                ani->mrcCCKOff = true;
 886
 887                        ani->ofdmsTurn = true;
 888                } else {
 889                        ani->spurImmunityLevel =
 890                                ATH9K_ANI_SPUR_IMMUNE_LVL_OLD;
 891                        ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_OLD;
 892
 893                        ani->cckWeakSigThreshold =
 894                                ATH9K_ANI_CCK_WEAK_SIG_THR;
 895                }
 896
 897                ani->rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH;
 898                ani->rssiThrLow = ATH9K_ANI_RSSI_THR_LOW;
 899                ani->ofdmWeakSigDetectOff =
 900                        !ATH9K_ANI_USE_OFDM_WEAK_SIG;
 901                ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;
 902        }
 903
 904        /*
 905         * since we expect some ongoing maintenance on the tables, let's sanity
 906         * check here default level should not modify INI setting.
 907         */
 908        if (use_new_ani(ah)) {
 909                const struct ani_ofdm_level_entry *entry_ofdm;
 910                const struct ani_cck_level_entry *entry_cck;
 911
 912                entry_ofdm = &ofdm_level_table[ATH9K_ANI_OFDM_DEF_LEVEL];
 913                entry_cck = &cck_level_table[ATH9K_ANI_CCK_DEF_LEVEL];
 914
 915                ah->aniperiod = ATH9K_ANI_PERIOD_NEW;
 916                ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL_NEW;
 917        } else {
 918                ah->aniperiod = ATH9K_ANI_PERIOD_OLD;
 919                ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL_OLD;
 920        }
 921
 922        if (ah->config.enable_ani)
 923                ah->proc_phyerr |= HAL_PROCESS_ANI;
 924
 925        ath9k_ani_restart(ah);
 926        ath9k_enable_mib_counters(ah);
 927}
 928
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.