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