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