2 * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
3 * Copyright (c) 2002-2008 Atheros Communications, Inc.
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.
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.
17 * $Id: ar5212_ani.c,v 1.1.1.1 2008/12/11 04:46:39 alc Exp $
22 #include "ah_internal.h"
25 #include "ar5212/ar5212.h"
26 #include "ar5212/ar5212reg.h"
27 #include "ar5212/ar5212phy.h"
30 * Anti noise immunity support. We track phy errors and react
31 * to excessive errors by adjusting the noise immunity parameters.
34 #define HAL_EP_RND(x, mul) \
35 ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
36 #define BEACON_RSSI(ahp) \
37 HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \
38 HAL_RSSI_EP_MULTIPLIER)
41 * ANI processing tunes radio parameters according to PHY errors
42 * and related information. This is done for for noise and spur
43 * immunity in all operating modes if the device indicates it's
44 * capable at attach time. In addition, when there is a reference
45 * rssi value (e.g. beacon frames from an ap in station mode)
46 * further tuning is done.
48 * ANI_ENA indicates whether any ANI processing should be done;
49 * this is specified at attach time.
51 * ANI_ENA_RSSI indicates whether rssi-based processing should
52 * done, this is enabled based on operating mode and is meaningful
53 * only if ANI_ENA is true.
55 * ANI parameters are typically controlled only by the hal. The
56 * AniControl interface however permits manual tuning through the
60 (AH5212(ah)->ah_procPhyErr & HAL_ANI_ENA)
61 #define ANI_ENA_RSSI(ah) \
62 (AH5212(ah)->ah_procPhyErr & HAL_RSSI_ANI_ENA)
64 #define ah_mibStats ah_stats.ast_mibstats
67 enableAniMIBCounters(struct ath_hal
*ah
, const struct ar5212AniParams
*params
)
69 struct ath_hal_5212
*ahp
= AH5212(ah
);
71 HALDEBUG(ah
, HAL_DEBUG_ANI
, "%s: Enable mib counters: "
72 "OfdmPhyErrBase 0x%x cckPhyErrBase 0x%x\n",
73 __func__
, params
->ofdmPhyErrBase
, params
->cckPhyErrBase
);
75 OS_REG_WRITE(ah
, AR_FILTOFDM
, 0);
76 OS_REG_WRITE(ah
, AR_FILTCCK
, 0);
78 OS_REG_WRITE(ah
, AR_PHYCNT1
, params
->ofdmPhyErrBase
);
79 OS_REG_WRITE(ah
, AR_PHYCNT2
, params
->cckPhyErrBase
);
80 OS_REG_WRITE(ah
, AR_PHYCNTMASK1
, AR_PHY_ERR_OFDM_TIMING
);
81 OS_REG_WRITE(ah
, AR_PHYCNTMASK2
, AR_PHY_ERR_CCK_TIMING
);
83 ar5212UpdateMibCounters(ah
, &ahp
->ah_mibStats
); /* save+clear counters*/
84 ar5212EnableMibCounters(ah
); /* enable everything */
88 disableAniMIBCounters(struct ath_hal
*ah
)
90 struct ath_hal_5212
*ahp
= AH5212(ah
);
92 HALDEBUG(ah
, HAL_DEBUG_ANI
, "Disable MIB counters\n");
94 ar5212UpdateMibCounters(ah
, &ahp
->ah_mibStats
); /* save stats */
95 ar5212DisableMibCounters(ah
); /* disable everything */
97 OS_REG_WRITE(ah
, AR_PHYCNTMASK1
, 0);
98 OS_REG_WRITE(ah
, AR_PHYCNTMASK2
, 0);
102 * This routine returns the index into the aniState array that
103 * corresponds to the channel in *chan. If no match is found and the
104 * array is still not fully utilized, a new entry is created for the
105 * channel. We assume the attach function has already initialized the
106 * ah_ani values and only the channel field needs to be set.
109 ar5212GetAniChannelIndex(struct ath_hal
*ah
, HAL_CHANNEL_INTERNAL
*chan
)
111 #define N(a) (sizeof(a) / sizeof(a[0]))
112 struct ath_hal_5212
*ahp
= AH5212(ah
);
115 for (i
= 0; i
< N(ahp
->ah_ani
); i
++) {
116 struct ar5212AniState
*asp
= &ahp
->ah_ani
[i
];
117 if (asp
->c
.channel
== chan
->channel
)
119 if (asp
->c
.channel
== 0) {
120 asp
->c
.channel
= chan
->channel
;
121 asp
->c
.channelFlags
= chan
->channelFlags
;
122 asp
->c
.privFlags
= chan
->privFlags
;
123 asp
->isSetup
= AH_FALSE
;
124 if (IS_CHAN_2GHZ(chan
))
125 asp
->params
= &ahp
->ah_aniParams24
;
127 asp
->params
= &ahp
->ah_aniParams5
;
132 HALDEBUG(ah
, HAL_DEBUG_ANY
,
133 "No more channel states left. Using channel 0\n");
134 return 0; /* XXX gotta return something valid */
139 * Return the current ANI state of the channel we're on
141 struct ar5212AniState
*
142 ar5212AniGetCurrentState(struct ath_hal
*ah
)
144 return AH5212(ah
)->ah_curani
;
148 * Return the current statistics.
151 ar5212AniGetCurrentStats(struct ath_hal
*ah
)
153 struct ath_hal_5212
*ahp
= AH5212(ah
);
155 /* update mib stats so we return current data */
156 /* XXX? side-effects to doing this here? */
157 ar5212UpdateMibCounters(ah
, &ahp
->ah_mibStats
);
158 return &ahp
->ah_stats
;
162 setPhyErrBase(struct ath_hal
*ah
, struct ar5212AniParams
*params
)
164 if (params
->ofdmTrigHigh
>= AR_PHY_COUNTMAX
) {
165 HALDEBUG(ah
, HAL_DEBUG_ANY
,
166 "OFDM Trigger %d is too high for hw counters, using max\n",
167 params
->ofdmTrigHigh
);
168 params
->ofdmPhyErrBase
= 0;
170 params
->ofdmPhyErrBase
= AR_PHY_COUNTMAX
- params
->ofdmTrigHigh
;
171 if (params
->cckTrigHigh
>= AR_PHY_COUNTMAX
) {
172 HALDEBUG(ah
, HAL_DEBUG_ANY
,
173 "CCK Trigger %d is too high for hw counters, using max\n",
174 params
->cckTrigHigh
);
175 params
->cckPhyErrBase
= 0;
177 params
->cckPhyErrBase
= AR_PHY_COUNTMAX
- params
->cckTrigHigh
;
181 * Setup ANI handling. Sets all thresholds and reset the
182 * channel statistics. Note that ar5212AniReset should be
183 * called by ar5212Reset before anything else happens and
184 * that's where we force initial settings.
187 ar5212AniAttach(struct ath_hal
*ah
, const struct ar5212AniParams
*params24
,
188 const struct ar5212AniParams
*params5
, HAL_BOOL enable
)
190 struct ath_hal_5212
*ahp
= AH5212(ah
);
192 ahp
->ah_hasHwPhyCounters
=
193 AH_PRIVATE(ah
)->ah_caps
.halHwPhyCounterSupport
;
195 if (params24
!= AH_NULL
) {
196 OS_MEMCPY(&ahp
->ah_aniParams24
, params24
, sizeof(*params24
));
197 setPhyErrBase(ah
, &ahp
->ah_aniParams24
);
199 if (params5
!= AH_NULL
) {
200 OS_MEMCPY(&ahp
->ah_aniParams5
, params5
, sizeof(*params5
));
201 setPhyErrBase(ah
, &ahp
->ah_aniParams5
);
204 OS_MEMZERO(ahp
->ah_ani
, sizeof(ahp
->ah_ani
));
205 if (ahp
->ah_hasHwPhyCounters
) {
206 /* Enable MIB Counters */
207 enableAniMIBCounters(ah
, &ahp
->ah_aniParams24
/*XXX*/);
209 if (enable
) { /* Enable ani now */
210 HALASSERT(params24
!= AH_NULL
&& params5
!= AH_NULL
);
211 ahp
->ah_procPhyErr
|= HAL_ANI_ENA
;
213 ahp
->ah_procPhyErr
&= ~HAL_ANI_ENA
;
218 ar5212AniSetParams(struct ath_hal
*ah
, const struct ar5212AniParams
*params24
,
219 const struct ar5212AniParams
*params5
)
221 struct ath_hal_5212
*ahp
= AH5212(ah
);
222 HAL_BOOL ena
= (ahp
->ah_procPhyErr
& HAL_ANI_ENA
) != 0;
224 ar5212AniControl(ah
, HAL_ANI_MODE
, AH_FALSE
);
226 OS_MEMCPY(&ahp
->ah_aniParams24
, params24
, sizeof(*params24
));
227 setPhyErrBase(ah
, &ahp
->ah_aniParams24
);
228 OS_MEMCPY(&ahp
->ah_aniParams5
, params5
, sizeof(*params5
));
229 setPhyErrBase(ah
, &ahp
->ah_aniParams5
);
231 OS_MEMZERO(ahp
->ah_ani
, sizeof(ahp
->ah_ani
));
232 ar5212AniReset(ah
, AH_PRIVATE(ah
)->ah_curchan
,
233 AH_PRIVATE(ah
)->ah_opmode
, AH_FALSE
);
235 ar5212AniControl(ah
, HAL_ANI_MODE
, ena
);
241 * Cleanup any ANI state setup.
244 ar5212AniDetach(struct ath_hal
*ah
)
246 struct ath_hal_5212
*ahp
= AH5212(ah
);
248 HALDEBUG(ah
, HAL_DEBUG_ANI
, "Detaching Ani\n");
249 if (ahp
->ah_hasHwPhyCounters
)
250 disableAniMIBCounters(ah
);
254 * Control Adaptive Noise Immunity Parameters
257 ar5212AniControl(struct ath_hal
*ah
, HAL_ANI_CMD cmd
, int param
)
260 struct ath_hal_5212
*ahp
= AH5212(ah
);
261 struct ar5212AniState
*aniState
= ahp
->ah_curani
;
262 const struct ar5212AniParams
*params
= aniState
->params
;
264 OS_MARK(ah
, AH_MARK_ANI_CONTROL
, cmd
);
267 case HAL_ANI_NOISE_IMMUNITY_LEVEL
: {
270 if (level
>= params
->maxNoiseImmunityLevel
) {
271 HALDEBUG(ah
, HAL_DEBUG_ANY
,
272 "%s: level out of range (%u > %u)\n",
273 __func__
, level
, params
->maxNoiseImmunityLevel
);
277 OS_REG_RMW_FIELD(ah
, AR_PHY_DESIRED_SZ
,
278 AR_PHY_DESIRED_SZ_TOT_DES
, params
->totalSizeDesired
[level
]);
279 OS_REG_RMW_FIELD(ah
, AR_PHY_AGC_CTL1
,
280 AR_PHY_AGC_CTL1_COARSE_LOW
, params
->coarseLow
[level
]);
281 OS_REG_RMW_FIELD(ah
, AR_PHY_AGC_CTL1
,
282 AR_PHY_AGC_CTL1_COARSE_HIGH
, params
->coarseHigh
[level
]);
283 OS_REG_RMW_FIELD(ah
, AR_PHY_FIND_SIG
,
284 AR_PHY_FIND_SIG_FIRPWR
, params
->firpwr
[level
]);
286 if (level
> aniState
->noiseImmunityLevel
)
287 ahp
->ah_stats
.ast_ani_niup
++;
288 else if (level
< aniState
->noiseImmunityLevel
)
289 ahp
->ah_stats
.ast_ani_nidown
++;
290 aniState
->noiseImmunityLevel
= level
;
293 case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION
: {
294 static const TABLE m1ThreshLow
= { 127, 50 };
295 static const TABLE m2ThreshLow
= { 127, 40 };
296 static const TABLE m1Thresh
= { 127, 0x4d };
297 static const TABLE m2Thresh
= { 127, 0x40 };
298 static const TABLE m2CountThr
= { 31, 16 };
299 static const TABLE m2CountThrLow
= { 63, 48 };
300 u_int on
= param
? 1 : 0;
302 OS_REG_RMW_FIELD(ah
, AR_PHY_SFCORR_LOW
,
303 AR_PHY_SFCORR_LOW_M1_THRESH_LOW
, m1ThreshLow
[on
]);
304 OS_REG_RMW_FIELD(ah
, AR_PHY_SFCORR_LOW
,
305 AR_PHY_SFCORR_LOW_M2_THRESH_LOW
, m2ThreshLow
[on
]);
306 OS_REG_RMW_FIELD(ah
, AR_PHY_SFCORR
,
307 AR_PHY_SFCORR_M1_THRESH
, m1Thresh
[on
]);
308 OS_REG_RMW_FIELD(ah
, AR_PHY_SFCORR
,
309 AR_PHY_SFCORR_M2_THRESH
, m2Thresh
[on
]);
310 OS_REG_RMW_FIELD(ah
, AR_PHY_SFCORR
,
311 AR_PHY_SFCORR_M2COUNT_THR
, m2CountThr
[on
]);
312 OS_REG_RMW_FIELD(ah
, AR_PHY_SFCORR_LOW
,
313 AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW
, m2CountThrLow
[on
]);
316 OS_REG_SET_BIT(ah
, AR_PHY_SFCORR_LOW
,
317 AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW
);
319 OS_REG_CLR_BIT(ah
, AR_PHY_SFCORR_LOW
,
320 AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW
);
323 ahp
->ah_stats
.ast_ani_ofdmon
++;
325 ahp
->ah_stats
.ast_ani_ofdmoff
++;
326 aniState
->ofdmWeakSigDetectOff
= !on
;
329 case HAL_ANI_CCK_WEAK_SIGNAL_THR
: {
330 static const TABLE weakSigThrCck
= { 8, 6 };
331 u_int high
= param
? 1 : 0;
333 OS_REG_RMW_FIELD(ah
, AR_PHY_CCK_DETECT
,
334 AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK
, weakSigThrCck
[high
]);
336 ahp
->ah_stats
.ast_ani_cckhigh
++;
338 ahp
->ah_stats
.ast_ani_ccklow
++;
339 aniState
->cckWeakSigThreshold
= high
;
342 case HAL_ANI_FIRSTEP_LEVEL
: {
345 if (level
>= params
->maxFirstepLevel
) {
346 HALDEBUG(ah
, HAL_DEBUG_ANY
,
347 "%s: level out of range (%u > %u)\n",
348 __func__
, level
, params
->maxFirstepLevel
);
351 OS_REG_RMW_FIELD(ah
, AR_PHY_FIND_SIG
,
352 AR_PHY_FIND_SIG_FIRSTEP
, params
->firstep
[level
]);
353 if (level
> aniState
->firstepLevel
)
354 ahp
->ah_stats
.ast_ani_stepup
++;
355 else if (level
< aniState
->firstepLevel
)
356 ahp
->ah_stats
.ast_ani_stepdown
++;
357 aniState
->firstepLevel
= level
;
360 case HAL_ANI_SPUR_IMMUNITY_LEVEL
: {
363 if (level
>= params
->maxSpurImmunityLevel
) {
364 HALDEBUG(ah
, HAL_DEBUG_ANY
,
365 "%s: level out of range (%u > %u)\n",
366 __func__
, level
, params
->maxSpurImmunityLevel
);
369 OS_REG_RMW_FIELD(ah
, AR_PHY_TIMING5
,
370 AR_PHY_TIMING5_CYCPWR_THR1
, params
->cycPwrThr1
[level
]);
371 if (level
> aniState
->spurImmunityLevel
)
372 ahp
->ah_stats
.ast_ani_spurup
++;
373 else if (level
< aniState
->spurImmunityLevel
)
374 ahp
->ah_stats
.ast_ani_spurdown
++;
375 aniState
->spurImmunityLevel
= level
;
378 case HAL_ANI_PRESENT
:
382 ahp
->ah_procPhyErr
&= ~HAL_ANI_ENA
;
383 /* Turn off HW counters if we have them */
385 ar5212SetRxFilter(ah
,
386 ar5212GetRxFilter(ah
) &~ HAL_RX_FILTER_PHYERR
);
387 } else { /* normal/auto mode */
388 /* don't mess with state if already enabled */
389 if (ahp
->ah_procPhyErr
& HAL_ANI_ENA
)
391 if (ahp
->ah_hasHwPhyCounters
) {
392 ar5212SetRxFilter(ah
,
393 ar5212GetRxFilter(ah
) &~ HAL_RX_FILTER_PHYERR
);
394 /* Enable MIB Counters */
395 enableAniMIBCounters(ah
,
396 ahp
->ah_curani
!= AH_NULL
?
397 ahp
->ah_curani
->params
:
398 &ahp
->ah_aniParams24
/*XXX*/);
400 ar5212SetRxFilter(ah
,
401 ar5212GetRxFilter(ah
) | HAL_RX_FILTER_PHYERR
);
403 ahp
->ah_procPhyErr
|= HAL_ANI_ENA
;
406 #ifdef AH_PRIVATE_DIAG
407 case HAL_ANI_PHYERR_RESET
:
408 ahp
->ah_stats
.ast_ani_ofdmerrs
= 0;
409 ahp
->ah_stats
.ast_ani_cckerrs
= 0;
411 #endif /* AH_PRIVATE_DIAG */
413 HALDEBUG(ah
, HAL_DEBUG_ANY
, "%s: invalid cmd %u\n",
421 ar5212AniOfdmErrTrigger(struct ath_hal
*ah
)
423 struct ath_hal_5212
*ahp
= AH5212(ah
);
424 HAL_CHANNEL_INTERNAL
*chan
= AH_PRIVATE(ah
)->ah_curchan
;
425 struct ar5212AniState
*aniState
;
426 const struct ar5212AniParams
*params
;
428 HALASSERT(chan
!= AH_NULL
);
433 aniState
= ahp
->ah_curani
;
434 params
= aniState
->params
;
435 /* First, raise noise immunity level, up to max */
436 if (aniState
->noiseImmunityLevel
+1 < params
->maxNoiseImmunityLevel
) {
437 HALDEBUG(ah
, HAL_DEBUG_ANI
, "%s: raise NI to %u\n", __func__
,
438 aniState
->noiseImmunityLevel
+ 1);
439 ar5212AniControl(ah
, HAL_ANI_NOISE_IMMUNITY_LEVEL
,
440 aniState
->noiseImmunityLevel
+ 1);
443 /* then, raise spur immunity level, up to max */
444 if (aniState
->spurImmunityLevel
+1 < params
->maxSpurImmunityLevel
) {
445 HALDEBUG(ah
, HAL_DEBUG_ANI
, "%s: raise SI to %u\n", __func__
,
446 aniState
->spurImmunityLevel
+ 1);
447 ar5212AniControl(ah
, HAL_ANI_SPUR_IMMUNITY_LEVEL
,
448 aniState
->spurImmunityLevel
+ 1);
452 if (ANI_ENA_RSSI(ah
)) {
453 int32_t rssi
= BEACON_RSSI(ahp
);
454 if (rssi
> params
->rssiThrHigh
) {
456 * Beacon rssi is high, can turn off ofdm
459 if (!aniState
->ofdmWeakSigDetectOff
) {
460 HALDEBUG(ah
, HAL_DEBUG_ANI
,
461 "%s: rssi %d OWSD off\n", __func__
, rssi
);
463 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION
,
466 HAL_ANI_SPUR_IMMUNITY_LEVEL
, 0);
470 * If weak sig detect is already off, as last resort,
471 * raise firstep level
473 if (aniState
->firstepLevel
+1 < params
->maxFirstepLevel
) {
474 HALDEBUG(ah
, HAL_DEBUG_ANI
,
475 "%s: rssi %d raise ST %u\n", __func__
, rssi
,
476 aniState
->firstepLevel
+1);
477 ar5212AniControl(ah
, HAL_ANI_FIRSTEP_LEVEL
,
478 aniState
->firstepLevel
+ 1);
481 } else if (rssi
> params
->rssiThrLow
) {
483 * Beacon rssi in mid range, need ofdm weak signal
484 * detect, but we can raise firststepLevel.
486 if (aniState
->ofdmWeakSigDetectOff
) {
487 HALDEBUG(ah
, HAL_DEBUG_ANI
,
488 "%s: rssi %d OWSD on\n", __func__
, rssi
);
490 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION
,
493 if (aniState
->firstepLevel
+1 < params
->maxFirstepLevel
) {
494 HALDEBUG(ah
, HAL_DEBUG_ANI
,
495 "%s: rssi %d raise ST %u\n", __func__
, rssi
,
496 aniState
->firstepLevel
+1);
497 ar5212AniControl(ah
, HAL_ANI_FIRSTEP_LEVEL
,
498 aniState
->firstepLevel
+ 1);
503 * Beacon rssi is low, if in 11b/g mode, turn off ofdm
504 * weak signal detection and zero firstepLevel to
505 * maximize CCK sensitivity
507 /* XXX can optimize */
508 if (IS_CHAN_B(chan
) || IS_CHAN_G(chan
)) {
509 if (!aniState
->ofdmWeakSigDetectOff
) {
510 HALDEBUG(ah
, HAL_DEBUG_ANI
,
511 "%s: rssi %d OWSD off\n",
514 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION
,
517 if (aniState
->firstepLevel
> 0) {
518 HALDEBUG(ah
, HAL_DEBUG_ANI
,
519 "%s: rssi %d zero ST (was %u)\n",
521 aniState
->firstepLevel
);
523 HAL_ANI_FIRSTEP_LEVEL
, 0);
532 ar5212AniCckErrTrigger(struct ath_hal
*ah
)
534 struct ath_hal_5212
*ahp
= AH5212(ah
);
535 HAL_CHANNEL_INTERNAL
*chan
= AH_PRIVATE(ah
)->ah_curchan
;
536 struct ar5212AniState
*aniState
;
537 const struct ar5212AniParams
*params
;
539 HALASSERT(chan
!= AH_NULL
);
544 /* first, raise noise immunity level, up to max */
545 aniState
= ahp
->ah_curani
;
546 params
= aniState
->params
;
547 if (aniState
->noiseImmunityLevel
+1 < params
->maxNoiseImmunityLevel
) {
548 HALDEBUG(ah
, HAL_DEBUG_ANI
, "%s: raise NI to %u\n", __func__
,
549 aniState
->noiseImmunityLevel
+ 1);
550 ar5212AniControl(ah
, HAL_ANI_NOISE_IMMUNITY_LEVEL
,
551 aniState
->noiseImmunityLevel
+ 1);
555 if (ANI_ENA_RSSI(ah
)) {
556 int32_t rssi
= BEACON_RSSI(ahp
);
557 if (rssi
> params
->rssiThrLow
) {
559 * Beacon signal in mid and high range,
560 * raise firstep level.
562 if (aniState
->firstepLevel
+1 < params
->maxFirstepLevel
) {
563 HALDEBUG(ah
, HAL_DEBUG_ANI
,
564 "%s: rssi %d raise ST %u\n", __func__
, rssi
,
565 aniState
->firstepLevel
+1);
566 ar5212AniControl(ah
, HAL_ANI_FIRSTEP_LEVEL
,
567 aniState
->firstepLevel
+ 1);
571 * Beacon rssi is low, zero firstep level to maximize
572 * CCK sensitivity in 11b/g mode.
574 /* XXX can optimize */
575 if (IS_CHAN_B(chan
) || IS_CHAN_G(chan
)) {
576 if (aniState
->firstepLevel
> 0) {
577 HALDEBUG(ah
, HAL_DEBUG_ANI
,
578 "%s: rssi %d zero ST (was %u)\n",
580 aniState
->firstepLevel
);
582 HAL_ANI_FIRSTEP_LEVEL
, 0);
590 ar5212AniRestart(struct ath_hal
*ah
, struct ar5212AniState
*aniState
)
592 struct ath_hal_5212
*ahp
= AH5212(ah
);
594 aniState
->listenTime
= 0;
595 if (ahp
->ah_hasHwPhyCounters
) {
596 const struct ar5212AniParams
*params
= aniState
->params
;
598 * NB: these are written on reset based on the
599 * ini so we must re-write them!
601 OS_REG_WRITE(ah
, AR_PHYCNT1
, params
->ofdmPhyErrBase
);
602 OS_REG_WRITE(ah
, AR_PHYCNT2
, params
->cckPhyErrBase
);
603 OS_REG_WRITE(ah
, AR_PHYCNTMASK1
, AR_PHY_ERR_OFDM_TIMING
);
604 OS_REG_WRITE(ah
, AR_PHYCNTMASK2
, AR_PHY_ERR_CCK_TIMING
);
606 /* Clear the mib counters and save them in the stats */
607 ar5212UpdateMibCounters(ah
, &ahp
->ah_mibStats
);
609 aniState
->ofdmPhyErrCount
= 0;
610 aniState
->cckPhyErrCount
= 0;
614 * Restore/reset the ANI parameters and reset the statistics.
615 * This routine must be called for every channel change.
617 * NOTE: This is where ah_curani is set; other ani code assumes
618 * it is setup to reflect the current channel.
621 ar5212AniReset(struct ath_hal
*ah
, HAL_CHANNEL_INTERNAL
*chan
,
622 HAL_OPMODE opmode
, int restore
)
624 struct ath_hal_5212
*ahp
= AH5212(ah
);
625 struct ar5212AniState
*aniState
;
629 index
= ar5212GetAniChannelIndex(ah
, chan
);
630 aniState
= &ahp
->ah_ani
[index
];
631 ahp
->ah_curani
= aniState
;
633 ath_hal_printf(ah
,"%s: chan %u/0x%x restore %d setup %d opmode %u\n",
634 __func__
, chan
->channel
, chan
->channelFlags
, restore
,
635 aniState
->isSetup
, opmode
);
637 HALDEBUG(ah
, HAL_DEBUG_ANI
,
638 "%s: chan %u/0x%x restore %d setup %d opmode %u\n",
639 __func__
, chan
->channel
, chan
->channelFlags
, restore
,
640 aniState
->isSetup
, opmode
);
642 OS_MARK(ah
, AH_MARK_ANI_RESET
, opmode
);
645 * Turn off PHY error frame delivery while we futz with settings.
647 rxfilter
= ar5212GetRxFilter(ah
);
648 ar5212SetRxFilter(ah
, rxfilter
&~ HAL_RX_FILTER_PHYERR
);
650 * Automatic processing is done only in station mode right now.
652 if (opmode
== HAL_M_STA
)
653 ahp
->ah_procPhyErr
|= HAL_RSSI_ANI_ENA
;
655 ahp
->ah_procPhyErr
&= ~HAL_RSSI_ANI_ENA
;
657 * Set all ani parameters. We either set them to initial
658 * values or restore the previous ones for the channel.
659 * XXX if ANI follows hardware, we don't care what mode we're
660 * XXX in, we should keep the ani parameters
662 if (restore
&& aniState
->isSetup
) {
663 ar5212AniControl(ah
, HAL_ANI_NOISE_IMMUNITY_LEVEL
,
664 aniState
->noiseImmunityLevel
);
665 ar5212AniControl(ah
, HAL_ANI_SPUR_IMMUNITY_LEVEL
,
666 aniState
->spurImmunityLevel
);
667 ar5212AniControl(ah
, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION
,
668 !aniState
->ofdmWeakSigDetectOff
);
669 ar5212AniControl(ah
, HAL_ANI_CCK_WEAK_SIGNAL_THR
,
670 aniState
->cckWeakSigThreshold
);
671 ar5212AniControl(ah
, HAL_ANI_FIRSTEP_LEVEL
,
672 aniState
->firstepLevel
);
674 ar5212AniControl(ah
, HAL_ANI_NOISE_IMMUNITY_LEVEL
, 0);
675 ar5212AniControl(ah
, HAL_ANI_SPUR_IMMUNITY_LEVEL
, 0);
676 ar5212AniControl(ah
, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION
,
678 ar5212AniControl(ah
, HAL_ANI_CCK_WEAK_SIGNAL_THR
, AH_FALSE
);
679 ar5212AniControl(ah
, HAL_ANI_FIRSTEP_LEVEL
, 0);
680 aniState
->isSetup
= AH_TRUE
;
682 ar5212AniRestart(ah
, aniState
);
684 /* restore RX filter mask */
685 ar5212SetRxFilter(ah
, rxfilter
);
689 * Process a MIB interrupt. We may potentially be invoked because
690 * any of the MIB counters overflow/trigger so don't assume we're
691 * here because a PHY error counter triggered.
694 ar5212ProcessMibIntr(struct ath_hal
*ah
, const HAL_NODE_STATS
*stats
)
696 struct ath_hal_5212
*ahp
= AH5212(ah
);
697 uint32_t phyCnt1
, phyCnt2
;
699 HALDEBUG(ah
, HAL_DEBUG_ANI
, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x "
700 "filtofdm 0x%x filtcck 0x%x\n",
701 __func__
, OS_REG_READ(ah
, AR_MIBC
),
702 OS_REG_READ(ah
, AR_PHYCNT1
), OS_REG_READ(ah
, AR_PHYCNT2
),
703 OS_REG_READ(ah
, AR_FILTOFDM
), OS_REG_READ(ah
, AR_FILTCCK
));
706 * First order of business is to clear whatever caused
707 * the interrupt so we don't keep getting interrupted.
708 * We have the usual mib counters that are reset-on-read
709 * and the additional counters that appeared starting in
710 * Hainan. We collect the mib counters and explicitly
711 * zero additional counters we are not using. Anything
712 * else is reset only if it caused the interrupt.
714 /* NB: these are not reset-on-read */
715 phyCnt1
= OS_REG_READ(ah
, AR_PHYCNT1
);
716 phyCnt2
= OS_REG_READ(ah
, AR_PHYCNT2
);
717 /* not used, always reset them in case they are the cause */
718 OS_REG_WRITE(ah
, AR_FILTOFDM
, 0);
719 OS_REG_WRITE(ah
, AR_FILTCCK
, 0);
721 /* Clear the mib counters and save them in the stats */
722 ar5212UpdateMibCounters(ah
, &ahp
->ah_mibStats
);
723 ahp
->ah_stats
.ast_nodestats
= *stats
;
726 * Check for an ani stat hitting the trigger threshold.
727 * When this happens we get a MIB interrupt and the top
728 * 2 bits of the counter register will be 0b11, hence
729 * the mask check of phyCnt?.
731 if (((phyCnt1
& AR_MIBCNT_INTRMASK
) == AR_MIBCNT_INTRMASK
) ||
732 ((phyCnt2
& AR_MIBCNT_INTRMASK
) == AR_MIBCNT_INTRMASK
)) {
733 struct ar5212AniState
*aniState
= ahp
->ah_curani
;
734 const struct ar5212AniParams
*params
= aniState
->params
;
735 uint32_t ofdmPhyErrCnt
, cckPhyErrCnt
;
737 ofdmPhyErrCnt
= phyCnt1
- params
->ofdmPhyErrBase
;
738 ahp
->ah_stats
.ast_ani_ofdmerrs
+=
739 ofdmPhyErrCnt
- aniState
->ofdmPhyErrCount
;
740 aniState
->ofdmPhyErrCount
= ofdmPhyErrCnt
;
742 cckPhyErrCnt
= phyCnt2
- params
->cckPhyErrBase
;
743 ahp
->ah_stats
.ast_ani_cckerrs
+=
744 cckPhyErrCnt
- aniState
->cckPhyErrCount
;
745 aniState
->cckPhyErrCount
= cckPhyErrCnt
;
748 * NB: figure out which counter triggered. If both
749 * trigger we'll only deal with one as the processing
750 * clobbers the error counter so the trigger threshold
751 * check will never be true.
753 if (aniState
->ofdmPhyErrCount
> params
->ofdmTrigHigh
)
754 ar5212AniOfdmErrTrigger(ah
);
755 if (aniState
->cckPhyErrCount
> params
->cckTrigHigh
)
756 ar5212AniCckErrTrigger(ah
);
757 /* NB: always restart to insure the h/w counters are reset */
758 ar5212AniRestart(ah
, aniState
);
763 ar5212AniPhyErrReport(struct ath_hal
*ah
, const struct ath_rx_status
*rs
)
765 struct ath_hal_5212
*ahp
= AH5212(ah
);
766 struct ar5212AniState
*aniState
;
767 const struct ar5212AniParams
*params
;
769 HALASSERT(!ahp
->ah_hasHwPhyCounters
&& rs
!= AH_NULL
);
771 aniState
= ahp
->ah_curani
;
772 params
= aniState
->params
;
773 if (rs
->rs_phyerr
== HAL_PHYERR_OFDM_TIMING
) {
774 aniState
->ofdmPhyErrCount
++;
775 ahp
->ah_stats
.ast_ani_ofdmerrs
++;
776 if (aniState
->ofdmPhyErrCount
> params
->ofdmTrigHigh
) {
777 ar5212AniOfdmErrTrigger(ah
);
778 ar5212AniRestart(ah
, aniState
);
780 } else if (rs
->rs_phyerr
== HAL_PHYERR_CCK_TIMING
) {
781 aniState
->cckPhyErrCount
++;
782 ahp
->ah_stats
.ast_ani_cckerrs
++;
783 if (aniState
->cckPhyErrCount
> params
->cckTrigHigh
) {
784 ar5212AniCckErrTrigger(ah
);
785 ar5212AniRestart(ah
, aniState
);
791 ar5212AniLowerImmunity(struct ath_hal
*ah
)
793 struct ath_hal_5212
*ahp
= AH5212(ah
);
794 struct ar5212AniState
*aniState
;
795 const struct ar5212AniParams
*params
;
797 HALASSERT(ANI_ENA(ah
));
799 aniState
= ahp
->ah_curani
;
800 params
= aniState
->params
;
801 if (ANI_ENA_RSSI(ah
)) {
802 int32_t rssi
= BEACON_RSSI(ahp
);
803 if (rssi
> params
->rssiThrHigh
) {
805 * Beacon signal is high, leave ofdm weak signal
806 * detection off or it may oscillate. Let it fall
809 } else if (rssi
> params
->rssiThrLow
) {
811 * Beacon rssi in mid range, turn on ofdm weak signal
812 * detection or lower firstep level.
814 if (aniState
->ofdmWeakSigDetectOff
) {
815 HALDEBUG(ah
, HAL_DEBUG_ANI
,
816 "%s: rssi %d OWSD on\n", __func__
, rssi
);
818 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION
,
822 if (aniState
->firstepLevel
> 0) {
823 HALDEBUG(ah
, HAL_DEBUG_ANI
,
824 "%s: rssi %d lower ST %u\n", __func__
, rssi
,
825 aniState
->firstepLevel
-1);
826 ar5212AniControl(ah
, HAL_ANI_FIRSTEP_LEVEL
,
827 aniState
->firstepLevel
- 1);
832 * Beacon rssi is low, reduce firstep level.
834 if (aniState
->firstepLevel
> 0) {
835 HALDEBUG(ah
, HAL_DEBUG_ANI
,
836 "%s: rssi %d lower ST %u\n", __func__
, rssi
,
837 aniState
->firstepLevel
-1);
838 ar5212AniControl(ah
, HAL_ANI_FIRSTEP_LEVEL
,
839 aniState
->firstepLevel
- 1);
844 /* then lower spur immunity level, down to zero */
845 if (aniState
->spurImmunityLevel
> 0) {
846 HALDEBUG(ah
, HAL_DEBUG_ANI
, "%s: lower SI %u\n",
847 __func__
, aniState
->spurImmunityLevel
-1);
848 ar5212AniControl(ah
, HAL_ANI_SPUR_IMMUNITY_LEVEL
,
849 aniState
->spurImmunityLevel
- 1);
853 * if all else fails, lower noise immunity level down to a min value
856 if (aniState
->noiseImmunityLevel
> 0) {
857 HALDEBUG(ah
, HAL_DEBUG_ANI
, "%s: lower NI %u\n",
858 __func__
, aniState
->noiseImmunityLevel
-1);
859 ar5212AniControl(ah
, HAL_ANI_NOISE_IMMUNITY_LEVEL
,
860 aniState
->noiseImmunityLevel
- 1);
865 #define CLOCK_RATE 44000 /* XXX use mac_usec or similar */
866 /* convert HW counter values to ms using 11g clock rate, goo9d enough
870 * Return an approximation of the time spent ``listening'' by
871 * deducting the cycles spent tx'ing and rx'ing from the total
872 * cycle count since our last call. A return value <0 indicates
873 * an invalid/inconsistent time.
876 ar5212AniGetListenTime(struct ath_hal
*ah
)
878 struct ath_hal_5212
*ahp
= AH5212(ah
);
879 struct ar5212AniState
*aniState
;
880 uint32_t txFrameCount
, rxFrameCount
, cycleCount
;
883 txFrameCount
= OS_REG_READ(ah
, AR_TFCNT
);
884 rxFrameCount
= OS_REG_READ(ah
, AR_RFCNT
);
885 cycleCount
= OS_REG_READ(ah
, AR_CCCNT
);
887 aniState
= ahp
->ah_curani
;
888 if (aniState
->cycleCount
== 0 || aniState
->cycleCount
> cycleCount
) {
890 * Cycle counter wrap (or initial call); it's not possible
891 * to accurately calculate a value because the registers
892 * right shift rather than wrap--so punt and return 0.
895 ahp
->ah_stats
.ast_ani_lzero
++;
897 int32_t ccdelta
= cycleCount
- aniState
->cycleCount
;
898 int32_t rfdelta
= rxFrameCount
- aniState
->rxFrameCount
;
899 int32_t tfdelta
= txFrameCount
- aniState
->txFrameCount
;
900 listenTime
= (ccdelta
- rfdelta
- tfdelta
) / CLOCK_RATE
;
902 aniState
->cycleCount
= cycleCount
;
903 aniState
->txFrameCount
= txFrameCount
;
904 aniState
->rxFrameCount
= rxFrameCount
;
909 * Update ani stats in preparation for listen time processing.
912 updateMIBStats(struct ath_hal
*ah
, struct ar5212AniState
*aniState
)
914 struct ath_hal_5212
*ahp
= AH5212(ah
);
915 const struct ar5212AniParams
*params
= aniState
->params
;
916 uint32_t phyCnt1
, phyCnt2
;
917 int32_t ofdmPhyErrCnt
, cckPhyErrCnt
;
919 HALASSERT(ahp
->ah_hasHwPhyCounters
);
921 /* Clear the mib counters and save them in the stats */
922 ar5212UpdateMibCounters(ah
, &ahp
->ah_mibStats
);
924 /* NB: these are not reset-on-read */
925 phyCnt1
= OS_REG_READ(ah
, AR_PHYCNT1
);
926 phyCnt2
= OS_REG_READ(ah
, AR_PHYCNT2
);
928 /* NB: these are spec'd to never roll-over */
929 ofdmPhyErrCnt
= phyCnt1
- params
->ofdmPhyErrBase
;
930 if (ofdmPhyErrCnt
< 0) {
931 HALDEBUG(ah
, HAL_DEBUG_ANI
, "OFDM phyErrCnt %d phyCnt1 0x%x\n",
932 ofdmPhyErrCnt
, phyCnt1
);
933 ofdmPhyErrCnt
= AR_PHY_COUNTMAX
;
935 ahp
->ah_stats
.ast_ani_ofdmerrs
+=
936 ofdmPhyErrCnt
- aniState
->ofdmPhyErrCount
;
937 aniState
->ofdmPhyErrCount
= ofdmPhyErrCnt
;
939 cckPhyErrCnt
= phyCnt2
- params
->cckPhyErrBase
;
940 if (cckPhyErrCnt
< 0) {
941 HALDEBUG(ah
, HAL_DEBUG_ANI
, "CCK phyErrCnt %d phyCnt2 0x%x\n",
942 cckPhyErrCnt
, phyCnt2
);
943 cckPhyErrCnt
= AR_PHY_COUNTMAX
;
945 ahp
->ah_stats
.ast_ani_cckerrs
+=
946 cckPhyErrCnt
- aniState
->cckPhyErrCount
;
947 aniState
->cckPhyErrCount
= cckPhyErrCnt
;
951 * Do periodic processing. This routine is called from the
952 * driver's rx interrupt handler after processing frames.
955 ar5212AniPoll(struct ath_hal
*ah
, const HAL_NODE_STATS
*stats
,
958 struct ath_hal_5212
*ahp
= AH5212(ah
);
959 struct ar5212AniState
*aniState
= ahp
->ah_curani
;
960 const struct ar5212AniParams
*params
;
963 ahp
->ah_stats
.ast_nodestats
.ns_avgbrssi
= stats
->ns_avgbrssi
;
965 /* XXX can aniState be null? */
966 if (aniState
== AH_NULL
)
971 listenTime
= ar5212AniGetListenTime(ah
);
972 if (listenTime
< 0) {
973 ahp
->ah_stats
.ast_ani_lneg
++;
974 /* restart ANI period if listenTime is invalid */
975 ar5212AniRestart(ah
, aniState
);
977 /* XXX beware of overflow? */
978 aniState
->listenTime
+= listenTime
;
980 OS_MARK(ah
, AH_MARK_ANI_POLL
, aniState
->listenTime
);
982 params
= aniState
->params
;
983 if (aniState
->listenTime
> 5*params
->period
) {
985 * Check to see if need to lower immunity if
986 * 5 aniPeriods have passed
988 if (ahp
->ah_hasHwPhyCounters
)
989 updateMIBStats(ah
, aniState
);
990 if (aniState
->ofdmPhyErrCount
<= aniState
->listenTime
*
991 params
->ofdmTrigLow
/1000 &&
992 aniState
->cckPhyErrCount
<= aniState
->listenTime
*
993 params
->cckTrigLow
/1000)
994 ar5212AniLowerImmunity(ah
);
995 ar5212AniRestart(ah
, aniState
);
996 } else if (aniState
->listenTime
> params
->period
) {
997 if (ahp
->ah_hasHwPhyCounters
)
998 updateMIBStats(ah
, aniState
);
999 /* check to see if need to raise immunity */
1000 if (aniState
->ofdmPhyErrCount
> aniState
->listenTime
*
1001 params
->ofdmTrigHigh
/ 1000) {
1002 HALDEBUG(ah
, HAL_DEBUG_ANI
,
1003 "%s: OFDM err %u listenTime %u\n", __func__
,
1004 aniState
->ofdmPhyErrCount
, aniState
->listenTime
);
1005 ar5212AniOfdmErrTrigger(ah
);
1006 ar5212AniRestart(ah
, aniState
);
1007 } else if (aniState
->cckPhyErrCount
> aniState
->listenTime
*
1008 params
->cckTrigHigh
/ 1000) {
1009 HALDEBUG(ah
, HAL_DEBUG_ANI
,
1010 "%s: CCK err %u listenTime %u\n", __func__
,
1011 aniState
->cckPhyErrCount
, aniState
->listenTime
);
1012 ar5212AniCckErrTrigger(ah
);
1013 ar5212AniRestart(ah
, aniState
);