1185377Ssam/*
2187831Ssam * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
3185377Ssam * Copyright (c) 2002-2008 Atheros Communications, Inc.
4185377Ssam *
5185377Ssam * Permission to use, copy, modify, and/or distribute this software for any
6185377Ssam * purpose with or without fee is hereby granted, provided that the above
7185377Ssam * copyright notice and this permission notice appear in all copies.
8185377Ssam *
9185377Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10185377Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11185377Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12185377Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13185377Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14185377Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15185377Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16185377Ssam *
17186332Ssam * $FreeBSD$
18185377Ssam */
19185377Ssam#include "opt_ah.h"
20185377Ssam
21185377Ssam#include "ah.h"
22185377Ssam#include "ah_internal.h"
23185377Ssam#include "ah_desc.h"
24185377Ssam
25185377Ssam#include "ar5212/ar5212.h"
26185377Ssam#include "ar5212/ar5212reg.h"
27185377Ssam#include "ar5212/ar5212phy.h"
28185377Ssam
29185377Ssam/*
30185377Ssam * Anti noise immunity support.  We track phy errors and react
31185377Ssam * to excessive errors by adjusting the noise immunity parameters.
32185377Ssam */
33185377Ssam
34185377Ssam#define HAL_EP_RND(x, mul) \
35185377Ssam	((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
36185377Ssam#define	BEACON_RSSI(ahp) \
37185377Ssam	HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \
38185377Ssam		HAL_RSSI_EP_MULTIPLIER)
39185377Ssam
40185377Ssam/*
41185377Ssam * ANI processing tunes radio parameters according to PHY errors
42185377Ssam * and related information.  This is done for for noise and spur
43185377Ssam * immunity in all operating modes if the device indicates it's
44185377Ssam * capable at attach time.  In addition, when there is a reference
45185377Ssam * rssi value (e.g. beacon frames from an ap in station mode)
46185377Ssam * further tuning is done.
47185377Ssam *
48185377Ssam * ANI_ENA indicates whether any ANI processing should be done;
49185377Ssam * this is specified at attach time.
50185377Ssam *
51185377Ssam * ANI_ENA_RSSI indicates whether rssi-based processing should
52185377Ssam * done, this is enabled based on operating mode and is meaningful
53185377Ssam * only if ANI_ENA is true.
54185377Ssam *
55185377Ssam * ANI parameters are typically controlled only by the hal.  The
56185377Ssam * AniControl interface however permits manual tuning through the
57185377Ssam * diagnostic api.
58185377Ssam */
59185377Ssam#define ANI_ENA(ah) \
60185377Ssam	(AH5212(ah)->ah_procPhyErr & HAL_ANI_ENA)
61185377Ssam#define ANI_ENA_RSSI(ah) \
62185377Ssam	(AH5212(ah)->ah_procPhyErr & HAL_RSSI_ANI_ENA)
63185377Ssam
64185377Ssam#define	ah_mibStats	ah_stats.ast_mibstats
65185377Ssam
66185377Ssamstatic void
67185377SsamenableAniMIBCounters(struct ath_hal *ah, const struct ar5212AniParams *params)
68185377Ssam{
69185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
70185377Ssam
71185377Ssam	HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Enable mib counters: "
72185377Ssam	    "OfdmPhyErrBase 0x%x cckPhyErrBase 0x%x\n",
73185377Ssam	    __func__, params->ofdmPhyErrBase, params->cckPhyErrBase);
74185377Ssam
75185377Ssam	OS_REG_WRITE(ah, AR_FILTOFDM, 0);
76185377Ssam	OS_REG_WRITE(ah, AR_FILTCCK, 0);
77185377Ssam
78185377Ssam	OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase);
79185377Ssam	OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase);
80185377Ssam	OS_REG_WRITE(ah, AR_PHYCNTMASK1, AR_PHY_ERR_OFDM_TIMING);
81185377Ssam	OS_REG_WRITE(ah, AR_PHYCNTMASK2, AR_PHY_ERR_CCK_TIMING);
82185377Ssam
83185377Ssam	ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);	/* save+clear counters*/
84185377Ssam	ar5212EnableMibCounters(ah);			/* enable everything */
85185377Ssam}
86185377Ssam
87185377Ssamstatic void
88185377SsamdisableAniMIBCounters(struct ath_hal *ah)
89185377Ssam{
90185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
91185377Ssam
92185377Ssam	HALDEBUG(ah, HAL_DEBUG_ANI, "Disable MIB counters\n");
93185377Ssam
94185377Ssam	ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);	/* save stats */
95185377Ssam	ar5212DisableMibCounters(ah);			/* disable everything */
96185377Ssam
97185377Ssam	OS_REG_WRITE(ah, AR_PHYCNTMASK1, 0);
98185377Ssam	OS_REG_WRITE(ah, AR_PHYCNTMASK2, 0);
99185377Ssam}
100185377Ssam
101185377Ssam/*
102185377Ssam * Return the current ANI state of the channel we're on
103185377Ssam */
104185377Ssamstruct ar5212AniState *
105185377Ssamar5212AniGetCurrentState(struct ath_hal *ah)
106185377Ssam{
107185377Ssam	return AH5212(ah)->ah_curani;
108185377Ssam}
109185377Ssam
110185377Ssam/*
111185377Ssam * Return the current statistics.
112185377Ssam */
113280940SadrianHAL_ANI_STATS *
114185377Ssamar5212AniGetCurrentStats(struct ath_hal *ah)
115185377Ssam{
116185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
117185377Ssam
118185377Ssam	/* update mib stats so we return current data */
119185377Ssam	/* XXX? side-effects to doing this here? */
120185377Ssam	ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
121185377Ssam	return &ahp->ah_stats;
122185377Ssam}
123185377Ssam
124185377Ssamstatic void
125185377SsamsetPhyErrBase(struct ath_hal *ah, struct ar5212AniParams *params)
126185377Ssam{
127185377Ssam	if (params->ofdmTrigHigh >= AR_PHY_COUNTMAX) {
128185377Ssam		HALDEBUG(ah, HAL_DEBUG_ANY,
129185377Ssam		    "OFDM Trigger %d is too high for hw counters, using max\n",
130185377Ssam		    params->ofdmTrigHigh);
131185377Ssam		params->ofdmPhyErrBase = 0;
132185377Ssam	} else
133185377Ssam		params->ofdmPhyErrBase = AR_PHY_COUNTMAX - params->ofdmTrigHigh;
134185377Ssam	if (params->cckTrigHigh >= AR_PHY_COUNTMAX) {
135185377Ssam		HALDEBUG(ah, HAL_DEBUG_ANY,
136185377Ssam		    "CCK Trigger %d is too high for hw counters, using max\n",
137185377Ssam		    params->cckTrigHigh);
138185377Ssam		params->cckPhyErrBase = 0;
139185377Ssam	} else
140185377Ssam		params->cckPhyErrBase = AR_PHY_COUNTMAX - params->cckTrigHigh;
141185377Ssam}
142185377Ssam
143185377Ssam/*
144185377Ssam * Setup ANI handling.  Sets all thresholds and reset the
145185377Ssam * channel statistics.  Note that ar5212AniReset should be
146185377Ssam * called by ar5212Reset before anything else happens and
147185377Ssam * that's where we force initial settings.
148185377Ssam */
149185377Ssamvoid
150185377Ssamar5212AniAttach(struct ath_hal *ah, const struct ar5212AniParams *params24,
151185377Ssam	const struct ar5212AniParams *params5, HAL_BOOL enable)
152185377Ssam{
153185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
154185377Ssam
155185377Ssam	ahp->ah_hasHwPhyCounters =
156185377Ssam		AH_PRIVATE(ah)->ah_caps.halHwPhyCounterSupport;
157185377Ssam
158185377Ssam	if (params24 != AH_NULL) {
159185377Ssam		OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24));
160185377Ssam		setPhyErrBase(ah, &ahp->ah_aniParams24);
161185377Ssam	}
162185377Ssam	if (params5 != AH_NULL) {
163185377Ssam		OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5));
164185377Ssam		setPhyErrBase(ah, &ahp->ah_aniParams5);
165185377Ssam	}
166185377Ssam
167185377Ssam	OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani));
168185377Ssam	if (ahp->ah_hasHwPhyCounters) {
169185377Ssam		/* Enable MIB Counters */
170185377Ssam		enableAniMIBCounters(ah, &ahp->ah_aniParams24 /*XXX*/);
171185377Ssam	}
172185377Ssam	if (enable) {		/* Enable ani now */
173185377Ssam		HALASSERT(params24 != AH_NULL && params5 != AH_NULL);
174185377Ssam		ahp->ah_procPhyErr |= HAL_ANI_ENA;
175185377Ssam	} else {
176185377Ssam		ahp->ah_procPhyErr &= ~HAL_ANI_ENA;
177185377Ssam	}
178185377Ssam}
179185377Ssam
180185377SsamHAL_BOOL
181185377Ssamar5212AniSetParams(struct ath_hal *ah, const struct ar5212AniParams *params24,
182185377Ssam	const struct ar5212AniParams *params5)
183185377Ssam{
184185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
185185377Ssam	HAL_BOOL ena = (ahp->ah_procPhyErr & HAL_ANI_ENA) != 0;
186185377Ssam
187185377Ssam	ar5212AniControl(ah, HAL_ANI_MODE, AH_FALSE);
188185377Ssam
189185377Ssam	OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24));
190185377Ssam	setPhyErrBase(ah, &ahp->ah_aniParams24);
191185377Ssam	OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5));
192185377Ssam	setPhyErrBase(ah, &ahp->ah_aniParams5);
193185377Ssam
194185377Ssam	OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani));
195185377Ssam	ar5212AniReset(ah, AH_PRIVATE(ah)->ah_curchan,
196185377Ssam	    AH_PRIVATE(ah)->ah_opmode, AH_FALSE);
197185377Ssam
198185377Ssam	ar5212AniControl(ah, HAL_ANI_MODE, ena);
199185377Ssam
200185377Ssam	return AH_TRUE;
201185377Ssam}
202185377Ssam
203185377Ssam/*
204185377Ssam * Cleanup any ANI state setup.
205185377Ssam */
206185377Ssamvoid
207185377Ssamar5212AniDetach(struct ath_hal *ah)
208185377Ssam{
209185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
210185377Ssam
211185377Ssam	HALDEBUG(ah, HAL_DEBUG_ANI, "Detaching Ani\n");
212185377Ssam	if (ahp->ah_hasHwPhyCounters)
213185377Ssam		disableAniMIBCounters(ah);
214185377Ssam}
215185377Ssam
216185377Ssam/*
217185377Ssam * Control Adaptive Noise Immunity Parameters
218185377Ssam */
219185377SsamHAL_BOOL
220185377Ssamar5212AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param)
221185377Ssam{
222185377Ssam	typedef int TABLE[];
223185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
224185377Ssam	struct ar5212AniState *aniState = ahp->ah_curani;
225224514Sadrian	const struct ar5212AniParams *params = AH_NULL;
226224514Sadrian
227224514Sadrian	/*
228224514Sadrian	 * This function may be called before there's a current
229224514Sadrian	 * channel (eg to disable ANI.)
230224514Sadrian	 */
231224514Sadrian	if (aniState != AH_NULL)
232224514Sadrian		params = aniState->params;
233185377Ssam
234185377Ssam	OS_MARK(ah, AH_MARK_ANI_CONTROL, cmd);
235185377Ssam
236185377Ssam	switch (cmd) {
237185377Ssam	case HAL_ANI_NOISE_IMMUNITY_LEVEL: {
238185377Ssam		u_int level = param;
239185377Ssam
240186332Ssam		if (level > params->maxNoiseImmunityLevel) {
241185377Ssam			HALDEBUG(ah, HAL_DEBUG_ANY,
242185377Ssam			    "%s: level out of range (%u > %u)\n",
243185377Ssam			    __func__, level, params->maxNoiseImmunityLevel);
244185377Ssam			return AH_FALSE;
245185377Ssam		}
246185377Ssam
247185377Ssam		OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ,
248185377Ssam		    AR_PHY_DESIRED_SZ_TOT_DES, params->totalSizeDesired[level]);
249185377Ssam		OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
250185377Ssam		    AR_PHY_AGC_CTL1_COARSE_LOW, params->coarseLow[level]);
251185377Ssam		OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
252185377Ssam		    AR_PHY_AGC_CTL1_COARSE_HIGH, params->coarseHigh[level]);
253185377Ssam		OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
254185377Ssam		    AR_PHY_FIND_SIG_FIRPWR, params->firpwr[level]);
255185377Ssam
256185377Ssam		if (level > aniState->noiseImmunityLevel)
257185377Ssam			ahp->ah_stats.ast_ani_niup++;
258185377Ssam		else if (level < aniState->noiseImmunityLevel)
259185377Ssam			ahp->ah_stats.ast_ani_nidown++;
260185377Ssam		aniState->noiseImmunityLevel = level;
261185377Ssam		break;
262185377Ssam	}
263185377Ssam	case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: {
264185377Ssam		static const TABLE m1ThreshLow   = { 127,   50 };
265185377Ssam		static const TABLE m2ThreshLow   = { 127,   40 };
266185377Ssam		static const TABLE m1Thresh      = { 127, 0x4d };
267185377Ssam		static const TABLE m2Thresh      = { 127, 0x40 };
268185377Ssam		static const TABLE m2CountThr    = {  31,   16 };
269185377Ssam		static const TABLE m2CountThrLow = {  63,   48 };
270185377Ssam		u_int on = param ? 1 : 0;
271185377Ssam
272185377Ssam		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
273185377Ssam			AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1ThreshLow[on]);
274185377Ssam		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
275185377Ssam			AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2ThreshLow[on]);
276185377Ssam		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,
277185377Ssam			AR_PHY_SFCORR_M1_THRESH, m1Thresh[on]);
278185377Ssam		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,
279185377Ssam			AR_PHY_SFCORR_M2_THRESH, m2Thresh[on]);
280185377Ssam		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,
281185377Ssam			AR_PHY_SFCORR_M2COUNT_THR, m2CountThr[on]);
282185377Ssam		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
283185377Ssam			AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2CountThrLow[on]);
284185377Ssam
285185377Ssam		if (on) {
286185377Ssam			OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
287185377Ssam				AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
288188444Ssam			ahp->ah_stats.ast_ani_ofdmon++;
289185377Ssam		} else {
290185377Ssam			OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,
291185377Ssam				AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
292188444Ssam			ahp->ah_stats.ast_ani_ofdmoff++;
293185377Ssam		}
294185377Ssam		aniState->ofdmWeakSigDetectOff = !on;
295185377Ssam		break;
296185377Ssam	}
297185377Ssam	case HAL_ANI_CCK_WEAK_SIGNAL_THR: {
298185377Ssam		static const TABLE weakSigThrCck = { 8, 6 };
299185377Ssam		u_int high = param ? 1 : 0;
300185377Ssam
301185377Ssam		OS_REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT,
302185377Ssam		    AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, weakSigThrCck[high]);
303185377Ssam		if (high)
304185377Ssam			ahp->ah_stats.ast_ani_cckhigh++;
305185377Ssam		else
306185377Ssam			ahp->ah_stats.ast_ani_ccklow++;
307185377Ssam		aniState->cckWeakSigThreshold = high;
308185377Ssam		break;
309185377Ssam	}
310185377Ssam	case HAL_ANI_FIRSTEP_LEVEL: {
311185377Ssam		u_int level = param;
312185377Ssam
313186332Ssam		if (level > params->maxFirstepLevel) {
314185377Ssam			HALDEBUG(ah, HAL_DEBUG_ANY,
315185377Ssam			    "%s: level out of range (%u > %u)\n",
316185377Ssam			    __func__, level, params->maxFirstepLevel);
317185377Ssam			return AH_FALSE;
318185377Ssam		}
319185377Ssam		OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
320185377Ssam		    AR_PHY_FIND_SIG_FIRSTEP, params->firstep[level]);
321185377Ssam		if (level > aniState->firstepLevel)
322185377Ssam			ahp->ah_stats.ast_ani_stepup++;
323185377Ssam		else if (level < aniState->firstepLevel)
324185377Ssam			ahp->ah_stats.ast_ani_stepdown++;
325185377Ssam		aniState->firstepLevel = level;
326185377Ssam		break;
327185377Ssam	}
328185377Ssam	case HAL_ANI_SPUR_IMMUNITY_LEVEL: {
329185377Ssam		u_int level = param;
330185377Ssam
331186332Ssam		if (level > params->maxSpurImmunityLevel) {
332185377Ssam			HALDEBUG(ah, HAL_DEBUG_ANY,
333185377Ssam			    "%s: level out of range (%u > %u)\n",
334185377Ssam			    __func__, level, params->maxSpurImmunityLevel);
335185377Ssam			return AH_FALSE;
336185377Ssam		}
337185377Ssam		OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5,
338185377Ssam		    AR_PHY_TIMING5_CYCPWR_THR1, params->cycPwrThr1[level]);
339185377Ssam		if (level > aniState->spurImmunityLevel)
340185377Ssam			ahp->ah_stats.ast_ani_spurup++;
341185377Ssam		else if (level < aniState->spurImmunityLevel)
342185377Ssam			ahp->ah_stats.ast_ani_spurdown++;
343185377Ssam		aniState->spurImmunityLevel = level;
344185377Ssam		break;
345185377Ssam	}
346185377Ssam	case HAL_ANI_PRESENT:
347185377Ssam		break;
348185377Ssam	case HAL_ANI_MODE:
349185377Ssam		if (param == 0) {
350185377Ssam			ahp->ah_procPhyErr &= ~HAL_ANI_ENA;
351185377Ssam			/* Turn off HW counters if we have them */
352185377Ssam			ar5212AniDetach(ah);
353224514Sadrian			ah->ah_setRxFilter(ah,
354224514Sadrian			    ah->ah_getRxFilter(ah) &~ HAL_RX_FILTER_PHYERR);
355185377Ssam		} else {			/* normal/auto mode */
356185377Ssam			/* don't mess with state if already enabled */
357185377Ssam			if (ahp->ah_procPhyErr & HAL_ANI_ENA)
358185377Ssam				break;
359185377Ssam			if (ahp->ah_hasHwPhyCounters) {
360185377Ssam				ar5212SetRxFilter(ah,
361185377Ssam					ar5212GetRxFilter(ah) &~ HAL_RX_FILTER_PHYERR);
362185377Ssam				/* Enable MIB Counters */
363185377Ssam				enableAniMIBCounters(ah,
364185377Ssam				    ahp->ah_curani != AH_NULL ?
365185377Ssam					ahp->ah_curani->params:
366185377Ssam					&ahp->ah_aniParams24 /*XXX*/);
367185377Ssam			} else {
368224514Sadrian				ah->ah_setRxFilter(ah,
369224514Sadrian				    ah->ah_getRxFilter(ah) | HAL_RX_FILTER_PHYERR);
370185377Ssam			}
371185377Ssam			ahp->ah_procPhyErr |= HAL_ANI_ENA;
372185377Ssam		}
373185377Ssam		break;
374185377Ssam#ifdef AH_PRIVATE_DIAG
375185377Ssam	case HAL_ANI_PHYERR_RESET:
376185377Ssam		ahp->ah_stats.ast_ani_ofdmerrs = 0;
377185377Ssam		ahp->ah_stats.ast_ani_cckerrs = 0;
378185377Ssam		break;
379185377Ssam#endif /* AH_PRIVATE_DIAG */
380185377Ssam	default:
381185377Ssam		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid cmd %u\n",
382185377Ssam		    __func__, cmd);
383185377Ssam		return AH_FALSE;
384185377Ssam	}
385185377Ssam	return AH_TRUE;
386185377Ssam}
387185377Ssam
388185377Ssamstatic void
389185377Ssamar5212AniOfdmErrTrigger(struct ath_hal *ah)
390185377Ssam{
391185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
392187831Ssam	const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
393185377Ssam	struct ar5212AniState *aniState;
394185377Ssam	const struct ar5212AniParams *params;
395185377Ssam
396185377Ssam	HALASSERT(chan != AH_NULL);
397185377Ssam
398185377Ssam	if (!ANI_ENA(ah))
399185377Ssam		return;
400185377Ssam
401185377Ssam	aniState = ahp->ah_curani;
402185377Ssam	params = aniState->params;
403185377Ssam	/* First, raise noise immunity level, up to max */
404186332Ssam	if (aniState->noiseImmunityLevel+1 <= params->maxNoiseImmunityLevel) {
405185380Ssam		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise NI to %u\n", __func__,
406185380Ssam		    aniState->noiseImmunityLevel + 1);
407185377Ssam		ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
408185377Ssam				 aniState->noiseImmunityLevel + 1);
409185377Ssam		return;
410185377Ssam	}
411185377Ssam	/* then, raise spur immunity level, up to max */
412186332Ssam	if (aniState->spurImmunityLevel+1 <= params->maxSpurImmunityLevel) {
413185380Ssam		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise SI to %u\n", __func__,
414185380Ssam		    aniState->spurImmunityLevel + 1);
415185377Ssam		ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
416185377Ssam				 aniState->spurImmunityLevel + 1);
417185377Ssam		return;
418185377Ssam	}
419185377Ssam
420185377Ssam	if (ANI_ENA_RSSI(ah)) {
421185377Ssam		int32_t rssi = BEACON_RSSI(ahp);
422185377Ssam		if (rssi > params->rssiThrHigh) {
423185377Ssam			/*
424185377Ssam			 * Beacon rssi is high, can turn off ofdm
425185377Ssam			 * weak sig detect.
426185377Ssam			 */
427185377Ssam			if (!aniState->ofdmWeakSigDetectOff) {
428185380Ssam				HALDEBUG(ah, HAL_DEBUG_ANI,
429185380Ssam				    "%s: rssi %d OWSD off\n", __func__, rssi);
430185377Ssam				ar5212AniControl(ah,
431185377Ssam				    HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
432185377Ssam				    AH_FALSE);
433185377Ssam				ar5212AniControl(ah,
434185377Ssam				    HAL_ANI_SPUR_IMMUNITY_LEVEL, 0);
435185377Ssam				return;
436185377Ssam			}
437185377Ssam			/*
438185377Ssam			 * If weak sig detect is already off, as last resort,
439185377Ssam			 * raise firstep level
440185377Ssam			 */
441186332Ssam			if (aniState->firstepLevel+1 <= params->maxFirstepLevel) {
442185380Ssam				HALDEBUG(ah, HAL_DEBUG_ANI,
443185380Ssam				    "%s: rssi %d raise ST %u\n", __func__, rssi,
444185380Ssam				    aniState->firstepLevel+1);
445185377Ssam				ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
446185377Ssam						 aniState->firstepLevel + 1);
447185377Ssam				return;
448185377Ssam			}
449185377Ssam		} else if (rssi > params->rssiThrLow) {
450185377Ssam			/*
451185377Ssam			 * Beacon rssi in mid range, need ofdm weak signal
452185377Ssam			 * detect, but we can raise firststepLevel.
453185377Ssam			 */
454185380Ssam			if (aniState->ofdmWeakSigDetectOff) {
455185380Ssam				HALDEBUG(ah, HAL_DEBUG_ANI,
456185380Ssam				    "%s: rssi %d OWSD on\n", __func__, rssi);
457185377Ssam				ar5212AniControl(ah,
458185377Ssam				    HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
459185377Ssam				    AH_TRUE);
460185380Ssam			}
461186332Ssam			if (aniState->firstepLevel+1 <= params->maxFirstepLevel) {
462185380Ssam				HALDEBUG(ah, HAL_DEBUG_ANI,
463185380Ssam				    "%s: rssi %d raise ST %u\n", __func__, rssi,
464185380Ssam				    aniState->firstepLevel+1);
465185377Ssam				ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
466185377Ssam				     aniState->firstepLevel + 1);
467185380Ssam			}
468185377Ssam			return;
469185377Ssam		} else {
470185377Ssam			/*
471185377Ssam			 * Beacon rssi is low, if in 11b/g mode, turn off ofdm
472185377Ssam			 * weak signal detection and zero firstepLevel to
473185377Ssam			 * maximize CCK sensitivity
474185377Ssam			 */
475187831Ssam			if (IEEE80211_IS_CHAN_CCK(chan)) {
476185380Ssam				if (!aniState->ofdmWeakSigDetectOff) {
477185380Ssam					HALDEBUG(ah, HAL_DEBUG_ANI,
478185380Ssam					    "%s: rssi %d OWSD off\n",
479185380Ssam					    __func__, rssi);
480185377Ssam					ar5212AniControl(ah,
481185377Ssam					    HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
482185377Ssam					    AH_FALSE);
483185380Ssam				}
484185380Ssam				if (aniState->firstepLevel > 0) {
485185380Ssam					HALDEBUG(ah, HAL_DEBUG_ANI,
486185380Ssam					    "%s: rssi %d zero ST (was %u)\n",
487185380Ssam					    __func__, rssi,
488185380Ssam					    aniState->firstepLevel);
489185377Ssam					ar5212AniControl(ah,
490185377Ssam					     HAL_ANI_FIRSTEP_LEVEL, 0);
491185380Ssam				}
492185377Ssam				return;
493185377Ssam			}
494185377Ssam		}
495185377Ssam	}
496185377Ssam}
497185377Ssam
498185377Ssamstatic void
499185377Ssamar5212AniCckErrTrigger(struct ath_hal *ah)
500185377Ssam{
501185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
502187831Ssam	const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
503185377Ssam	struct ar5212AniState *aniState;
504185377Ssam	const struct ar5212AniParams *params;
505185377Ssam
506185377Ssam	HALASSERT(chan != AH_NULL);
507185377Ssam
508185377Ssam	if (!ANI_ENA(ah))
509185377Ssam		return;
510185377Ssam
511185377Ssam	/* first, raise noise immunity level, up to max */
512185377Ssam	aniState = ahp->ah_curani;
513185377Ssam	params = aniState->params;
514186332Ssam	if (aniState->noiseImmunityLevel+1 <= params->maxNoiseImmunityLevel) {
515185380Ssam		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise NI to %u\n", __func__,
516185380Ssam		    aniState->noiseImmunityLevel + 1);
517185377Ssam		ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
518185377Ssam				 aniState->noiseImmunityLevel + 1);
519185377Ssam		return;
520185377Ssam	}
521185377Ssam
522185377Ssam	if (ANI_ENA_RSSI(ah)) {
523185377Ssam		int32_t rssi = BEACON_RSSI(ahp);
524185377Ssam		if (rssi >  params->rssiThrLow) {
525185377Ssam			/*
526185377Ssam			 * Beacon signal in mid and high range,
527185377Ssam			 * raise firstep level.
528185377Ssam			 */
529186332Ssam			if (aniState->firstepLevel+1 <= params->maxFirstepLevel) {
530185380Ssam				HALDEBUG(ah, HAL_DEBUG_ANI,
531185380Ssam				    "%s: rssi %d raise ST %u\n", __func__, rssi,
532185380Ssam				    aniState->firstepLevel+1);
533185377Ssam				ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
534185377Ssam						 aniState->firstepLevel + 1);
535185380Ssam			}
536185377Ssam		} else {
537185377Ssam			/*
538185377Ssam			 * Beacon rssi is low, zero firstep level to maximize
539185377Ssam			 * CCK sensitivity in 11b/g mode.
540185377Ssam			 */
541185377Ssam			/* XXX can optimize */
542187831Ssam			if (IEEE80211_IS_CHAN_B(chan) ||
543187831Ssam			    IEEE80211_IS_CHAN_G(chan)) {
544185380Ssam				if (aniState->firstepLevel > 0) {
545185380Ssam					HALDEBUG(ah, HAL_DEBUG_ANI,
546185380Ssam					    "%s: rssi %d zero ST (was %u)\n",
547185380Ssam					    __func__, rssi,
548185380Ssam					    aniState->firstepLevel);
549185377Ssam					ar5212AniControl(ah,
550185377Ssam					    HAL_ANI_FIRSTEP_LEVEL, 0);
551185380Ssam				}
552185377Ssam			}
553185377Ssam		}
554185377Ssam	}
555185377Ssam}
556185377Ssam
557185377Ssamstatic void
558185377Ssamar5212AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState)
559185377Ssam{
560185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
561185377Ssam
562185377Ssam	aniState->listenTime = 0;
563185377Ssam	if (ahp->ah_hasHwPhyCounters) {
564185377Ssam		const struct ar5212AniParams *params = aniState->params;
565185377Ssam		/*
566185377Ssam		 * NB: these are written on reset based on the
567185377Ssam		 *     ini so we must re-write them!
568185377Ssam		 */
569185377Ssam		OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase);
570185377Ssam		OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase);
571185377Ssam		OS_REG_WRITE(ah, AR_PHYCNTMASK1, AR_PHY_ERR_OFDM_TIMING);
572185377Ssam		OS_REG_WRITE(ah, AR_PHYCNTMASK2, AR_PHY_ERR_CCK_TIMING);
573185377Ssam
574185377Ssam		/* Clear the mib counters and save them in the stats */
575185377Ssam		ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
576185377Ssam	}
577185377Ssam	aniState->ofdmPhyErrCount = 0;
578185377Ssam	aniState->cckPhyErrCount = 0;
579185377Ssam}
580185377Ssam
581185377Ssam/*
582185377Ssam * Restore/reset the ANI parameters and reset the statistics.
583185377Ssam * This routine must be called for every channel change.
584185377Ssam */
585185377Ssamvoid
586187831Ssamar5212AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan,
587185377Ssam	HAL_OPMODE opmode, int restore)
588185377Ssam{
589185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
590187831Ssam	HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
591187831Ssam	/* XXX bounds check ic_devdata */
592187831Ssam	struct ar5212AniState *aniState = &ahp->ah_ani[chan->ic_devdata];
593185377Ssam	uint32_t rxfilter;
594185377Ssam
595187831Ssam	if ((ichan->privFlags & CHANNEL_ANI_INIT) == 0) {
596187831Ssam		OS_MEMZERO(aniState, sizeof(*aniState));
597187831Ssam		if (IEEE80211_IS_CHAN_2GHZ(chan))
598187831Ssam			aniState->params = &ahp->ah_aniParams24;
599187831Ssam		else
600187831Ssam			aniState->params = &ahp->ah_aniParams5;
601187831Ssam		ichan->privFlags |= CHANNEL_ANI_INIT;
602187831Ssam		HALASSERT((ichan->privFlags & CHANNEL_ANI_SETUP) == 0);
603187831Ssam	}
604185377Ssam	ahp->ah_curani = aniState;
605185377Ssam#if 0
606187831Ssam	ath_hal_printf(ah,"%s: chan %u/0x%x restore %d opmode %u%s\n",
607187831Ssam	    __func__, chan->ic_freq, chan->ic_flags, restore, opmode,
608187831Ssam	    ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : "");
609185377Ssam#else
610187831Ssam	HALDEBUG(ah, HAL_DEBUG_ANI, "%s: chan %u/0x%x restore %d opmode %u%s\n",
611187831Ssam	    __func__, chan->ic_freq, chan->ic_flags, restore, opmode,
612187831Ssam	    ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : "");
613185377Ssam#endif
614185377Ssam	OS_MARK(ah, AH_MARK_ANI_RESET, opmode);
615185377Ssam
616185377Ssam	/*
617185377Ssam	 * Turn off PHY error frame delivery while we futz with settings.
618185377Ssam	 */
619224514Sadrian	rxfilter = ah->ah_getRxFilter(ah);
620224514Sadrian	ah->ah_setRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR);
621224514Sadrian
622185377Ssam	/*
623224514Sadrian	 * If ANI is disabled at this point, don't set the default
624224514Sadrian	 * ANI parameter settings - leave the HAL settings there.
625224514Sadrian	 * This is (currently) needed for reliable radar detection.
626224514Sadrian	 */
627224514Sadrian	if (! ANI_ENA(ah)) {
628224514Sadrian		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: ANI disabled\n",
629224514Sadrian		    __func__);
630224514Sadrian		goto finish;
631224514Sadrian	}
632224514Sadrian
633224514Sadrian	/*
634185377Ssam	 * Automatic processing is done only in station mode right now.
635185377Ssam	 */
636185377Ssam	if (opmode == HAL_M_STA)
637185377Ssam		ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA;
638185377Ssam	else
639185377Ssam		ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA;
640185377Ssam	/*
641185377Ssam	 * Set all ani parameters.  We either set them to initial
642185377Ssam	 * values or restore the previous ones for the channel.
643185377Ssam	 * XXX if ANI follows hardware, we don't care what mode we're
644185377Ssam	 * XXX in, we should keep the ani parameters
645185377Ssam	 */
646187831Ssam	if (restore && (ichan->privFlags & CHANNEL_ANI_SETUP)) {
647185377Ssam		ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
648185377Ssam				 aniState->noiseImmunityLevel);
649185377Ssam		ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
650185377Ssam				 aniState->spurImmunityLevel);
651185377Ssam		ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
652185377Ssam				 !aniState->ofdmWeakSigDetectOff);
653185377Ssam		ar5212AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR,
654185377Ssam				 aniState->cckWeakSigThreshold);
655185377Ssam		ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
656185377Ssam				 aniState->firstepLevel);
657185377Ssam	} else {
658185377Ssam		ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0);
659185377Ssam		ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0);
660185377Ssam		ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
661185377Ssam			AH_TRUE);
662185377Ssam		ar5212AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE);
663185377Ssam		ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0);
664187831Ssam		ichan->privFlags |= CHANNEL_ANI_SETUP;
665185377Ssam	}
666224514Sadrian	/*
667224514Sadrian	 * In case the counters haven't yet been setup; set them up.
668224514Sadrian	 */
669224514Sadrian	enableAniMIBCounters(ah, ahp->ah_curani->params);
670185377Ssam	ar5212AniRestart(ah, aniState);
671185377Ssam
672224514Sadrianfinish:
673185377Ssam	/* restore RX filter mask */
674224514Sadrian	ah->ah_setRxFilter(ah, rxfilter);
675185377Ssam}
676185377Ssam
677185377Ssam/*
678185377Ssam * Process a MIB interrupt.  We may potentially be invoked because
679185377Ssam * any of the MIB counters overflow/trigger so don't assume we're
680185377Ssam * here because a PHY error counter triggered.
681185377Ssam */
682185377Ssamvoid
683185377Ssamar5212ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats)
684185377Ssam{
685185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
686185377Ssam	uint32_t phyCnt1, phyCnt2;
687185377Ssam
688185377Ssam	HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x "
689185377Ssam	    "filtofdm 0x%x filtcck 0x%x\n",
690185377Ssam	    __func__, OS_REG_READ(ah, AR_MIBC),
691185377Ssam	    OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2),
692185377Ssam	    OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK));
693185377Ssam
694185377Ssam	/*
695185377Ssam	 * First order of business is to clear whatever caused
696185377Ssam	 * the interrupt so we don't keep getting interrupted.
697185377Ssam	 * We have the usual mib counters that are reset-on-read
698185377Ssam	 * and the additional counters that appeared starting in
699185377Ssam	 * Hainan.  We collect the mib counters and explicitly
700185377Ssam	 * zero additional counters we are not using.  Anything
701185377Ssam	 * else is reset only if it caused the interrupt.
702185377Ssam	 */
703185377Ssam	/* NB: these are not reset-on-read */
704185377Ssam	phyCnt1 = OS_REG_READ(ah, AR_PHYCNT1);
705185377Ssam	phyCnt2 = OS_REG_READ(ah, AR_PHYCNT2);
706185377Ssam	/* not used, always reset them in case they are the cause */
707185377Ssam	OS_REG_WRITE(ah, AR_FILTOFDM, 0);
708185377Ssam	OS_REG_WRITE(ah, AR_FILTCCK, 0);
709185377Ssam
710185377Ssam	/* Clear the mib counters and save them in the stats */
711185377Ssam	ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
712185377Ssam	ahp->ah_stats.ast_nodestats = *stats;
713185377Ssam
714185377Ssam	/*
715185377Ssam	 * Check for an ani stat hitting the trigger threshold.
716185377Ssam	 * When this happens we get a MIB interrupt and the top
717185377Ssam	 * 2 bits of the counter register will be 0b11, hence
718185377Ssam	 * the mask check of phyCnt?.
719185377Ssam	 */
720185377Ssam	if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
721185377Ssam	    ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
722185377Ssam		struct ar5212AniState *aniState = ahp->ah_curani;
723185377Ssam		const struct ar5212AniParams *params = aniState->params;
724185377Ssam		uint32_t ofdmPhyErrCnt, cckPhyErrCnt;
725185377Ssam
726185377Ssam		ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase;
727185377Ssam		ahp->ah_stats.ast_ani_ofdmerrs +=
728185377Ssam			ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
729185377Ssam		aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
730185377Ssam
731185377Ssam		cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase;
732185377Ssam		ahp->ah_stats.ast_ani_cckerrs +=
733185377Ssam			cckPhyErrCnt - aniState->cckPhyErrCount;
734185377Ssam		aniState->cckPhyErrCount = cckPhyErrCnt;
735185377Ssam
736185377Ssam		/*
737185377Ssam		 * NB: figure out which counter triggered.  If both
738185377Ssam		 * trigger we'll only deal with one as the processing
739185377Ssam		 * clobbers the error counter so the trigger threshold
740185377Ssam		 * check will never be true.
741185377Ssam		 */
742185377Ssam		if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh)
743185377Ssam			ar5212AniOfdmErrTrigger(ah);
744185377Ssam		if (aniState->cckPhyErrCount > params->cckTrigHigh)
745185377Ssam			ar5212AniCckErrTrigger(ah);
746185377Ssam		/* NB: always restart to insure the h/w counters are reset */
747185377Ssam		ar5212AniRestart(ah, aniState);
748185377Ssam	}
749185377Ssam}
750185377Ssam
751185377Ssamvoid
752185377Ssamar5212AniPhyErrReport(struct ath_hal *ah, const struct ath_rx_status *rs)
753185377Ssam{
754185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
755185377Ssam	struct ar5212AniState *aniState;
756185377Ssam	const struct ar5212AniParams *params;
757185377Ssam
758185377Ssam	HALASSERT(!ahp->ah_hasHwPhyCounters && rs != AH_NULL);
759185377Ssam
760185377Ssam	aniState = ahp->ah_curani;
761185377Ssam	params = aniState->params;
762185377Ssam	if (rs->rs_phyerr == HAL_PHYERR_OFDM_TIMING) {
763185377Ssam		aniState->ofdmPhyErrCount++;
764185377Ssam		ahp->ah_stats.ast_ani_ofdmerrs++;
765185377Ssam		if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh) {
766185377Ssam			ar5212AniOfdmErrTrigger(ah);
767185377Ssam			ar5212AniRestart(ah, aniState);
768185377Ssam		}
769185377Ssam	} else if (rs->rs_phyerr == HAL_PHYERR_CCK_TIMING) {
770185377Ssam		aniState->cckPhyErrCount++;
771185377Ssam		ahp->ah_stats.ast_ani_cckerrs++;
772185377Ssam		if (aniState->cckPhyErrCount > params->cckTrigHigh) {
773185377Ssam			ar5212AniCckErrTrigger(ah);
774185377Ssam			ar5212AniRestart(ah, aniState);
775185377Ssam		}
776185377Ssam	}
777185377Ssam}
778185377Ssam
779185377Ssamstatic void
780185377Ssamar5212AniLowerImmunity(struct ath_hal *ah)
781185377Ssam{
782185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
783185377Ssam	struct ar5212AniState *aniState;
784185377Ssam	const struct ar5212AniParams *params;
785185377Ssam
786185377Ssam	HALASSERT(ANI_ENA(ah));
787185377Ssam
788185377Ssam	aniState = ahp->ah_curani;
789185377Ssam	params = aniState->params;
790185377Ssam	if (ANI_ENA_RSSI(ah)) {
791185377Ssam		int32_t rssi = BEACON_RSSI(ahp);
792185377Ssam		if (rssi > params->rssiThrHigh) {
793185377Ssam			/*
794185377Ssam			 * Beacon signal is high, leave ofdm weak signal
795185377Ssam			 * detection off or it may oscillate.  Let it fall
796185377Ssam			 * through.
797185377Ssam			 */
798185377Ssam		} else if (rssi > params->rssiThrLow) {
799185377Ssam			/*
800185377Ssam			 * Beacon rssi in mid range, turn on ofdm weak signal
801185377Ssam			 * detection or lower firstep level.
802185377Ssam			 */
803185377Ssam			if (aniState->ofdmWeakSigDetectOff) {
804185380Ssam				HALDEBUG(ah, HAL_DEBUG_ANI,
805185380Ssam				    "%s: rssi %d OWSD on\n", __func__, rssi);
806185377Ssam				ar5212AniControl(ah,
807185377Ssam				    HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
808185377Ssam				    AH_TRUE);
809185377Ssam				return;
810185377Ssam			}
811185377Ssam			if (aniState->firstepLevel > 0) {
812185380Ssam				HALDEBUG(ah, HAL_DEBUG_ANI,
813185380Ssam				    "%s: rssi %d lower ST %u\n", __func__, rssi,
814185380Ssam				    aniState->firstepLevel-1);
815185377Ssam				ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
816185377Ssam						 aniState->firstepLevel - 1);
817185377Ssam				return;
818185377Ssam			}
819185377Ssam		} else {
820185377Ssam			/*
821185377Ssam			 * Beacon rssi is low, reduce firstep level.
822185377Ssam			 */
823185377Ssam			if (aniState->firstepLevel > 0) {
824185380Ssam				HALDEBUG(ah, HAL_DEBUG_ANI,
825185380Ssam				    "%s: rssi %d lower ST %u\n", __func__, rssi,
826185380Ssam				    aniState->firstepLevel-1);
827185377Ssam				ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
828185377Ssam						 aniState->firstepLevel - 1);
829185377Ssam				return;
830185377Ssam			}
831185377Ssam		}
832185377Ssam	}
833185377Ssam	/* then lower spur immunity level, down to zero */
834185377Ssam	if (aniState->spurImmunityLevel > 0) {
835185380Ssam		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: lower SI %u\n",
836185380Ssam		    __func__, aniState->spurImmunityLevel-1);
837185377Ssam		ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
838185377Ssam				 aniState->spurImmunityLevel - 1);
839185377Ssam		return;
840185377Ssam	}
841185377Ssam	/*
842185377Ssam	 * if all else fails, lower noise immunity level down to a min value
843185377Ssam	 * zero for now
844185377Ssam	 */
845185377Ssam	if (aniState->noiseImmunityLevel > 0) {
846185380Ssam		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: lower NI %u\n",
847185380Ssam		    __func__, aniState->noiseImmunityLevel-1);
848185377Ssam		ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
849185377Ssam				 aniState->noiseImmunityLevel - 1);
850185377Ssam		return;
851185377Ssam	}
852185377Ssam}
853185377Ssam
854185377Ssam#define CLOCK_RATE 44000	/* XXX use mac_usec or similar */
855185377Ssam/* convert HW counter values to ms using 11g clock rate, goo9d enough
856185377Ssam   for 11a and Turbo */
857185377Ssam
858185377Ssam/*
859185377Ssam * Return an approximation of the time spent ``listening'' by
860185377Ssam * deducting the cycles spent tx'ing and rx'ing from the total
861185377Ssam * cycle count since our last call.  A return value <0 indicates
862185377Ssam * an invalid/inconsistent time.
863185377Ssam */
864185377Ssamstatic int32_t
865185377Ssamar5212AniGetListenTime(struct ath_hal *ah)
866185377Ssam{
867185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
868256139Sadrian	struct ar5212AniState *aniState = NULL;
869256139Sadrian	int32_t listenTime = 0;
870256139Sadrian	int good;
871256139Sadrian	HAL_SURVEY_SAMPLE hs;
872185377Ssam
873256139Sadrian	/*
874256139Sadrian	 * We shouldn't see ah_curchan be NULL, but just in case..
875256139Sadrian	 */
876256139Sadrian	if (AH_PRIVATE(ah)->ah_curchan == AH_NULL) {
877256139Sadrian		ath_hal_printf(ah, "%s: ah_curchan = NULL?\n", __func__);
878256139Sadrian		return (0);
879256139Sadrian	}
880185377Ssam
881256139Sadrian	/*
882256139Sadrian	 * Fetch the current statistics, squirrel away the current
883256139Sadrian	 * sample, bump the sequence/sample counter.
884256139Sadrian	 */
885256139Sadrian	OS_MEMZERO(&hs, sizeof(hs));
886256139Sadrian	good = ar5212GetMibCycleCounts(ah, &hs);
887280828Sadrian	ath_hal_survey_add_sample(ah, &hs);
888256139Sadrian
889256139Sadrian	if (ANI_ENA(ah))
890256139Sadrian		aniState = ahp->ah_curani;
891256139Sadrian
892256139Sadrian	if (good == AH_FALSE) {
893185377Ssam		/*
894185377Ssam		 * Cycle counter wrap (or initial call); it's not possible
895185377Ssam		 * to accurately calculate a value because the registers
896185377Ssam		 * right shift rather than wrap--so punt and return 0.
897185377Ssam		 */
898185377Ssam		listenTime = 0;
899185377Ssam		ahp->ah_stats.ast_ani_lzero++;
900256139Sadrian	} else if (ANI_ENA(ah)) {
901256139Sadrian		/*
902256139Sadrian		 * Only calculate and update the cycle count if we have
903256139Sadrian		 * an ANI state.
904256139Sadrian		 */
905256139Sadrian		int32_t ccdelta =
906256139Sadrian		    AH5212(ah)->ah_cycleCount - aniState->cycleCount;
907256139Sadrian		int32_t rfdelta =
908256139Sadrian		    AH5212(ah)->ah_rxBusy - aniState->rxFrameCount;
909256139Sadrian		int32_t tfdelta =
910256139Sadrian		    AH5212(ah)->ah_txBusy - aniState->txFrameCount;
911185377Ssam		listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE;
912185377Ssam	}
913256139Sadrian
914256139Sadrian	/*
915256139Sadrian	 * Again, only update ANI state if we have it.
916256139Sadrian	 */
917256139Sadrian	if (ANI_ENA(ah)) {
918256139Sadrian		aniState->cycleCount = AH5212(ah)->ah_cycleCount;
919256139Sadrian		aniState->rxFrameCount = AH5212(ah)->ah_rxBusy;
920256139Sadrian		aniState->txFrameCount = AH5212(ah)->ah_txBusy;
921256139Sadrian	}
922256139Sadrian
923185377Ssam	return listenTime;
924185377Ssam}
925185377Ssam
926185377Ssam/*
927185377Ssam * Update ani stats in preparation for listen time processing.
928185377Ssam */
929185377Ssamstatic void
930185377SsamupdateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState)
931185377Ssam{
932185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
933185377Ssam	const struct ar5212AniParams *params = aniState->params;
934185377Ssam	uint32_t phyCnt1, phyCnt2;
935185377Ssam	int32_t ofdmPhyErrCnt, cckPhyErrCnt;
936185377Ssam
937185377Ssam	HALASSERT(ahp->ah_hasHwPhyCounters);
938185377Ssam
939185377Ssam	/* Clear the mib counters and save them in the stats */
940185377Ssam	ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
941185377Ssam
942185377Ssam	/* NB: these are not reset-on-read */
943185377Ssam	phyCnt1 = OS_REG_READ(ah, AR_PHYCNT1);
944185377Ssam	phyCnt2 = OS_REG_READ(ah, AR_PHYCNT2);
945185377Ssam
946185377Ssam	/* NB: these are spec'd to never roll-over */
947185377Ssam	ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase;
948185377Ssam	if (ofdmPhyErrCnt < 0) {
949185377Ssam		HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n",
950185377Ssam		    ofdmPhyErrCnt, phyCnt1);
951185377Ssam		ofdmPhyErrCnt = AR_PHY_COUNTMAX;
952185377Ssam	}
953185377Ssam	ahp->ah_stats.ast_ani_ofdmerrs +=
954185377Ssam	     ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
955185377Ssam	aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
956185377Ssam
957185377Ssam	cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase;
958185377Ssam	if (cckPhyErrCnt < 0) {
959185377Ssam		HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n",
960185377Ssam		    cckPhyErrCnt, phyCnt2);
961185377Ssam		cckPhyErrCnt = AR_PHY_COUNTMAX;
962185377Ssam	}
963185377Ssam	ahp->ah_stats.ast_ani_cckerrs +=
964185377Ssam		cckPhyErrCnt - aniState->cckPhyErrCount;
965185377Ssam	aniState->cckPhyErrCount = cckPhyErrCnt;
966185377Ssam}
967185377Ssam
968217684Sadrianvoid
969217684Sadrianar5212RxMonitor(struct ath_hal *ah, const HAL_NODE_STATS *stats,
970217684Sadrian		const struct ieee80211_channel *chan)
971217684Sadrian{
972217684Sadrian	struct ath_hal_5212 *ahp = AH5212(ah);
973217684Sadrian	ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi;
974217684Sadrian}
975217684Sadrian
976185377Ssam/*
977185377Ssam * Do periodic processing.  This routine is called from the
978185377Ssam * driver's rx interrupt handler after processing frames.
979185377Ssam */
980185377Ssamvoid
981217684Sadrianar5212AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan)
982185377Ssam{
983185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
984185377Ssam	struct ar5212AniState *aniState = ahp->ah_curani;
985185377Ssam	const struct ar5212AniParams *params;
986185377Ssam	int32_t listenTime;
987185377Ssam
988256139Sadrian	/* Always update from the MIB, for statistics gathering */
989256139Sadrian	listenTime = ar5212AniGetListenTime(ah);
990256139Sadrian
991185377Ssam	/* XXX can aniState be null? */
992185377Ssam	if (aniState == AH_NULL)
993185377Ssam		return;
994185377Ssam	if (!ANI_ENA(ah))
995185377Ssam		return;
996185377Ssam
997185377Ssam	if (listenTime < 0) {
998185377Ssam		ahp->ah_stats.ast_ani_lneg++;
999185377Ssam		/* restart ANI period if listenTime is invalid */
1000185377Ssam		ar5212AniRestart(ah, aniState);
1001185377Ssam	}
1002185377Ssam	/* XXX beware of overflow? */
1003185377Ssam	aniState->listenTime += listenTime;
1004185377Ssam
1005185377Ssam	OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime);
1006185377Ssam
1007185377Ssam	params = aniState->params;
1008185377Ssam	if (aniState->listenTime > 5*params->period) {
1009185377Ssam		/*
1010185377Ssam		 * Check to see if need to lower immunity if
1011185377Ssam		 * 5 aniPeriods have passed
1012185377Ssam		 */
1013185377Ssam		if (ahp->ah_hasHwPhyCounters)
1014185377Ssam			updateMIBStats(ah, aniState);
1015185377Ssam		if (aniState->ofdmPhyErrCount <= aniState->listenTime *
1016185377Ssam		    params->ofdmTrigLow/1000 &&
1017185377Ssam		    aniState->cckPhyErrCount <= aniState->listenTime *
1018185377Ssam		    params->cckTrigLow/1000)
1019185377Ssam			ar5212AniLowerImmunity(ah);
1020185377Ssam		ar5212AniRestart(ah, aniState);
1021185377Ssam	} else if (aniState->listenTime > params->period) {
1022185377Ssam		if (ahp->ah_hasHwPhyCounters)
1023185377Ssam			updateMIBStats(ah, aniState);
1024185377Ssam		/* check to see if need to raise immunity */
1025185377Ssam		if (aniState->ofdmPhyErrCount > aniState->listenTime *
1026185377Ssam		    params->ofdmTrigHigh / 1000) {
1027185380Ssam			HALDEBUG(ah, HAL_DEBUG_ANI,
1028185380Ssam			    "%s: OFDM err %u listenTime %u\n", __func__,
1029185380Ssam			    aniState->ofdmPhyErrCount, aniState->listenTime);
1030185377Ssam			ar5212AniOfdmErrTrigger(ah);
1031185377Ssam			ar5212AniRestart(ah, aniState);
1032185377Ssam		} else if (aniState->cckPhyErrCount > aniState->listenTime *
1033185377Ssam			   params->cckTrigHigh / 1000) {
1034185380Ssam			HALDEBUG(ah, HAL_DEBUG_ANI,
1035185380Ssam			    "%s: CCK err %u listenTime %u\n", __func__,
1036185380Ssam			    aniState->cckPhyErrCount, aniState->listenTime);
1037185377Ssam			ar5212AniCckErrTrigger(ah);
1038185377Ssam			ar5212AniRestart(ah, aniState);
1039185377Ssam		}
1040185377Ssam	}
1041185377Ssam}
1042