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