blob: b9595647810a8b7438fcb8970f22a77243d478dd [file] [log] [blame]
Sujithf1dc5602008-10-29 10:16:30 +05301/*
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -04002 * Copyright (c) 2008-2010 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
Nikitas Angelinasbbce80e2010-09-08 22:25:42 +010017#include <linux/kernel.h>
Luis R. Rodriguezcfe8cba2009-09-13 23:39:31 -070018#include "hw.h"
Felix Fietkauc16fcb42010-04-15 17:38:39 -040019#include "hw-ops.h"
Sujithf1dc5602008-10-29 10:16:30 +053020
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -040021struct ani_ofdm_level_entry {
22 int spur_immunity_level;
23 int fir_step_level;
24 int ofdm_weak_signal_on;
25};
26
27/* values here are relative to the INI */
28
29/*
30 * Legend:
31 *
32 * SI: Spur immunity
33 * FS: FIR Step
34 * WS: OFDM / CCK Weak Signal detection
35 * MRC-CCK: Maximal Ratio Combining for CCK
36 */
37
38static const struct ani_ofdm_level_entry ofdm_level_table[] = {
39 /* SI FS WS */
40 { 0, 0, 1 }, /* lvl 0 */
41 { 1, 1, 1 }, /* lvl 1 */
42 { 2, 2, 1 }, /* lvl 2 */
43 { 3, 2, 1 }, /* lvl 3 (default) */
44 { 4, 3, 1 }, /* lvl 4 */
45 { 5, 4, 1 }, /* lvl 5 */
46 { 6, 5, 1 }, /* lvl 6 */
47 { 7, 6, 1 }, /* lvl 7 */
48 { 7, 7, 1 }, /* lvl 8 */
49 { 7, 8, 0 } /* lvl 9 */
50};
51#define ATH9K_ANI_OFDM_NUM_LEVEL \
Nikitas Angelinasbbce80e2010-09-08 22:25:42 +010052 ARRAY_SIZE(ofdm_level_table)
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -040053#define ATH9K_ANI_OFDM_MAX_LEVEL \
54 (ATH9K_ANI_OFDM_NUM_LEVEL-1)
55#define ATH9K_ANI_OFDM_DEF_LEVEL \
56 3 /* default level - matches the INI settings */
57
58/*
59 * MRC (Maximal Ratio Combining) has always been used with multi-antenna ofdm.
60 * With OFDM for single stream you just add up all antenna inputs, you're
61 * only interested in what you get after FFT. Signal aligment is also not
62 * required for OFDM because any phase difference adds up in the frequency
63 * domain.
64 *
65 * MRC requires extra work for use with CCK. You need to align the antenna
66 * signals from the different antenna before you can add the signals together.
67 * You need aligment of signals as CCK is in time domain, so addition can cancel
68 * your signal completely if phase is 180 degrees (think of adding sine waves).
69 * You also need to remove noise before the addition and this is where ANI
70 * MRC CCK comes into play. One of the antenna inputs may be stronger but
71 * lower SNR, so just adding after alignment can be dangerous.
72 *
73 * Regardless of alignment in time, the antenna signals add constructively after
74 * FFT and improve your reception. For more information:
75 *
76 * http://en.wikipedia.org/wiki/Maximal-ratio_combining
77 */
78
79struct ani_cck_level_entry {
80 int fir_step_level;
81 int mrc_cck_on;
82};
83
84static const struct ani_cck_level_entry cck_level_table[] = {
85 /* FS MRC-CCK */
86 { 0, 1 }, /* lvl 0 */
87 { 1, 1 }, /* lvl 1 */
88 { 2, 1 }, /* lvl 2 (default) */
89 { 3, 1 }, /* lvl 3 */
90 { 4, 0 }, /* lvl 4 */
91 { 5, 0 }, /* lvl 5 */
92 { 6, 0 }, /* lvl 6 */
93 { 7, 0 }, /* lvl 7 (only for high rssi) */
94 { 8, 0 } /* lvl 8 (only for high rssi) */
95};
96
97#define ATH9K_ANI_CCK_NUM_LEVEL \
Nikitas Angelinasbbce80e2010-09-08 22:25:42 +010098 ARRAY_SIZE(cck_level_table)
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -040099#define ATH9K_ANI_CCK_MAX_LEVEL \
100 (ATH9K_ANI_CCK_NUM_LEVEL-1)
101#define ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI \
102 (ATH9K_ANI_CCK_NUM_LEVEL-3)
103#define ATH9K_ANI_CCK_DEF_LEVEL \
104 2 /* default level - matches the INI settings */
105
Felix Fietkau71ea4202010-10-04 20:09:46 +0200106static bool use_new_ani(struct ath_hw *ah)
107{
108 return AR_SREV_9300_20_OR_LATER(ah) || modparam_force_new_ani;
109}
110
Sujithcbe61d82009-02-09 13:27:12 +0530111static void ath9k_hw_update_mibstats(struct ath_hw *ah,
Sujithf1dc5602008-10-29 10:16:30 +0530112 struct ath9k_mib_stats *stats)
113{
114 stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL);
115 stats->rts_bad += REG_READ(ah, AR_RTS_FAIL);
116 stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL);
117 stats->rts_good += REG_READ(ah, AR_RTS_OK);
118 stats->beacons += REG_READ(ah, AR_BEACON_CNT);
119}
120
Felix Fietkau093115b2010-10-04 20:09:47 +0200121static void ath9k_ani_restart(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530122{
Sujithf1dc5602008-10-29 10:16:30 +0530123 struct ar5416AniState *aniState;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700124 struct ath_common *common = ath9k_hw_common(ah);
Felix Fietkau093115b2010-10-04 20:09:47 +0200125 u32 ofdm_base = 0, cck_base = 0;
Sujithf1dc5602008-10-29 10:16:30 +0530126
127 if (!DO_ANI(ah))
128 return;
129
Felix Fietkau093115b2010-10-04 20:09:47 +0200130 aniState = &ah->curchan->ani;
Sujithf1dc5602008-10-29 10:16:30 +0530131 aniState->listenTime = 0;
Sujithf1dc5602008-10-29 10:16:30 +0530132
Felix Fietkau093115b2010-10-04 20:09:47 +0200133 if (!use_new_ani(ah)) {
134 ofdm_base = AR_PHY_COUNTMAX - ah->config.ofdm_trig_high;
135 cck_base = AR_PHY_COUNTMAX - ah->config.cck_trig_high;
Sujithf1dc5602008-10-29 10:16:30 +0530136 }
Felix Fietkau093115b2010-10-04 20:09:47 +0200137
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700138 ath_print(common, ATH_DBG_ANI,
Felix Fietkau093115b2010-10-04 20:09:47 +0200139 "Writing ofdmbase=%u cckbase=%u\n", ofdm_base, cck_base);
Sujith7d0d0df2010-04-16 11:53:57 +0530140
141 ENABLE_REGWRITE_BUFFER(ah);
142
Felix Fietkau093115b2010-10-04 20:09:47 +0200143 REG_WRITE(ah, AR_PHY_ERR_1, ofdm_base);
144 REG_WRITE(ah, AR_PHY_ERR_2, cck_base);
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400145 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
146 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
147
148 REGWRITE_BUFFER_FLUSH(ah);
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400149
150 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
151
152 aniState->ofdmPhyErrCount = 0;
153 aniState->cckPhyErrCount = 0;
154}
155
156static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530157{
Luis R. Rodriguezb002a4a2009-09-13 00:03:27 -0700158 struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
Sujithf1dc5602008-10-29 10:16:30 +0530159 struct ar5416AniState *aniState;
Sujithf1dc5602008-10-29 10:16:30 +0530160 int32_t rssi;
161
Felix Fietkau093115b2010-10-04 20:09:47 +0200162 aniState = &ah->curchan->ani;
Sujithf1dc5602008-10-29 10:16:30 +0530163
164 if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
165 if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
166 aniState->noiseImmunityLevel + 1)) {
167 return;
168 }
169 }
170
171 if (aniState->spurImmunityLevel < HAL_SPUR_IMMUNE_MAX) {
172 if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
173 aniState->spurImmunityLevel + 1)) {
174 return;
175 }
176 }
177
Sujith2660b812009-02-09 13:27:26 +0530178 if (ah->opmode == NL80211_IFTYPE_AP) {
Sujithf1dc5602008-10-29 10:16:30 +0530179 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
180 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
181 aniState->firstepLevel + 1);
182 }
183 return;
184 }
Sujithcbe61d82009-02-09 13:27:12 +0530185 rssi = BEACON_RSSI(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530186 if (rssi > aniState->rssiThrHigh) {
187 if (!aniState->ofdmWeakSigDetectOff) {
188 if (ath9k_hw_ani_control(ah,
189 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
190 false)) {
191 ath9k_hw_ani_control(ah,
192 ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
193 return;
194 }
195 }
196 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
197 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
198 aniState->firstepLevel + 1);
199 return;
200 }
201 } else if (rssi > aniState->rssiThrLow) {
202 if (aniState->ofdmWeakSigDetectOff)
203 ath9k_hw_ani_control(ah,
204 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
205 true);
206 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
207 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
208 aniState->firstepLevel + 1);
209 return;
210 } else {
Sujithd37b7da2009-09-11 08:30:03 +0530211 if ((conf->channel->band == IEEE80211_BAND_2GHZ) &&
212 !conf_is_ht(conf)) {
Sujithf1dc5602008-10-29 10:16:30 +0530213 if (!aniState->ofdmWeakSigDetectOff)
214 ath9k_hw_ani_control(ah,
215 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
216 false);
217 if (aniState->firstepLevel > 0)
218 ath9k_hw_ani_control(ah,
219 ATH9K_ANI_FIRSTEP_LEVEL, 0);
220 return;
221 }
222 }
223}
224
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400225static void ath9k_hw_ani_cck_err_trigger_old(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530226{
Luis R. Rodriguezb002a4a2009-09-13 00:03:27 -0700227 struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
Sujithf1dc5602008-10-29 10:16:30 +0530228 struct ar5416AniState *aniState;
Sujithf1dc5602008-10-29 10:16:30 +0530229 int32_t rssi;
230
Felix Fietkau093115b2010-10-04 20:09:47 +0200231 aniState = &ah->curchan->ani;
Sujithf1dc5602008-10-29 10:16:30 +0530232 if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
233 if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
234 aniState->noiseImmunityLevel + 1)) {
235 return;
236 }
237 }
Sujith2660b812009-02-09 13:27:26 +0530238 if (ah->opmode == NL80211_IFTYPE_AP) {
Sujithf1dc5602008-10-29 10:16:30 +0530239 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
240 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
241 aniState->firstepLevel + 1);
242 }
243 return;
244 }
Sujithcbe61d82009-02-09 13:27:12 +0530245 rssi = BEACON_RSSI(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530246 if (rssi > aniState->rssiThrLow) {
247 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
248 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
249 aniState->firstepLevel + 1);
250 } else {
Sujithd37b7da2009-09-11 08:30:03 +0530251 if ((conf->channel->band == IEEE80211_BAND_2GHZ) &&
252 !conf_is_ht(conf)) {
Sujithf1dc5602008-10-29 10:16:30 +0530253 if (aniState->firstepLevel > 0)
254 ath9k_hw_ani_control(ah,
255 ATH9K_ANI_FIRSTEP_LEVEL, 0);
256 }
257 }
258}
259
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400260/* Adjust the OFDM Noise Immunity Level */
261static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel)
262{
Felix Fietkau093115b2010-10-04 20:09:47 +0200263 struct ar5416AniState *aniState = &ah->curchan->ani;
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400264 struct ath_common *common = ath9k_hw_common(ah);
265 const struct ani_ofdm_level_entry *entry_ofdm;
266 const struct ani_cck_level_entry *entry_cck;
267
268 aniState->noiseFloor = BEACON_RSSI(ah);
269
270 ath_print(common, ATH_DBG_ANI,
271 "**** ofdmlevel %d=>%d, rssi=%d[lo=%d hi=%d]\n",
272 aniState->ofdmNoiseImmunityLevel,
273 immunityLevel, aniState->noiseFloor,
274 aniState->rssiThrLow, aniState->rssiThrHigh);
275
276 aniState->ofdmNoiseImmunityLevel = immunityLevel;
277
278 entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel];
279 entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel];
280
281 if (aniState->spurImmunityLevel != entry_ofdm->spur_immunity_level)
282 ath9k_hw_ani_control(ah,
283 ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
284 entry_ofdm->spur_immunity_level);
285
286 if (aniState->firstepLevel != entry_ofdm->fir_step_level &&
287 entry_ofdm->fir_step_level >= entry_cck->fir_step_level)
288 ath9k_hw_ani_control(ah,
289 ATH9K_ANI_FIRSTEP_LEVEL,
290 entry_ofdm->fir_step_level);
291
292 if ((ah->opmode != NL80211_IFTYPE_STATION &&
293 ah->opmode != NL80211_IFTYPE_ADHOC) ||
294 aniState->noiseFloor <= aniState->rssiThrHigh) {
295 if (aniState->ofdmWeakSigDetectOff)
296 /* force on ofdm weak sig detect */
297 ath9k_hw_ani_control(ah,
298 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
299 true);
300 else if (aniState->ofdmWeakSigDetectOff ==
301 entry_ofdm->ofdm_weak_signal_on)
302 ath9k_hw_ani_control(ah,
303 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
304 entry_ofdm->ofdm_weak_signal_on);
305 }
306}
307
Felix Fietkau8eb49802010-10-04 20:09:49 +0200308static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400309{
310 struct ar5416AniState *aniState;
311
312 if (!DO_ANI(ah))
313 return;
314
Felix Fietkau8eb49802010-10-04 20:09:49 +0200315 if (!use_new_ani(ah)) {
316 ath9k_hw_ani_ofdm_err_trigger_old(ah);
317 return;
318 }
319
Felix Fietkau093115b2010-10-04 20:09:47 +0200320 aniState = &ah->curchan->ani;
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400321
322 if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL)
323 ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1);
324}
325
326/*
327 * Set the ANI settings to match an CCK level.
328 */
329static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel)
330{
Felix Fietkau093115b2010-10-04 20:09:47 +0200331 struct ar5416AniState *aniState = &ah->curchan->ani;
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400332 struct ath_common *common = ath9k_hw_common(ah);
333 const struct ani_ofdm_level_entry *entry_ofdm;
334 const struct ani_cck_level_entry *entry_cck;
335
336 aniState->noiseFloor = BEACON_RSSI(ah);
337 ath_print(common, ATH_DBG_ANI,
338 "**** ccklevel %d=>%d, rssi=%d[lo=%d hi=%d]\n",
339 aniState->cckNoiseImmunityLevel, immunityLevel,
340 aniState->noiseFloor, aniState->rssiThrLow,
341 aniState->rssiThrHigh);
342
343 if ((ah->opmode == NL80211_IFTYPE_STATION ||
344 ah->opmode == NL80211_IFTYPE_ADHOC) &&
345 aniState->noiseFloor <= aniState->rssiThrLow &&
346 immunityLevel > ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI)
347 immunityLevel = ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI;
348
349 aniState->cckNoiseImmunityLevel = immunityLevel;
350
351 entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel];
352 entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel];
353
354 if (aniState->firstepLevel != entry_cck->fir_step_level &&
355 entry_cck->fir_step_level >= entry_ofdm->fir_step_level)
356 ath9k_hw_ani_control(ah,
357 ATH9K_ANI_FIRSTEP_LEVEL,
358 entry_cck->fir_step_level);
359
360 /* Skip MRC CCK for pre AR9003 families */
361 if (!AR_SREV_9300_20_OR_LATER(ah))
362 return;
363
364 if (aniState->mrcCCKOff == entry_cck->mrc_cck_on)
365 ath9k_hw_ani_control(ah,
366 ATH9K_ANI_MRC_CCK,
367 entry_cck->mrc_cck_on);
368}
369
Felix Fietkau8eb49802010-10-04 20:09:49 +0200370static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah)
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400371{
372 struct ar5416AniState *aniState;
373
374 if (!DO_ANI(ah))
375 return;
376
Felix Fietkau8eb49802010-10-04 20:09:49 +0200377 if (!use_new_ani(ah)) {
378 ath9k_hw_ani_cck_err_trigger_old(ah);
379 return;
380 }
381
Felix Fietkau093115b2010-10-04 20:09:47 +0200382 aniState = &ah->curchan->ani;
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400383
384 if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL)
385 ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1);
386}
387
Luis R. Rodriguezac0bb762010-06-12 00:33:42 -0400388static void ath9k_hw_ani_lower_immunity_old(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530389{
Sujithf1dc5602008-10-29 10:16:30 +0530390 struct ar5416AniState *aniState;
391 int32_t rssi;
392
Felix Fietkau093115b2010-10-04 20:09:47 +0200393 aniState = &ah->curchan->ani;
Sujithf1dc5602008-10-29 10:16:30 +0530394
Sujith2660b812009-02-09 13:27:26 +0530395 if (ah->opmode == NL80211_IFTYPE_AP) {
Sujithf1dc5602008-10-29 10:16:30 +0530396 if (aniState->firstepLevel > 0) {
397 if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
398 aniState->firstepLevel - 1))
399 return;
400 }
401 } else {
Sujithcbe61d82009-02-09 13:27:12 +0530402 rssi = BEACON_RSSI(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530403 if (rssi > aniState->rssiThrHigh) {
404 /* XXX: Handle me */
405 } else if (rssi > aniState->rssiThrLow) {
406 if (aniState->ofdmWeakSigDetectOff) {
407 if (ath9k_hw_ani_control(ah,
408 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
409 true) == true)
410 return;
411 }
412 if (aniState->firstepLevel > 0) {
413 if (ath9k_hw_ani_control(ah,
414 ATH9K_ANI_FIRSTEP_LEVEL,
415 aniState->firstepLevel - 1) == true)
416 return;
417 }
418 } else {
419 if (aniState->firstepLevel > 0) {
420 if (ath9k_hw_ani_control(ah,
421 ATH9K_ANI_FIRSTEP_LEVEL,
422 aniState->firstepLevel - 1) == true)
423 return;
424 }
425 }
426 }
427
428 if (aniState->spurImmunityLevel > 0) {
429 if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
430 aniState->spurImmunityLevel - 1))
431 return;
432 }
433
434 if (aniState->noiseImmunityLevel > 0) {
435 ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
436 aniState->noiseImmunityLevel - 1);
437 return;
438 }
439}
440
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400441/*
442 * only lower either OFDM or CCK errors per turn
443 * we lower the other one next time
444 */
Felix Fietkau8eb49802010-10-04 20:09:49 +0200445static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400446{
447 struct ar5416AniState *aniState;
448
Felix Fietkau093115b2010-10-04 20:09:47 +0200449 aniState = &ah->curchan->ani;
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400450
Felix Fietkau8eb49802010-10-04 20:09:49 +0200451 if (!use_new_ani(ah)) {
452 ath9k_hw_ani_lower_immunity_old(ah);
453 return;
454 }
455
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400456 /* lower OFDM noise immunity */
457 if (aniState->ofdmNoiseImmunityLevel > 0 &&
458 (aniState->ofdmsTurn || aniState->cckNoiseImmunityLevel == 0)) {
459 ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel - 1);
460 return;
461 }
462
463 /* lower CCK noise immunity */
464 if (aniState->cckNoiseImmunityLevel > 0)
465 ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel - 1);
466}
467
Luis R. Rodriguez37e5bf62010-06-12 00:33:40 -0400468static u8 ath9k_hw_chan_2_clockrate_mhz(struct ath_hw *ah)
469{
470 struct ath9k_channel *chan = ah->curchan;
471 struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
472 u8 clockrate; /* in MHz */
473
474 if (!ah->curchan) /* should really check for CCK instead */
475 clockrate = ATH9K_CLOCK_RATE_CCK;
476 else if (conf->channel->band == IEEE80211_BAND_2GHZ)
477 clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM;
478 else if (IS_CHAN_A_FAST_CLOCK(ah, chan))
479 clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM;
480 else
481 clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM;
482
483 if (conf_is_ht40(conf))
484 return clockrate * 2;
485
Felix Fietkau918df622010-10-03 19:07:19 +0200486 return clockrate;
Luis R. Rodriguez37e5bf62010-06-12 00:33:40 -0400487}
488
Sujithcbe61d82009-02-09 13:27:12 +0530489static int32_t ath9k_hw_ani_get_listen_time(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530490{
Felix Fietkau9dbebc72010-10-03 19:07:17 +0200491 int32_t listen_time;
492 int32_t clock_rate;
Sujithf1dc5602008-10-29 10:16:30 +0530493
Felix Fietkau9dbebc72010-10-03 19:07:17 +0200494 ath9k_hw_update_cycle_counters(ah);
495 clock_rate = ath9k_hw_chan_2_clockrate_mhz(ah) * 1000;
496 listen_time = ah->listen_time / clock_rate;
497 ah->listen_time = 0;
Sujithf1dc5602008-10-29 10:16:30 +0530498
Felix Fietkau9dbebc72010-10-03 19:07:17 +0200499 return listen_time;
Sujithf1dc5602008-10-29 10:16:30 +0530500}
501
Luis R. Rodriguez40346b62010-06-12 00:33:44 -0400502static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning)
Sujithf1dc5602008-10-29 10:16:30 +0530503{
Sujithf1dc5602008-10-29 10:16:30 +0530504 struct ar5416AniState *aniState;
Sujith2660b812009-02-09 13:27:26 +0530505 struct ath9k_channel *chan = ah->curchan;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700506 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530507
508 if (!DO_ANI(ah))
509 return;
510
Felix Fietkau093115b2010-10-04 20:09:47 +0200511 aniState = &ah->curchan->ani;
Sujithf1dc5602008-10-29 10:16:30 +0530512
Felix Fietkau093115b2010-10-04 20:09:47 +0200513 if (ah->opmode != NL80211_IFTYPE_STATION
Sujith2660b812009-02-09 13:27:26 +0530514 && ah->opmode != NL80211_IFTYPE_ADHOC) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700515 ath_print(common, ATH_DBG_ANI,
516 "Reset ANI state opmode %u\n", ah->opmode);
Sujith2660b812009-02-09 13:27:26 +0530517 ah->stats.ast_ani_reset++;
Sujithf1dc5602008-10-29 10:16:30 +0530518
Luis R. Rodriguezc66284f2009-07-16 10:17:35 -0700519 if (ah->opmode == NL80211_IFTYPE_AP) {
520 /*
521 * ath9k_hw_ani_control() will only process items set on
522 * ah->ani_function
523 */
524 if (IS_CHAN_2GHZ(chan))
525 ah->ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL |
526 ATH9K_ANI_FIRSTEP_LEVEL);
527 else
528 ah->ani_function = 0;
529 }
530
Sujithf1dc5602008-10-29 10:16:30 +0530531 ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0);
532 ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
533 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0);
534 ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
535 !ATH9K_ANI_USE_OFDM_WEAK_SIG);
536 ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
537 ATH9K_ANI_CCK_WEAK_SIG_THR);
538
539 ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) |
540 ATH9K_RX_FILTER_PHYERR);
541
Felix Fietkau093115b2010-10-04 20:09:47 +0200542 ath9k_ani_restart(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530543 return;
544 }
545
546 if (aniState->noiseImmunityLevel != 0)
547 ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
548 aniState->noiseImmunityLevel);
549 if (aniState->spurImmunityLevel != 0)
550 ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
551 aniState->spurImmunityLevel);
552 if (aniState->ofdmWeakSigDetectOff)
553 ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
554 !aniState->ofdmWeakSigDetectOff);
555 if (aniState->cckWeakSigThreshold)
556 ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
557 aniState->cckWeakSigThreshold);
558 if (aniState->firstepLevel != 0)
559 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
560 aniState->firstepLevel);
Sujithf1dc5602008-10-29 10:16:30 +0530561
Sujith1aa8e842009-08-13 09:34:25 +0530562 ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) &
563 ~ATH9K_RX_FILTER_PHYERR);
Felix Fietkau093115b2010-10-04 20:09:47 +0200564 ath9k_ani_restart(ah);
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400565
566 ENABLE_REGWRITE_BUFFER(ah);
567
568 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
569 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
570
571 REGWRITE_BUFFER_FLUSH(ah);
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400572}
573
574/*
575 * Restore the ANI parameters in the HAL and reset the statistics.
576 * This routine should be called for every hardware reset and for
577 * every channel change.
578 */
Felix Fietkau8eb49802010-10-04 20:09:49 +0200579void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400580{
Felix Fietkau093115b2010-10-04 20:09:47 +0200581 struct ar5416AniState *aniState = &ah->curchan->ani;
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400582 struct ath9k_channel *chan = ah->curchan;
583 struct ath_common *common = ath9k_hw_common(ah);
584
585 if (!DO_ANI(ah))
586 return;
587
Felix Fietkau8eb49802010-10-04 20:09:49 +0200588 if (!use_new_ani(ah))
589 return ath9k_ani_reset_old(ah, is_scanning);
590
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400591 BUG_ON(aniState == NULL);
592 ah->stats.ast_ani_reset++;
593
594 /* only allow a subset of functions in AP mode */
595 if (ah->opmode == NL80211_IFTYPE_AP) {
596 if (IS_CHAN_2GHZ(chan)) {
597 ah->ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL |
598 ATH9K_ANI_FIRSTEP_LEVEL);
599 if (AR_SREV_9300_20_OR_LATER(ah))
600 ah->ani_function |= ATH9K_ANI_MRC_CCK;
601 } else
602 ah->ani_function = 0;
603 }
604
605 /* always allow mode (on/off) to be controlled */
606 ah->ani_function |= ATH9K_ANI_MODE;
607
608 if (is_scanning ||
609 (ah->opmode != NL80211_IFTYPE_STATION &&
610 ah->opmode != NL80211_IFTYPE_ADHOC)) {
611 /*
612 * If we're scanning or in AP mode, the defaults (ini)
613 * should be in place. For an AP we assume the historical
614 * levels for this channel are probably outdated so start
615 * from defaults instead.
616 */
617 if (aniState->ofdmNoiseImmunityLevel !=
618 ATH9K_ANI_OFDM_DEF_LEVEL ||
619 aniState->cckNoiseImmunityLevel !=
620 ATH9K_ANI_CCK_DEF_LEVEL) {
621 ath_print(common, ATH_DBG_ANI,
622 "Restore defaults: opmode %u "
623 "chan %d Mhz/0x%x is_scanning=%d "
624 "ofdm:%d cck:%d\n",
625 ah->opmode,
626 chan->channel,
627 chan->channelFlags,
628 is_scanning,
629 aniState->ofdmNoiseImmunityLevel,
630 aniState->cckNoiseImmunityLevel);
631
632 ath9k_hw_set_ofdm_nil(ah, ATH9K_ANI_OFDM_DEF_LEVEL);
633 ath9k_hw_set_cck_nil(ah, ATH9K_ANI_CCK_DEF_LEVEL);
634 }
635 } else {
636 /*
637 * restore historical levels for this channel
638 */
639 ath_print(common, ATH_DBG_ANI,
640 "Restore history: opmode %u "
641 "chan %d Mhz/0x%x is_scanning=%d "
642 "ofdm:%d cck:%d\n",
643 ah->opmode,
644 chan->channel,
645 chan->channelFlags,
646 is_scanning,
647 aniState->ofdmNoiseImmunityLevel,
648 aniState->cckNoiseImmunityLevel);
649
650 ath9k_hw_set_ofdm_nil(ah,
651 aniState->ofdmNoiseImmunityLevel);
652 ath9k_hw_set_cck_nil(ah,
653 aniState->cckNoiseImmunityLevel);
654 }
655
656 /*
657 * enable phy counters if hw supports or if not, enable phy
658 * interrupts (so we can count each one)
659 */
Felix Fietkau093115b2010-10-04 20:09:47 +0200660 ath9k_ani_restart(ah);
Sujith7d0d0df2010-04-16 11:53:57 +0530661
662 ENABLE_REGWRITE_BUFFER(ah);
663
Sujith1aa8e842009-08-13 09:34:25 +0530664 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
665 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
Sujith7d0d0df2010-04-16 11:53:57 +0530666
667 REGWRITE_BUFFER_FLUSH(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530668}
669
Felix Fietkaubfc472b2010-10-04 20:09:48 +0200670static void ath9k_hw_ani_read_counters(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530671{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700672 struct ath_common *common = ath9k_hw_common(ah);
Felix Fietkaubfc472b2010-10-04 20:09:48 +0200673 struct ar5416AniState *aniState = &ah->curchan->ani;
674 u32 ofdm_base = 0;
675 u32 cck_base = 0;
Sujith1aa8e842009-08-13 09:34:25 +0530676 u32 ofdmPhyErrCnt, cckPhyErrCnt;
Felix Fietkaubfc472b2010-10-04 20:09:48 +0200677 u32 phyCnt1, phyCnt2;
678 int32_t listenTime;
Sujithf1dc5602008-10-29 10:16:30 +0530679
680 listenTime = ath9k_hw_ani_get_listen_time(ah);
681 if (listenTime < 0) {
Sujith2660b812009-02-09 13:27:26 +0530682 ah->stats.ast_ani_lneg++;
Felix Fietkau093115b2010-10-04 20:09:47 +0200683 ath9k_ani_restart(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530684 return;
685 }
686
Felix Fietkaubfc472b2010-10-04 20:09:48 +0200687 if (!use_new_ani(ah)) {
688 ofdm_base = AR_PHY_COUNTMAX - ah->config.ofdm_trig_high;
689 cck_base = AR_PHY_COUNTMAX - ah->config.cck_trig_high;
690 }
691
Sujithf1dc5602008-10-29 10:16:30 +0530692 aniState->listenTime += listenTime;
693
Sujith1aa8e842009-08-13 09:34:25 +0530694 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
Sujithf1dc5602008-10-29 10:16:30 +0530695
Sujith1aa8e842009-08-13 09:34:25 +0530696 phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
697 phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
Sujithf1dc5602008-10-29 10:16:30 +0530698
Felix Fietkaubfc472b2010-10-04 20:09:48 +0200699 if (use_new_ani(ah) && (phyCnt1 < ofdm_base || phyCnt2 < cck_base)) {
Felix Fietkau093115b2010-10-04 20:09:47 +0200700 if (phyCnt1 < ofdm_base) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700701 ath_print(common, ATH_DBG_ANI,
702 "phyCnt1 0x%x, resetting "
703 "counter value to 0x%x\n",
Felix Fietkau093115b2010-10-04 20:09:47 +0200704 phyCnt1, ofdm_base);
705 REG_WRITE(ah, AR_PHY_ERR_1, ofdm_base);
Sujith1aa8e842009-08-13 09:34:25 +0530706 REG_WRITE(ah, AR_PHY_ERR_MASK_1,
707 AR_PHY_ERR_OFDM_TIMING);
Sujithf1dc5602008-10-29 10:16:30 +0530708 }
Felix Fietkau093115b2010-10-04 20:09:47 +0200709 if (phyCnt2 < cck_base) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700710 ath_print(common, ATH_DBG_ANI,
711 "phyCnt2 0x%x, resetting "
712 "counter value to 0x%x\n",
Felix Fietkau093115b2010-10-04 20:09:47 +0200713 phyCnt2, cck_base);
714 REG_WRITE(ah, AR_PHY_ERR_2, cck_base);
Sujith1aa8e842009-08-13 09:34:25 +0530715 REG_WRITE(ah, AR_PHY_ERR_MASK_2,
716 AR_PHY_ERR_CCK_TIMING);
717 }
718 return;
Sujithf1dc5602008-10-29 10:16:30 +0530719 }
720
Felix Fietkau093115b2010-10-04 20:09:47 +0200721 ofdmPhyErrCnt = phyCnt1 - ofdm_base;
Sujith1aa8e842009-08-13 09:34:25 +0530722 ah->stats.ast_ani_ofdmerrs +=
723 ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
724 aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
725
Felix Fietkau093115b2010-10-04 20:09:47 +0200726 cckPhyErrCnt = phyCnt2 - cck_base;
Sujith1aa8e842009-08-13 09:34:25 +0530727 ah->stats.ast_ani_cckerrs +=
728 cckPhyErrCnt - aniState->cckPhyErrCount;
729 aniState->cckPhyErrCount = cckPhyErrCnt;
730
Felix Fietkaubfc472b2010-10-04 20:09:48 +0200731}
732
733static void ath9k_hw_ani_monitor_old(struct ath_hw *ah,
734 struct ath9k_channel *chan)
735{
736 struct ar5416AniState *aniState;
737
738 if (!DO_ANI(ah))
739 return;
740
741 aniState = &ah->curchan->ani;
742 ath9k_hw_ani_read_counters(ah);
743
Sujith2660b812009-02-09 13:27:26 +0530744 if (aniState->listenTime > 5 * ah->aniperiod) {
Sujithf1dc5602008-10-29 10:16:30 +0530745 if (aniState->ofdmPhyErrCount <= aniState->listenTime *
Felix Fietkau093115b2010-10-04 20:09:47 +0200746 ah->config.ofdm_trig_low / 1000 &&
Sujithf1dc5602008-10-29 10:16:30 +0530747 aniState->cckPhyErrCount <= aniState->listenTime *
Felix Fietkau093115b2010-10-04 20:09:47 +0200748 ah->config.cck_trig_low / 1000)
Sujithf1dc5602008-10-29 10:16:30 +0530749 ath9k_hw_ani_lower_immunity(ah);
Felix Fietkau093115b2010-10-04 20:09:47 +0200750 ath9k_ani_restart(ah);
Sujith2660b812009-02-09 13:27:26 +0530751 } else if (aniState->listenTime > ah->aniperiod) {
Sujithf1dc5602008-10-29 10:16:30 +0530752 if (aniState->ofdmPhyErrCount > aniState->listenTime *
Felix Fietkau093115b2010-10-04 20:09:47 +0200753 ah->config.ofdm_trig_high / 1000) {
Felix Fietkau8eb49802010-10-04 20:09:49 +0200754 ath9k_hw_ani_ofdm_err_trigger(ah);
Felix Fietkau093115b2010-10-04 20:09:47 +0200755 ath9k_ani_restart(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530756 } else if (aniState->cckPhyErrCount >
Felix Fietkau093115b2010-10-04 20:09:47 +0200757 aniState->listenTime * ah->config.cck_trig_high /
Sujithf1dc5602008-10-29 10:16:30 +0530758 1000) {
Felix Fietkau8eb49802010-10-04 20:09:49 +0200759 ath9k_hw_ani_cck_err_trigger(ah);
Felix Fietkau093115b2010-10-04 20:09:47 +0200760 ath9k_ani_restart(ah);
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400761 }
762 }
763}
764
765static void ath9k_hw_ani_monitor_new(struct ath_hw *ah,
766 struct ath9k_channel *chan)
767{
768 struct ar5416AniState *aniState;
769 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400770 u32 ofdmPhyErrRate, cckPhyErrRate;
771
772 if (!DO_ANI(ah))
773 return;
774
Felix Fietkau093115b2010-10-04 20:09:47 +0200775 aniState = &ah->curchan->ani;
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400776 if (WARN_ON(!aniState))
777 return;
778
Felix Fietkaubfc472b2010-10-04 20:09:48 +0200779 ath9k_hw_ani_read_counters(ah);
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400780
781 ofdmPhyErrRate = aniState->ofdmPhyErrCount * 1000 /
782 aniState->listenTime;
783 cckPhyErrRate = aniState->cckPhyErrCount * 1000 /
784 aniState->listenTime;
785
786 ath_print(common, ATH_DBG_ANI,
787 "listenTime=%d OFDM:%d errs=%d/s CCK:%d "
788 "errs=%d/s ofdm_turn=%d\n",
Felix Fietkaubfc472b2010-10-04 20:09:48 +0200789 aniState->listenTime,
790 aniState->ofdmNoiseImmunityLevel,
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400791 ofdmPhyErrRate, aniState->cckNoiseImmunityLevel,
792 cckPhyErrRate, aniState->ofdmsTurn);
793
794 if (aniState->listenTime > 5 * ah->aniperiod) {
Felix Fietkau093115b2010-10-04 20:09:47 +0200795 if (ofdmPhyErrRate <= ah->config.ofdm_trig_low &&
796 cckPhyErrRate <= ah->config.cck_trig_low) {
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400797 ath_print(common, ATH_DBG_ANI,
798 "1. listenTime=%d OFDM:%d errs=%d/s(<%d) "
799 "CCK:%d errs=%d/s(<%d) -> "
800 "ath9k_hw_ani_lower_immunity()\n",
801 aniState->listenTime,
802 aniState->ofdmNoiseImmunityLevel,
803 ofdmPhyErrRate,
Felix Fietkau093115b2010-10-04 20:09:47 +0200804 ah->config.ofdm_trig_low,
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400805 aniState->cckNoiseImmunityLevel,
806 cckPhyErrRate,
Felix Fietkau093115b2010-10-04 20:09:47 +0200807 ah->config.cck_trig_low);
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400808 ath9k_hw_ani_lower_immunity(ah);
809 aniState->ofdmsTurn = !aniState->ofdmsTurn;
810 }
811 ath_print(common, ATH_DBG_ANI,
812 "1 listenTime=%d ofdm=%d/s cck=%d/s - "
Felix Fietkau093115b2010-10-04 20:09:47 +0200813 "calling ath9k_ani_restart()\n",
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400814 aniState->listenTime, ofdmPhyErrRate, cckPhyErrRate);
Felix Fietkau093115b2010-10-04 20:09:47 +0200815 ath9k_ani_restart(ah);
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400816 } else if (aniState->listenTime > ah->aniperiod) {
817 /* check to see if need to raise immunity */
Felix Fietkau093115b2010-10-04 20:09:47 +0200818 if (ofdmPhyErrRate > ah->config.ofdm_trig_high &&
819 (cckPhyErrRate <= ah->config.cck_trig_high ||
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400820 aniState->ofdmsTurn)) {
821 ath_print(common, ATH_DBG_ANI,
822 "2 listenTime=%d OFDM:%d errs=%d/s(>%d) -> "
Felix Fietkau8eb49802010-10-04 20:09:49 +0200823 "ath9k_hw_ani_ofdm_err_trigger()\n",
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400824 aniState->listenTime,
825 aniState->ofdmNoiseImmunityLevel,
826 ofdmPhyErrRate,
Felix Fietkau093115b2010-10-04 20:09:47 +0200827 ah->config.ofdm_trig_high);
Felix Fietkau8eb49802010-10-04 20:09:49 +0200828 ath9k_hw_ani_ofdm_err_trigger(ah);
Felix Fietkau093115b2010-10-04 20:09:47 +0200829 ath9k_ani_restart(ah);
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400830 aniState->ofdmsTurn = false;
Felix Fietkau093115b2010-10-04 20:09:47 +0200831 } else if (cckPhyErrRate > ah->config.cck_trig_high) {
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400832 ath_print(common, ATH_DBG_ANI,
833 "3 listenTime=%d CCK:%d errs=%d/s(>%d) -> "
Felix Fietkau8eb49802010-10-04 20:09:49 +0200834 "ath9k_hw_ani_cck_err_trigger()\n",
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400835 aniState->listenTime,
836 aniState->cckNoiseImmunityLevel,
837 cckPhyErrRate,
Felix Fietkau093115b2010-10-04 20:09:47 +0200838 ah->config.cck_trig_high);
Felix Fietkau8eb49802010-10-04 20:09:49 +0200839 ath9k_hw_ani_cck_err_trigger(ah);
Felix Fietkau093115b2010-10-04 20:09:47 +0200840 ath9k_ani_restart(ah);
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400841 aniState->ofdmsTurn = true;
Sujithf1dc5602008-10-29 10:16:30 +0530842 }
843 }
844}
845
Sujithcbe61d82009-02-09 13:27:12 +0530846void ath9k_enable_mib_counters(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530847{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700848 struct ath_common *common = ath9k_hw_common(ah);
849
850 ath_print(common, ATH_DBG_ANI, "Enable MIB counters\n");
Sujithf1dc5602008-10-29 10:16:30 +0530851
Sujithcbe61d82009-02-09 13:27:12 +0530852 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
Sujithf1dc5602008-10-29 10:16:30 +0530853
Sujith7d0d0df2010-04-16 11:53:57 +0530854 ENABLE_REGWRITE_BUFFER(ah);
855
Sujithf1dc5602008-10-29 10:16:30 +0530856 REG_WRITE(ah, AR_FILT_OFDM, 0);
857 REG_WRITE(ah, AR_FILT_CCK, 0);
858 REG_WRITE(ah, AR_MIBC,
859 ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS)
860 & 0x0f);
861 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
862 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
Sujith7d0d0df2010-04-16 11:53:57 +0530863
864 REGWRITE_BUFFER_FLUSH(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530865}
866
Sujith0fd06c92009-02-12 10:06:51 +0530867/* Freeze the MIB counters, get the stats and then clear them */
Sujithcbe61d82009-02-09 13:27:12 +0530868void ath9k_hw_disable_mib_counters(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530869{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700870 struct ath_common *common = ath9k_hw_common(ah);
871
872 ath_print(common, ATH_DBG_ANI, "Disable MIB counters\n");
873
Sujith0fd06c92009-02-12 10:06:51 +0530874 REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC);
Sujithcbe61d82009-02-09 13:27:12 +0530875 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
Sujith0fd06c92009-02-12 10:06:51 +0530876 REG_WRITE(ah, AR_MIBC, AR_MIBC_CMC);
Sujithf1dc5602008-10-29 10:16:30 +0530877 REG_WRITE(ah, AR_FILT_OFDM, 0);
878 REG_WRITE(ah, AR_FILT_CCK, 0);
879}
Sujith21d51302010-06-01 15:14:18 +0530880EXPORT_SYMBOL(ath9k_hw_disable_mib_counters);
Sujithf1dc5602008-10-29 10:16:30 +0530881
Felix Fietkau9dbebc72010-10-03 19:07:17 +0200882void ath9k_hw_update_cycle_counters(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530883{
Felix Fietkau9dbebc72010-10-03 19:07:17 +0200884 struct ath_cycle_counters cc;
885 bool clear;
Sujithf1dc5602008-10-29 10:16:30 +0530886
Felix Fietkau9dbebc72010-10-03 19:07:17 +0200887 memcpy(&cc, &ah->cc, sizeof(cc));
Sujithf1dc5602008-10-29 10:16:30 +0530888
Felix Fietkau9dbebc72010-10-03 19:07:17 +0200889 /* freeze counters */
890 REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC);
Sujithf1dc5602008-10-29 10:16:30 +0530891
Felix Fietkau9dbebc72010-10-03 19:07:17 +0200892 ah->cc.cycles = REG_READ(ah, AR_CCCNT);
893 if (ah->cc.cycles < cc.cycles) {
894 clear = true;
895 goto skip;
Sujithf1dc5602008-10-29 10:16:30 +0530896 }
897
Felix Fietkau9dbebc72010-10-03 19:07:17 +0200898 ah->cc.rx_clear = REG_READ(ah, AR_RCCNT);
899 ah->cc.rx_frame = REG_READ(ah, AR_RFCNT);
900 ah->cc.tx_frame = REG_READ(ah, AR_TFCNT);
Sujithf1dc5602008-10-29 10:16:30 +0530901
Felix Fietkau9dbebc72010-10-03 19:07:17 +0200902 /* prevent wraparound */
903 if (ah->cc.cycles & BIT(31))
904 clear = true;
905
906#define CC_DELTA(_field, _reg) ah->cc_delta._field += ah->cc._field - cc._field
907 CC_DELTA(cycles, AR_CCCNT);
908 CC_DELTA(rx_frame, AR_RFCNT);
909 CC_DELTA(rx_clear, AR_RCCNT);
910 CC_DELTA(tx_frame, AR_TFCNT);
911#undef CC_DELTA
912
913 ah->listen_time += (ah->cc.cycles - cc.cycles) -
914 ((ah->cc.rx_frame - cc.rx_frame) +
915 (ah->cc.tx_frame - cc.tx_frame));
916
917skip:
918 if (clear) {
919 REG_WRITE(ah, AR_CCCNT, 0);
920 REG_WRITE(ah, AR_RFCNT, 0);
921 REG_WRITE(ah, AR_RCCNT, 0);
922 REG_WRITE(ah, AR_TFCNT, 0);
923 memset(&ah->cc, 0, sizeof(ah->cc));
924 }
925
926 /* unfreeze counters */
927 REG_WRITE(ah, AR_MIBC, 0);
Sujithf1dc5602008-10-29 10:16:30 +0530928}
929
930/*
931 * Process a MIB interrupt. We may potentially be invoked because
932 * any of the MIB counters overflow/trigger so don't assume we're
933 * here because a PHY error counter triggered.
934 */
Felix Fietkaubfc472b2010-10-04 20:09:48 +0200935void ath9k_hw_proc_mib_event(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530936{
Sujithf1dc5602008-10-29 10:16:30 +0530937 u32 phyCnt1, phyCnt2;
938
939 /* Reset these counters regardless */
940 REG_WRITE(ah, AR_FILT_OFDM, 0);
941 REG_WRITE(ah, AR_FILT_CCK, 0);
942 if (!(REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING))
943 REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);
944
945 /* Clear the mib counters and save them in the stats */
Sujithcbe61d82009-02-09 13:27:12 +0530946 ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
Sujithf1dc5602008-10-29 10:16:30 +0530947
Luis R. Rodriguez6e97f0f2010-06-12 00:33:41 -0400948 if (!DO_ANI(ah)) {
949 /*
950 * We must always clear the interrupt cause by
951 * resetting the phy error regs.
952 */
953 REG_WRITE(ah, AR_PHY_ERR_1, 0);
954 REG_WRITE(ah, AR_PHY_ERR_2, 0);
Sujithf1dc5602008-10-29 10:16:30 +0530955 return;
Luis R. Rodriguez6e97f0f2010-06-12 00:33:41 -0400956 }
Sujithf1dc5602008-10-29 10:16:30 +0530957
958 /* NB: these are not reset-on-read */
959 phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
960 phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
961 if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
962 ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
Sujithf1dc5602008-10-29 10:16:30 +0530963
Felix Fietkaubfc472b2010-10-04 20:09:48 +0200964 if (!use_new_ani(ah))
965 ath9k_hw_ani_read_counters(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530966
Sujithf1dc5602008-10-29 10:16:30 +0530967 /* NB: always restart to insure the h/w counters are reset */
Felix Fietkau093115b2010-10-04 20:09:47 +0200968 ath9k_ani_restart(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530969 }
970}
Felix Fietkaubfc472b2010-10-04 20:09:48 +0200971EXPORT_SYMBOL(ath9k_hw_proc_mib_event);
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400972
Sujithcbe61d82009-02-09 13:27:12 +0530973void ath9k_hw_ani_setup(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530974{
Sujithf1dc5602008-10-29 10:16:30 +0530975 int i;
976
977 const int totalSizeDesired[] = { -55, -55, -55, -55, -62 };
978 const int coarseHigh[] = { -14, -14, -14, -14, -12 };
979 const int coarseLow[] = { -64, -64, -64, -64, -70 };
980 const int firpwr[] = { -78, -78, -78, -78, -80 };
981
982 for (i = 0; i < 5; i++) {
Sujith2660b812009-02-09 13:27:26 +0530983 ah->totalSizeDesired[i] = totalSizeDesired[i];
984 ah->coarse_high[i] = coarseHigh[i];
985 ah->coarse_low[i] = coarseLow[i];
986 ah->firpwr[i] = firpwr[i];
Sujithf1dc5602008-10-29 10:16:30 +0530987 }
988}
989
Luis R. Rodriguezf637cfd2009-08-03 12:24:46 -0700990void ath9k_hw_ani_init(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530991{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700992 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530993 int i;
994
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700995 ath_print(common, ATH_DBG_ANI, "Initialize ANI\n");
Sujithf1dc5602008-10-29 10:16:30 +0530996
Felix Fietkau093115b2010-10-04 20:09:47 +0200997 if (use_new_ani(ah)) {
998 ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_NEW;
999 ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_NEW;
1000
1001 ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH_NEW;
1002 ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW_NEW;
1003 } else {
1004 ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_OLD;
1005 ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_OLD;
1006
1007 ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH_OLD;
1008 ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW_OLD;
1009 }
1010
1011 for (i = 0; i < ARRAY_SIZE(ah->channels); i++) {
1012 struct ath9k_channel *chan = &ah->channels[i];
1013 struct ar5416AniState *ani = &chan->ani;
1014
Felix Fietkau71ea4202010-10-04 20:09:46 +02001015 if (use_new_ani(ah)) {
Felix Fietkau093115b2010-10-04 20:09:47 +02001016 ani->spurImmunityLevel =
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -04001017 ATH9K_ANI_SPUR_IMMUNE_LVL_NEW;
1018
Felix Fietkau093115b2010-10-04 20:09:47 +02001019 ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW;
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -04001020
1021 if (AR_SREV_9300_20_OR_LATER(ah))
Felix Fietkau093115b2010-10-04 20:09:47 +02001022 ani->mrcCCKOff =
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -04001023 !ATH9K_ANI_ENABLE_MRC_CCK;
1024 else
Felix Fietkau093115b2010-10-04 20:09:47 +02001025 ani->mrcCCKOff = true;
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -04001026
Felix Fietkau093115b2010-10-04 20:09:47 +02001027 ani->ofdmsTurn = true;
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -04001028 } else {
Felix Fietkau093115b2010-10-04 20:09:47 +02001029 ani->spurImmunityLevel =
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -04001030 ATH9K_ANI_SPUR_IMMUNE_LVL_OLD;
Felix Fietkau093115b2010-10-04 20:09:47 +02001031 ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_OLD;
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -04001032
Felix Fietkau093115b2010-10-04 20:09:47 +02001033 ani->cckWeakSigThreshold =
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -04001034 ATH9K_ANI_CCK_WEAK_SIG_THR;
1035 }
1036
Felix Fietkau093115b2010-10-04 20:09:47 +02001037 ani->rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH;
1038 ani->rssiThrLow = ATH9K_ANI_RSSI_THR_LOW;
1039 ani->ofdmWeakSigDetectOff =
Sujithf1dc5602008-10-29 10:16:30 +05301040 !ATH9K_ANI_USE_OFDM_WEAK_SIG;
Felix Fietkau093115b2010-10-04 20:09:47 +02001041 ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -04001042 }
1043
1044 /*
1045 * since we expect some ongoing maintenance on the tables, let's sanity
1046 * check here default level should not modify INI setting.
1047 */
Felix Fietkau71ea4202010-10-04 20:09:46 +02001048 if (use_new_ani(ah)) {
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -04001049 const struct ani_ofdm_level_entry *entry_ofdm;
1050 const struct ani_cck_level_entry *entry_cck;
1051
1052 entry_ofdm = &ofdm_level_table[ATH9K_ANI_OFDM_DEF_LEVEL];
1053 entry_cck = &cck_level_table[ATH9K_ANI_CCK_DEF_LEVEL];
1054
1055 ah->aniperiod = ATH9K_ANI_PERIOD_NEW;
1056 ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL_NEW;
1057 } else {
1058 ah->aniperiod = ATH9K_ANI_PERIOD_OLD;
1059 ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL_OLD;
Sujithf1dc5602008-10-29 10:16:30 +05301060 }
Sujithf1dc5602008-10-29 10:16:30 +05301061
Sujith2660b812009-02-09 13:27:26 +05301062 if (ah->config.enable_ani)
1063 ah->proc_phyerr |= HAL_PROCESS_ANI;
Felix Fietkau093115b2010-10-04 20:09:47 +02001064
1065 ath9k_ani_restart(ah);
1066 ath9k_enable_mib_counters(ah);
Sujithf1dc5602008-10-29 10:16:30 +05301067}
Luis R. Rodriguezac0bb762010-06-12 00:33:42 -04001068
1069void ath9k_hw_attach_ani_ops_old(struct ath_hw *ah)
1070{
Luis R. Rodriguezac0bb762010-06-12 00:33:42 -04001071 struct ath_hw_ops *ops = ath9k_hw_ops(ah);
1072
Luis R. Rodriguezac0bb762010-06-12 00:33:42 -04001073 ops->ani_monitor = ath9k_hw_ani_monitor_old;
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -04001074
1075 ath_print(ath9k_hw_common(ah), ATH_DBG_ANY, "Using ANI v1\n");
1076}
1077
1078void ath9k_hw_attach_ani_ops_new(struct ath_hw *ah)
1079{
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -04001080 struct ath_hw_ops *ops = ath9k_hw_ops(ah);
1081
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -04001082 ops->ani_monitor = ath9k_hw_ani_monitor_new;
1083
1084 ath_print(ath9k_hw_common(ah), ATH_DBG_ANY, "Using ANI v2\n");
Luis R. Rodriguezac0bb762010-06-12 00:33:42 -04001085}