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