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