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