blob: d75bd6e5b158926fca1c680c77a9996edb6b3f47 [file] [log] [blame]
Sujithf1dc5602008-10-29 10:16:30 +05301/*
2 * Copyright (c) 2008 Atheros Communications Inc.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
Sujith394cf0a2009-02-09 13:26:54 +053017#include "ath9k.h"
Sujithf1dc5602008-10-29 10:16:30 +053018
19static int ath9k_hw_get_ani_channel_idx(struct ath_hal *ah,
20 struct ath9k_channel *chan)
21{
22 struct ath_hal_5416 *ahp = AH5416(ah);
23 int i;
24
25 for (i = 0; i < ARRAY_SIZE(ahp->ah_ani); i++) {
26 if (ahp->ah_ani[i].c.channel == chan->channel)
27 return i;
28 if (ahp->ah_ani[i].c.channel == 0) {
29 ahp->ah_ani[i].c.channel = chan->channel;
30 ahp->ah_ani[i].c.channelFlags = chan->channelFlags;
31 return i;
32 }
33 }
34
35 DPRINTF(ah->ah_sc, ATH_DBG_ANI,
36 "No more channel states left. Using channel 0\n");
37
38 return 0;
39}
40
41static bool ath9k_hw_ani_control(struct ath_hal *ah,
42 enum ath9k_ani_cmd cmd, int param)
43{
44 struct ath_hal_5416 *ahp = AH5416(ah);
45 struct ar5416AniState *aniState = ahp->ah_curani;
46
47 switch (cmd & ahp->ah_ani_function) {
48 case ATH9K_ANI_NOISE_IMMUNITY_LEVEL:{
49 u32 level = param;
50
51 if (level >= ARRAY_SIZE(ahp->ah_totalSizeDesired)) {
52 DPRINTF(ah->ah_sc, ATH_DBG_ANI,
Sujith04bd4632008-11-28 22:18:05 +053053 "level out of range (%u > %u)\n",
54 level,
Sujithf1dc5602008-10-29 10:16:30 +053055 (unsigned)ARRAY_SIZE(ahp->ah_totalSizeDesired));
56 return false;
57 }
58
59 REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ,
60 AR_PHY_DESIRED_SZ_TOT_DES,
61 ahp->ah_totalSizeDesired[level]);
62 REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
63 AR_PHY_AGC_CTL1_COARSE_LOW,
64 ahp->ah_coarseLow[level]);
65 REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
66 AR_PHY_AGC_CTL1_COARSE_HIGH,
67 ahp->ah_coarseHigh[level]);
68 REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
69 AR_PHY_FIND_SIG_FIRPWR,
70 ahp->ah_firpwr[level]);
71
72 if (level > aniState->noiseImmunityLevel)
73 ahp->ah_stats.ast_ani_niup++;
74 else if (level < aniState->noiseImmunityLevel)
75 ahp->ah_stats.ast_ani_nidown++;
76 aniState->noiseImmunityLevel = level;
77 break;
78 }
79 case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{
80 const int m1ThreshLow[] = { 127, 50 };
81 const int m2ThreshLow[] = { 127, 40 };
82 const int m1Thresh[] = { 127, 0x4d };
83 const int m2Thresh[] = { 127, 0x40 };
84 const int m2CountThr[] = { 31, 16 };
85 const int m2CountThrLow[] = { 63, 48 };
86 u32 on = param ? 1 : 0;
87
88 REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
89 AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
90 m1ThreshLow[on]);
91 REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
92 AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
93 m2ThreshLow[on]);
94 REG_RMW_FIELD(ah, AR_PHY_SFCORR,
95 AR_PHY_SFCORR_M1_THRESH,
96 m1Thresh[on]);
97 REG_RMW_FIELD(ah, AR_PHY_SFCORR,
98 AR_PHY_SFCORR_M2_THRESH,
99 m2Thresh[on]);
100 REG_RMW_FIELD(ah, AR_PHY_SFCORR,
101 AR_PHY_SFCORR_M2COUNT_THR,
102 m2CountThr[on]);
103 REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
104 AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
105 m2CountThrLow[on]);
106
107 REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
108 AR_PHY_SFCORR_EXT_M1_THRESH_LOW,
109 m1ThreshLow[on]);
110 REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
111 AR_PHY_SFCORR_EXT_M2_THRESH_LOW,
112 m2ThreshLow[on]);
113 REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
114 AR_PHY_SFCORR_EXT_M1_THRESH,
115 m1Thresh[on]);
116 REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
117 AR_PHY_SFCORR_EXT_M2_THRESH,
118 m2Thresh[on]);
119
120 if (on)
121 REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
122 AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
123 else
124 REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,
125 AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
126
127 if (!on != aniState->ofdmWeakSigDetectOff) {
128 if (on)
129 ahp->ah_stats.ast_ani_ofdmon++;
130 else
131 ahp->ah_stats.ast_ani_ofdmoff++;
132 aniState->ofdmWeakSigDetectOff = !on;
133 }
134 break;
135 }
136 case ATH9K_ANI_CCK_WEAK_SIGNAL_THR:{
137 const int weakSigThrCck[] = { 8, 6 };
138 u32 high = param ? 1 : 0;
139
140 REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT,
141 AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK,
142 weakSigThrCck[high]);
143 if (high != aniState->cckWeakSigThreshold) {
144 if (high)
145 ahp->ah_stats.ast_ani_cckhigh++;
146 else
147 ahp->ah_stats.ast_ani_ccklow++;
148 aniState->cckWeakSigThreshold = high;
149 }
150 break;
151 }
152 case ATH9K_ANI_FIRSTEP_LEVEL:{
153 const int firstep[] = { 0, 4, 8 };
154 u32 level = param;
155
156 if (level >= ARRAY_SIZE(firstep)) {
157 DPRINTF(ah->ah_sc, ATH_DBG_ANI,
Sujith04bd4632008-11-28 22:18:05 +0530158 "level out of range (%u > %u)\n",
159 level,
Sujithf1dc5602008-10-29 10:16:30 +0530160 (unsigned) ARRAY_SIZE(firstep));
161 return false;
162 }
163 REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
164 AR_PHY_FIND_SIG_FIRSTEP,
165 firstep[level]);
166 if (level > aniState->firstepLevel)
167 ahp->ah_stats.ast_ani_stepup++;
168 else if (level < aniState->firstepLevel)
169 ahp->ah_stats.ast_ani_stepdown++;
170 aniState->firstepLevel = level;
171 break;
172 }
173 case ATH9K_ANI_SPUR_IMMUNITY_LEVEL:{
174 const int cycpwrThr1[] =
175 { 2, 4, 6, 8, 10, 12, 14, 16 };
176 u32 level = param;
177
178 if (level >= ARRAY_SIZE(cycpwrThr1)) {
179 DPRINTF(ah->ah_sc, ATH_DBG_ANI,
Sujith04bd4632008-11-28 22:18:05 +0530180 "level out of range (%u > %u)\n",
181 level,
Sujithf1dc5602008-10-29 10:16:30 +0530182 (unsigned)
183 ARRAY_SIZE(cycpwrThr1));
184 return false;
185 }
186 REG_RMW_FIELD(ah, AR_PHY_TIMING5,
187 AR_PHY_TIMING5_CYCPWR_THR1,
188 cycpwrThr1[level]);
189 if (level > aniState->spurImmunityLevel)
190 ahp->ah_stats.ast_ani_spurup++;
191 else if (level < aniState->spurImmunityLevel)
192 ahp->ah_stats.ast_ani_spurdown++;
193 aniState->spurImmunityLevel = level;
194 break;
195 }
196 case ATH9K_ANI_PRESENT:
197 break;
198 default:
199 DPRINTF(ah->ah_sc, ATH_DBG_ANI,
Sujith04bd4632008-11-28 22:18:05 +0530200 "invalid cmd %u\n", cmd);
Sujithf1dc5602008-10-29 10:16:30 +0530201 return false;
202 }
203
Sujith04bd4632008-11-28 22:18:05 +0530204 DPRINTF(ah->ah_sc, ATH_DBG_ANI, "ANI parameters:\n");
Sujithf1dc5602008-10-29 10:16:30 +0530205 DPRINTF(ah->ah_sc, ATH_DBG_ANI,
206 "noiseImmunityLevel=%d, spurImmunityLevel=%d, "
207 "ofdmWeakSigDetectOff=%d\n",
208 aniState->noiseImmunityLevel, aniState->spurImmunityLevel,
209 !aniState->ofdmWeakSigDetectOff);
210 DPRINTF(ah->ah_sc, ATH_DBG_ANI,
211 "cckWeakSigThreshold=%d, "
212 "firstepLevel=%d, listenTime=%d\n",
213 aniState->cckWeakSigThreshold, aniState->firstepLevel,
214 aniState->listenTime);
215 DPRINTF(ah->ah_sc, ATH_DBG_ANI,
216 "cycleCount=%d, ofdmPhyErrCount=%d, cckPhyErrCount=%d\n\n",
217 aniState->cycleCount, aniState->ofdmPhyErrCount,
218 aniState->cckPhyErrCount);
219
220 return true;
221}
222
223static void ath9k_hw_update_mibstats(struct ath_hal *ah,
224 struct ath9k_mib_stats *stats)
225{
226 stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL);
227 stats->rts_bad += REG_READ(ah, AR_RTS_FAIL);
228 stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL);
229 stats->rts_good += REG_READ(ah, AR_RTS_OK);
230 stats->beacons += REG_READ(ah, AR_BEACON_CNT);
231}
232
233static void ath9k_ani_restart(struct ath_hal *ah)
234{
235 struct ath_hal_5416 *ahp = AH5416(ah);
236 struct ar5416AniState *aniState;
237
238 if (!DO_ANI(ah))
239 return;
240
241 aniState = ahp->ah_curani;
242
243 aniState->listenTime = 0;
244 if (ahp->ah_hasHwPhyCounters) {
245 if (aniState->ofdmTrigHigh > AR_PHY_COUNTMAX) {
246 aniState->ofdmPhyErrBase = 0;
247 DPRINTF(ah->ah_sc, ATH_DBG_ANI,
248 "OFDM Trigger is too high for hw counters\n");
249 } else {
250 aniState->ofdmPhyErrBase =
251 AR_PHY_COUNTMAX - aniState->ofdmTrigHigh;
252 }
253 if (aniState->cckTrigHigh > AR_PHY_COUNTMAX) {
254 aniState->cckPhyErrBase = 0;
255 DPRINTF(ah->ah_sc, ATH_DBG_ANI,
256 "CCK Trigger is too high for hw counters\n");
257 } else {
258 aniState->cckPhyErrBase =
259 AR_PHY_COUNTMAX - aniState->cckTrigHigh;
260 }
261 DPRINTF(ah->ah_sc, ATH_DBG_ANI,
Sujith04bd4632008-11-28 22:18:05 +0530262 "Writing ofdmbase=%u cckbase=%u\n",
263 aniState->ofdmPhyErrBase,
Sujithf1dc5602008-10-29 10:16:30 +0530264 aniState->cckPhyErrBase);
265 REG_WRITE(ah, AR_PHY_ERR_1, aniState->ofdmPhyErrBase);
266 REG_WRITE(ah, AR_PHY_ERR_2, aniState->cckPhyErrBase);
267 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
268 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
269
270 ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
271 }
272 aniState->ofdmPhyErrCount = 0;
273 aniState->cckPhyErrCount = 0;
274}
275
276static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hal *ah)
277{
278 struct ath_hal_5416 *ahp = AH5416(ah);
Luis R. Rodriguez38b33702008-12-23 15:58:46 -0800279 struct ieee80211_conf *conf = &ah->ah_sc->hw->conf;
Sujithf1dc5602008-10-29 10:16:30 +0530280 struct ar5416AniState *aniState;
Sujithf1dc5602008-10-29 10:16:30 +0530281 int32_t rssi;
282
283 if (!DO_ANI(ah))
284 return;
285
286 aniState = ahp->ah_curani;
287
288 if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
289 if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
290 aniState->noiseImmunityLevel + 1)) {
291 return;
292 }
293 }
294
295 if (aniState->spurImmunityLevel < HAL_SPUR_IMMUNE_MAX) {
296 if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
297 aniState->spurImmunityLevel + 1)) {
298 return;
299 }
300 }
301
Colin McCabed97809d2008-12-01 13:38:55 -0800302 if (ah->ah_opmode == NL80211_IFTYPE_AP) {
Sujithf1dc5602008-10-29 10:16:30 +0530303 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
304 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
305 aniState->firstepLevel + 1);
306 }
307 return;
308 }
309 rssi = BEACON_RSSI(ahp);
310 if (rssi > aniState->rssiThrHigh) {
311 if (!aniState->ofdmWeakSigDetectOff) {
312 if (ath9k_hw_ani_control(ah,
313 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
314 false)) {
315 ath9k_hw_ani_control(ah,
316 ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
317 return;
318 }
319 }
320 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
321 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
322 aniState->firstepLevel + 1);
323 return;
324 }
325 } else if (rssi > aniState->rssiThrLow) {
326 if (aniState->ofdmWeakSigDetectOff)
327 ath9k_hw_ani_control(ah,
328 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
329 true);
330 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
331 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
332 aniState->firstepLevel + 1);
333 return;
334 } else {
Luis R. Rodriguez38b33702008-12-23 15:58:46 -0800335 if (conf->channel->band == IEEE80211_BAND_2GHZ) {
Sujithf1dc5602008-10-29 10:16:30 +0530336 if (!aniState->ofdmWeakSigDetectOff)
337 ath9k_hw_ani_control(ah,
338 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
339 false);
340 if (aniState->firstepLevel > 0)
341 ath9k_hw_ani_control(ah,
342 ATH9K_ANI_FIRSTEP_LEVEL, 0);
343 return;
344 }
345 }
346}
347
348static void ath9k_hw_ani_cck_err_trigger(struct ath_hal *ah)
349{
350 struct ath_hal_5416 *ahp = AH5416(ah);
Luis R. Rodriguez38b33702008-12-23 15:58:46 -0800351 struct ieee80211_conf *conf = &ah->ah_sc->hw->conf;
Sujithf1dc5602008-10-29 10:16:30 +0530352 struct ar5416AniState *aniState;
Sujithf1dc5602008-10-29 10:16:30 +0530353 int32_t rssi;
354
355 if (!DO_ANI(ah))
356 return;
357
358 aniState = ahp->ah_curani;
359 if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
360 if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
361 aniState->noiseImmunityLevel + 1)) {
362 return;
363 }
364 }
Colin McCabed97809d2008-12-01 13:38:55 -0800365 if (ah->ah_opmode == NL80211_IFTYPE_AP) {
Sujithf1dc5602008-10-29 10:16:30 +0530366 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
367 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
368 aniState->firstepLevel + 1);
369 }
370 return;
371 }
372 rssi = BEACON_RSSI(ahp);
373 if (rssi > aniState->rssiThrLow) {
374 if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
375 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
376 aniState->firstepLevel + 1);
377 } else {
Luis R. Rodriguez38b33702008-12-23 15:58:46 -0800378 if (conf->channel->band == IEEE80211_BAND_2GHZ) {
Sujithf1dc5602008-10-29 10:16:30 +0530379 if (aniState->firstepLevel > 0)
380 ath9k_hw_ani_control(ah,
381 ATH9K_ANI_FIRSTEP_LEVEL, 0);
382 }
383 }
384}
385
386static void ath9k_hw_ani_lower_immunity(struct ath_hal *ah)
387{
388 struct ath_hal_5416 *ahp = AH5416(ah);
389 struct ar5416AniState *aniState;
390 int32_t rssi;
391
392 aniState = ahp->ah_curani;
393
Colin McCabed97809d2008-12-01 13:38:55 -0800394 if (ah->ah_opmode == NL80211_IFTYPE_AP) {
Sujithf1dc5602008-10-29 10:16:30 +0530395 if (aniState->firstepLevel > 0) {
396 if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
397 aniState->firstepLevel - 1))
398 return;
399 }
400 } else {
401 rssi = BEACON_RSSI(ahp);
402 if (rssi > aniState->rssiThrHigh) {
403 /* XXX: Handle me */
404 } else if (rssi > aniState->rssiThrLow) {
405 if (aniState->ofdmWeakSigDetectOff) {
406 if (ath9k_hw_ani_control(ah,
407 ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
408 true) == true)
409 return;
410 }
411 if (aniState->firstepLevel > 0) {
412 if (ath9k_hw_ani_control(ah,
413 ATH9K_ANI_FIRSTEP_LEVEL,
414 aniState->firstepLevel - 1) == true)
415 return;
416 }
417 } else {
418 if (aniState->firstepLevel > 0) {
419 if (ath9k_hw_ani_control(ah,
420 ATH9K_ANI_FIRSTEP_LEVEL,
421 aniState->firstepLevel - 1) == true)
422 return;
423 }
424 }
425 }
426
427 if (aniState->spurImmunityLevel > 0) {
428 if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
429 aniState->spurImmunityLevel - 1))
430 return;
431 }
432
433 if (aniState->noiseImmunityLevel > 0) {
434 ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
435 aniState->noiseImmunityLevel - 1);
436 return;
437 }
438}
439
440static int32_t ath9k_hw_ani_get_listen_time(struct ath_hal *ah)
441{
442 struct ath_hal_5416 *ahp = AH5416(ah);
443 struct ar5416AniState *aniState;
444 u32 txFrameCount, rxFrameCount, cycleCount;
445 int32_t listenTime;
446
447 txFrameCount = REG_READ(ah, AR_TFCNT);
448 rxFrameCount = REG_READ(ah, AR_RFCNT);
449 cycleCount = REG_READ(ah, AR_CCCNT);
450
451 aniState = ahp->ah_curani;
452 if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) {
453
454 listenTime = 0;
455 ahp->ah_stats.ast_ani_lzero++;
456 } else {
457 int32_t ccdelta = cycleCount - aniState->cycleCount;
458 int32_t rfdelta = rxFrameCount - aniState->rxFrameCount;
459 int32_t tfdelta = txFrameCount - aniState->txFrameCount;
460 listenTime = (ccdelta - rfdelta - tfdelta) / 44000;
461 }
462 aniState->cycleCount = cycleCount;
463 aniState->txFrameCount = txFrameCount;
464 aniState->rxFrameCount = rxFrameCount;
465
466 return listenTime;
467}
468
469void ath9k_ani_reset(struct ath_hal *ah)
470{
471 struct ath_hal_5416 *ahp = AH5416(ah);
472 struct ar5416AniState *aniState;
473 struct ath9k_channel *chan = ah->ah_curchan;
474 int index;
475
476 if (!DO_ANI(ah))
477 return;
478
479 index = ath9k_hw_get_ani_channel_idx(ah, chan);
480 aniState = &ahp->ah_ani[index];
481 ahp->ah_curani = aniState;
482
Colin McCabed97809d2008-12-01 13:38:55 -0800483 if (DO_ANI(ah) && ah->ah_opmode != NL80211_IFTYPE_STATION
484 && ah->ah_opmode != NL80211_IFTYPE_ADHOC) {
Sujithf1dc5602008-10-29 10:16:30 +0530485 DPRINTF(ah->ah_sc, ATH_DBG_ANI,
Sujith04bd4632008-11-28 22:18:05 +0530486 "Reset ANI state opmode %u\n", ah->ah_opmode);
Sujithf1dc5602008-10-29 10:16:30 +0530487 ahp->ah_stats.ast_ani_reset++;
488
489 ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0);
490 ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
491 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0);
492 ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
493 !ATH9K_ANI_USE_OFDM_WEAK_SIG);
494 ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
495 ATH9K_ANI_CCK_WEAK_SIG_THR);
496
497 ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) |
498 ATH9K_RX_FILTER_PHYERR);
499
Colin McCabed97809d2008-12-01 13:38:55 -0800500 if (ah->ah_opmode == NL80211_IFTYPE_AP) {
Sujithf1dc5602008-10-29 10:16:30 +0530501 ahp->ah_curani->ofdmTrigHigh =
502 ah->ah_config.ofdm_trig_high;
503 ahp->ah_curani->ofdmTrigLow =
504 ah->ah_config.ofdm_trig_low;
505 ahp->ah_curani->cckTrigHigh =
506 ah->ah_config.cck_trig_high;
507 ahp->ah_curani->cckTrigLow =
508 ah->ah_config.cck_trig_low;
509 }
510 ath9k_ani_restart(ah);
511 return;
512 }
513
514 if (aniState->noiseImmunityLevel != 0)
515 ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
516 aniState->noiseImmunityLevel);
517 if (aniState->spurImmunityLevel != 0)
518 ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
519 aniState->spurImmunityLevel);
520 if (aniState->ofdmWeakSigDetectOff)
521 ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
522 !aniState->ofdmWeakSigDetectOff);
523 if (aniState->cckWeakSigThreshold)
524 ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
525 aniState->cckWeakSigThreshold);
526 if (aniState->firstepLevel != 0)
527 ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
528 aniState->firstepLevel);
529 if (ahp->ah_hasHwPhyCounters) {
530 ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) &
531 ~ATH9K_RX_FILTER_PHYERR);
532 ath9k_ani_restart(ah);
533 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
534 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
535
536 } else {
537 ath9k_ani_restart(ah);
538 ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) |
539 ATH9K_RX_FILTER_PHYERR);
540 }
541}
542
543void ath9k_hw_ani_monitor(struct ath_hal *ah,
544 const struct ath9k_node_stats *stats,
545 struct ath9k_channel *chan)
546{
547 struct ath_hal_5416 *ahp = AH5416(ah);
548 struct ar5416AniState *aniState;
549 int32_t listenTime;
550
Gabor Juhos99506882009-01-14 20:17:11 +0100551 if (!DO_ANI(ah))
552 return;
553
Sujithf1dc5602008-10-29 10:16:30 +0530554 aniState = ahp->ah_curani;
555 ahp->ah_stats.ast_nodestats = *stats;
556
557 listenTime = ath9k_hw_ani_get_listen_time(ah);
558 if (listenTime < 0) {
559 ahp->ah_stats.ast_ani_lneg++;
560 ath9k_ani_restart(ah);
561 return;
562 }
563
564 aniState->listenTime += listenTime;
565
566 if (ahp->ah_hasHwPhyCounters) {
567 u32 phyCnt1, phyCnt2;
568 u32 ofdmPhyErrCnt, cckPhyErrCnt;
569
570 ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
571
572 phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
573 phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
574
575 if (phyCnt1 < aniState->ofdmPhyErrBase ||
576 phyCnt2 < aniState->cckPhyErrBase) {
577 if (phyCnt1 < aniState->ofdmPhyErrBase) {
578 DPRINTF(ah->ah_sc, ATH_DBG_ANI,
Sujith04bd4632008-11-28 22:18:05 +0530579 "phyCnt1 0x%x, resetting "
Sujithf1dc5602008-10-29 10:16:30 +0530580 "counter value to 0x%x\n",
Sujith04bd4632008-11-28 22:18:05 +0530581 phyCnt1,
Sujithf1dc5602008-10-29 10:16:30 +0530582 aniState->ofdmPhyErrBase);
583 REG_WRITE(ah, AR_PHY_ERR_1,
584 aniState->ofdmPhyErrBase);
585 REG_WRITE(ah, AR_PHY_ERR_MASK_1,
586 AR_PHY_ERR_OFDM_TIMING);
587 }
588 if (phyCnt2 < aniState->cckPhyErrBase) {
589 DPRINTF(ah->ah_sc, ATH_DBG_ANI,
Sujith04bd4632008-11-28 22:18:05 +0530590 "phyCnt2 0x%x, resetting "
Sujithf1dc5602008-10-29 10:16:30 +0530591 "counter value to 0x%x\n",
Sujith04bd4632008-11-28 22:18:05 +0530592 phyCnt2,
Sujithf1dc5602008-10-29 10:16:30 +0530593 aniState->cckPhyErrBase);
594 REG_WRITE(ah, AR_PHY_ERR_2,
595 aniState->cckPhyErrBase);
596 REG_WRITE(ah, AR_PHY_ERR_MASK_2,
597 AR_PHY_ERR_CCK_TIMING);
598 }
599 return;
600 }
601
602 ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase;
603 ahp->ah_stats.ast_ani_ofdmerrs +=
604 ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
605 aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
606
607 cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase;
608 ahp->ah_stats.ast_ani_cckerrs +=
609 cckPhyErrCnt - aniState->cckPhyErrCount;
610 aniState->cckPhyErrCount = cckPhyErrCnt;
611 }
612
Sujithf1dc5602008-10-29 10:16:30 +0530613 if (aniState->listenTime > 5 * ahp->ah_aniPeriod) {
614 if (aniState->ofdmPhyErrCount <= aniState->listenTime *
615 aniState->ofdmTrigLow / 1000 &&
616 aniState->cckPhyErrCount <= aniState->listenTime *
617 aniState->cckTrigLow / 1000)
618 ath9k_hw_ani_lower_immunity(ah);
619 ath9k_ani_restart(ah);
620 } else if (aniState->listenTime > ahp->ah_aniPeriod) {
621 if (aniState->ofdmPhyErrCount > aniState->listenTime *
622 aniState->ofdmTrigHigh / 1000) {
623 ath9k_hw_ani_ofdm_err_trigger(ah);
624 ath9k_ani_restart(ah);
625 } else if (aniState->cckPhyErrCount >
626 aniState->listenTime * aniState->cckTrigHigh /
627 1000) {
628 ath9k_hw_ani_cck_err_trigger(ah);
629 ath9k_ani_restart(ah);
630 }
631 }
632}
633
634bool ath9k_hw_phycounters(struct ath_hal *ah)
635{
636 struct ath_hal_5416 *ahp = AH5416(ah);
637
638 return ahp->ah_hasHwPhyCounters ? true : false;
639}
640
641void ath9k_enable_mib_counters(struct ath_hal *ah)
642{
643 struct ath_hal_5416 *ahp = AH5416(ah);
644
645 DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Enable MIB counters\n");
646
647 ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
648
649 REG_WRITE(ah, AR_FILT_OFDM, 0);
650 REG_WRITE(ah, AR_FILT_CCK, 0);
651 REG_WRITE(ah, AR_MIBC,
652 ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS)
653 & 0x0f);
654 REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
655 REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
656}
657
658void ath9k_hw_disable_mib_counters(struct ath_hal *ah)
659{
660 struct ath_hal_5416 *ahp = AH5416(ah);
661
662 DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Disable MIB counters\n");
663
664 REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC | AR_MIBC_CMC);
665
666 ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
667
668 REG_WRITE(ah, AR_FILT_OFDM, 0);
669 REG_WRITE(ah, AR_FILT_CCK, 0);
670}
671
672u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hal *ah,
673 u32 *rxc_pcnt,
674 u32 *rxf_pcnt,
675 u32 *txf_pcnt)
676{
677 static u32 cycles, rx_clear, rx_frame, tx_frame;
678 u32 good = 1;
679
680 u32 rc = REG_READ(ah, AR_RCCNT);
681 u32 rf = REG_READ(ah, AR_RFCNT);
682 u32 tf = REG_READ(ah, AR_TFCNT);
683 u32 cc = REG_READ(ah, AR_CCCNT);
684
685 if (cycles == 0 || cycles > cc) {
686 DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
Sujith04bd4632008-11-28 22:18:05 +0530687 "cycle counter wrap. ExtBusy = 0\n");
Sujithf1dc5602008-10-29 10:16:30 +0530688 good = 0;
689 } else {
690 u32 cc_d = cc - cycles;
691 u32 rc_d = rc - rx_clear;
692 u32 rf_d = rf - rx_frame;
693 u32 tf_d = tf - tx_frame;
694
695 if (cc_d != 0) {
696 *rxc_pcnt = rc_d * 100 / cc_d;
697 *rxf_pcnt = rf_d * 100 / cc_d;
698 *txf_pcnt = tf_d * 100 / cc_d;
699 } else {
700 good = 0;
701 }
702 }
703
704 cycles = cc;
705 rx_frame = rf;
706 rx_clear = rc;
707 tx_frame = tf;
708
709 return good;
710}
711
712/*
713 * Process a MIB interrupt. We may potentially be invoked because
714 * any of the MIB counters overflow/trigger so don't assume we're
715 * here because a PHY error counter triggered.
716 */
717void ath9k_hw_procmibevent(struct ath_hal *ah,
718 const struct ath9k_node_stats *stats)
719{
720 struct ath_hal_5416 *ahp = AH5416(ah);
721 u32 phyCnt1, phyCnt2;
722
723 /* Reset these counters regardless */
724 REG_WRITE(ah, AR_FILT_OFDM, 0);
725 REG_WRITE(ah, AR_FILT_CCK, 0);
726 if (!(REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING))
727 REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);
728
729 /* Clear the mib counters and save them in the stats */
730 ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
731 ahp->ah_stats.ast_nodestats = *stats;
732
733 if (!DO_ANI(ah))
734 return;
735
736 /* NB: these are not reset-on-read */
737 phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
738 phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
739 if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
740 ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
741 struct ar5416AniState *aniState = ahp->ah_curani;
742 u32 ofdmPhyErrCnt, cckPhyErrCnt;
743
744 /* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */
745 ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase;
746 ahp->ah_stats.ast_ani_ofdmerrs +=
747 ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
748 aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
749
750 cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase;
751 ahp->ah_stats.ast_ani_cckerrs +=
752 cckPhyErrCnt - aniState->cckPhyErrCount;
753 aniState->cckPhyErrCount = cckPhyErrCnt;
754
755 /*
756 * NB: figure out which counter triggered. If both
757 * trigger we'll only deal with one as the processing
758 * clobbers the error counter so the trigger threshold
759 * check will never be true.
760 */
761 if (aniState->ofdmPhyErrCount > aniState->ofdmTrigHigh)
762 ath9k_hw_ani_ofdm_err_trigger(ah);
763 if (aniState->cckPhyErrCount > aniState->cckTrigHigh)
764 ath9k_hw_ani_cck_err_trigger(ah);
765 /* NB: always restart to insure the h/w counters are reset */
766 ath9k_ani_restart(ah);
767 }
768}
769
770void ath9k_hw_ani_setup(struct ath_hal *ah)
771{
772 struct ath_hal_5416 *ahp = AH5416(ah);
773 int i;
774
775 const int totalSizeDesired[] = { -55, -55, -55, -55, -62 };
776 const int coarseHigh[] = { -14, -14, -14, -14, -12 };
777 const int coarseLow[] = { -64, -64, -64, -64, -70 };
778 const int firpwr[] = { -78, -78, -78, -78, -80 };
779
780 for (i = 0; i < 5; i++) {
781 ahp->ah_totalSizeDesired[i] = totalSizeDesired[i];
782 ahp->ah_coarseHigh[i] = coarseHigh[i];
783 ahp->ah_coarseLow[i] = coarseLow[i];
784 ahp->ah_firpwr[i] = firpwr[i];
785 }
786}
787
788void ath9k_hw_ani_attach(struct ath_hal *ah)
789{
790 struct ath_hal_5416 *ahp = AH5416(ah);
791 int i;
792
793 DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Attach ANI\n");
794
795 ahp->ah_hasHwPhyCounters = 1;
796
797 memset(ahp->ah_ani, 0, sizeof(ahp->ah_ani));
798 for (i = 0; i < ARRAY_SIZE(ahp->ah_ani); i++) {
799 ahp->ah_ani[i].ofdmTrigHigh = ATH9K_ANI_OFDM_TRIG_HIGH;
800 ahp->ah_ani[i].ofdmTrigLow = ATH9K_ANI_OFDM_TRIG_LOW;
801 ahp->ah_ani[i].cckTrigHigh = ATH9K_ANI_CCK_TRIG_HIGH;
802 ahp->ah_ani[i].cckTrigLow = ATH9K_ANI_CCK_TRIG_LOW;
803 ahp->ah_ani[i].rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH;
804 ahp->ah_ani[i].rssiThrLow = ATH9K_ANI_RSSI_THR_LOW;
805 ahp->ah_ani[i].ofdmWeakSigDetectOff =
806 !ATH9K_ANI_USE_OFDM_WEAK_SIG;
807 ahp->ah_ani[i].cckWeakSigThreshold =
808 ATH9K_ANI_CCK_WEAK_SIG_THR;
809 ahp->ah_ani[i].spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
810 ahp->ah_ani[i].firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
811 if (ahp->ah_hasHwPhyCounters) {
812 ahp->ah_ani[i].ofdmPhyErrBase =
813 AR_PHY_COUNTMAX - ATH9K_ANI_OFDM_TRIG_HIGH;
814 ahp->ah_ani[i].cckPhyErrBase =
815 AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH;
816 }
817 }
818 if (ahp->ah_hasHwPhyCounters) {
819 DPRINTF(ah->ah_sc, ATH_DBG_ANI,
820 "Setting OfdmErrBase = 0x%08x\n",
821 ahp->ah_ani[0].ofdmPhyErrBase);
822 DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n",
823 ahp->ah_ani[0].cckPhyErrBase);
824
825 REG_WRITE(ah, AR_PHY_ERR_1, ahp->ah_ani[0].ofdmPhyErrBase);
826 REG_WRITE(ah, AR_PHY_ERR_2, ahp->ah_ani[0].cckPhyErrBase);
827 ath9k_enable_mib_counters(ah);
828 }
829 ahp->ah_aniPeriod = ATH9K_ANI_PERIOD;
830 if (ah->ah_config.enable_ani)
831 ahp->ah_procPhyErr |= HAL_PROCESS_ANI;
832}
833
834void ath9k_hw_ani_detach(struct ath_hal *ah)
835{
836 struct ath_hal_5416 *ahp = AH5416(ah);
837
838 DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Detach ANI\n");
839
840 if (ahp->ah_hasHwPhyCounters) {
841 ath9k_hw_disable_mib_counters(ah);
842 REG_WRITE(ah, AR_PHY_ERR_1, 0);
843 REG_WRITE(ah, AR_PHY_ERR_2, 0);
844 }
845}