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