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: ar5416_ani.c,v 1.1.1.1 2008/12/11 04:46:47 alc Exp $
22 * XXX this is virtually the same code as for 5212; we reuse
23 * storage in the 5212 state block; need to refactor.
26 #include "ah_internal.h"
29 #include "ar5416/ar5416.h"
30 #include "ar5416/ar5416reg.h"
31 #include "ar5416/ar5416phy.h"
34 * Anti noise immunity support. We track phy errors and react
35 * to excessive errors by adjusting the noise immunity parameters.
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)
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.
52 * ANI_ENA indicates whether any ANI processing should be done;
53 * this is specified at attach time.
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.
59 * ANI parameters are typically controlled only by the hal. The
60 * AniControl interface however permits manual tuning through the
64 (AH5212(ah)->ah_procPhyErr & HAL_ANI_ENA)
65 #define ANI_ENA_RSSI(ah) \
66 (AH5212(ah)->ah_procPhyErr & HAL_RSSI_ANI_ENA)
68 #define ah_mibStats ah_stats.ast_mibstats
71 enableAniMIBCounters(struct ath_hal
*ah
, const struct ar5212AniParams
*params
)
73 struct ath_hal_5212
*ahp
= AH5212(ah
);
75 HALDEBUG(ah
, HAL_DEBUG_ANI
, "%s: Enable mib counters: "
76 "OfdmPhyErrBase 0x%x cckPhyErrBase 0x%x\n",
77 __func__
, params
->ofdmPhyErrBase
, params
->cckPhyErrBase
);
79 OS_REG_WRITE(ah
, AR_FILTOFDM
, 0);
80 OS_REG_WRITE(ah
, AR_FILTCCK
, 0);
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
);
87 ar5212UpdateMibCounters(ah
, &ahp
->ah_mibStats
); /* save+clear counters*/
88 ar5212EnableMibCounters(ah
); /* enable everything */
92 disableAniMIBCounters(struct ath_hal
*ah
)
94 struct ath_hal_5212
*ahp
= AH5212(ah
);
96 HALDEBUG(ah
, HAL_DEBUG_ANI
, "Disable MIB counters\n");
98 ar5212UpdateMibCounters(ah
, &ahp
->ah_mibStats
); /* save stats */
99 ar5212DisableMibCounters(ah
); /* disable everything */
101 OS_REG_WRITE(ah
, AR_PHY_ERR_MASK_1
, 0);
102 OS_REG_WRITE(ah
, AR_PHY_ERR_MASK_2
, 0);
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.
113 ar5416GetAniChannelIndex(struct ath_hal
*ah
, HAL_CHANNEL_INTERNAL
*chan
)
115 #define N(a) (sizeof(a) / sizeof(a[0]))
116 struct ath_hal_5212
*ahp
= AH5212(ah
);
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
)
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
;
131 asp
->params
= &ahp
->ah_aniParams5
;
136 HALDEBUG(ah
, HAL_DEBUG_ANY
,
137 "No more channel states left. Using channel 0\n");
138 return 0; /* XXX gotta return something valid */
143 setPhyErrBase(struct ath_hal
*ah
, struct ar5212AniParams
*params
)
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;
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;
158 params
->cckPhyErrBase
= AR_PHY_COUNTMAX
- params
->cckTrigHigh
;
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.
168 ar5416AniAttach(struct ath_hal
*ah
, const struct ar5212AniParams
*params24
,
169 const struct ar5212AniParams
*params5
, HAL_BOOL enable
)
171 struct ath_hal_5212
*ahp
= AH5212(ah
);
173 if (params24
!= AH_NULL
) {
174 OS_MEMCPY(&ahp
->ah_aniParams24
, params24
, sizeof(*params24
));
175 setPhyErrBase(ah
, &ahp
->ah_aniParams24
);
177 if (params5
!= AH_NULL
) {
178 OS_MEMCPY(&ahp
->ah_aniParams5
, params5
, sizeof(*params5
));
179 setPhyErrBase(ah
, &ahp
->ah_aniParams5
);
182 OS_MEMZERO(ahp
->ah_ani
, sizeof(ahp
->ah_ani
));
183 /* Enable MIB Counters */
184 enableAniMIBCounters(ah
, &ahp
->ah_aniParams24
/*XXX*/);
186 if (enable
) { /* Enable ani now */
187 HALASSERT(params24
!= AH_NULL
&& params5
!= AH_NULL
);
188 ahp
->ah_procPhyErr
|= HAL_ANI_ENA
;
190 ahp
->ah_procPhyErr
&= ~HAL_ANI_ENA
;
195 * Cleanup any ANI state setup.
198 ar5416AniDetach(struct ath_hal
*ah
)
200 HALDEBUG(ah
, HAL_DEBUG_ANI
, "Detaching Ani\n");
201 disableAniMIBCounters(ah
);
205 * Control Adaptive Noise Immunity Parameters
208 ar5416AniControl(struct ath_hal
*ah
, HAL_ANI_CMD cmd
, int param
)
211 struct ath_hal_5212
*ahp
= AH5212(ah
);
212 struct ar5212AniState
*aniState
= ahp
->ah_curani
;
213 const struct ar5212AniParams
*params
= aniState
->params
;
215 OS_MARK(ah
, AH_MARK_ANI_CONTROL
, cmd
);
218 case HAL_ANI_NOISE_IMMUNITY_LEVEL
: {
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
);
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
]);
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
;
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;
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
]);
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
]);
276 OS_REG_SET_BIT(ah
, AR_PHY_SFCORR_LOW
,
277 AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW
);
279 OS_REG_CLR_BIT(ah
, AR_PHY_SFCORR_LOW
,
280 AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW
);
283 ahp
->ah_stats
.ast_ani_ofdmon
++;
285 ahp
->ah_stats
.ast_ani_ofdmoff
++;
286 aniState
->ofdmWeakSigDetectOff
= !on
;
289 case HAL_ANI_CCK_WEAK_SIGNAL_THR
: {
290 static const TABLE weakSigThrCck
= { 8, 6 };
291 u_int high
= param
? 1 : 0;
293 OS_REG_RMW_FIELD(ah
, AR_PHY_CCK_DETECT
,
294 AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK
, weakSigThrCck
[high
]);
296 ahp
->ah_stats
.ast_ani_cckhigh
++;
298 ahp
->ah_stats
.ast_ani_ccklow
++;
299 aniState
->cckWeakSigThreshold
= high
;
302 case HAL_ANI_FIRSTEP_LEVEL
: {
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
);
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
;
320 case HAL_ANI_SPUR_IMMUNITY_LEVEL
: {
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
);
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
;
338 case HAL_ANI_PRESENT
:
342 ahp
->ah_procPhyErr
&= ~HAL_ANI_ENA
;
343 /* Turn off HW counters if we have them */
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
)
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
;
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;
364 #endif /* AH_PRIVATE_DIAG */
366 HALDEBUG(ah
, HAL_DEBUG_ANY
, "%s: invalid cmd %u\n",
374 ar5416AniOfdmErrTrigger(struct ath_hal
*ah
)
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
;
381 HALASSERT(chan
!= AH_NULL
);
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);
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);
401 if (ANI_ENA_RSSI(ah
)) {
402 int32_t rssi
= BEACON_RSSI(ahp
);
403 if (rssi
> params
->rssiThrHigh
) {
405 * Beacon rssi is high, can turn off ofdm
408 if (!aniState
->ofdmWeakSigDetectOff
) {
410 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION
,
413 HAL_ANI_SPUR_IMMUNITY_LEVEL
, 0);
417 * If weak sig detect is already off, as last resort,
418 * raise firstep level
420 if (aniState
->firstepLevel
+1 < params
->maxFirstepLevel
) {
421 ar5416AniControl(ah
, HAL_ANI_FIRSTEP_LEVEL
,
422 aniState
->firstepLevel
+ 1);
425 } else if (rssi
> params
->rssiThrLow
) {
427 * Beacon rssi in mid range, need ofdm weak signal
428 * detect, but we can raise firststepLevel.
430 if (aniState
->ofdmWeakSigDetectOff
)
432 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION
,
434 if (aniState
->firstepLevel
+1 < params
->maxFirstepLevel
)
435 ar5416AniControl(ah
, HAL_ANI_FIRSTEP_LEVEL
,
436 aniState
->firstepLevel
+ 1);
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
444 /* XXX can optimize */
445 if (IS_CHAN_B(chan
) || IS_CHAN_G(chan
)) {
446 if (!aniState
->ofdmWeakSigDetectOff
)
448 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION
,
450 if (aniState
->firstepLevel
> 0)
452 HAL_ANI_FIRSTEP_LEVEL
, 0);
460 ar5416AniCckErrTrigger(struct ath_hal
*ah
)
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
;
467 HALASSERT(chan
!= AH_NULL
);
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);
481 if (ANI_ENA_RSSI(ah
)) {
482 int32_t rssi
= BEACON_RSSI(ahp
);
483 if (rssi
> params
->rssiThrLow
) {
485 * Beacon signal in mid and high range,
486 * raise firstep level.
488 if (aniState
->firstepLevel
+1 < params
->maxFirstepLevel
)
489 ar5416AniControl(ah
, HAL_ANI_FIRSTEP_LEVEL
,
490 aniState
->firstepLevel
+ 1);
493 * Beacon rssi is low, zero firstep level to maximize
494 * CCK sensitivity in 11b/g mode.
496 /* XXX can optimize */
497 if (IS_CHAN_B(chan
) || IS_CHAN_G(chan
)) {
498 if (aniState
->firstepLevel
> 0)
500 HAL_ANI_FIRSTEP_LEVEL
, 0);
507 ar5416AniRestart(struct ath_hal
*ah
, struct ar5212AniState
*aniState
)
509 struct ath_hal_5212
*ahp
= AH5212(ah
);
510 const struct ar5212AniParams
*params
= aniState
->params
;
512 aniState
->listenTime
= 0;
514 * NB: these are written on reset based on the
515 * ini so we must re-write them!
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
);
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;
532 * Restore/reset the ANI parameters and reset the statistics.
533 * This routine must be called for every channel change.
535 * NOTE: This is where ah_curani is set; other ani code assumes
536 * it is setup to reflect the current channel.
539 ar5416AniReset(struct ath_hal
*ah
, HAL_CHANNEL_INTERNAL
*chan
,
540 HAL_OPMODE opmode
, int restore
)
542 struct ath_hal_5212
*ahp
= AH5212(ah
);
543 struct ar5212AniState
*aniState
;
547 index
= ar5416GetAniChannelIndex(ah
, chan
);
548 aniState
= &ahp
->ah_ani
[index
];
549 ahp
->ah_curani
= aniState
;
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
);
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
);
560 OS_MARK(ah
, AH_MARK_ANI_RESET
, opmode
);
563 * Turn off PHY error frame delivery while we futz with settings.
565 rxfilter
= ar5212GetRxFilter(ah
);
566 ar5212SetRxFilter(ah
, rxfilter
&~ HAL_RX_FILTER_PHYERR
);
568 * Automatic processing is done only in station mode right now.
570 if (opmode
== HAL_M_STA
)
571 ahp
->ah_procPhyErr
|= HAL_RSSI_ANI_ENA
;
573 ahp
->ah_procPhyErr
&= ~HAL_RSSI_ANI_ENA
;
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
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
);
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
,
596 ar5416AniControl(ah
, HAL_ANI_CCK_WEAK_SIGNAL_THR
, AH_FALSE
);
597 ar5416AniControl(ah
, HAL_ANI_FIRSTEP_LEVEL
, 0);
598 aniState
->isSetup
= AH_TRUE
;
600 ar5416AniRestart(ah
, aniState
);
602 /* restore RX filter mask */
603 ar5212SetRxFilter(ah
, rxfilter
);
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.
612 ar5416ProcessMibIntr(struct ath_hal
*ah
, const HAL_NODE_STATS
*stats
)
614 struct ath_hal_5212
*ahp
= AH5212(ah
);
615 uint32_t phyCnt1
, phyCnt2
;
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
));
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.
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
);
641 /* Clear the mib counters and save them in the stats */
642 ar5212UpdateMibCounters(ah
, &ahp
->ah_mibStats
);
643 ahp
->ah_stats
.ast_nodestats
= *stats
;
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?.
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
;
657 ofdmPhyErrCnt
= phyCnt1
- params
->ofdmPhyErrBase
;
658 ahp
->ah_stats
.ast_ani_ofdmerrs
+=
659 ofdmPhyErrCnt
- aniState
->ofdmPhyErrCount
;
660 aniState
->ofdmPhyErrCount
= ofdmPhyErrCnt
;
662 cckPhyErrCnt
= phyCnt2
- params
->cckPhyErrBase
;
663 ahp
->ah_stats
.ast_ani_cckerrs
+=
664 cckPhyErrCnt
- aniState
->cckPhyErrCount
;
665 aniState
->cckPhyErrCount
= cckPhyErrCnt
;
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.
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
);
683 ar5416AniLowerImmunity(struct ath_hal
*ah
)
685 struct ath_hal_5212
*ahp
= AH5212(ah
);
686 struct ar5212AniState
*aniState
;
687 const struct ar5212AniParams
*params
;
689 HALASSERT(ANI_ENA(ah
));
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
) {
697 * Beacon signal is high, leave ofdm weak signal
698 * detection off or it may oscillate. Let it fall
701 } else if (rssi
> params
->rssiThrLow
) {
703 * Beacon rssi in mid range, turn on ofdm weak signal
704 * detection or lower firstep level.
706 if (aniState
->ofdmWeakSigDetectOff
) {
708 HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION
,
712 if (aniState
->firstepLevel
> 0) {
713 ar5416AniControl(ah
, HAL_ANI_FIRSTEP_LEVEL
,
714 aniState
->firstepLevel
- 1);
719 * Beacon rssi is low, reduce firstep level.
721 if (aniState
->firstepLevel
> 0) {
722 ar5416AniControl(ah
, HAL_ANI_FIRSTEP_LEVEL
,
723 aniState
->firstepLevel
- 1);
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);
735 * if all else fails, lower noise immunity level down to a min value
738 if (aniState
->noiseImmunityLevel
> 0) {
739 ar5416AniControl(ah
, HAL_ANI_NOISE_IMMUNITY_LEVEL
,
740 aniState
->noiseImmunityLevel
- 1);
745 #define CLOCK_RATE 44000 /* XXX use mac_usec or similar */
746 /* convert HW counter values to ms using 11g clock rate, goo9d enough
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.
756 ar5416AniGetListenTime(struct ath_hal
*ah
)
758 struct ath_hal_5212
*ahp
= AH5212(ah
);
759 struct ar5212AniState
*aniState
;
760 uint32_t txFrameCount
, rxFrameCount
, cycleCount
;
763 txFrameCount
= OS_REG_READ(ah
, AR_TFCNT
);
764 rxFrameCount
= OS_REG_READ(ah
, AR_RFCNT
);
765 cycleCount
= OS_REG_READ(ah
, AR_CCCNT
);
767 aniState
= ahp
->ah_curani
;
768 if (aniState
->cycleCount
== 0 || aniState
->cycleCount
> cycleCount
) {
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.
775 ahp
->ah_stats
.ast_ani_lzero
++;
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
;
782 aniState
->cycleCount
= cycleCount
;
783 aniState
->txFrameCount
= txFrameCount
;
784 aniState
->rxFrameCount
= rxFrameCount
;
789 * Update ani stats in preparation for listen time processing.
792 updateMIBStats(struct ath_hal
*ah
, struct ar5212AniState
*aniState
)
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
;
799 /* Clear the mib counters and save them in the stats */
800 ar5212UpdateMibCounters(ah
, &ahp
->ah_mibStats
);
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
);
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
;
813 ahp
->ah_stats
.ast_ani_ofdmerrs
+=
814 ofdmPhyErrCnt
- aniState
->ofdmPhyErrCount
;
815 aniState
->ofdmPhyErrCount
= ofdmPhyErrCnt
;
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
;
823 ahp
->ah_stats
.ast_ani_cckerrs
+=
824 cckPhyErrCnt
- aniState
->cckPhyErrCount
;
825 aniState
->cckPhyErrCount
= cckPhyErrCnt
;
829 * Do periodic processing. This routine is called from the
830 * driver's rx interrupt handler after processing frames.
833 ar5416AniPoll(struct ath_hal
*ah
, const HAL_NODE_STATS
*stats
,
836 struct ath_hal_5212
*ahp
= AH5212(ah
);
837 struct ar5212AniState
*aniState
= ahp
->ah_curani
;
838 const struct ar5212AniParams
*params
;
841 ahp
->ah_stats
.ast_nodestats
.ns_avgbrssi
= stats
->ns_avgbrssi
;
843 /* XXX can aniState be null? */
844 if (aniState
== AH_NULL
)
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
);
855 /* XXX beware of overflow? */
856 aniState
->listenTime
+= listenTime
;
858 OS_MARK(ah
, AH_MARK_ANI_POLL
, aniState
->listenTime
);
860 params
= aniState
->params
;
861 if (aniState
->listenTime
> 5*params
->period
) {
863 * Check to see if need to lower immunity if
864 * 5 aniPeriods have passed
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
);