16e778a7eSPedro F. Giffuni /*-
26e778a7eSPedro F. Giffuni * SPDX-License-Identifier: ISC
36e778a7eSPedro F. Giffuni *
459efa8b5SSam Leffler * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
514779705SSam Leffler * Copyright (c) 2002-2008 Atheros Communications, Inc.
614779705SSam Leffler *
714779705SSam Leffler * Permission to use, copy, modify, and/or distribute this software for any
814779705SSam Leffler * purpose with or without fee is hereby granted, provided that the above
914779705SSam Leffler * copyright notice and this permission notice appear in all copies.
1014779705SSam Leffler *
1114779705SSam Leffler * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1214779705SSam Leffler * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1314779705SSam Leffler * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1414779705SSam Leffler * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1514779705SSam Leffler * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1614779705SSam Leffler * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1714779705SSam Leffler * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1814779705SSam Leffler */
1914779705SSam Leffler #include "opt_ah.h"
2014779705SSam Leffler
2114779705SSam Leffler /*
2214779705SSam Leffler * XXX this is virtually the same code as for 5212; we reuse
2314779705SSam Leffler * storage in the 5212 state block; need to refactor.
2414779705SSam Leffler */
2514779705SSam Leffler #include "ah.h"
2614779705SSam Leffler #include "ah_internal.h"
2714779705SSam Leffler #include "ah_desc.h"
2814779705SSam Leffler
2914779705SSam Leffler #include "ar5416/ar5416.h"
3014779705SSam Leffler #include "ar5416/ar5416reg.h"
3114779705SSam Leffler #include "ar5416/ar5416phy.h"
3214779705SSam Leffler
3314779705SSam Leffler /*
3414779705SSam Leffler * Anti noise immunity support. We track phy errors and react
3514779705SSam Leffler * to excessive errors by adjusting the noise immunity parameters.
3614779705SSam Leffler */
3714779705SSam Leffler
3814779705SSam Leffler #define HAL_EP_RND(x, mul) \
3914779705SSam Leffler ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
4014779705SSam Leffler #define BEACON_RSSI(ahp) \
4114779705SSam Leffler HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \
4214779705SSam Leffler HAL_RSSI_EP_MULTIPLIER)
4314779705SSam Leffler
4414779705SSam Leffler /*
4514779705SSam Leffler * ANI processing tunes radio parameters according to PHY errors
46*6cff32b3SGordon Bergling * and related information. This is done for noise and spur
4714779705SSam Leffler * immunity in all operating modes if the device indicates it's
4814779705SSam Leffler * capable at attach time. In addition, when there is a reference
4914779705SSam Leffler * rssi value (e.g. beacon frames from an ap in station mode)
5014779705SSam Leffler * further tuning is done.
5114779705SSam Leffler *
5214779705SSam Leffler * ANI_ENA indicates whether any ANI processing should be done;
5314779705SSam Leffler * this is specified at attach time.
5414779705SSam Leffler *
5514779705SSam Leffler * ANI_ENA_RSSI indicates whether rssi-based processing should
5614779705SSam Leffler * done, this is enabled based on operating mode and is meaningful
5714779705SSam Leffler * only if ANI_ENA is true.
5814779705SSam Leffler *
5914779705SSam Leffler * ANI parameters are typically controlled only by the hal. The
6014779705SSam Leffler * AniControl interface however permits manual tuning through the
6114779705SSam Leffler * diagnostic api.
6214779705SSam Leffler */
6314779705SSam Leffler #define ANI_ENA(ah) \
6414779705SSam Leffler (AH5212(ah)->ah_procPhyErr & HAL_ANI_ENA)
6514779705SSam Leffler #define ANI_ENA_RSSI(ah) \
6614779705SSam Leffler (AH5212(ah)->ah_procPhyErr & HAL_RSSI_ANI_ENA)
6714779705SSam Leffler
6814779705SSam Leffler #define ah_mibStats ah_stats.ast_mibstats
6914779705SSam Leffler
7014779705SSam Leffler static void
enableAniMIBCounters(struct ath_hal * ah,const struct ar5212AniParams * params)7114779705SSam Leffler enableAniMIBCounters(struct ath_hal *ah, const struct ar5212AniParams *params)
7214779705SSam Leffler {
7314779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
7414779705SSam Leffler
7514779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Enable mib counters: "
7614779705SSam Leffler "OfdmPhyErrBase 0x%x cckPhyErrBase 0x%x\n",
7714779705SSam Leffler __func__, params->ofdmPhyErrBase, params->cckPhyErrBase);
7814779705SSam Leffler
7914779705SSam Leffler OS_REG_WRITE(ah, AR_FILTOFDM, 0);
8014779705SSam Leffler OS_REG_WRITE(ah, AR_FILTCCK, 0);
8114779705SSam Leffler
8214779705SSam Leffler OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase);
8314779705SSam Leffler OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase);
8414779705SSam Leffler OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
8514779705SSam Leffler OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
8614779705SSam Leffler
8714779705SSam Leffler ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save+clear counters*/
8814779705SSam Leffler ar5212EnableMibCounters(ah); /* enable everything */
8914779705SSam Leffler }
9014779705SSam Leffler
9114779705SSam Leffler static void
disableAniMIBCounters(struct ath_hal * ah)9214779705SSam Leffler disableAniMIBCounters(struct ath_hal *ah)
9314779705SSam Leffler {
9414779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
9514779705SSam Leffler
9614779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANI, "Disable MIB counters\n");
9714779705SSam Leffler
9814779705SSam Leffler ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save stats */
9914779705SSam Leffler ar5212DisableMibCounters(ah); /* disable everything */
10014779705SSam Leffler
10114779705SSam Leffler OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, 0);
10214779705SSam Leffler OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, 0);
10314779705SSam Leffler }
10414779705SSam Leffler
10514779705SSam Leffler static void
setPhyErrBase(struct ath_hal * ah,struct ar5212AniParams * params)10614779705SSam Leffler setPhyErrBase(struct ath_hal *ah, struct ar5212AniParams *params)
10714779705SSam Leffler {
10814779705SSam Leffler if (params->ofdmTrigHigh >= AR_PHY_COUNTMAX) {
10914779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
11014779705SSam Leffler "OFDM Trigger %d is too high for hw counters, using max\n",
11114779705SSam Leffler params->ofdmTrigHigh);
11214779705SSam Leffler params->ofdmPhyErrBase = 0;
11314779705SSam Leffler } else
11414779705SSam Leffler params->ofdmPhyErrBase = AR_PHY_COUNTMAX - params->ofdmTrigHigh;
11514779705SSam Leffler if (params->cckTrigHigh >= AR_PHY_COUNTMAX) {
11614779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
11714779705SSam Leffler "CCK Trigger %d is too high for hw counters, using max\n",
11814779705SSam Leffler params->cckTrigHigh);
11914779705SSam Leffler params->cckPhyErrBase = 0;
12014779705SSam Leffler } else
12114779705SSam Leffler params->cckPhyErrBase = AR_PHY_COUNTMAX - params->cckTrigHigh;
12214779705SSam Leffler }
12314779705SSam Leffler
12414779705SSam Leffler /*
12514779705SSam Leffler * Setup ANI handling. Sets all thresholds and reset the
12614779705SSam Leffler * channel statistics. Note that ar5416AniReset should be
12714779705SSam Leffler * called by ar5416Reset before anything else happens and
12814779705SSam Leffler * that's where we force initial settings.
12914779705SSam Leffler */
13014779705SSam Leffler void
ar5416AniAttach(struct ath_hal * ah,const struct ar5212AniParams * params24,const struct ar5212AniParams * params5,HAL_BOOL enable)13114779705SSam Leffler ar5416AniAttach(struct ath_hal *ah, const struct ar5212AniParams *params24,
13214779705SSam Leffler const struct ar5212AniParams *params5, HAL_BOOL enable)
13314779705SSam Leffler {
13414779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
13514779705SSam Leffler
13614779705SSam Leffler if (params24 != AH_NULL) {
13714779705SSam Leffler OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24));
13814779705SSam Leffler setPhyErrBase(ah, &ahp->ah_aniParams24);
13914779705SSam Leffler }
14014779705SSam Leffler if (params5 != AH_NULL) {
14114779705SSam Leffler OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5));
14214779705SSam Leffler setPhyErrBase(ah, &ahp->ah_aniParams5);
14314779705SSam Leffler }
14414779705SSam Leffler
14514779705SSam Leffler OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani));
14614779705SSam Leffler /* Enable MIB Counters */
14714779705SSam Leffler enableAniMIBCounters(ah, &ahp->ah_aniParams24 /*XXX*/);
14814779705SSam Leffler
14914779705SSam Leffler if (enable) { /* Enable ani now */
15014779705SSam Leffler HALASSERT(params24 != AH_NULL && params5 != AH_NULL);
15114779705SSam Leffler ahp->ah_procPhyErr |= HAL_ANI_ENA;
15214779705SSam Leffler } else {
15314779705SSam Leffler ahp->ah_procPhyErr &= ~HAL_ANI_ENA;
15414779705SSam Leffler }
15514779705SSam Leffler }
15614779705SSam Leffler
15714779705SSam Leffler /*
15814779705SSam Leffler * Cleanup any ANI state setup.
159e875139aSAdrian Chadd *
160e875139aSAdrian Chadd * This doesn't restore registers to their default settings!
16114779705SSam Leffler */
16214779705SSam Leffler void
ar5416AniDetach(struct ath_hal * ah)16314779705SSam Leffler ar5416AniDetach(struct ath_hal *ah)
16414779705SSam Leffler {
16514779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANI, "Detaching Ani\n");
16614779705SSam Leffler disableAniMIBCounters(ah);
16714779705SSam Leffler }
16814779705SSam Leffler
16914779705SSam Leffler /*
17014779705SSam Leffler * Control Adaptive Noise Immunity Parameters
17114779705SSam Leffler */
17214779705SSam Leffler HAL_BOOL
ar5416AniControl(struct ath_hal * ah,HAL_ANI_CMD cmd,int param)17314779705SSam Leffler ar5416AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param)
17414779705SSam Leffler {
17514779705SSam Leffler typedef int TABLE[];
17614779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
17714779705SSam Leffler struct ar5212AniState *aniState = ahp->ah_curani;
178e875139aSAdrian Chadd const struct ar5212AniParams *params = AH_NULL;
179e875139aSAdrian Chadd
180e875139aSAdrian Chadd /*
181e875139aSAdrian Chadd * This function may be called before there's a current
182e875139aSAdrian Chadd * channel (eg to disable ANI.)
183e875139aSAdrian Chadd */
184e875139aSAdrian Chadd if (aniState != AH_NULL)
185e875139aSAdrian Chadd params = aniState->params;
186e875139aSAdrian Chadd
187e875139aSAdrian Chadd OS_MARK(ah, AH_MARK_ANI_CONTROL, cmd);
188e875139aSAdrian Chadd
189e875139aSAdrian Chadd /* These commands can't be disabled */
190e875139aSAdrian Chadd if (cmd == HAL_ANI_PRESENT)
191e875139aSAdrian Chadd return AH_TRUE;
192e875139aSAdrian Chadd
193e875139aSAdrian Chadd if (cmd == HAL_ANI_MODE) {
194e875139aSAdrian Chadd if (param == 0) {
195e875139aSAdrian Chadd ahp->ah_procPhyErr &= ~HAL_ANI_ENA;
196e875139aSAdrian Chadd /* Turn off HW counters if we have them */
197e875139aSAdrian Chadd ar5416AniDetach(ah);
198e875139aSAdrian Chadd } else { /* normal/auto mode */
199e875139aSAdrian Chadd /* don't mess with state if already enabled */
200e875139aSAdrian Chadd if (! (ahp->ah_procPhyErr & HAL_ANI_ENA)) {
201e875139aSAdrian Chadd /* Enable MIB Counters */
202e875139aSAdrian Chadd /*
203e875139aSAdrian Chadd * XXX use 2.4ghz params if no channel is
204e875139aSAdrian Chadd * available
205e875139aSAdrian Chadd */
206e875139aSAdrian Chadd enableAniMIBCounters(ah,
207e875139aSAdrian Chadd ahp->ah_curani != AH_NULL ?
208e875139aSAdrian Chadd ahp->ah_curani->params:
209e875139aSAdrian Chadd &ahp->ah_aniParams24);
210e875139aSAdrian Chadd ahp->ah_procPhyErr |= HAL_ANI_ENA;
211e875139aSAdrian Chadd }
212e875139aSAdrian Chadd }
213e875139aSAdrian Chadd return AH_TRUE;
214e875139aSAdrian Chadd }
21514779705SSam Leffler
216241d9a34SAdrian Chadd /* Check whether the particular function is enabled */
217241d9a34SAdrian Chadd if (((1 << cmd) & AH5416(ah)->ah_ani_function) == 0) {
218241d9a34SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_ANI, "%s: command %d disabled\n",
219241d9a34SAdrian Chadd __func__, cmd);
220241d9a34SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_ANI, "%s: cmd %d; mask %x\n", __func__, cmd, AH5416(ah)->ah_ani_function);
221241d9a34SAdrian Chadd return AH_FALSE;
222241d9a34SAdrian Chadd }
223241d9a34SAdrian Chadd
224241d9a34SAdrian Chadd switch (cmd) {
22514779705SSam Leffler case HAL_ANI_NOISE_IMMUNITY_LEVEL: {
22614779705SSam Leffler u_int level = param;
22714779705SSam Leffler
228a108ab63SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_NOISE_IMMUNITY_LEVEL: set level = %d\n", __func__, level);
229581449cbSAdrian Chadd if (level > params->maxNoiseImmunityLevel) {
2304ceb8559SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_ANI,
231f3d3bf87SRui Paulo "%s: immunity level out of range (%u > %u)\n",
23214779705SSam Leffler __func__, level, params->maxNoiseImmunityLevel);
23314779705SSam Leffler return AH_FALSE;
23414779705SSam Leffler }
23514779705SSam Leffler
23614779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ,
23714779705SSam Leffler AR_PHY_DESIRED_SZ_TOT_DES, params->totalSizeDesired[level]);
23814779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
23914779705SSam Leffler AR_PHY_AGC_CTL1_COARSE_LOW, params->coarseLow[level]);
24014779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
24114779705SSam Leffler AR_PHY_AGC_CTL1_COARSE_HIGH, params->coarseHigh[level]);
24214779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
24314779705SSam Leffler AR_PHY_FIND_SIG_FIRPWR, params->firpwr[level]);
24414779705SSam Leffler
24514779705SSam Leffler if (level > aniState->noiseImmunityLevel)
24614779705SSam Leffler ahp->ah_stats.ast_ani_niup++;
24714779705SSam Leffler else if (level < aniState->noiseImmunityLevel)
24814779705SSam Leffler ahp->ah_stats.ast_ani_nidown++;
24914779705SSam Leffler aniState->noiseImmunityLevel = level;
25014779705SSam Leffler break;
25114779705SSam Leffler }
25214779705SSam Leffler case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: {
25314779705SSam Leffler static const TABLE m1ThreshLow = { 127, 50 };
25414779705SSam Leffler static const TABLE m2ThreshLow = { 127, 40 };
25514779705SSam Leffler static const TABLE m1Thresh = { 127, 0x4d };
25614779705SSam Leffler static const TABLE m2Thresh = { 127, 0x40 };
25714779705SSam Leffler static const TABLE m2CountThr = { 31, 16 };
25814779705SSam Leffler static const TABLE m2CountThrLow = { 63, 48 };
25914779705SSam Leffler u_int on = param ? 1 : 0;
26014779705SSam Leffler
261a108ab63SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: %s\n", __func__, on ? "enabled" : "disabled");
26214779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
26314779705SSam Leffler AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1ThreshLow[on]);
26414779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
26514779705SSam Leffler AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2ThreshLow[on]);
26614779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,
26714779705SSam Leffler AR_PHY_SFCORR_M1_THRESH, m1Thresh[on]);
26814779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,
26914779705SSam Leffler AR_PHY_SFCORR_M2_THRESH, m2Thresh[on]);
27014779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,
27114779705SSam Leffler AR_PHY_SFCORR_M2COUNT_THR, m2CountThr[on]);
27214779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
27314779705SSam Leffler AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2CountThrLow[on]);
27414779705SSam Leffler
27514779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
27614779705SSam Leffler AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLow[on]);
27714779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
27814779705SSam Leffler AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLow[on]);
27914779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
28014779705SSam Leffler AR_PHY_SFCORR_EXT_M1_THRESH, m1Thresh[on]);
28114779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
28214779705SSam Leffler AR_PHY_SFCORR_EXT_M2_THRESH, m2Thresh[on]);
28314779705SSam Leffler
28414779705SSam Leffler if (on) {
28514779705SSam Leffler OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
28614779705SSam Leffler AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
28714779705SSam Leffler } else {
28814779705SSam Leffler OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,
28914779705SSam Leffler AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
29014779705SSam Leffler }
29114779705SSam Leffler if (on)
29214779705SSam Leffler ahp->ah_stats.ast_ani_ofdmon++;
29314779705SSam Leffler else
29414779705SSam Leffler ahp->ah_stats.ast_ani_ofdmoff++;
29514779705SSam Leffler aniState->ofdmWeakSigDetectOff = !on;
29614779705SSam Leffler break;
29714779705SSam Leffler }
29814779705SSam Leffler case HAL_ANI_CCK_WEAK_SIGNAL_THR: {
29914779705SSam Leffler static const TABLE weakSigThrCck = { 8, 6 };
30014779705SSam Leffler u_int high = param ? 1 : 0;
30114779705SSam Leffler
302a108ab63SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_CCK_WEAK_SIGNAL_THR: %s\n", __func__, high ? "high" : "low");
30314779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT,
30414779705SSam Leffler AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, weakSigThrCck[high]);
30514779705SSam Leffler if (high)
30614779705SSam Leffler ahp->ah_stats.ast_ani_cckhigh++;
30714779705SSam Leffler else
30814779705SSam Leffler ahp->ah_stats.ast_ani_ccklow++;
30914779705SSam Leffler aniState->cckWeakSigThreshold = high;
31014779705SSam Leffler break;
31114779705SSam Leffler }
31214779705SSam Leffler case HAL_ANI_FIRSTEP_LEVEL: {
31314779705SSam Leffler u_int level = param;
31414779705SSam Leffler
315a108ab63SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_FIRSTEP_LEVEL: level = %d\n", __func__, level);
316581449cbSAdrian Chadd if (level > params->maxFirstepLevel) {
3174ceb8559SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_ANI,
318f3d3bf87SRui Paulo "%s: firstep level out of range (%u > %u)\n",
31914779705SSam Leffler __func__, level, params->maxFirstepLevel);
32014779705SSam Leffler return AH_FALSE;
32114779705SSam Leffler }
32214779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
32314779705SSam Leffler AR_PHY_FIND_SIG_FIRSTEP, params->firstep[level]);
32414779705SSam Leffler if (level > aniState->firstepLevel)
32514779705SSam Leffler ahp->ah_stats.ast_ani_stepup++;
32614779705SSam Leffler else if (level < aniState->firstepLevel)
32714779705SSam Leffler ahp->ah_stats.ast_ani_stepdown++;
32814779705SSam Leffler aniState->firstepLevel = level;
32914779705SSam Leffler break;
33014779705SSam Leffler }
33114779705SSam Leffler case HAL_ANI_SPUR_IMMUNITY_LEVEL: {
33214779705SSam Leffler u_int level = param;
33314779705SSam Leffler
334a108ab63SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_SPUR_IMMUNITY_LEVEL: level = %d\n", __func__, level);
335581449cbSAdrian Chadd if (level > params->maxSpurImmunityLevel) {
3364ceb8559SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_ANI,
337f3d3bf87SRui Paulo "%s: spur immunity level out of range (%u > %u)\n",
33814779705SSam Leffler __func__, level, params->maxSpurImmunityLevel);
33914779705SSam Leffler return AH_FALSE;
34014779705SSam Leffler }
34114779705SSam Leffler OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5,
34214779705SSam Leffler AR_PHY_TIMING5_CYCPWR_THR1, params->cycPwrThr1[level]);
343b657b11dSAdrian Chadd
34414779705SSam Leffler if (level > aniState->spurImmunityLevel)
34514779705SSam Leffler ahp->ah_stats.ast_ani_spurup++;
34614779705SSam Leffler else if (level < aniState->spurImmunityLevel)
34714779705SSam Leffler ahp->ah_stats.ast_ani_spurdown++;
34814779705SSam Leffler aniState->spurImmunityLevel = level;
34914779705SSam Leffler break;
35014779705SSam Leffler }
35114779705SSam Leffler #ifdef AH_PRIVATE_DIAG
35214779705SSam Leffler case HAL_ANI_PHYERR_RESET:
35314779705SSam Leffler ahp->ah_stats.ast_ani_ofdmerrs = 0;
35414779705SSam Leffler ahp->ah_stats.ast_ani_cckerrs = 0;
35514779705SSam Leffler break;
35614779705SSam Leffler #endif /* AH_PRIVATE_DIAG */
35714779705SSam Leffler default:
3584ceb8559SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_ANI, "%s: invalid cmd %u\n",
35914779705SSam Leffler __func__, cmd);
36014779705SSam Leffler return AH_FALSE;
36114779705SSam Leffler }
36214779705SSam Leffler return AH_TRUE;
36314779705SSam Leffler }
36414779705SSam Leffler
36514779705SSam Leffler static void
ar5416AniOfdmErrTrigger(struct ath_hal * ah)36614779705SSam Leffler ar5416AniOfdmErrTrigger(struct ath_hal *ah)
36714779705SSam Leffler {
36814779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
36959efa8b5SSam Leffler const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
37014779705SSam Leffler struct ar5212AniState *aniState;
37114779705SSam Leffler const struct ar5212AniParams *params;
37214779705SSam Leffler
37314779705SSam Leffler HALASSERT(chan != AH_NULL);
37414779705SSam Leffler
37514779705SSam Leffler if (!ANI_ENA(ah))
37614779705SSam Leffler return;
37714779705SSam Leffler
37814779705SSam Leffler aniState = ahp->ah_curani;
37914779705SSam Leffler params = aniState->params;
38014779705SSam Leffler /* First, raise noise immunity level, up to max */
381fcf3bb80SAdrian Chadd if (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel) {
382fcf3bb80SAdrian Chadd if (ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
383fcf3bb80SAdrian Chadd aniState->noiseImmunityLevel + 1))
38414779705SSam Leffler return;
38514779705SSam Leffler }
38614779705SSam Leffler /* then, raise spur immunity level, up to max */
387fcf3bb80SAdrian Chadd if (aniState->spurImmunityLevel+1 < params->maxSpurImmunityLevel) {
388fcf3bb80SAdrian Chadd if (ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
389fcf3bb80SAdrian Chadd aniState->spurImmunityLevel + 1))
39014779705SSam Leffler return;
39114779705SSam Leffler }
39214779705SSam Leffler
3937f6a8ccaSAdrian Chadd /*
3947f6a8ccaSAdrian Chadd * In the case of AP mode operation, we cannot bucketize beacons
3957f6a8ccaSAdrian Chadd * according to RSSI. Instead, raise Firstep level, up to max, and
3967f6a8ccaSAdrian Chadd * simply return.
3977f6a8ccaSAdrian Chadd */
3987f6a8ccaSAdrian Chadd if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) {
3997f6a8ccaSAdrian Chadd if (aniState->firstepLevel < params->maxFirstepLevel) {
4007f6a8ccaSAdrian Chadd if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
4017f6a8ccaSAdrian Chadd aniState->firstepLevel + 1))
4027f6a8ccaSAdrian Chadd return;
4037f6a8ccaSAdrian Chadd }
4047f6a8ccaSAdrian Chadd }
40514779705SSam Leffler if (ANI_ENA_RSSI(ah)) {
40614779705SSam Leffler int32_t rssi = BEACON_RSSI(ahp);
40714779705SSam Leffler if (rssi > params->rssiThrHigh) {
40814779705SSam Leffler /*
40914779705SSam Leffler * Beacon rssi is high, can turn off ofdm
41014779705SSam Leffler * weak sig detect.
41114779705SSam Leffler */
41214779705SSam Leffler if (!aniState->ofdmWeakSigDetectOff) {
41314779705SSam Leffler ar5416AniControl(ah,
41414779705SSam Leffler HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
41514779705SSam Leffler AH_FALSE);
41614779705SSam Leffler ar5416AniControl(ah,
41714779705SSam Leffler HAL_ANI_SPUR_IMMUNITY_LEVEL, 0);
41814779705SSam Leffler return;
41914779705SSam Leffler }
42014779705SSam Leffler /*
42114779705SSam Leffler * If weak sig detect is already off, as last resort,
42214779705SSam Leffler * raise firstep level
42314779705SSam Leffler */
4245b0c1ea0SAdrian Chadd if (aniState->firstepLevel < params->maxFirstepLevel) {
425581449cbSAdrian Chadd if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
426581449cbSAdrian Chadd aniState->firstepLevel + 1))
42714779705SSam Leffler return;
42814779705SSam Leffler }
42914779705SSam Leffler } else if (rssi > params->rssiThrLow) {
43014779705SSam Leffler /*
43114779705SSam Leffler * Beacon rssi in mid range, need ofdm weak signal
43214779705SSam Leffler * detect, but we can raise firststepLevel.
43314779705SSam Leffler */
43414779705SSam Leffler if (aniState->ofdmWeakSigDetectOff)
43514779705SSam Leffler ar5416AniControl(ah,
43614779705SSam Leffler HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
43714779705SSam Leffler AH_TRUE);
4385b0c1ea0SAdrian Chadd if (aniState->firstepLevel < params->maxFirstepLevel)
439581449cbSAdrian Chadd if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
440581449cbSAdrian Chadd aniState->firstepLevel + 1))
44114779705SSam Leffler return;
44214779705SSam Leffler } else {
44314779705SSam Leffler /*
44414779705SSam Leffler * Beacon rssi is low, if in 11b/g mode, turn off ofdm
44514779705SSam Leffler * weak signal detection and zero firstepLevel to
44614779705SSam Leffler * maximize CCK sensitivity
44714779705SSam Leffler */
44859efa8b5SSam Leffler if (IEEE80211_IS_CHAN_CCK(chan)) {
44914779705SSam Leffler if (!aniState->ofdmWeakSigDetectOff)
45014779705SSam Leffler ar5416AniControl(ah,
45114779705SSam Leffler HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
45214779705SSam Leffler AH_FALSE);
45314779705SSam Leffler if (aniState->firstepLevel > 0)
454581449cbSAdrian Chadd if (ar5416AniControl(ah,
455581449cbSAdrian Chadd HAL_ANI_FIRSTEP_LEVEL, 0))
45614779705SSam Leffler return;
45714779705SSam Leffler }
45814779705SSam Leffler }
45914779705SSam Leffler }
46014779705SSam Leffler }
46114779705SSam Leffler
46214779705SSam Leffler static void
ar5416AniCckErrTrigger(struct ath_hal * ah)46314779705SSam Leffler ar5416AniCckErrTrigger(struct ath_hal *ah)
46414779705SSam Leffler {
46514779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
46659efa8b5SSam Leffler const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
46714779705SSam Leffler struct ar5212AniState *aniState;
46814779705SSam Leffler const struct ar5212AniParams *params;
46914779705SSam Leffler
47014779705SSam Leffler HALASSERT(chan != AH_NULL);
47114779705SSam Leffler
47214779705SSam Leffler if (!ANI_ENA(ah))
47314779705SSam Leffler return;
47414779705SSam Leffler
47514779705SSam Leffler /* first, raise noise immunity level, up to max */
47614779705SSam Leffler aniState = ahp->ah_curani;
47714779705SSam Leffler params = aniState->params;
478241d9a34SAdrian Chadd if ((AH5416(ah)->ah_ani_function & (1 << HAL_ANI_NOISE_IMMUNITY_LEVEL) &&
479241d9a34SAdrian Chadd aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel)) {
48014779705SSam Leffler ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
48114779705SSam Leffler aniState->noiseImmunityLevel + 1);
48214779705SSam Leffler return;
48314779705SSam Leffler }
48414779705SSam Leffler
48514779705SSam Leffler if (ANI_ENA_RSSI(ah)) {
48614779705SSam Leffler int32_t rssi = BEACON_RSSI(ahp);
48714779705SSam Leffler if (rssi > params->rssiThrLow) {
48814779705SSam Leffler /*
48914779705SSam Leffler * Beacon signal in mid and high range,
49014779705SSam Leffler * raise firstep level.
49114779705SSam Leffler */
4925b0c1ea0SAdrian Chadd if (aniState->firstepLevel < params->maxFirstepLevel)
49314779705SSam Leffler ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
49414779705SSam Leffler aniState->firstepLevel + 1);
49514779705SSam Leffler } else {
49614779705SSam Leffler /*
49714779705SSam Leffler * Beacon rssi is low, zero firstep level to maximize
49814779705SSam Leffler * CCK sensitivity in 11b/g mode.
49914779705SSam Leffler */
50059efa8b5SSam Leffler if (IEEE80211_IS_CHAN_CCK(chan)) {
50114779705SSam Leffler if (aniState->firstepLevel > 0)
50214779705SSam Leffler ar5416AniControl(ah,
50314779705SSam Leffler HAL_ANI_FIRSTEP_LEVEL, 0);
50414779705SSam Leffler }
50514779705SSam Leffler }
50614779705SSam Leffler }
50714779705SSam Leffler }
50814779705SSam Leffler
50914779705SSam Leffler static void
ar5416AniRestart(struct ath_hal * ah,struct ar5212AniState * aniState)51014779705SSam Leffler ar5416AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState)
51114779705SSam Leffler {
51214779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
51314779705SSam Leffler const struct ar5212AniParams *params = aniState->params;
51414779705SSam Leffler
51514779705SSam Leffler aniState->listenTime = 0;
51614779705SSam Leffler /*
51714779705SSam Leffler * NB: these are written on reset based on the
51814779705SSam Leffler * ini so we must re-write them!
51914779705SSam Leffler */
52014779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANI,
52114779705SSam Leffler "%s: Writing ofdmbase=%u cckbase=%u\n", __func__,
52214779705SSam Leffler params->ofdmPhyErrBase, params->cckPhyErrBase);
52314779705SSam Leffler OS_REG_WRITE(ah, AR_PHY_ERR_1, params->ofdmPhyErrBase);
52414779705SSam Leffler OS_REG_WRITE(ah, AR_PHY_ERR_2, params->cckPhyErrBase);
52514779705SSam Leffler OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
526507de802SAdrian Chadd OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
52714779705SSam Leffler
52814779705SSam Leffler /* Clear the mib counters and save them in the stats */
52914779705SSam Leffler ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
53014779705SSam Leffler aniState->ofdmPhyErrCount = 0;
53114779705SSam Leffler aniState->cckPhyErrCount = 0;
53214779705SSam Leffler }
53314779705SSam Leffler
53414779705SSam Leffler /*
53514779705SSam Leffler * Restore/reset the ANI parameters and reset the statistics.
53614779705SSam Leffler * This routine must be called for every channel change.
53714779705SSam Leffler *
53814779705SSam Leffler * NOTE: This is where ah_curani is set; other ani code assumes
53914779705SSam Leffler * it is setup to reflect the current channel.
54014779705SSam Leffler */
54114779705SSam Leffler void
ar5416AniReset(struct ath_hal * ah,const struct ieee80211_channel * chan,HAL_OPMODE opmode,int restore)54259efa8b5SSam Leffler ar5416AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan,
54314779705SSam Leffler HAL_OPMODE opmode, int restore)
54414779705SSam Leffler {
54514779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
54659efa8b5SSam Leffler HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
54759efa8b5SSam Leffler /* XXX bounds check ic_devdata */
54859efa8b5SSam Leffler struct ar5212AniState *aniState = &ahp->ah_ani[chan->ic_devdata];
54914779705SSam Leffler uint32_t rxfilter;
55014779705SSam Leffler
55159efa8b5SSam Leffler if ((ichan->privFlags & CHANNEL_ANI_INIT) == 0) {
55259efa8b5SSam Leffler OS_MEMZERO(aniState, sizeof(*aniState));
55359efa8b5SSam Leffler if (IEEE80211_IS_CHAN_2GHZ(chan))
55459efa8b5SSam Leffler aniState->params = &ahp->ah_aniParams24;
55559efa8b5SSam Leffler else
55659efa8b5SSam Leffler aniState->params = &ahp->ah_aniParams5;
55759efa8b5SSam Leffler ichan->privFlags |= CHANNEL_ANI_INIT;
55859efa8b5SSam Leffler HALASSERT((ichan->privFlags & CHANNEL_ANI_SETUP) == 0);
55959efa8b5SSam Leffler }
56014779705SSam Leffler ahp->ah_curani = aniState;
56114779705SSam Leffler #if 0
56259efa8b5SSam Leffler ath_hal_printf(ah,"%s: chan %u/0x%x restore %d opmode %u%s\n",
56359efa8b5SSam Leffler __func__, chan->ic_freq, chan->ic_flags, restore, opmode,
56459efa8b5SSam Leffler ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : "");
56514779705SSam Leffler #else
56659efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANI, "%s: chan %u/0x%x restore %d opmode %u%s\n",
56759efa8b5SSam Leffler __func__, chan->ic_freq, chan->ic_flags, restore, opmode,
56859efa8b5SSam Leffler ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : "");
56914779705SSam Leffler #endif
57014779705SSam Leffler OS_MARK(ah, AH_MARK_ANI_RESET, opmode);
57114779705SSam Leffler
57214779705SSam Leffler /*
57314779705SSam Leffler * Turn off PHY error frame delivery while we futz with settings.
57414779705SSam Leffler */
575e875139aSAdrian Chadd rxfilter = ah->ah_getRxFilter(ah);
576e875139aSAdrian Chadd ah->ah_setRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR);
577e875139aSAdrian Chadd
578e875139aSAdrian Chadd /*
579e875139aSAdrian Chadd * If ANI is disabled at this point, don't set the default
580e875139aSAdrian Chadd * ANI parameter settings - leave the HAL settings there.
581e875139aSAdrian Chadd * This is (currently) needed for reliable radar detection.
582e875139aSAdrian Chadd */
583e875139aSAdrian Chadd if (! ANI_ENA(ah)) {
584e875139aSAdrian Chadd HALDEBUG(ah, HAL_DEBUG_ANI, "%s: ANI disabled\n",
585e875139aSAdrian Chadd __func__);
586e875139aSAdrian Chadd goto finish;
587e875139aSAdrian Chadd }
588e875139aSAdrian Chadd
589eb81a8f6SAdrian Chadd /*
590eb81a8f6SAdrian Chadd * Use a restrictive set of ANI parameters for hostap mode.
591eb81a8f6SAdrian Chadd */
592eb81a8f6SAdrian Chadd if (opmode == HAL_M_HOSTAP) {
593eb81a8f6SAdrian Chadd if (IEEE80211_IS_CHAN_2GHZ(chan))
594eb81a8f6SAdrian Chadd AH5416(ah)->ah_ani_function =
595eb81a8f6SAdrian Chadd HAL_ANI_SPUR_IMMUNITY_LEVEL | HAL_ANI_FIRSTEP_LEVEL;
596eb81a8f6SAdrian Chadd else
597eb81a8f6SAdrian Chadd AH5416(ah)->ah_ani_function = 0;
598eb81a8f6SAdrian Chadd }
599e875139aSAdrian Chadd
60014779705SSam Leffler /*
60114779705SSam Leffler * Automatic processing is done only in station mode right now.
60214779705SSam Leffler */
60314779705SSam Leffler if (opmode == HAL_M_STA)
60414779705SSam Leffler ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA;
60514779705SSam Leffler else
60614779705SSam Leffler ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA;
60714779705SSam Leffler /*
60814779705SSam Leffler * Set all ani parameters. We either set them to initial
60914779705SSam Leffler * values or restore the previous ones for the channel.
61014779705SSam Leffler * XXX if ANI follows hardware, we don't care what mode we're
61114779705SSam Leffler * XXX in, we should keep the ani parameters
61214779705SSam Leffler */
61359efa8b5SSam Leffler if (restore && (ichan->privFlags & CHANNEL_ANI_SETUP)) {
61414779705SSam Leffler ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
61514779705SSam Leffler aniState->noiseImmunityLevel);
61614779705SSam Leffler ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
61714779705SSam Leffler aniState->spurImmunityLevel);
61814779705SSam Leffler ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
61914779705SSam Leffler !aniState->ofdmWeakSigDetectOff);
62014779705SSam Leffler ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR,
62114779705SSam Leffler aniState->cckWeakSigThreshold);
62214779705SSam Leffler ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
62314779705SSam Leffler aniState->firstepLevel);
62414779705SSam Leffler } else {
62514779705SSam Leffler ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0);
62614779705SSam Leffler ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0);
62714779705SSam Leffler ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
62826ae0bb4SAdrian Chadd AH_FALSE);
62914779705SSam Leffler ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE);
63014779705SSam Leffler ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0);
63159efa8b5SSam Leffler ichan->privFlags |= CHANNEL_ANI_SETUP;
63214779705SSam Leffler }
633e875139aSAdrian Chadd
634e875139aSAdrian Chadd /*
635e875139aSAdrian Chadd * In case the counters haven't yet been setup; set them up.
636e875139aSAdrian Chadd */
637e875139aSAdrian Chadd enableAniMIBCounters(ah, aniState->params);
63814779705SSam Leffler ar5416AniRestart(ah, aniState);
63914779705SSam Leffler
640e875139aSAdrian Chadd finish:
64114779705SSam Leffler /* restore RX filter mask */
642e875139aSAdrian Chadd ah->ah_setRxFilter(ah, rxfilter);
64314779705SSam Leffler }
64414779705SSam Leffler
64514779705SSam Leffler /*
64614779705SSam Leffler * Process a MIB interrupt. We may potentially be invoked because
64714779705SSam Leffler * any of the MIB counters overflow/trigger so don't assume we're
64814779705SSam Leffler * here because a PHY error counter triggered.
64914779705SSam Leffler */
65014779705SSam Leffler void
ar5416ProcessMibIntr(struct ath_hal * ah,const HAL_NODE_STATS * stats)65114779705SSam Leffler ar5416ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats)
65214779705SSam Leffler {
65314779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
65414779705SSam Leffler uint32_t phyCnt1, phyCnt2;
65514779705SSam Leffler
65614779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x "
65714779705SSam Leffler "filtofdm 0x%x filtcck 0x%x\n",
65814779705SSam Leffler __func__, OS_REG_READ(ah, AR_MIBC),
65914779705SSam Leffler OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2),
66014779705SSam Leffler OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK));
66114779705SSam Leffler
66214779705SSam Leffler /*
66314779705SSam Leffler * First order of business is to clear whatever caused
66414779705SSam Leffler * the interrupt so we don't keep getting interrupted.
66514779705SSam Leffler * We have the usual mib counters that are reset-on-read
66614779705SSam Leffler * and the additional counters that appeared starting in
66714779705SSam Leffler * Hainan. We collect the mib counters and explicitly
66814779705SSam Leffler * zero additional counters we are not using. Anything
66914779705SSam Leffler * else is reset only if it caused the interrupt.
67014779705SSam Leffler */
67114779705SSam Leffler /* NB: these are not reset-on-read */
67214779705SSam Leffler phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1);
67314779705SSam Leffler phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2);
67414779705SSam Leffler /* not used, always reset them in case they are the cause */
67514779705SSam Leffler OS_REG_WRITE(ah, AR_FILTOFDM, 0);
67614779705SSam Leffler OS_REG_WRITE(ah, AR_FILTCCK, 0);
67714779705SSam Leffler if ((OS_REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING) == 0)
67814779705SSam Leffler OS_REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);
67914779705SSam Leffler
68014779705SSam Leffler /* Clear the mib counters and save them in the stats */
68114779705SSam Leffler ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
68214779705SSam Leffler ahp->ah_stats.ast_nodestats = *stats;
68314779705SSam Leffler
68414779705SSam Leffler /*
68514779705SSam Leffler * Check for an ani stat hitting the trigger threshold.
68614779705SSam Leffler * When this happens we get a MIB interrupt and the top
68714779705SSam Leffler * 2 bits of the counter register will be 0b11, hence
68814779705SSam Leffler * the mask check of phyCnt?.
68914779705SSam Leffler */
69014779705SSam Leffler if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
69114779705SSam Leffler ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
69214779705SSam Leffler struct ar5212AniState *aniState = ahp->ah_curani;
69314779705SSam Leffler const struct ar5212AniParams *params = aniState->params;
69414779705SSam Leffler uint32_t ofdmPhyErrCnt, cckPhyErrCnt;
69514779705SSam Leffler
69614779705SSam Leffler ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase;
69714779705SSam Leffler ahp->ah_stats.ast_ani_ofdmerrs +=
69814779705SSam Leffler ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
69914779705SSam Leffler aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
70014779705SSam Leffler
70114779705SSam Leffler cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase;
70214779705SSam Leffler ahp->ah_stats.ast_ani_cckerrs +=
70314779705SSam Leffler cckPhyErrCnt - aniState->cckPhyErrCount;
70414779705SSam Leffler aniState->cckPhyErrCount = cckPhyErrCnt;
70514779705SSam Leffler
70614779705SSam Leffler /*
70714779705SSam Leffler * NB: figure out which counter triggered. If both
70814779705SSam Leffler * trigger we'll only deal with one as the processing
70914779705SSam Leffler * clobbers the error counter so the trigger threshold
71014779705SSam Leffler * check will never be true.
71114779705SSam Leffler */
71214779705SSam Leffler if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh)
71314779705SSam Leffler ar5416AniOfdmErrTrigger(ah);
71414779705SSam Leffler if (aniState->cckPhyErrCount > params->cckTrigHigh)
71514779705SSam Leffler ar5416AniCckErrTrigger(ah);
71614779705SSam Leffler /* NB: always restart to insure the h/w counters are reset */
71714779705SSam Leffler ar5416AniRestart(ah, aniState);
71814779705SSam Leffler }
71914779705SSam Leffler }
72014779705SSam Leffler
72114779705SSam Leffler static void
ar5416AniLowerImmunity(struct ath_hal * ah)72214779705SSam Leffler ar5416AniLowerImmunity(struct ath_hal *ah)
72314779705SSam Leffler {
72414779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
72514779705SSam Leffler struct ar5212AniState *aniState;
72614779705SSam Leffler const struct ar5212AniParams *params;
72714779705SSam Leffler
72814779705SSam Leffler HALASSERT(ANI_ENA(ah));
72914779705SSam Leffler
73014779705SSam Leffler aniState = ahp->ah_curani;
73114779705SSam Leffler params = aniState->params;
7327f6a8ccaSAdrian Chadd
7337f6a8ccaSAdrian Chadd /*
7347f6a8ccaSAdrian Chadd * In the case of AP mode operation, we cannot bucketize beacons
7357f6a8ccaSAdrian Chadd * according to RSSI. Instead, lower Firstep level, down to min, and
7367f6a8ccaSAdrian Chadd * simply return.
7377f6a8ccaSAdrian Chadd */
7387f6a8ccaSAdrian Chadd if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) {
7397f6a8ccaSAdrian Chadd if (aniState->firstepLevel > 0) {
7407f6a8ccaSAdrian Chadd if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
7417f6a8ccaSAdrian Chadd aniState->firstepLevel - 1))
7427f6a8ccaSAdrian Chadd return;
7437f6a8ccaSAdrian Chadd }
7447f6a8ccaSAdrian Chadd }
74514779705SSam Leffler if (ANI_ENA_RSSI(ah)) {
74614779705SSam Leffler int32_t rssi = BEACON_RSSI(ahp);
74714779705SSam Leffler if (rssi > params->rssiThrHigh) {
74814779705SSam Leffler /*
74914779705SSam Leffler * Beacon signal is high, leave ofdm weak signal
75014779705SSam Leffler * detection off or it may oscillate. Let it fall
75114779705SSam Leffler * through.
75214779705SSam Leffler */
75314779705SSam Leffler } else if (rssi > params->rssiThrLow) {
75414779705SSam Leffler /*
75514779705SSam Leffler * Beacon rssi in mid range, turn on ofdm weak signal
75614779705SSam Leffler * detection or lower firstep level.
75714779705SSam Leffler */
75814779705SSam Leffler if (aniState->ofdmWeakSigDetectOff) {
759581449cbSAdrian Chadd if (ar5416AniControl(ah,
76014779705SSam Leffler HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
761581449cbSAdrian Chadd AH_TRUE))
76214779705SSam Leffler return;
76314779705SSam Leffler }
76414779705SSam Leffler if (aniState->firstepLevel > 0) {
765581449cbSAdrian Chadd if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
766581449cbSAdrian Chadd aniState->firstepLevel - 1))
76714779705SSam Leffler return;
76814779705SSam Leffler }
76914779705SSam Leffler } else {
77014779705SSam Leffler /*
77114779705SSam Leffler * Beacon rssi is low, reduce firstep level.
77214779705SSam Leffler */
77314779705SSam Leffler if (aniState->firstepLevel > 0) {
774581449cbSAdrian Chadd if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
775581449cbSAdrian Chadd aniState->firstepLevel - 1))
77614779705SSam Leffler return;
77714779705SSam Leffler }
77814779705SSam Leffler }
77914779705SSam Leffler }
78014779705SSam Leffler /* then lower spur immunity level, down to zero */
78114779705SSam Leffler if (aniState->spurImmunityLevel > 0) {
782581449cbSAdrian Chadd if (ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
783581449cbSAdrian Chadd aniState->spurImmunityLevel - 1))
78414779705SSam Leffler return;
78514779705SSam Leffler }
78614779705SSam Leffler /*
78714779705SSam Leffler * if all else fails, lower noise immunity level down to a min value
78814779705SSam Leffler * zero for now
78914779705SSam Leffler */
79014779705SSam Leffler if (aniState->noiseImmunityLevel > 0) {
791581449cbSAdrian Chadd if (ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
792581449cbSAdrian Chadd aniState->noiseImmunityLevel - 1))
79314779705SSam Leffler return;
79414779705SSam Leffler }
79514779705SSam Leffler }
79614779705SSam Leffler
79714779705SSam Leffler #define CLOCK_RATE 44000 /* XXX use mac_usec or similar */
79814779705SSam Leffler /* convert HW counter values to ms using 11g clock rate, goo9d enough
79914779705SSam Leffler for 11a and Turbo */
80014779705SSam Leffler
80114779705SSam Leffler /*
80214779705SSam Leffler * Return an approximation of the time spent ``listening'' by
80314779705SSam Leffler * deducting the cycles spent tx'ing and rx'ing from the total
80414779705SSam Leffler * cycle count since our last call. A return value <0 indicates
80514779705SSam Leffler * an invalid/inconsistent time.
806af5336e3SAdrian Chadd *
807af5336e3SAdrian Chadd * This may be called with ANI disabled; in which case simply keep
808af5336e3SAdrian Chadd * the statistics and don't write to the aniState pointer.
809af5336e3SAdrian Chadd *
810af5336e3SAdrian Chadd * XXX TODO: Make this cleaner!
81114779705SSam Leffler */
81214779705SSam Leffler static int32_t
ar5416AniGetListenTime(struct ath_hal * ah)81314779705SSam Leffler ar5416AniGetListenTime(struct ath_hal *ah)
81414779705SSam Leffler {
81514779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
816af5336e3SAdrian Chadd struct ar5212AniState *aniState = NULL;
817641d61c7SAdrian Chadd int32_t listenTime = 0;
8182e4464a4SAdrian Chadd int good;
819af5336e3SAdrian Chadd HAL_SURVEY_SAMPLE hs;
82014779705SSam Leffler
821af5336e3SAdrian Chadd /*
822af5336e3SAdrian Chadd * We shouldn't see ah_curchan be NULL, but just in case..
823af5336e3SAdrian Chadd */
824af5336e3SAdrian Chadd if (AH_PRIVATE(ah)->ah_curchan == AH_NULL) {
825af5336e3SAdrian Chadd ath_hal_printf(ah, "%s: ah_curchan = NULL?\n", __func__);
826af5336e3SAdrian Chadd return (0);
827af5336e3SAdrian Chadd }
8286af85006SAdrian Chadd
829af5336e3SAdrian Chadd /*
830af5336e3SAdrian Chadd * Fetch the current statistics, squirrel away the current
831b0602becSAdrian Chadd * sample.
832af5336e3SAdrian Chadd */
833af5336e3SAdrian Chadd OS_MEMZERO(&hs, sizeof(hs));
834af5336e3SAdrian Chadd good = ar5416GetMibCycleCounts(ah, &hs);
835b0602becSAdrian Chadd ath_hal_survey_add_sample(ah, &hs);
836af5336e3SAdrian Chadd
837af5336e3SAdrian Chadd if (ANI_ENA(ah))
83814779705SSam Leffler aniState = ahp->ah_curani;
839af5336e3SAdrian Chadd
840352f07f6SAdrian Chadd if (good == AH_FALSE) {
84114779705SSam Leffler /*
84214779705SSam Leffler * Cycle counter wrap (or initial call); it's not possible
84314779705SSam Leffler * to accurately calculate a value because the registers
84414779705SSam Leffler * right shift rather than wrap--so punt and return 0.
84514779705SSam Leffler */
84614779705SSam Leffler listenTime = 0;
84714779705SSam Leffler ahp->ah_stats.ast_ani_lzero++;
848af5336e3SAdrian Chadd } else if (ANI_ENA(ah)) {
849af5336e3SAdrian Chadd /*
850af5336e3SAdrian Chadd * Only calculate and update the cycle count if we have
851af5336e3SAdrian Chadd * an ANI state.
852af5336e3SAdrian Chadd */
853af5336e3SAdrian Chadd int32_t ccdelta =
854af5336e3SAdrian Chadd AH5416(ah)->ah_cycleCount - aniState->cycleCount;
855af5336e3SAdrian Chadd int32_t rfdelta =
856af5336e3SAdrian Chadd AH5416(ah)->ah_rxBusy - aniState->rxFrameCount;
857af5336e3SAdrian Chadd int32_t tfdelta =
858af5336e3SAdrian Chadd AH5416(ah)->ah_txBusy - aniState->txFrameCount;
85914779705SSam Leffler listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE;
86014779705SSam Leffler }
861af5336e3SAdrian Chadd
862af5336e3SAdrian Chadd /*
863af5336e3SAdrian Chadd * Again, only update ANI state if we have it.
864af5336e3SAdrian Chadd */
865af5336e3SAdrian Chadd if (ANI_ENA(ah)) {
8662e4464a4SAdrian Chadd aniState->cycleCount = AH5416(ah)->ah_cycleCount;
86708977788SAdrian Chadd aniState->rxFrameCount = AH5416(ah)->ah_rxBusy;
86808977788SAdrian Chadd aniState->txFrameCount = AH5416(ah)->ah_txBusy;
869af5336e3SAdrian Chadd }
8702e4464a4SAdrian Chadd
87114779705SSam Leffler return listenTime;
87214779705SSam Leffler }
87314779705SSam Leffler
87414779705SSam Leffler /*
87514779705SSam Leffler * Update ani stats in preparation for listen time processing.
87614779705SSam Leffler */
87714779705SSam Leffler static void
updateMIBStats(struct ath_hal * ah,struct ar5212AniState * aniState)87814779705SSam Leffler updateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState)
87914779705SSam Leffler {
88014779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
88114779705SSam Leffler const struct ar5212AniParams *params = aniState->params;
88214779705SSam Leffler uint32_t phyCnt1, phyCnt2;
88314779705SSam Leffler int32_t ofdmPhyErrCnt, cckPhyErrCnt;
88414779705SSam Leffler
88514779705SSam Leffler /* Clear the mib counters and save them in the stats */
88614779705SSam Leffler ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
88714779705SSam Leffler
88814779705SSam Leffler /* NB: these are not reset-on-read */
88914779705SSam Leffler phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1);
89014779705SSam Leffler phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2);
89114779705SSam Leffler
89214779705SSam Leffler /* NB: these are spec'd to never roll-over */
89314779705SSam Leffler ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase;
89414779705SSam Leffler if (ofdmPhyErrCnt < 0) {
89514779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n",
89614779705SSam Leffler ofdmPhyErrCnt, phyCnt1);
89714779705SSam Leffler ofdmPhyErrCnt = AR_PHY_COUNTMAX;
89814779705SSam Leffler }
89914779705SSam Leffler ahp->ah_stats.ast_ani_ofdmerrs +=
90014779705SSam Leffler ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
90114779705SSam Leffler aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
90214779705SSam Leffler
90314779705SSam Leffler cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase;
90414779705SSam Leffler if (cckPhyErrCnt < 0) {
90514779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n",
90614779705SSam Leffler cckPhyErrCnt, phyCnt2);
90714779705SSam Leffler cckPhyErrCnt = AR_PHY_COUNTMAX;
90814779705SSam Leffler }
90914779705SSam Leffler ahp->ah_stats.ast_ani_cckerrs +=
91014779705SSam Leffler cckPhyErrCnt - aniState->cckPhyErrCount;
91114779705SSam Leffler aniState->cckPhyErrCount = cckPhyErrCnt;
91214779705SSam Leffler }
91314779705SSam Leffler
914a108ab63SAdrian Chadd void
ar5416RxMonitor(struct ath_hal * ah,const HAL_NODE_STATS * stats,const struct ieee80211_channel * chan)915a108ab63SAdrian Chadd ar5416RxMonitor(struct ath_hal *ah, const HAL_NODE_STATS *stats,
916a108ab63SAdrian Chadd const struct ieee80211_channel *chan)
917a108ab63SAdrian Chadd {
918a108ab63SAdrian Chadd struct ath_hal_5212 *ahp = AH5212(ah);
919a108ab63SAdrian Chadd ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi;
920a108ab63SAdrian Chadd }
921a108ab63SAdrian Chadd
92214779705SSam Leffler /*
92314779705SSam Leffler * Do periodic processing. This routine is called from the
92414779705SSam Leffler * driver's rx interrupt handler after processing frames.
92514779705SSam Leffler */
92614779705SSam Leffler void
ar5416AniPoll(struct ath_hal * ah,const struct ieee80211_channel * chan)927a108ab63SAdrian Chadd ar5416AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan)
92814779705SSam Leffler {
92914779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah);
93014779705SSam Leffler struct ar5212AniState *aniState = ahp->ah_curani;
93114779705SSam Leffler const struct ar5212AniParams *params;
93214779705SSam Leffler int32_t listenTime;
93314779705SSam Leffler
934af5336e3SAdrian Chadd /* Always update from the MIB, for statistics gathering */
935af5336e3SAdrian Chadd listenTime = ar5416AniGetListenTime(ah);
936af5336e3SAdrian Chadd
93714779705SSam Leffler /* XXX can aniState be null? */
93814779705SSam Leffler if (aniState == AH_NULL)
93914779705SSam Leffler return;
9402e4464a4SAdrian Chadd
94114779705SSam Leffler if (!ANI_ENA(ah))
94214779705SSam Leffler return;
94314779705SSam Leffler
94414779705SSam Leffler if (listenTime < 0) {
94514779705SSam Leffler ahp->ah_stats.ast_ani_lneg++;
94614779705SSam Leffler /* restart ANI period if listenTime is invalid */
9475b0c1ea0SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_ANI, "%s: invalid listenTime\n",
9485b0c1ea0SAdrian Chadd __func__);
94914779705SSam Leffler ar5416AniRestart(ah, aniState);
9507b1c2c4eSAdrian Chadd
9517b1c2c4eSAdrian Chadd /* Don't do any further processing */
9527b1c2c4eSAdrian Chadd return;
95314779705SSam Leffler }
95414779705SSam Leffler /* XXX beware of overflow? */
95514779705SSam Leffler aniState->listenTime += listenTime;
95614779705SSam Leffler
95714779705SSam Leffler OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime);
95814779705SSam Leffler
95914779705SSam Leffler params = aniState->params;
96014779705SSam Leffler if (aniState->listenTime > 5*params->period) {
96114779705SSam Leffler /*
96214779705SSam Leffler * Check to see if need to lower immunity if
96314779705SSam Leffler * 5 aniPeriods have passed
96414779705SSam Leffler */
96514779705SSam Leffler updateMIBStats(ah, aniState);
96614779705SSam Leffler if (aniState->ofdmPhyErrCount <= aniState->listenTime *
96714779705SSam Leffler params->ofdmTrigLow/1000 &&
96814779705SSam Leffler aniState->cckPhyErrCount <= aniState->listenTime *
96914779705SSam Leffler params->cckTrigLow/1000)
97014779705SSam Leffler ar5416AniLowerImmunity(ah);
9715b0c1ea0SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_ANI, "%s: lower immunity\n",
9725b0c1ea0SAdrian Chadd __func__);
97314779705SSam Leffler ar5416AniRestart(ah, aniState);
97414779705SSam Leffler } else if (aniState->listenTime > params->period) {
97514779705SSam Leffler updateMIBStats(ah, aniState);
97614779705SSam Leffler /* check to see if need to raise immunity */
97714779705SSam Leffler if (aniState->ofdmPhyErrCount > aniState->listenTime *
97814779705SSam Leffler params->ofdmTrigHigh / 1000) {
9799082beb0SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_ANI,
9809082beb0SAdrian Chadd "%s: OFDM err %u listenTime %u\n", __func__,
9819082beb0SAdrian Chadd aniState->ofdmPhyErrCount, aniState->listenTime);
98214779705SSam Leffler ar5416AniOfdmErrTrigger(ah);
98314779705SSam Leffler ar5416AniRestart(ah, aniState);
98414779705SSam Leffler } else if (aniState->cckPhyErrCount > aniState->listenTime *
98514779705SSam Leffler params->cckTrigHigh / 1000) {
9869082beb0SAdrian Chadd HALDEBUG(ah, HAL_DEBUG_ANI,
9879082beb0SAdrian Chadd "%s: CCK err %u listenTime %u\n", __func__,
988810f2a9cSAdrian Chadd aniState->cckPhyErrCount, aniState->listenTime);
98914779705SSam Leffler ar5416AniCckErrTrigger(ah);
99014779705SSam Leffler ar5416AniRestart(ah, aniState);
99114779705SSam Leffler }
99214779705SSam Leffler }
99314779705SSam Leffler }
994