ar5416_ani.c revision 217925
1/*
2 * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
3 * Copyright (c) 2002-2008 Atheros Communications, Inc.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 *
17 * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c 217925 2011-01-27 08:42:50Z adrian $
18 */
19#include "opt_ah.h"
20
21/*
22 * XXX this is virtually the same code as for 5212; we reuse
23 * storage in the 5212 state block; need to refactor.
24 */
25#include "ah.h"
26#include "ah_internal.h"
27#include "ah_desc.h"
28
29#include "ar5416/ar5416.h"
30#include "ar5416/ar5416reg.h"
31#include "ar5416/ar5416phy.h"
32
33/*
34 * Anti noise immunity support.  We track phy errors and react
35 * to excessive errors by adjusting the noise immunity parameters.
36 */
37
38#define HAL_EP_RND(x, mul) \
39	((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
40#define	BEACON_RSSI(ahp) \
41	HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \
42		HAL_RSSI_EP_MULTIPLIER)
43
44/*
45 * ANI processing tunes radio parameters according to PHY errors
46 * and related information.  This is done for for noise and spur
47 * immunity in all operating modes if the device indicates it's
48 * capable at attach time.  In addition, when there is a reference
49 * rssi value (e.g. beacon frames from an ap in station mode)
50 * further tuning is done.
51 *
52 * ANI_ENA indicates whether any ANI processing should be done;
53 * this is specified at attach time.
54 *
55 * ANI_ENA_RSSI indicates whether rssi-based processing should
56 * done, this is enabled based on operating mode and is meaningful
57 * only if ANI_ENA is true.
58 *
59 * ANI parameters are typically controlled only by the hal.  The
60 * AniControl interface however permits manual tuning through the
61 * diagnostic api.
62 */
63#define ANI_ENA(ah) \
64	(AH5212(ah)->ah_procPhyErr & HAL_ANI_ENA)
65#define ANI_ENA_RSSI(ah) \
66	(AH5212(ah)->ah_procPhyErr & HAL_RSSI_ANI_ENA)
67
68#define	ah_mibStats	ah_stats.ast_mibstats
69
70static void
71enableAniMIBCounters(struct ath_hal *ah, const struct ar5212AniParams *params)
72{
73	struct ath_hal_5212 *ahp = AH5212(ah);
74
75	HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Enable mib counters: "
76	    "OfdmPhyErrBase 0x%x cckPhyErrBase 0x%x\n",
77	    __func__, params->ofdmPhyErrBase, params->cckPhyErrBase);
78
79	OS_REG_WRITE(ah, AR_FILTOFDM, 0);
80	OS_REG_WRITE(ah, AR_FILTCCK, 0);
81
82	OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase);
83	OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase);
84	OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
85	OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
86
87	ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);	/* save+clear counters*/
88	ar5212EnableMibCounters(ah);			/* enable everything */
89}
90
91static void
92disableAniMIBCounters(struct ath_hal *ah)
93{
94	struct ath_hal_5212 *ahp = AH5212(ah);
95
96	HALDEBUG(ah, HAL_DEBUG_ANI, "Disable MIB counters\n");
97
98	ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);	/* save stats */
99	ar5212DisableMibCounters(ah);			/* disable everything */
100
101	OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, 0);
102	OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, 0);
103}
104
105static void
106setPhyErrBase(struct ath_hal *ah, struct ar5212AniParams *params)
107{
108	if (params->ofdmTrigHigh >= AR_PHY_COUNTMAX) {
109		HALDEBUG(ah, HAL_DEBUG_ANY,
110		    "OFDM Trigger %d is too high for hw counters, using max\n",
111		    params->ofdmTrigHigh);
112		params->ofdmPhyErrBase = 0;
113	} else
114		params->ofdmPhyErrBase = AR_PHY_COUNTMAX - params->ofdmTrigHigh;
115	if (params->cckTrigHigh >= AR_PHY_COUNTMAX) {
116		HALDEBUG(ah, HAL_DEBUG_ANY,
117		    "CCK Trigger %d is too high for hw counters, using max\n",
118		    params->cckTrigHigh);
119		params->cckPhyErrBase = 0;
120	} else
121		params->cckPhyErrBase = AR_PHY_COUNTMAX - params->cckTrigHigh;
122}
123
124/*
125 * Setup ANI handling.  Sets all thresholds and reset the
126 * channel statistics.  Note that ar5416AniReset should be
127 * called by ar5416Reset before anything else happens and
128 * that's where we force initial settings.
129 */
130void
131ar5416AniAttach(struct ath_hal *ah, const struct ar5212AniParams *params24,
132	const struct ar5212AniParams *params5, HAL_BOOL enable)
133{
134	struct ath_hal_5212 *ahp = AH5212(ah);
135
136	if (params24 != AH_NULL) {
137		OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24));
138		setPhyErrBase(ah, &ahp->ah_aniParams24);
139	}
140	if (params5 != AH_NULL) {
141		OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5));
142		setPhyErrBase(ah, &ahp->ah_aniParams5);
143	}
144
145	OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani));
146	/* Enable MIB Counters */
147	enableAniMIBCounters(ah, &ahp->ah_aniParams24 /*XXX*/);
148
149	if (enable) {		/* Enable ani now */
150		HALASSERT(params24 != AH_NULL && params5 != AH_NULL);
151		ahp->ah_procPhyErr |= HAL_ANI_ENA;
152	} else {
153		ahp->ah_procPhyErr &= ~HAL_ANI_ENA;
154	}
155}
156
157/*
158 * Cleanup any ANI state setup.
159 */
160void
161ar5416AniDetach(struct ath_hal *ah)
162{
163	HALDEBUG(ah, HAL_DEBUG_ANI, "Detaching Ani\n");
164	disableAniMIBCounters(ah);
165}
166
167/*
168 * Control Adaptive Noise Immunity Parameters
169 */
170HAL_BOOL
171ar5416AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param)
172{
173	typedef int TABLE[];
174	struct ath_hal_5212 *ahp = AH5212(ah);
175	struct ar5212AniState *aniState = ahp->ah_curani;
176	const struct ar5212AniParams *params = aniState->params;
177
178	OS_MARK(ah, AH_MARK_ANI_CONTROL, cmd);
179
180	switch (cmd) {
181	case HAL_ANI_NOISE_IMMUNITY_LEVEL: {
182		u_int level = param;
183
184		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_NOISE_IMMUNITY_LEVEL: set level = %d\n", __func__, level);
185		if (level >= params->maxNoiseImmunityLevel) {
186			HALDEBUG(ah, HAL_DEBUG_ANI,
187			    "%s: immunity level out of range (%u > %u)\n",
188			    __func__, level, params->maxNoiseImmunityLevel);
189			return AH_FALSE;
190		}
191
192		OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ,
193		    AR_PHY_DESIRED_SZ_TOT_DES, params->totalSizeDesired[level]);
194		OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
195		    AR_PHY_AGC_CTL1_COARSE_LOW, params->coarseLow[level]);
196		OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
197		    AR_PHY_AGC_CTL1_COARSE_HIGH, params->coarseHigh[level]);
198		OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
199		    AR_PHY_FIND_SIG_FIRPWR, params->firpwr[level]);
200
201		if (level > aniState->noiseImmunityLevel)
202			ahp->ah_stats.ast_ani_niup++;
203		else if (level < aniState->noiseImmunityLevel)
204			ahp->ah_stats.ast_ani_nidown++;
205		aniState->noiseImmunityLevel = level;
206		break;
207	}
208	case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: {
209		static const TABLE m1ThreshLow   = { 127,   50 };
210		static const TABLE m2ThreshLow   = { 127,   40 };
211		static const TABLE m1Thresh      = { 127, 0x4d };
212		static const TABLE m2Thresh      = { 127, 0x40 };
213		static const TABLE m2CountThr    = {  31,   16 };
214		static const TABLE m2CountThrLow = {  63,   48 };
215		u_int on = param ? 1 : 0;
216
217		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: %s\n", __func__, on ? "enabled" : "disabled");
218		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
219			AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1ThreshLow[on]);
220		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
221			AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2ThreshLow[on]);
222		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,
223			AR_PHY_SFCORR_M1_THRESH, m1Thresh[on]);
224		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,
225			AR_PHY_SFCORR_M2_THRESH, m2Thresh[on]);
226		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,
227			AR_PHY_SFCORR_M2COUNT_THR, m2CountThr[on]);
228		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
229			AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2CountThrLow[on]);
230
231		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
232			AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLow[on]);
233		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
234			AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLow[on]);
235		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
236			AR_PHY_SFCORR_EXT_M1_THRESH, m1Thresh[on]);
237		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
238			AR_PHY_SFCORR_EXT_M2_THRESH, m2Thresh[on]);
239
240		if (on) {
241			OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
242				AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
243		} else {
244			OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,
245				AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
246		}
247		if (on)
248			ahp->ah_stats.ast_ani_ofdmon++;
249		else
250			ahp->ah_stats.ast_ani_ofdmoff++;
251		aniState->ofdmWeakSigDetectOff = !on;
252		break;
253	}
254	case HAL_ANI_CCK_WEAK_SIGNAL_THR: {
255		static const TABLE weakSigThrCck = { 8, 6 };
256		u_int high = param ? 1 : 0;
257
258		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_CCK_WEAK_SIGNAL_THR: %s\n", __func__, high ? "high" : "low");
259		OS_REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT,
260		    AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, weakSigThrCck[high]);
261		if (high)
262			ahp->ah_stats.ast_ani_cckhigh++;
263		else
264			ahp->ah_stats.ast_ani_ccklow++;
265		aniState->cckWeakSigThreshold = high;
266		break;
267	}
268	case HAL_ANI_FIRSTEP_LEVEL: {
269		u_int level = param;
270
271		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_FIRSTEP_LEVEL: level = %d\n", __func__, level);
272		if (level >= params->maxFirstepLevel) {
273			HALDEBUG(ah, HAL_DEBUG_ANI,
274			    "%s: firstep level out of range (%u > %u)\n",
275			    __func__, level, params->maxFirstepLevel);
276			return AH_FALSE;
277		}
278		OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
279		    AR_PHY_FIND_SIG_FIRSTEP, params->firstep[level]);
280		if (level > aniState->firstepLevel)
281			ahp->ah_stats.ast_ani_stepup++;
282		else if (level < aniState->firstepLevel)
283			ahp->ah_stats.ast_ani_stepdown++;
284		aniState->firstepLevel = level;
285		break;
286	}
287	case HAL_ANI_SPUR_IMMUNITY_LEVEL: {
288		u_int level = param;
289
290		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_SPUR_IMMUNITY_LEVEL: level = %d\n", __func__, level);
291		if (level >= params->maxSpurImmunityLevel) {
292			HALDEBUG(ah, HAL_DEBUG_ANI,
293			    "%s: spur immunity level out of range (%u > %u)\n",
294			    __func__, level, params->maxSpurImmunityLevel);
295			return AH_FALSE;
296		}
297		OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5,
298		    AR_PHY_TIMING5_CYCPWR_THR1, params->cycPwrThr1[level]);
299		if (level > aniState->spurImmunityLevel)
300			ahp->ah_stats.ast_ani_spurup++;
301		else if (level < aniState->spurImmunityLevel)
302			ahp->ah_stats.ast_ani_spurdown++;
303		aniState->spurImmunityLevel = level;
304		break;
305	}
306	case HAL_ANI_PRESENT:
307		break;
308	case HAL_ANI_MODE:
309		if (param == 0) {
310			ahp->ah_procPhyErr &= ~HAL_ANI_ENA;
311			/* Turn off HW counters if we have them */
312			ar5416AniDetach(ah);
313			ar5212SetRxFilter(ah,
314				ar5212GetRxFilter(ah) &~ HAL_RX_FILTER_PHYERR);
315		} else {			/* normal/auto mode */
316			/* don't mess with state if already enabled */
317			if (ahp->ah_procPhyErr & HAL_ANI_ENA)
318				break;
319			ar5212SetRxFilter(ah,
320				ar5212GetRxFilter(ah) &~ HAL_RX_FILTER_PHYERR);
321			/* Enable MIB Counters */
322			enableAniMIBCounters(ah, ahp->ah_curani != AH_NULL ?
323			    ahp->ah_curani->params: &ahp->ah_aniParams24 /*XXX*/);
324			ahp->ah_procPhyErr |= HAL_ANI_ENA;
325		}
326		break;
327#ifdef AH_PRIVATE_DIAG
328	case HAL_ANI_PHYERR_RESET:
329		ahp->ah_stats.ast_ani_ofdmerrs = 0;
330		ahp->ah_stats.ast_ani_cckerrs = 0;
331		break;
332#endif /* AH_PRIVATE_DIAG */
333	default:
334		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: invalid cmd %u\n",
335		    __func__, cmd);
336		return AH_FALSE;
337	}
338	return AH_TRUE;
339}
340
341static void
342ar5416AniOfdmErrTrigger(struct ath_hal *ah)
343{
344	struct ath_hal_5212 *ahp = AH5212(ah);
345	const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
346	struct ar5212AniState *aniState;
347	const struct ar5212AniParams *params;
348
349	HALASSERT(chan != AH_NULL);
350
351	if (!ANI_ENA(ah))
352		return;
353
354	aniState = ahp->ah_curani;
355	params = aniState->params;
356	/* First, raise noise immunity level, up to max */
357	if (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel) {
358		ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
359				 aniState->noiseImmunityLevel + 1);
360		return;
361	}
362	/* then, raise spur immunity level, up to max */
363	if (aniState->spurImmunityLevel+1 < params->maxSpurImmunityLevel) {
364		ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
365				 aniState->spurImmunityLevel + 1);
366		return;
367	}
368
369	if (ANI_ENA_RSSI(ah)) {
370		int32_t rssi = BEACON_RSSI(ahp);
371		if (rssi > params->rssiThrHigh) {
372			/*
373			 * Beacon rssi is high, can turn off ofdm
374			 * weak sig detect.
375			 */
376			if (!aniState->ofdmWeakSigDetectOff) {
377				ar5416AniControl(ah,
378				    HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
379				    AH_FALSE);
380				ar5416AniControl(ah,
381				    HAL_ANI_SPUR_IMMUNITY_LEVEL, 0);
382				return;
383			}
384			/*
385			 * If weak sig detect is already off, as last resort,
386			 * raise firstep level
387			 */
388			if (aniState->firstepLevel+1 < params->maxFirstepLevel) {
389				ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
390						 aniState->firstepLevel + 1);
391				return;
392			}
393		} else if (rssi > params->rssiThrLow) {
394			/*
395			 * Beacon rssi in mid range, need ofdm weak signal
396			 * detect, but we can raise firststepLevel.
397			 */
398			if (aniState->ofdmWeakSigDetectOff)
399				ar5416AniControl(ah,
400				    HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
401				    AH_TRUE);
402			if (aniState->firstepLevel+1 < params->maxFirstepLevel)
403				ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
404				     aniState->firstepLevel + 1);
405			return;
406		} else {
407			/*
408			 * Beacon rssi is low, if in 11b/g mode, turn off ofdm
409			 * weak signal detection and zero firstepLevel to
410			 * maximize CCK sensitivity
411			 */
412			if (IEEE80211_IS_CHAN_CCK(chan)) {
413				if (!aniState->ofdmWeakSigDetectOff)
414					ar5416AniControl(ah,
415					    HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
416					    AH_FALSE);
417				if (aniState->firstepLevel > 0)
418					ar5416AniControl(ah,
419					     HAL_ANI_FIRSTEP_LEVEL, 0);
420				return;
421			}
422		}
423	}
424}
425
426static void
427ar5416AniCckErrTrigger(struct ath_hal *ah)
428{
429	struct ath_hal_5212 *ahp = AH5212(ah);
430	const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
431	struct ar5212AniState *aniState;
432	const struct ar5212AniParams *params;
433
434	HALASSERT(chan != AH_NULL);
435
436	if (!ANI_ENA(ah))
437		return;
438
439	/* first, raise noise immunity level, up to max */
440	aniState = ahp->ah_curani;
441	params = aniState->params;
442	if (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel) {
443		ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
444				 aniState->noiseImmunityLevel + 1);
445		return;
446	}
447
448	if (ANI_ENA_RSSI(ah)) {
449		int32_t rssi = BEACON_RSSI(ahp);
450		if (rssi >  params->rssiThrLow) {
451			/*
452			 * Beacon signal in mid and high range,
453			 * raise firstep level.
454			 */
455			if (aniState->firstepLevel+1 < params->maxFirstepLevel)
456				ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
457						 aniState->firstepLevel + 1);
458		} else {
459			/*
460			 * Beacon rssi is low, zero firstep level to maximize
461			 * CCK sensitivity in 11b/g mode.
462			 */
463			if (IEEE80211_IS_CHAN_CCK(chan)) {
464				if (aniState->firstepLevel > 0)
465					ar5416AniControl(ah,
466					    HAL_ANI_FIRSTEP_LEVEL, 0);
467			}
468		}
469	}
470}
471
472static void
473ar5416AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState)
474{
475	struct ath_hal_5212 *ahp = AH5212(ah);
476	const struct ar5212AniParams *params = aniState->params;
477
478	aniState->listenTime = 0;
479	/*
480	 * NB: these are written on reset based on the
481	 *     ini so we must re-write them!
482	 */
483	HALDEBUG(ah, HAL_DEBUG_ANI,
484	    "%s: Writing ofdmbase=%u   cckbase=%u\n", __func__,
485	    params->ofdmPhyErrBase, params->cckPhyErrBase);
486	OS_REG_WRITE(ah, AR_PHY_ERR_1, params->ofdmPhyErrBase);
487	OS_REG_WRITE(ah, AR_PHY_ERR_2, params->cckPhyErrBase);
488	OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
489	OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_CCK_TIMING);
490
491	/* Clear the mib counters and save them in the stats */
492	ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
493	aniState->ofdmPhyErrCount = 0;
494	aniState->cckPhyErrCount = 0;
495}
496
497/*
498 * Restore/reset the ANI parameters and reset the statistics.
499 * This routine must be called for every channel change.
500 *
501 * NOTE: This is where ah_curani is set; other ani code assumes
502 *       it is setup to reflect the current channel.
503 */
504void
505ar5416AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan,
506	HAL_OPMODE opmode, int restore)
507{
508	struct ath_hal_5212 *ahp = AH5212(ah);
509	HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
510	/* XXX bounds check ic_devdata */
511	struct ar5212AniState *aniState = &ahp->ah_ani[chan->ic_devdata];
512	uint32_t rxfilter;
513
514	if ((ichan->privFlags & CHANNEL_ANI_INIT) == 0) {
515		OS_MEMZERO(aniState, sizeof(*aniState));
516		if (IEEE80211_IS_CHAN_2GHZ(chan))
517			aniState->params = &ahp->ah_aniParams24;
518		else
519			aniState->params = &ahp->ah_aniParams5;
520		ichan->privFlags |= CHANNEL_ANI_INIT;
521		HALASSERT((ichan->privFlags & CHANNEL_ANI_SETUP) == 0);
522	}
523	ahp->ah_curani = aniState;
524#if 0
525	ath_hal_printf(ah,"%s: chan %u/0x%x restore %d opmode %u%s\n",
526	    __func__, chan->ic_freq, chan->ic_flags, restore, opmode,
527	    ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : "");
528#else
529	HALDEBUG(ah, HAL_DEBUG_ANI, "%s: chan %u/0x%x restore %d opmode %u%s\n",
530	    __func__, chan->ic_freq, chan->ic_flags, restore, opmode,
531	    ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : "");
532#endif
533	OS_MARK(ah, AH_MARK_ANI_RESET, opmode);
534
535	/*
536	 * Turn off PHY error frame delivery while we futz with settings.
537	 */
538	rxfilter = ar5212GetRxFilter(ah);
539	ar5212SetRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR);
540	/*
541	 * Automatic processing is done only in station mode right now.
542	 */
543	if (opmode == HAL_M_STA)
544		ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA;
545	else
546		ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA;
547	/*
548	 * Set all ani parameters.  We either set them to initial
549	 * values or restore the previous ones for the channel.
550	 * XXX if ANI follows hardware, we don't care what mode we're
551	 * XXX in, we should keep the ani parameters
552	 */
553	if (restore && (ichan->privFlags & CHANNEL_ANI_SETUP)) {
554		ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
555				 aniState->noiseImmunityLevel);
556		ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
557				 aniState->spurImmunityLevel);
558		ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
559				 !aniState->ofdmWeakSigDetectOff);
560		ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR,
561				 aniState->cckWeakSigThreshold);
562		ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
563				 aniState->firstepLevel);
564	} else {
565		ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0);
566		ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0);
567		ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
568			AH_TRUE);
569		ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE);
570		ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0);
571		ichan->privFlags |= CHANNEL_ANI_SETUP;
572	}
573	ar5416AniRestart(ah, aniState);
574
575	/* restore RX filter mask */
576	ar5212SetRxFilter(ah, rxfilter);
577}
578
579/*
580 * Process a MIB interrupt.  We may potentially be invoked because
581 * any of the MIB counters overflow/trigger so don't assume we're
582 * here because a PHY error counter triggered.
583 */
584void
585ar5416ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats)
586{
587	struct ath_hal_5212 *ahp = AH5212(ah);
588	uint32_t phyCnt1, phyCnt2;
589
590	HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x "
591	    "filtofdm 0x%x filtcck 0x%x\n",
592	    __func__, OS_REG_READ(ah, AR_MIBC),
593	    OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2),
594	    OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK));
595
596	/*
597	 * First order of business is to clear whatever caused
598	 * the interrupt so we don't keep getting interrupted.
599	 * We have the usual mib counters that are reset-on-read
600	 * and the additional counters that appeared starting in
601	 * Hainan.  We collect the mib counters and explicitly
602	 * zero additional counters we are not using.  Anything
603	 * else is reset only if it caused the interrupt.
604	 */
605	/* NB: these are not reset-on-read */
606	phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1);
607	phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2);
608	/* not used, always reset them in case they are the cause */
609	OS_REG_WRITE(ah, AR_FILTOFDM, 0);
610	OS_REG_WRITE(ah, AR_FILTCCK, 0);
611	if ((OS_REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING) == 0)
612		OS_REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);
613
614	/* Clear the mib counters and save them in the stats */
615	ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
616	ahp->ah_stats.ast_nodestats = *stats;
617
618	/*
619	 * Check for an ani stat hitting the trigger threshold.
620	 * When this happens we get a MIB interrupt and the top
621	 * 2 bits of the counter register will be 0b11, hence
622	 * the mask check of phyCnt?.
623	 */
624	if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
625	    ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
626		struct ar5212AniState *aniState = ahp->ah_curani;
627		const struct ar5212AniParams *params = aniState->params;
628		uint32_t ofdmPhyErrCnt, cckPhyErrCnt;
629
630		ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase;
631		ahp->ah_stats.ast_ani_ofdmerrs +=
632			ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
633		aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
634
635		cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase;
636		ahp->ah_stats.ast_ani_cckerrs +=
637			cckPhyErrCnt - aniState->cckPhyErrCount;
638		aniState->cckPhyErrCount = cckPhyErrCnt;
639
640		/*
641		 * NB: figure out which counter triggered.  If both
642		 * trigger we'll only deal with one as the processing
643		 * clobbers the error counter so the trigger threshold
644		 * check will never be true.
645		 */
646		if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh)
647			ar5416AniOfdmErrTrigger(ah);
648		if (aniState->cckPhyErrCount > params->cckTrigHigh)
649			ar5416AniCckErrTrigger(ah);
650		/* NB: always restart to insure the h/w counters are reset */
651		ar5416AniRestart(ah, aniState);
652	}
653}
654
655static void
656ar5416AniLowerImmunity(struct ath_hal *ah)
657{
658	struct ath_hal_5212 *ahp = AH5212(ah);
659	struct ar5212AniState *aniState;
660	const struct ar5212AniParams *params;
661
662	HALASSERT(ANI_ENA(ah));
663
664	aniState = ahp->ah_curani;
665	params = aniState->params;
666	if (ANI_ENA_RSSI(ah)) {
667		int32_t rssi = BEACON_RSSI(ahp);
668		if (rssi > params->rssiThrHigh) {
669			/*
670			 * Beacon signal is high, leave ofdm weak signal
671			 * detection off or it may oscillate.  Let it fall
672			 * through.
673			 */
674		} else if (rssi > params->rssiThrLow) {
675			/*
676			 * Beacon rssi in mid range, turn on ofdm weak signal
677			 * detection or lower firstep level.
678			 */
679			if (aniState->ofdmWeakSigDetectOff) {
680				ar5416AniControl(ah,
681				    HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
682				    AH_TRUE);
683				return;
684			}
685			if (aniState->firstepLevel > 0) {
686				ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
687						 aniState->firstepLevel - 1);
688				return;
689			}
690		} else {
691			/*
692			 * Beacon rssi is low, reduce firstep level.
693			 */
694			if (aniState->firstepLevel > 0) {
695				ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
696						 aniState->firstepLevel - 1);
697				return;
698			}
699		}
700	}
701	/* then lower spur immunity level, down to zero */
702	if (aniState->spurImmunityLevel > 0) {
703		ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
704				 aniState->spurImmunityLevel - 1);
705		return;
706	}
707	/*
708	 * if all else fails, lower noise immunity level down to a min value
709	 * zero for now
710	 */
711	if (aniState->noiseImmunityLevel > 0) {
712		ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
713				 aniState->noiseImmunityLevel - 1);
714		return;
715	}
716}
717
718#define CLOCK_RATE 44000	/* XXX use mac_usec or similar */
719/* convert HW counter values to ms using 11g clock rate, goo9d enough
720   for 11a and Turbo */
721
722/*
723 * Return an approximation of the time spent ``listening'' by
724 * deducting the cycles spent tx'ing and rx'ing from the total
725 * cycle count since our last call.  A return value <0 indicates
726 * an invalid/inconsistent time.
727 */
728static int32_t
729ar5416AniGetListenTime(struct ath_hal *ah)
730{
731	struct ath_hal_5212 *ahp = AH5212(ah);
732	struct ar5212AniState *aniState;
733	uint32_t txFrameCount, rxFrameCount, cycleCount;
734	int32_t listenTime;
735
736	txFrameCount = OS_REG_READ(ah, AR_TFCNT);
737	rxFrameCount = OS_REG_READ(ah, AR_RFCNT);
738	cycleCount = OS_REG_READ(ah, AR_CCCNT);
739
740	aniState = ahp->ah_curani;
741	if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) {
742		/*
743		 * Cycle counter wrap (or initial call); it's not possible
744		 * to accurately calculate a value because the registers
745		 * right shift rather than wrap--so punt and return 0.
746		 */
747		listenTime = 0;
748		ahp->ah_stats.ast_ani_lzero++;
749	} else {
750		int32_t ccdelta = cycleCount - aniState->cycleCount;
751		int32_t rfdelta = rxFrameCount - aniState->rxFrameCount;
752		int32_t tfdelta = txFrameCount - aniState->txFrameCount;
753		listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE;
754	}
755	aniState->cycleCount = cycleCount;
756	aniState->txFrameCount = txFrameCount;
757	aniState->rxFrameCount = rxFrameCount;
758	return listenTime;
759}
760
761/*
762 * Update ani stats in preparation for listen time processing.
763 */
764static void
765updateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState)
766{
767	struct ath_hal_5212 *ahp = AH5212(ah);
768	const struct ar5212AniParams *params = aniState->params;
769	uint32_t phyCnt1, phyCnt2;
770	int32_t ofdmPhyErrCnt, cckPhyErrCnt;
771
772	/* Clear the mib counters and save them in the stats */
773	ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
774
775	/* NB: these are not reset-on-read */
776	phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1);
777	phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2);
778
779	/* NB: these are spec'd to never roll-over */
780	ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase;
781	if (ofdmPhyErrCnt < 0) {
782		HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n",
783		    ofdmPhyErrCnt, phyCnt1);
784		ofdmPhyErrCnt = AR_PHY_COUNTMAX;
785	}
786	ahp->ah_stats.ast_ani_ofdmerrs +=
787	     ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
788	aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
789
790	cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase;
791	if (cckPhyErrCnt < 0) {
792		HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n",
793		    cckPhyErrCnt, phyCnt2);
794		cckPhyErrCnt = AR_PHY_COUNTMAX;
795	}
796	ahp->ah_stats.ast_ani_cckerrs +=
797		cckPhyErrCnt - aniState->cckPhyErrCount;
798	aniState->cckPhyErrCount = cckPhyErrCnt;
799}
800
801void
802ar5416RxMonitor(struct ath_hal *ah, const HAL_NODE_STATS *stats,
803		const struct ieee80211_channel *chan)
804{
805	struct ath_hal_5212 *ahp = AH5212(ah);
806	ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi;
807}
808
809/*
810 * Do periodic processing.  This routine is called from the
811 * driver's rx interrupt handler after processing frames.
812 */
813void
814ar5416AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan)
815{
816	struct ath_hal_5212 *ahp = AH5212(ah);
817	struct ar5212AniState *aniState = ahp->ah_curani;
818	const struct ar5212AniParams *params;
819	int32_t listenTime;
820
821	/* XXX can aniState be null? */
822	if (aniState == AH_NULL)
823		return;
824	if (!ANI_ENA(ah))
825		return;
826
827	listenTime = ar5416AniGetListenTime(ah);
828	if (listenTime < 0) {
829		ahp->ah_stats.ast_ani_lneg++;
830		/* restart ANI period if listenTime is invalid */
831		ar5416AniRestart(ah, aniState);
832	}
833	/* XXX beware of overflow? */
834	aniState->listenTime += listenTime;
835
836	OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime);
837
838	params = aniState->params;
839	if (aniState->listenTime > 5*params->period) {
840		/*
841		 * Check to see if need to lower immunity if
842		 * 5 aniPeriods have passed
843		 */
844		updateMIBStats(ah, aniState);
845		if (aniState->ofdmPhyErrCount <= aniState->listenTime *
846		    params->ofdmTrigLow/1000 &&
847		    aniState->cckPhyErrCount <= aniState->listenTime *
848		    params->cckTrigLow/1000)
849			ar5416AniLowerImmunity(ah);
850		ar5416AniRestart(ah, aniState);
851	} else if (aniState->listenTime > params->period) {
852		updateMIBStats(ah, aniState);
853		/* check to see if need to raise immunity */
854		if (aniState->ofdmPhyErrCount > aniState->listenTime *
855		    params->ofdmTrigHigh / 1000) {
856			ar5416AniOfdmErrTrigger(ah);
857			ar5416AniRestart(ah, aniState);
858		} else if (aniState->cckPhyErrCount > aniState->listenTime *
859			   params->cckTrigHigh / 1000) {
860			ar5416AniCckErrTrigger(ah);
861			ar5416AniRestart(ah, aniState);
862		}
863	}
864}
865