Path: blob/main/sys/dev/ath/ath_hal/ar5416/ar5416_ani.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/*21* XXX this is virtually the same code as for 5212; we reuse22* storage in the 5212 state block; need to refactor.23*/24#include "ah.h"25#include "ah_internal.h"26#include "ah_desc.h"2728#include "ar5416/ar5416.h"29#include "ar5416/ar5416reg.h"30#include "ar5416/ar5416phy.h"3132/*33* Anti noise immunity support. We track phy errors and react34* to excessive errors by adjusting the noise immunity parameters.35*/3637#define HAL_EP_RND(x, mul) \38((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))39#define BEACON_RSSI(ahp) \40HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \41HAL_RSSI_EP_MULTIPLIER)4243/*44* ANI processing tunes radio parameters according to PHY errors45* and related information. This is done for noise and spur46* immunity in all operating modes if the device indicates it's47* capable at attach time. In addition, when there is a reference48* rssi value (e.g. beacon frames from an ap in station mode)49* further tuning is done.50*51* ANI_ENA indicates whether any ANI processing should be done;52* this is specified at attach time.53*54* ANI_ENA_RSSI indicates whether rssi-based processing should55* done, this is enabled based on operating mode and is meaningful56* only if ANI_ENA is true.57*58* ANI parameters are typically controlled only by the hal. The59* AniControl interface however permits manual tuning through the60* diagnostic api.61*/62#define ANI_ENA(ah) \63(AH5212(ah)->ah_procPhyErr & HAL_ANI_ENA)64#define ANI_ENA_RSSI(ah) \65(AH5212(ah)->ah_procPhyErr & HAL_RSSI_ANI_ENA)6667#define ah_mibStats ah_stats.ast_mibstats6869static void70enableAniMIBCounters(struct ath_hal *ah, const struct ar5212AniParams *params)71{72struct ath_hal_5212 *ahp = AH5212(ah);7374HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Enable mib counters: "75"OfdmPhyErrBase 0x%x cckPhyErrBase 0x%x\n",76__func__, params->ofdmPhyErrBase, params->cckPhyErrBase);7778OS_REG_WRITE(ah, AR_FILTOFDM, 0);79OS_REG_WRITE(ah, AR_FILTCCK, 0);8081OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase);82OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase);83OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);84OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);8586ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save+clear counters*/87ar5212EnableMibCounters(ah); /* enable everything */88}8990static void91disableAniMIBCounters(struct ath_hal *ah)92{93struct ath_hal_5212 *ahp = AH5212(ah);9495HALDEBUG(ah, HAL_DEBUG_ANI, "Disable MIB counters\n");9697ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save stats */98ar5212DisableMibCounters(ah); /* disable everything */99100OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, 0);101OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, 0);102}103104static void105setPhyErrBase(struct ath_hal *ah, struct ar5212AniParams *params)106{107if (params->ofdmTrigHigh >= AR_PHY_COUNTMAX) {108HALDEBUG(ah, HAL_DEBUG_ANY,109"OFDM Trigger %d is too high for hw counters, using max\n",110params->ofdmTrigHigh);111params->ofdmPhyErrBase = 0;112} else113params->ofdmPhyErrBase = AR_PHY_COUNTMAX - params->ofdmTrigHigh;114if (params->cckTrigHigh >= AR_PHY_COUNTMAX) {115HALDEBUG(ah, HAL_DEBUG_ANY,116"CCK Trigger %d is too high for hw counters, using max\n",117params->cckTrigHigh);118params->cckPhyErrBase = 0;119} else120params->cckPhyErrBase = AR_PHY_COUNTMAX - params->cckTrigHigh;121}122123/*124* Setup ANI handling. Sets all thresholds and reset the125* channel statistics. Note that ar5416AniReset should be126* called by ar5416Reset before anything else happens and127* that's where we force initial settings.128*/129void130ar5416AniAttach(struct ath_hal *ah, const struct ar5212AniParams *params24,131const struct ar5212AniParams *params5, HAL_BOOL enable)132{133struct ath_hal_5212 *ahp = AH5212(ah);134135if (params24 != AH_NULL) {136OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24));137setPhyErrBase(ah, &ahp->ah_aniParams24);138}139if (params5 != AH_NULL) {140OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5));141setPhyErrBase(ah, &ahp->ah_aniParams5);142}143144OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani));145/* Enable MIB Counters */146enableAniMIBCounters(ah, &ahp->ah_aniParams24 /*XXX*/);147148if (enable) { /* Enable ani now */149HALASSERT(params24 != AH_NULL && params5 != AH_NULL);150ahp->ah_procPhyErr |= HAL_ANI_ENA;151} else {152ahp->ah_procPhyErr &= ~HAL_ANI_ENA;153}154}155156/*157* Cleanup any ANI state setup.158*159* This doesn't restore registers to their default settings!160*/161void162ar5416AniDetach(struct ath_hal *ah)163{164HALDEBUG(ah, HAL_DEBUG_ANI, "Detaching Ani\n");165disableAniMIBCounters(ah);166}167168/*169* Control Adaptive Noise Immunity Parameters170*/171HAL_BOOL172ar5416AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param)173{174typedef int TABLE[];175struct ath_hal_5212 *ahp = AH5212(ah);176struct ar5212AniState *aniState = ahp->ah_curani;177const struct ar5212AniParams *params = AH_NULL;178179/*180* This function may be called before there's a current181* channel (eg to disable ANI.)182*/183if (aniState != AH_NULL)184params = aniState->params;185186OS_MARK(ah, AH_MARK_ANI_CONTROL, cmd);187188/* These commands can't be disabled */189if (cmd == HAL_ANI_PRESENT)190return AH_TRUE;191192if (cmd == HAL_ANI_MODE) {193if (param == 0) {194ahp->ah_procPhyErr &= ~HAL_ANI_ENA;195/* Turn off HW counters if we have them */196ar5416AniDetach(ah);197} else { /* normal/auto mode */198/* don't mess with state if already enabled */199if (! (ahp->ah_procPhyErr & HAL_ANI_ENA)) {200/* Enable MIB Counters */201/*202* XXX use 2.4ghz params if no channel is203* available204*/205enableAniMIBCounters(ah,206ahp->ah_curani != AH_NULL ?207ahp->ah_curani->params:208&ahp->ah_aniParams24);209ahp->ah_procPhyErr |= HAL_ANI_ENA;210}211}212return AH_TRUE;213}214215/* Check whether the particular function is enabled */216if (((1 << cmd) & AH5416(ah)->ah_ani_function) == 0) {217HALDEBUG(ah, HAL_DEBUG_ANI, "%s: command %d disabled\n",218__func__, cmd);219HALDEBUG(ah, HAL_DEBUG_ANI, "%s: cmd %d; mask %x\n", __func__, cmd, AH5416(ah)->ah_ani_function);220return AH_FALSE;221}222223switch (cmd) {224case HAL_ANI_NOISE_IMMUNITY_LEVEL: {225u_int level = param;226227HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_NOISE_IMMUNITY_LEVEL: set level = %d\n", __func__, level);228if (level > params->maxNoiseImmunityLevel) {229HALDEBUG(ah, HAL_DEBUG_ANI,230"%s: immunity level out of range (%u > %u)\n",231__func__, level, params->maxNoiseImmunityLevel);232return AH_FALSE;233}234235OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ,236AR_PHY_DESIRED_SZ_TOT_DES, params->totalSizeDesired[level]);237OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,238AR_PHY_AGC_CTL1_COARSE_LOW, params->coarseLow[level]);239OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,240AR_PHY_AGC_CTL1_COARSE_HIGH, params->coarseHigh[level]);241OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,242AR_PHY_FIND_SIG_FIRPWR, params->firpwr[level]);243244if (level > aniState->noiseImmunityLevel)245ahp->ah_stats.ast_ani_niup++;246else if (level < aniState->noiseImmunityLevel)247ahp->ah_stats.ast_ani_nidown++;248aniState->noiseImmunityLevel = level;249break;250}251case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: {252static const TABLE m1ThreshLow = { 127, 50 };253static const TABLE m2ThreshLow = { 127, 40 };254static const TABLE m1Thresh = { 127, 0x4d };255static const TABLE m2Thresh = { 127, 0x40 };256static const TABLE m2CountThr = { 31, 16 };257static const TABLE m2CountThrLow = { 63, 48 };258u_int on = param ? 1 : 0;259260HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: %s\n", __func__, on ? "enabled" : "disabled");261OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,262AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1ThreshLow[on]);263OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,264AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2ThreshLow[on]);265OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,266AR_PHY_SFCORR_M1_THRESH, m1Thresh[on]);267OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,268AR_PHY_SFCORR_M2_THRESH, m2Thresh[on]);269OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,270AR_PHY_SFCORR_M2COUNT_THR, m2CountThr[on]);271OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,272AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2CountThrLow[on]);273274OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,275AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLow[on]);276OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,277AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLow[on]);278OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,279AR_PHY_SFCORR_EXT_M1_THRESH, m1Thresh[on]);280OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,281AR_PHY_SFCORR_EXT_M2_THRESH, m2Thresh[on]);282283if (on) {284OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,285AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);286} else {287OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,288AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);289}290if (on)291ahp->ah_stats.ast_ani_ofdmon++;292else293ahp->ah_stats.ast_ani_ofdmoff++;294aniState->ofdmWeakSigDetectOff = !on;295break;296}297case HAL_ANI_CCK_WEAK_SIGNAL_THR: {298static const TABLE weakSigThrCck = { 8, 6 };299u_int high = param ? 1 : 0;300301HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_CCK_WEAK_SIGNAL_THR: %s\n", __func__, high ? "high" : "low");302OS_REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT,303AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, weakSigThrCck[high]);304if (high)305ahp->ah_stats.ast_ani_cckhigh++;306else307ahp->ah_stats.ast_ani_ccklow++;308aniState->cckWeakSigThreshold = high;309break;310}311case HAL_ANI_FIRSTEP_LEVEL: {312u_int level = param;313314HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_FIRSTEP_LEVEL: level = %d\n", __func__, level);315if (level > params->maxFirstepLevel) {316HALDEBUG(ah, HAL_DEBUG_ANI,317"%s: firstep level out of range (%u > %u)\n",318__func__, level, params->maxFirstepLevel);319return AH_FALSE;320}321OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,322AR_PHY_FIND_SIG_FIRSTEP, params->firstep[level]);323if (level > aniState->firstepLevel)324ahp->ah_stats.ast_ani_stepup++;325else if (level < aniState->firstepLevel)326ahp->ah_stats.ast_ani_stepdown++;327aniState->firstepLevel = level;328break;329}330case HAL_ANI_SPUR_IMMUNITY_LEVEL: {331u_int level = param;332333HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_SPUR_IMMUNITY_LEVEL: level = %d\n", __func__, level);334if (level > params->maxSpurImmunityLevel) {335HALDEBUG(ah, HAL_DEBUG_ANI,336"%s: spur immunity level out of range (%u > %u)\n",337__func__, level, params->maxSpurImmunityLevel);338return AH_FALSE;339}340OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5,341AR_PHY_TIMING5_CYCPWR_THR1, params->cycPwrThr1[level]);342343if (level > aniState->spurImmunityLevel)344ahp->ah_stats.ast_ani_spurup++;345else if (level < aniState->spurImmunityLevel)346ahp->ah_stats.ast_ani_spurdown++;347aniState->spurImmunityLevel = level;348break;349}350#ifdef AH_PRIVATE_DIAG351case HAL_ANI_PHYERR_RESET:352ahp->ah_stats.ast_ani_ofdmerrs = 0;353ahp->ah_stats.ast_ani_cckerrs = 0;354break;355#endif /* AH_PRIVATE_DIAG */356default:357HALDEBUG(ah, HAL_DEBUG_ANI, "%s: invalid cmd %u\n",358__func__, cmd);359return AH_FALSE;360}361return AH_TRUE;362}363364static void365ar5416AniOfdmErrTrigger(struct ath_hal *ah)366{367struct ath_hal_5212 *ahp = AH5212(ah);368const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;369struct ar5212AniState *aniState;370const struct ar5212AniParams *params;371372HALASSERT(chan != AH_NULL);373374if (!ANI_ENA(ah))375return;376377aniState = ahp->ah_curani;378params = aniState->params;379/* First, raise noise immunity level, up to max */380if (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel) {381if (ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,382aniState->noiseImmunityLevel + 1))383return;384}385/* then, raise spur immunity level, up to max */386if (aniState->spurImmunityLevel+1 < params->maxSpurImmunityLevel) {387if (ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,388aniState->spurImmunityLevel + 1))389return;390}391392/*393* In the case of AP mode operation, we cannot bucketize beacons394* according to RSSI. Instead, raise Firstep level, up to max, and395* simply return.396*/397if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) {398if (aniState->firstepLevel < params->maxFirstepLevel) {399if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,400aniState->firstepLevel + 1))401return;402}403}404if (ANI_ENA_RSSI(ah)) {405int32_t rssi = BEACON_RSSI(ahp);406if (rssi > params->rssiThrHigh) {407/*408* Beacon rssi is high, can turn off ofdm409* weak sig detect.410*/411if (!aniState->ofdmWeakSigDetectOff) {412ar5416AniControl(ah,413HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,414AH_FALSE);415ar5416AniControl(ah,416HAL_ANI_SPUR_IMMUNITY_LEVEL, 0);417return;418}419/*420* If weak sig detect is already off, as last resort,421* raise firstep level422*/423if (aniState->firstepLevel < params->maxFirstepLevel) {424if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,425aniState->firstepLevel + 1))426return;427}428} else if (rssi > params->rssiThrLow) {429/*430* Beacon rssi in mid range, need ofdm weak signal431* detect, but we can raise firststepLevel.432*/433if (aniState->ofdmWeakSigDetectOff)434ar5416AniControl(ah,435HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,436AH_TRUE);437if (aniState->firstepLevel < params->maxFirstepLevel)438if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,439aniState->firstepLevel + 1))440return;441} else {442/*443* Beacon rssi is low, if in 11b/g mode, turn off ofdm444* weak signal detection and zero firstepLevel to445* maximize CCK sensitivity446*/447if (IEEE80211_IS_CHAN_CCK(chan)) {448if (!aniState->ofdmWeakSigDetectOff)449ar5416AniControl(ah,450HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,451AH_FALSE);452if (aniState->firstepLevel > 0)453if (ar5416AniControl(ah,454HAL_ANI_FIRSTEP_LEVEL, 0))455return;456}457}458}459}460461static void462ar5416AniCckErrTrigger(struct ath_hal *ah)463{464struct ath_hal_5212 *ahp = AH5212(ah);465const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;466struct ar5212AniState *aniState;467const struct ar5212AniParams *params;468469HALASSERT(chan != AH_NULL);470471if (!ANI_ENA(ah))472return;473474/* first, raise noise immunity level, up to max */475aniState = ahp->ah_curani;476params = aniState->params;477if ((AH5416(ah)->ah_ani_function & (1 << HAL_ANI_NOISE_IMMUNITY_LEVEL) &&478aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel)) {479ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,480aniState->noiseImmunityLevel + 1);481return;482}483484if (ANI_ENA_RSSI(ah)) {485int32_t rssi = BEACON_RSSI(ahp);486if (rssi > params->rssiThrLow) {487/*488* Beacon signal in mid and high range,489* raise firstep level.490*/491if (aniState->firstepLevel < params->maxFirstepLevel)492ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,493aniState->firstepLevel + 1);494} else {495/*496* Beacon rssi is low, zero firstep level to maximize497* CCK sensitivity in 11b/g mode.498*/499if (IEEE80211_IS_CHAN_CCK(chan)) {500if (aniState->firstepLevel > 0)501ar5416AniControl(ah,502HAL_ANI_FIRSTEP_LEVEL, 0);503}504}505}506}507508static void509ar5416AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState)510{511struct ath_hal_5212 *ahp = AH5212(ah);512const struct ar5212AniParams *params = aniState->params;513514aniState->listenTime = 0;515/*516* NB: these are written on reset based on the517* ini so we must re-write them!518*/519HALDEBUG(ah, HAL_DEBUG_ANI,520"%s: Writing ofdmbase=%u cckbase=%u\n", __func__,521params->ofdmPhyErrBase, params->cckPhyErrBase);522OS_REG_WRITE(ah, AR_PHY_ERR_1, params->ofdmPhyErrBase);523OS_REG_WRITE(ah, AR_PHY_ERR_2, params->cckPhyErrBase);524OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);525OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);526527/* Clear the mib counters and save them in the stats */528ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);529aniState->ofdmPhyErrCount = 0;530aniState->cckPhyErrCount = 0;531}532533/*534* Restore/reset the ANI parameters and reset the statistics.535* This routine must be called for every channel change.536*537* NOTE: This is where ah_curani is set; other ani code assumes538* it is setup to reflect the current channel.539*/540void541ar5416AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan,542HAL_OPMODE opmode, int restore)543{544struct ath_hal_5212 *ahp = AH5212(ah);545HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);546/* XXX bounds check ic_devdata */547struct ar5212AniState *aniState = &ahp->ah_ani[chan->ic_devdata];548uint32_t rxfilter;549550if ((ichan->privFlags & CHANNEL_ANI_INIT) == 0) {551OS_MEMZERO(aniState, sizeof(*aniState));552if (IEEE80211_IS_CHAN_2GHZ(chan))553aniState->params = &ahp->ah_aniParams24;554else555aniState->params = &ahp->ah_aniParams5;556ichan->privFlags |= CHANNEL_ANI_INIT;557HALASSERT((ichan->privFlags & CHANNEL_ANI_SETUP) == 0);558}559ahp->ah_curani = aniState;560#if 0561ath_hal_printf(ah,"%s: chan %u/0x%x restore %d opmode %u%s\n",562__func__, chan->ic_freq, chan->ic_flags, restore, opmode,563ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : "");564#else565HALDEBUG(ah, HAL_DEBUG_ANI, "%s: chan %u/0x%x restore %d opmode %u%s\n",566__func__, chan->ic_freq, chan->ic_flags, restore, opmode,567ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : "");568#endif569OS_MARK(ah, AH_MARK_ANI_RESET, opmode);570571/*572* Turn off PHY error frame delivery while we futz with settings.573*/574rxfilter = ah->ah_getRxFilter(ah);575ah->ah_setRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR);576577/*578* If ANI is disabled at this point, don't set the default579* ANI parameter settings - leave the HAL settings there.580* This is (currently) needed for reliable radar detection.581*/582if (! ANI_ENA(ah)) {583HALDEBUG(ah, HAL_DEBUG_ANI, "%s: ANI disabled\n",584__func__);585goto finish;586}587588/*589* Use a restrictive set of ANI parameters for hostap mode.590*/591if (opmode == HAL_M_HOSTAP) {592if (IEEE80211_IS_CHAN_2GHZ(chan))593AH5416(ah)->ah_ani_function =594HAL_ANI_SPUR_IMMUNITY_LEVEL | HAL_ANI_FIRSTEP_LEVEL;595else596AH5416(ah)->ah_ani_function = 0;597}598599/*600* Automatic processing is done only in station mode right now.601*/602if (opmode == HAL_M_STA)603ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA;604else605ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA;606/*607* Set all ani parameters. We either set them to initial608* values or restore the previous ones for the channel.609* XXX if ANI follows hardware, we don't care what mode we're610* XXX in, we should keep the ani parameters611*/612if (restore && (ichan->privFlags & CHANNEL_ANI_SETUP)) {613ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,614aniState->noiseImmunityLevel);615ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,616aniState->spurImmunityLevel);617ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,618!aniState->ofdmWeakSigDetectOff);619ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR,620aniState->cckWeakSigThreshold);621ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,622aniState->firstepLevel);623} else {624ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0);625ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0);626ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,627AH_FALSE);628ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE);629ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0);630ichan->privFlags |= CHANNEL_ANI_SETUP;631}632633/*634* In case the counters haven't yet been setup; set them up.635*/636enableAniMIBCounters(ah, aniState->params);637ar5416AniRestart(ah, aniState);638639finish:640/* restore RX filter mask */641ah->ah_setRxFilter(ah, rxfilter);642}643644/*645* Process a MIB interrupt. We may potentially be invoked because646* any of the MIB counters overflow/trigger so don't assume we're647* here because a PHY error counter triggered.648*/649void650ar5416ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats)651{652struct ath_hal_5212 *ahp = AH5212(ah);653uint32_t phyCnt1, phyCnt2;654655HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x "656"filtofdm 0x%x filtcck 0x%x\n",657__func__, OS_REG_READ(ah, AR_MIBC),658OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2),659OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK));660661/*662* First order of business is to clear whatever caused663* the interrupt so we don't keep getting interrupted.664* We have the usual mib counters that are reset-on-read665* and the additional counters that appeared starting in666* Hainan. We collect the mib counters and explicitly667* zero additional counters we are not using. Anything668* else is reset only if it caused the interrupt.669*/670/* NB: these are not reset-on-read */671phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1);672phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2);673/* not used, always reset them in case they are the cause */674OS_REG_WRITE(ah, AR_FILTOFDM, 0);675OS_REG_WRITE(ah, AR_FILTCCK, 0);676if ((OS_REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING) == 0)677OS_REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);678679/* Clear the mib counters and save them in the stats */680ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);681ahp->ah_stats.ast_nodestats = *stats;682683/*684* Check for an ani stat hitting the trigger threshold.685* When this happens we get a MIB interrupt and the top686* 2 bits of the counter register will be 0b11, hence687* the mask check of phyCnt?.688*/689if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||690((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {691struct ar5212AniState *aniState = ahp->ah_curani;692const struct ar5212AniParams *params = aniState->params;693uint32_t ofdmPhyErrCnt, cckPhyErrCnt;694695ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase;696ahp->ah_stats.ast_ani_ofdmerrs +=697ofdmPhyErrCnt - aniState->ofdmPhyErrCount;698aniState->ofdmPhyErrCount = ofdmPhyErrCnt;699700cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase;701ahp->ah_stats.ast_ani_cckerrs +=702cckPhyErrCnt - aniState->cckPhyErrCount;703aniState->cckPhyErrCount = cckPhyErrCnt;704705/*706* NB: figure out which counter triggered. If both707* trigger we'll only deal with one as the processing708* clobbers the error counter so the trigger threshold709* check will never be true.710*/711if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh)712ar5416AniOfdmErrTrigger(ah);713if (aniState->cckPhyErrCount > params->cckTrigHigh)714ar5416AniCckErrTrigger(ah);715/* NB: always restart to insure the h/w counters are reset */716ar5416AniRestart(ah, aniState);717}718}719720static void721ar5416AniLowerImmunity(struct ath_hal *ah)722{723struct ath_hal_5212 *ahp = AH5212(ah);724struct ar5212AniState *aniState;725const struct ar5212AniParams *params;726727HALASSERT(ANI_ENA(ah));728729aniState = ahp->ah_curani;730params = aniState->params;731732/*733* In the case of AP mode operation, we cannot bucketize beacons734* according to RSSI. Instead, lower Firstep level, down to min, and735* simply return.736*/737if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) {738if (aniState->firstepLevel > 0) {739if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,740aniState->firstepLevel - 1))741return;742}743}744if (ANI_ENA_RSSI(ah)) {745int32_t rssi = BEACON_RSSI(ahp);746if (rssi > params->rssiThrHigh) {747/*748* Beacon signal is high, leave ofdm weak signal749* detection off or it may oscillate. Let it fall750* through.751*/752} else if (rssi > params->rssiThrLow) {753/*754* Beacon rssi in mid range, turn on ofdm weak signal755* detection or lower firstep level.756*/757if (aniState->ofdmWeakSigDetectOff) {758if (ar5416AniControl(ah,759HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,760AH_TRUE))761return;762}763if (aniState->firstepLevel > 0) {764if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,765aniState->firstepLevel - 1))766return;767}768} else {769/*770* Beacon rssi is low, reduce firstep level.771*/772if (aniState->firstepLevel > 0) {773if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,774aniState->firstepLevel - 1))775return;776}777}778}779/* then lower spur immunity level, down to zero */780if (aniState->spurImmunityLevel > 0) {781if (ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,782aniState->spurImmunityLevel - 1))783return;784}785/*786* if all else fails, lower noise immunity level down to a min value787* zero for now788*/789if (aniState->noiseImmunityLevel > 0) {790if (ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,791aniState->noiseImmunityLevel - 1))792return;793}794}795796#define CLOCK_RATE 44000 /* XXX use mac_usec or similar */797/* convert HW counter values to ms using 11g clock rate, goo9d enough798for 11a and Turbo */799800/*801* Return an approximation of the time spent ``listening'' by802* deducting the cycles spent tx'ing and rx'ing from the total803* cycle count since our last call. A return value <0 indicates804* an invalid/inconsistent time.805*806* This may be called with ANI disabled; in which case simply keep807* the statistics and don't write to the aniState pointer.808*809* XXX TODO: Make this cleaner!810*/811static int32_t812ar5416AniGetListenTime(struct ath_hal *ah)813{814struct ath_hal_5212 *ahp = AH5212(ah);815struct ar5212AniState *aniState = NULL;816int32_t listenTime = 0;817int good;818HAL_SURVEY_SAMPLE hs;819820/*821* We shouldn't see ah_curchan be NULL, but just in case..822*/823if (AH_PRIVATE(ah)->ah_curchan == AH_NULL) {824ath_hal_printf(ah, "%s: ah_curchan = NULL?\n", __func__);825return (0);826}827828/*829* Fetch the current statistics, squirrel away the current830* sample.831*/832OS_MEMZERO(&hs, sizeof(hs));833good = ar5416GetMibCycleCounts(ah, &hs);834ath_hal_survey_add_sample(ah, &hs);835836if (ANI_ENA(ah))837aniState = ahp->ah_curani;838839if (good == AH_FALSE) {840/*841* Cycle counter wrap (or initial call); it's not possible842* to accurately calculate a value because the registers843* right shift rather than wrap--so punt and return 0.844*/845listenTime = 0;846ahp->ah_stats.ast_ani_lzero++;847} else if (ANI_ENA(ah)) {848/*849* Only calculate and update the cycle count if we have850* an ANI state.851*/852int32_t ccdelta =853AH5416(ah)->ah_cycleCount - aniState->cycleCount;854int32_t rfdelta =855AH5416(ah)->ah_rxBusy - aniState->rxFrameCount;856int32_t tfdelta =857AH5416(ah)->ah_txBusy - aniState->txFrameCount;858listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE;859}860861/*862* Again, only update ANI state if we have it.863*/864if (ANI_ENA(ah)) {865aniState->cycleCount = AH5416(ah)->ah_cycleCount;866aniState->rxFrameCount = AH5416(ah)->ah_rxBusy;867aniState->txFrameCount = AH5416(ah)->ah_txBusy;868}869870return listenTime;871}872873/*874* Update ani stats in preparation for listen time processing.875*/876static void877updateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState)878{879struct ath_hal_5212 *ahp = AH5212(ah);880const struct ar5212AniParams *params = aniState->params;881uint32_t phyCnt1, phyCnt2;882int32_t ofdmPhyErrCnt, cckPhyErrCnt;883884/* Clear the mib counters and save them in the stats */885ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);886887/* NB: these are not reset-on-read */888phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1);889phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2);890891/* NB: these are spec'd to never roll-over */892ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase;893if (ofdmPhyErrCnt < 0) {894HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n",895ofdmPhyErrCnt, phyCnt1);896ofdmPhyErrCnt = AR_PHY_COUNTMAX;897}898ahp->ah_stats.ast_ani_ofdmerrs +=899ofdmPhyErrCnt - aniState->ofdmPhyErrCount;900aniState->ofdmPhyErrCount = ofdmPhyErrCnt;901902cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase;903if (cckPhyErrCnt < 0) {904HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n",905cckPhyErrCnt, phyCnt2);906cckPhyErrCnt = AR_PHY_COUNTMAX;907}908ahp->ah_stats.ast_ani_cckerrs +=909cckPhyErrCnt - aniState->cckPhyErrCount;910aniState->cckPhyErrCount = cckPhyErrCnt;911}912913void914ar5416RxMonitor(struct ath_hal *ah, const HAL_NODE_STATS *stats,915const struct ieee80211_channel *chan)916{917struct ath_hal_5212 *ahp = AH5212(ah);918ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi;919}920921/*922* Do periodic processing. This routine is called from the923* driver's rx interrupt handler after processing frames.924*/925void926ar5416AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan)927{928struct ath_hal_5212 *ahp = AH5212(ah);929struct ar5212AniState *aniState = ahp->ah_curani;930const struct ar5212AniParams *params;931int32_t listenTime;932933/* Always update from the MIB, for statistics gathering */934listenTime = ar5416AniGetListenTime(ah);935936/* XXX can aniState be null? */937if (aniState == AH_NULL)938return;939940if (!ANI_ENA(ah))941return;942943if (listenTime < 0) {944ahp->ah_stats.ast_ani_lneg++;945/* restart ANI period if listenTime is invalid */946HALDEBUG(ah, HAL_DEBUG_ANI, "%s: invalid listenTime\n",947__func__);948ar5416AniRestart(ah, aniState);949950/* Don't do any further processing */951return;952}953/* XXX beware of overflow? */954aniState->listenTime += listenTime;955956OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime);957958params = aniState->params;959if (aniState->listenTime > 5*params->period) {960/*961* Check to see if need to lower immunity if962* 5 aniPeriods have passed963*/964updateMIBStats(ah, aniState);965if (aniState->ofdmPhyErrCount <= aniState->listenTime *966params->ofdmTrigLow/1000 &&967aniState->cckPhyErrCount <= aniState->listenTime *968params->cckTrigLow/1000)969ar5416AniLowerImmunity(ah);970HALDEBUG(ah, HAL_DEBUG_ANI, "%s: lower immunity\n",971__func__);972ar5416AniRestart(ah, aniState);973} else if (aniState->listenTime > params->period) {974updateMIBStats(ah, aniState);975/* check to see if need to raise immunity */976if (aniState->ofdmPhyErrCount > aniState->listenTime *977params->ofdmTrigHigh / 1000) {978HALDEBUG(ah, HAL_DEBUG_ANI,979"%s: OFDM err %u listenTime %u\n", __func__,980aniState->ofdmPhyErrCount, aniState->listenTime);981ar5416AniOfdmErrTrigger(ah);982ar5416AniRestart(ah, aniState);983} else if (aniState->cckPhyErrCount > aniState->listenTime *984params->cckTrigHigh / 1000) {985HALDEBUG(ah, HAL_DEBUG_ANI,986"%s: CCK err %u listenTime %u\n", __func__,987aniState->cckPhyErrCount, aniState->listenTime);988ar5416AniCckErrTrigger(ah);989ar5416AniRestart(ah, aniState);990}991}992}993994995