blob: f5b971973d31951cfd3c4dc4572d37368474ee76 [file] [log] [blame]
Sujithf1dc5602008-10-29 10:16:30 +05301/*
Sujithcee075a2009-03-13 09:07:23 +05302 * Copyright (c) 2008-2009 Atheros Communications Inc.
Sujithf1dc5602008-10-29 10:16:30 +05303 *
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
Luis R. Rodriguezcfe8cba2009-09-13 23:39:31 -070017#include "hw.h"
Felix Fietkauc16fcb42010-04-15 17:38:39 -040018#include "hw-ops.h"
Sujithf1dc5602008-10-29 10:16:30 +053019
Luis R. Rodriguezac0bb762010-06-12 00:33:42 -040020/* Private to ani.c */
21static inline void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
22{
23 ath9k_hw_private_ops(ah)->ani_lower_immunity(ah);
24}
25
Sujithcbe61d82009-02-09 13:27:12 +053026static int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah,
Sujithf1dc5602008-10-29 10:16:30 +053027 struct ath9k_channel *chan)
28{
Sujithf1dc5602008-10-29 10:16:30 +053029 int i;
30
Sujith2660b812009-02-09 13:27:26 +053031 for (i = 0; i < ARRAY_SIZE(ah->ani); i++) {
32 if (ah->ani[i].c &&
33 ah->ani[i].c->channel == chan->channel)
Sujithf1dc5602008-10-29 10:16:30 +053034 return i;
Sujith2660b812009-02-09 13:27:26 +053035 if (ah->ani[i].c == NULL) {
36 ah->ani[i].c = chan;
Sujithf1dc5602008-10-29 10:16:30 +053037 return i;
38 }
39 }
40
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -070041 ath_print(ath9k_hw_common(ah), ATH_DBG_ANI,
42 "No more channel states left. Using channel 0\n");
Sujithf1dc5602008-10-29 10:16:30 +053043
44 return 0;
45}
46
Sujithcbe61d82009-02-09 13:27:12 +053047static void ath9k_hw_update_mibstats(struct ath_hw *ah,
Sujithf1dc5602008-10-29 10:16:30 +053048 struct ath9k_mib_stats *stats)
49{
50 stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL);
51 stats->rts_bad += REG_READ(ah, AR_RTS_FAIL);
52 stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL);
53 stats->rts_good += REG_READ(ah, AR_RTS_OK);
54 stats->beacons += REG_READ(ah, AR_BEACON_CNT);
55}
56
Sujithcbe61d82009-02-09 13:27:12 +053057static void ath9k_ani_restart(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +053058{
Sujithf1dc5602008-10-29 10:16:30 +053059 struct ar5416AniState *aniState;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -070060 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +053061
62 if (!DO_ANI(ah))
63 return;
64
Sujith2660b812009-02-09 13:27:26 +053065 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +053066 aniState->listenTime = 0;
Sujithf1dc5602008-10-29 10:16:30 +053067
Sujith1aa8e842009-08-13 09:34:25 +053068 if (aniState->ofdmTrigHigh > AR_PHY_COUNTMAX) {
69 aniState->ofdmPhyErrBase = 0;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -070070 ath_print(common, ATH_DBG_ANI,
71 "OFDM Trigger is too high for hw counters\n");
Sujith1aa8e842009-08-13 09:34:25 +053072 } else {
73 aniState->ofdmPhyErrBase =
74 AR_PHY_COUNTMAX - aniState->ofdmTrigHigh;
Sujithf1dc5602008-10-29 10:16:30 +053075 }
Sujith1aa8e842009-08-13 09:34:25 +053076 if (aniState->cckTrigHigh > AR_PHY_COUNTMAX) {
77 aniState->cckPhyErrBase = 0;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -070078 ath_print(common, ATH_DBG_ANI,
79 "CCK Trigger is too high for hw counters\n");
Sujith1aa8e842009-08-13 09:34:25 +053080 } else {
81 aniState->cckPhyErrBase =
82 AR_PHY_COUNTMAX - aniState->cckTrigHigh;
83 }
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -070084 ath_print(common, ATH_DBG_ANI,
85 "Writing ofdmbase=%u cckbase=%u\n",
86 aniState->ofdmPhyErrBase,
87 aniState->cckPhyErrBase);
Sujith7d0d0df2010-04-16 11:53:57 +053088
89 ENABLE_REGWRITE_BUFFER(ah);
90
Sujith1aa8e842009-08-13 09:34:25 +053091 REG_WRITE(ah, AR_PHY_ERR_1, aniState->ofdmPhyErrBase);
92 REG_WRITE(ah, AR_PHY_ERR_2, aniState->cckPhyErrBase);
93 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
94 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
95
Sujith7d0d0df2010-04-16 11:53:57 +053096 REGWRITE_BUFFER_FLUSH(ah);
97 DISABLE_REGWRITE_BUFFER(ah);
98
Sujith1aa8e842009-08-13 09:34:25 +053099 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
100
Sujithf1dc5602008-10-29 10:16:30 +0530101 aniState->ofdmPhyErrCount = 0;
102 aniState->cckPhyErrCount = 0;
103}
104
Sujithcbe61d82009-02-09 13:27:12 +0530105static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530106{
Luis R. Rodriguezb002a4a2009-09-13 00:03:27 -0700107 struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
Sujithf1dc5602008-10-29 10:16:30 +0530108 struct ar5416AniState *aniState;
Sujithf1dc5602008-10-29 10:16:30 +0530109 int32_t rssi;
110
111 if (!DO_ANI(ah))
112 return;
113
Sujith2660b812009-02-09 13:27:26 +0530114 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530115
116 if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
117 if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
118 aniState->noiseImmunityLevel + 1)) {
119 return;
120 }
121 }
122
123 if (aniState->spurImmunityLevel < HAL_SPUR_IMMUNE_MAX) {
124 if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
125 aniState->spurImmunityLevel + 1)) {
126 return;
127 }
128 }
129
Sujith2660b812009-02-09 13:27:26 +0530130 if (ah->opmode == NL80211_IFTYPE_AP) {
Sujithf1dc5602008-10-29 10:16:30 +0530131 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
132 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
133 aniState->firstepLevel + 1);
134 }
135 return;
136 }
Sujithcbe61d82009-02-09 13:27:12 +0530137 rssi = BEACON_RSSI(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530138 if (rssi > aniState->rssiThrHigh) {
139 if (!aniState->ofdmWeakSigDetectOff) {
140 if (ath9k_hw_ani_control(ah,
141 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
142 false)) {
143 ath9k_hw_ani_control(ah,
144 ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
145 return;
146 }
147 }
148 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
149 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
150 aniState->firstepLevel + 1);
151 return;
152 }
153 } else if (rssi > aniState->rssiThrLow) {
154 if (aniState->ofdmWeakSigDetectOff)
155 ath9k_hw_ani_control(ah,
156 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
157 true);
158 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
159 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
160 aniState->firstepLevel + 1);
161 return;
162 } else {
Sujithd37b7da2009-09-11 08:30:03 +0530163 if ((conf->channel->band == IEEE80211_BAND_2GHZ) &&
164 !conf_is_ht(conf)) {
Sujithf1dc5602008-10-29 10:16:30 +0530165 if (!aniState->ofdmWeakSigDetectOff)
166 ath9k_hw_ani_control(ah,
167 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
168 false);
169 if (aniState->firstepLevel > 0)
170 ath9k_hw_ani_control(ah,
171 ATH9K_ANI_FIRSTEP_LEVEL, 0);
172 return;
173 }
174 }
175}
176
Sujithcbe61d82009-02-09 13:27:12 +0530177static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530178{
Luis R. Rodriguezb002a4a2009-09-13 00:03:27 -0700179 struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
Sujithf1dc5602008-10-29 10:16:30 +0530180 struct ar5416AniState *aniState;
Sujithf1dc5602008-10-29 10:16:30 +0530181 int32_t rssi;
182
183 if (!DO_ANI(ah))
184 return;
185
Sujith2660b812009-02-09 13:27:26 +0530186 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530187 if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
188 if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
189 aniState->noiseImmunityLevel + 1)) {
190 return;
191 }
192 }
Sujith2660b812009-02-09 13:27:26 +0530193 if (ah->opmode == NL80211_IFTYPE_AP) {
Sujithf1dc5602008-10-29 10:16:30 +0530194 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
195 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
196 aniState->firstepLevel + 1);
197 }
198 return;
199 }
Sujithcbe61d82009-02-09 13:27:12 +0530200 rssi = BEACON_RSSI(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530201 if (rssi > aniState->rssiThrLow) {
202 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
203 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
204 aniState->firstepLevel + 1);
205 } else {
Sujithd37b7da2009-09-11 08:30:03 +0530206 if ((conf->channel->band == IEEE80211_BAND_2GHZ) &&
207 !conf_is_ht(conf)) {
Sujithf1dc5602008-10-29 10:16:30 +0530208 if (aniState->firstepLevel > 0)
209 ath9k_hw_ani_control(ah,
210 ATH9K_ANI_FIRSTEP_LEVEL, 0);
211 }
212 }
213}
214
Luis R. Rodriguezac0bb762010-06-12 00:33:42 -0400215static void ath9k_hw_ani_lower_immunity_old(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530216{
Sujithf1dc5602008-10-29 10:16:30 +0530217 struct ar5416AniState *aniState;
218 int32_t rssi;
219
Sujith2660b812009-02-09 13:27:26 +0530220 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530221
Sujith2660b812009-02-09 13:27:26 +0530222 if (ah->opmode == NL80211_IFTYPE_AP) {
Sujithf1dc5602008-10-29 10:16:30 +0530223 if (aniState->firstepLevel > 0) {
224 if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
225 aniState->firstepLevel - 1))
226 return;
227 }
228 } else {
Sujithcbe61d82009-02-09 13:27:12 +0530229 rssi = BEACON_RSSI(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530230 if (rssi > aniState->rssiThrHigh) {
231 /* XXX: Handle me */
232 } else if (rssi > aniState->rssiThrLow) {
233 if (aniState->ofdmWeakSigDetectOff) {
234 if (ath9k_hw_ani_control(ah,
235 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
236 true) == true)
237 return;
238 }
239 if (aniState->firstepLevel > 0) {
240 if (ath9k_hw_ani_control(ah,
241 ATH9K_ANI_FIRSTEP_LEVEL,
242 aniState->firstepLevel - 1) == true)
243 return;
244 }
245 } else {
246 if (aniState->firstepLevel > 0) {
247 if (ath9k_hw_ani_control(ah,
248 ATH9K_ANI_FIRSTEP_LEVEL,
249 aniState->firstepLevel - 1) == true)
250 return;
251 }
252 }
253 }
254
255 if (aniState->spurImmunityLevel > 0) {
256 if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
257 aniState->spurImmunityLevel - 1))
258 return;
259 }
260
261 if (aniState->noiseImmunityLevel > 0) {
262 ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
263 aniState->noiseImmunityLevel - 1);
264 return;
265 }
266}
267
Luis R. Rodriguez37e5bf62010-06-12 00:33:40 -0400268static u8 ath9k_hw_chan_2_clockrate_mhz(struct ath_hw *ah)
269{
270 struct ath9k_channel *chan = ah->curchan;
271 struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
272 u8 clockrate; /* in MHz */
273
274 if (!ah->curchan) /* should really check for CCK instead */
275 clockrate = ATH9K_CLOCK_RATE_CCK;
276 else if (conf->channel->band == IEEE80211_BAND_2GHZ)
277 clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM;
278 else if (IS_CHAN_A_FAST_CLOCK(ah, chan))
279 clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM;
280 else
281 clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM;
282
283 if (conf_is_ht40(conf))
284 return clockrate * 2;
285
286 return clockrate * 2;
287}
288
Sujithcbe61d82009-02-09 13:27:12 +0530289static int32_t ath9k_hw_ani_get_listen_time(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530290{
Sujithf1dc5602008-10-29 10:16:30 +0530291 struct ar5416AniState *aniState;
292 u32 txFrameCount, rxFrameCount, cycleCount;
293 int32_t listenTime;
294
295 txFrameCount = REG_READ(ah, AR_TFCNT);
296 rxFrameCount = REG_READ(ah, AR_RFCNT);
297 cycleCount = REG_READ(ah, AR_CCCNT);
298
Sujith2660b812009-02-09 13:27:26 +0530299 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530300 if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) {
301
302 listenTime = 0;
Sujith2660b812009-02-09 13:27:26 +0530303 ah->stats.ast_ani_lzero++;
Sujithf1dc5602008-10-29 10:16:30 +0530304 } else {
305 int32_t ccdelta = cycleCount - aniState->cycleCount;
306 int32_t rfdelta = rxFrameCount - aniState->rxFrameCount;
307 int32_t tfdelta = txFrameCount - aniState->txFrameCount;
Luis R. Rodriguez37e5bf62010-06-12 00:33:40 -0400308 int32_t clock_rate = ath9k_hw_chan_2_clockrate_mhz(ah) * 1000;;
309
310 /*
311 * convert HW counter values to ms using mode
312 * specifix clock rate
313 */
314 clock_rate = ath9k_hw_chan_2_clockrate_mhz(ah) * 1000;;
315
316 listenTime = (ccdelta - rfdelta - tfdelta) / clock_rate;
Sujithf1dc5602008-10-29 10:16:30 +0530317 }
318 aniState->cycleCount = cycleCount;
319 aniState->txFrameCount = txFrameCount;
320 aniState->rxFrameCount = rxFrameCount;
321
322 return listenTime;
323}
324
Luis R. Rodriguez40346b62010-06-12 00:33:44 -0400325static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning)
Sujithf1dc5602008-10-29 10:16:30 +0530326{
Sujithf1dc5602008-10-29 10:16:30 +0530327 struct ar5416AniState *aniState;
Sujith2660b812009-02-09 13:27:26 +0530328 struct ath9k_channel *chan = ah->curchan;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700329 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530330 int index;
331
332 if (!DO_ANI(ah))
333 return;
334
335 index = ath9k_hw_get_ani_channel_idx(ah, chan);
Sujith2660b812009-02-09 13:27:26 +0530336 aniState = &ah->ani[index];
337 ah->curani = aniState;
Sujithf1dc5602008-10-29 10:16:30 +0530338
Sujith2660b812009-02-09 13:27:26 +0530339 if (DO_ANI(ah) && ah->opmode != NL80211_IFTYPE_STATION
340 && ah->opmode != NL80211_IFTYPE_ADHOC) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700341 ath_print(common, ATH_DBG_ANI,
342 "Reset ANI state opmode %u\n", ah->opmode);
Sujith2660b812009-02-09 13:27:26 +0530343 ah->stats.ast_ani_reset++;
Sujithf1dc5602008-10-29 10:16:30 +0530344
Luis R. Rodriguezc66284f2009-07-16 10:17:35 -0700345 if (ah->opmode == NL80211_IFTYPE_AP) {
346 /*
347 * ath9k_hw_ani_control() will only process items set on
348 * ah->ani_function
349 */
350 if (IS_CHAN_2GHZ(chan))
351 ah->ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL |
352 ATH9K_ANI_FIRSTEP_LEVEL);
353 else
354 ah->ani_function = 0;
355 }
356
Sujithf1dc5602008-10-29 10:16:30 +0530357 ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0);
358 ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
359 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0);
360 ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
361 !ATH9K_ANI_USE_OFDM_WEAK_SIG);
362 ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
363 ATH9K_ANI_CCK_WEAK_SIG_THR);
364
365 ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) |
366 ATH9K_RX_FILTER_PHYERR);
367
Sujith2660b812009-02-09 13:27:26 +0530368 if (ah->opmode == NL80211_IFTYPE_AP) {
369 ah->curani->ofdmTrigHigh =
370 ah->config.ofdm_trig_high;
371 ah->curani->ofdmTrigLow =
372 ah->config.ofdm_trig_low;
373 ah->curani->cckTrigHigh =
374 ah->config.cck_trig_high;
375 ah->curani->cckTrigLow =
376 ah->config.cck_trig_low;
Sujithf1dc5602008-10-29 10:16:30 +0530377 }
378 ath9k_ani_restart(ah);
379 return;
380 }
381
382 if (aniState->noiseImmunityLevel != 0)
383 ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
384 aniState->noiseImmunityLevel);
385 if (aniState->spurImmunityLevel != 0)
386 ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
387 aniState->spurImmunityLevel);
388 if (aniState->ofdmWeakSigDetectOff)
389 ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
390 !aniState->ofdmWeakSigDetectOff);
391 if (aniState->cckWeakSigThreshold)
392 ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
393 aniState->cckWeakSigThreshold);
394 if (aniState->firstepLevel != 0)
395 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
396 aniState->firstepLevel);
Sujithf1dc5602008-10-29 10:16:30 +0530397
Sujith1aa8e842009-08-13 09:34:25 +0530398 ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) &
399 ~ATH9K_RX_FILTER_PHYERR);
400 ath9k_ani_restart(ah);
Sujith7d0d0df2010-04-16 11:53:57 +0530401
402 ENABLE_REGWRITE_BUFFER(ah);
403
Sujith1aa8e842009-08-13 09:34:25 +0530404 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
405 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
Sujith7d0d0df2010-04-16 11:53:57 +0530406
407 REGWRITE_BUFFER_FLUSH(ah);
408 DISABLE_REGWRITE_BUFFER(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530409}
410
Luis R. Rodriguezac0bb762010-06-12 00:33:42 -0400411static void ath9k_hw_ani_monitor_old(struct ath_hw *ah,
412 struct ath9k_channel *chan)
Sujithf1dc5602008-10-29 10:16:30 +0530413{
Sujithf1dc5602008-10-29 10:16:30 +0530414 struct ar5416AniState *aniState;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700415 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530416 int32_t listenTime;
Sujith1aa8e842009-08-13 09:34:25 +0530417 u32 phyCnt1, phyCnt2;
418 u32 ofdmPhyErrCnt, cckPhyErrCnt;
Sujithf1dc5602008-10-29 10:16:30 +0530419
Gabor Juhos99506882009-01-14 20:17:11 +0100420 if (!DO_ANI(ah))
421 return;
422
Sujith2660b812009-02-09 13:27:26 +0530423 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530424
425 listenTime = ath9k_hw_ani_get_listen_time(ah);
426 if (listenTime < 0) {
Sujith2660b812009-02-09 13:27:26 +0530427 ah->stats.ast_ani_lneg++;
Sujithf1dc5602008-10-29 10:16:30 +0530428 ath9k_ani_restart(ah);
429 return;
430 }
431
432 aniState->listenTime += listenTime;
433
Sujith1aa8e842009-08-13 09:34:25 +0530434 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
Sujithf1dc5602008-10-29 10:16:30 +0530435
Sujith1aa8e842009-08-13 09:34:25 +0530436 phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
437 phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
Sujithf1dc5602008-10-29 10:16:30 +0530438
Sujith1aa8e842009-08-13 09:34:25 +0530439 if (phyCnt1 < aniState->ofdmPhyErrBase ||
440 phyCnt2 < aniState->cckPhyErrBase) {
441 if (phyCnt1 < aniState->ofdmPhyErrBase) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700442 ath_print(common, ATH_DBG_ANI,
443 "phyCnt1 0x%x, resetting "
444 "counter value to 0x%x\n",
445 phyCnt1,
446 aniState->ofdmPhyErrBase);
Sujith1aa8e842009-08-13 09:34:25 +0530447 REG_WRITE(ah, AR_PHY_ERR_1,
448 aniState->ofdmPhyErrBase);
449 REG_WRITE(ah, AR_PHY_ERR_MASK_1,
450 AR_PHY_ERR_OFDM_TIMING);
Sujithf1dc5602008-10-29 10:16:30 +0530451 }
Sujith1aa8e842009-08-13 09:34:25 +0530452 if (phyCnt2 < aniState->cckPhyErrBase) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700453 ath_print(common, ATH_DBG_ANI,
454 "phyCnt2 0x%x, resetting "
455 "counter value to 0x%x\n",
456 phyCnt2,
457 aniState->cckPhyErrBase);
Sujith1aa8e842009-08-13 09:34:25 +0530458 REG_WRITE(ah, AR_PHY_ERR_2,
459 aniState->cckPhyErrBase);
460 REG_WRITE(ah, AR_PHY_ERR_MASK_2,
461 AR_PHY_ERR_CCK_TIMING);
462 }
463 return;
Sujithf1dc5602008-10-29 10:16:30 +0530464 }
465
Sujith1aa8e842009-08-13 09:34:25 +0530466 ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase;
467 ah->stats.ast_ani_ofdmerrs +=
468 ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
469 aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
470
471 cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase;
472 ah->stats.ast_ani_cckerrs +=
473 cckPhyErrCnt - aniState->cckPhyErrCount;
474 aniState->cckPhyErrCount = cckPhyErrCnt;
475
Sujith2660b812009-02-09 13:27:26 +0530476 if (aniState->listenTime > 5 * ah->aniperiod) {
Sujithf1dc5602008-10-29 10:16:30 +0530477 if (aniState->ofdmPhyErrCount <= aniState->listenTime *
478 aniState->ofdmTrigLow / 1000 &&
479 aniState->cckPhyErrCount <= aniState->listenTime *
480 aniState->cckTrigLow / 1000)
481 ath9k_hw_ani_lower_immunity(ah);
482 ath9k_ani_restart(ah);
Sujith2660b812009-02-09 13:27:26 +0530483 } else if (aniState->listenTime > ah->aniperiod) {
Sujithf1dc5602008-10-29 10:16:30 +0530484 if (aniState->ofdmPhyErrCount > aniState->listenTime *
485 aniState->ofdmTrigHigh / 1000) {
486 ath9k_hw_ani_ofdm_err_trigger(ah);
487 ath9k_ani_restart(ah);
488 } else if (aniState->cckPhyErrCount >
489 aniState->listenTime * aniState->cckTrigHigh /
490 1000) {
491 ath9k_hw_ani_cck_err_trigger(ah);
492 ath9k_ani_restart(ah);
493 }
494 }
495}
496
Sujithcbe61d82009-02-09 13:27:12 +0530497void ath9k_enable_mib_counters(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530498{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700499 struct ath_common *common = ath9k_hw_common(ah);
500
501 ath_print(common, ATH_DBG_ANI, "Enable MIB counters\n");
Sujithf1dc5602008-10-29 10:16:30 +0530502
Sujithcbe61d82009-02-09 13:27:12 +0530503 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
Sujithf1dc5602008-10-29 10:16:30 +0530504
Sujith7d0d0df2010-04-16 11:53:57 +0530505 ENABLE_REGWRITE_BUFFER(ah);
506
Sujithf1dc5602008-10-29 10:16:30 +0530507 REG_WRITE(ah, AR_FILT_OFDM, 0);
508 REG_WRITE(ah, AR_FILT_CCK, 0);
509 REG_WRITE(ah, AR_MIBC,
510 ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS)
511 & 0x0f);
512 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
513 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
Sujith7d0d0df2010-04-16 11:53:57 +0530514
515 REGWRITE_BUFFER_FLUSH(ah);
516 DISABLE_REGWRITE_BUFFER(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530517}
518
Sujith0fd06c92009-02-12 10:06:51 +0530519/* Freeze the MIB counters, get the stats and then clear them */
Sujithcbe61d82009-02-09 13:27:12 +0530520void ath9k_hw_disable_mib_counters(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530521{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700522 struct ath_common *common = ath9k_hw_common(ah);
523
524 ath_print(common, ATH_DBG_ANI, "Disable MIB counters\n");
525
Sujith0fd06c92009-02-12 10:06:51 +0530526 REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC);
Sujithcbe61d82009-02-09 13:27:12 +0530527 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
Sujith0fd06c92009-02-12 10:06:51 +0530528 REG_WRITE(ah, AR_MIBC, AR_MIBC_CMC);
Sujithf1dc5602008-10-29 10:16:30 +0530529 REG_WRITE(ah, AR_FILT_OFDM, 0);
530 REG_WRITE(ah, AR_FILT_CCK, 0);
531}
Sujith21d51302010-06-01 15:14:18 +0530532EXPORT_SYMBOL(ath9k_hw_disable_mib_counters);
Sujithf1dc5602008-10-29 10:16:30 +0530533
Sujithcbe61d82009-02-09 13:27:12 +0530534u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah,
Sujithf1dc5602008-10-29 10:16:30 +0530535 u32 *rxc_pcnt,
536 u32 *rxf_pcnt,
537 u32 *txf_pcnt)
538{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700539 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530540 static u32 cycles, rx_clear, rx_frame, tx_frame;
541 u32 good = 1;
542
543 u32 rc = REG_READ(ah, AR_RCCNT);
544 u32 rf = REG_READ(ah, AR_RFCNT);
545 u32 tf = REG_READ(ah, AR_TFCNT);
546 u32 cc = REG_READ(ah, AR_CCCNT);
547
548 if (cycles == 0 || cycles > cc) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700549 ath_print(common, ATH_DBG_ANI,
550 "cycle counter wrap. ExtBusy = 0\n");
Sujithf1dc5602008-10-29 10:16:30 +0530551 good = 0;
552 } else {
553 u32 cc_d = cc - cycles;
554 u32 rc_d = rc - rx_clear;
555 u32 rf_d = rf - rx_frame;
556 u32 tf_d = tf - tx_frame;
557
558 if (cc_d != 0) {
559 *rxc_pcnt = rc_d * 100 / cc_d;
560 *rxf_pcnt = rf_d * 100 / cc_d;
561 *txf_pcnt = tf_d * 100 / cc_d;
562 } else {
563 good = 0;
564 }
565 }
566
567 cycles = cc;
568 rx_frame = rf;
569 rx_clear = rc;
570 tx_frame = tf;
571
572 return good;
573}
574
575/*
576 * Process a MIB interrupt. We may potentially be invoked because
577 * any of the MIB counters overflow/trigger so don't assume we're
578 * here because a PHY error counter triggered.
579 */
Luis R. Rodriguezac0bb762010-06-12 00:33:42 -0400580static void ath9k_hw_proc_mib_event_old(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530581{
Sujithf1dc5602008-10-29 10:16:30 +0530582 u32 phyCnt1, phyCnt2;
583
584 /* Reset these counters regardless */
585 REG_WRITE(ah, AR_FILT_OFDM, 0);
586 REG_WRITE(ah, AR_FILT_CCK, 0);
587 if (!(REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING))
588 REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);
589
590 /* Clear the mib counters and save them in the stats */
Sujithcbe61d82009-02-09 13:27:12 +0530591 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
Sujithf1dc5602008-10-29 10:16:30 +0530592
Luis R. Rodriguez6e97f0f2010-06-12 00:33:41 -0400593 if (!DO_ANI(ah)) {
594 /*
595 * We must always clear the interrupt cause by
596 * resetting the phy error regs.
597 */
598 REG_WRITE(ah, AR_PHY_ERR_1, 0);
599 REG_WRITE(ah, AR_PHY_ERR_2, 0);
Sujithf1dc5602008-10-29 10:16:30 +0530600 return;
Luis R. Rodriguez6e97f0f2010-06-12 00:33:41 -0400601 }
Sujithf1dc5602008-10-29 10:16:30 +0530602
603 /* NB: these are not reset-on-read */
604 phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
605 phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
606 if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
607 ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
Sujith2660b812009-02-09 13:27:26 +0530608 struct ar5416AniState *aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530609 u32 ofdmPhyErrCnt, cckPhyErrCnt;
610
611 /* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */
612 ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase;
Sujith2660b812009-02-09 13:27:26 +0530613 ah->stats.ast_ani_ofdmerrs +=
Sujithf1dc5602008-10-29 10:16:30 +0530614 ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
615 aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
616
617 cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase;
Sujith2660b812009-02-09 13:27:26 +0530618 ah->stats.ast_ani_cckerrs +=
Sujithf1dc5602008-10-29 10:16:30 +0530619 cckPhyErrCnt - aniState->cckPhyErrCount;
620 aniState->cckPhyErrCount = cckPhyErrCnt;
621
622 /*
623 * NB: figure out which counter triggered. If both
624 * trigger we'll only deal with one as the processing
625 * clobbers the error counter so the trigger threshold
626 * check will never be true.
627 */
628 if (aniState->ofdmPhyErrCount > aniState->ofdmTrigHigh)
629 ath9k_hw_ani_ofdm_err_trigger(ah);
630 if (aniState->cckPhyErrCount > aniState->cckTrigHigh)
631 ath9k_hw_ani_cck_err_trigger(ah);
632 /* NB: always restart to insure the h/w counters are reset */
633 ath9k_ani_restart(ah);
634 }
635}
636
Sujithcbe61d82009-02-09 13:27:12 +0530637void ath9k_hw_ani_setup(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530638{
Sujithf1dc5602008-10-29 10:16:30 +0530639 int i;
640
641 const int totalSizeDesired[] = { -55, -55, -55, -55, -62 };
642 const int coarseHigh[] = { -14, -14, -14, -14, -12 };
643 const int coarseLow[] = { -64, -64, -64, -64, -70 };
644 const int firpwr[] = { -78, -78, -78, -78, -80 };
645
646 for (i = 0; i < 5; i++) {
Sujith2660b812009-02-09 13:27:26 +0530647 ah->totalSizeDesired[i] = totalSizeDesired[i];
648 ah->coarse_high[i] = coarseHigh[i];
649 ah->coarse_low[i] = coarseLow[i];
650 ah->firpwr[i] = firpwr[i];
Sujithf1dc5602008-10-29 10:16:30 +0530651 }
652}
653
Luis R. Rodriguezf637cfd2009-08-03 12:24:46 -0700654void ath9k_hw_ani_init(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530655{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700656 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530657 int i;
658
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700659 ath_print(common, ATH_DBG_ANI, "Initialize ANI\n");
Sujithf1dc5602008-10-29 10:16:30 +0530660
Sujith2660b812009-02-09 13:27:26 +0530661 memset(ah->ani, 0, sizeof(ah->ani));
662 for (i = 0; i < ARRAY_SIZE(ah->ani); i++) {
663 ah->ani[i].ofdmTrigHigh = ATH9K_ANI_OFDM_TRIG_HIGH;
664 ah->ani[i].ofdmTrigLow = ATH9K_ANI_OFDM_TRIG_LOW;
665 ah->ani[i].cckTrigHigh = ATH9K_ANI_CCK_TRIG_HIGH;
666 ah->ani[i].cckTrigLow = ATH9K_ANI_CCK_TRIG_LOW;
667 ah->ani[i].rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH;
668 ah->ani[i].rssiThrLow = ATH9K_ANI_RSSI_THR_LOW;
669 ah->ani[i].ofdmWeakSigDetectOff =
Sujithf1dc5602008-10-29 10:16:30 +0530670 !ATH9K_ANI_USE_OFDM_WEAK_SIG;
Sujith2660b812009-02-09 13:27:26 +0530671 ah->ani[i].cckWeakSigThreshold =
Sujithf1dc5602008-10-29 10:16:30 +0530672 ATH9K_ANI_CCK_WEAK_SIG_THR;
Sujith2660b812009-02-09 13:27:26 +0530673 ah->ani[i].spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
674 ah->ani[i].firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
Sujith1aa8e842009-08-13 09:34:25 +0530675 ah->ani[i].ofdmPhyErrBase =
676 AR_PHY_COUNTMAX - ATH9K_ANI_OFDM_TRIG_HIGH;
677 ah->ani[i].cckPhyErrBase =
678 AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH;
Sujithf1dc5602008-10-29 10:16:30 +0530679 }
Sujithf1dc5602008-10-29 10:16:30 +0530680
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700681 ath_print(common, ATH_DBG_ANI,
682 "Setting OfdmErrBase = 0x%08x\n",
683 ah->ani[0].ofdmPhyErrBase);
684 ath_print(common, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n",
685 ah->ani[0].cckPhyErrBase);
Sujith1aa8e842009-08-13 09:34:25 +0530686
Sujith7d0d0df2010-04-16 11:53:57 +0530687 ENABLE_REGWRITE_BUFFER(ah);
688
Sujith1aa8e842009-08-13 09:34:25 +0530689 REG_WRITE(ah, AR_PHY_ERR_1, ah->ani[0].ofdmPhyErrBase);
690 REG_WRITE(ah, AR_PHY_ERR_2, ah->ani[0].cckPhyErrBase);
Sujith7d0d0df2010-04-16 11:53:57 +0530691
692 REGWRITE_BUFFER_FLUSH(ah);
693 DISABLE_REGWRITE_BUFFER(ah);
694
Sujith1aa8e842009-08-13 09:34:25 +0530695 ath9k_enable_mib_counters(ah);
696
Sujith2660b812009-02-09 13:27:26 +0530697 ah->aniperiod = ATH9K_ANI_PERIOD;
698 if (ah->config.enable_ani)
699 ah->proc_phyerr |= HAL_PROCESS_ANI;
Sujithf1dc5602008-10-29 10:16:30 +0530700}
Luis R. Rodriguezac0bb762010-06-12 00:33:42 -0400701
702void ath9k_hw_attach_ani_ops_old(struct ath_hw *ah)
703{
704 struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
705 struct ath_hw_ops *ops = ath9k_hw_ops(ah);
706
707 priv_ops->ani_reset = ath9k_ani_reset_old;
708 priv_ops->ani_lower_immunity = ath9k_hw_ani_lower_immunity_old;
709
710 ops->ani_proc_mib_event = ath9k_hw_proc_mib_event_old;
711 ops->ani_monitor = ath9k_hw_ani_monitor_old;
712}