xref: /freebsd/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c (revision 884a2a699669ec61e2366e3e358342dbc94be24a)
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$
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 
70 static void
71 enableAniMIBCounters(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 
91 static void
92 disableAniMIBCounters(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 
105 static void
106 setPhyErrBase(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  */
130 void
131 ar5416AniAttach(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  */
160 void
161 ar5416AniDetach(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  */
170 HAL_BOOL
171 ar5416AniControl(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 	/* Check whether the particular function is enabled */
179 	if (((1 << cmd) & AH5416(ah)->ah_ani_function) == 0) {
180 		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: command %d disabled\n",
181 		    __func__, cmd);
182 		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: cmd %d; mask %x\n", __func__, cmd, AH5416(ah)->ah_ani_function);
183 		return AH_FALSE;
184 	}
185 
186 	OS_MARK(ah, AH_MARK_ANI_CONTROL, cmd);
187 
188 	switch (cmd) {
189 	case HAL_ANI_NOISE_IMMUNITY_LEVEL: {
190 		u_int level = param;
191 
192 		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_NOISE_IMMUNITY_LEVEL: set level = %d\n", __func__, level);
193 		if (level >= params->maxNoiseImmunityLevel) {
194 			HALDEBUG(ah, HAL_DEBUG_ANI,
195 			    "%s: immunity level out of range (%u > %u)\n",
196 			    __func__, level, params->maxNoiseImmunityLevel);
197 			return AH_FALSE;
198 		}
199 
200 		OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ,
201 		    AR_PHY_DESIRED_SZ_TOT_DES, params->totalSizeDesired[level]);
202 		OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
203 		    AR_PHY_AGC_CTL1_COARSE_LOW, params->coarseLow[level]);
204 		OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
205 		    AR_PHY_AGC_CTL1_COARSE_HIGH, params->coarseHigh[level]);
206 		OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
207 		    AR_PHY_FIND_SIG_FIRPWR, params->firpwr[level]);
208 
209 		if (level > aniState->noiseImmunityLevel)
210 			ahp->ah_stats.ast_ani_niup++;
211 		else if (level < aniState->noiseImmunityLevel)
212 			ahp->ah_stats.ast_ani_nidown++;
213 		aniState->noiseImmunityLevel = level;
214 		break;
215 	}
216 	case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: {
217 		static const TABLE m1ThreshLow   = { 127,   50 };
218 		static const TABLE m2ThreshLow   = { 127,   40 };
219 		static const TABLE m1Thresh      = { 127, 0x4d };
220 		static const TABLE m2Thresh      = { 127, 0x40 };
221 		static const TABLE m2CountThr    = {  31,   16 };
222 		static const TABLE m2CountThrLow = {  63,   48 };
223 		u_int on = param ? 1 : 0;
224 
225 		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: %s\n", __func__, on ? "enabled" : "disabled");
226 		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
227 			AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1ThreshLow[on]);
228 		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
229 			AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2ThreshLow[on]);
230 		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,
231 			AR_PHY_SFCORR_M1_THRESH, m1Thresh[on]);
232 		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,
233 			AR_PHY_SFCORR_M2_THRESH, m2Thresh[on]);
234 		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,
235 			AR_PHY_SFCORR_M2COUNT_THR, m2CountThr[on]);
236 		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
237 			AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2CountThrLow[on]);
238 
239 		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
240 			AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLow[on]);
241 		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
242 			AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLow[on]);
243 		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
244 			AR_PHY_SFCORR_EXT_M1_THRESH, m1Thresh[on]);
245 		OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
246 			AR_PHY_SFCORR_EXT_M2_THRESH, m2Thresh[on]);
247 
248 		if (on) {
249 			OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
250 				AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
251 		} else {
252 			OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,
253 				AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
254 		}
255 		if (on)
256 			ahp->ah_stats.ast_ani_ofdmon++;
257 		else
258 			ahp->ah_stats.ast_ani_ofdmoff++;
259 		aniState->ofdmWeakSigDetectOff = !on;
260 		break;
261 	}
262 	case HAL_ANI_CCK_WEAK_SIGNAL_THR: {
263 		static const TABLE weakSigThrCck = { 8, 6 };
264 		u_int high = param ? 1 : 0;
265 
266 		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_CCK_WEAK_SIGNAL_THR: %s\n", __func__, high ? "high" : "low");
267 		OS_REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT,
268 		    AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, weakSigThrCck[high]);
269 		if (high)
270 			ahp->ah_stats.ast_ani_cckhigh++;
271 		else
272 			ahp->ah_stats.ast_ani_ccklow++;
273 		aniState->cckWeakSigThreshold = high;
274 		break;
275 	}
276 	case HAL_ANI_FIRSTEP_LEVEL: {
277 		u_int level = param;
278 
279 		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_FIRSTEP_LEVEL: level = %d\n", __func__, level);
280 		if (level >= params->maxFirstepLevel) {
281 			HALDEBUG(ah, HAL_DEBUG_ANI,
282 			    "%s: firstep level out of range (%u > %u)\n",
283 			    __func__, level, params->maxFirstepLevel);
284 			return AH_FALSE;
285 		}
286 		OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
287 		    AR_PHY_FIND_SIG_FIRSTEP, params->firstep[level]);
288 		if (level > aniState->firstepLevel)
289 			ahp->ah_stats.ast_ani_stepup++;
290 		else if (level < aniState->firstepLevel)
291 			ahp->ah_stats.ast_ani_stepdown++;
292 		aniState->firstepLevel = level;
293 		break;
294 	}
295 	case HAL_ANI_SPUR_IMMUNITY_LEVEL: {
296 		u_int level = param;
297 
298 		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_SPUR_IMMUNITY_LEVEL: level = %d\n", __func__, level);
299 		if (level >= params->maxSpurImmunityLevel) {
300 			HALDEBUG(ah, HAL_DEBUG_ANI,
301 			    "%s: spur immunity level out of range (%u > %u)\n",
302 			    __func__, level, params->maxSpurImmunityLevel);
303 			return AH_FALSE;
304 		}
305 		OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5,
306 		    AR_PHY_TIMING5_CYCPWR_THR1, params->cycPwrThr1[level]);
307 
308 		/* Only set the ext channel cycpwr_thr1 field for ht/40 */
309 		if (IEEE80211_IS_CHAN_HT40(AH_PRIVATE(ah)->ah_curchan))
310 			OS_REG_RMW_FIELD(ah, AR_PHY_EXT_CCA,
311 			    AR_PHY_EXT_TIMING5_CYCPWR_THR1, params->cycPwrThr1[level]);
312 
313 		if (level > aniState->spurImmunityLevel)
314 			ahp->ah_stats.ast_ani_spurup++;
315 		else if (level < aniState->spurImmunityLevel)
316 			ahp->ah_stats.ast_ani_spurdown++;
317 		aniState->spurImmunityLevel = level;
318 		break;
319 	}
320 	case HAL_ANI_PRESENT:
321 		break;
322 	case HAL_ANI_MODE:
323 		if (param == 0) {
324 			ahp->ah_procPhyErr &= ~HAL_ANI_ENA;
325 			/* Turn off HW counters if we have them */
326 			ar5416AniDetach(ah);
327 		} else {			/* normal/auto mode */
328 			/* don't mess with state if already enabled */
329 			if (ahp->ah_procPhyErr & HAL_ANI_ENA)
330 				break;
331 			/* Enable MIB Counters */
332 			enableAniMIBCounters(ah, ahp->ah_curani != AH_NULL ?
333 			    ahp->ah_curani->params: &ahp->ah_aniParams24 /*XXX*/);
334 			ahp->ah_procPhyErr |= HAL_ANI_ENA;
335 		}
336 		break;
337 #ifdef AH_PRIVATE_DIAG
338 	case HAL_ANI_PHYERR_RESET:
339 		ahp->ah_stats.ast_ani_ofdmerrs = 0;
340 		ahp->ah_stats.ast_ani_cckerrs = 0;
341 		break;
342 #endif /* AH_PRIVATE_DIAG */
343 	default:
344 		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: invalid cmd %u\n",
345 		    __func__, cmd);
346 		return AH_FALSE;
347 	}
348 	return AH_TRUE;
349 }
350 
351 static void
352 ar5416AniOfdmErrTrigger(struct ath_hal *ah)
353 {
354 	struct ath_hal_5212 *ahp = AH5212(ah);
355 	const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
356 	struct ar5212AniState *aniState;
357 	const struct ar5212AniParams *params;
358 
359 	HALASSERT(chan != AH_NULL);
360 
361 	if (!ANI_ENA(ah))
362 		return;
363 
364 	aniState = ahp->ah_curani;
365 	params = aniState->params;
366 	/* First, raise noise immunity level, up to max */
367 	if ((AH5416(ah)->ah_ani_function & (1 << HAL_ANI_NOISE_IMMUNITY_LEVEL)) &&
368 	    (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel)) {
369 		ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
370 				 aniState->noiseImmunityLevel + 1);
371 		return;
372 	}
373 	/* then, raise spur immunity level, up to max */
374 	if ((AH5416(ah)->ah_ani_function & (1 << HAL_ANI_SPUR_IMMUNITY_LEVEL)) &&
375 	    (aniState->spurImmunityLevel+1 < params->maxSpurImmunityLevel)) {
376 		ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
377 				 aniState->spurImmunityLevel + 1);
378 		return;
379 	}
380 
381 	if (ANI_ENA_RSSI(ah)) {
382 		int32_t rssi = BEACON_RSSI(ahp);
383 		if (rssi > params->rssiThrHigh) {
384 			/*
385 			 * Beacon rssi is high, can turn off ofdm
386 			 * weak sig detect.
387 			 */
388 			if (!aniState->ofdmWeakSigDetectOff) {
389 				ar5416AniControl(ah,
390 				    HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
391 				    AH_FALSE);
392 				ar5416AniControl(ah,
393 				    HAL_ANI_SPUR_IMMUNITY_LEVEL, 0);
394 				return;
395 			}
396 			/*
397 			 * If weak sig detect is already off, as last resort,
398 			 * raise firstep level
399 			 */
400 			if (aniState->firstepLevel+1 < params->maxFirstepLevel) {
401 				ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
402 						 aniState->firstepLevel + 1);
403 				return;
404 			}
405 		} else if (rssi > params->rssiThrLow) {
406 			/*
407 			 * Beacon rssi in mid range, need ofdm weak signal
408 			 * detect, but we can raise firststepLevel.
409 			 */
410 			if (aniState->ofdmWeakSigDetectOff)
411 				ar5416AniControl(ah,
412 				    HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
413 				    AH_TRUE);
414 			if (aniState->firstepLevel+1 < params->maxFirstepLevel)
415 				ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
416 				     aniState->firstepLevel + 1);
417 			return;
418 		} else {
419 			/*
420 			 * Beacon rssi is low, if in 11b/g mode, turn off ofdm
421 			 * weak signal detection and zero firstepLevel to
422 			 * maximize CCK sensitivity
423 			 */
424 			if (IEEE80211_IS_CHAN_CCK(chan)) {
425 				if (!aniState->ofdmWeakSigDetectOff)
426 					ar5416AniControl(ah,
427 					    HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
428 					    AH_FALSE);
429 				if (aniState->firstepLevel > 0)
430 					ar5416AniControl(ah,
431 					     HAL_ANI_FIRSTEP_LEVEL, 0);
432 				return;
433 			}
434 		}
435 	}
436 }
437 
438 static void
439 ar5416AniCckErrTrigger(struct ath_hal *ah)
440 {
441 	struct ath_hal_5212 *ahp = AH5212(ah);
442 	const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
443 	struct ar5212AniState *aniState;
444 	const struct ar5212AniParams *params;
445 
446 	HALASSERT(chan != AH_NULL);
447 
448 	if (!ANI_ENA(ah))
449 		return;
450 
451 	/* first, raise noise immunity level, up to max */
452 	aniState = ahp->ah_curani;
453 	params = aniState->params;
454 	if ((AH5416(ah)->ah_ani_function & (1 << HAL_ANI_NOISE_IMMUNITY_LEVEL) &&
455 	    aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel)) {
456 		ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
457 				 aniState->noiseImmunityLevel + 1);
458 		return;
459 	}
460 
461 	if (ANI_ENA_RSSI(ah)) {
462 		int32_t rssi = BEACON_RSSI(ahp);
463 		if (rssi >  params->rssiThrLow) {
464 			/*
465 			 * Beacon signal in mid and high range,
466 			 * raise firstep level.
467 			 */
468 			if (aniState->firstepLevel+1 < params->maxFirstepLevel)
469 				ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
470 						 aniState->firstepLevel + 1);
471 		} else {
472 			/*
473 			 * Beacon rssi is low, zero firstep level to maximize
474 			 * CCK sensitivity in 11b/g mode.
475 			 */
476 			if (IEEE80211_IS_CHAN_CCK(chan)) {
477 				if (aniState->firstepLevel > 0)
478 					ar5416AniControl(ah,
479 					    HAL_ANI_FIRSTEP_LEVEL, 0);
480 			}
481 		}
482 	}
483 }
484 
485 static void
486 ar5416AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState)
487 {
488 	struct ath_hal_5212 *ahp = AH5212(ah);
489 	const struct ar5212AniParams *params = aniState->params;
490 
491 	aniState->listenTime = 0;
492 	/*
493 	 * NB: these are written on reset based on the
494 	 *     ini so we must re-write them!
495 	 */
496 	HALDEBUG(ah, HAL_DEBUG_ANI,
497 	    "%s: Writing ofdmbase=%u   cckbase=%u\n", __func__,
498 	    params->ofdmPhyErrBase, params->cckPhyErrBase);
499 	OS_REG_WRITE(ah, AR_PHY_ERR_1, params->ofdmPhyErrBase);
500 	OS_REG_WRITE(ah, AR_PHY_ERR_2, params->cckPhyErrBase);
501 	OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
502 	OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
503 
504 	/* Clear the mib counters and save them in the stats */
505 	ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
506 	aniState->ofdmPhyErrCount = 0;
507 	aniState->cckPhyErrCount = 0;
508 }
509 
510 /*
511  * Restore/reset the ANI parameters and reset the statistics.
512  * This routine must be called for every channel change.
513  *
514  * NOTE: This is where ah_curani is set; other ani code assumes
515  *       it is setup to reflect the current channel.
516  */
517 void
518 ar5416AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan,
519 	HAL_OPMODE opmode, int restore)
520 {
521 	struct ath_hal_5212 *ahp = AH5212(ah);
522 	HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
523 	/* XXX bounds check ic_devdata */
524 	struct ar5212AniState *aniState = &ahp->ah_ani[chan->ic_devdata];
525 	uint32_t rxfilter;
526 
527 	if ((ichan->privFlags & CHANNEL_ANI_INIT) == 0) {
528 		OS_MEMZERO(aniState, sizeof(*aniState));
529 		if (IEEE80211_IS_CHAN_2GHZ(chan))
530 			aniState->params = &ahp->ah_aniParams24;
531 		else
532 			aniState->params = &ahp->ah_aniParams5;
533 		ichan->privFlags |= CHANNEL_ANI_INIT;
534 		HALASSERT((ichan->privFlags & CHANNEL_ANI_SETUP) == 0);
535 	}
536 	ahp->ah_curani = aniState;
537 #if 0
538 	ath_hal_printf(ah,"%s: chan %u/0x%x restore %d opmode %u%s\n",
539 	    __func__, chan->ic_freq, chan->ic_flags, restore, opmode,
540 	    ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : "");
541 #else
542 	HALDEBUG(ah, HAL_DEBUG_ANI, "%s: chan %u/0x%x restore %d opmode %u%s\n",
543 	    __func__, chan->ic_freq, chan->ic_flags, restore, opmode,
544 	    ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : "");
545 #endif
546 	OS_MARK(ah, AH_MARK_ANI_RESET, opmode);
547 
548 	/*
549 	 * Turn off PHY error frame delivery while we futz with settings.
550 	 */
551 	rxfilter = ar5212GetRxFilter(ah);
552 	ar5212SetRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR);
553 	/*
554 	 * Automatic processing is done only in station mode right now.
555 	 */
556 	if (opmode == HAL_M_STA)
557 		ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA;
558 	else
559 		ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA;
560 	/*
561 	 * Set all ani parameters.  We either set them to initial
562 	 * values or restore the previous ones for the channel.
563 	 * XXX if ANI follows hardware, we don't care what mode we're
564 	 * XXX in, we should keep the ani parameters
565 	 */
566 	if (restore && (ichan->privFlags & CHANNEL_ANI_SETUP)) {
567 		ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
568 				 aniState->noiseImmunityLevel);
569 		ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
570 				 aniState->spurImmunityLevel);
571 		ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
572 				 !aniState->ofdmWeakSigDetectOff);
573 		ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR,
574 				 aniState->cckWeakSigThreshold);
575 		ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
576 				 aniState->firstepLevel);
577 	} else {
578 		ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0);
579 		ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0);
580 		ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
581 			AH_TRUE);
582 		ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE);
583 		ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0);
584 		ichan->privFlags |= CHANNEL_ANI_SETUP;
585 	}
586 	ar5416AniRestart(ah, aniState);
587 
588 	/* restore RX filter mask */
589 	ar5212SetRxFilter(ah, rxfilter);
590 }
591 
592 /*
593  * Process a MIB interrupt.  We may potentially be invoked because
594  * any of the MIB counters overflow/trigger so don't assume we're
595  * here because a PHY error counter triggered.
596  */
597 void
598 ar5416ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats)
599 {
600 	struct ath_hal_5212 *ahp = AH5212(ah);
601 	uint32_t phyCnt1, phyCnt2;
602 
603 	HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x "
604 	    "filtofdm 0x%x filtcck 0x%x\n",
605 	    __func__, OS_REG_READ(ah, AR_MIBC),
606 	    OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2),
607 	    OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK));
608 
609 	/*
610 	 * First order of business is to clear whatever caused
611 	 * the interrupt so we don't keep getting interrupted.
612 	 * We have the usual mib counters that are reset-on-read
613 	 * and the additional counters that appeared starting in
614 	 * Hainan.  We collect the mib counters and explicitly
615 	 * zero additional counters we are not using.  Anything
616 	 * else is reset only if it caused the interrupt.
617 	 */
618 	/* NB: these are not reset-on-read */
619 	phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1);
620 	phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2);
621 	/* not used, always reset them in case they are the cause */
622 	OS_REG_WRITE(ah, AR_FILTOFDM, 0);
623 	OS_REG_WRITE(ah, AR_FILTCCK, 0);
624 	if ((OS_REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING) == 0)
625 		OS_REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);
626 
627 	/* Clear the mib counters and save them in the stats */
628 	ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
629 	ahp->ah_stats.ast_nodestats = *stats;
630 
631 	/*
632 	 * Check for an ani stat hitting the trigger threshold.
633 	 * When this happens we get a MIB interrupt and the top
634 	 * 2 bits of the counter register will be 0b11, hence
635 	 * the mask check of phyCnt?.
636 	 */
637 	if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
638 	    ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
639 		struct ar5212AniState *aniState = ahp->ah_curani;
640 		const struct ar5212AniParams *params = aniState->params;
641 		uint32_t ofdmPhyErrCnt, cckPhyErrCnt;
642 
643 		ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase;
644 		ahp->ah_stats.ast_ani_ofdmerrs +=
645 			ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
646 		aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
647 
648 		cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase;
649 		ahp->ah_stats.ast_ani_cckerrs +=
650 			cckPhyErrCnt - aniState->cckPhyErrCount;
651 		aniState->cckPhyErrCount = cckPhyErrCnt;
652 
653 		/*
654 		 * NB: figure out which counter triggered.  If both
655 		 * trigger we'll only deal with one as the processing
656 		 * clobbers the error counter so the trigger threshold
657 		 * check will never be true.
658 		 */
659 		if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh)
660 			ar5416AniOfdmErrTrigger(ah);
661 		if (aniState->cckPhyErrCount > params->cckTrigHigh)
662 			ar5416AniCckErrTrigger(ah);
663 		/* NB: always restart to insure the h/w counters are reset */
664 		ar5416AniRestart(ah, aniState);
665 	}
666 }
667 
668 static void
669 ar5416AniLowerImmunity(struct ath_hal *ah)
670 {
671 	struct ath_hal_5212 *ahp = AH5212(ah);
672 	struct ar5212AniState *aniState;
673 	const struct ar5212AniParams *params;
674 
675 	HALASSERT(ANI_ENA(ah));
676 
677 	aniState = ahp->ah_curani;
678 	params = aniState->params;
679 	if (ANI_ENA_RSSI(ah)) {
680 		int32_t rssi = BEACON_RSSI(ahp);
681 		if (rssi > params->rssiThrHigh) {
682 			/*
683 			 * Beacon signal is high, leave ofdm weak signal
684 			 * detection off or it may oscillate.  Let it fall
685 			 * through.
686 			 */
687 		} else if (rssi > params->rssiThrLow) {
688 			/*
689 			 * Beacon rssi in mid range, turn on ofdm weak signal
690 			 * detection or lower firstep level.
691 			 */
692 			if (aniState->ofdmWeakSigDetectOff) {
693 				ar5416AniControl(ah,
694 				    HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
695 				    AH_TRUE);
696 				return;
697 			}
698 			if (aniState->firstepLevel > 0) {
699 				ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
700 						 aniState->firstepLevel - 1);
701 				return;
702 			}
703 		} else {
704 			/*
705 			 * Beacon rssi is low, reduce firstep level.
706 			 */
707 			if (aniState->firstepLevel > 0) {
708 				ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
709 						 aniState->firstepLevel - 1);
710 				return;
711 			}
712 		}
713 	}
714 	/* then lower spur immunity level, down to zero */
715 	if (aniState->spurImmunityLevel > 0) {
716 		ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
717 				 aniState->spurImmunityLevel - 1);
718 		return;
719 	}
720 	/*
721 	 * if all else fails, lower noise immunity level down to a min value
722 	 * zero for now
723 	 */
724 	if (aniState->noiseImmunityLevel > 0) {
725 		ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
726 				 aniState->noiseImmunityLevel - 1);
727 		return;
728 	}
729 }
730 
731 #define CLOCK_RATE 44000	/* XXX use mac_usec or similar */
732 /* convert HW counter values to ms using 11g clock rate, goo9d enough
733    for 11a and Turbo */
734 
735 /*
736  * Return an approximation of the time spent ``listening'' by
737  * deducting the cycles spent tx'ing and rx'ing from the total
738  * cycle count since our last call.  A return value <0 indicates
739  * an invalid/inconsistent time.
740  */
741 static int32_t
742 ar5416AniGetListenTime(struct ath_hal *ah)
743 {
744 	struct ath_hal_5212 *ahp = AH5212(ah);
745 	struct ar5212AniState *aniState;
746 	uint32_t txFrameCount, rxFrameCount, cycleCount;
747 	int32_t listenTime;
748 
749 	txFrameCount = OS_REG_READ(ah, AR_TFCNT);
750 	rxFrameCount = OS_REG_READ(ah, AR_RFCNT);
751 	cycleCount = OS_REG_READ(ah, AR_CCCNT);
752 
753 	aniState = ahp->ah_curani;
754 	if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) {
755 		/*
756 		 * Cycle counter wrap (or initial call); it's not possible
757 		 * to accurately calculate a value because the registers
758 		 * right shift rather than wrap--so punt and return 0.
759 		 */
760 		listenTime = 0;
761 		ahp->ah_stats.ast_ani_lzero++;
762 	} else {
763 		int32_t ccdelta = cycleCount - aniState->cycleCount;
764 		int32_t rfdelta = rxFrameCount - aniState->rxFrameCount;
765 		int32_t tfdelta = txFrameCount - aniState->txFrameCount;
766 		listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE;
767 	}
768 	aniState->cycleCount = cycleCount;
769 	aniState->txFrameCount = txFrameCount;
770 	aniState->rxFrameCount = rxFrameCount;
771 	return listenTime;
772 }
773 
774 /*
775  * Update ani stats in preparation for listen time processing.
776  */
777 static void
778 updateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState)
779 {
780 	struct ath_hal_5212 *ahp = AH5212(ah);
781 	const struct ar5212AniParams *params = aniState->params;
782 	uint32_t phyCnt1, phyCnt2;
783 	int32_t ofdmPhyErrCnt, cckPhyErrCnt;
784 
785 	/* Clear the mib counters and save them in the stats */
786 	ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
787 
788 	/* NB: these are not reset-on-read */
789 	phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1);
790 	phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2);
791 
792 	/* NB: these are spec'd to never roll-over */
793 	ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase;
794 	if (ofdmPhyErrCnt < 0) {
795 		HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n",
796 		    ofdmPhyErrCnt, phyCnt1);
797 		ofdmPhyErrCnt = AR_PHY_COUNTMAX;
798 	}
799 	ahp->ah_stats.ast_ani_ofdmerrs +=
800 	     ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
801 	aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
802 
803 	cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase;
804 	if (cckPhyErrCnt < 0) {
805 		HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n",
806 		    cckPhyErrCnt, phyCnt2);
807 		cckPhyErrCnt = AR_PHY_COUNTMAX;
808 	}
809 	ahp->ah_stats.ast_ani_cckerrs +=
810 		cckPhyErrCnt - aniState->cckPhyErrCount;
811 	aniState->cckPhyErrCount = cckPhyErrCnt;
812 }
813 
814 void
815 ar5416RxMonitor(struct ath_hal *ah, const HAL_NODE_STATS *stats,
816 		const struct ieee80211_channel *chan)
817 {
818 	struct ath_hal_5212 *ahp = AH5212(ah);
819 	ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi;
820 }
821 
822 /*
823  * Do periodic processing.  This routine is called from the
824  * driver's rx interrupt handler after processing frames.
825  */
826 void
827 ar5416AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan)
828 {
829 	struct ath_hal_5212 *ahp = AH5212(ah);
830 	struct ar5212AniState *aniState = ahp->ah_curani;
831 	const struct ar5212AniParams *params;
832 	int32_t listenTime;
833 
834 	/* XXX can aniState be null? */
835 	if (aniState == AH_NULL)
836 		return;
837 	if (!ANI_ENA(ah))
838 		return;
839 
840 	listenTime = ar5416AniGetListenTime(ah);
841 	if (listenTime < 0) {
842 		ahp->ah_stats.ast_ani_lneg++;
843 		/* restart ANI period if listenTime is invalid */
844 		ar5416AniRestart(ah, aniState);
845 	}
846 	/* XXX beware of overflow? */
847 	aniState->listenTime += listenTime;
848 
849 	OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime);
850 
851 	params = aniState->params;
852 	if (aniState->listenTime > 5*params->period) {
853 		/*
854 		 * Check to see if need to lower immunity if
855 		 * 5 aniPeriods have passed
856 		 */
857 		updateMIBStats(ah, aniState);
858 		if (aniState->ofdmPhyErrCount <= aniState->listenTime *
859 		    params->ofdmTrigLow/1000 &&
860 		    aniState->cckPhyErrCount <= aniState->listenTime *
861 		    params->cckTrigLow/1000)
862 			ar5416AniLowerImmunity(ah);
863 		ar5416AniRestart(ah, aniState);
864 	} else if (aniState->listenTime > params->period) {
865 		updateMIBStats(ah, aniState);
866 		/* check to see if need to raise immunity */
867 		if (aniState->ofdmPhyErrCount > aniState->listenTime *
868 		    params->ofdmTrigHigh / 1000) {
869                         HALDEBUG(ah, HAL_DEBUG_ANI,
870                             "%s: OFDM err %u listenTime %u\n", __func__,
871                             aniState->ofdmPhyErrCount, aniState->listenTime);
872 			ar5416AniOfdmErrTrigger(ah);
873 			ar5416AniRestart(ah, aniState);
874 		} else if (aniState->cckPhyErrCount > aniState->listenTime *
875 			   params->cckTrigHigh / 1000) {
876                         HALDEBUG(ah, HAL_DEBUG_ANI,
877                             "%s: CCK err %u listenTime %u\n", __func__,
878                             aniState->ofdmPhyErrCount, aniState->listenTime);
879 			ar5416AniCckErrTrigger(ah);
880 			ar5416AniRestart(ah, aniState);
881 		}
882 	}
883 }
884