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