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