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