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