blob: 5a2d867a4e92767366ff3af00be1b7cb275b20ae [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
Sujithcbe61d82009-02-09 13:27:12 +053020static int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah,
Sujithf1dc5602008-10-29 10:16:30 +053021 struct ath9k_channel *chan)
22{
Sujithf1dc5602008-10-29 10:16:30 +053023 int i;
24
Sujith2660b812009-02-09 13:27:26 +053025 for (i = 0; i < ARRAY_SIZE(ah->ani); i++) {
26 if (ah->ani[i].c &&
27 ah->ani[i].c->channel == chan->channel)
Sujithf1dc5602008-10-29 10:16:30 +053028 return i;
Sujith2660b812009-02-09 13:27:26 +053029 if (ah->ani[i].c == NULL) {
30 ah->ani[i].c = chan;
Sujithf1dc5602008-10-29 10:16:30 +053031 return i;
32 }
33 }
34
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -070035 ath_print(ath9k_hw_common(ah), ATH_DBG_ANI,
36 "No more channel states left. Using channel 0\n");
Sujithf1dc5602008-10-29 10:16:30 +053037
38 return 0;
39}
40
Sujithcbe61d82009-02-09 13:27:12 +053041static void ath9k_hw_update_mibstats(struct ath_hw *ah,
Sujithf1dc5602008-10-29 10:16:30 +053042 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
Sujithcbe61d82009-02-09 13:27:12 +053051static void ath9k_ani_restart(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +053052{
Sujithf1dc5602008-10-29 10:16:30 +053053 struct ar5416AniState *aniState;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -070054 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +053055
56 if (!DO_ANI(ah))
57 return;
58
Sujith2660b812009-02-09 13:27:26 +053059 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +053060 aniState->listenTime = 0;
Sujithf1dc5602008-10-29 10:16:30 +053061
Sujith1aa8e842009-08-13 09:34:25 +053062 if (aniState->ofdmTrigHigh > AR_PHY_COUNTMAX) {
63 aniState->ofdmPhyErrBase = 0;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -070064 ath_print(common, ATH_DBG_ANI,
65 "OFDM Trigger is too high for hw counters\n");
Sujith1aa8e842009-08-13 09:34:25 +053066 } else {
67 aniState->ofdmPhyErrBase =
68 AR_PHY_COUNTMAX - aniState->ofdmTrigHigh;
Sujithf1dc5602008-10-29 10:16:30 +053069 }
Sujith1aa8e842009-08-13 09:34:25 +053070 if (aniState->cckTrigHigh > AR_PHY_COUNTMAX) {
71 aniState->cckPhyErrBase = 0;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -070072 ath_print(common, ATH_DBG_ANI,
73 "CCK Trigger is too high for hw counters\n");
Sujith1aa8e842009-08-13 09:34:25 +053074 } else {
75 aniState->cckPhyErrBase =
76 AR_PHY_COUNTMAX - aniState->cckTrigHigh;
77 }
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -070078 ath_print(common, ATH_DBG_ANI,
79 "Writing ofdmbase=%u cckbase=%u\n",
80 aniState->ofdmPhyErrBase,
81 aniState->cckPhyErrBase);
Sujith1aa8e842009-08-13 09:34:25 +053082 REG_WRITE(ah, AR_PHY_ERR_1, aniState->ofdmPhyErrBase);
83 REG_WRITE(ah, AR_PHY_ERR_2, aniState->cckPhyErrBase);
84 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
85 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
86
87 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
88
Sujithf1dc5602008-10-29 10:16:30 +053089 aniState->ofdmPhyErrCount = 0;
90 aniState->cckPhyErrCount = 0;
91}
92
Sujithcbe61d82009-02-09 13:27:12 +053093static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +053094{
Luis R. Rodriguezb002a4a2009-09-13 00:03:27 -070095 struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
Sujithf1dc5602008-10-29 10:16:30 +053096 struct ar5416AniState *aniState;
Sujithf1dc5602008-10-29 10:16:30 +053097 int32_t rssi;
98
99 if (!DO_ANI(ah))
100 return;
101
Sujith2660b812009-02-09 13:27:26 +0530102 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530103
104 if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
105 if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
106 aniState->noiseImmunityLevel + 1)) {
107 return;
108 }
109 }
110
111 if (aniState->spurImmunityLevel < HAL_SPUR_IMMUNE_MAX) {
112 if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
113 aniState->spurImmunityLevel + 1)) {
114 return;
115 }
116 }
117
Sujith2660b812009-02-09 13:27:26 +0530118 if (ah->opmode == NL80211_IFTYPE_AP) {
Sujithf1dc5602008-10-29 10:16:30 +0530119 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
120 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
121 aniState->firstepLevel + 1);
122 }
123 return;
124 }
Sujithcbe61d82009-02-09 13:27:12 +0530125 rssi = BEACON_RSSI(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530126 if (rssi > aniState->rssiThrHigh) {
127 if (!aniState->ofdmWeakSigDetectOff) {
128 if (ath9k_hw_ani_control(ah,
129 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
130 false)) {
131 ath9k_hw_ani_control(ah,
132 ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
133 return;
134 }
135 }
136 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
137 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
138 aniState->firstepLevel + 1);
139 return;
140 }
141 } else if (rssi > aniState->rssiThrLow) {
142 if (aniState->ofdmWeakSigDetectOff)
143 ath9k_hw_ani_control(ah,
144 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
145 true);
146 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
147 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
148 aniState->firstepLevel + 1);
149 return;
150 } else {
Sujithd37b7da2009-09-11 08:30:03 +0530151 if ((conf->channel->band == IEEE80211_BAND_2GHZ) &&
152 !conf_is_ht(conf)) {
Sujithf1dc5602008-10-29 10:16:30 +0530153 if (!aniState->ofdmWeakSigDetectOff)
154 ath9k_hw_ani_control(ah,
155 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
156 false);
157 if (aniState->firstepLevel > 0)
158 ath9k_hw_ani_control(ah,
159 ATH9K_ANI_FIRSTEP_LEVEL, 0);
160 return;
161 }
162 }
163}
164
Sujithcbe61d82009-02-09 13:27:12 +0530165static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530166{
Luis R. Rodriguezb002a4a2009-09-13 00:03:27 -0700167 struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
Sujithf1dc5602008-10-29 10:16:30 +0530168 struct ar5416AniState *aniState;
Sujithf1dc5602008-10-29 10:16:30 +0530169 int32_t rssi;
170
171 if (!DO_ANI(ah))
172 return;
173
Sujith2660b812009-02-09 13:27:26 +0530174 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530175 if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
176 if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
177 aniState->noiseImmunityLevel + 1)) {
178 return;
179 }
180 }
Sujith2660b812009-02-09 13:27:26 +0530181 if (ah->opmode == NL80211_IFTYPE_AP) {
Sujithf1dc5602008-10-29 10:16:30 +0530182 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
183 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
184 aniState->firstepLevel + 1);
185 }
186 return;
187 }
Sujithcbe61d82009-02-09 13:27:12 +0530188 rssi = BEACON_RSSI(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530189 if (rssi > aniState->rssiThrLow) {
190 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
191 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
192 aniState->firstepLevel + 1);
193 } else {
Sujithd37b7da2009-09-11 08:30:03 +0530194 if ((conf->channel->band == IEEE80211_BAND_2GHZ) &&
195 !conf_is_ht(conf)) {
Sujithf1dc5602008-10-29 10:16:30 +0530196 if (aniState->firstepLevel > 0)
197 ath9k_hw_ani_control(ah,
198 ATH9K_ANI_FIRSTEP_LEVEL, 0);
199 }
200 }
201}
202
Sujithcbe61d82009-02-09 13:27:12 +0530203static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530204{
Sujithf1dc5602008-10-29 10:16:30 +0530205 struct ar5416AniState *aniState;
206 int32_t rssi;
207
Sujith2660b812009-02-09 13:27:26 +0530208 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530209
Sujith2660b812009-02-09 13:27:26 +0530210 if (ah->opmode == NL80211_IFTYPE_AP) {
Sujithf1dc5602008-10-29 10:16:30 +0530211 if (aniState->firstepLevel > 0) {
212 if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
213 aniState->firstepLevel - 1))
214 return;
215 }
216 } else {
Sujithcbe61d82009-02-09 13:27:12 +0530217 rssi = BEACON_RSSI(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530218 if (rssi > aniState->rssiThrHigh) {
219 /* XXX: Handle me */
220 } else if (rssi > aniState->rssiThrLow) {
221 if (aniState->ofdmWeakSigDetectOff) {
222 if (ath9k_hw_ani_control(ah,
223 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
224 true) == true)
225 return;
226 }
227 if (aniState->firstepLevel > 0) {
228 if (ath9k_hw_ani_control(ah,
229 ATH9K_ANI_FIRSTEP_LEVEL,
230 aniState->firstepLevel - 1) == true)
231 return;
232 }
233 } else {
234 if (aniState->firstepLevel > 0) {
235 if (ath9k_hw_ani_control(ah,
236 ATH9K_ANI_FIRSTEP_LEVEL,
237 aniState->firstepLevel - 1) == true)
238 return;
239 }
240 }
241 }
242
243 if (aniState->spurImmunityLevel > 0) {
244 if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
245 aniState->spurImmunityLevel - 1))
246 return;
247 }
248
249 if (aniState->noiseImmunityLevel > 0) {
250 ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
251 aniState->noiseImmunityLevel - 1);
252 return;
253 }
254}
255
Sujithcbe61d82009-02-09 13:27:12 +0530256static int32_t ath9k_hw_ani_get_listen_time(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530257{
Sujithf1dc5602008-10-29 10:16:30 +0530258 struct ar5416AniState *aniState;
259 u32 txFrameCount, rxFrameCount, cycleCount;
260 int32_t listenTime;
261
262 txFrameCount = REG_READ(ah, AR_TFCNT);
263 rxFrameCount = REG_READ(ah, AR_RFCNT);
264 cycleCount = REG_READ(ah, AR_CCCNT);
265
Sujith2660b812009-02-09 13:27:26 +0530266 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530267 if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) {
268
269 listenTime = 0;
Sujith2660b812009-02-09 13:27:26 +0530270 ah->stats.ast_ani_lzero++;
Sujithf1dc5602008-10-29 10:16:30 +0530271 } else {
272 int32_t ccdelta = cycleCount - aniState->cycleCount;
273 int32_t rfdelta = rxFrameCount - aniState->rxFrameCount;
274 int32_t tfdelta = txFrameCount - aniState->txFrameCount;
275 listenTime = (ccdelta - rfdelta - tfdelta) / 44000;
276 }
277 aniState->cycleCount = cycleCount;
278 aniState->txFrameCount = txFrameCount;
279 aniState->rxFrameCount = rxFrameCount;
280
281 return listenTime;
282}
283
Sujithcbe61d82009-02-09 13:27:12 +0530284void ath9k_ani_reset(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530285{
Sujithf1dc5602008-10-29 10:16:30 +0530286 struct ar5416AniState *aniState;
Sujith2660b812009-02-09 13:27:26 +0530287 struct ath9k_channel *chan = ah->curchan;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700288 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530289 int index;
290
291 if (!DO_ANI(ah))
292 return;
293
294 index = ath9k_hw_get_ani_channel_idx(ah, chan);
Sujith2660b812009-02-09 13:27:26 +0530295 aniState = &ah->ani[index];
296 ah->curani = aniState;
Sujithf1dc5602008-10-29 10:16:30 +0530297
Sujith2660b812009-02-09 13:27:26 +0530298 if (DO_ANI(ah) && ah->opmode != NL80211_IFTYPE_STATION
299 && ah->opmode != NL80211_IFTYPE_ADHOC) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700300 ath_print(common, ATH_DBG_ANI,
301 "Reset ANI state opmode %u\n", ah->opmode);
Sujith2660b812009-02-09 13:27:26 +0530302 ah->stats.ast_ani_reset++;
Sujithf1dc5602008-10-29 10:16:30 +0530303
Luis R. Rodriguezc66284f2009-07-16 10:17:35 -0700304 if (ah->opmode == NL80211_IFTYPE_AP) {
305 /*
306 * ath9k_hw_ani_control() will only process items set on
307 * ah->ani_function
308 */
309 if (IS_CHAN_2GHZ(chan))
310 ah->ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL |
311 ATH9K_ANI_FIRSTEP_LEVEL);
312 else
313 ah->ani_function = 0;
314 }
315
Sujithf1dc5602008-10-29 10:16:30 +0530316 ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0);
317 ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
318 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0);
319 ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
320 !ATH9K_ANI_USE_OFDM_WEAK_SIG);
321 ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
322 ATH9K_ANI_CCK_WEAK_SIG_THR);
323
324 ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) |
325 ATH9K_RX_FILTER_PHYERR);
326
Sujith2660b812009-02-09 13:27:26 +0530327 if (ah->opmode == NL80211_IFTYPE_AP) {
328 ah->curani->ofdmTrigHigh =
329 ah->config.ofdm_trig_high;
330 ah->curani->ofdmTrigLow =
331 ah->config.ofdm_trig_low;
332 ah->curani->cckTrigHigh =
333 ah->config.cck_trig_high;
334 ah->curani->cckTrigLow =
335 ah->config.cck_trig_low;
Sujithf1dc5602008-10-29 10:16:30 +0530336 }
337 ath9k_ani_restart(ah);
338 return;
339 }
340
341 if (aniState->noiseImmunityLevel != 0)
342 ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
343 aniState->noiseImmunityLevel);
344 if (aniState->spurImmunityLevel != 0)
345 ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
346 aniState->spurImmunityLevel);
347 if (aniState->ofdmWeakSigDetectOff)
348 ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
349 !aniState->ofdmWeakSigDetectOff);
350 if (aniState->cckWeakSigThreshold)
351 ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
352 aniState->cckWeakSigThreshold);
353 if (aniState->firstepLevel != 0)
354 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
355 aniState->firstepLevel);
Sujithf1dc5602008-10-29 10:16:30 +0530356
Sujith1aa8e842009-08-13 09:34:25 +0530357 ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) &
358 ~ATH9K_RX_FILTER_PHYERR);
359 ath9k_ani_restart(ah);
360 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
361 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
Sujithf1dc5602008-10-29 10:16:30 +0530362}
363
Sujithcbe61d82009-02-09 13:27:12 +0530364void ath9k_hw_ani_monitor(struct ath_hw *ah,
Sujithf1dc5602008-10-29 10:16:30 +0530365 struct ath9k_channel *chan)
366{
Sujithf1dc5602008-10-29 10:16:30 +0530367 struct ar5416AniState *aniState;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700368 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530369 int32_t listenTime;
Sujith1aa8e842009-08-13 09:34:25 +0530370 u32 phyCnt1, phyCnt2;
371 u32 ofdmPhyErrCnt, cckPhyErrCnt;
Sujithf1dc5602008-10-29 10:16:30 +0530372
Gabor Juhos99506882009-01-14 20:17:11 +0100373 if (!DO_ANI(ah))
374 return;
375
Sujith2660b812009-02-09 13:27:26 +0530376 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530377
378 listenTime = ath9k_hw_ani_get_listen_time(ah);
379 if (listenTime < 0) {
Sujith2660b812009-02-09 13:27:26 +0530380 ah->stats.ast_ani_lneg++;
Sujithf1dc5602008-10-29 10:16:30 +0530381 ath9k_ani_restart(ah);
382 return;
383 }
384
385 aniState->listenTime += listenTime;
386
Sujith1aa8e842009-08-13 09:34:25 +0530387 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
Sujithf1dc5602008-10-29 10:16:30 +0530388
Sujith1aa8e842009-08-13 09:34:25 +0530389 phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
390 phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
Sujithf1dc5602008-10-29 10:16:30 +0530391
Sujith1aa8e842009-08-13 09:34:25 +0530392 if (phyCnt1 < aniState->ofdmPhyErrBase ||
393 phyCnt2 < aniState->cckPhyErrBase) {
394 if (phyCnt1 < aniState->ofdmPhyErrBase) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700395 ath_print(common, ATH_DBG_ANI,
396 "phyCnt1 0x%x, resetting "
397 "counter value to 0x%x\n",
398 phyCnt1,
399 aniState->ofdmPhyErrBase);
Sujith1aa8e842009-08-13 09:34:25 +0530400 REG_WRITE(ah, AR_PHY_ERR_1,
401 aniState->ofdmPhyErrBase);
402 REG_WRITE(ah, AR_PHY_ERR_MASK_1,
403 AR_PHY_ERR_OFDM_TIMING);
Sujithf1dc5602008-10-29 10:16:30 +0530404 }
Sujith1aa8e842009-08-13 09:34:25 +0530405 if (phyCnt2 < aniState->cckPhyErrBase) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700406 ath_print(common, ATH_DBG_ANI,
407 "phyCnt2 0x%x, resetting "
408 "counter value to 0x%x\n",
409 phyCnt2,
410 aniState->cckPhyErrBase);
Sujith1aa8e842009-08-13 09:34:25 +0530411 REG_WRITE(ah, AR_PHY_ERR_2,
412 aniState->cckPhyErrBase);
413 REG_WRITE(ah, AR_PHY_ERR_MASK_2,
414 AR_PHY_ERR_CCK_TIMING);
415 }
416 return;
Sujithf1dc5602008-10-29 10:16:30 +0530417 }
418
Sujith1aa8e842009-08-13 09:34:25 +0530419 ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase;
420 ah->stats.ast_ani_ofdmerrs +=
421 ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
422 aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
423
424 cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase;
425 ah->stats.ast_ani_cckerrs +=
426 cckPhyErrCnt - aniState->cckPhyErrCount;
427 aniState->cckPhyErrCount = cckPhyErrCnt;
428
Sujith2660b812009-02-09 13:27:26 +0530429 if (aniState->listenTime > 5 * ah->aniperiod) {
Sujithf1dc5602008-10-29 10:16:30 +0530430 if (aniState->ofdmPhyErrCount <= aniState->listenTime *
431 aniState->ofdmTrigLow / 1000 &&
432 aniState->cckPhyErrCount <= aniState->listenTime *
433 aniState->cckTrigLow / 1000)
434 ath9k_hw_ani_lower_immunity(ah);
435 ath9k_ani_restart(ah);
Sujith2660b812009-02-09 13:27:26 +0530436 } else if (aniState->listenTime > ah->aniperiod) {
Sujithf1dc5602008-10-29 10:16:30 +0530437 if (aniState->ofdmPhyErrCount > aniState->listenTime *
438 aniState->ofdmTrigHigh / 1000) {
439 ath9k_hw_ani_ofdm_err_trigger(ah);
440 ath9k_ani_restart(ah);
441 } else if (aniState->cckPhyErrCount >
442 aniState->listenTime * aniState->cckTrigHigh /
443 1000) {
444 ath9k_hw_ani_cck_err_trigger(ah);
445 ath9k_ani_restart(ah);
446 }
447 }
448}
Luis R. Rodriguez7322fd12009-09-23 23:07:00 -0400449EXPORT_SYMBOL(ath9k_hw_ani_monitor);
Sujithf1dc5602008-10-29 10:16:30 +0530450
Sujithcbe61d82009-02-09 13:27:12 +0530451void ath9k_enable_mib_counters(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530452{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700453 struct ath_common *common = ath9k_hw_common(ah);
454
455 ath_print(common, ATH_DBG_ANI, "Enable MIB counters\n");
Sujithf1dc5602008-10-29 10:16:30 +0530456
Sujithcbe61d82009-02-09 13:27:12 +0530457 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
Sujithf1dc5602008-10-29 10:16:30 +0530458
459 REG_WRITE(ah, AR_FILT_OFDM, 0);
460 REG_WRITE(ah, AR_FILT_CCK, 0);
461 REG_WRITE(ah, AR_MIBC,
462 ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS)
463 & 0x0f);
464 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
465 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
466}
467
Sujith0fd06c92009-02-12 10:06:51 +0530468/* Freeze the MIB counters, get the stats and then clear them */
Sujithcbe61d82009-02-09 13:27:12 +0530469void ath9k_hw_disable_mib_counters(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530470{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700471 struct ath_common *common = ath9k_hw_common(ah);
472
473 ath_print(common, ATH_DBG_ANI, "Disable MIB counters\n");
474
Sujith0fd06c92009-02-12 10:06:51 +0530475 REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC);
Sujithcbe61d82009-02-09 13:27:12 +0530476 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
Sujith0fd06c92009-02-12 10:06:51 +0530477 REG_WRITE(ah, AR_MIBC, AR_MIBC_CMC);
Sujithf1dc5602008-10-29 10:16:30 +0530478 REG_WRITE(ah, AR_FILT_OFDM, 0);
479 REG_WRITE(ah, AR_FILT_CCK, 0);
480}
481
Sujithcbe61d82009-02-09 13:27:12 +0530482u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah,
Sujithf1dc5602008-10-29 10:16:30 +0530483 u32 *rxc_pcnt,
484 u32 *rxf_pcnt,
485 u32 *txf_pcnt)
486{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700487 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530488 static u32 cycles, rx_clear, rx_frame, tx_frame;
489 u32 good = 1;
490
491 u32 rc = REG_READ(ah, AR_RCCNT);
492 u32 rf = REG_READ(ah, AR_RFCNT);
493 u32 tf = REG_READ(ah, AR_TFCNT);
494 u32 cc = REG_READ(ah, AR_CCCNT);
495
496 if (cycles == 0 || cycles > cc) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700497 ath_print(common, ATH_DBG_ANI,
498 "cycle counter wrap. ExtBusy = 0\n");
Sujithf1dc5602008-10-29 10:16:30 +0530499 good = 0;
500 } else {
501 u32 cc_d = cc - cycles;
502 u32 rc_d = rc - rx_clear;
503 u32 rf_d = rf - rx_frame;
504 u32 tf_d = tf - tx_frame;
505
506 if (cc_d != 0) {
507 *rxc_pcnt = rc_d * 100 / cc_d;
508 *rxf_pcnt = rf_d * 100 / cc_d;
509 *txf_pcnt = tf_d * 100 / cc_d;
510 } else {
511 good = 0;
512 }
513 }
514
515 cycles = cc;
516 rx_frame = rf;
517 rx_clear = rc;
518 tx_frame = tf;
519
520 return good;
521}
522
523/*
524 * Process a MIB interrupt. We may potentially be invoked because
525 * any of the MIB counters overflow/trigger so don't assume we're
526 * here because a PHY error counter triggered.
527 */
Vasanthakumar Thiagarajan22e66a42009-08-19 16:23:40 +0530528void ath9k_hw_procmibevent(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530529{
Sujithf1dc5602008-10-29 10:16:30 +0530530 u32 phyCnt1, phyCnt2;
531
532 /* Reset these counters regardless */
533 REG_WRITE(ah, AR_FILT_OFDM, 0);
534 REG_WRITE(ah, AR_FILT_CCK, 0);
535 if (!(REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING))
536 REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);
537
538 /* Clear the mib counters and save them in the stats */
Sujithcbe61d82009-02-09 13:27:12 +0530539 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
Sujithf1dc5602008-10-29 10:16:30 +0530540
541 if (!DO_ANI(ah))
542 return;
543
544 /* NB: these are not reset-on-read */
545 phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
546 phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
547 if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
548 ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
Sujith2660b812009-02-09 13:27:26 +0530549 struct ar5416AniState *aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530550 u32 ofdmPhyErrCnt, cckPhyErrCnt;
551
552 /* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */
553 ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase;
Sujith2660b812009-02-09 13:27:26 +0530554 ah->stats.ast_ani_ofdmerrs +=
Sujithf1dc5602008-10-29 10:16:30 +0530555 ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
556 aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
557
558 cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase;
Sujith2660b812009-02-09 13:27:26 +0530559 ah->stats.ast_ani_cckerrs +=
Sujithf1dc5602008-10-29 10:16:30 +0530560 cckPhyErrCnt - aniState->cckPhyErrCount;
561 aniState->cckPhyErrCount = cckPhyErrCnt;
562
563 /*
564 * NB: figure out which counter triggered. If both
565 * trigger we'll only deal with one as the processing
566 * clobbers the error counter so the trigger threshold
567 * check will never be true.
568 */
569 if (aniState->ofdmPhyErrCount > aniState->ofdmTrigHigh)
570 ath9k_hw_ani_ofdm_err_trigger(ah);
571 if (aniState->cckPhyErrCount > aniState->cckTrigHigh)
572 ath9k_hw_ani_cck_err_trigger(ah);
573 /* NB: always restart to insure the h/w counters are reset */
574 ath9k_ani_restart(ah);
575 }
576}
Luis R. Rodriguez7322fd12009-09-23 23:07:00 -0400577EXPORT_SYMBOL(ath9k_hw_procmibevent);
Sujithf1dc5602008-10-29 10:16:30 +0530578
Sujithcbe61d82009-02-09 13:27:12 +0530579void ath9k_hw_ani_setup(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530580{
Sujithf1dc5602008-10-29 10:16:30 +0530581 int i;
582
583 const int totalSizeDesired[] = { -55, -55, -55, -55, -62 };
584 const int coarseHigh[] = { -14, -14, -14, -14, -12 };
585 const int coarseLow[] = { -64, -64, -64, -64, -70 };
586 const int firpwr[] = { -78, -78, -78, -78, -80 };
587
588 for (i = 0; i < 5; i++) {
Sujith2660b812009-02-09 13:27:26 +0530589 ah->totalSizeDesired[i] = totalSizeDesired[i];
590 ah->coarse_high[i] = coarseHigh[i];
591 ah->coarse_low[i] = coarseLow[i];
592 ah->firpwr[i] = firpwr[i];
Sujithf1dc5602008-10-29 10:16:30 +0530593 }
594}
595
Luis R. Rodriguezf637cfd2009-08-03 12:24:46 -0700596void ath9k_hw_ani_init(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530597{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700598 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530599 int i;
600
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700601 ath_print(common, ATH_DBG_ANI, "Initialize ANI\n");
Sujithf1dc5602008-10-29 10:16:30 +0530602
Sujith2660b812009-02-09 13:27:26 +0530603 memset(ah->ani, 0, sizeof(ah->ani));
604 for (i = 0; i < ARRAY_SIZE(ah->ani); i++) {
605 ah->ani[i].ofdmTrigHigh = ATH9K_ANI_OFDM_TRIG_HIGH;
606 ah->ani[i].ofdmTrigLow = ATH9K_ANI_OFDM_TRIG_LOW;
607 ah->ani[i].cckTrigHigh = ATH9K_ANI_CCK_TRIG_HIGH;
608 ah->ani[i].cckTrigLow = ATH9K_ANI_CCK_TRIG_LOW;
609 ah->ani[i].rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH;
610 ah->ani[i].rssiThrLow = ATH9K_ANI_RSSI_THR_LOW;
611 ah->ani[i].ofdmWeakSigDetectOff =
Sujithf1dc5602008-10-29 10:16:30 +0530612 !ATH9K_ANI_USE_OFDM_WEAK_SIG;
Sujith2660b812009-02-09 13:27:26 +0530613 ah->ani[i].cckWeakSigThreshold =
Sujithf1dc5602008-10-29 10:16:30 +0530614 ATH9K_ANI_CCK_WEAK_SIG_THR;
Sujith2660b812009-02-09 13:27:26 +0530615 ah->ani[i].spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
616 ah->ani[i].firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
Sujith1aa8e842009-08-13 09:34:25 +0530617 ah->ani[i].ofdmPhyErrBase =
618 AR_PHY_COUNTMAX - ATH9K_ANI_OFDM_TRIG_HIGH;
619 ah->ani[i].cckPhyErrBase =
620 AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH;
Sujithf1dc5602008-10-29 10:16:30 +0530621 }
Sujithf1dc5602008-10-29 10:16:30 +0530622
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700623 ath_print(common, ATH_DBG_ANI,
624 "Setting OfdmErrBase = 0x%08x\n",
625 ah->ani[0].ofdmPhyErrBase);
626 ath_print(common, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n",
627 ah->ani[0].cckPhyErrBase);
Sujith1aa8e842009-08-13 09:34:25 +0530628
629 REG_WRITE(ah, AR_PHY_ERR_1, ah->ani[0].ofdmPhyErrBase);
630 REG_WRITE(ah, AR_PHY_ERR_2, ah->ani[0].cckPhyErrBase);
631 ath9k_enable_mib_counters(ah);
632
Sujith2660b812009-02-09 13:27:26 +0530633 ah->aniperiod = ATH9K_ANI_PERIOD;
634 if (ah->config.enable_ani)
635 ah->proc_phyerr |= HAL_PROCESS_ANI;
Sujithf1dc5602008-10-29 10:16:30 +0530636}
637
Luis R. Rodrigueze70c0cf2009-08-03 12:24:51 -0700638void ath9k_hw_ani_disable(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530639{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700640 ath_print(ath9k_hw_common(ah), ATH_DBG_ANI, "Disabling ANI\n");
Sujithf1dc5602008-10-29 10:16:30 +0530641
Sujith1aa8e842009-08-13 09:34:25 +0530642 ath9k_hw_disable_mib_counters(ah);
643 REG_WRITE(ah, AR_PHY_ERR_1, 0);
644 REG_WRITE(ah, AR_PHY_ERR_2, 0);
Sujithf1dc5602008-10-29 10:16:30 +0530645}