Path: blob/main/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c
39566 views
/*-1* SPDX-License-Identifier: ISC2*3* Copyright (c) 2002-2009 Sam Leffler, Errno Consulting4* Copyright (c) 2002-2008 Atheros Communications, Inc.5*6* Permission to use, copy, modify, and/or distribute this software for any7* purpose with or without fee is hereby granted, provided that the above8* copyright notice and this permission notice appear in all copies.9*10* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES11* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF12* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR13* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES14* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN15* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF16* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.17*/18#include "opt_ah.h"1920#include "ah.h"21#include "ah_internal.h"22#include "ah_devid.h"2324#include "ah_eeprom_v14.h"2526#include "ar5212/ar5212.h" /* for NF cal related declarations */2728#include "ar5416/ar5416.h"29#include "ar5416/ar5416reg.h"30#include "ar5416/ar5416phy.h"3132/* Owl specific stuff */33#define NUM_NOISEFLOOR_READINGS 6 /* 3 chains * (ctl + ext) */3435static void ar5416StartNFCal(struct ath_hal *ah);36static HAL_BOOL ar5416LoadNF(struct ath_hal *ah, const struct ieee80211_channel *);37static int16_t ar5416GetNf(struct ath_hal *, struct ieee80211_channel *);3839static uint16_t ar5416GetDefaultNF(struct ath_hal *ah, const struct ieee80211_channel *chan);40static void ar5416SanitizeNF(struct ath_hal *ah, int16_t *nf);4142/*43* Determine if calibration is supported by device and channel flags44*/4546/*47* ADC GAIN/DC offset calibration is for calibrating two ADCs that48* are acting as one by interleaving incoming symbols. This isn't49* relevant for 2.4GHz 20MHz wide modes because, as far as I can tell,50* the secondary ADC is never enabled. It is enabled however for51* 5GHz modes.52*53* It hasn't been confirmed whether doing this calibration is needed54* at all in the above modes and/or whether it's actually harmful.55* So for now, let's leave it enabled and just remember to get56* confirmation that it needs to be clarified.57*58* See US Patent No: US 7,541,952 B1:59* " Method and Apparatus for Offset and Gain Compensation for60* Analog-to-Digital Converters."61*/62static OS_INLINE HAL_BOOL63ar5416IsCalSupp(struct ath_hal *ah, const struct ieee80211_channel *chan,64HAL_CAL_TYPE calType)65{66struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;6768switch (calType & cal->suppCals) {69case IQ_MISMATCH_CAL:70/* Run IQ Mismatch for non-CCK only */71return !IEEE80211_IS_CHAN_B(chan);72case ADC_GAIN_CAL:73case ADC_DC_CAL:74/*75* Run ADC Gain Cal for either 5ghz any or 2ghz HT40.76*77* Don't run ADC calibrations for 5ghz fast clock mode78* in HT20 - only one ADC is used.79*/80if (IEEE80211_IS_CHAN_HT20(chan) &&81(IS_5GHZ_FAST_CLOCK_EN(ah, chan)))82return AH_FALSE;83if (IEEE80211_IS_CHAN_5GHZ(chan))84return AH_TRUE;85if (IEEE80211_IS_CHAN_HT40(chan))86return AH_TRUE;87return AH_FALSE;88}89return AH_FALSE;90}9192/*93* Setup HW to collect samples used for current cal94*/95static void96ar5416SetupMeasurement(struct ath_hal *ah, HAL_CAL_LIST *currCal)97{98/* Start calibration w/ 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples */99OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4,100AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX,101currCal->calData->calCountMax);102103/* Select calibration to run */104switch (currCal->calData->calType) {105case IQ_MISMATCH_CAL:106OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ);107HALDEBUG(ah, HAL_DEBUG_PERCAL,108"%s: start IQ Mismatch calibration\n", __func__);109break;110case ADC_GAIN_CAL:111OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN);112HALDEBUG(ah, HAL_DEBUG_PERCAL,113"%s: start ADC Gain calibration\n", __func__);114break;115case ADC_DC_CAL:116OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER);117HALDEBUG(ah, HAL_DEBUG_PERCAL,118"%s: start ADC DC calibration\n", __func__);119break;120case ADC_DC_INIT_CAL:121OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_INIT);122HALDEBUG(ah, HAL_DEBUG_PERCAL,123"%s: start Init ADC DC calibration\n", __func__);124break;125}126/* Kick-off cal */127OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_CAL);128}129130/*131* Initialize shared data structures and prepare a cal to be run.132*/133static void134ar5416ResetMeasurement(struct ath_hal *ah, HAL_CAL_LIST *currCal)135{136struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;137138/* Reset data structures shared between different calibrations */139OS_MEMZERO(cal->caldata, sizeof(cal->caldata));140cal->calSamples = 0;141142/* Setup HW for new calibration */143ar5416SetupMeasurement(ah, currCal);144145/* Change SW state to RUNNING for this calibration */146currCal->calState = CAL_RUNNING;147}148149#if 0150/*151* Run non-periodic calibrations.152*/153static HAL_BOOL154ar5416RunInitCals(struct ath_hal *ah, int init_cal_count)155{156struct ath_hal_5416 *ahp = AH5416(ah);157struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;158HAL_CHANNEL_INTERNAL ichan; /* XXX bogus */159HAL_CAL_LIST *curCal = ahp->ah_cal_curr;160HAL_BOOL isCalDone;161int i;162163if (curCal == AH_NULL)164return AH_FALSE;165166ichan.calValid = 0;167for (i = 0; i < init_cal_count; i++) {168/* Reset this Cal */169ar5416ResetMeasurement(ah, curCal);170/* Poll for offset calibration complete */171if (!ath_hal_wait(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_CAL, 0)) {172HALDEBUG(ah, HAL_DEBUG_ANY,173"%s: Cal %d failed to finish in 100ms.\n",174__func__, curCal->calData->calType);175/* Re-initialize list pointers for periodic cals */176cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;177return AH_FALSE;178}179/* Run this cal */180ar5416DoCalibration(ah, &ichan, ahp->ah_rxchainmask,181curCal, &isCalDone);182if (!isCalDone)183HALDEBUG(ah, HAL_DEBUG_ANY,184"%s: init cal %d did not complete.\n",185__func__, curCal->calData->calType);186if (curCal->calNext != AH_NULL)187curCal = curCal->calNext;188}189190/* Re-initialize list pointers for periodic cals */191cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;192return AH_TRUE;193}194#endif195196/*197* AGC calibration for the AR5416, AR9130, AR9160, AR9280.198*/199HAL_BOOL200ar5416InitCalHardware(struct ath_hal *ah, const struct ieee80211_channel *chan)201{202203if (AR_SREV_MERLIN_10_OR_LATER(ah)) {204/* Disable ADC */205OS_REG_CLR_BIT(ah, AR_PHY_ADC_CTL,206AR_PHY_ADC_CTL_OFF_PWDADC);207208/* Enable Rx Filter Cal */209OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,210AR_PHY_AGC_CONTROL_FLTR_CAL);211}212213/* Calibrate the AGC */214OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL);215216/* Poll for offset calibration complete */217if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) {218HALDEBUG(ah, HAL_DEBUG_ANY,219"%s: offset calibration did not complete in 1ms; "220"noisy environment?\n", __func__);221return AH_FALSE;222}223224if (AR_SREV_MERLIN_10_OR_LATER(ah)) {225/* Enable ADC */226OS_REG_SET_BIT(ah, AR_PHY_ADC_CTL,227AR_PHY_ADC_CTL_OFF_PWDADC);228229/* Disable Rx Filter Cal */230OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,231AR_PHY_AGC_CONTROL_FLTR_CAL);232}233234return AH_TRUE;235}236237/*238* Initialize Calibration infrastructure.239*/240#define MAX_CAL_CHECK 32241HAL_BOOL242ar5416InitCal(struct ath_hal *ah, const struct ieee80211_channel *chan)243{244struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;245HAL_CHANNEL_INTERNAL *ichan;246247ichan = ath_hal_checkchannel(ah, chan);248HALASSERT(ichan != AH_NULL);249250/* Do initial chipset-specific calibration */251if (! AH5416(ah)->ah_cal_initcal(ah, chan)) {252HALDEBUG(ah, HAL_DEBUG_ANY,253"%s: initial chipset calibration did "254"not complete in time; noisy environment?\n", __func__);255return AH_FALSE;256}257258/* If there's PA Cal, do it */259if (AH5416(ah)->ah_cal_pacal)260AH5416(ah)->ah_cal_pacal(ah, AH_TRUE);261262/*263* Do NF calibration after DC offset and other CALs.264* Per system engineers, noise floor value can sometimes be 20 dB265* higher than normal value if DC offset and noise floor cal are266* triggered at the same time.267*/268OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);269270/*271* This may take a while to run; make sure subsequent272* calibration routines check that this has completed273* before reading the value and triggering a subsequent274* calibration.275*/276277/* Initialize list pointers */278cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;279280/*281* Enable IQ, ADC Gain, ADC DC Offset Cals282*/283if (AR_SREV_HOWL(ah) || AR_SREV_SOWL_10_OR_LATER(ah)) {284/* Setup all non-periodic, init time only calibrations */285/* XXX: Init DC Offset not working yet */286#if 0287if (ar5416IsCalSupp(ah, chan, ADC_DC_INIT_CAL)) {288INIT_CAL(&cal->adcDcCalInitData);289INSERT_CAL(cal, &cal->adcDcCalInitData);290}291/* Initialize current pointer to first element in list */292cal->cal_curr = cal->cal_list;293294if (cal->ah_cal_curr != AH_NULL && !ar5416RunInitCals(ah, 0))295return AH_FALSE;296#endif297}298299/* If Cals are supported, add them to list via INIT/INSERT_CAL */300if (ar5416IsCalSupp(ah, chan, ADC_GAIN_CAL)) {301INIT_CAL(&cal->adcGainCalData);302INSERT_CAL(cal, &cal->adcGainCalData);303HALDEBUG(ah, HAL_DEBUG_PERCAL,304"%s: enable ADC Gain Calibration.\n", __func__);305}306if (ar5416IsCalSupp(ah, chan, ADC_DC_CAL)) {307INIT_CAL(&cal->adcDcCalData);308INSERT_CAL(cal, &cal->adcDcCalData);309HALDEBUG(ah, HAL_DEBUG_PERCAL,310"%s: enable ADC DC Calibration.\n", __func__);311}312if (ar5416IsCalSupp(ah, chan, IQ_MISMATCH_CAL)) {313INIT_CAL(&cal->iqCalData);314INSERT_CAL(cal, &cal->iqCalData);315HALDEBUG(ah, HAL_DEBUG_PERCAL,316"%s: enable IQ Calibration.\n", __func__);317}318/* Initialize current pointer to first element in list */319cal->cal_curr = cal->cal_list;320321/* Kick off measurements for the first cal */322if (cal->cal_curr != AH_NULL)323ar5416ResetMeasurement(ah, cal->cal_curr);324325/* Mark all calibrations on this channel as being invalid */326ichan->calValid = 0;327328return AH_TRUE;329#undef MAX_CAL_CHECK330}331332/*333* Entry point for upper layers to restart current cal.334* Reset the calibration valid bit in channel.335*/336HAL_BOOL337ar5416ResetCalValid(struct ath_hal *ah, const struct ieee80211_channel *chan)338{339struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;340HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);341HAL_CAL_LIST *currCal = cal->cal_curr;342343if (!AR_SREV_SOWL_10_OR_LATER(ah))344return AH_FALSE;345if (currCal == AH_NULL)346return AH_FALSE;347if (ichan == AH_NULL) {348HALDEBUG(ah, HAL_DEBUG_ANY,349"%s: invalid channel %u/0x%x; no mapping\n",350__func__, chan->ic_freq, chan->ic_flags);351return AH_FALSE;352}353/*354* Expected that this calibration has run before, post-reset.355* Current state should be done356*/357if (currCal->calState != CAL_DONE) {358HALDEBUG(ah, HAL_DEBUG_ANY,359"%s: Calibration state incorrect, %d\n",360__func__, currCal->calState);361return AH_FALSE;362}363364/* Verify Cal is supported on this channel */365if (!ar5416IsCalSupp(ah, chan, currCal->calData->calType))366return AH_FALSE;367368HALDEBUG(ah, HAL_DEBUG_PERCAL,369"%s: Resetting Cal %d state for channel %u/0x%x\n",370__func__, currCal->calData->calType, chan->ic_freq,371chan->ic_flags);372373/* Disable cal validity in channel */374ichan->calValid &= ~currCal->calData->calType;375currCal->calState = CAL_WAITING;376377return AH_TRUE;378}379380/*381* Recalibrate the lower PHY chips to account for temperature/environment382* changes.383*/384static void385ar5416DoCalibration(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan,386uint8_t rxchainmask, HAL_CAL_LIST *currCal, HAL_BOOL *isCalDone)387{388struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;389390/* Cal is assumed not done until explicitly set below */391*isCalDone = AH_FALSE;392393HALDEBUG(ah, HAL_DEBUG_PERCAL,394"%s: %s Calibration, state %d, calValid 0x%x\n",395__func__, currCal->calData->calName, currCal->calState,396ichan->calValid);397398/* Calibration in progress. */399if (currCal->calState == CAL_RUNNING) {400/* Check to see if it has finished. */401if (!(OS_REG_READ(ah, AR_PHY_TIMING_CTRL4) & AR_PHY_TIMING_CTRL4_DO_CAL)) {402HALDEBUG(ah, HAL_DEBUG_PERCAL,403"%s: sample %d of %d finished\n",404__func__, cal->calSamples,405currCal->calData->calNumSamples);406/*407* Collect measurements for active chains.408*/409currCal->calData->calCollect(ah);410if (++cal->calSamples >= currCal->calData->calNumSamples) {411int i, numChains = 0;412for (i = 0; i < AR5416_MAX_CHAINS; i++) {413if (rxchainmask & (1 << i))414numChains++;415}416/*417* Process accumulated data418*/419currCal->calData->calPostProc(ah, numChains);420421/* Calibration has finished. */422ichan->calValid |= currCal->calData->calType;423currCal->calState = CAL_DONE;424*isCalDone = AH_TRUE;425} else {426/*427* Set-up to collect of another sub-sample.428*/429ar5416SetupMeasurement(ah, currCal);430}431}432} else if (!(ichan->calValid & currCal->calData->calType)) {433/* If current cal is marked invalid in channel, kick it off */434ar5416ResetMeasurement(ah, currCal);435}436}437438/*439* Internal interface to schedule periodic calibration work.440*/441HAL_BOOL442ar5416PerCalibrationN(struct ath_hal *ah, struct ieee80211_channel *chan,443u_int rxchainmask, HAL_BOOL longcal, HAL_BOOL *isCalDone)444{445struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;446HAL_CAL_LIST *currCal = cal->cal_curr;447HAL_CHANNEL_INTERNAL *ichan;448int r;449450OS_MARK(ah, AH_MARK_PERCAL, chan->ic_freq);451452*isCalDone = AH_TRUE;453454/*455* Since ath_hal calls the PerCal method with rxchainmask=0x1;456* override it with the current chainmask. The upper levels currently457* doesn't know about the chainmask.458*/459rxchainmask = AH5416(ah)->ah_rx_chainmask;460461/* Invalid channel check */462ichan = ath_hal_checkchannel(ah, chan);463if (ichan == AH_NULL) {464HALDEBUG(ah, HAL_DEBUG_ANY,465"%s: invalid channel %u/0x%x; no mapping\n",466__func__, chan->ic_freq, chan->ic_flags);467return AH_FALSE;468}469470/*471* For given calibration:472* 1. Call generic cal routine473* 2. When this cal is done (isCalDone) if we have more cals waiting474* (eg after reset), mask this to upper layers by not propagating475* isCalDone if it is set to TRUE.476* Instead, change isCalDone to FALSE and setup the waiting cal(s)477* to be run.478*/479if (currCal != AH_NULL &&480(currCal->calState == CAL_RUNNING ||481currCal->calState == CAL_WAITING)) {482ar5416DoCalibration(ah, ichan, rxchainmask, currCal, isCalDone);483if (*isCalDone == AH_TRUE) {484cal->cal_curr = currCal = currCal->calNext;485if (currCal->calState == CAL_WAITING) {486*isCalDone = AH_FALSE;487ar5416ResetMeasurement(ah, currCal);488}489}490}491492/* Do NF cal only at longer intervals */493if (longcal) {494/* Do PA calibration if the chipset supports */495if (AH5416(ah)->ah_cal_pacal)496AH5416(ah)->ah_cal_pacal(ah, AH_FALSE);497498/* Do open-loop temperature compensation if the chipset needs it */499if (ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))500AH5416(ah)->ah_olcTempCompensation(ah);501502/*503* Get the value from the previous NF cal504* and update the history buffer.505*/506r = ar5416GetNf(ah, chan);507if (r == 0 || r == -1) {508/* NF calibration result isn't valid */509HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: NF calibration"510" didn't finish; delaying CCA\n", __func__);511} else {512int ret;513/*514* NF calibration result is valid.515*516* Load the NF from history buffer of the current channel.517* NF is slow time-variant, so it is OK to use a518* historical value.519*/520ret = ar5416LoadNF(ah, AH_PRIVATE(ah)->ah_curchan);521522/* start NF calibration, without updating BB NF register*/523ar5416StartNFCal(ah);524525/*526* If we failed calibration then tell the driver527* we failed and it should do a full chip reset528*/529if (! ret)530return AH_FALSE;531}532}533return AH_TRUE;534}535536/*537* Recalibrate the lower PHY chips to account for temperature/environment538* changes.539*/540HAL_BOOL541ar5416PerCalibration(struct ath_hal *ah, struct ieee80211_channel *chan,542HAL_BOOL *isIQdone)543{544struct ath_hal_5416 *ahp = AH5416(ah);545struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;546HAL_CAL_LIST *curCal = cal->cal_curr;547548if (curCal != AH_NULL && curCal->calData->calType == IQ_MISMATCH_CAL) {549return ar5416PerCalibrationN(ah, chan, ahp->ah_rx_chainmask,550AH_TRUE, isIQdone);551} else {552HAL_BOOL isCalDone;553554*isIQdone = AH_FALSE;555return ar5416PerCalibrationN(ah, chan, ahp->ah_rx_chainmask,556AH_TRUE, &isCalDone);557}558}559560static HAL_BOOL561ar5416GetEepromNoiseFloorThresh(struct ath_hal *ah,562const struct ieee80211_channel *chan, int16_t *nft)563{564if (IEEE80211_IS_CHAN_5GHZ(chan)) {565ath_hal_eepromGet(ah, AR_EEP_NFTHRESH_5, nft);566return AH_TRUE;567}568if (IEEE80211_IS_CHAN_2GHZ(chan)) {569ath_hal_eepromGet(ah, AR_EEP_NFTHRESH_2, nft);570return AH_TRUE;571}572HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n",573__func__, chan->ic_flags);574return AH_FALSE;575}576577static void578ar5416StartNFCal(struct ath_hal *ah)579{580OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);581OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);582OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);583}584585static HAL_BOOL586ar5416LoadNF(struct ath_hal *ah, const struct ieee80211_channel *chan)587{588static const uint32_t ar5416_cca_regs[] = {589AR_PHY_CCA,590AR_PHY_CH1_CCA,591AR_PHY_CH2_CCA,592AR_PHY_EXT_CCA,593AR_PHY_CH1_EXT_CCA,594AR_PHY_CH2_EXT_CCA595};596struct ar5212NfCalHist *h;597int i;598int32_t val;599uint8_t chainmask;600int16_t default_nf = ar5416GetDefaultNF(ah, chan);601602/*603* Force NF calibration for all chains.604*/605if (AR_SREV_KITE(ah)) {606/* Kite has only one chain */607chainmask = 0x9;608} else if (AR_SREV_MERLIN(ah) || AR_SREV_KIWI(ah)) {609/* Merlin/Kiwi has only two chains */610chainmask = 0x1B;611} else {612chainmask = 0x3F;613}614615/*616* Write filtered NF values into maxCCApwr register parameter617* so we can load below.618*/619h = AH5416(ah)->ah_cal.nfCalHist;620HALDEBUG(ah, HAL_DEBUG_NFCAL, "CCA: ");621for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {622/* Don't write to EXT radio CCA registers unless in HT/40 mode */623/* XXX this check should really be cleaner! */624if (i > 2 && !IEEE80211_IS_CHAN_HT40(chan))625continue;626627if (chainmask & (1 << i)) {628int16_t nf_val;629630if (h)631nf_val = h[i].privNF;632else633nf_val = default_nf;634635val = OS_REG_READ(ah, ar5416_cca_regs[i]);636val &= 0xFFFFFE00;637val |= (((uint32_t) nf_val << 1) & 0x1ff);638HALDEBUG(ah, HAL_DEBUG_NFCAL, "[%d: %d]", i, nf_val);639OS_REG_WRITE(ah, ar5416_cca_regs[i], val);640}641}642HALDEBUG(ah, HAL_DEBUG_NFCAL, "\n");643644/* Load software filtered NF value into baseband internal minCCApwr variable. */645OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);646OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);647OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);648649/* Wait for load to complete, should be fast, a few 10s of us. */650if (! ar5212WaitNFCalComplete(ah, 1000)) {651/*652* We timed out waiting for the noisefloor to load, probably due to an653* in-progress rx. Simply return here and allow the load plenty of time654* to complete before the next calibration interval. We need to avoid655* trying to load -50 (which happens below) while the previous load is656* still in progress as this can cause rx deafness. Instead by returning657* here, the baseband nf cal will just be capped by our present658* noisefloor until the next calibration timer.659*/660HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "Timeout while waiting for "661"nf to load: AR_PHY_AGC_CONTROL=0x%x\n",662OS_REG_READ(ah, AR_PHY_AGC_CONTROL));663return AH_FALSE;664}665666/*667* Restore maxCCAPower register parameter again so that we're not capped668* by the median we just loaded. This will be initial (and max) value669* of next noise floor calibration the baseband does.670*/671for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {672/* Don't write to EXT radio CCA registers unless in HT/40 mode */673/* XXX this check should really be cleaner! */674if (i > 2 && !IEEE80211_IS_CHAN_HT40(chan))675continue;676677if (chainmask & (1 << i)) {678val = OS_REG_READ(ah, ar5416_cca_regs[i]);679val &= 0xFFFFFE00;680val |= (((uint32_t)(-50) << 1) & 0x1ff);681OS_REG_WRITE(ah, ar5416_cca_regs[i], val);682}683}684return AH_TRUE;685}686687/*688* This just initialises the "good" values for AR5416 which689* may not be right; it'lll be overridden by ar5416SanitizeNF()690* to nominal values.691*/692void693ar5416InitNfHistBuff(struct ar5212NfCalHist *h)694{695int i, j;696697for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {698h[i].currIndex = 0;699h[i].privNF = AR5416_CCA_MAX_GOOD_VALUE;700h[i].invalidNFcount = AR512_NF_CAL_HIST_MAX;701for (j = 0; j < AR512_NF_CAL_HIST_MAX; j ++)702h[i].nfCalBuffer[j] = AR5416_CCA_MAX_GOOD_VALUE;703}704}705706/*707* Update the noise floor buffer as a ring buffer708*/709static void710ar5416UpdateNFHistBuff(struct ath_hal *ah, struct ar5212NfCalHist *h,711int16_t *nfarray)712{713int i;714715/* XXX TODO: don't record nfarray[] entries for inactive chains */716for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {717h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];718719if (++h[i].currIndex >= AR512_NF_CAL_HIST_MAX)720h[i].currIndex = 0;721if (h[i].invalidNFcount > 0) {722if (nfarray[i] < AR5416_CCA_MIN_BAD_VALUE ||723nfarray[i] > AR5416_CCA_MAX_HIGH_VALUE) {724h[i].invalidNFcount = AR512_NF_CAL_HIST_MAX;725} else {726h[i].invalidNFcount--;727h[i].privNF = nfarray[i];728}729} else {730h[i].privNF = ar5212GetNfHistMid(h[i].nfCalBuffer);731}732}733}734735static uint16_t736ar5416GetDefaultNF(struct ath_hal *ah, const struct ieee80211_channel *chan)737{738struct ar5416NfLimits *limit;739740if (!chan || IEEE80211_IS_CHAN_2GHZ(chan))741limit = &AH5416(ah)->nf_2g;742else743limit = &AH5416(ah)->nf_5g;744745return limit->nominal;746}747748static void749ar5416SanitizeNF(struct ath_hal *ah, int16_t *nf)750{751752struct ar5416NfLimits *limit;753int i;754755if (IEEE80211_IS_CHAN_2GHZ(AH_PRIVATE(ah)->ah_curchan))756limit = &AH5416(ah)->nf_2g;757else758limit = &AH5416(ah)->nf_5g;759760for (i = 0; i < AR5416_NUM_NF_READINGS; i++) {761if (!nf[i])762continue;763764if (nf[i] > limit->max) {765HALDEBUG(ah, HAL_DEBUG_NFCAL,766"NF[%d] (%d) > MAX (%d), correcting to MAX\n",767i, nf[i], limit->max);768nf[i] = limit->max;769} else if (nf[i] < limit->min) {770HALDEBUG(ah, HAL_DEBUG_NFCAL,771"NF[%d] (%d) < MIN (%d), correcting to NOM\n",772i, nf[i], limit->min);773nf[i] = limit->nominal;774}775}776}777778/*779* Read the NF and check it against the noise floor threshold780*781* Return 0 if the NF calibration hadn't finished, 0 if it was782* invalid, or > 0 for a valid NF reading.783*/784static int16_t785ar5416GetNf(struct ath_hal *ah, struct ieee80211_channel *chan)786{787int16_t nf, nfThresh;788int i;789int retval = 0;790791if (ar5212IsNFCalInProgress(ah)) {792HALDEBUG(ah, HAL_DEBUG_ANY,793"%s: NF didn't complete in calibration window\n", __func__);794nf = 0;795retval = -1; /* NF didn't finish */796} else {797/* Finished NF cal, check against threshold */798int16_t nfarray[NUM_NOISEFLOOR_READINGS] = { 0 };799HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);800801/* TODO - enhance for multiple chains and ext ch */802ath_hal_getNoiseFloor(ah, nfarray);803nf = nfarray[0];804ar5416SanitizeNF(ah, nfarray);805if (ar5416GetEepromNoiseFloorThresh(ah, chan, &nfThresh)) {806if (nf > nfThresh) {807HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,808"%s: noise floor failed detected; "809"detected %d, threshold %d\n", __func__,810nf, nfThresh);811/*812* NB: Don't discriminate 2.4 vs 5Ghz, if this813* happens it indicates a problem regardless814* of the band.815*/816chan->ic_state |= IEEE80211_CHANSTATE_CWINT;817nf = 0;818retval = 0;819}820} else {821nf = 0;822retval = 0;823}824/* Update MIMO channel statistics, regardless of validity or not (for now) */825for (i = 0; i < 3; i++) {826ichan->noiseFloorCtl[i] = nfarray[i];827ichan->noiseFloorExt[i] = nfarray[i + 3];828}829ichan->privFlags |= CHANNEL_MIMO_NF_VALID;830831ar5416UpdateNFHistBuff(ah, AH5416(ah)->ah_cal.nfCalHist, nfarray);832ichan->rawNoiseFloor = nf;833retval = nf;834}835return retval;836}837838839