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