1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright (C) 2010 Bruno Randolf <br1@einfach.org>
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Permission to use, copy, modify, and/or distribute this software for any
5*4882a593Smuzhiyun * purpose with or without fee is hereby granted, provided that the above
6*4882a593Smuzhiyun * copyright notice and this permission notice appear in all copies.
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9*4882a593Smuzhiyun * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10*4882a593Smuzhiyun * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11*4882a593Smuzhiyun * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12*4882a593Smuzhiyun * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13*4882a593Smuzhiyun * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14*4882a593Smuzhiyun * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15*4882a593Smuzhiyun */
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun #include "ath5k.h"
20*4882a593Smuzhiyun #include "reg.h"
21*4882a593Smuzhiyun #include "debug.h"
22*4882a593Smuzhiyun #include "ani.h"
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun /**
25*4882a593Smuzhiyun * DOC: Basic ANI Operation
26*4882a593Smuzhiyun *
27*4882a593Smuzhiyun * Adaptive Noise Immunity (ANI) controls five noise immunity parameters
28*4882a593Smuzhiyun * depending on the amount of interference in the environment, increasing
29*4882a593Smuzhiyun * or reducing sensitivity as necessary.
30*4882a593Smuzhiyun *
31*4882a593Smuzhiyun * The parameters are:
32*4882a593Smuzhiyun *
33*4882a593Smuzhiyun * - "noise immunity"
34*4882a593Smuzhiyun *
35*4882a593Smuzhiyun * - "spur immunity"
36*4882a593Smuzhiyun *
37*4882a593Smuzhiyun * - "firstep level"
38*4882a593Smuzhiyun *
39*4882a593Smuzhiyun * - "OFDM weak signal detection"
40*4882a593Smuzhiyun *
41*4882a593Smuzhiyun * - "CCK weak signal detection"
42*4882a593Smuzhiyun *
43*4882a593Smuzhiyun * Basically we look at the amount of ODFM and CCK timing errors we get and then
44*4882a593Smuzhiyun * raise or lower immunity accordingly by setting one or more of these
45*4882a593Smuzhiyun * parameters.
46*4882a593Smuzhiyun *
47*4882a593Smuzhiyun * Newer chipsets have PHY error counters in hardware which will generate a MIB
48*4882a593Smuzhiyun * interrupt when they overflow. Older hardware has too enable PHY error frames
49*4882a593Smuzhiyun * by setting a RX flag and then count every single PHY error. When a specified
50*4882a593Smuzhiyun * threshold of errors has been reached we will raise immunity.
51*4882a593Smuzhiyun * Also we regularly check the amount of errors and lower or raise immunity as
52*4882a593Smuzhiyun * necessary.
53*4882a593Smuzhiyun */
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun /***********************\
57*4882a593Smuzhiyun * ANI parameter control *
58*4882a593Smuzhiyun \***********************/
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun /**
61*4882a593Smuzhiyun * ath5k_ani_set_noise_immunity_level() - Set noise immunity level
62*4882a593Smuzhiyun * @ah: The &struct ath5k_hw
63*4882a593Smuzhiyun * @level: level between 0 and @ATH5K_ANI_MAX_NOISE_IMM_LVL
64*4882a593Smuzhiyun */
65*4882a593Smuzhiyun void
ath5k_ani_set_noise_immunity_level(struct ath5k_hw * ah,int level)66*4882a593Smuzhiyun ath5k_ani_set_noise_immunity_level(struct ath5k_hw *ah, int level)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun /* TODO:
69*4882a593Smuzhiyun * ANI documents suggest the following five levels to use, but the HAL
70*4882a593Smuzhiyun * and ath9k use only the last two levels, making this
71*4882a593Smuzhiyun * essentially an on/off option. There *may* be a reason for this (???),
72*4882a593Smuzhiyun * so i stick with the HAL version for now...
73*4882a593Smuzhiyun */
74*4882a593Smuzhiyun #if 0
75*4882a593Smuzhiyun static const s8 lo[] = { -52, -56, -60, -64, -70 };
76*4882a593Smuzhiyun static const s8 hi[] = { -18, -18, -16, -14, -12 };
77*4882a593Smuzhiyun static const s8 sz[] = { -34, -41, -48, -55, -62 };
78*4882a593Smuzhiyun static const s8 fr[] = { -70, -72, -75, -78, -80 };
79*4882a593Smuzhiyun #else
80*4882a593Smuzhiyun static const s8 lo[] = { -64, -70 };
81*4882a593Smuzhiyun static const s8 hi[] = { -14, -12 };
82*4882a593Smuzhiyun static const s8 sz[] = { -55, -62 };
83*4882a593Smuzhiyun static const s8 fr[] = { -78, -80 };
84*4882a593Smuzhiyun #endif
85*4882a593Smuzhiyun if (level < 0 || level >= ARRAY_SIZE(sz)) {
86*4882a593Smuzhiyun ATH5K_ERR(ah, "noise immunity level %d out of range",
87*4882a593Smuzhiyun level);
88*4882a593Smuzhiyun return;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE,
92*4882a593Smuzhiyun AR5K_PHY_DESIRED_SIZE_TOT, sz[level]);
93*4882a593Smuzhiyun AR5K_REG_WRITE_BITS(ah, AR5K_PHY_AGCCOARSE,
94*4882a593Smuzhiyun AR5K_PHY_AGCCOARSE_LO, lo[level]);
95*4882a593Smuzhiyun AR5K_REG_WRITE_BITS(ah, AR5K_PHY_AGCCOARSE,
96*4882a593Smuzhiyun AR5K_PHY_AGCCOARSE_HI, hi[level]);
97*4882a593Smuzhiyun AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG,
98*4882a593Smuzhiyun AR5K_PHY_SIG_FIRPWR, fr[level]);
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun ah->ani_state.noise_imm_level = level;
101*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun /**
105*4882a593Smuzhiyun * ath5k_ani_set_spur_immunity_level() - Set spur immunity level
106*4882a593Smuzhiyun * @ah: The &struct ath5k_hw
107*4882a593Smuzhiyun * @level: level between 0 and @max_spur_level (the maximum level is dependent
108*4882a593Smuzhiyun * on the chip revision).
109*4882a593Smuzhiyun */
110*4882a593Smuzhiyun void
ath5k_ani_set_spur_immunity_level(struct ath5k_hw * ah,int level)111*4882a593Smuzhiyun ath5k_ani_set_spur_immunity_level(struct ath5k_hw *ah, int level)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun static const int val[] = { 2, 4, 6, 8, 10, 12, 14, 16 };
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun if (level < 0 || level >= ARRAY_SIZE(val) ||
116*4882a593Smuzhiyun level > ah->ani_state.max_spur_level) {
117*4882a593Smuzhiyun ATH5K_ERR(ah, "spur immunity level %d out of range",
118*4882a593Smuzhiyun level);
119*4882a593Smuzhiyun return;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR,
123*4882a593Smuzhiyun AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1, val[level]);
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun ah->ani_state.spur_level = level;
126*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun /**
130*4882a593Smuzhiyun * ath5k_ani_set_firstep_level() - Set "firstep" level
131*4882a593Smuzhiyun * @ah: The &struct ath5k_hw
132*4882a593Smuzhiyun * @level: level between 0 and @ATH5K_ANI_MAX_FIRSTEP_LVL
133*4882a593Smuzhiyun */
134*4882a593Smuzhiyun void
ath5k_ani_set_firstep_level(struct ath5k_hw * ah,int level)135*4882a593Smuzhiyun ath5k_ani_set_firstep_level(struct ath5k_hw *ah, int level)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun static const int val[] = { 0, 4, 8 };
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun if (level < 0 || level >= ARRAY_SIZE(val)) {
140*4882a593Smuzhiyun ATH5K_ERR(ah, "firstep level %d out of range", level);
141*4882a593Smuzhiyun return;
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG,
145*4882a593Smuzhiyun AR5K_PHY_SIG_FIRSTEP, val[level]);
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun ah->ani_state.firstep_level = level;
148*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun /**
152*4882a593Smuzhiyun * ath5k_ani_set_ofdm_weak_signal_detection() - Set OFDM weak signal detection
153*4882a593Smuzhiyun * @ah: The &struct ath5k_hw
154*4882a593Smuzhiyun * @on: turn on or off
155*4882a593Smuzhiyun */
156*4882a593Smuzhiyun void
ath5k_ani_set_ofdm_weak_signal_detection(struct ath5k_hw * ah,bool on)157*4882a593Smuzhiyun ath5k_ani_set_ofdm_weak_signal_detection(struct ath5k_hw *ah, bool on)
158*4882a593Smuzhiyun {
159*4882a593Smuzhiyun static const int m1l[] = { 127, 50 };
160*4882a593Smuzhiyun static const int m2l[] = { 127, 40 };
161*4882a593Smuzhiyun static const int m1[] = { 127, 0x4d };
162*4882a593Smuzhiyun static const int m2[] = { 127, 0x40 };
163*4882a593Smuzhiyun static const int m2cnt[] = { 31, 16 };
164*4882a593Smuzhiyun static const int m2lcnt[] = { 63, 48 };
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
167*4882a593Smuzhiyun AR5K_PHY_WEAK_OFDM_LOW_THR_M1, m1l[on]);
168*4882a593Smuzhiyun AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
169*4882a593Smuzhiyun AR5K_PHY_WEAK_OFDM_LOW_THR_M2, m2l[on]);
170*4882a593Smuzhiyun AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR,
171*4882a593Smuzhiyun AR5K_PHY_WEAK_OFDM_HIGH_THR_M1, m1[on]);
172*4882a593Smuzhiyun AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR,
173*4882a593Smuzhiyun AR5K_PHY_WEAK_OFDM_HIGH_THR_M2, m2[on]);
174*4882a593Smuzhiyun AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR,
175*4882a593Smuzhiyun AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT, m2cnt[on]);
176*4882a593Smuzhiyun AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
177*4882a593Smuzhiyun AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT, m2lcnt[on]);
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun if (on)
180*4882a593Smuzhiyun AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
181*4882a593Smuzhiyun AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN);
182*4882a593Smuzhiyun else
183*4882a593Smuzhiyun AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
184*4882a593Smuzhiyun AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN);
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun ah->ani_state.ofdm_weak_sig = on;
187*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s",
188*4882a593Smuzhiyun on ? "on" : "off");
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun /**
192*4882a593Smuzhiyun * ath5k_ani_set_cck_weak_signal_detection() - Set CCK weak signal detection
193*4882a593Smuzhiyun * @ah: The &struct ath5k_hw
194*4882a593Smuzhiyun * @on: turn on or off
195*4882a593Smuzhiyun */
196*4882a593Smuzhiyun void
ath5k_ani_set_cck_weak_signal_detection(struct ath5k_hw * ah,bool on)197*4882a593Smuzhiyun ath5k_ani_set_cck_weak_signal_detection(struct ath5k_hw *ah, bool on)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun static const int val[] = { 8, 6 };
200*4882a593Smuzhiyun AR5K_REG_WRITE_BITS(ah, AR5K_PHY_CCK_CROSSCORR,
201*4882a593Smuzhiyun AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR, val[on]);
202*4882a593Smuzhiyun ah->ani_state.cck_weak_sig = on;
203*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s",
204*4882a593Smuzhiyun on ? "on" : "off");
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun /***************\
209*4882a593Smuzhiyun * ANI algorithm *
210*4882a593Smuzhiyun \***************/
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun /**
213*4882a593Smuzhiyun * ath5k_ani_raise_immunity() - Increase noise immunity
214*4882a593Smuzhiyun * @ah: The &struct ath5k_hw
215*4882a593Smuzhiyun * @as: The &struct ath5k_ani_state
216*4882a593Smuzhiyun * @ofdm_trigger: If this is true we are called because of too many OFDM errors,
217*4882a593Smuzhiyun * the algorithm will tune more parameters then.
218*4882a593Smuzhiyun *
219*4882a593Smuzhiyun * Try to raise noise immunity (=decrease sensitivity) in several steps
220*4882a593Smuzhiyun * depending on the average RSSI of the beacons we received.
221*4882a593Smuzhiyun */
222*4882a593Smuzhiyun static void
ath5k_ani_raise_immunity(struct ath5k_hw * ah,struct ath5k_ani_state * as,bool ofdm_trigger)223*4882a593Smuzhiyun ath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as,
224*4882a593Smuzhiyun bool ofdm_trigger)
225*4882a593Smuzhiyun {
226*4882a593Smuzhiyun int rssi = ewma_beacon_rssi_read(&ah->ah_beacon_rssi_avg);
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "raise immunity (%s)",
229*4882a593Smuzhiyun ofdm_trigger ? "ODFM" : "CCK");
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun /* first: raise noise immunity */
232*4882a593Smuzhiyun if (as->noise_imm_level < ATH5K_ANI_MAX_NOISE_IMM_LVL) {
233*4882a593Smuzhiyun ath5k_ani_set_noise_immunity_level(ah, as->noise_imm_level + 1);
234*4882a593Smuzhiyun return;
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun /* only OFDM: raise spur immunity level */
238*4882a593Smuzhiyun if (ofdm_trigger &&
239*4882a593Smuzhiyun as->spur_level < ah->ani_state.max_spur_level) {
240*4882a593Smuzhiyun ath5k_ani_set_spur_immunity_level(ah, as->spur_level + 1);
241*4882a593Smuzhiyun return;
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun /* AP mode */
245*4882a593Smuzhiyun if (ah->opmode == NL80211_IFTYPE_AP) {
246*4882a593Smuzhiyun if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL)
247*4882a593Smuzhiyun ath5k_ani_set_firstep_level(ah, as->firstep_level + 1);
248*4882a593Smuzhiyun return;
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun /* STA and IBSS mode */
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun /* TODO: for IBSS mode it would be better to keep a beacon RSSI average
254*4882a593Smuzhiyun * per each neighbour node and use the minimum of these, to make sure we
255*4882a593Smuzhiyun * don't shut out a remote node by raising immunity too high. */
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun if (rssi > ATH5K_ANI_RSSI_THR_HIGH) {
258*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
259*4882a593Smuzhiyun "beacon RSSI high");
260*4882a593Smuzhiyun /* only OFDM: beacon RSSI is high, we can disable ODFM weak
261*4882a593Smuzhiyun * signal detection */
262*4882a593Smuzhiyun if (ofdm_trigger && as->ofdm_weak_sig) {
263*4882a593Smuzhiyun ath5k_ani_set_ofdm_weak_signal_detection(ah, false);
264*4882a593Smuzhiyun ath5k_ani_set_spur_immunity_level(ah, 0);
265*4882a593Smuzhiyun return;
266*4882a593Smuzhiyun }
267*4882a593Smuzhiyun /* as a last resort or CCK: raise firstep level */
268*4882a593Smuzhiyun if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) {
269*4882a593Smuzhiyun ath5k_ani_set_firstep_level(ah, as->firstep_level + 1);
270*4882a593Smuzhiyun return;
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) {
273*4882a593Smuzhiyun /* beacon RSSI in mid range, we need OFDM weak signal detect,
274*4882a593Smuzhiyun * but can raise firstep level */
275*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
276*4882a593Smuzhiyun "beacon RSSI mid");
277*4882a593Smuzhiyun if (ofdm_trigger && !as->ofdm_weak_sig)
278*4882a593Smuzhiyun ath5k_ani_set_ofdm_weak_signal_detection(ah, true);
279*4882a593Smuzhiyun if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL)
280*4882a593Smuzhiyun ath5k_ani_set_firstep_level(ah, as->firstep_level + 1);
281*4882a593Smuzhiyun return;
282*4882a593Smuzhiyun } else if (ah->ah_current_channel->band == NL80211_BAND_2GHZ) {
283*4882a593Smuzhiyun /* beacon RSSI is low. in B/G mode turn of OFDM weak signal
284*4882a593Smuzhiyun * detect and zero firstep level to maximize CCK sensitivity */
285*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
286*4882a593Smuzhiyun "beacon RSSI low, 2GHz");
287*4882a593Smuzhiyun if (ofdm_trigger && as->ofdm_weak_sig)
288*4882a593Smuzhiyun ath5k_ani_set_ofdm_weak_signal_detection(ah, false);
289*4882a593Smuzhiyun if (as->firstep_level > 0)
290*4882a593Smuzhiyun ath5k_ani_set_firstep_level(ah, 0);
291*4882a593Smuzhiyun return;
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun /* TODO: why not?:
295*4882a593Smuzhiyun if (as->cck_weak_sig == true) {
296*4882a593Smuzhiyun ath5k_ani_set_cck_weak_signal_detection(ah, false);
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun */
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun /**
302*4882a593Smuzhiyun * ath5k_ani_lower_immunity() - Decrease noise immunity
303*4882a593Smuzhiyun * @ah: The &struct ath5k_hw
304*4882a593Smuzhiyun * @as: The &struct ath5k_ani_state
305*4882a593Smuzhiyun *
306*4882a593Smuzhiyun * Try to lower noise immunity (=increase sensitivity) in several steps
307*4882a593Smuzhiyun * depending on the average RSSI of the beacons we received.
308*4882a593Smuzhiyun */
309*4882a593Smuzhiyun static void
ath5k_ani_lower_immunity(struct ath5k_hw * ah,struct ath5k_ani_state * as)310*4882a593Smuzhiyun ath5k_ani_lower_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as)
311*4882a593Smuzhiyun {
312*4882a593Smuzhiyun int rssi = ewma_beacon_rssi_read(&ah->ah_beacon_rssi_avg);
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "lower immunity");
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun if (ah->opmode == NL80211_IFTYPE_AP) {
317*4882a593Smuzhiyun /* AP mode */
318*4882a593Smuzhiyun if (as->firstep_level > 0) {
319*4882a593Smuzhiyun ath5k_ani_set_firstep_level(ah, as->firstep_level - 1);
320*4882a593Smuzhiyun return;
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun } else {
323*4882a593Smuzhiyun /* STA and IBSS mode (see TODO above) */
324*4882a593Smuzhiyun if (rssi > ATH5K_ANI_RSSI_THR_HIGH) {
325*4882a593Smuzhiyun /* beacon signal is high, leave OFDM weak signal
326*4882a593Smuzhiyun * detection off or it may oscillate
327*4882a593Smuzhiyun * TODO: who said it's off??? */
328*4882a593Smuzhiyun } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) {
329*4882a593Smuzhiyun /* beacon RSSI is mid-range: turn on ODFM weak signal
330*4882a593Smuzhiyun * detection and next, lower firstep level */
331*4882a593Smuzhiyun if (!as->ofdm_weak_sig) {
332*4882a593Smuzhiyun ath5k_ani_set_ofdm_weak_signal_detection(ah,
333*4882a593Smuzhiyun true);
334*4882a593Smuzhiyun return;
335*4882a593Smuzhiyun }
336*4882a593Smuzhiyun if (as->firstep_level > 0) {
337*4882a593Smuzhiyun ath5k_ani_set_firstep_level(ah,
338*4882a593Smuzhiyun as->firstep_level - 1);
339*4882a593Smuzhiyun return;
340*4882a593Smuzhiyun }
341*4882a593Smuzhiyun } else {
342*4882a593Smuzhiyun /* beacon signal is low: only reduce firstep level */
343*4882a593Smuzhiyun if (as->firstep_level > 0) {
344*4882a593Smuzhiyun ath5k_ani_set_firstep_level(ah,
345*4882a593Smuzhiyun as->firstep_level - 1);
346*4882a593Smuzhiyun return;
347*4882a593Smuzhiyun }
348*4882a593Smuzhiyun }
349*4882a593Smuzhiyun }
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun /* all modes */
352*4882a593Smuzhiyun if (as->spur_level > 0) {
353*4882a593Smuzhiyun ath5k_ani_set_spur_immunity_level(ah, as->spur_level - 1);
354*4882a593Smuzhiyun return;
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun /* finally, reduce noise immunity */
358*4882a593Smuzhiyun if (as->noise_imm_level > 0) {
359*4882a593Smuzhiyun ath5k_ani_set_noise_immunity_level(ah, as->noise_imm_level - 1);
360*4882a593Smuzhiyun return;
361*4882a593Smuzhiyun }
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun /**
365*4882a593Smuzhiyun * ath5k_hw_ani_get_listen_time() - Update counters and return listening time
366*4882a593Smuzhiyun * @ah: The &struct ath5k_hw
367*4882a593Smuzhiyun * @as: The &struct ath5k_ani_state
368*4882a593Smuzhiyun *
369*4882a593Smuzhiyun * Return an approximation of the time spent "listening" in milliseconds (ms)
370*4882a593Smuzhiyun * since the last call of this function.
371*4882a593Smuzhiyun * Save a snapshot of the counter values for debugging/statistics.
372*4882a593Smuzhiyun */
373*4882a593Smuzhiyun static int
ath5k_hw_ani_get_listen_time(struct ath5k_hw * ah,struct ath5k_ani_state * as)374*4882a593Smuzhiyun ath5k_hw_ani_get_listen_time(struct ath5k_hw *ah, struct ath5k_ani_state *as)
375*4882a593Smuzhiyun {
376*4882a593Smuzhiyun struct ath_common *common = ath5k_hw_common(ah);
377*4882a593Smuzhiyun int listen;
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun spin_lock_bh(&common->cc_lock);
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun ath_hw_cycle_counters_update(common);
382*4882a593Smuzhiyun memcpy(&as->last_cc, &common->cc_ani, sizeof(as->last_cc));
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun /* clears common->cc_ani */
385*4882a593Smuzhiyun listen = ath_hw_get_listen_time(common);
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun spin_unlock_bh(&common->cc_lock);
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun return listen;
390*4882a593Smuzhiyun }
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun /**
393*4882a593Smuzhiyun * ath5k_ani_save_and_clear_phy_errors() - Clear and save PHY error counters
394*4882a593Smuzhiyun * @ah: The &struct ath5k_hw
395*4882a593Smuzhiyun * @as: The &struct ath5k_ani_state
396*4882a593Smuzhiyun *
397*4882a593Smuzhiyun * Clear the PHY error counters as soon as possible, since this might be called
398*4882a593Smuzhiyun * from a MIB interrupt and we want to make sure we don't get interrupted again.
399*4882a593Smuzhiyun * Add the count of CCK and OFDM errors to our internal state, so it can be used
400*4882a593Smuzhiyun * by the algorithm later.
401*4882a593Smuzhiyun *
402*4882a593Smuzhiyun * Will be called from interrupt and tasklet context.
403*4882a593Smuzhiyun * Returns 0 if both counters are zero.
404*4882a593Smuzhiyun */
405*4882a593Smuzhiyun static int
ath5k_ani_save_and_clear_phy_errors(struct ath5k_hw * ah,struct ath5k_ani_state * as)406*4882a593Smuzhiyun ath5k_ani_save_and_clear_phy_errors(struct ath5k_hw *ah,
407*4882a593Smuzhiyun struct ath5k_ani_state *as)
408*4882a593Smuzhiyun {
409*4882a593Smuzhiyun unsigned int ofdm_err, cck_err;
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun if (!ah->ah_capabilities.cap_has_phyerr_counters)
412*4882a593Smuzhiyun return 0;
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun ofdm_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1);
415*4882a593Smuzhiyun cck_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2);
416*4882a593Smuzhiyun
417*4882a593Smuzhiyun /* reset counters first, we might be in a hurry (interrupt) */
418*4882a593Smuzhiyun ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH,
419*4882a593Smuzhiyun AR5K_PHYERR_CNT1);
420*4882a593Smuzhiyun ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH,
421*4882a593Smuzhiyun AR5K_PHYERR_CNT2);
422*4882a593Smuzhiyun
423*4882a593Smuzhiyun ofdm_err = ATH5K_ANI_OFDM_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - ofdm_err);
424*4882a593Smuzhiyun cck_err = ATH5K_ANI_CCK_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - cck_err);
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun /* sometimes both can be zero, especially when there is a superfluous
427*4882a593Smuzhiyun * second interrupt. detect that here and return an error. */
428*4882a593Smuzhiyun if (ofdm_err <= 0 && cck_err <= 0)
429*4882a593Smuzhiyun return 0;
430*4882a593Smuzhiyun
431*4882a593Smuzhiyun /* avoid negative values should one of the registers overflow */
432*4882a593Smuzhiyun if (ofdm_err > 0) {
433*4882a593Smuzhiyun as->ofdm_errors += ofdm_err;
434*4882a593Smuzhiyun as->sum_ofdm_errors += ofdm_err;
435*4882a593Smuzhiyun }
436*4882a593Smuzhiyun if (cck_err > 0) {
437*4882a593Smuzhiyun as->cck_errors += cck_err;
438*4882a593Smuzhiyun as->sum_cck_errors += cck_err;
439*4882a593Smuzhiyun }
440*4882a593Smuzhiyun return 1;
441*4882a593Smuzhiyun }
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun /**
444*4882a593Smuzhiyun * ath5k_ani_period_restart() - Restart ANI period
445*4882a593Smuzhiyun * @as: The &struct ath5k_ani_state
446*4882a593Smuzhiyun *
447*4882a593Smuzhiyun * Just reset counters, so they are clear for the next "ani period".
448*4882a593Smuzhiyun */
449*4882a593Smuzhiyun static void
ath5k_ani_period_restart(struct ath5k_ani_state * as)450*4882a593Smuzhiyun ath5k_ani_period_restart(struct ath5k_ani_state *as)
451*4882a593Smuzhiyun {
452*4882a593Smuzhiyun /* keep last values for debugging */
453*4882a593Smuzhiyun as->last_ofdm_errors = as->ofdm_errors;
454*4882a593Smuzhiyun as->last_cck_errors = as->cck_errors;
455*4882a593Smuzhiyun as->last_listen = as->listen_time;
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun as->ofdm_errors = 0;
458*4882a593Smuzhiyun as->cck_errors = 0;
459*4882a593Smuzhiyun as->listen_time = 0;
460*4882a593Smuzhiyun }
461*4882a593Smuzhiyun
462*4882a593Smuzhiyun /**
463*4882a593Smuzhiyun * ath5k_ani_calibration() - The main ANI calibration function
464*4882a593Smuzhiyun * @ah: The &struct ath5k_hw
465*4882a593Smuzhiyun *
466*4882a593Smuzhiyun * We count OFDM and CCK errors relative to the time where we did not send or
467*4882a593Smuzhiyun * receive ("listen" time) and raise or lower immunity accordingly.
468*4882a593Smuzhiyun * This is called regularly (every second) from the calibration timer, but also
469*4882a593Smuzhiyun * when an error threshold has been reached.
470*4882a593Smuzhiyun *
471*4882a593Smuzhiyun * In order to synchronize access from different contexts, this should be
472*4882a593Smuzhiyun * called only indirectly by scheduling the ANI tasklet!
473*4882a593Smuzhiyun */
474*4882a593Smuzhiyun void
ath5k_ani_calibration(struct ath5k_hw * ah)475*4882a593Smuzhiyun ath5k_ani_calibration(struct ath5k_hw *ah)
476*4882a593Smuzhiyun {
477*4882a593Smuzhiyun struct ath5k_ani_state *as = &ah->ani_state;
478*4882a593Smuzhiyun int listen, ofdm_high, ofdm_low, cck_high, cck_low;
479*4882a593Smuzhiyun
480*4882a593Smuzhiyun /* get listen time since last call and add it to the counter because we
481*4882a593Smuzhiyun * might not have restarted the "ani period" last time.
482*4882a593Smuzhiyun * always do this to calculate the busy time also in manual mode */
483*4882a593Smuzhiyun listen = ath5k_hw_ani_get_listen_time(ah, as);
484*4882a593Smuzhiyun as->listen_time += listen;
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun if (as->ani_mode != ATH5K_ANI_MODE_AUTO)
487*4882a593Smuzhiyun return;
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun ath5k_ani_save_and_clear_phy_errors(ah, as);
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun ofdm_high = as->listen_time * ATH5K_ANI_OFDM_TRIG_HIGH / 1000;
492*4882a593Smuzhiyun cck_high = as->listen_time * ATH5K_ANI_CCK_TRIG_HIGH / 1000;
493*4882a593Smuzhiyun ofdm_low = as->listen_time * ATH5K_ANI_OFDM_TRIG_LOW / 1000;
494*4882a593Smuzhiyun cck_low = as->listen_time * ATH5K_ANI_CCK_TRIG_LOW / 1000;
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
497*4882a593Smuzhiyun "listen %d (now %d)", as->listen_time, listen);
498*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
499*4882a593Smuzhiyun "check high ofdm %d/%d cck %d/%d",
500*4882a593Smuzhiyun as->ofdm_errors, ofdm_high, as->cck_errors, cck_high);
501*4882a593Smuzhiyun
502*4882a593Smuzhiyun if (as->ofdm_errors > ofdm_high || as->cck_errors > cck_high) {
503*4882a593Smuzhiyun /* too many PHY errors - we have to raise immunity */
504*4882a593Smuzhiyun bool ofdm_flag = as->ofdm_errors > ofdm_high;
505*4882a593Smuzhiyun ath5k_ani_raise_immunity(ah, as, ofdm_flag);
506*4882a593Smuzhiyun ath5k_ani_period_restart(as);
507*4882a593Smuzhiyun
508*4882a593Smuzhiyun } else if (as->listen_time > 5 * ATH5K_ANI_LISTEN_PERIOD) {
509*4882a593Smuzhiyun /* If more than 5 (TODO: why 5?) periods have passed and we got
510*4882a593Smuzhiyun * relatively little errors we can try to lower immunity */
511*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
512*4882a593Smuzhiyun "check low ofdm %d/%d cck %d/%d",
513*4882a593Smuzhiyun as->ofdm_errors, ofdm_low, as->cck_errors, cck_low);
514*4882a593Smuzhiyun
515*4882a593Smuzhiyun if (as->ofdm_errors <= ofdm_low && as->cck_errors <= cck_low)
516*4882a593Smuzhiyun ath5k_ani_lower_immunity(ah, as);
517*4882a593Smuzhiyun
518*4882a593Smuzhiyun ath5k_ani_period_restart(as);
519*4882a593Smuzhiyun }
520*4882a593Smuzhiyun }
521*4882a593Smuzhiyun
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun /*******************\
524*4882a593Smuzhiyun * Interrupt handler *
525*4882a593Smuzhiyun \*******************/
526*4882a593Smuzhiyun
527*4882a593Smuzhiyun /**
528*4882a593Smuzhiyun * ath5k_ani_mib_intr() - Interrupt handler for ANI MIB counters
529*4882a593Smuzhiyun * @ah: The &struct ath5k_hw
530*4882a593Smuzhiyun *
531*4882a593Smuzhiyun * Just read & reset the registers quickly, so they don't generate more
532*4882a593Smuzhiyun * interrupts, save the counters and schedule the tasklet to decide whether
533*4882a593Smuzhiyun * to raise immunity or not.
534*4882a593Smuzhiyun *
535*4882a593Smuzhiyun * We just need to handle PHY error counters, ath5k_hw_update_mib_counters()
536*4882a593Smuzhiyun * should take care of all "normal" MIB interrupts.
537*4882a593Smuzhiyun */
538*4882a593Smuzhiyun void
ath5k_ani_mib_intr(struct ath5k_hw * ah)539*4882a593Smuzhiyun ath5k_ani_mib_intr(struct ath5k_hw *ah)
540*4882a593Smuzhiyun {
541*4882a593Smuzhiyun struct ath5k_ani_state *as = &ah->ani_state;
542*4882a593Smuzhiyun
543*4882a593Smuzhiyun /* nothing to do here if HW does not have PHY error counters - they
544*4882a593Smuzhiyun * can't be the reason for the MIB interrupt then */
545*4882a593Smuzhiyun if (!ah->ah_capabilities.cap_has_phyerr_counters)
546*4882a593Smuzhiyun return;
547*4882a593Smuzhiyun
548*4882a593Smuzhiyun /* not in use but clear anyways */
549*4882a593Smuzhiyun ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT);
550*4882a593Smuzhiyun ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT);
551*4882a593Smuzhiyun
552*4882a593Smuzhiyun if (ah->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO)
553*4882a593Smuzhiyun return;
554*4882a593Smuzhiyun
555*4882a593Smuzhiyun /* If one of the errors triggered, we can get a superfluous second
556*4882a593Smuzhiyun * interrupt, even though we have already reset the register. The
557*4882a593Smuzhiyun * function detects that so we can return early. */
558*4882a593Smuzhiyun if (ath5k_ani_save_and_clear_phy_errors(ah, as) == 0)
559*4882a593Smuzhiyun return;
560*4882a593Smuzhiyun
561*4882a593Smuzhiyun if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH ||
562*4882a593Smuzhiyun as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH)
563*4882a593Smuzhiyun tasklet_schedule(&ah->ani_tasklet);
564*4882a593Smuzhiyun }
565*4882a593Smuzhiyun
566*4882a593Smuzhiyun /**
567*4882a593Smuzhiyun * ath5k_ani_phy_error_report - Used by older HW to report PHY errors
568*4882a593Smuzhiyun *
569*4882a593Smuzhiyun * @ah: The &struct ath5k_hw
570*4882a593Smuzhiyun * @phyerr: One of enum ath5k_phy_error_code
571*4882a593Smuzhiyun *
572*4882a593Smuzhiyun * This is used by hardware without PHY error counters to report PHY errors
573*4882a593Smuzhiyun * on a frame-by-frame basis, instead of the interrupt.
574*4882a593Smuzhiyun */
575*4882a593Smuzhiyun void
ath5k_ani_phy_error_report(struct ath5k_hw * ah,enum ath5k_phy_error_code phyerr)576*4882a593Smuzhiyun ath5k_ani_phy_error_report(struct ath5k_hw *ah,
577*4882a593Smuzhiyun enum ath5k_phy_error_code phyerr)
578*4882a593Smuzhiyun {
579*4882a593Smuzhiyun struct ath5k_ani_state *as = &ah->ani_state;
580*4882a593Smuzhiyun
581*4882a593Smuzhiyun if (phyerr == AR5K_RX_PHY_ERROR_OFDM_TIMING) {
582*4882a593Smuzhiyun as->ofdm_errors++;
583*4882a593Smuzhiyun if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH)
584*4882a593Smuzhiyun tasklet_schedule(&ah->ani_tasklet);
585*4882a593Smuzhiyun } else if (phyerr == AR5K_RX_PHY_ERROR_CCK_TIMING) {
586*4882a593Smuzhiyun as->cck_errors++;
587*4882a593Smuzhiyun if (as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH)
588*4882a593Smuzhiyun tasklet_schedule(&ah->ani_tasklet);
589*4882a593Smuzhiyun }
590*4882a593Smuzhiyun }
591*4882a593Smuzhiyun
592*4882a593Smuzhiyun
593*4882a593Smuzhiyun /****************\
594*4882a593Smuzhiyun * Initialization *
595*4882a593Smuzhiyun \****************/
596*4882a593Smuzhiyun
597*4882a593Smuzhiyun /**
598*4882a593Smuzhiyun * ath5k_enable_phy_err_counters() - Enable PHY error counters
599*4882a593Smuzhiyun * @ah: The &struct ath5k_hw
600*4882a593Smuzhiyun *
601*4882a593Smuzhiyun * Enable PHY error counters for OFDM and CCK timing errors.
602*4882a593Smuzhiyun */
603*4882a593Smuzhiyun static void
ath5k_enable_phy_err_counters(struct ath5k_hw * ah)604*4882a593Smuzhiyun ath5k_enable_phy_err_counters(struct ath5k_hw *ah)
605*4882a593Smuzhiyun {
606*4882a593Smuzhiyun ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH,
607*4882a593Smuzhiyun AR5K_PHYERR_CNT1);
608*4882a593Smuzhiyun ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH,
609*4882a593Smuzhiyun AR5K_PHYERR_CNT2);
610*4882a593Smuzhiyun ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_OFDM, AR5K_PHYERR_CNT1_MASK);
611*4882a593Smuzhiyun ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_CCK, AR5K_PHYERR_CNT2_MASK);
612*4882a593Smuzhiyun
613*4882a593Smuzhiyun /* not in use */
614*4882a593Smuzhiyun ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT);
615*4882a593Smuzhiyun ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT);
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun /**
619*4882a593Smuzhiyun * ath5k_disable_phy_err_counters() - Disable PHY error counters
620*4882a593Smuzhiyun * @ah: The &struct ath5k_hw
621*4882a593Smuzhiyun *
622*4882a593Smuzhiyun * Disable PHY error counters for OFDM and CCK timing errors.
623*4882a593Smuzhiyun */
624*4882a593Smuzhiyun static void
ath5k_disable_phy_err_counters(struct ath5k_hw * ah)625*4882a593Smuzhiyun ath5k_disable_phy_err_counters(struct ath5k_hw *ah)
626*4882a593Smuzhiyun {
627*4882a593Smuzhiyun ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1);
628*4882a593Smuzhiyun ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2);
629*4882a593Smuzhiyun ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1_MASK);
630*4882a593Smuzhiyun ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2_MASK);
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun /* not in use */
633*4882a593Smuzhiyun ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT);
634*4882a593Smuzhiyun ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT);
635*4882a593Smuzhiyun }
636*4882a593Smuzhiyun
637*4882a593Smuzhiyun /**
638*4882a593Smuzhiyun * ath5k_ani_init() - Initialize ANI
639*4882a593Smuzhiyun * @ah: The &struct ath5k_hw
640*4882a593Smuzhiyun * @mode: One of enum ath5k_ani_mode
641*4882a593Smuzhiyun *
642*4882a593Smuzhiyun * Initialize ANI according to mode.
643*4882a593Smuzhiyun */
644*4882a593Smuzhiyun void
ath5k_ani_init(struct ath5k_hw * ah,enum ath5k_ani_mode mode)645*4882a593Smuzhiyun ath5k_ani_init(struct ath5k_hw *ah, enum ath5k_ani_mode mode)
646*4882a593Smuzhiyun {
647*4882a593Smuzhiyun /* ANI is only possible on 5212 and newer */
648*4882a593Smuzhiyun if (ah->ah_version < AR5K_AR5212)
649*4882a593Smuzhiyun return;
650*4882a593Smuzhiyun
651*4882a593Smuzhiyun if (mode < ATH5K_ANI_MODE_OFF || mode > ATH5K_ANI_MODE_AUTO) {
652*4882a593Smuzhiyun ATH5K_ERR(ah, "ANI mode %d out of range", mode);
653*4882a593Smuzhiyun return;
654*4882a593Smuzhiyun }
655*4882a593Smuzhiyun
656*4882a593Smuzhiyun /* clear old state information */
657*4882a593Smuzhiyun memset(&ah->ani_state, 0, sizeof(ah->ani_state));
658*4882a593Smuzhiyun
659*4882a593Smuzhiyun /* older hardware has more spur levels than newer */
660*4882a593Smuzhiyun if (ah->ah_mac_srev < AR5K_SREV_AR2414)
661*4882a593Smuzhiyun ah->ani_state.max_spur_level = 7;
662*4882a593Smuzhiyun else
663*4882a593Smuzhiyun ah->ani_state.max_spur_level = 2;
664*4882a593Smuzhiyun
665*4882a593Smuzhiyun /* initial values for our ani parameters */
666*4882a593Smuzhiyun if (mode == ATH5K_ANI_MODE_OFF) {
667*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI off\n");
668*4882a593Smuzhiyun } else if (mode == ATH5K_ANI_MODE_MANUAL_LOW) {
669*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
670*4882a593Smuzhiyun "ANI manual low -> high sensitivity\n");
671*4882a593Smuzhiyun ath5k_ani_set_noise_immunity_level(ah, 0);
672*4882a593Smuzhiyun ath5k_ani_set_spur_immunity_level(ah, 0);
673*4882a593Smuzhiyun ath5k_ani_set_firstep_level(ah, 0);
674*4882a593Smuzhiyun ath5k_ani_set_ofdm_weak_signal_detection(ah, true);
675*4882a593Smuzhiyun ath5k_ani_set_cck_weak_signal_detection(ah, true);
676*4882a593Smuzhiyun } else if (mode == ATH5K_ANI_MODE_MANUAL_HIGH) {
677*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
678*4882a593Smuzhiyun "ANI manual high -> low sensitivity\n");
679*4882a593Smuzhiyun ath5k_ani_set_noise_immunity_level(ah,
680*4882a593Smuzhiyun ATH5K_ANI_MAX_NOISE_IMM_LVL);
681*4882a593Smuzhiyun ath5k_ani_set_spur_immunity_level(ah,
682*4882a593Smuzhiyun ah->ani_state.max_spur_level);
683*4882a593Smuzhiyun ath5k_ani_set_firstep_level(ah, ATH5K_ANI_MAX_FIRSTEP_LVL);
684*4882a593Smuzhiyun ath5k_ani_set_ofdm_weak_signal_detection(ah, false);
685*4882a593Smuzhiyun ath5k_ani_set_cck_weak_signal_detection(ah, false);
686*4882a593Smuzhiyun } else if (mode == ATH5K_ANI_MODE_AUTO) {
687*4882a593Smuzhiyun ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI auto\n");
688*4882a593Smuzhiyun ath5k_ani_set_noise_immunity_level(ah, 0);
689*4882a593Smuzhiyun ath5k_ani_set_spur_immunity_level(ah, 0);
690*4882a593Smuzhiyun ath5k_ani_set_firstep_level(ah, 0);
691*4882a593Smuzhiyun ath5k_ani_set_ofdm_weak_signal_detection(ah, true);
692*4882a593Smuzhiyun ath5k_ani_set_cck_weak_signal_detection(ah, false);
693*4882a593Smuzhiyun }
694*4882a593Smuzhiyun
695*4882a593Smuzhiyun /* newer hardware has PHY error counter registers which we can use to
696*4882a593Smuzhiyun * get OFDM and CCK error counts. older hardware has to set rxfilter and
697*4882a593Smuzhiyun * report every single PHY error by calling ath5k_ani_phy_error_report()
698*4882a593Smuzhiyun */
699*4882a593Smuzhiyun if (mode == ATH5K_ANI_MODE_AUTO) {
700*4882a593Smuzhiyun if (ah->ah_capabilities.cap_has_phyerr_counters)
701*4882a593Smuzhiyun ath5k_enable_phy_err_counters(ah);
702*4882a593Smuzhiyun else
703*4882a593Smuzhiyun ath5k_hw_set_rx_filter(ah, ath5k_hw_get_rx_filter(ah) |
704*4882a593Smuzhiyun AR5K_RX_FILTER_PHYERR);
705*4882a593Smuzhiyun } else {
706*4882a593Smuzhiyun if (ah->ah_capabilities.cap_has_phyerr_counters)
707*4882a593Smuzhiyun ath5k_disable_phy_err_counters(ah);
708*4882a593Smuzhiyun else
709*4882a593Smuzhiyun ath5k_hw_set_rx_filter(ah, ath5k_hw_get_rx_filter(ah) &
710*4882a593Smuzhiyun ~AR5K_RX_FILTER_PHYERR);
711*4882a593Smuzhiyun }
712*4882a593Smuzhiyun
713*4882a593Smuzhiyun ah->ani_state.ani_mode = mode;
714*4882a593Smuzhiyun }
715*4882a593Smuzhiyun
716*4882a593Smuzhiyun
717*4882a593Smuzhiyun /**************\
718*4882a593Smuzhiyun * Debug output *
719*4882a593Smuzhiyun \**************/
720*4882a593Smuzhiyun
721*4882a593Smuzhiyun #ifdef CONFIG_ATH5K_DEBUG
722*4882a593Smuzhiyun
723*4882a593Smuzhiyun /**
724*4882a593Smuzhiyun * ath5k_ani_print_counters() - Print ANI counters
725*4882a593Smuzhiyun * @ah: The &struct ath5k_hw
726*4882a593Smuzhiyun *
727*4882a593Smuzhiyun * Used for debugging ANI
728*4882a593Smuzhiyun */
729*4882a593Smuzhiyun void
ath5k_ani_print_counters(struct ath5k_hw * ah)730*4882a593Smuzhiyun ath5k_ani_print_counters(struct ath5k_hw *ah)
731*4882a593Smuzhiyun {
732*4882a593Smuzhiyun /* clears too */
733*4882a593Smuzhiyun pr_notice("ACK fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_ACK_FAIL));
734*4882a593Smuzhiyun pr_notice("RTS fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_FAIL));
735*4882a593Smuzhiyun pr_notice("RTS success\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_OK));
736*4882a593Smuzhiyun pr_notice("FCS error\t%d\n", ath5k_hw_reg_read(ah, AR5K_FCS_FAIL));
737*4882a593Smuzhiyun
738*4882a593Smuzhiyun /* no clear */
739*4882a593Smuzhiyun pr_notice("tx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_TX));
740*4882a593Smuzhiyun pr_notice("rx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RX));
741*4882a593Smuzhiyun pr_notice("busy\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RXCLR));
742*4882a593Smuzhiyun pr_notice("cycles\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_CYCLE));
743*4882a593Smuzhiyun
744*4882a593Smuzhiyun pr_notice("AR5K_PHYERR_CNT1\t%d\n",
745*4882a593Smuzhiyun ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1));
746*4882a593Smuzhiyun pr_notice("AR5K_PHYERR_CNT2\t%d\n",
747*4882a593Smuzhiyun ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2));
748*4882a593Smuzhiyun pr_notice("AR5K_OFDM_FIL_CNT\t%d\n",
749*4882a593Smuzhiyun ath5k_hw_reg_read(ah, AR5K_OFDM_FIL_CNT));
750*4882a593Smuzhiyun pr_notice("AR5K_CCK_FIL_CNT\t%d\n",
751*4882a593Smuzhiyun ath5k_hw_reg_read(ah, AR5K_CCK_FIL_CNT));
752*4882a593Smuzhiyun }
753*4882a593Smuzhiyun
754*4882a593Smuzhiyun #endif
755