ar5416_ani.c revision 219862
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: head/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c 219862 2011-03-22 07:19:49Z adrian $
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
70static void
71enableAniMIBCounters(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
91static void
92disableAniMIBCounters(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
105static void
106setPhyErrBase(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 */
130void
131ar5416AniAttach(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 */
160void
161ar5416AniDetach(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 */
170HAL_BOOL
171ar5416AniControl(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
341static void
342ar5416AniOfdmErrTrigger(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
428static void
429ar5416AniCckErrTrigger(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
474static void
475ar5416AniRestart(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_2, 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 */
506void
507ar5416AniReset(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 */
586void
587ar5416ProcessMibIntr(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
657static void
658ar5416AniLowerImmunity(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 */
730static int32_t
731ar5416AniGetListenTime(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 */
766static void
767updateMIBStats(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
803void
804ar5416RxMonitor(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 */
815void
816ar5416AniPoll(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                        HALDEBUG(ah, HAL_DEBUG_ANI,
859                            "%s: OFDM err %u listenTime %u\n", __func__,
860                            aniState->ofdmPhyErrCount, aniState->listenTime);
861			ar5416AniOfdmErrTrigger(ah);
862			ar5416AniRestart(ah, aniState);
863		} else if (aniState->cckPhyErrCount > aniState->listenTime *
864			   params->cckTrigHigh / 1000) {
865                        HALDEBUG(ah, HAL_DEBUG_ANI,
866                            "%s: CCK err %u listenTime %u\n", __func__,
867                            aniState->ofdmPhyErrCount, aniState->listenTime);
868			ar5416AniCckErrTrigger(ah);
869			ar5416AniRestart(ah, aniState);
870		}
871	}
872}
873