The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/ath/ath_hal/ar5416/ar5416_cal.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: ISC
    3  *
    4  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
    5  * Copyright (c) 2002-2008 Atheros Communications, Inc.
    6  *
    7  * Permission to use, copy, modify, and/or distribute this software for any
    8  * purpose with or without fee is hereby granted, provided that the above
    9  * copyright notice and this permission notice appear in all copies.
   10  *
   11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   18  *
   19  * $FreeBSD$
   20  */
   21 #include "opt_ah.h"
   22 
   23 #include "ah.h"
   24 #include "ah_internal.h"
   25 #include "ah_devid.h"
   26 
   27 #include "ah_eeprom_v14.h"
   28 
   29 #include "ar5212/ar5212.h"      /* for NF cal related declarations */
   30 
   31 #include "ar5416/ar5416.h"
   32 #include "ar5416/ar5416reg.h"
   33 #include "ar5416/ar5416phy.h"
   34 
   35 /* Owl specific stuff */
   36 #define NUM_NOISEFLOOR_READINGS 6       /* 3 chains * (ctl + ext) */
   37 
   38 static void ar5416StartNFCal(struct ath_hal *ah);
   39 static HAL_BOOL ar5416LoadNF(struct ath_hal *ah, const struct ieee80211_channel *);
   40 static int16_t ar5416GetNf(struct ath_hal *, struct ieee80211_channel *);
   41 
   42 static uint16_t ar5416GetDefaultNF(struct ath_hal *ah, const struct ieee80211_channel *chan);
   43 static void ar5416SanitizeNF(struct ath_hal *ah, int16_t *nf);
   44 
   45 /*
   46  * Determine if calibration is supported by device and channel flags
   47  */
   48 
   49 /*
   50  * ADC GAIN/DC offset calibration is for calibrating two ADCs that
   51  * are acting as one by interleaving incoming symbols. This isn't
   52  * relevant for 2.4GHz 20MHz wide modes because, as far as I can tell,
   53  * the secondary ADC is never enabled. It is enabled however for
   54  * 5GHz modes.
   55  *
   56  * It hasn't been confirmed whether doing this calibration is needed
   57  * at all in the above modes and/or whether it's actually harmful.
   58  * So for now, let's leave it enabled and just remember to get
   59  * confirmation that it needs to be clarified.
   60  *
   61  * See US Patent No: US 7,541,952 B1:
   62  *  " Method and Apparatus for Offset and Gain Compensation for
   63  *    Analog-to-Digital Converters."
   64  */
   65 static OS_INLINE HAL_BOOL
   66 ar5416IsCalSupp(struct ath_hal *ah, const struct ieee80211_channel *chan,
   67         HAL_CAL_TYPE calType) 
   68 {
   69         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
   70 
   71         switch (calType & cal->suppCals) {
   72         case IQ_MISMATCH_CAL:
   73                 /* Run IQ Mismatch for non-CCK only */
   74                 return !IEEE80211_IS_CHAN_B(chan);
   75         case ADC_GAIN_CAL:
   76         case ADC_DC_CAL:
   77                 /*
   78                  * Run ADC Gain Cal for either 5ghz any or 2ghz HT40.
   79                  *
   80                  * Don't run ADC calibrations for 5ghz fast clock mode
   81                  * in HT20 - only one ADC is used.
   82                  */
   83                 if (IEEE80211_IS_CHAN_HT20(chan) &&
   84                     (IS_5GHZ_FAST_CLOCK_EN(ah, chan)))
   85                         return AH_FALSE;
   86                 if (IEEE80211_IS_CHAN_5GHZ(chan))
   87                         return AH_TRUE;
   88                 if (IEEE80211_IS_CHAN_HT40(chan))
   89                         return AH_TRUE;
   90                 return AH_FALSE;
   91         }
   92         return AH_FALSE;
   93 }
   94 
   95 /*
   96  * Setup HW to collect samples used for current cal
   97  */
   98 static void
   99 ar5416SetupMeasurement(struct ath_hal *ah, HAL_CAL_LIST *currCal)
  100 {
  101         /* Start calibration w/ 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples */
  102         OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4,
  103             AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX,
  104             currCal->calData->calCountMax);
  105 
  106         /* Select calibration to run */
  107         switch (currCal->calData->calType) {
  108         case IQ_MISMATCH_CAL:
  109                 OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ);
  110                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
  111                     "%s: start IQ Mismatch calibration\n", __func__);
  112                 break;
  113         case ADC_GAIN_CAL:
  114                 OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN);
  115                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
  116                     "%s: start ADC Gain calibration\n", __func__);
  117                 break;
  118         case ADC_DC_CAL:
  119                 OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER);
  120                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
  121                     "%s: start ADC DC calibration\n", __func__);
  122                 break;
  123         case ADC_DC_INIT_CAL:
  124                 OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_INIT);
  125                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
  126                     "%s: start Init ADC DC calibration\n", __func__);
  127                 break;
  128         }
  129         /* Kick-off cal */
  130         OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_CAL);
  131 }
  132 
  133 /*
  134  * Initialize shared data structures and prepare a cal to be run.
  135  */
  136 static void
  137 ar5416ResetMeasurement(struct ath_hal *ah, HAL_CAL_LIST *currCal)
  138 {
  139         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
  140 
  141         /* Reset data structures shared between different calibrations */
  142         OS_MEMZERO(cal->caldata, sizeof(cal->caldata));
  143         cal->calSamples = 0;
  144 
  145         /* Setup HW for new calibration */
  146         ar5416SetupMeasurement(ah, currCal);
  147 
  148         /* Change SW state to RUNNING for this calibration */
  149         currCal->calState = CAL_RUNNING;
  150 }
  151 
  152 #if 0
  153 /*
  154  * Run non-periodic calibrations.
  155  */
  156 static HAL_BOOL
  157 ar5416RunInitCals(struct ath_hal *ah, int init_cal_count)
  158 {
  159         struct ath_hal_5416 *ahp = AH5416(ah);
  160         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
  161         HAL_CHANNEL_INTERNAL ichan;     /* XXX bogus */
  162         HAL_CAL_LIST *curCal = ahp->ah_cal_curr;
  163         HAL_BOOL isCalDone;
  164         int i;
  165 
  166         if (curCal == AH_NULL)
  167                 return AH_FALSE;
  168 
  169         ichan.calValid = 0;
  170         for (i = 0; i < init_cal_count; i++) {
  171                 /* Reset this Cal */
  172                 ar5416ResetMeasurement(ah, curCal);
  173                 /* Poll for offset calibration complete */
  174                 if (!ath_hal_wait(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_CAL, 0)) {
  175                         HALDEBUG(ah, HAL_DEBUG_ANY,
  176                             "%s: Cal %d failed to finish in 100ms.\n",
  177                             __func__, curCal->calData->calType);
  178                         /* Re-initialize list pointers for periodic cals */
  179                         cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;
  180                         return AH_FALSE;
  181                 }
  182                 /* Run this cal */
  183                 ar5416DoCalibration(ah, &ichan, ahp->ah_rxchainmask,
  184                     curCal, &isCalDone);
  185                 if (!isCalDone)
  186                         HALDEBUG(ah, HAL_DEBUG_ANY,
  187                             "%s: init cal %d did not complete.\n",
  188                             __func__, curCal->calData->calType);
  189                 if (curCal->calNext != AH_NULL)
  190                         curCal = curCal->calNext;
  191         }
  192 
  193         /* Re-initialize list pointers for periodic cals */
  194         cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;
  195         return AH_TRUE;
  196 }
  197 #endif
  198 
  199 /*
  200  * AGC calibration for the AR5416, AR9130, AR9160, AR9280.
  201  */
  202 HAL_BOOL
  203 ar5416InitCalHardware(struct ath_hal *ah, const struct ieee80211_channel *chan)
  204 {
  205 
  206         if (AR_SREV_MERLIN_10_OR_LATER(ah)) {
  207                 /* Disable ADC */
  208                 OS_REG_CLR_BIT(ah, AR_PHY_ADC_CTL,
  209                     AR_PHY_ADC_CTL_OFF_PWDADC);
  210 
  211                 /* Enable Rx Filter Cal */
  212                 OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
  213                     AR_PHY_AGC_CONTROL_FLTR_CAL);
  214         }       
  215 
  216         /* Calibrate the AGC */
  217         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL);
  218 
  219         /* Poll for offset calibration complete */
  220         if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) {
  221                 HALDEBUG(ah, HAL_DEBUG_ANY,
  222                     "%s: offset calibration did not complete in 1ms; "
  223                     "noisy environment?\n", __func__);
  224                 return AH_FALSE;
  225         }
  226 
  227         if (AR_SREV_MERLIN_10_OR_LATER(ah)) {
  228                 /* Enable ADC */
  229                 OS_REG_SET_BIT(ah, AR_PHY_ADC_CTL,
  230                     AR_PHY_ADC_CTL_OFF_PWDADC);
  231 
  232                 /* Disable Rx Filter Cal */
  233                 OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
  234                     AR_PHY_AGC_CONTROL_FLTR_CAL);
  235         }
  236 
  237         return AH_TRUE;
  238 }
  239 
  240 /*
  241  * Initialize Calibration infrastructure.
  242  */
  243 #define MAX_CAL_CHECK           32
  244 HAL_BOOL
  245 ar5416InitCal(struct ath_hal *ah, const struct ieee80211_channel *chan)
  246 {
  247         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
  248         HAL_CHANNEL_INTERNAL *ichan;
  249 
  250         ichan = ath_hal_checkchannel(ah, chan);
  251         HALASSERT(ichan != AH_NULL);
  252 
  253         /* Do initial chipset-specific calibration */
  254         if (! AH5416(ah)->ah_cal_initcal(ah, chan)) {
  255                 HALDEBUG(ah, HAL_DEBUG_ANY,
  256                     "%s: initial chipset calibration did "
  257                     "not complete in time; noisy environment?\n", __func__);
  258                 return AH_FALSE;
  259         }
  260 
  261         /* If there's PA Cal, do it */
  262         if (AH5416(ah)->ah_cal_pacal)
  263                 AH5416(ah)->ah_cal_pacal(ah, AH_TRUE);
  264 
  265         /* 
  266          * Do NF calibration after DC offset and other CALs.
  267          * Per system engineers, noise floor value can sometimes be 20 dB
  268          * higher than normal value if DC offset and noise floor cal are
  269          * triggered at the same time.
  270          */
  271         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
  272 
  273         /*
  274          * This may take a while to run; make sure subsequent
  275          * calibration routines check that this has completed
  276          * before reading the value and triggering a subsequent
  277          * calibration.
  278          */
  279 
  280         /* Initialize list pointers */
  281         cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;
  282 
  283         /*
  284          * Enable IQ, ADC Gain, ADC DC Offset Cals
  285          */
  286         if (AR_SREV_HOWL(ah) || AR_SREV_SOWL_10_OR_LATER(ah)) {
  287                 /* Setup all non-periodic, init time only calibrations */
  288                 /* XXX: Init DC Offset not working yet */
  289 #if 0
  290                 if (ar5416IsCalSupp(ah, chan, ADC_DC_INIT_CAL)) {
  291                         INIT_CAL(&cal->adcDcCalInitData);
  292                         INSERT_CAL(cal, &cal->adcDcCalInitData);
  293                 }
  294                 /* Initialize current pointer to first element in list */
  295                 cal->cal_curr = cal->cal_list;
  296 
  297                 if (cal->ah_cal_curr != AH_NULL && !ar5416RunInitCals(ah, 0))
  298                         return AH_FALSE;
  299 #endif
  300         }
  301 
  302         /* If Cals are supported, add them to list via INIT/INSERT_CAL */
  303         if (ar5416IsCalSupp(ah, chan, ADC_GAIN_CAL)) {
  304                 INIT_CAL(&cal->adcGainCalData);
  305                 INSERT_CAL(cal, &cal->adcGainCalData);
  306                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
  307                     "%s: enable ADC Gain Calibration.\n", __func__);
  308         }
  309         if (ar5416IsCalSupp(ah, chan, ADC_DC_CAL)) {
  310                 INIT_CAL(&cal->adcDcCalData);
  311                 INSERT_CAL(cal, &cal->adcDcCalData);
  312                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
  313                     "%s: enable ADC DC Calibration.\n", __func__);
  314         }
  315         if (ar5416IsCalSupp(ah, chan, IQ_MISMATCH_CAL)) {
  316                 INIT_CAL(&cal->iqCalData);
  317                 INSERT_CAL(cal, &cal->iqCalData);
  318                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
  319                     "%s: enable IQ Calibration.\n", __func__);
  320         }
  321         /* Initialize current pointer to first element in list */
  322         cal->cal_curr = cal->cal_list;
  323 
  324         /* Kick off measurements for the first cal */
  325         if (cal->cal_curr != AH_NULL)
  326                 ar5416ResetMeasurement(ah, cal->cal_curr);
  327 
  328         /* Mark all calibrations on this channel as being invalid */
  329         ichan->calValid = 0;
  330 
  331         return AH_TRUE;
  332 #undef  MAX_CAL_CHECK
  333 }
  334 
  335 /*
  336  * Entry point for upper layers to restart current cal.
  337  * Reset the calibration valid bit in channel.
  338  */
  339 HAL_BOOL
  340 ar5416ResetCalValid(struct ath_hal *ah, const struct ieee80211_channel *chan)
  341 {
  342         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
  343         HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
  344         HAL_CAL_LIST *currCal = cal->cal_curr;
  345 
  346         if (!AR_SREV_SOWL_10_OR_LATER(ah))
  347                 return AH_FALSE;
  348         if (currCal == AH_NULL)
  349                 return AH_FALSE;
  350         if (ichan == AH_NULL) {
  351                 HALDEBUG(ah, HAL_DEBUG_ANY,
  352                     "%s: invalid channel %u/0x%x; no mapping\n",
  353                     __func__, chan->ic_freq, chan->ic_flags);
  354                 return AH_FALSE;
  355         }
  356         /*
  357          * Expected that this calibration has run before, post-reset.
  358          * Current state should be done
  359          */
  360         if (currCal->calState != CAL_DONE) {
  361                 HALDEBUG(ah, HAL_DEBUG_ANY,
  362                     "%s: Calibration state incorrect, %d\n",
  363                     __func__, currCal->calState);
  364                 return AH_FALSE;
  365         }
  366 
  367         /* Verify Cal is supported on this channel */
  368         if (!ar5416IsCalSupp(ah, chan, currCal->calData->calType))
  369                 return AH_FALSE;
  370 
  371         HALDEBUG(ah, HAL_DEBUG_PERCAL,
  372             "%s: Resetting Cal %d state for channel %u/0x%x\n",
  373             __func__, currCal->calData->calType, chan->ic_freq,
  374             chan->ic_flags);
  375 
  376         /* Disable cal validity in channel */
  377         ichan->calValid &= ~currCal->calData->calType;
  378         currCal->calState = CAL_WAITING;
  379 
  380         return AH_TRUE;
  381 }
  382 
  383 /*
  384  * Recalibrate the lower PHY chips to account for temperature/environment
  385  * changes.
  386  */
  387 static void
  388 ar5416DoCalibration(struct ath_hal *ah,  HAL_CHANNEL_INTERNAL *ichan,
  389         uint8_t rxchainmask, HAL_CAL_LIST *currCal, HAL_BOOL *isCalDone)
  390 {
  391         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
  392 
  393         /* Cal is assumed not done until explicitly set below */
  394         *isCalDone = AH_FALSE;
  395 
  396         HALDEBUG(ah, HAL_DEBUG_PERCAL,
  397             "%s: %s Calibration, state %d, calValid 0x%x\n",
  398             __func__, currCal->calData->calName, currCal->calState,
  399             ichan->calValid);
  400 
  401         /* Calibration in progress. */
  402         if (currCal->calState == CAL_RUNNING) {
  403                 /* Check to see if it has finished. */
  404                 if (!(OS_REG_READ(ah, AR_PHY_TIMING_CTRL4) & AR_PHY_TIMING_CTRL4_DO_CAL)) {
  405                         HALDEBUG(ah, HAL_DEBUG_PERCAL,
  406                             "%s: sample %d of %d finished\n",
  407                             __func__, cal->calSamples,
  408                             currCal->calData->calNumSamples);
  409                         /* 
  410                          * Collect measurements for active chains.
  411                          */
  412                         currCal->calData->calCollect(ah);
  413                         if (++cal->calSamples >= currCal->calData->calNumSamples) {
  414                                 int i, numChains = 0;
  415                                 for (i = 0; i < AR5416_MAX_CHAINS; i++) {
  416                                         if (rxchainmask & (1 << i))
  417                                                 numChains++;
  418                                 }
  419                                 /* 
  420                                  * Process accumulated data
  421                                  */
  422                                 currCal->calData->calPostProc(ah, numChains);
  423 
  424                                 /* Calibration has finished. */
  425                                 ichan->calValid |= currCal->calData->calType;
  426                                 currCal->calState = CAL_DONE;
  427                                 *isCalDone = AH_TRUE;
  428                         } else {
  429                                 /*
  430                                  * Set-up to collect of another sub-sample.
  431                                  */
  432                                 ar5416SetupMeasurement(ah, currCal);
  433                         }
  434                 }
  435         } else if (!(ichan->calValid & currCal->calData->calType)) {
  436                 /* If current cal is marked invalid in channel, kick it off */
  437                 ar5416ResetMeasurement(ah, currCal);
  438         }
  439 }
  440 
  441 /*
  442  * Internal interface to schedule periodic calibration work.
  443  */
  444 HAL_BOOL
  445 ar5416PerCalibrationN(struct ath_hal *ah, struct ieee80211_channel *chan,
  446         u_int rxchainmask, HAL_BOOL longcal, HAL_BOOL *isCalDone)
  447 {
  448         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
  449         HAL_CAL_LIST *currCal = cal->cal_curr;
  450         HAL_CHANNEL_INTERNAL *ichan;
  451         int r;
  452 
  453         OS_MARK(ah, AH_MARK_PERCAL, chan->ic_freq);
  454 
  455         *isCalDone = AH_TRUE;
  456 
  457         /*
  458          * Since ath_hal calls the PerCal method with rxchainmask=0x1;
  459          * override it with the current chainmask. The upper levels currently
  460          * doesn't know about the chainmask.
  461          */
  462         rxchainmask = AH5416(ah)->ah_rx_chainmask;
  463 
  464         /* Invalid channel check */
  465         ichan = ath_hal_checkchannel(ah, chan);
  466         if (ichan == AH_NULL) {
  467                 HALDEBUG(ah, HAL_DEBUG_ANY,
  468                     "%s: invalid channel %u/0x%x; no mapping\n",
  469                     __func__, chan->ic_freq, chan->ic_flags);
  470                 return AH_FALSE;
  471         }
  472 
  473         /*
  474          * For given calibration:
  475          * 1. Call generic cal routine
  476          * 2. When this cal is done (isCalDone) if we have more cals waiting
  477          *    (eg after reset), mask this to upper layers by not propagating
  478          *    isCalDone if it is set to TRUE.
  479          *    Instead, change isCalDone to FALSE and setup the waiting cal(s)
  480          *    to be run.
  481          */
  482         if (currCal != AH_NULL &&
  483             (currCal->calState == CAL_RUNNING ||
  484              currCal->calState == CAL_WAITING)) {
  485                 ar5416DoCalibration(ah, ichan, rxchainmask, currCal, isCalDone);
  486                 if (*isCalDone == AH_TRUE) {
  487                         cal->cal_curr = currCal = currCal->calNext;
  488                         if (currCal->calState == CAL_WAITING) {
  489                                 *isCalDone = AH_FALSE;
  490                                 ar5416ResetMeasurement(ah, currCal);
  491                         }
  492                 }
  493         }
  494 
  495         /* Do NF cal only at longer intervals */
  496         if (longcal) {
  497                 /* Do PA calibration if the chipset supports */
  498                 if (AH5416(ah)->ah_cal_pacal)
  499                         AH5416(ah)->ah_cal_pacal(ah, AH_FALSE);
  500 
  501                 /* Do open-loop temperature compensation if the chipset needs it */
  502                 if (ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
  503                         AH5416(ah)->ah_olcTempCompensation(ah);
  504 
  505                 /*
  506                  * Get the value from the previous NF cal
  507                  * and update the history buffer.
  508                  */
  509                 r = ar5416GetNf(ah, chan);
  510                 if (r == 0 || r == -1) {
  511                         /* NF calibration result isn't valid */
  512                         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: NF calibration"
  513                             " didn't finish; delaying CCA\n", __func__);
  514                 } else {
  515                         int ret;
  516                         /* 
  517                          * NF calibration result is valid.
  518                          *
  519                          * Load the NF from history buffer of the current channel.
  520                          * NF is slow time-variant, so it is OK to use a
  521                          * historical value.
  522                          */
  523                         ret = ar5416LoadNF(ah, AH_PRIVATE(ah)->ah_curchan);
  524 
  525                         /* start NF calibration, without updating BB NF register*/
  526                         ar5416StartNFCal(ah);
  527 
  528                         /*
  529                          * If we failed calibration then tell the driver
  530                          * we failed and it should do a full chip reset
  531                          */
  532                         if (! ret)
  533                                 return AH_FALSE;
  534                 }
  535         }
  536         return AH_TRUE;
  537 }
  538 
  539 /*
  540  * Recalibrate the lower PHY chips to account for temperature/environment
  541  * changes.
  542  */
  543 HAL_BOOL
  544 ar5416PerCalibration(struct ath_hal *ah, struct ieee80211_channel *chan,
  545         HAL_BOOL *isIQdone)
  546 {
  547         struct ath_hal_5416 *ahp = AH5416(ah);
  548         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
  549         HAL_CAL_LIST *curCal = cal->cal_curr;
  550 
  551         if (curCal != AH_NULL && curCal->calData->calType == IQ_MISMATCH_CAL) {
  552                 return ar5416PerCalibrationN(ah, chan, ahp->ah_rx_chainmask,
  553                     AH_TRUE, isIQdone);
  554         } else {
  555                 HAL_BOOL isCalDone;
  556 
  557                 *isIQdone = AH_FALSE;
  558                 return ar5416PerCalibrationN(ah, chan, ahp->ah_rx_chainmask,
  559                     AH_TRUE, &isCalDone);
  560         }
  561 }
  562 
  563 static HAL_BOOL
  564 ar5416GetEepromNoiseFloorThresh(struct ath_hal *ah,
  565         const struct ieee80211_channel *chan, int16_t *nft)
  566 {
  567         if (IEEE80211_IS_CHAN_5GHZ(chan)) {
  568                 ath_hal_eepromGet(ah, AR_EEP_NFTHRESH_5, nft);
  569                 return AH_TRUE;
  570         }
  571         if (IEEE80211_IS_CHAN_2GHZ(chan)) {
  572                 ath_hal_eepromGet(ah, AR_EEP_NFTHRESH_2, nft);
  573                 return AH_TRUE;
  574         }
  575         HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n",
  576             __func__, chan->ic_flags);
  577         return AH_FALSE;
  578 }
  579 
  580 static void
  581 ar5416StartNFCal(struct ath_hal *ah)
  582 {
  583         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);
  584         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
  585         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
  586 }
  587 
  588 static HAL_BOOL
  589 ar5416LoadNF(struct ath_hal *ah, const struct ieee80211_channel *chan)
  590 {
  591         static const uint32_t ar5416_cca_regs[] = {
  592                 AR_PHY_CCA,
  593                 AR_PHY_CH1_CCA,
  594                 AR_PHY_CH2_CCA,
  595                 AR_PHY_EXT_CCA,
  596                 AR_PHY_CH1_EXT_CCA,
  597                 AR_PHY_CH2_EXT_CCA
  598         };
  599         struct ar5212NfCalHist *h;
  600         int i;
  601         int32_t val;
  602         uint8_t chainmask;
  603         int16_t default_nf = ar5416GetDefaultNF(ah, chan);
  604 
  605         /*
  606          * Force NF calibration for all chains.
  607          */
  608         if (AR_SREV_KITE(ah)) {
  609                 /* Kite has only one chain */
  610                 chainmask = 0x9;
  611         } else if (AR_SREV_MERLIN(ah) || AR_SREV_KIWI(ah)) {
  612                 /* Merlin/Kiwi has only two chains */
  613                 chainmask = 0x1B;
  614         } else {
  615                 chainmask = 0x3F;
  616         }
  617 
  618         /*
  619          * Write filtered NF values into maxCCApwr register parameter
  620          * so we can load below.
  621          */
  622         h = AH5416(ah)->ah_cal.nfCalHist;
  623         HALDEBUG(ah, HAL_DEBUG_NFCAL, "CCA: ");
  624         for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
  625                 /* Don't write to EXT radio CCA registers unless in HT/40 mode */
  626                 /* XXX this check should really be cleaner! */
  627                 if (i > 2 && !IEEE80211_IS_CHAN_HT40(chan))
  628                         continue;
  629 
  630                 if (chainmask & (1 << i)) { 
  631                         int16_t nf_val;
  632 
  633                         if (h)
  634                                 nf_val = h[i].privNF;
  635                         else
  636                                 nf_val = default_nf;
  637 
  638                         val = OS_REG_READ(ah, ar5416_cca_regs[i]);
  639                         val &= 0xFFFFFE00;
  640                         val |= (((uint32_t) nf_val << 1) & 0x1ff);
  641                         HALDEBUG(ah, HAL_DEBUG_NFCAL, "[%d: %d]", i, nf_val);
  642                         OS_REG_WRITE(ah, ar5416_cca_regs[i], val);
  643                 }
  644         }
  645         HALDEBUG(ah, HAL_DEBUG_NFCAL, "\n");
  646 
  647         /* Load software filtered NF value into baseband internal minCCApwr variable. */
  648         OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);
  649         OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
  650         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
  651 
  652         /* Wait for load to complete, should be fast, a few 10s of us. */
  653         if (! ar5212WaitNFCalComplete(ah, 1000)) {
  654                 /*
  655                  * We timed out waiting for the noisefloor to load, probably due to an
  656                  * in-progress rx. Simply return here and allow the load plenty of time
  657                  * to complete before the next calibration interval.  We need to avoid
  658                  * trying to load -50 (which happens below) while the previous load is
  659                  * still in progress as this can cause rx deafness. Instead by returning
  660                  * here, the baseband nf cal will just be capped by our present
  661                  * noisefloor until the next calibration timer.
  662                  */
  663                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "Timeout while waiting for "
  664                     "nf to load: AR_PHY_AGC_CONTROL=0x%x\n",
  665                     OS_REG_READ(ah, AR_PHY_AGC_CONTROL));
  666                 return AH_FALSE;
  667         }
  668 
  669         /*
  670          * Restore maxCCAPower register parameter again so that we're not capped
  671          * by the median we just loaded.  This will be initial (and max) value
  672          * of next noise floor calibration the baseband does.  
  673          */
  674         for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
  675                 /* Don't write to EXT radio CCA registers unless in HT/40 mode */
  676                 /* XXX this check should really be cleaner! */
  677                 if (i > 2 && !IEEE80211_IS_CHAN_HT40(chan))
  678                         continue;
  679 
  680                 if (chainmask & (1 << i)) {     
  681                         val = OS_REG_READ(ah, ar5416_cca_regs[i]);
  682                         val &= 0xFFFFFE00;
  683                         val |= (((uint32_t)(-50) << 1) & 0x1ff);
  684                         OS_REG_WRITE(ah, ar5416_cca_regs[i], val);
  685                 }
  686         }
  687         return AH_TRUE;
  688 }
  689 
  690 /*
  691  * This just initialises the "good" values for AR5416 which
  692  * may not be right; it'lll be overridden by ar5416SanitizeNF()
  693  * to nominal values.
  694  */
  695 void
  696 ar5416InitNfHistBuff(struct ar5212NfCalHist *h)
  697 {
  698         int i, j;
  699 
  700         for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
  701                 h[i].currIndex = 0;
  702                 h[i].privNF = AR5416_CCA_MAX_GOOD_VALUE;
  703                 h[i].invalidNFcount = AR512_NF_CAL_HIST_MAX;
  704                 for (j = 0; j < AR512_NF_CAL_HIST_MAX; j ++)
  705                         h[i].nfCalBuffer[j] = AR5416_CCA_MAX_GOOD_VALUE;
  706         }
  707 }
  708 
  709 /*
  710  * Update the noise floor buffer as a ring buffer
  711  */
  712 static void
  713 ar5416UpdateNFHistBuff(struct ath_hal *ah, struct ar5212NfCalHist *h,
  714     int16_t *nfarray)
  715 {
  716         int i;
  717 
  718         /* XXX TODO: don't record nfarray[] entries for inactive chains */
  719         for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
  720                 h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
  721 
  722                 if (++h[i].currIndex >= AR512_NF_CAL_HIST_MAX)
  723                         h[i].currIndex = 0;
  724                 if (h[i].invalidNFcount > 0) {
  725                         if (nfarray[i] < AR5416_CCA_MIN_BAD_VALUE ||
  726                             nfarray[i] > AR5416_CCA_MAX_HIGH_VALUE) {
  727                                 h[i].invalidNFcount = AR512_NF_CAL_HIST_MAX;
  728                         } else {
  729                                 h[i].invalidNFcount--;
  730                                 h[i].privNF = nfarray[i];
  731                         }
  732                 } else {
  733                         h[i].privNF = ar5212GetNfHistMid(h[i].nfCalBuffer);
  734                 }
  735         }
  736 }   
  737 
  738 static uint16_t
  739 ar5416GetDefaultNF(struct ath_hal *ah, const struct ieee80211_channel *chan)
  740 {
  741         struct ar5416NfLimits *limit;
  742 
  743         if (!chan || IEEE80211_IS_CHAN_2GHZ(chan))
  744                 limit = &AH5416(ah)->nf_2g;
  745         else
  746                 limit = &AH5416(ah)->nf_5g;
  747 
  748         return limit->nominal;
  749 }
  750 
  751 static void
  752 ar5416SanitizeNF(struct ath_hal *ah, int16_t *nf)
  753 {
  754 
  755         struct ar5416NfLimits *limit;
  756         int i;
  757 
  758         if (IEEE80211_IS_CHAN_2GHZ(AH_PRIVATE(ah)->ah_curchan))
  759                 limit = &AH5416(ah)->nf_2g;
  760         else
  761                 limit = &AH5416(ah)->nf_5g;
  762 
  763         for (i = 0; i < AR5416_NUM_NF_READINGS; i++) {
  764                 if (!nf[i])
  765                         continue;
  766 
  767                 if (nf[i] > limit->max) {
  768                         HALDEBUG(ah, HAL_DEBUG_NFCAL,
  769                                   "NF[%d] (%d) > MAX (%d), correcting to MAX\n",
  770                                   i, nf[i], limit->max);
  771                         nf[i] = limit->max;
  772                 } else if (nf[i] < limit->min) {
  773                         HALDEBUG(ah, HAL_DEBUG_NFCAL,
  774                                   "NF[%d] (%d) < MIN (%d), correcting to NOM\n",
  775                                   i, nf[i], limit->min);
  776                         nf[i] = limit->nominal;
  777                 }
  778         }
  779 }
  780 
  781 /*
  782  * Read the NF and check it against the noise floor threshold
  783  *
  784  * Return 0 if the NF calibration hadn't finished, 0 if it was
  785  * invalid, or > 0 for a valid NF reading.
  786  */
  787 static int16_t
  788 ar5416GetNf(struct ath_hal *ah, struct ieee80211_channel *chan)
  789 {
  790         int16_t nf, nfThresh;
  791         int i;
  792         int retval = 0;
  793 
  794         if (ar5212IsNFCalInProgress(ah)) {
  795                 HALDEBUG(ah, HAL_DEBUG_ANY,
  796                     "%s: NF didn't complete in calibration window\n", __func__);
  797                 nf = 0;
  798                 retval = -1;    /* NF didn't finish */
  799         } else {
  800                 /* Finished NF cal, check against threshold */
  801                 int16_t nfarray[NUM_NOISEFLOOR_READINGS] = { 0 };
  802                 HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
  803                         
  804                 /* TODO - enhance for multiple chains and ext ch */
  805                 ath_hal_getNoiseFloor(ah, nfarray);
  806                 nf = nfarray[0];
  807                 ar5416SanitizeNF(ah, nfarray);
  808                 if (ar5416GetEepromNoiseFloorThresh(ah, chan, &nfThresh)) {
  809                         if (nf > nfThresh) {
  810                                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
  811                                     "%s: noise floor failed detected; "
  812                                     "detected %d, threshold %d\n", __func__,
  813                                     nf, nfThresh);
  814                                 /*
  815                                  * NB: Don't discriminate 2.4 vs 5Ghz, if this
  816                                  *     happens it indicates a problem regardless
  817                                  *     of the band.
  818                                  */
  819                                 chan->ic_state |= IEEE80211_CHANSTATE_CWINT;
  820                                 nf = 0;
  821                                 retval = 0;
  822                         }
  823                 } else {
  824                         nf = 0;
  825                         retval = 0;
  826                 }
  827                 /* Update MIMO channel statistics, regardless of validity or not (for now) */
  828                 for (i = 0; i < 3; i++) {
  829                         ichan->noiseFloorCtl[i] = nfarray[i];
  830                         ichan->noiseFloorExt[i] = nfarray[i + 3];
  831                 }
  832                 ichan->privFlags |= CHANNEL_MIMO_NF_VALID;
  833 
  834                 ar5416UpdateNFHistBuff(ah, AH5416(ah)->ah_cal.nfCalHist, nfarray);
  835                 ichan->rawNoiseFloor = nf;
  836                 retval = nf;
  837         }
  838         return retval;
  839 }

Cache object: 5e635123af3033dfa9201c07c4e1cd38


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.