linux/drivers/net/wireless/ath/ath9k/calib.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2008-2011 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 "hw.h"
  18#include "hw-ops.h"
  19#include <linux/export.h>
  20
  21/* Common calibration code */
  22
  23
  24static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
  25{
  26        int16_t nfval;
  27        int16_t sort[ATH9K_NF_CAL_HIST_MAX];
  28        int i, j;
  29
  30        for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++)
  31                sort[i] = nfCalBuffer[i];
  32
  33        for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) {
  34                for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) {
  35                        if (sort[j] > sort[j - 1]) {
  36                                nfval = sort[j];
  37                                sort[j] = sort[j - 1];
  38                                sort[j - 1] = nfval;
  39                        }
  40                }
  41        }
  42        nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1];
  43
  44        return nfval;
  45}
  46
  47static struct ath_nf_limits *ath9k_hw_get_nf_limits(struct ath_hw *ah,
  48                                                    struct ath9k_channel *chan)
  49{
  50        struct ath_nf_limits *limit;
  51
  52        if (!chan || IS_CHAN_2GHZ(chan))
  53                limit = &ah->nf_2g;
  54        else
  55                limit = &ah->nf_5g;
  56
  57        return limit;
  58}
  59
  60static s16 ath9k_hw_get_default_nf(struct ath_hw *ah,
  61                                   struct ath9k_channel *chan,
  62                                   int chain)
  63{
  64        s16 calib_nf = ath9k_hw_get_nf_limits(ah, chan)->cal[chain];
  65
  66        if (calib_nf)
  67                return calib_nf;
  68        else
  69                return ath9k_hw_get_nf_limits(ah, chan)->nominal;
  70}
  71
  72s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan,
  73                           s16 nf)
  74{
  75        s8 noise = ATH_DEFAULT_NOISE_FLOOR;
  76
  77        if (nf) {
  78                s8 delta = nf - ATH9K_NF_CAL_NOISE_THRESH -
  79                           ath9k_hw_get_default_nf(ah, chan, 0);
  80                if (delta > 0)
  81                        noise += delta;
  82        }
  83        return noise;
  84}
  85EXPORT_SYMBOL(ath9k_hw_getchan_noise);
  86
  87static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah,
  88                                              struct ath9k_hw_cal_data *cal,
  89                                              int16_t *nfarray)
  90{
  91        struct ath_common *common = ath9k_hw_common(ah);
  92        struct ath_nf_limits *limit;
  93        struct ath9k_nfcal_hist *h;
  94        bool high_nf_mid = false;
  95        u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
  96        int i;
  97
  98        h = cal->nfCalHist;
  99        limit = ath9k_hw_get_nf_limits(ah, ah->curchan);
 100
 101        for (i = 0; i < NUM_NF_READINGS; i++) {
 102                if (!(chainmask & (1 << i)) ||
 103                    ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(ah->curchan)))
 104                        continue;
 105
 106                h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
 107
 108                if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX)
 109                        h[i].currIndex = 0;
 110
 111                if (h[i].invalidNFcount > 0) {
 112                        h[i].invalidNFcount--;
 113                        h[i].privNF = nfarray[i];
 114                } else {
 115                        h[i].privNF =
 116                                ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer);
 117                }
 118
 119                if (!h[i].privNF)
 120                        continue;
 121
 122                if (h[i].privNF > limit->max) {
 123                        high_nf_mid = true;
 124
 125                        ath_dbg(common, CALIBRATE,
 126                                "NFmid[%d] (%d) > MAX (%d), %s\n",
 127                                i, h[i].privNF, limit->max,
 128                                (test_bit(NFCAL_INTF, &cal->cal_flags) ?
 129                                 "not corrected (due to interference)" :
 130                                 "correcting to MAX"));
 131
 132                        /*
 133                         * Normally we limit the average noise floor by the
 134                         * hardware specific maximum here. However if we have
 135                         * encountered stuck beacons because of interference,
 136                         * we bypass this limit here in order to better deal
 137                         * with our environment.
 138                         */
 139                        if (!test_bit(NFCAL_INTF, &cal->cal_flags))
 140                                h[i].privNF = limit->max;
 141                }
 142        }
 143
 144        /*
 145         * If the noise floor seems normal for all chains, assume that
 146         * there is no significant interference in the environment anymore.
 147         * Re-enable the enforcement of the NF maximum again.
 148         */
 149        if (!high_nf_mid)
 150                clear_bit(NFCAL_INTF, &cal->cal_flags);
 151}
 152
 153static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah,
 154                                   enum nl80211_band band,
 155                                   int16_t *nft)
 156{
 157        switch (band) {
 158        case NL80211_BAND_5GHZ:
 159                *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_5);
 160                break;
 161        case NL80211_BAND_2GHZ:
 162                *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_2);
 163                break;
 164        default:
 165                BUG_ON(1);
 166                return false;
 167        }
 168
 169        return true;
 170}
 171
 172void ath9k_hw_reset_calibration(struct ath_hw *ah,
 173                                struct ath9k_cal_list *currCal)
 174{
 175        int i;
 176
 177        ath9k_hw_setup_calibration(ah, currCal);
 178
 179        currCal->calState = CAL_RUNNING;
 180
 181        for (i = 0; i < AR5416_MAX_CHAINS; i++) {
 182                ah->meas0.sign[i] = 0;
 183                ah->meas1.sign[i] = 0;
 184                ah->meas2.sign[i] = 0;
 185                ah->meas3.sign[i] = 0;
 186        }
 187
 188        ah->cal_samples = 0;
 189}
 190
 191/* This is done for the currently configured channel */
 192bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
 193{
 194        struct ath_common *common = ath9k_hw_common(ah);
 195        struct ath9k_cal_list *currCal = ah->cal_list_curr;
 196
 197        if (!ah->caldata)
 198                return true;
 199
 200        if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah))
 201                return true;
 202
 203        if (currCal == NULL)
 204                return true;
 205
 206        if (currCal->calState != CAL_DONE) {
 207                ath_dbg(common, CALIBRATE, "Calibration state incorrect, %d\n",
 208                        currCal->calState);
 209                return true;
 210        }
 211
 212        if (!(ah->supp_cals & currCal->calData->calType))
 213                return true;
 214
 215        ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n",
 216                currCal->calData->calType, ah->curchan->chan->center_freq);
 217
 218        ah->caldata->CalValid &= ~currCal->calData->calType;
 219        currCal->calState = CAL_WAITING;
 220
 221        return false;
 222}
 223EXPORT_SYMBOL(ath9k_hw_reset_calvalid);
 224
 225void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update)
 226{
 227        if (ah->caldata)
 228                set_bit(NFCAL_PENDING, &ah->caldata->cal_flags);
 229
 230        REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
 231                    AR_PHY_AGC_CONTROL_ENABLE_NF);
 232
 233        if (update)
 234                REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
 235                    AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
 236        else
 237                REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
 238                    AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
 239
 240        REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
 241}
 242
 243int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
 244{
 245        struct ath9k_nfcal_hist *h = NULL;
 246        unsigned i, j;
 247        u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
 248        struct ath_common *common = ath9k_hw_common(ah);
 249        s16 default_nf = ath9k_hw_get_nf_limits(ah, chan)->nominal;
 250        u32 bb_agc_ctl = REG_READ(ah, AR_PHY_AGC_CONTROL);
 251
 252        if (ah->caldata)
 253                h = ah->caldata->nfCalHist;
 254
 255        ENABLE_REG_RMW_BUFFER(ah);
 256        for (i = 0; i < NUM_NF_READINGS; i++) {
 257                if (chainmask & (1 << i)) {
 258                        s16 nfval;
 259
 260                        if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan))
 261                                continue;
 262
 263                        if (ah->nf_override)
 264                                nfval = ah->nf_override;
 265                        else if (h)
 266                                nfval = h[i].privNF;
 267                        else {
 268                                /* Try to get calibrated noise floor value */
 269                                nfval =
 270                                    ath9k_hw_get_nf_limits(ah, chan)->cal[i];
 271                                if (nfval > -60 || nfval < -127)
 272                                        nfval = default_nf;
 273                        }
 274
 275                        REG_RMW(ah, ah->nf_regs[i],
 276                                (((u32) nfval << 1) & 0x1ff), 0x1ff);
 277                }
 278        }
 279
 280        /*
 281         * stop NF cal if ongoing to ensure NF load completes immediately
 282         * (or after end rx/tx frame if ongoing)
 283         */
 284        if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) {
 285                REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
 286                REG_RMW_BUFFER_FLUSH(ah);
 287                ENABLE_REG_RMW_BUFFER(ah);
 288        }
 289
 290        /*
 291         * Load software filtered NF value into baseband internal minCCApwr
 292         * variable.
 293         */
 294        REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
 295                    AR_PHY_AGC_CONTROL_ENABLE_NF);
 296        REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
 297                    AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
 298        REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
 299        REG_RMW_BUFFER_FLUSH(ah);
 300
 301        /*
 302         * Wait for load to complete, should be fast, a few 10s of us.
 303         * The max delay was changed from an original 250us to 22.2 msec.
 304         * This would increase timeout to the longest possible frame
 305         * (11n max length 22.1 msec)
 306         */
 307        for (j = 0; j < 22200; j++) {
 308                if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
 309                              AR_PHY_AGC_CONTROL_NF) == 0)
 310                        break;
 311                udelay(10);
 312        }
 313
 314        /*
 315         * Restart NF so it can continue.
 316         */
 317        if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) {
 318                ENABLE_REG_RMW_BUFFER(ah);
 319                if (bb_agc_ctl & AR_PHY_AGC_CONTROL_ENABLE_NF)
 320                        REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
 321                                    AR_PHY_AGC_CONTROL_ENABLE_NF);
 322                if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NO_UPDATE_NF)
 323                        REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
 324                                    AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
 325                REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
 326                REG_RMW_BUFFER_FLUSH(ah);
 327        }
 328
 329        /*
 330         * We timed out waiting for the noisefloor to load, probably due to an
 331         * in-progress rx. Simply return here and allow the load plenty of time
 332         * to complete before the next calibration interval.  We need to avoid
 333         * trying to load -50 (which happens below) while the previous load is
 334         * still in progress as this can cause rx deafness. Instead by returning
 335         * here, the baseband nf cal will just be capped by our present
 336         * noisefloor until the next calibration timer.
 337         */
 338        if (j == 22200) {
 339                ath_dbg(common, ANY,
 340                        "Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n",
 341                        REG_READ(ah, AR_PHY_AGC_CONTROL));
 342                return -ETIMEDOUT;
 343        }
 344
 345        /*
 346         * Restore maxCCAPower register parameter again so that we're not capped
 347         * by the median we just loaded.  This will be initial (and max) value
 348         * of next noise floor calibration the baseband does.
 349         */
 350        ENABLE_REG_RMW_BUFFER(ah);
 351        for (i = 0; i < NUM_NF_READINGS; i++) {
 352                if (chainmask & (1 << i)) {
 353                        if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan))
 354                                continue;
 355
 356                        REG_RMW(ah, ah->nf_regs[i],
 357                                        (((u32) (-50) << 1) & 0x1ff), 0x1ff);
 358                }
 359        }
 360        REG_RMW_BUFFER_FLUSH(ah);
 361
 362        return 0;
 363}
 364EXPORT_SYMBOL(ath9k_hw_loadnf);
 365
 366
 367static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf)
 368{
 369        struct ath_common *common = ath9k_hw_common(ah);
 370        struct ath_nf_limits *limit;
 371        int i;
 372
 373        if (IS_CHAN_2GHZ(ah->curchan))
 374                limit = &ah->nf_2g;
 375        else
 376                limit = &ah->nf_5g;
 377
 378        for (i = 0; i < NUM_NF_READINGS; i++) {
 379                if (!nf[i])
 380                        continue;
 381
 382                ath_dbg(common, CALIBRATE,
 383                        "NF calibrated [%s] [chain %d] is %d\n",
 384                        (i >= 3 ? "ext" : "ctl"), i % 3, nf[i]);
 385
 386                if (nf[i] > limit->max) {
 387                        ath_dbg(common, CALIBRATE,
 388                                "NF[%d] (%d) > MAX (%d), correcting to MAX\n",
 389                                i, nf[i], limit->max);
 390                        nf[i] = limit->max;
 391                } else if (nf[i] < limit->min) {
 392                        ath_dbg(common, CALIBRATE,
 393                                "NF[%d] (%d) < MIN (%d), correcting to NOM\n",
 394                                i, nf[i], limit->min);
 395                        nf[i] = limit->nominal;
 396                }
 397        }
 398}
 399
 400bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
 401{
 402        struct ath_common *common = ath9k_hw_common(ah);
 403        int16_t nf, nfThresh;
 404        int16_t nfarray[NUM_NF_READINGS] = { 0 };
 405        struct ath9k_nfcal_hist *h;
 406        struct ieee80211_channel *c = chan->chan;
 407        struct ath9k_hw_cal_data *caldata = ah->caldata;
 408
 409        if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
 410                ath_dbg(common, CALIBRATE,
 411                        "NF did not complete in calibration window\n");
 412                return false;
 413        }
 414
 415        ath9k_hw_do_getnf(ah, nfarray);
 416        ath9k_hw_nf_sanitize(ah, nfarray);
 417        nf = nfarray[0];
 418        if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh)
 419            && nf > nfThresh) {
 420                ath_dbg(common, CALIBRATE,
 421                        "noise floor failed detected; detected %d, threshold %d\n",
 422                        nf, nfThresh);
 423        }
 424
 425        if (!caldata) {
 426                chan->noisefloor = nf;
 427                return false;
 428        }
 429
 430        h = caldata->nfCalHist;
 431        clear_bit(NFCAL_PENDING, &caldata->cal_flags);
 432        ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray);
 433        chan->noisefloor = h[0].privNF;
 434        ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor);
 435        return true;
 436}
 437EXPORT_SYMBOL(ath9k_hw_getnf);
 438
 439void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
 440                                  struct ath9k_channel *chan)
 441{
 442        struct ath9k_nfcal_hist *h;
 443        int i, j, k = 0;
 444
 445        ah->caldata->channel = chan->channel;
 446        ah->caldata->channelFlags = chan->channelFlags;
 447        h = ah->caldata->nfCalHist;
 448        for (i = 0; i < NUM_NF_READINGS; i++) {
 449                h[i].currIndex = 0;
 450                h[i].privNF = ath9k_hw_get_default_nf(ah, chan, k);
 451                h[i].invalidNFcount = AR_PHY_CCA_FILTERWINDOW_LENGTH;
 452                for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++)
 453                        h[i].nfCalBuffer[j] = h[i].privNF;
 454                if (++k >= AR5416_MAX_CHAINS)
 455                        k = 0;
 456        }
 457}
 458
 459
 460void ath9k_hw_bstuck_nfcal(struct ath_hw *ah)
 461{
 462        struct ath9k_hw_cal_data *caldata = ah->caldata;
 463
 464        if (unlikely(!caldata))
 465                return;
 466
 467        /*
 468         * If beacons are stuck, the most likely cause is interference.
 469         * Triggering a noise floor calibration at this point helps the
 470         * hardware adapt to a noisy environment much faster.
 471         * To ensure that we recover from stuck beacons quickly, let
 472         * the baseband update the internal NF value itself, similar to
 473         * what is being done after a full reset.
 474         */
 475        if (!test_bit(NFCAL_PENDING, &caldata->cal_flags))
 476                ath9k_hw_start_nfcal(ah, true);
 477        else if (!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF))
 478                ath9k_hw_getnf(ah, ah->curchan);
 479
 480        set_bit(NFCAL_INTF, &caldata->cal_flags);
 481}
 482EXPORT_SYMBOL(ath9k_hw_bstuck_nfcal);
 483
 484