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