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 (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 (ah) \ |
66 | (AH5212(ah)->ah_procPhyErr & HAL_RSSI_ANI_ENA) |
67 | |
68 | #define ah_mibStats ah_stats.ast_mibstats |
69 | |
70 | static void |
71 | enableAniMIBCounters(struct ath_hal *ah, const struct ar5212AniParams *params) |
72 | { |
73 | struct ath_hal_5212 *ahp = AH5212(ah); |
74 | |
75 | HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Enable mib counters: " |
76 | "OfdmPhyErrBase 0x%x cckPhyErrBase 0x%x\n" , |
77 | __func__, params->ofdmPhyErrBase, params->cckPhyErrBase); |
78 | |
79 | OS_REG_WRITE(ah, AR_FILTOFDM, 0); |
80 | OS_REG_WRITE(ah, AR_FILTCCK, 0); |
81 | |
82 | OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase); |
83 | OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase); |
84 | OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); |
85 | OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); |
86 | |
87 | ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save+clear counters*/ |
88 | ar5212EnableMibCounters(ah); /* enable everything */ |
89 | } |
90 | |
91 | static void |
92 | disableAniMIBCounters(struct ath_hal *ah) |
93 | { |
94 | struct ath_hal_5212 *ahp = AH5212(ah); |
95 | |
96 | HALDEBUG(ah, HAL_DEBUG_ANI, "Disable MIB counters\n" ); |
97 | |
98 | ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save stats */ |
99 | ar5212DisableMibCounters(ah); /* disable everything */ |
100 | |
101 | OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, 0); |
102 | OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, 0); |
103 | } |
104 | |
105 | /* |
106 | * This routine returns the index into the aniState array that |
107 | * corresponds to the channel in *chan. If no match is found and the |
108 | * array is still not fully utilized, a new entry is created for the |
109 | * channel. We assume the attach function has already initialized the |
110 | * ah_ani values and only the channel field needs to be set. |
111 | */ |
112 | static int |
113 | ar5416GetAniChannelIndex(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan) |
114 | { |
115 | #define N(a) (sizeof(a) / sizeof(a[0])) |
116 | struct ath_hal_5212 *ahp = AH5212(ah); |
117 | int i; |
118 | |
119 | for (i = 0; i < N(ahp->ah_ani); i++) { |
120 | struct ar5212AniState *asp = &ahp->ah_ani[i]; |
121 | if (asp->c.channel == chan->channel) |
122 | return i; |
123 | if (asp->c.channel == 0) { |
124 | asp->c.channel = chan->channel; |
125 | asp->c.channelFlags = chan->channelFlags; |
126 | asp->c.privFlags = chan->privFlags; |
127 | asp->isSetup = AH_FALSE; |
128 | if (IS_CHAN_2GHZ(chan)) |
129 | asp->params = &ahp->ah_aniParams24; |
130 | else |
131 | asp->params = &ahp->ah_aniParams5; |
132 | return i; |
133 | } |
134 | } |
135 | /* XXX statistic */ |
136 | HALDEBUG(ah, HAL_DEBUG_ANY, |
137 | "No more channel states left. Using channel 0\n" ); |
138 | return 0; /* XXX gotta return something valid */ |
139 | #undef N |
140 | } |
141 | |
142 | static void |
143 | setPhyErrBase(struct ath_hal *ah, struct ar5212AniParams *params) |
144 | { |
145 | if (params->ofdmTrigHigh >= AR_PHY_COUNTMAX) { |
146 | HALDEBUG(ah, HAL_DEBUG_ANY, |
147 | "OFDM Trigger %d is too high for hw counters, using max\n" , |
148 | params->ofdmTrigHigh); |
149 | params->ofdmPhyErrBase = 0; |
150 | } else |
151 | params->ofdmPhyErrBase = AR_PHY_COUNTMAX - params->ofdmTrigHigh; |
152 | if (params->cckTrigHigh >= AR_PHY_COUNTMAX) { |
153 | HALDEBUG(ah, HAL_DEBUG_ANY, |
154 | "CCK Trigger %d is too high for hw counters, using max\n" , |
155 | params->cckTrigHigh); |
156 | params->cckPhyErrBase = 0; |
157 | } else |
158 | params->cckPhyErrBase = AR_PHY_COUNTMAX - params->cckTrigHigh; |
159 | } |
160 | |
161 | /* |
162 | * Setup ANI handling. Sets all thresholds and reset the |
163 | * channel statistics. Note that ar5416AniReset should be |
164 | * called by ar5416Reset before anything else happens and |
165 | * that's where we force initial settings. |
166 | */ |
167 | void |
168 | ar5416AniAttach(struct ath_hal *ah, const struct ar5212AniParams *params24, |
169 | const struct ar5212AniParams *params5, HAL_BOOL enable) |
170 | { |
171 | struct ath_hal_5212 *ahp = AH5212(ah); |
172 | |
173 | if (params24 != AH_NULL) { |
174 | OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24)); |
175 | setPhyErrBase(ah, &ahp->ah_aniParams24); |
176 | } |
177 | if (params5 != AH_NULL) { |
178 | OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5)); |
179 | setPhyErrBase(ah, &ahp->ah_aniParams5); |
180 | } |
181 | |
182 | OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani)); |
183 | /* Enable MIB Counters */ |
184 | enableAniMIBCounters(ah, &ahp->ah_aniParams24 /*XXX*/); |
185 | |
186 | if (enable) { /* Enable ani now */ |
187 | HALASSERT(params24 != AH_NULL && params5 != AH_NULL); |
188 | ahp->ah_procPhyErr |= HAL_ANI_ENA; |
189 | } else { |
190 | ahp->ah_procPhyErr &= ~HAL_ANI_ENA; |
191 | } |
192 | } |
193 | |
194 | /* |
195 | * Cleanup any ANI state setup. |
196 | */ |
197 | void |
198 | ar5416AniDetach(struct ath_hal *ah) |
199 | { |
200 | HALDEBUG(ah, HAL_DEBUG_ANI, "Detaching Ani\n" ); |
201 | disableAniMIBCounters(ah); |
202 | } |
203 | |
204 | /* |
205 | * Control Adaptive Noise Immunity Parameters |
206 | */ |
207 | HAL_BOOL |
208 | ar5416AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param) |
209 | { |
210 | typedef int TABLE[]; |
211 | struct ath_hal_5212 *ahp = AH5212(ah); |
212 | struct ar5212AniState *aniState = ahp->ah_curani; |
213 | const struct ar5212AniParams *params = aniState->params; |
214 | |
215 | OS_MARK(ah, AH_MARK_ANI_CONTROL, cmd); |
216 | |
217 | switch (cmd) { |
218 | case HAL_ANI_NOISE_IMMUNITY_LEVEL: { |
219 | u_int level = param; |
220 | |
221 | if (level >= params->maxNoiseImmunityLevel) { |
222 | HALDEBUG(ah, HAL_DEBUG_ANY, |
223 | "%s: 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 |
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 = BEACON_RSSI(ahp); |
403 | if (rssi > params->rssiThrHigh) { |
404 | /* |
405 | * Beacon rssi is high, can turn off ofdm |
406 | * weak sig detect. |
407 | */ |
408 | if (!aniState->ofdmWeakSigDetectOff) { |
409 | ar5416AniControl(ah, |
410 | HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, |
411 | AH_FALSE); |
412 | ar5416AniControl(ah, |
413 | HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); |
414 | return; |
415 | } |
416 | /* |
417 | * If weak sig detect is already off, as last resort, |
418 | * raise firstep level |
419 | */ |
420 | if (aniState->firstepLevel+1 < params->maxFirstepLevel) { |
421 | ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, |
422 | aniState->firstepLevel + 1); |
423 | return; |
424 | } |
425 | } else if (rssi > params->rssiThrLow) { |
426 | /* |
427 | * Beacon rssi in mid range, need ofdm weak signal |
428 | * detect, but we can raise firststepLevel. |
429 | */ |
430 | if (aniState->ofdmWeakSigDetectOff) |
431 | ar5416AniControl(ah, |
432 | HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, |
433 | AH_TRUE); |
434 | if (aniState->firstepLevel+1 < params->maxFirstepLevel) |
435 | ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, |
436 | aniState->firstepLevel + 1); |
437 | return; |
438 | } else { |
439 | /* |
440 | * Beacon rssi is low, if in 11b/g mode, turn off ofdm |
441 | * weak signal detection and zero firstepLevel to |
442 | * maximize CCK sensitivity |
443 | */ |
444 | /* XXX can optimize */ |
445 | if (IS_CHAN_B(chan) || IS_CHAN_G(chan)) { |
446 | if (!aniState->ofdmWeakSigDetectOff) |
447 | ar5416AniControl(ah, |
448 | HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, |
449 | AH_FALSE); |
450 | if (aniState->firstepLevel > 0) |
451 | ar5416AniControl(ah, |
452 | HAL_ANI_FIRSTEP_LEVEL, 0); |
453 | return; |
454 | } |
455 | } |
456 | } |
457 | } |
458 | |
459 | static void |
460 | ar5416AniCckErrTrigger(struct ath_hal *ah) |
461 | { |
462 | struct ath_hal_5212 *ahp = AH5212(ah); |
463 | HAL_CHANNEL_INTERNAL *chan = AH_PRIVATE(ah)->ah_curchan; |
464 | struct ar5212AniState *aniState; |
465 | const struct ar5212AniParams *params; |
466 | |
467 | HALASSERT(chan != AH_NULL); |
468 | |
469 | if (!ANI_ENA(ah)) |
470 | return; |
471 | |
472 | /* first, raise noise immunity level, up to max */ |
473 | aniState = ahp->ah_curani; |
474 | params = aniState->params; |
475 | if (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel) { |
476 | ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, |
477 | aniState->noiseImmunityLevel + 1); |
478 | return; |
479 | } |
480 | |
481 | if (ANI_ENA_RSSI(ah)) { |
482 | int32_t = BEACON_RSSI(ahp); |
483 | if (rssi > params->rssiThrLow) { |
484 | /* |
485 | * Beacon signal in mid and high range, |
486 | * raise firstep level. |
487 | */ |
488 | if (aniState->firstepLevel+1 < params->maxFirstepLevel) |
489 | ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, |
490 | aniState->firstepLevel + 1); |
491 | } else { |
492 | /* |
493 | * Beacon rssi is low, zero firstep level to maximize |
494 | * CCK sensitivity in 11b/g mode. |
495 | */ |
496 | /* XXX can optimize */ |
497 | if (IS_CHAN_B(chan) || IS_CHAN_G(chan)) { |
498 | if (aniState->firstepLevel > 0) |
499 | ar5416AniControl(ah, |
500 | HAL_ANI_FIRSTEP_LEVEL, 0); |
501 | } |
502 | } |
503 | } |
504 | } |
505 | |
506 | static void |
507 | ar5416AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState) |
508 | { |
509 | struct ath_hal_5212 *ahp = AH5212(ah); |
510 | const struct ar5212AniParams *params = aniState->params; |
511 | |
512 | aniState->listenTime = 0; |
513 | /* |
514 | * NB: these are written on reset based on the |
515 | * ini so we must re-write them! |
516 | */ |
517 | HALDEBUG(ah, HAL_DEBUG_ANI, |
518 | "%s: Writing ofdmbase=%u cckbase=%u\n" , __func__, |
519 | params->ofdmPhyErrBase, params->cckPhyErrBase); |
520 | OS_REG_WRITE(ah, AR_PHY_ERR_1, params->ofdmPhyErrBase); |
521 | OS_REG_WRITE(ah, AR_PHY_ERR_2, params->cckPhyErrBase); |
522 | OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); |
523 | OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_CCK_TIMING); |
524 | |
525 | /* Clear the mib counters and save them in the stats */ |
526 | ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); |
527 | aniState->ofdmPhyErrCount = 0; |
528 | aniState->cckPhyErrCount = 0; |
529 | } |
530 | |
531 | /* |
532 | * Restore/reset the ANI parameters and reset the statistics. |
533 | * This routine must be called for every channel change. |
534 | * |
535 | * NOTE: This is where ah_curani is set; other ani code assumes |
536 | * it is setup to reflect the current channel. |
537 | */ |
538 | void |
539 | ar5416AniReset(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan, |
540 | HAL_OPMODE opmode, int restore) |
541 | { |
542 | struct ath_hal_5212 *ahp = AH5212(ah); |
543 | struct ar5212AniState *aniState; |
544 | uint32_t rxfilter; |
545 | int index; |
546 | |
547 | index = ar5416GetAniChannelIndex(ah, chan); |
548 | aniState = &ahp->ah_ani[index]; |
549 | ahp->ah_curani = aniState; |
550 | #if 0 |
551 | ath_hal_printf(ah,"%s: chan %u/0x%x restore %d setup %d opmode %u\n" , |
552 | __func__, chan->channel, chan->channelFlags, restore, |
553 | aniState->isSetup, opmode); |
554 | #else |
555 | HALDEBUG(ah, HAL_DEBUG_ANI, |
556 | "%s: chan %u/0x%x restore %d setup %d opmode %u\n" , |
557 | __func__, chan->channel, chan->channelFlags, restore, |
558 | aniState->isSetup, opmode); |
559 | #endif |
560 | OS_MARK(ah, AH_MARK_ANI_RESET, opmode); |
561 | |
562 | /* |
563 | * Turn off PHY error frame delivery while we futz with settings. |
564 | */ |
565 | rxfilter = ar5212GetRxFilter(ah); |
566 | ar5212SetRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR); |
567 | /* |
568 | * Automatic processing is done only in station mode right now. |
569 | */ |
570 | if (opmode == HAL_M_STA) |
571 | ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA; |
572 | else |
573 | ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA; |
574 | /* |
575 | * Set all ani parameters. We either set them to initial |
576 | * values or restore the previous ones for the channel. |
577 | * XXX if ANI follows hardware, we don't care what mode we're |
578 | * XXX in, we should keep the ani parameters |
579 | */ |
580 | if (restore && aniState->isSetup) { |
581 | ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, |
582 | aniState->noiseImmunityLevel); |
583 | ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, |
584 | aniState->spurImmunityLevel); |
585 | ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, |
586 | !aniState->ofdmWeakSigDetectOff); |
587 | ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, |
588 | aniState->cckWeakSigThreshold); |
589 | ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, |
590 | aniState->firstepLevel); |
591 | } else { |
592 | ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0); |
593 | ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); |
594 | ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, |
595 | AH_TRUE); |
596 | ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE); |
597 | ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); |
598 | aniState->isSetup = AH_TRUE; |
599 | } |
600 | ar5416AniRestart(ah, aniState); |
601 | |
602 | /* restore RX filter mask */ |
603 | ar5212SetRxFilter(ah, rxfilter); |
604 | } |
605 | |
606 | /* |
607 | * Process a MIB interrupt. We may potentially be invoked because |
608 | * any of the MIB counters overflow/trigger so don't assume we're |
609 | * here because a PHY error counter triggered. |
610 | */ |
611 | void |
612 | ar5416ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats) |
613 | { |
614 | struct ath_hal_5212 *ahp = AH5212(ah); |
615 | uint32_t phyCnt1, phyCnt2; |
616 | |
617 | HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x " |
618 | "filtofdm 0x%x filtcck 0x%x\n" , |
619 | __func__, OS_REG_READ(ah, AR_MIBC), |
620 | OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2), |
621 | OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK)); |
622 | |
623 | /* |
624 | * First order of business is to clear whatever caused |
625 | * the interrupt so we don't keep getting interrupted. |
626 | * We have the usual mib counters that are reset-on-read |
627 | * and the additional counters that appeared starting in |
628 | * Hainan. We collect the mib counters and explicitly |
629 | * zero additional counters we are not using. Anything |
630 | * else is reset only if it caused the interrupt. |
631 | */ |
632 | /* NB: these are not reset-on-read */ |
633 | phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1); |
634 | phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2); |
635 | /* not used, always reset them in case they are the cause */ |
636 | OS_REG_WRITE(ah, AR_FILTOFDM, 0); |
637 | OS_REG_WRITE(ah, AR_FILTCCK, 0); |
638 | if ((OS_REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING) == 0) |
639 | OS_REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR); |
640 | |
641 | /* Clear the mib counters and save them in the stats */ |
642 | ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); |
643 | ahp->ah_stats.ast_nodestats = *stats; |
644 | |
645 | /* |
646 | * Check for an ani stat hitting the trigger threshold. |
647 | * When this happens we get a MIB interrupt and the top |
648 | * 2 bits of the counter register will be 0b11, hence |
649 | * the mask check of phyCnt?. |
650 | */ |
651 | if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || |
652 | ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { |
653 | struct ar5212AniState *aniState = ahp->ah_curani; |
654 | const struct ar5212AniParams *params = aniState->params; |
655 | uint32_t ofdmPhyErrCnt, cckPhyErrCnt; |
656 | |
657 | ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; |
658 | ahp->ah_stats.ast_ani_ofdmerrs += |
659 | ofdmPhyErrCnt - aniState->ofdmPhyErrCount; |
660 | aniState->ofdmPhyErrCount = ofdmPhyErrCnt; |
661 | |
662 | cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; |
663 | ahp->ah_stats.ast_ani_cckerrs += |
664 | cckPhyErrCnt - aniState->cckPhyErrCount; |
665 | aniState->cckPhyErrCount = cckPhyErrCnt; |
666 | |
667 | /* |
668 | * NB: figure out which counter triggered. If both |
669 | * trigger we'll only deal with one as the processing |
670 | * clobbers the error counter so the trigger threshold |
671 | * check will never be true. |
672 | */ |
673 | if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh) |
674 | ar5416AniOfdmErrTrigger(ah); |
675 | if (aniState->cckPhyErrCount > params->cckTrigHigh) |
676 | ar5416AniCckErrTrigger(ah); |
677 | /* NB: always restart to insure the h/w counters are reset */ |
678 | ar5416AniRestart(ah, aniState); |
679 | } |
680 | } |
681 | |
682 | static void |
683 | ar5416AniLowerImmunity(struct ath_hal *ah) |
684 | { |
685 | struct ath_hal_5212 *ahp = AH5212(ah); |
686 | struct ar5212AniState *aniState; |
687 | const struct ar5212AniParams *params; |
688 | |
689 | HALASSERT(ANI_ENA(ah)); |
690 | |
691 | aniState = ahp->ah_curani; |
692 | params = aniState->params; |
693 | if (ANI_ENA_RSSI(ah)) { |
694 | int32_t = BEACON_RSSI(ahp); |
695 | if (rssi > params->rssiThrHigh) { |
696 | /* |
697 | * Beacon signal is high, leave ofdm weak signal |
698 | * detection off or it may oscillate. Let it fall |
699 | * through. |
700 | */ |
701 | } else if (rssi > params->rssiThrLow) { |
702 | /* |
703 | * Beacon rssi in mid range, turn on ofdm weak signal |
704 | * detection or lower firstep level. |
705 | */ |
706 | if (aniState->ofdmWeakSigDetectOff) { |
707 | ar5416AniControl(ah, |
708 | HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, |
709 | AH_TRUE); |
710 | return; |
711 | } |
712 | if (aniState->firstepLevel > 0) { |
713 | ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, |
714 | aniState->firstepLevel - 1); |
715 | return; |
716 | } |
717 | } else { |
718 | /* |
719 | * Beacon rssi is low, reduce firstep level. |
720 | */ |
721 | if (aniState->firstepLevel > 0) { |
722 | ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, |
723 | aniState->firstepLevel - 1); |
724 | return; |
725 | } |
726 | } |
727 | } |
728 | /* then lower spur immunity level, down to zero */ |
729 | if (aniState->spurImmunityLevel > 0) { |
730 | ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, |
731 | aniState->spurImmunityLevel - 1); |
732 | return; |
733 | } |
734 | /* |
735 | * if all else fails, lower noise immunity level down to a min value |
736 | * zero for now |
737 | */ |
738 | if (aniState->noiseImmunityLevel > 0) { |
739 | ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, |
740 | aniState->noiseImmunityLevel - 1); |
741 | return; |
742 | } |
743 | } |
744 | |
745 | #define CLOCK_RATE 44000 /* XXX use mac_usec or similar */ |
746 | /* convert HW counter values to ms using 11g clock rate, goo9d enough |
747 | for 11a and Turbo */ |
748 | |
749 | /* |
750 | * Return an approximation of the time spent ``listening'' by |
751 | * deducting the cycles spent tx'ing and rx'ing from the total |
752 | * cycle count since our last call. A return value <0 indicates |
753 | * an invalid/inconsistent time. |
754 | */ |
755 | static int32_t |
756 | ar5416AniGetListenTime(struct ath_hal *ah) |
757 | { |
758 | struct ath_hal_5212 *ahp = AH5212(ah); |
759 | struct ar5212AniState *aniState; |
760 | uint32_t txFrameCount, rxFrameCount, cycleCount; |
761 | int32_t listenTime; |
762 | |
763 | txFrameCount = OS_REG_READ(ah, AR_TFCNT); |
764 | rxFrameCount = OS_REG_READ(ah, AR_RFCNT); |
765 | cycleCount = OS_REG_READ(ah, AR_CCCNT); |
766 | |
767 | aniState = ahp->ah_curani; |
768 | if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) { |
769 | /* |
770 | * Cycle counter wrap (or initial call); it's not possible |
771 | * to accurately calculate a value because the registers |
772 | * right shift rather than wrap--so punt and return 0. |
773 | */ |
774 | listenTime = 0; |
775 | ahp->ah_stats.ast_ani_lzero++; |
776 | } else { |
777 | int32_t ccdelta = cycleCount - aniState->cycleCount; |
778 | int32_t rfdelta = rxFrameCount - aniState->rxFrameCount; |
779 | int32_t tfdelta = txFrameCount - aniState->txFrameCount; |
780 | listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE; |
781 | } |
782 | aniState->cycleCount = cycleCount; |
783 | aniState->txFrameCount = txFrameCount; |
784 | aniState->rxFrameCount = rxFrameCount; |
785 | return listenTime; |
786 | } |
787 | |
788 | /* |
789 | * Update ani stats in preparation for listen time processing. |
790 | */ |
791 | static void |
792 | updateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState) |
793 | { |
794 | struct ath_hal_5212 *ahp = AH5212(ah); |
795 | const struct ar5212AniParams *params = aniState->params; |
796 | uint32_t phyCnt1, phyCnt2; |
797 | int32_t ofdmPhyErrCnt, cckPhyErrCnt; |
798 | |
799 | /* Clear the mib counters and save them in the stats */ |
800 | ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); |
801 | |
802 | /* NB: these are not reset-on-read */ |
803 | phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1); |
804 | phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2); |
805 | |
806 | /* NB: these are spec'd to never roll-over */ |
807 | ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; |
808 | if (ofdmPhyErrCnt < 0) { |
809 | HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n" , |
810 | ofdmPhyErrCnt, phyCnt1); |
811 | ofdmPhyErrCnt = AR_PHY_COUNTMAX; |
812 | } |
813 | ahp->ah_stats.ast_ani_ofdmerrs += |
814 | ofdmPhyErrCnt - aniState->ofdmPhyErrCount; |
815 | aniState->ofdmPhyErrCount = ofdmPhyErrCnt; |
816 | |
817 | cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; |
818 | if (cckPhyErrCnt < 0) { |
819 | HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n" , |
820 | cckPhyErrCnt, phyCnt2); |
821 | cckPhyErrCnt = AR_PHY_COUNTMAX; |
822 | } |
823 | ahp->ah_stats.ast_ani_cckerrs += |
824 | cckPhyErrCnt - aniState->cckPhyErrCount; |
825 | aniState->cckPhyErrCount = cckPhyErrCnt; |
826 | } |
827 | |
828 | /* |
829 | * Do periodic processing. This routine is called from the |
830 | * driver's rx interrupt handler after processing frames. |
831 | */ |
832 | void |
833 | ar5416AniPoll(struct ath_hal *ah, const HAL_NODE_STATS *stats, |
834 | HAL_CHANNEL *chan) |
835 | { |
836 | struct ath_hal_5212 *ahp = AH5212(ah); |
837 | struct ar5212AniState *aniState = ahp->ah_curani; |
838 | const struct ar5212AniParams *params; |
839 | int32_t listenTime; |
840 | |
841 | ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi; |
842 | |
843 | /* XXX can aniState be null? */ |
844 | if (aniState == AH_NULL) |
845 | return; |
846 | if (!ANI_ENA(ah)) |
847 | return; |
848 | |
849 | listenTime = ar5416AniGetListenTime(ah); |
850 | if (listenTime < 0) { |
851 | ahp->ah_stats.ast_ani_lneg++; |
852 | /* restart ANI period if listenTime is invalid */ |
853 | ar5416AniRestart(ah, aniState); |
854 | } |
855 | /* XXX beware of overflow? */ |
856 | aniState->listenTime += listenTime; |
857 | |
858 | OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime); |
859 | |
860 | params = aniState->params; |
861 | if (aniState->listenTime > 5*params->period) { |
862 | /* |
863 | * Check to see if need to lower immunity if |
864 | * 5 aniPeriods have passed |
865 | */ |
866 | updateMIBStats(ah, aniState); |
867 | if (aniState->ofdmPhyErrCount <= aniState->listenTime * |
868 | params->ofdmTrigLow/1000 && |
869 | aniState->cckPhyErrCount <= aniState->listenTime * |
870 | params->cckTrigLow/1000) |
871 | ar5416AniLowerImmunity(ah); |
872 | ar5416AniRestart(ah, aniState); |
873 | } else if (aniState->listenTime > params->period) { |
874 | updateMIBStats(ah, aniState); |
875 | /* check to see if need to raise immunity */ |
876 | if (aniState->ofdmPhyErrCount > aniState->listenTime * |
877 | params->ofdmTrigHigh / 1000) { |
878 | ar5416AniOfdmErrTrigger(ah); |
879 | ar5416AniRestart(ah, aniState); |
880 | } else if (aniState->cckPhyErrCount > aniState->listenTime * |
881 | params->cckTrigHigh / 1000) { |
882 | ar5416AniCckErrTrigger(ah); |
883 | ar5416AniRestart(ah, aniState); |
884 | } |
885 | } |
886 | } |
887 | |