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