Commit | Line | Data |
---|---|---|
f1dc5600 | 1 | /* |
cee075a2 | 2 | * Copyright (c) 2008-2009 Atheros Communications Inc. |
f1dc5600 S |
3 | * |
4 | * Permission to use, copy, modify, and/or distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | */ | |
16 | ||
cfe8cba9 | 17 | #include "hw.h" |
c16fcb49 | 18 | #include "hw-ops.h" |
f1dc5600 | 19 | |
cbe61d8a | 20 | static int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah, |
f1dc5600 S |
21 | struct ath9k_channel *chan) |
22 | { | |
f1dc5600 S |
23 | int i; |
24 | ||
2660b81a S |
25 | for (i = 0; i < ARRAY_SIZE(ah->ani); i++) { |
26 | if (ah->ani[i].c && | |
27 | ah->ani[i].c->channel == chan->channel) | |
f1dc5600 | 28 | return i; |
2660b81a S |
29 | if (ah->ani[i].c == NULL) { |
30 | ah->ani[i].c = chan; | |
f1dc5600 S |
31 | return i; |
32 | } | |
33 | } | |
34 | ||
c46917bb LR |
35 | ath_print(ath9k_hw_common(ah), ATH_DBG_ANI, |
36 | "No more channel states left. Using channel 0\n"); | |
f1dc5600 S |
37 | |
38 | return 0; | |
39 | } | |
40 | ||
cbe61d8a | 41 | static void ath9k_hw_update_mibstats(struct ath_hw *ah, |
f1dc5600 S |
42 | struct ath9k_mib_stats *stats) |
43 | { | |
44 | stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL); | |
45 | stats->rts_bad += REG_READ(ah, AR_RTS_FAIL); | |
46 | stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL); | |
47 | stats->rts_good += REG_READ(ah, AR_RTS_OK); | |
48 | stats->beacons += REG_READ(ah, AR_BEACON_CNT); | |
49 | } | |
50 | ||
cbe61d8a | 51 | static void ath9k_ani_restart(struct ath_hw *ah) |
f1dc5600 | 52 | { |
f1dc5600 | 53 | struct ar5416AniState *aniState; |
c46917bb | 54 | struct ath_common *common = ath9k_hw_common(ah); |
f1dc5600 S |
55 | |
56 | if (!DO_ANI(ah)) | |
57 | return; | |
58 | ||
2660b81a | 59 | aniState = ah->curani; |
f1dc5600 | 60 | aniState->listenTime = 0; |
1aa8e847 S |
61 | |
62 | if (aniState->ofdmTrigHigh > AR_PHY_COUNTMAX) { | |
63 | aniState->ofdmPhyErrBase = 0; | |
c46917bb LR |
64 | ath_print(common, ATH_DBG_ANI, |
65 | "OFDM Trigger is too high for hw counters\n"); | |
1aa8e847 S |
66 | } else { |
67 | aniState->ofdmPhyErrBase = | |
68 | AR_PHY_COUNTMAX - aniState->ofdmTrigHigh; | |
69 | } | |
70 | if (aniState->cckTrigHigh > AR_PHY_COUNTMAX) { | |
71 | aniState->cckPhyErrBase = 0; | |
c46917bb LR |
72 | ath_print(common, ATH_DBG_ANI, |
73 | "CCK Trigger is too high for hw counters\n"); | |
1aa8e847 S |
74 | } else { |
75 | aniState->cckPhyErrBase = | |
76 | AR_PHY_COUNTMAX - aniState->cckTrigHigh; | |
f1dc5600 | 77 | } |
c46917bb LR |
78 | ath_print(common, ATH_DBG_ANI, |
79 | "Writing ofdmbase=%u cckbase=%u\n", | |
80 | aniState->ofdmPhyErrBase, | |
81 | aniState->cckPhyErrBase); | |
7d0d0df0 S |
82 | |
83 | ENABLE_REGWRITE_BUFFER(ah); | |
84 | ||
1aa8e847 S |
85 | REG_WRITE(ah, AR_PHY_ERR_1, aniState->ofdmPhyErrBase); |
86 | REG_WRITE(ah, AR_PHY_ERR_2, aniState->cckPhyErrBase); | |
87 | REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); | |
88 | REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); | |
89 | ||
7d0d0df0 S |
90 | REGWRITE_BUFFER_FLUSH(ah); |
91 | DISABLE_REGWRITE_BUFFER(ah); | |
92 | ||
1aa8e847 S |
93 | ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); |
94 | ||
f1dc5600 S |
95 | aniState->ofdmPhyErrCount = 0; |
96 | aniState->cckPhyErrCount = 0; | |
97 | } | |
98 | ||
cbe61d8a | 99 | static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah) |
f1dc5600 | 100 | { |
b002a4a9 | 101 | struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; |
f1dc5600 | 102 | struct ar5416AniState *aniState; |
f1dc5600 S |
103 | int32_t rssi; |
104 | ||
105 | if (!DO_ANI(ah)) | |
106 | return; | |
107 | ||
2660b81a | 108 | aniState = ah->curani; |
f1dc5600 S |
109 | |
110 | if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) { | |
111 | if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, | |
112 | aniState->noiseImmunityLevel + 1)) { | |
113 | return; | |
114 | } | |
115 | } | |
116 | ||
117 | if (aniState->spurImmunityLevel < HAL_SPUR_IMMUNE_MAX) { | |
118 | if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, | |
119 | aniState->spurImmunityLevel + 1)) { | |
120 | return; | |
121 | } | |
122 | } | |
123 | ||
2660b81a | 124 | if (ah->opmode == NL80211_IFTYPE_AP) { |
f1dc5600 S |
125 | if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) { |
126 | ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, | |
127 | aniState->firstepLevel + 1); | |
128 | } | |
129 | return; | |
130 | } | |
cbe61d8a | 131 | rssi = BEACON_RSSI(ah); |
f1dc5600 S |
132 | if (rssi > aniState->rssiThrHigh) { |
133 | if (!aniState->ofdmWeakSigDetectOff) { | |
134 | if (ath9k_hw_ani_control(ah, | |
135 | ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, | |
136 | false)) { | |
137 | ath9k_hw_ani_control(ah, | |
138 | ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0); | |
139 | return; | |
140 | } | |
141 | } | |
142 | if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) { | |
143 | ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, | |
144 | aniState->firstepLevel + 1); | |
145 | return; | |
146 | } | |
147 | } else if (rssi > aniState->rssiThrLow) { | |
148 | if (aniState->ofdmWeakSigDetectOff) | |
149 | ath9k_hw_ani_control(ah, | |
150 | ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, | |
151 | true); | |
152 | if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) | |
153 | ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, | |
154 | aniState->firstepLevel + 1); | |
155 | return; | |
156 | } else { | |
d37b7da3 S |
157 | if ((conf->channel->band == IEEE80211_BAND_2GHZ) && |
158 | !conf_is_ht(conf)) { | |
f1dc5600 S |
159 | if (!aniState->ofdmWeakSigDetectOff) |
160 | ath9k_hw_ani_control(ah, | |
161 | ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, | |
162 | false); | |
163 | if (aniState->firstepLevel > 0) | |
164 | ath9k_hw_ani_control(ah, | |
165 | ATH9K_ANI_FIRSTEP_LEVEL, 0); | |
166 | return; | |
167 | } | |
168 | } | |
169 | } | |
170 | ||
cbe61d8a | 171 | static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah) |
f1dc5600 | 172 | { |
b002a4a9 | 173 | struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; |
f1dc5600 | 174 | struct ar5416AniState *aniState; |
f1dc5600 S |
175 | int32_t rssi; |
176 | ||
177 | if (!DO_ANI(ah)) | |
178 | return; | |
179 | ||
2660b81a | 180 | aniState = ah->curani; |
f1dc5600 S |
181 | if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) { |
182 | if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, | |
183 | aniState->noiseImmunityLevel + 1)) { | |
184 | return; | |
185 | } | |
186 | } | |
2660b81a | 187 | if (ah->opmode == NL80211_IFTYPE_AP) { |
f1dc5600 S |
188 | if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) { |
189 | ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, | |
190 | aniState->firstepLevel + 1); | |
191 | } | |
192 | return; | |
193 | } | |
cbe61d8a | 194 | rssi = BEACON_RSSI(ah); |
f1dc5600 S |
195 | if (rssi > aniState->rssiThrLow) { |
196 | if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) | |
197 | ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, | |
198 | aniState->firstepLevel + 1); | |
199 | } else { | |
d37b7da3 S |
200 | if ((conf->channel->band == IEEE80211_BAND_2GHZ) && |
201 | !conf_is_ht(conf)) { | |
f1dc5600 S |
202 | if (aniState->firstepLevel > 0) |
203 | ath9k_hw_ani_control(ah, | |
204 | ATH9K_ANI_FIRSTEP_LEVEL, 0); | |
205 | } | |
206 | } | |
207 | } | |
208 | ||
cbe61d8a | 209 | static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah) |
f1dc5600 | 210 | { |
f1dc5600 S |
211 | struct ar5416AniState *aniState; |
212 | int32_t rssi; | |
213 | ||
2660b81a | 214 | aniState = ah->curani; |
f1dc5600 | 215 | |
2660b81a | 216 | if (ah->opmode == NL80211_IFTYPE_AP) { |
f1dc5600 S |
217 | if (aniState->firstepLevel > 0) { |
218 | if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, | |
219 | aniState->firstepLevel - 1)) | |
220 | return; | |
221 | } | |
222 | } else { | |
cbe61d8a | 223 | rssi = BEACON_RSSI(ah); |
f1dc5600 S |
224 | if (rssi > aniState->rssiThrHigh) { |
225 | /* XXX: Handle me */ | |
226 | } else if (rssi > aniState->rssiThrLow) { | |
227 | if (aniState->ofdmWeakSigDetectOff) { | |
228 | if (ath9k_hw_ani_control(ah, | |
229 | ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, | |
230 | true) == true) | |
231 | return; | |
232 | } | |
233 | if (aniState->firstepLevel > 0) { | |
234 | if (ath9k_hw_ani_control(ah, | |
235 | ATH9K_ANI_FIRSTEP_LEVEL, | |
236 | aniState->firstepLevel - 1) == true) | |
237 | return; | |
238 | } | |
239 | } else { | |
240 | if (aniState->firstepLevel > 0) { | |
241 | if (ath9k_hw_ani_control(ah, | |
242 | ATH9K_ANI_FIRSTEP_LEVEL, | |
243 | aniState->firstepLevel - 1) == true) | |
244 | return; | |
245 | } | |
246 | } | |
247 | } | |
248 | ||
249 | if (aniState->spurImmunityLevel > 0) { | |
250 | if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, | |
251 | aniState->spurImmunityLevel - 1)) | |
252 | return; | |
253 | } | |
254 | ||
255 | if (aniState->noiseImmunityLevel > 0) { | |
256 | ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, | |
257 | aniState->noiseImmunityLevel - 1); | |
258 | return; | |
259 | } | |
260 | } | |
261 | ||
cbe61d8a | 262 | static int32_t ath9k_hw_ani_get_listen_time(struct ath_hw *ah) |
f1dc5600 | 263 | { |
f1dc5600 S |
264 | struct ar5416AniState *aniState; |
265 | u32 txFrameCount, rxFrameCount, cycleCount; | |
266 | int32_t listenTime; | |
267 | ||
268 | txFrameCount = REG_READ(ah, AR_TFCNT); | |
269 | rxFrameCount = REG_READ(ah, AR_RFCNT); | |
270 | cycleCount = REG_READ(ah, AR_CCCNT); | |
271 | ||
2660b81a | 272 | aniState = ah->curani; |
f1dc5600 S |
273 | if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) { |
274 | ||
275 | listenTime = 0; | |
2660b81a | 276 | ah->stats.ast_ani_lzero++; |
f1dc5600 S |
277 | } else { |
278 | int32_t ccdelta = cycleCount - aniState->cycleCount; | |
279 | int32_t rfdelta = rxFrameCount - aniState->rxFrameCount; | |
280 | int32_t tfdelta = txFrameCount - aniState->txFrameCount; | |
281 | listenTime = (ccdelta - rfdelta - tfdelta) / 44000; | |
282 | } | |
283 | aniState->cycleCount = cycleCount; | |
284 | aniState->txFrameCount = txFrameCount; | |
285 | aniState->rxFrameCount = rxFrameCount; | |
286 | ||
287 | return listenTime; | |
288 | } | |
289 | ||
cbe61d8a | 290 | void ath9k_ani_reset(struct ath_hw *ah) |
f1dc5600 | 291 | { |
f1dc5600 | 292 | struct ar5416AniState *aniState; |
2660b81a | 293 | struct ath9k_channel *chan = ah->curchan; |
c46917bb | 294 | struct ath_common *common = ath9k_hw_common(ah); |
f1dc5600 S |
295 | int index; |
296 | ||
297 | if (!DO_ANI(ah)) | |
298 | return; | |
299 | ||
300 | index = ath9k_hw_get_ani_channel_idx(ah, chan); | |
2660b81a S |
301 | aniState = &ah->ani[index]; |
302 | ah->curani = aniState; | |
f1dc5600 | 303 | |
2660b81a S |
304 | if (DO_ANI(ah) && ah->opmode != NL80211_IFTYPE_STATION |
305 | && ah->opmode != NL80211_IFTYPE_ADHOC) { | |
c46917bb LR |
306 | ath_print(common, ATH_DBG_ANI, |
307 | "Reset ANI state opmode %u\n", ah->opmode); | |
2660b81a | 308 | ah->stats.ast_ani_reset++; |
f1dc5600 | 309 | |
c66284f2 LR |
310 | if (ah->opmode == NL80211_IFTYPE_AP) { |
311 | /* | |
312 | * ath9k_hw_ani_control() will only process items set on | |
313 | * ah->ani_function | |
314 | */ | |
315 | if (IS_CHAN_2GHZ(chan)) | |
316 | ah->ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL | | |
317 | ATH9K_ANI_FIRSTEP_LEVEL); | |
318 | else | |
319 | ah->ani_function = 0; | |
320 | } | |
321 | ||
f1dc5600 S |
322 | ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0); |
323 | ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0); | |
324 | ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0); | |
325 | ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, | |
326 | !ATH9K_ANI_USE_OFDM_WEAK_SIG); | |
327 | ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR, | |
328 | ATH9K_ANI_CCK_WEAK_SIG_THR); | |
329 | ||
330 | ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) | | |
331 | ATH9K_RX_FILTER_PHYERR); | |
332 | ||
2660b81a S |
333 | if (ah->opmode == NL80211_IFTYPE_AP) { |
334 | ah->curani->ofdmTrigHigh = | |
335 | ah->config.ofdm_trig_high; | |
336 | ah->curani->ofdmTrigLow = | |
337 | ah->config.ofdm_trig_low; | |
338 | ah->curani->cckTrigHigh = | |
339 | ah->config.cck_trig_high; | |
340 | ah->curani->cckTrigLow = | |
341 | ah->config.cck_trig_low; | |
f1dc5600 S |
342 | } |
343 | ath9k_ani_restart(ah); | |
344 | return; | |
345 | } | |
346 | ||
347 | if (aniState->noiseImmunityLevel != 0) | |
348 | ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, | |
349 | aniState->noiseImmunityLevel); | |
350 | if (aniState->spurImmunityLevel != 0) | |
351 | ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, | |
352 | aniState->spurImmunityLevel); | |
353 | if (aniState->ofdmWeakSigDetectOff) | |
354 | ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, | |
355 | !aniState->ofdmWeakSigDetectOff); | |
356 | if (aniState->cckWeakSigThreshold) | |
357 | ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR, | |
358 | aniState->cckWeakSigThreshold); | |
359 | if (aniState->firstepLevel != 0) | |
360 | ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, | |
361 | aniState->firstepLevel); | |
f1dc5600 | 362 | |
1aa8e847 S |
363 | ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) & |
364 | ~ATH9K_RX_FILTER_PHYERR); | |
365 | ath9k_ani_restart(ah); | |
7d0d0df0 S |
366 | |
367 | ENABLE_REGWRITE_BUFFER(ah); | |
368 | ||
1aa8e847 S |
369 | REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); |
370 | REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); | |
7d0d0df0 S |
371 | |
372 | REGWRITE_BUFFER_FLUSH(ah); | |
373 | DISABLE_REGWRITE_BUFFER(ah); | |
f1dc5600 S |
374 | } |
375 | ||
cbe61d8a | 376 | void ath9k_hw_ani_monitor(struct ath_hw *ah, |
f1dc5600 S |
377 | struct ath9k_channel *chan) |
378 | { | |
f1dc5600 | 379 | struct ar5416AniState *aniState; |
c46917bb | 380 | struct ath_common *common = ath9k_hw_common(ah); |
f1dc5600 | 381 | int32_t listenTime; |
1aa8e847 S |
382 | u32 phyCnt1, phyCnt2; |
383 | u32 ofdmPhyErrCnt, cckPhyErrCnt; | |
f1dc5600 | 384 | |
99506882 GJ |
385 | if (!DO_ANI(ah)) |
386 | return; | |
387 | ||
2660b81a | 388 | aniState = ah->curani; |
f1dc5600 S |
389 | |
390 | listenTime = ath9k_hw_ani_get_listen_time(ah); | |
391 | if (listenTime < 0) { | |
2660b81a | 392 | ah->stats.ast_ani_lneg++; |
f1dc5600 S |
393 | ath9k_ani_restart(ah); |
394 | return; | |
395 | } | |
396 | ||
397 | aniState->listenTime += listenTime; | |
398 | ||
1aa8e847 | 399 | ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); |
f1dc5600 | 400 | |
1aa8e847 S |
401 | phyCnt1 = REG_READ(ah, AR_PHY_ERR_1); |
402 | phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); | |
403 | ||
404 | if (phyCnt1 < aniState->ofdmPhyErrBase || | |
405 | phyCnt2 < aniState->cckPhyErrBase) { | |
406 | if (phyCnt1 < aniState->ofdmPhyErrBase) { | |
c46917bb LR |
407 | ath_print(common, ATH_DBG_ANI, |
408 | "phyCnt1 0x%x, resetting " | |
409 | "counter value to 0x%x\n", | |
410 | phyCnt1, | |
411 | aniState->ofdmPhyErrBase); | |
1aa8e847 S |
412 | REG_WRITE(ah, AR_PHY_ERR_1, |
413 | aniState->ofdmPhyErrBase); | |
414 | REG_WRITE(ah, AR_PHY_ERR_MASK_1, | |
415 | AR_PHY_ERR_OFDM_TIMING); | |
416 | } | |
417 | if (phyCnt2 < aniState->cckPhyErrBase) { | |
c46917bb LR |
418 | ath_print(common, ATH_DBG_ANI, |
419 | "phyCnt2 0x%x, resetting " | |
420 | "counter value to 0x%x\n", | |
421 | phyCnt2, | |
422 | aniState->cckPhyErrBase); | |
1aa8e847 S |
423 | REG_WRITE(ah, AR_PHY_ERR_2, |
424 | aniState->cckPhyErrBase); | |
425 | REG_WRITE(ah, AR_PHY_ERR_MASK_2, | |
426 | AR_PHY_ERR_CCK_TIMING); | |
f1dc5600 | 427 | } |
1aa8e847 S |
428 | return; |
429 | } | |
f1dc5600 | 430 | |
1aa8e847 S |
431 | ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase; |
432 | ah->stats.ast_ani_ofdmerrs += | |
433 | ofdmPhyErrCnt - aniState->ofdmPhyErrCount; | |
434 | aniState->ofdmPhyErrCount = ofdmPhyErrCnt; | |
f1dc5600 | 435 | |
1aa8e847 S |
436 | cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase; |
437 | ah->stats.ast_ani_cckerrs += | |
438 | cckPhyErrCnt - aniState->cckPhyErrCount; | |
439 | aniState->cckPhyErrCount = cckPhyErrCnt; | |
f1dc5600 | 440 | |
2660b81a | 441 | if (aniState->listenTime > 5 * ah->aniperiod) { |
f1dc5600 S |
442 | if (aniState->ofdmPhyErrCount <= aniState->listenTime * |
443 | aniState->ofdmTrigLow / 1000 && | |
444 | aniState->cckPhyErrCount <= aniState->listenTime * | |
445 | aniState->cckTrigLow / 1000) | |
446 | ath9k_hw_ani_lower_immunity(ah); | |
447 | ath9k_ani_restart(ah); | |
2660b81a | 448 | } else if (aniState->listenTime > ah->aniperiod) { |
f1dc5600 S |
449 | if (aniState->ofdmPhyErrCount > aniState->listenTime * |
450 | aniState->ofdmTrigHigh / 1000) { | |
451 | ath9k_hw_ani_ofdm_err_trigger(ah); | |
452 | ath9k_ani_restart(ah); | |
453 | } else if (aniState->cckPhyErrCount > | |
454 | aniState->listenTime * aniState->cckTrigHigh / | |
455 | 1000) { | |
456 | ath9k_hw_ani_cck_err_trigger(ah); | |
457 | ath9k_ani_restart(ah); | |
458 | } | |
459 | } | |
460 | } | |
7322fd19 | 461 | EXPORT_SYMBOL(ath9k_hw_ani_monitor); |
f1dc5600 | 462 | |
cbe61d8a | 463 | void ath9k_enable_mib_counters(struct ath_hw *ah) |
f1dc5600 | 464 | { |
c46917bb LR |
465 | struct ath_common *common = ath9k_hw_common(ah); |
466 | ||
467 | ath_print(common, ATH_DBG_ANI, "Enable MIB counters\n"); | |
f1dc5600 | 468 | |
cbe61d8a | 469 | ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); |
f1dc5600 | 470 | |
7d0d0df0 S |
471 | ENABLE_REGWRITE_BUFFER(ah); |
472 | ||
f1dc5600 S |
473 | REG_WRITE(ah, AR_FILT_OFDM, 0); |
474 | REG_WRITE(ah, AR_FILT_CCK, 0); | |
475 | REG_WRITE(ah, AR_MIBC, | |
476 | ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS) | |
477 | & 0x0f); | |
478 | REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); | |
479 | REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); | |
7d0d0df0 S |
480 | |
481 | REGWRITE_BUFFER_FLUSH(ah); | |
482 | DISABLE_REGWRITE_BUFFER(ah); | |
f1dc5600 S |
483 | } |
484 | ||
0fd06c90 | 485 | /* Freeze the MIB counters, get the stats and then clear them */ |
cbe61d8a | 486 | void ath9k_hw_disable_mib_counters(struct ath_hw *ah) |
f1dc5600 | 487 | { |
c46917bb LR |
488 | struct ath_common *common = ath9k_hw_common(ah); |
489 | ||
490 | ath_print(common, ATH_DBG_ANI, "Disable MIB counters\n"); | |
491 | ||
0fd06c90 | 492 | REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC); |
cbe61d8a | 493 | ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); |
0fd06c90 | 494 | REG_WRITE(ah, AR_MIBC, AR_MIBC_CMC); |
f1dc5600 S |
495 | REG_WRITE(ah, AR_FILT_OFDM, 0); |
496 | REG_WRITE(ah, AR_FILT_CCK, 0); | |
497 | } | |
498 | ||
cbe61d8a | 499 | u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah, |
f1dc5600 S |
500 | u32 *rxc_pcnt, |
501 | u32 *rxf_pcnt, | |
502 | u32 *txf_pcnt) | |
503 | { | |
c46917bb | 504 | struct ath_common *common = ath9k_hw_common(ah); |
f1dc5600 S |
505 | static u32 cycles, rx_clear, rx_frame, tx_frame; |
506 | u32 good = 1; | |
507 | ||
508 | u32 rc = REG_READ(ah, AR_RCCNT); | |
509 | u32 rf = REG_READ(ah, AR_RFCNT); | |
510 | u32 tf = REG_READ(ah, AR_TFCNT); | |
511 | u32 cc = REG_READ(ah, AR_CCCNT); | |
512 | ||
513 | if (cycles == 0 || cycles > cc) { | |
c46917bb LR |
514 | ath_print(common, ATH_DBG_ANI, |
515 | "cycle counter wrap. ExtBusy = 0\n"); | |
f1dc5600 S |
516 | good = 0; |
517 | } else { | |
518 | u32 cc_d = cc - cycles; | |
519 | u32 rc_d = rc - rx_clear; | |
520 | u32 rf_d = rf - rx_frame; | |
521 | u32 tf_d = tf - tx_frame; | |
522 | ||
523 | if (cc_d != 0) { | |
524 | *rxc_pcnt = rc_d * 100 / cc_d; | |
525 | *rxf_pcnt = rf_d * 100 / cc_d; | |
526 | *txf_pcnt = tf_d * 100 / cc_d; | |
527 | } else { | |
528 | good = 0; | |
529 | } | |
530 | } | |
531 | ||
532 | cycles = cc; | |
533 | rx_frame = rf; | |
534 | rx_clear = rc; | |
535 | tx_frame = tf; | |
536 | ||
537 | return good; | |
538 | } | |
539 | ||
540 | /* | |
541 | * Process a MIB interrupt. We may potentially be invoked because | |
542 | * any of the MIB counters overflow/trigger so don't assume we're | |
543 | * here because a PHY error counter triggered. | |
544 | */ | |
22e66a4c | 545 | void ath9k_hw_procmibevent(struct ath_hw *ah) |
f1dc5600 | 546 | { |
f1dc5600 S |
547 | u32 phyCnt1, phyCnt2; |
548 | ||
549 | /* Reset these counters regardless */ | |
550 | REG_WRITE(ah, AR_FILT_OFDM, 0); | |
551 | REG_WRITE(ah, AR_FILT_CCK, 0); | |
552 | if (!(REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING)) | |
553 | REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR); | |
554 | ||
555 | /* Clear the mib counters and save them in the stats */ | |
cbe61d8a | 556 | ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); |
f1dc5600 S |
557 | |
558 | if (!DO_ANI(ah)) | |
559 | return; | |
560 | ||
561 | /* NB: these are not reset-on-read */ | |
562 | phyCnt1 = REG_READ(ah, AR_PHY_ERR_1); | |
563 | phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); | |
564 | if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || | |
565 | ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { | |
2660b81a | 566 | struct ar5416AniState *aniState = ah->curani; |
f1dc5600 S |
567 | u32 ofdmPhyErrCnt, cckPhyErrCnt; |
568 | ||
569 | /* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */ | |
570 | ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase; | |
2660b81a | 571 | ah->stats.ast_ani_ofdmerrs += |
f1dc5600 S |
572 | ofdmPhyErrCnt - aniState->ofdmPhyErrCount; |
573 | aniState->ofdmPhyErrCount = ofdmPhyErrCnt; | |
574 | ||
575 | cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase; | |
2660b81a | 576 | ah->stats.ast_ani_cckerrs += |
f1dc5600 S |
577 | cckPhyErrCnt - aniState->cckPhyErrCount; |
578 | aniState->cckPhyErrCount = cckPhyErrCnt; | |
579 | ||
580 | /* | |
581 | * NB: figure out which counter triggered. If both | |
582 | * trigger we'll only deal with one as the processing | |
583 | * clobbers the error counter so the trigger threshold | |
584 | * check will never be true. | |
585 | */ | |
586 | if (aniState->ofdmPhyErrCount > aniState->ofdmTrigHigh) | |
587 | ath9k_hw_ani_ofdm_err_trigger(ah); | |
588 | if (aniState->cckPhyErrCount > aniState->cckTrigHigh) | |
589 | ath9k_hw_ani_cck_err_trigger(ah); | |
590 | /* NB: always restart to insure the h/w counters are reset */ | |
591 | ath9k_ani_restart(ah); | |
592 | } | |
593 | } | |
7322fd19 | 594 | EXPORT_SYMBOL(ath9k_hw_procmibevent); |
f1dc5600 | 595 | |
cbe61d8a | 596 | void ath9k_hw_ani_setup(struct ath_hw *ah) |
f1dc5600 | 597 | { |
f1dc5600 S |
598 | int i; |
599 | ||
600 | const int totalSizeDesired[] = { -55, -55, -55, -55, -62 }; | |
601 | const int coarseHigh[] = { -14, -14, -14, -14, -12 }; | |
602 | const int coarseLow[] = { -64, -64, -64, -64, -70 }; | |
603 | const int firpwr[] = { -78, -78, -78, -78, -80 }; | |
604 | ||
605 | for (i = 0; i < 5; i++) { | |
2660b81a S |
606 | ah->totalSizeDesired[i] = totalSizeDesired[i]; |
607 | ah->coarse_high[i] = coarseHigh[i]; | |
608 | ah->coarse_low[i] = coarseLow[i]; | |
609 | ah->firpwr[i] = firpwr[i]; | |
f1dc5600 S |
610 | } |
611 | } | |
612 | ||
f637cfd6 | 613 | void ath9k_hw_ani_init(struct ath_hw *ah) |
f1dc5600 | 614 | { |
c46917bb | 615 | struct ath_common *common = ath9k_hw_common(ah); |
f1dc5600 S |
616 | int i; |
617 | ||
c46917bb | 618 | ath_print(common, ATH_DBG_ANI, "Initialize ANI\n"); |
2660b81a S |
619 | |
620 | memset(ah->ani, 0, sizeof(ah->ani)); | |
621 | for (i = 0; i < ARRAY_SIZE(ah->ani); i++) { | |
622 | ah->ani[i].ofdmTrigHigh = ATH9K_ANI_OFDM_TRIG_HIGH; | |
623 | ah->ani[i].ofdmTrigLow = ATH9K_ANI_OFDM_TRIG_LOW; | |
624 | ah->ani[i].cckTrigHigh = ATH9K_ANI_CCK_TRIG_HIGH; | |
625 | ah->ani[i].cckTrigLow = ATH9K_ANI_CCK_TRIG_LOW; | |
626 | ah->ani[i].rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH; | |
627 | ah->ani[i].rssiThrLow = ATH9K_ANI_RSSI_THR_LOW; | |
628 | ah->ani[i].ofdmWeakSigDetectOff = | |
f1dc5600 | 629 | !ATH9K_ANI_USE_OFDM_WEAK_SIG; |
2660b81a | 630 | ah->ani[i].cckWeakSigThreshold = |
f1dc5600 | 631 | ATH9K_ANI_CCK_WEAK_SIG_THR; |
2660b81a S |
632 | ah->ani[i].spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; |
633 | ah->ani[i].firstepLevel = ATH9K_ANI_FIRSTEP_LVL; | |
1aa8e847 S |
634 | ah->ani[i].ofdmPhyErrBase = |
635 | AR_PHY_COUNTMAX - ATH9K_ANI_OFDM_TRIG_HIGH; | |
636 | ah->ani[i].cckPhyErrBase = | |
637 | AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH; | |
f1dc5600 | 638 | } |
1aa8e847 | 639 | |
c46917bb LR |
640 | ath_print(common, ATH_DBG_ANI, |
641 | "Setting OfdmErrBase = 0x%08x\n", | |
642 | ah->ani[0].ofdmPhyErrBase); | |
643 | ath_print(common, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n", | |
644 | ah->ani[0].cckPhyErrBase); | |
1aa8e847 | 645 | |
7d0d0df0 S |
646 | ENABLE_REGWRITE_BUFFER(ah); |
647 | ||
1aa8e847 S |
648 | REG_WRITE(ah, AR_PHY_ERR_1, ah->ani[0].ofdmPhyErrBase); |
649 | REG_WRITE(ah, AR_PHY_ERR_2, ah->ani[0].cckPhyErrBase); | |
7d0d0df0 S |
650 | |
651 | REGWRITE_BUFFER_FLUSH(ah); | |
652 | DISABLE_REGWRITE_BUFFER(ah); | |
653 | ||
1aa8e847 S |
654 | ath9k_enable_mib_counters(ah); |
655 | ||
2660b81a S |
656 | ah->aniperiod = ATH9K_ANI_PERIOD; |
657 | if (ah->config.enable_ani) | |
658 | ah->proc_phyerr |= HAL_PROCESS_ANI; | |
f1dc5600 | 659 | } |