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