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