blob: e879055c058f3fda7a0b5243f9d9e9aee4f09528 [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);
Sujith7d0d0df2010-04-16 11:53:57 +053082
83 ENABLE_REGWRITE_BUFFER(ah);
84
Sujith1aa8e842009-08-13 09:34:25 +053085 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
Sujith7d0d0df2010-04-16 11:53:57 +053090 REGWRITE_BUFFER_FLUSH(ah);
91 DISABLE_REGWRITE_BUFFER(ah);
92
Sujith1aa8e842009-08-13 09:34:25 +053093 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
94
Sujithf1dc5602008-10-29 10:16:30 +053095 aniState->ofdmPhyErrCount = 0;
96 aniState->cckPhyErrCount = 0;
97}
98
Sujithcbe61d82009-02-09 13:27:12 +053099static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530100{
Luis R. Rodriguezb002a4a2009-09-13 00:03:27 -0700101 struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
Sujithf1dc5602008-10-29 10:16:30 +0530102 struct ar5416AniState *aniState;
Sujithf1dc5602008-10-29 10:16:30 +0530103 int32_t rssi;
104
105 if (!DO_ANI(ah))
106 return;
107
Sujith2660b812009-02-09 13:27:26 +0530108 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530109
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
Sujith2660b812009-02-09 13:27:26 +0530124 if (ah->opmode == NL80211_IFTYPE_AP) {
Sujithf1dc5602008-10-29 10:16:30 +0530125 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 }
Sujithcbe61d82009-02-09 13:27:12 +0530131 rssi = BEACON_RSSI(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530132 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 {
Sujithd37b7da2009-09-11 08:30:03 +0530157 if ((conf->channel->band == IEEE80211_BAND_2GHZ) &&
158 !conf_is_ht(conf)) {
Sujithf1dc5602008-10-29 10:16:30 +0530159 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
Sujithcbe61d82009-02-09 13:27:12 +0530171static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530172{
Luis R. Rodriguezb002a4a2009-09-13 00:03:27 -0700173 struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
Sujithf1dc5602008-10-29 10:16:30 +0530174 struct ar5416AniState *aniState;
Sujithf1dc5602008-10-29 10:16:30 +0530175 int32_t rssi;
176
177 if (!DO_ANI(ah))
178 return;
179
Sujith2660b812009-02-09 13:27:26 +0530180 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530181 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 }
Sujith2660b812009-02-09 13:27:26 +0530187 if (ah->opmode == NL80211_IFTYPE_AP) {
Sujithf1dc5602008-10-29 10:16:30 +0530188 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 }
Sujithcbe61d82009-02-09 13:27:12 +0530194 rssi = BEACON_RSSI(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530195 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 {
Sujithd37b7da2009-09-11 08:30:03 +0530200 if ((conf->channel->band == IEEE80211_BAND_2GHZ) &&
201 !conf_is_ht(conf)) {
Sujithf1dc5602008-10-29 10:16:30 +0530202 if (aniState->firstepLevel > 0)
203 ath9k_hw_ani_control(ah,
204 ATH9K_ANI_FIRSTEP_LEVEL, 0);
205 }
206 }
207}
208
Sujithcbe61d82009-02-09 13:27:12 +0530209static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530210{
Sujithf1dc5602008-10-29 10:16:30 +0530211 struct ar5416AniState *aniState;
212 int32_t rssi;
213
Sujith2660b812009-02-09 13:27:26 +0530214 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530215
Sujith2660b812009-02-09 13:27:26 +0530216 if (ah->opmode == NL80211_IFTYPE_AP) {
Sujithf1dc5602008-10-29 10:16:30 +0530217 if (aniState->firstepLevel > 0) {
218 if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
219 aniState->firstepLevel - 1))
220 return;
221 }
222 } else {
Sujithcbe61d82009-02-09 13:27:12 +0530223 rssi = BEACON_RSSI(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530224 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
Luis R. Rodriguez37e5bf62010-06-12 00:33:40 -0400262static u8 ath9k_hw_chan_2_clockrate_mhz(struct ath_hw *ah)
263{
264 struct ath9k_channel *chan = ah->curchan;
265 struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
266 u8 clockrate; /* in MHz */
267
268 if (!ah->curchan) /* should really check for CCK instead */
269 clockrate = ATH9K_CLOCK_RATE_CCK;
270 else if (conf->channel->band == IEEE80211_BAND_2GHZ)
271 clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM;
272 else if (IS_CHAN_A_FAST_CLOCK(ah, chan))
273 clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM;
274 else
275 clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM;
276
277 if (conf_is_ht40(conf))
278 return clockrate * 2;
279
280 return clockrate * 2;
281}
282
Sujithcbe61d82009-02-09 13:27:12 +0530283static int32_t ath9k_hw_ani_get_listen_time(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530284{
Sujithf1dc5602008-10-29 10:16:30 +0530285 struct ar5416AniState *aniState;
286 u32 txFrameCount, rxFrameCount, cycleCount;
287 int32_t listenTime;
288
289 txFrameCount = REG_READ(ah, AR_TFCNT);
290 rxFrameCount = REG_READ(ah, AR_RFCNT);
291 cycleCount = REG_READ(ah, AR_CCCNT);
292
Sujith2660b812009-02-09 13:27:26 +0530293 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530294 if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) {
295
296 listenTime = 0;
Sujith2660b812009-02-09 13:27:26 +0530297 ah->stats.ast_ani_lzero++;
Sujithf1dc5602008-10-29 10:16:30 +0530298 } else {
299 int32_t ccdelta = cycleCount - aniState->cycleCount;
300 int32_t rfdelta = rxFrameCount - aniState->rxFrameCount;
301 int32_t tfdelta = txFrameCount - aniState->txFrameCount;
Luis R. Rodriguez37e5bf62010-06-12 00:33:40 -0400302 int32_t clock_rate = ath9k_hw_chan_2_clockrate_mhz(ah) * 1000;;
303
304 /*
305 * convert HW counter values to ms using mode
306 * specifix clock rate
307 */
308 clock_rate = ath9k_hw_chan_2_clockrate_mhz(ah) * 1000;;
309
310 listenTime = (ccdelta - rfdelta - tfdelta) / clock_rate;
Sujithf1dc5602008-10-29 10:16:30 +0530311 }
312 aniState->cycleCount = cycleCount;
313 aniState->txFrameCount = txFrameCount;
314 aniState->rxFrameCount = rxFrameCount;
315
316 return listenTime;
317}
318
Sujithcbe61d82009-02-09 13:27:12 +0530319void ath9k_ani_reset(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530320{
Sujithf1dc5602008-10-29 10:16:30 +0530321 struct ar5416AniState *aniState;
Sujith2660b812009-02-09 13:27:26 +0530322 struct ath9k_channel *chan = ah->curchan;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700323 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530324 int index;
325
326 if (!DO_ANI(ah))
327 return;
328
329 index = ath9k_hw_get_ani_channel_idx(ah, chan);
Sujith2660b812009-02-09 13:27:26 +0530330 aniState = &ah->ani[index];
331 ah->curani = aniState;
Sujithf1dc5602008-10-29 10:16:30 +0530332
Sujith2660b812009-02-09 13:27:26 +0530333 if (DO_ANI(ah) && ah->opmode != NL80211_IFTYPE_STATION
334 && ah->opmode != NL80211_IFTYPE_ADHOC) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700335 ath_print(common, ATH_DBG_ANI,
336 "Reset ANI state opmode %u\n", ah->opmode);
Sujith2660b812009-02-09 13:27:26 +0530337 ah->stats.ast_ani_reset++;
Sujithf1dc5602008-10-29 10:16:30 +0530338
Luis R. Rodriguezc66284f2009-07-16 10:17:35 -0700339 if (ah->opmode == NL80211_IFTYPE_AP) {
340 /*
341 * ath9k_hw_ani_control() will only process items set on
342 * ah->ani_function
343 */
344 if (IS_CHAN_2GHZ(chan))
345 ah->ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL |
346 ATH9K_ANI_FIRSTEP_LEVEL);
347 else
348 ah->ani_function = 0;
349 }
350
Sujithf1dc5602008-10-29 10:16:30 +0530351 ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0);
352 ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
353 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0);
354 ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
355 !ATH9K_ANI_USE_OFDM_WEAK_SIG);
356 ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
357 ATH9K_ANI_CCK_WEAK_SIG_THR);
358
359 ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) |
360 ATH9K_RX_FILTER_PHYERR);
361
Sujith2660b812009-02-09 13:27:26 +0530362 if (ah->opmode == NL80211_IFTYPE_AP) {
363 ah->curani->ofdmTrigHigh =
364 ah->config.ofdm_trig_high;
365 ah->curani->ofdmTrigLow =
366 ah->config.ofdm_trig_low;
367 ah->curani->cckTrigHigh =
368 ah->config.cck_trig_high;
369 ah->curani->cckTrigLow =
370 ah->config.cck_trig_low;
Sujithf1dc5602008-10-29 10:16:30 +0530371 }
372 ath9k_ani_restart(ah);
373 return;
374 }
375
376 if (aniState->noiseImmunityLevel != 0)
377 ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
378 aniState->noiseImmunityLevel);
379 if (aniState->spurImmunityLevel != 0)
380 ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
381 aniState->spurImmunityLevel);
382 if (aniState->ofdmWeakSigDetectOff)
383 ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
384 !aniState->ofdmWeakSigDetectOff);
385 if (aniState->cckWeakSigThreshold)
386 ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
387 aniState->cckWeakSigThreshold);
388 if (aniState->firstepLevel != 0)
389 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
390 aniState->firstepLevel);
Sujithf1dc5602008-10-29 10:16:30 +0530391
Sujith1aa8e842009-08-13 09:34:25 +0530392 ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) &
393 ~ATH9K_RX_FILTER_PHYERR);
394 ath9k_ani_restart(ah);
Sujith7d0d0df2010-04-16 11:53:57 +0530395
396 ENABLE_REGWRITE_BUFFER(ah);
397
Sujith1aa8e842009-08-13 09:34:25 +0530398 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
399 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
Sujith7d0d0df2010-04-16 11:53:57 +0530400
401 REGWRITE_BUFFER_FLUSH(ah);
402 DISABLE_REGWRITE_BUFFER(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530403}
404
Sujithcbe61d82009-02-09 13:27:12 +0530405void ath9k_hw_ani_monitor(struct ath_hw *ah,
Sujithf1dc5602008-10-29 10:16:30 +0530406 struct ath9k_channel *chan)
407{
Sujithf1dc5602008-10-29 10:16:30 +0530408 struct ar5416AniState *aniState;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700409 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530410 int32_t listenTime;
Sujith1aa8e842009-08-13 09:34:25 +0530411 u32 phyCnt1, phyCnt2;
412 u32 ofdmPhyErrCnt, cckPhyErrCnt;
Sujithf1dc5602008-10-29 10:16:30 +0530413
Gabor Juhos99506882009-01-14 20:17:11 +0100414 if (!DO_ANI(ah))
415 return;
416
Sujith2660b812009-02-09 13:27:26 +0530417 aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530418
419 listenTime = ath9k_hw_ani_get_listen_time(ah);
420 if (listenTime < 0) {
Sujith2660b812009-02-09 13:27:26 +0530421 ah->stats.ast_ani_lneg++;
Sujithf1dc5602008-10-29 10:16:30 +0530422 ath9k_ani_restart(ah);
423 return;
424 }
425
426 aniState->listenTime += listenTime;
427
Sujith1aa8e842009-08-13 09:34:25 +0530428 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
Sujithf1dc5602008-10-29 10:16:30 +0530429
Sujith1aa8e842009-08-13 09:34:25 +0530430 phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
431 phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
Sujithf1dc5602008-10-29 10:16:30 +0530432
Sujith1aa8e842009-08-13 09:34:25 +0530433 if (phyCnt1 < aniState->ofdmPhyErrBase ||
434 phyCnt2 < aniState->cckPhyErrBase) {
435 if (phyCnt1 < aniState->ofdmPhyErrBase) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700436 ath_print(common, ATH_DBG_ANI,
437 "phyCnt1 0x%x, resetting "
438 "counter value to 0x%x\n",
439 phyCnt1,
440 aniState->ofdmPhyErrBase);
Sujith1aa8e842009-08-13 09:34:25 +0530441 REG_WRITE(ah, AR_PHY_ERR_1,
442 aniState->ofdmPhyErrBase);
443 REG_WRITE(ah, AR_PHY_ERR_MASK_1,
444 AR_PHY_ERR_OFDM_TIMING);
Sujithf1dc5602008-10-29 10:16:30 +0530445 }
Sujith1aa8e842009-08-13 09:34:25 +0530446 if (phyCnt2 < aniState->cckPhyErrBase) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700447 ath_print(common, ATH_DBG_ANI,
448 "phyCnt2 0x%x, resetting "
449 "counter value to 0x%x\n",
450 phyCnt2,
451 aniState->cckPhyErrBase);
Sujith1aa8e842009-08-13 09:34:25 +0530452 REG_WRITE(ah, AR_PHY_ERR_2,
453 aniState->cckPhyErrBase);
454 REG_WRITE(ah, AR_PHY_ERR_MASK_2,
455 AR_PHY_ERR_CCK_TIMING);
456 }
457 return;
Sujithf1dc5602008-10-29 10:16:30 +0530458 }
459
Sujith1aa8e842009-08-13 09:34:25 +0530460 ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase;
461 ah->stats.ast_ani_ofdmerrs +=
462 ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
463 aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
464
465 cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase;
466 ah->stats.ast_ani_cckerrs +=
467 cckPhyErrCnt - aniState->cckPhyErrCount;
468 aniState->cckPhyErrCount = cckPhyErrCnt;
469
Sujith2660b812009-02-09 13:27:26 +0530470 if (aniState->listenTime > 5 * ah->aniperiod) {
Sujithf1dc5602008-10-29 10:16:30 +0530471 if (aniState->ofdmPhyErrCount <= aniState->listenTime *
472 aniState->ofdmTrigLow / 1000 &&
473 aniState->cckPhyErrCount <= aniState->listenTime *
474 aniState->cckTrigLow / 1000)
475 ath9k_hw_ani_lower_immunity(ah);
476 ath9k_ani_restart(ah);
Sujith2660b812009-02-09 13:27:26 +0530477 } else if (aniState->listenTime > ah->aniperiod) {
Sujithf1dc5602008-10-29 10:16:30 +0530478 if (aniState->ofdmPhyErrCount > aniState->listenTime *
479 aniState->ofdmTrigHigh / 1000) {
480 ath9k_hw_ani_ofdm_err_trigger(ah);
481 ath9k_ani_restart(ah);
482 } else if (aniState->cckPhyErrCount >
483 aniState->listenTime * aniState->cckTrigHigh /
484 1000) {
485 ath9k_hw_ani_cck_err_trigger(ah);
486 ath9k_ani_restart(ah);
487 }
488 }
489}
Luis R. Rodriguez7322fd12009-09-23 23:07:00 -0400490EXPORT_SYMBOL(ath9k_hw_ani_monitor);
Sujithf1dc5602008-10-29 10:16:30 +0530491
Sujithcbe61d82009-02-09 13:27:12 +0530492void ath9k_enable_mib_counters(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530493{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700494 struct ath_common *common = ath9k_hw_common(ah);
495
496 ath_print(common, ATH_DBG_ANI, "Enable MIB counters\n");
Sujithf1dc5602008-10-29 10:16:30 +0530497
Sujithcbe61d82009-02-09 13:27:12 +0530498 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
Sujithf1dc5602008-10-29 10:16:30 +0530499
Sujith7d0d0df2010-04-16 11:53:57 +0530500 ENABLE_REGWRITE_BUFFER(ah);
501
Sujithf1dc5602008-10-29 10:16:30 +0530502 REG_WRITE(ah, AR_FILT_OFDM, 0);
503 REG_WRITE(ah, AR_FILT_CCK, 0);
504 REG_WRITE(ah, AR_MIBC,
505 ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS)
506 & 0x0f);
507 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
508 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
Sujith7d0d0df2010-04-16 11:53:57 +0530509
510 REGWRITE_BUFFER_FLUSH(ah);
511 DISABLE_REGWRITE_BUFFER(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530512}
513
Sujith0fd06c92009-02-12 10:06:51 +0530514/* Freeze the MIB counters, get the stats and then clear them */
Sujithcbe61d82009-02-09 13:27:12 +0530515void ath9k_hw_disable_mib_counters(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530516{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700517 struct ath_common *common = ath9k_hw_common(ah);
518
519 ath_print(common, ATH_DBG_ANI, "Disable MIB counters\n");
520
Sujith0fd06c92009-02-12 10:06:51 +0530521 REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC);
Sujithcbe61d82009-02-09 13:27:12 +0530522 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
Sujith0fd06c92009-02-12 10:06:51 +0530523 REG_WRITE(ah, AR_MIBC, AR_MIBC_CMC);
Sujithf1dc5602008-10-29 10:16:30 +0530524 REG_WRITE(ah, AR_FILT_OFDM, 0);
525 REG_WRITE(ah, AR_FILT_CCK, 0);
526}
Sujith21d51302010-06-01 15:14:18 +0530527EXPORT_SYMBOL(ath9k_hw_disable_mib_counters);
Sujithf1dc5602008-10-29 10:16:30 +0530528
Sujithcbe61d82009-02-09 13:27:12 +0530529u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah,
Sujithf1dc5602008-10-29 10:16:30 +0530530 u32 *rxc_pcnt,
531 u32 *rxf_pcnt,
532 u32 *txf_pcnt)
533{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700534 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530535 static u32 cycles, rx_clear, rx_frame, tx_frame;
536 u32 good = 1;
537
538 u32 rc = REG_READ(ah, AR_RCCNT);
539 u32 rf = REG_READ(ah, AR_RFCNT);
540 u32 tf = REG_READ(ah, AR_TFCNT);
541 u32 cc = REG_READ(ah, AR_CCCNT);
542
543 if (cycles == 0 || cycles > cc) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700544 ath_print(common, ATH_DBG_ANI,
545 "cycle counter wrap. ExtBusy = 0\n");
Sujithf1dc5602008-10-29 10:16:30 +0530546 good = 0;
547 } else {
548 u32 cc_d = cc - cycles;
549 u32 rc_d = rc - rx_clear;
550 u32 rf_d = rf - rx_frame;
551 u32 tf_d = tf - tx_frame;
552
553 if (cc_d != 0) {
554 *rxc_pcnt = rc_d * 100 / cc_d;
555 *rxf_pcnt = rf_d * 100 / cc_d;
556 *txf_pcnt = tf_d * 100 / cc_d;
557 } else {
558 good = 0;
559 }
560 }
561
562 cycles = cc;
563 rx_frame = rf;
564 rx_clear = rc;
565 tx_frame = tf;
566
567 return good;
568}
569
570/*
571 * Process a MIB interrupt. We may potentially be invoked because
572 * any of the MIB counters overflow/trigger so don't assume we're
573 * here because a PHY error counter triggered.
574 */
Vasanthakumar Thiagarajan22e66a42009-08-19 16:23:40 +0530575void ath9k_hw_procmibevent(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530576{
Sujithf1dc5602008-10-29 10:16:30 +0530577 u32 phyCnt1, phyCnt2;
578
579 /* Reset these counters regardless */
580 REG_WRITE(ah, AR_FILT_OFDM, 0);
581 REG_WRITE(ah, AR_FILT_CCK, 0);
582 if (!(REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING))
583 REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);
584
585 /* Clear the mib counters and save them in the stats */
Sujithcbe61d82009-02-09 13:27:12 +0530586 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
Sujithf1dc5602008-10-29 10:16:30 +0530587
588 if (!DO_ANI(ah))
589 return;
590
591 /* NB: these are not reset-on-read */
592 phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
593 phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
594 if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
595 ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
Sujith2660b812009-02-09 13:27:26 +0530596 struct ar5416AniState *aniState = ah->curani;
Sujithf1dc5602008-10-29 10:16:30 +0530597 u32 ofdmPhyErrCnt, cckPhyErrCnt;
598
599 /* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */
600 ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase;
Sujith2660b812009-02-09 13:27:26 +0530601 ah->stats.ast_ani_ofdmerrs +=
Sujithf1dc5602008-10-29 10:16:30 +0530602 ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
603 aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
604
605 cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase;
Sujith2660b812009-02-09 13:27:26 +0530606 ah->stats.ast_ani_cckerrs +=
Sujithf1dc5602008-10-29 10:16:30 +0530607 cckPhyErrCnt - aniState->cckPhyErrCount;
608 aniState->cckPhyErrCount = cckPhyErrCnt;
609
610 /*
611 * NB: figure out which counter triggered. If both
612 * trigger we'll only deal with one as the processing
613 * clobbers the error counter so the trigger threshold
614 * check will never be true.
615 */
616 if (aniState->ofdmPhyErrCount > aniState->ofdmTrigHigh)
617 ath9k_hw_ani_ofdm_err_trigger(ah);
618 if (aniState->cckPhyErrCount > aniState->cckTrigHigh)
619 ath9k_hw_ani_cck_err_trigger(ah);
620 /* NB: always restart to insure the h/w counters are reset */
621 ath9k_ani_restart(ah);
622 }
623}
Luis R. Rodriguez7322fd12009-09-23 23:07:00 -0400624EXPORT_SYMBOL(ath9k_hw_procmibevent);
Sujithf1dc5602008-10-29 10:16:30 +0530625
Sujithcbe61d82009-02-09 13:27:12 +0530626void ath9k_hw_ani_setup(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530627{
Sujithf1dc5602008-10-29 10:16:30 +0530628 int i;
629
630 const int totalSizeDesired[] = { -55, -55, -55, -55, -62 };
631 const int coarseHigh[] = { -14, -14, -14, -14, -12 };
632 const int coarseLow[] = { -64, -64, -64, -64, -70 };
633 const int firpwr[] = { -78, -78, -78, -78, -80 };
634
635 for (i = 0; i < 5; i++) {
Sujith2660b812009-02-09 13:27:26 +0530636 ah->totalSizeDesired[i] = totalSizeDesired[i];
637 ah->coarse_high[i] = coarseHigh[i];
638 ah->coarse_low[i] = coarseLow[i];
639 ah->firpwr[i] = firpwr[i];
Sujithf1dc5602008-10-29 10:16:30 +0530640 }
641}
642
Luis R. Rodriguezf637cfd2009-08-03 12:24:46 -0700643void ath9k_hw_ani_init(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530644{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700645 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530646 int i;
647
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700648 ath_print(common, ATH_DBG_ANI, "Initialize ANI\n");
Sujithf1dc5602008-10-29 10:16:30 +0530649
Sujith2660b812009-02-09 13:27:26 +0530650 memset(ah->ani, 0, sizeof(ah->ani));
651 for (i = 0; i < ARRAY_SIZE(ah->ani); i++) {
652 ah->ani[i].ofdmTrigHigh = ATH9K_ANI_OFDM_TRIG_HIGH;
653 ah->ani[i].ofdmTrigLow = ATH9K_ANI_OFDM_TRIG_LOW;
654 ah->ani[i].cckTrigHigh = ATH9K_ANI_CCK_TRIG_HIGH;
655 ah->ani[i].cckTrigLow = ATH9K_ANI_CCK_TRIG_LOW;
656 ah->ani[i].rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH;
657 ah->ani[i].rssiThrLow = ATH9K_ANI_RSSI_THR_LOW;
658 ah->ani[i].ofdmWeakSigDetectOff =
Sujithf1dc5602008-10-29 10:16:30 +0530659 !ATH9K_ANI_USE_OFDM_WEAK_SIG;
Sujith2660b812009-02-09 13:27:26 +0530660 ah->ani[i].cckWeakSigThreshold =
Sujithf1dc5602008-10-29 10:16:30 +0530661 ATH9K_ANI_CCK_WEAK_SIG_THR;
Sujith2660b812009-02-09 13:27:26 +0530662 ah->ani[i].spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
663 ah->ani[i].firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
Sujith1aa8e842009-08-13 09:34:25 +0530664 ah->ani[i].ofdmPhyErrBase =
665 AR_PHY_COUNTMAX - ATH9K_ANI_OFDM_TRIG_HIGH;
666 ah->ani[i].cckPhyErrBase =
667 AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH;
Sujithf1dc5602008-10-29 10:16:30 +0530668 }
Sujithf1dc5602008-10-29 10:16:30 +0530669
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700670 ath_print(common, ATH_DBG_ANI,
671 "Setting OfdmErrBase = 0x%08x\n",
672 ah->ani[0].ofdmPhyErrBase);
673 ath_print(common, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n",
674 ah->ani[0].cckPhyErrBase);
Sujith1aa8e842009-08-13 09:34:25 +0530675
Sujith7d0d0df2010-04-16 11:53:57 +0530676 ENABLE_REGWRITE_BUFFER(ah);
677
Sujith1aa8e842009-08-13 09:34:25 +0530678 REG_WRITE(ah, AR_PHY_ERR_1, ah->ani[0].ofdmPhyErrBase);
679 REG_WRITE(ah, AR_PHY_ERR_2, ah->ani[0].cckPhyErrBase);
Sujith7d0d0df2010-04-16 11:53:57 +0530680
681 REGWRITE_BUFFER_FLUSH(ah);
682 DISABLE_REGWRITE_BUFFER(ah);
683
Sujith1aa8e842009-08-13 09:34:25 +0530684 ath9k_enable_mib_counters(ah);
685
Sujith2660b812009-02-09 13:27:26 +0530686 ah->aniperiod = ATH9K_ANI_PERIOD;
687 if (ah->config.enable_ani)
688 ah->proc_phyerr |= HAL_PROCESS_ANI;
Sujithf1dc5602008-10-29 10:16:30 +0530689}