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.2 2011/03/07 11:25:44 cegger 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
enableAniMIBCounters(struct ath_hal * ah,const struct ar5212AniParams * params)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
disableAniMIBCounters(struct ath_hal * ah)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
ar5416GetAniChannelIndex(struct ath_hal * ah,HAL_CHANNEL_INTERNAL * chan)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
setPhyErrBase(struct ath_hal * ah,struct ar5212AniParams * params)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
ar5416AniAttach(struct ath_hal * ah,const struct ar5212AniParams * params24,const struct ar5212AniParams * params5,HAL_BOOL enable)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
ar5416AniDetach(struct ath_hal * ah)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
ar5416AniControl(struct ath_hal * ah,HAL_ANI_CMD cmd,int param)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: immunity 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: firstep 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: spur 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
ar5416AniOfdmErrTrigger(struct ath_hal * ah)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
ar5416AniCckErrTrigger(struct ath_hal * ah)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
ar5416AniRestart(struct ath_hal * ah,struct ar5212AniState * aniState)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
ar5416AniReset(struct ath_hal * ah,HAL_CHANNEL_INTERNAL * chan,HAL_OPMODE opmode,int restore)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
ar5416ProcessMibIntr(struct ath_hal * ah,const HAL_NODE_STATS * stats)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
ar5416AniLowerImmunity(struct ath_hal * ah)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
ar5416AniGetListenTime(struct ath_hal * ah)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
updateMIBStats(struct ath_hal * ah,struct ar5212AniState * aniState)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
ar5416AniPoll(struct ath_hal * ah,const HAL_NODE_STATS * stats,HAL_CHANNEL * chan)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 }
887