iPXE
ath9k_calib.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008-2011 Atheros Communications Inc.
00003  *
00004  * Modified for iPXE by Scott K Logan <logans@cottsay.net> July 2011
00005  * Original from Linux kernel 3.0.1
00006  *
00007  * Permission to use, copy, modify, and/or distribute this software for any
00008  * purpose with or without fee is hereby granted, provided that the above
00009  * copyright notice and this permission notice appear in all copies.
00010  *
00011  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
00012  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00013  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
00014  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00015  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00016  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
00017  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00018  */
00019 
00020 #include "hw.h"
00021 #include "hw-ops.h"
00022 
00023 /* Common calibration code */
00024 
00025 #define ATH9K_NF_TOO_HIGH       -60
00026 
00027 static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
00028 {
00029         int16_t nfval;
00030         int16_t sort[ATH9K_NF_CAL_HIST_MAX];
00031         int i, j;
00032 
00033         for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++)
00034                 sort[i] = nfCalBuffer[i];
00035 
00036         for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) {
00037                 for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) {
00038                         if (sort[j] > sort[j - 1]) {
00039                                 nfval = sort[j];
00040                                 sort[j] = sort[j - 1];
00041                                 sort[j - 1] = nfval;
00042                         }
00043                 }
00044         }
00045         nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1];
00046 
00047         return nfval;
00048 }
00049 
00050 static struct ath_nf_limits *ath9k_hw_get_nf_limits(struct ath_hw *ah,
00051                                                     struct ath9k_channel *chan)
00052 {
00053         struct ath_nf_limits *limit;
00054 
00055         if (!chan || IS_CHAN_2GHZ(chan))
00056                 limit = &ah->nf_2g;
00057         else
00058                 limit = &ah->nf_5g;
00059 
00060         return limit;
00061 }
00062 
00063 static s16 ath9k_hw_get_default_nf(struct ath_hw *ah,
00064                                    struct ath9k_channel *chan)
00065 {
00066         return ath9k_hw_get_nf_limits(ah, chan)->nominal;
00067 }
00068 
00069 
00070 static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah,
00071                                               struct ath9k_hw_cal_data *cal,
00072                                               int16_t *nfarray)
00073 {
00074         struct ath_nf_limits *limit;
00075         struct ath9k_nfcal_hist *h;
00076         int high_nf_mid = 0;
00077         u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
00078         int i;
00079 
00080         h = cal->nfCalHist;
00081         limit = ath9k_hw_get_nf_limits(ah, ah->curchan);
00082 
00083         for (i = 0; i < NUM_NF_READINGS; i++) {
00084                 if (!(chainmask & (1 << i)) ||
00085                     (i >= AR5416_MAX_CHAINS))
00086                         continue;
00087 
00088                 h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
00089 
00090                 if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX)
00091                         h[i].currIndex = 0;
00092 
00093                 if (h[i].invalidNFcount > 0) {
00094                         h[i].invalidNFcount--;
00095                         h[i].privNF = nfarray[i];
00096                 } else {
00097                         h[i].privNF =
00098                                 ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer);
00099                 }
00100 
00101                 if (!h[i].privNF)
00102                         continue;
00103 
00104                 if (h[i].privNF > limit->max) {
00105                         high_nf_mid = 1;
00106 
00107                         DBG2("ath9k: "
00108                                 "NFmid[%d] (%d) > MAX (%d), %s\n",
00109                                 i, h[i].privNF, limit->max,
00110                                 (cal->nfcal_interference ?
00111                                  "not corrected (due to interference)" :
00112                                  "correcting to MAX"));
00113 
00114                         /*
00115                          * Normally we limit the average noise floor by the
00116                          * hardware specific maximum here. However if we have
00117                          * encountered stuck beacons because of interference,
00118                          * we bypass this limit here in order to better deal
00119                          * with our environment.
00120                          */
00121                         if (!cal->nfcal_interference)
00122                                 h[i].privNF = limit->max;
00123                 }
00124         }
00125 
00126         /*
00127          * If the noise floor seems normal for all chains, assume that
00128          * there is no significant interference in the environment anymore.
00129          * Re-enable the enforcement of the NF maximum again.
00130          */
00131         if (!high_nf_mid)
00132                 cal->nfcal_interference = 0;
00133 }
00134 
00135 static int ath9k_hw_get_nf_thresh(struct ath_hw *ah,
00136                                    int band,
00137                                    int16_t *nft)
00138 {
00139         switch (band) {
00140         case NET80211_BAND_5GHZ:
00141                 *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_5);
00142                 break;
00143         case NET80211_BAND_2GHZ:
00144                 *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_2);
00145                 break;
00146         default:
00147                 return 0;
00148         }
00149 
00150         return 1;
00151 }
00152 
00153 void ath9k_hw_reset_calibration(struct ath_hw *ah,
00154                                 struct ath9k_cal_list *currCal)
00155 {
00156         int i;
00157 
00158         ath9k_hw_setup_calibration(ah, currCal);
00159 
00160         currCal->calState = CAL_RUNNING;
00161 
00162         for (i = 0; i < AR5416_MAX_CHAINS; i++) {
00163                 ah->meas0.sign[i] = 0;
00164                 ah->meas1.sign[i] = 0;
00165                 ah->meas2.sign[i] = 0;
00166                 ah->meas3.sign[i] = 0;
00167         }
00168 
00169         ah->cal_samples = 0;
00170 }
00171 
00172 /* This is done for the currently configured channel */
00173 int ath9k_hw_reset_calvalid(struct ath_hw *ah)
00174 {
00175         struct ath9k_cal_list *currCal = ah->cal_list_curr;
00176 
00177         if (!ah->caldata)
00178                 return 1;
00179 
00180         if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah))
00181                 return 1;
00182 
00183         if (currCal == NULL)
00184                 return 1;
00185 
00186         if (currCal->calState != CAL_DONE) {
00187                 DBG("ath9k: "
00188                         "Calibration state incorrect, %d\n",
00189                         currCal->calState);
00190                 return 1;
00191         }
00192 
00193         if (!(ah->supp_cals & currCal->calData->calType))
00194                 return 1;
00195 
00196         DBG("ath9k: "
00197                 "Resetting Cal %d state for channel %d\n",
00198                 currCal->calData->calType, (ah->dev->channels + ah->dev->channel)->center_freq);
00199 
00200         ah->caldata->CalValid &= ~currCal->calData->calType;
00201         currCal->calState = CAL_WAITING;
00202 
00203         return 0;
00204 }
00205 
00206 void ath9k_hw_start_nfcal(struct ath_hw *ah, int update)
00207 {
00208         if (ah->caldata)
00209                 ah->caldata->nfcal_pending = 1;
00210 
00211         REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
00212                     AR_PHY_AGC_CONTROL_ENABLE_NF);
00213 
00214         if (update)
00215                 REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
00216                     AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
00217         else
00218                 REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
00219                     AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
00220 
00221         REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
00222 }
00223 
00224 void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
00225 {
00226         struct ath9k_nfcal_hist *h = NULL;
00227         unsigned i, j;
00228         int32_t val;
00229         u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
00230         s16 default_nf = ath9k_hw_get_default_nf(ah, chan);
00231 
00232         if (ah->caldata)
00233                 h = ah->caldata->nfCalHist;
00234 
00235         for (i = 0; i < NUM_NF_READINGS; i++) {
00236                 if (chainmask & (1 << i)) {
00237                         s16 nfval;
00238 
00239                         if (i >= AR5416_MAX_CHAINS)
00240                                 continue;
00241 
00242                         if (h)
00243                                 nfval = h[i].privNF;
00244                         else
00245                                 nfval = default_nf;
00246 
00247                         val = REG_READ(ah, ah->nf_regs[i]);
00248                         val &= 0xFFFFFE00;
00249                         val |= (((u32) nfval << 1) & 0x1ff);
00250                         REG_WRITE(ah, ah->nf_regs[i], val);
00251                 }
00252         }
00253 
00254         /*
00255          * Load software filtered NF value into baseband internal minCCApwr
00256          * variable.
00257          */
00258         REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
00259                     AR_PHY_AGC_CONTROL_ENABLE_NF);
00260         REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
00261                     AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
00262         REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
00263 
00264         /*
00265          * Wait for load to complete, should be fast, a few 10s of us.
00266          * The max delay was changed from an original 250us to 10000us
00267          * since 250us often results in NF load timeout and causes deaf
00268          * condition during stress testing 12/12/2009
00269          */
00270         for (j = 0; j < 10000; j++) {
00271                 if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
00272                      AR_PHY_AGC_CONTROL_NF) == 0)
00273                         break;
00274                 udelay(10);
00275         }
00276 
00277         /*
00278          * We timed out waiting for the noisefloor to load, probably due to an
00279          * in-progress rx. Simply return here and allow the load plenty of time
00280          * to complete before the next calibration interval.  We need to avoid
00281          * trying to load -50 (which happens below) while the previous load is
00282          * still in progress as this can cause rx deafness. Instead by returning
00283          * here, the baseband nf cal will just be capped by our present
00284          * noisefloor until the next calibration timer.
00285          */
00286         if (j == 10000) {
00287                 DBG("ath9k: "
00288                         "Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n",
00289                         REG_READ(ah, AR_PHY_AGC_CONTROL));
00290                 return;
00291         }
00292 
00293         /*
00294          * Restore maxCCAPower register parameter again so that we're not capped
00295          * by the median we just loaded.  This will be initial (and max) value
00296          * of next noise floor calibration the baseband does.
00297          */
00298         ENABLE_REGWRITE_BUFFER(ah);
00299         for (i = 0; i < NUM_NF_READINGS; i++) {
00300                 if (chainmask & (1 << i)) {
00301                         if (i >= AR5416_MAX_CHAINS)
00302                                 continue;
00303 
00304                         val = REG_READ(ah, ah->nf_regs[i]);
00305                         val &= 0xFFFFFE00;
00306                         val |= (((u32) (-50) << 1) & 0x1ff);
00307                         REG_WRITE(ah, ah->nf_regs[i], val);
00308                 }
00309         }
00310         REGWRITE_BUFFER_FLUSH(ah);
00311 }
00312 
00313 
00314 static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf)
00315 {
00316         struct ath_nf_limits *limit;
00317         int i;
00318 
00319         if (IS_CHAN_2GHZ(ah->curchan))
00320                 limit = &ah->nf_2g;
00321         else
00322                 limit = &ah->nf_5g;
00323 
00324         for (i = 0; i < NUM_NF_READINGS; i++) {
00325                 if (!nf[i])
00326                         continue;
00327 
00328                 DBG2("ath9k: "
00329                         "NF calibrated [%s] [chain %d] is %d\n",
00330                         (i >= 3 ? "ext" : "ctl"), i % 3, nf[i]);
00331 
00332                 if (nf[i] > ATH9K_NF_TOO_HIGH) {
00333                         DBG("ath9k: "
00334                                 "NF[%d] (%d) > MAX (%d), correcting to MAX\n",
00335                                 i, nf[i], ATH9K_NF_TOO_HIGH);
00336                         nf[i] = limit->max;
00337                 } else if (nf[i] < limit->min) {
00338                         DBG("ath9k: "
00339                                 "NF[%d] (%d) < MIN (%d), correcting to NOM\n",
00340                                 i, nf[i], limit->min);
00341                         nf[i] = limit->nominal;
00342                 }
00343         }
00344 }
00345 
00346 int ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
00347 {
00348         int16_t nf, nfThresh;
00349         int16_t nfarray[NUM_NF_READINGS] = { 0 };
00350         struct ath9k_nfcal_hist *h;
00351         struct net80211_channel *c = chan->chan;
00352         struct ath9k_hw_cal_data *caldata = ah->caldata;
00353 
00354         chan->channelFlags &= (~CHANNEL_CW_INT);
00355         if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
00356                 DBG("ath9k: "
00357                         "NF did not complete in calibration window\n");
00358                 return 0;
00359         }
00360 
00361         ath9k_hw_do_getnf(ah, nfarray);
00362         ath9k_hw_nf_sanitize(ah, nfarray);
00363         nf = nfarray[0];
00364         if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh)
00365             && nf > nfThresh) {
00366                 DBG2("ath9k: "
00367                         "noise floor failed detected; detected %d, threshold %d\n",
00368                         nf, nfThresh);
00369                 chan->channelFlags |= CHANNEL_CW_INT;
00370         }
00371 
00372         if (!caldata) {
00373                 chan->noisefloor = nf;
00374                 return 0;
00375         }
00376 
00377         h = caldata->nfCalHist;
00378         caldata->nfcal_pending = 0;
00379         ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray);
00380         chan->noisefloor = h[0].privNF;
00381         return 1;
00382 }
00383 
00384 void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
00385                                   struct ath9k_channel *chan)
00386 {
00387         struct ath9k_nfcal_hist *h;
00388         s16 default_nf;
00389         int i, j;
00390 
00391         ah->caldata->channel = chan->channel;
00392         ah->caldata->channelFlags = chan->channelFlags & ~CHANNEL_CW_INT;
00393         h = ah->caldata->nfCalHist;
00394         default_nf = ath9k_hw_get_default_nf(ah, chan);
00395         for (i = 0; i < NUM_NF_READINGS; i++) {
00396                 h[i].currIndex = 0;
00397                 h[i].privNF = default_nf;
00398                 h[i].invalidNFcount = AR_PHY_CCA_FILTERWINDOW_LENGTH;
00399                 for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) {
00400                         h[i].nfCalBuffer[j] = default_nf;
00401                 }
00402         }
00403 }