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