ar9300_ani.c revision 250003
1/*
2 * Copyright (c) 2013 Qualcomm Atheros, Inc.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "opt_ah.h"
18
19#ifdef AH_SUPPORT_AR9300
20
21#include "ah.h"
22#include "ah_internal.h"
23#include "ah_desc.h"
24#include "ah_pktlog.h"
25
26#include "ar9300/ar9300.h"
27#include "ar9300/ar9300reg.h"
28#include "ar9300/ar9300phy.h"
29
30extern  void ar9300_set_rx_filter(struct ath_hal *ah, u_int32_t bits);
31extern  u_int32_t ar9300_get_rx_filter(struct ath_hal *ah);
32
33#define HAL_ANI_DEBUG 0
34
35/*
36 * Anti noise immunity support.  We track phy errors and react
37 * to excessive errors by adjusting the noise immunity parameters.
38 */
39
40/******************************************************************************
41 *
42 * New Ani Algorithm for Station side only
43 *
44 *****************************************************************************/
45
46#define HAL_ANI_OFDM_TRIG_HIGH     1000 /* units are errors per second */
47#define HAL_ANI_OFDM_TRIG_LOW       400 /* units are errors per second */
48#define HAL_ANI_CCK_TRIG_HIGH       600 /* units are errors per second */
49#define HAL_ANI_CCK_TRIG_LOW        300 /* units are errors per second */
50#define HAL_ANI_USE_OFDM_WEAK_SIG  true
51#define HAL_ANI_ENABLE_MRC_CCK     AH_TRUE /* default is enabled */
52#define HAL_ANI_DEF_SPUR_IMMUNE_LVL   3
53#define HAL_ANI_DEF_FIRSTEP_LVL       2
54#define HAL_ANI_RSSI_THR_HIGH        40
55#define HAL_ANI_RSSI_THR_LOW          7
56#define HAL_ANI_PERIOD             1000
57
58#define HAL_NOISE_DETECT_PERIOD     100
59#define HAL_NOISE_RECOVER_PERIOD    5000
60
61#define HAL_SIG_FIRSTEP_SETTING_MIN   0
62#define HAL_SIG_FIRSTEP_SETTING_MAX  20
63#define HAL_SIG_SPUR_IMM_SETTING_MIN  0
64#define HAL_SIG_SPUR_IMM_SETTING_MAX 22
65
66#define HAL_EP_RND(x, mul) \
67    ((((x) % (mul)) >= ((mul) / 2)) ? ((x) + ((mul) - 1)) / (mul) : (x) / (mul))
68#define BEACON_RSSI(ahp) \
69    HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \
70        HAL_RSSI_EP_MULTIPLIER)
71
72typedef int TABLE[];
73/*
74 *                            level:    0   1   2   3   4   5   6   7   8
75 * firstep_table:    lvl 0-8, default 2
76 */
77static const TABLE firstep_table    = { -4, -2,  0,  2,  4,  6,  8, 10, 12};
78/* cycpwr_thr1_table: lvl 0-7, default 3 */
79static const TABLE cycpwr_thr1_table = { -6, -4, -2,  0,  2,  4,  6,  8 };
80/* values here are relative to the INI */
81
82typedef struct _HAL_ANI_OFDM_LEVEL_ENTRY {
83    int spur_immunity_level;
84    int fir_step_level;
85    int ofdm_weak_signal_on;
86} HAL_ANI_OFDM_LEVEL_ENTRY;
87static const HAL_ANI_OFDM_LEVEL_ENTRY ofdm_level_table[] = {
88/*     SI  FS  WS */
89     {  0,  0,  1  }, /* lvl 0 */
90     {  1,  1,  1  }, /* lvl 1 */
91     {  2,  2,  1  }, /* lvl 2 */
92     {  3,  2,  1  }, /* lvl 3  (default) */
93     {  4,  3,  1  }, /* lvl 4 */
94     {  5,  4,  1  }, /* lvl 5 */
95     {  6,  5,  1  }, /* lvl 6 */
96     {  7,  6,  1  }, /* lvl 7 */
97     {  7,  7,  1  }, /* lvl 8 */
98     {  7,  8,  0  }  /* lvl 9 */
99};
100#define HAL_ANI_OFDM_NUM_LEVEL \
101    (sizeof(ofdm_level_table) / sizeof(ofdm_level_table[0]))
102#define HAL_ANI_OFDM_MAX_LEVEL (HAL_ANI_OFDM_NUM_LEVEL - 1)
103#define HAL_ANI_OFDM_DEF_LEVEL 3 /* default level - matches the INI settings */
104
105typedef struct _HAL_ANI_CCK_LEVEL_ENTRY {
106    int fir_step_level;
107    int mrc_cck_on;
108} HAL_ANI_CCK_LEVEL_ENTRY;
109
110static const HAL_ANI_CCK_LEVEL_ENTRY cck_level_table[] = {
111/*     FS  MRC-CCK */
112     {  0,  1  },  /* lvl 0 */
113     {  1,  1  },  /* lvl 1 */
114     {  2,  1  },  /* lvl 2  (default) */
115     {  3,  1  },  /* lvl 3 */
116     {  4,  0  },  /* lvl 4 */
117     {  5,  0  },  /* lvl 5 */
118     {  6,  0  },  /* lvl 6 */
119     {  7,  0  },  /* lvl 7 (only for high rssi) */
120     {  8,  0  }   /* lvl 8 (only for high rssi) */
121};
122#define HAL_ANI_CCK_NUM_LEVEL \
123    (sizeof(cck_level_table) / sizeof(cck_level_table[0]))
124#define HAL_ANI_CCK_MAX_LEVEL           (HAL_ANI_CCK_NUM_LEVEL - 1)
125#define HAL_ANI_CCK_MAX_LEVEL_LOW_RSSI  (HAL_ANI_CCK_NUM_LEVEL - 3)
126#define HAL_ANI_CCK_DEF_LEVEL 2 /* default level - matches the INI settings */
127
128/*
129 * register values to turn OFDM weak signal detection OFF
130 */
131static const int m1_thresh_low_off     = 127;
132static const int m2_thresh_low_off     = 127;
133static const int m1_thresh_off         = 127;
134static const int m2_thresh_off         = 127;
135static const int m2_count_thr_off      =  31;
136static const int m2_count_thr_low_off  =  63;
137static const int m1_thresh_low_ext_off = 127;
138static const int m2_thresh_low_ext_off = 127;
139static const int m1_thresh_ext_off     = 127;
140static const int m2_thresh_ext_off     = 127;
141
142void
143ar9300_enable_mib_counters(struct ath_hal *ah)
144{
145    HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Enable MIB counters\n", __func__);
146    /* Clear the mib counters and save them in the stats */
147    ar9300_update_mib_mac_stats(ah);
148
149    OS_REG_WRITE(ah, AR_FILT_OFDM, 0);
150    OS_REG_WRITE(ah, AR_FILT_CCK, 0);
151    OS_REG_WRITE(ah, AR_MIBC,
152        ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS) & 0x0f);
153    OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
154    OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
155
156}
157
158void
159ar9300_disable_mib_counters(struct ath_hal *ah)
160{
161    HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Disabling MIB counters\n", __func__);
162
163    OS_REG_WRITE(ah, AR_MIBC,  AR_MIBC_FMC | AR_MIBC_CMC);
164
165    /* Clear the mib counters and save them in the stats */
166    ar9300_update_mib_mac_stats(ah);
167
168    OS_REG_WRITE(ah, AR_FILT_OFDM, 0);
169    OS_REG_WRITE(ah, AR_FILT_CCK, 0);
170}
171
172/*
173 * This routine returns the index into the ani_state array that
174 * corresponds to the channel in *chan.  If no match is found and the
175 * array is still not fully utilized, a new entry is created for the
176 * channel.  We assume the attach function has already initialized the
177 * ah_ani values and only the channel field needs to be set.
178 */
179static int
180ar9300_get_ani_channel_index(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan)
181{
182    struct ath_hal_9300 *ahp = AH9300(ah);
183    int i;
184
185    for (i = 0; i < ARRAY_LENGTH(ahp->ah_ani); i++) {
186        if (ahp->ah_ani[i].c.channel == chan->channel) {
187            return i;
188        }
189        if (ahp->ah_ani[i].c.channel == 0) {
190            ahp->ah_ani[i].c.channel = chan->channel;
191            ahp->ah_ani[i].c.channel_flags = chan->channel_flags;
192            return i;
193        }
194    }
195    /* XXX statistic */
196    HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
197        "%s: No more channel states left. Using channel 0\n", __func__);
198    return 0;        /* XXX gotta return something valid */
199}
200
201/*
202 * Return the current ANI state of the channel we're on
203 */
204struct ar9300_ani_state *
205ar9300_ani_get_current_state(struct ath_hal *ah)
206{
207    return AH9300(ah)->ah_curani;
208}
209
210/*
211 * Return the current statistics.
212 */
213struct ar9300_stats *
214ar9300_ani_get_current_stats(struct ath_hal *ah)
215{
216    return &AH9300(ah)->ah_stats;
217}
218
219/*
220 * Setup ANI handling.  Sets all thresholds and levels to default level AND
221 * resets the channel statistics
222 */
223
224void
225ar9300_ani_attach(struct ath_hal *ah)
226{
227    struct ath_hal_9300 *ahp = AH9300(ah);
228    int i;
229
230    OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani));
231    for (i = 0; i < ARRAY_LENGTH(ahp->ah_ani); i++) {
232        ahp->ah_ani[i].ofdm_trig_high = HAL_ANI_OFDM_TRIG_HIGH;
233        ahp->ah_ani[i].ofdm_trig_low = HAL_ANI_OFDM_TRIG_LOW;
234        ahp->ah_ani[i].cck_trig_high = HAL_ANI_CCK_TRIG_HIGH;
235        ahp->ah_ani[i].cck_trig_low = HAL_ANI_CCK_TRIG_LOW;
236        ahp->ah_ani[i].rssi_thr_high = HAL_ANI_RSSI_THR_HIGH;
237        ahp->ah_ani[i].rssi_thr_low = HAL_ANI_RSSI_THR_LOW;
238        ahp->ah_ani[i].ofdm_noise_immunity_level = HAL_ANI_OFDM_DEF_LEVEL;
239        ahp->ah_ani[i].cck_noise_immunity_level = HAL_ANI_CCK_DEF_LEVEL;
240        ahp->ah_ani[i].ofdm_weak_sig_detect_off = !HAL_ANI_USE_OFDM_WEAK_SIG;
241        ahp->ah_ani[i].spur_immunity_level = HAL_ANI_DEF_SPUR_IMMUNE_LVL;
242        ahp->ah_ani[i].firstep_level = HAL_ANI_DEF_FIRSTEP_LVL;
243        ahp->ah_ani[i].mrc_cck_off = !HAL_ANI_ENABLE_MRC_CCK;
244        ahp->ah_ani[i].ofdms_turn = AH_TRUE;
245        ahp->ah_ani[i].must_restore = AH_FALSE;
246    }
247
248    /*
249     * Since we expect some ongoing maintenance on the tables,
250     * let's sanity check here.
251     * The default level should not modify INI setting.
252     */
253    HALASSERT(firstep_table[HAL_ANI_DEF_FIRSTEP_LVL] == 0);
254    HALASSERT(cycpwr_thr1_table[HAL_ANI_DEF_SPUR_IMMUNE_LVL] == 0);
255    HALASSERT(
256        ofdm_level_table[HAL_ANI_OFDM_DEF_LEVEL].fir_step_level ==
257        HAL_ANI_DEF_FIRSTEP_LVL);
258    HALASSERT(
259        ofdm_level_table[HAL_ANI_OFDM_DEF_LEVEL].spur_immunity_level ==
260        HAL_ANI_DEF_SPUR_IMMUNE_LVL);
261    HALASSERT(
262        cck_level_table[HAL_ANI_CCK_DEF_LEVEL].fir_step_level ==
263        HAL_ANI_DEF_FIRSTEP_LVL);
264
265    /* Initialize and enable MIB Counters */
266    OS_REG_WRITE(ah, AR_PHY_ERR_1, 0);
267    OS_REG_WRITE(ah, AR_PHY_ERR_2, 0);
268    ar9300_enable_mib_counters(ah);
269
270    ahp->ah_ani_period = HAL_ANI_PERIOD;
271    if (AH_PRIVATE(ah)->ah_config.ath_hal_enable_ani) {
272        ahp->ah_proc_phy_err |= HAL_PROCESS_ANI;
273    }
274}
275
276/*
277 * Cleanup any ANI state setup.
278 */
279void
280ar9300_ani_detach(struct ath_hal *ah)
281{
282    HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Detaching Ani\n", __func__);
283    ar9300_disable_mib_counters(ah);
284    OS_REG_WRITE(ah, AR_PHY_ERR_1, 0);
285    OS_REG_WRITE(ah, AR_PHY_ERR_2, 0);
286}
287
288/*
289 * Initialize the ANI register values with default (ini) values.
290 * This routine is called during a (full) hardware reset after
291 * all the registers are initialised from the INI.
292 */
293void
294ar9300_ani_init_defaults(struct ath_hal *ah, HAL_HT_MACMODE macmode)
295{
296    struct ath_hal_9300 *ahp = AH9300(ah);
297    struct ar9300_ani_state *ani_state;
298    HAL_CHANNEL_INTERNAL *chan = AH_PRIVATE(ah)->ah_curchan;
299    int index;
300    u_int32_t val;
301
302    HALASSERT(chan != AH_NULL);
303    index = ar9300_get_ani_channel_index(ah, chan);
304    ani_state = &ahp->ah_ani[index];
305    ahp->ah_curani = ani_state;
306
307    HALDEBUG(ah, HAL_DEBUG_ANI,
308        "%s: ver %d.%d opmode %u chan %d Mhz/0x%x macmode %d\n",
309        __func__, AH_PRIVATE(ah)->ah_macVersion, AH_PRIVATE(ah)->ah_macRev,
310        AH_PRIVATE(ah)->ah_opmode, chan->channel, chan->channel_flags, macmode);
311
312    val = OS_REG_READ(ah, AR_PHY_SFCORR);
313    ani_state->ini_def.m1_thresh = MS(val, AR_PHY_SFCORR_M1_THRESH);
314    ani_state->ini_def.m2_thresh = MS(val, AR_PHY_SFCORR_M2_THRESH);
315    ani_state->ini_def.m2_count_thr = MS(val, AR_PHY_SFCORR_M2COUNT_THR);
316
317    val = OS_REG_READ(ah, AR_PHY_SFCORR_LOW);
318    ani_state->ini_def.m1_thresh_low =
319        MS(val, AR_PHY_SFCORR_LOW_M1_THRESH_LOW);
320    ani_state->ini_def.m2_thresh_low =
321        MS(val, AR_PHY_SFCORR_LOW_M2_THRESH_LOW);
322    ani_state->ini_def.m2_count_thr_low =
323        MS(val, AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW);
324
325    val = OS_REG_READ(ah, AR_PHY_SFCORR_EXT);
326    ani_state->ini_def.m1_thresh_ext = MS(val, AR_PHY_SFCORR_EXT_M1_THRESH);
327    ani_state->ini_def.m2_thresh_ext = MS(val, AR_PHY_SFCORR_EXT_M2_THRESH);
328    ani_state->ini_def.m1_thresh_low_ext =
329        MS(val, AR_PHY_SFCORR_EXT_M1_THRESH_LOW);
330    ani_state->ini_def.m2_thresh_low_ext =
331        MS(val, AR_PHY_SFCORR_EXT_M2_THRESH_LOW);
332
333    ani_state->ini_def.firstep =
334        OS_REG_READ_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRSTEP);
335    ani_state->ini_def.firstep_low =
336        OS_REG_READ_FIELD(
337            ah, AR_PHY_FIND_SIG_LOW, AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW);
338    ani_state->ini_def.cycpwr_thr1 =
339        OS_REG_READ_FIELD(ah, AR_PHY_TIMING5, AR_PHY_TIMING5_CYCPWR_THR1);
340    ani_state->ini_def.cycpwr_thr1_ext =
341        OS_REG_READ_FIELD(ah, AR_PHY_EXT_CCA, AR_PHY_EXT_CYCPWR_THR1);
342
343    /* these levels just got reset to defaults by the INI */
344    ani_state->spur_immunity_level = HAL_ANI_DEF_SPUR_IMMUNE_LVL;
345    ani_state->firstep_level = HAL_ANI_DEF_FIRSTEP_LVL;
346    ani_state->ofdm_weak_sig_detect_off = !HAL_ANI_USE_OFDM_WEAK_SIG;
347    ani_state->mrc_cck_off = !HAL_ANI_ENABLE_MRC_CCK;
348
349    ani_state->cycle_count = 0;
350}
351
352/*
353 * Set the ANI settings to match an OFDM level.
354 */
355static void
356ar9300_ani_set_odfm_noise_immunity_level(struct ath_hal *ah,
357                                   u_int8_t ofdm_noise_immunity_level)
358{
359    struct ath_hal_9300 *ahp = AH9300(ah);
360    struct ar9300_ani_state *ani_state = ahp->ah_curani;
361
362    ani_state->rssi = BEACON_RSSI(ahp);
363    HALDEBUG(ah, HAL_DEBUG_ANI,
364        "**** %s: ofdmlevel %d=>%d, rssi=%d[lo=%d hi=%d]\n", __func__,
365        ani_state->ofdm_noise_immunity_level, ofdm_noise_immunity_level,
366        ani_state->rssi, ani_state->rssi_thr_low, ani_state->rssi_thr_high);
367
368    ani_state->ofdm_noise_immunity_level = ofdm_noise_immunity_level;
369
370    if (ani_state->spur_immunity_level !=
371        ofdm_level_table[ofdm_noise_immunity_level].spur_immunity_level)
372    {
373        ar9300_ani_control(
374            ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
375            ofdm_level_table[ofdm_noise_immunity_level].spur_immunity_level);
376    }
377
378    if (ani_state->firstep_level !=
379            ofdm_level_table[ofdm_noise_immunity_level].fir_step_level &&
380        ofdm_level_table[ofdm_noise_immunity_level].fir_step_level >=
381            cck_level_table[ani_state->cck_noise_immunity_level].fir_step_level)
382    {
383        ar9300_ani_control(
384            ah, HAL_ANI_FIRSTEP_LEVEL,
385            ofdm_level_table[ofdm_noise_immunity_level].fir_step_level);
386    }
387
388    if ((AH_PRIVATE(ah)->ah_opmode != HAL_M_STA ||
389        ani_state->rssi <= ani_state->rssi_thr_high))
390    {
391        if (ani_state->ofdm_weak_sig_detect_off) {
392            /*
393             * force on ofdm weak sig detect.
394             */
395            ar9300_ani_control(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_TRUE);
396        }
397    } else if (ani_state->ofdm_weak_sig_detect_off ==
398               ofdm_level_table[ofdm_noise_immunity_level].ofdm_weak_signal_on)
399    {
400        ar9300_ani_control(
401            ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
402            ofdm_level_table[ofdm_noise_immunity_level].ofdm_weak_signal_on);
403    }
404}
405
406/*
407 * Set the ANI settings to match a CCK level.
408 */
409static void
410ar9300_ani_set_cck_noise_immunity_level(struct ath_hal *ah,
411                                  u_int8_t cck_noise_immunity_level)
412{
413    struct ath_hal_9300 *ahp = AH9300(ah);
414    struct ar9300_ani_state *ani_state = ahp->ah_curani;
415    int level;
416
417    ani_state->rssi = BEACON_RSSI(ahp);
418    HALDEBUG(ah, HAL_DEBUG_ANI,
419        "**** %s: ccklevel %d=>%d, rssi=%d[lo=%d hi=%d]\n",
420        __func__, ani_state->cck_noise_immunity_level, cck_noise_immunity_level,
421        ani_state->rssi, ani_state->rssi_thr_low, ani_state->rssi_thr_high);
422
423    if (AH_PRIVATE(ah)->ah_opmode == HAL_M_STA &&
424        ani_state->rssi <= ani_state->rssi_thr_low &&
425        cck_noise_immunity_level > HAL_ANI_CCK_MAX_LEVEL_LOW_RSSI)
426    {
427        cck_noise_immunity_level = HAL_ANI_CCK_MAX_LEVEL_LOW_RSSI;
428    }
429
430    ani_state->cck_noise_immunity_level = cck_noise_immunity_level;
431
432    level = ani_state->ofdm_noise_immunity_level;
433    if (ani_state->firstep_level !=
434            cck_level_table[cck_noise_immunity_level].fir_step_level &&
435        cck_level_table[cck_noise_immunity_level].fir_step_level >=
436            ofdm_level_table[level].fir_step_level)
437    {
438        ar9300_ani_control(
439            ah, HAL_ANI_FIRSTEP_LEVEL,
440            cck_level_table[cck_noise_immunity_level].fir_step_level);
441    }
442
443    if (ani_state->mrc_cck_off ==
444        cck_level_table[cck_noise_immunity_level].mrc_cck_on)
445    {
446        ar9300_ani_control(
447            ah, HAL_ANI_MRC_CCK,
448            cck_level_table[cck_noise_immunity_level].mrc_cck_on);
449    }
450}
451
452/*
453 * Control Adaptive Noise Immunity Parameters
454 */
455HAL_BOOL
456ar9300_ani_control(struct ath_hal *ah, HAL_ANI_CMD cmd, int param)
457{
458    struct ath_hal_9300 *ahp = AH9300(ah);
459    struct ar9300_ani_state *ani_state = ahp->ah_curani;
460    HAL_CHANNEL_INTERNAL *chan = AH_PRIVATE(ah)->ah_curchan;
461    int32_t value, value2;
462    u_int level = param;
463    u_int is_on;
464
465	if (chan == NULL && cmd != HAL_ANI_MODE) {
466		HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
467            "%s: ignoring cmd 0x%02x - no channel\n", __func__, cmd);
468		return AH_FALSE;
469	}
470
471    switch (cmd & ahp->ah_ani_function) {
472    case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION:
473        {
474            int m1_thresh_low, m2_thresh_low;
475            int m1_thresh, m2_thresh;
476            int m2_count_thr, m2_count_thr_low;
477            int m1_thresh_low_ext, m2_thresh_low_ext;
478            int m1_thresh_ext, m2_thresh_ext;
479            /*
480             * is_on == 1 means ofdm weak signal detection is ON
481             * (default, less noise imm)
482             * is_on == 0 means ofdm weak signal detection is OFF
483             * (more noise imm)
484             */
485            is_on = param ? 1 : 0;
486
487            /*
488             * make register setting for default (weak sig detect ON)
489             * come from INI file
490             */
491            m1_thresh_low    = is_on ?
492                ani_state->ini_def.m1_thresh_low    : m1_thresh_low_off;
493            m2_thresh_low    = is_on ?
494                ani_state->ini_def.m2_thresh_low    : m2_thresh_low_off;
495            m1_thresh       = is_on ?
496                ani_state->ini_def.m1_thresh       : m1_thresh_off;
497            m2_thresh       = is_on ?
498                ani_state->ini_def.m2_thresh       : m2_thresh_off;
499            m2_count_thr     = is_on ?
500                ani_state->ini_def.m2_count_thr     : m2_count_thr_off;
501            m2_count_thr_low  = is_on ?
502                ani_state->ini_def.m2_count_thr_low  : m2_count_thr_low_off;
503            m1_thresh_low_ext = is_on ?
504                ani_state->ini_def.m1_thresh_low_ext : m1_thresh_low_ext_off;
505            m2_thresh_low_ext = is_on ?
506                ani_state->ini_def.m2_thresh_low_ext : m2_thresh_low_ext_off;
507            m1_thresh_ext    = is_on ?
508                ani_state->ini_def.m1_thresh_ext    : m1_thresh_ext_off;
509            m2_thresh_ext    = is_on ?
510                ani_state->ini_def.m2_thresh_ext    : m2_thresh_ext_off;
511            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
512                AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1_thresh_low);
513            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
514                AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2_thresh_low);
515            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M1_THRESH,
516                m1_thresh);
517            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2_THRESH,
518                m2_thresh);
519            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2COUNT_THR,
520                m2_count_thr);
521            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
522                AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2_count_thr_low);
523            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
524                AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1_thresh_low_ext);
525            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
526                AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2_thresh_low_ext);
527            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M1_THRESH,
528                m1_thresh_ext);
529            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M2_THRESH,
530                m2_thresh_ext);
531            if (is_on) {
532                OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
533                    AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
534            } else {
535                OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,
536                    AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
537            }
538            if (!is_on != ani_state->ofdm_weak_sig_detect_off) {
539                HALDEBUG(ah, HAL_DEBUG_ANI,
540                    "%s: ** ch %d: ofdm weak signal: %s=>%s\n",
541                    __func__, chan->channel,
542                    !ani_state->ofdm_weak_sig_detect_off ? "on" : "off",
543                    is_on ? "on" : "off");
544                if (is_on) {
545                    ahp->ah_stats.ast_ani_ofdmon++;
546                } else {
547                    ahp->ah_stats.ast_ani_ofdmoff++;
548                }
549                ani_state->ofdm_weak_sig_detect_off = !is_on;
550            }
551            break;
552        }
553    case HAL_ANI_FIRSTEP_LEVEL:
554        if (level >= ARRAY_LENGTH(firstep_table)) {
555            HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
556                "%s: HAL_ANI_FIRSTEP_LEVEL level out of range (%u > %u)\n",
557                __func__, level, (unsigned) ARRAY_LENGTH(firstep_table));
558            return AH_FALSE;
559        }
560        /*
561         * make register setting relative to default
562         * from INI file & cap value
563         */
564        value =
565            firstep_table[level] -
566            firstep_table[HAL_ANI_DEF_FIRSTEP_LVL] +
567            ani_state->ini_def.firstep;
568        if (value < HAL_SIG_FIRSTEP_SETTING_MIN) {
569            value = HAL_SIG_FIRSTEP_SETTING_MIN;
570        }
571        if (value > HAL_SIG_FIRSTEP_SETTING_MAX) {
572            value = HAL_SIG_FIRSTEP_SETTING_MAX;
573        }
574        OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRSTEP, value);
575        /*
576         * we need to set first step low register too
577         * make register setting relative to default from INI file & cap value
578         */
579        value2 =
580            firstep_table[level] -
581            firstep_table[HAL_ANI_DEF_FIRSTEP_LVL] +
582            ani_state->ini_def.firstep_low;
583        if (value2 < HAL_SIG_FIRSTEP_SETTING_MIN) {
584            value2 = HAL_SIG_FIRSTEP_SETTING_MIN;
585        }
586        if (value2 > HAL_SIG_FIRSTEP_SETTING_MAX) {
587            value2 = HAL_SIG_FIRSTEP_SETTING_MAX;
588        }
589        OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG_LOW,
590            AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW, value2);
591
592        if (level != ani_state->firstep_level) {
593            HALDEBUG(ah, HAL_DEBUG_ANI,
594                "%s: ** ch %d: level %d=>%d[def:%d] firstep[level]=%d ini=%d\n",
595                __func__, chan->channel, ani_state->firstep_level, level,
596                HAL_ANI_DEF_FIRSTEP_LVL, value, ani_state->ini_def.firstep);
597            HALDEBUG(ah, HAL_DEBUG_ANI,
598                "%s: ** ch %d: level %d=>%d[def:%d] "
599                "firstep_low[level]=%d ini=%d\n",
600                __func__, chan->channel, ani_state->firstep_level, level,
601                HAL_ANI_DEF_FIRSTEP_LVL, value2,
602                ani_state->ini_def.firstep_low);
603            if (level > ani_state->firstep_level) {
604                ahp->ah_stats.ast_ani_stepup++;
605            } else if (level < ani_state->firstep_level) {
606                ahp->ah_stats.ast_ani_stepdown++;
607            }
608            ani_state->firstep_level = level;
609        }
610        break;
611    case HAL_ANI_SPUR_IMMUNITY_LEVEL:
612        if (level >= ARRAY_LENGTH(cycpwr_thr1_table)) {
613            HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
614                "%s: HAL_ANI_SPUR_IMMUNITY_LEVEL level "
615                "out of range (%u > %u)\n",
616                __func__, level, (unsigned) ARRAY_LENGTH(cycpwr_thr1_table));
617            return AH_FALSE;
618        }
619        /*
620         * make register setting relative to default from INI file & cap value
621         */
622        value =
623            cycpwr_thr1_table[level] -
624            cycpwr_thr1_table[HAL_ANI_DEF_SPUR_IMMUNE_LVL] +
625            ani_state->ini_def.cycpwr_thr1;
626        if (value < HAL_SIG_SPUR_IMM_SETTING_MIN) {
627            value = HAL_SIG_SPUR_IMM_SETTING_MIN;
628        }
629        if (value > HAL_SIG_SPUR_IMM_SETTING_MAX) {
630            value = HAL_SIG_SPUR_IMM_SETTING_MAX;
631        }
632        OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5, AR_PHY_TIMING5_CYCPWR_THR1, value);
633
634        /*
635         * set AR_PHY_EXT_CCA for extension channel
636         * make register setting relative to default from INI file & cap value
637         */
638        value2 =
639            cycpwr_thr1_table[level] -
640            cycpwr_thr1_table[HAL_ANI_DEF_SPUR_IMMUNE_LVL] +
641            ani_state->ini_def.cycpwr_thr1_ext;
642        if (value2 < HAL_SIG_SPUR_IMM_SETTING_MIN) {
643            value2 = HAL_SIG_SPUR_IMM_SETTING_MIN;
644        }
645        if (value2 > HAL_SIG_SPUR_IMM_SETTING_MAX) {
646            value2 = HAL_SIG_SPUR_IMM_SETTING_MAX;
647        }
648        OS_REG_RMW_FIELD(ah, AR_PHY_EXT_CCA, AR_PHY_EXT_CYCPWR_THR1, value2);
649
650        if (level != ani_state->spur_immunity_level) {
651            HALDEBUG(ah, HAL_DEBUG_ANI,
652                "%s: ** ch %d: level %d=>%d[def:%d] "
653                "cycpwr_thr1[level]=%d ini=%d\n",
654                __func__, chan->channel, ani_state->spur_immunity_level, level,
655                HAL_ANI_DEF_SPUR_IMMUNE_LVL, value,
656                ani_state->ini_def.cycpwr_thr1);
657            HALDEBUG(ah, HAL_DEBUG_ANI,
658                "%s: ** ch %d: level %d=>%d[def:%d] "
659                "cycpwr_thr1_ext[level]=%d ini=%d\n",
660                __func__, chan->channel, ani_state->spur_immunity_level, level,
661                HAL_ANI_DEF_SPUR_IMMUNE_LVL, value2,
662                ani_state->ini_def.cycpwr_thr1_ext);
663            if (level > ani_state->spur_immunity_level) {
664                ahp->ah_stats.ast_ani_spurup++;
665            } else if (level < ani_state->spur_immunity_level) {
666                ahp->ah_stats.ast_ani_spurdown++;
667            }
668            ani_state->spur_immunity_level = level;
669        }
670        break;
671    case HAL_ANI_MRC_CCK:
672        /*
673         * is_on == 1 means MRC CCK ON (default, less noise imm)
674         * is_on == 0 means MRC CCK is OFF (more noise imm)
675         */
676        is_on = param ? 1 : 0;
677        if (!AR_SREV_POSEIDON(ah)) {
678            OS_REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL,
679                AR_PHY_MRC_CCK_ENABLE, is_on);
680            OS_REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL,
681                AR_PHY_MRC_CCK_MUX_REG, is_on);
682        }
683        if (!is_on != ani_state->mrc_cck_off) {
684            HALDEBUG(ah, HAL_DEBUG_ANI,
685                "%s: ** ch %d: MRC CCK: %s=>%s\n", __func__, chan->channel,
686                !ani_state->mrc_cck_off ? "on" : "off", is_on ? "on" : "off");
687            if (is_on) {
688                ahp->ah_stats.ast_ani_ccklow++;
689            } else {
690                ahp->ah_stats.ast_ani_cckhigh++;
691            }
692            ani_state->mrc_cck_off = !is_on;
693        }
694        break;
695    case HAL_ANI_PRESENT:
696        break;
697#ifdef AH_PRIVATE_DIAG
698    case HAL_ANI_MODE:
699        if (param == 0) {
700            ahp->ah_proc_phy_err &= ~HAL_PROCESS_ANI;
701            /* Turn off HW counters if we have them */
702            ar9300_ani_detach(ah);
703            if (AH_PRIVATE(ah)->ah_curchan == NULL) {
704                return AH_TRUE;
705            }
706            /* if we're turning off ANI, reset regs back to INI settings */
707            if (AH_PRIVATE(ah)->ah_config.ath_hal_enable_ani) {
708                HAL_ANI_CMD savefunc = ahp->ah_ani_function;
709                /* temporarly allow all functions so we can reset */
710                ahp->ah_ani_function = HAL_ANI_ALL;
711                HALDEBUG(ah, HAL_DEBUG_ANI,
712                    "%s: disable all ANI functions\n", __func__);
713                ar9300_ani_set_odfm_noise_immunity_level(
714                    ah, HAL_ANI_OFDM_DEF_LEVEL);
715                ar9300_ani_set_cck_noise_immunity_level(
716                    ah, HAL_ANI_CCK_DEF_LEVEL);
717                ahp->ah_ani_function = savefunc;
718            }
719        } else {            /* normal/auto mode */
720            HALDEBUG(ah, HAL_DEBUG_ANI, "%s: enabled\n", __func__);
721            ahp->ah_proc_phy_err |= HAL_PROCESS_ANI;
722            if (AH_PRIVATE(ah)->ah_curchan == NULL) {
723                return AH_TRUE;
724            }
725            ar9300_enable_mib_counters(ah);
726            ar9300_ani_reset(ah, AH_FALSE);
727            ani_state = ahp->ah_curani;
728        }
729        HALDEBUG(ah, HAL_DEBUG_ANI, "5 ANC: ahp->ah_proc_phy_err %x \n",
730                 ahp->ah_proc_phy_err);
731        break;
732    case HAL_ANI_PHYERR_RESET:
733        ahp->ah_stats.ast_ani_ofdmerrs = 0;
734        ahp->ah_stats.ast_ani_cckerrs = 0;
735        break;
736#endif /* AH_PRIVATE_DIAG */
737    default:
738#if HAL_ANI_DEBUG
739        HALDEBUG(ah, HAL_DEBUG_ANI,
740            "%s: invalid cmd 0x%02x (allowed=0x%02x)\n",
741            __func__, cmd, ahp->ah_ani_function);
742#endif
743        return AH_FALSE;
744    }
745
746#if HAL_ANI_DEBUG
747    HALDEBUG(ah, HAL_DEBUG_ANI,
748        "%s: ANI parameters: SI=%d, ofdm_ws=%s FS=%d MRCcck=%s listen_time=%d "
749        "CC=%d listen=%d ofdm_errs=%d cck_errs=%d\n",
750        __func__, ani_state->spur_immunity_level,
751        !ani_state->ofdm_weak_sig_detect_off ? "on" : "off",
752        ani_state->firstep_level, !ani_state->mrc_cck_off ? "on" : "off",
753        ani_state->listen_time, ani_state->cycle_count,
754        ani_state->listen_time, ani_state->ofdm_phy_err_count,
755        ani_state->cck_phy_err_count);
756#endif
757
758#ifndef REMOVE_PKT_LOG
759    /* do pktlog */
760    {
761        struct log_ani log_data;
762
763        /* Populate the ani log record */
764        log_data.phy_stats_disable = DO_ANI(ah);
765        log_data.noise_immun_lvl = ani_state->ofdm_noise_immunity_level;
766        log_data.spur_immun_lvl = ani_state->spur_immunity_level;
767        log_data.ofdm_weak_det = ani_state->ofdm_weak_sig_detect_off;
768        log_data.cck_weak_thr = ani_state->cck_noise_immunity_level;
769        log_data.fir_lvl = ani_state->firstep_level;
770        log_data.listen_time = ani_state->listen_time;
771        log_data.cycle_count = ani_state->cycle_count;
772        /* express ofdm_phy_err_count as errors/second */
773        log_data.ofdm_phy_err_count = ani_state->listen_time ?
774            ani_state->ofdm_phy_err_count * 1000 / ani_state->listen_time : 0;
775        /* express cck_phy_err_count as errors/second */
776        log_data.cck_phy_err_count =  ani_state->listen_time ?
777            ani_state->cck_phy_err_count * 1000 / ani_state->listen_time  : 0;
778        log_data.rssi = ani_state->rssi;
779
780        /* clear interrupt context flag */
781        ath_hal_log_ani(AH_PRIVATE(ah)->ah_sc, &log_data, 0);
782    }
783#endif
784
785    return AH_TRUE;
786}
787
788static void
789ar9300_ani_restart(struct ath_hal *ah)
790{
791    struct ath_hal_9300 *ahp = AH9300(ah);
792    struct ar9300_ani_state *ani_state;
793
794    if (!DO_ANI(ah)) {
795        return;
796    }
797
798    ani_state = ahp->ah_curani;
799
800    ani_state->listen_time = 0;
801
802    OS_REG_WRITE(ah, AR_PHY_ERR_1, 0);
803    OS_REG_WRITE(ah, AR_PHY_ERR_2, 0);
804    OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
805    OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
806
807    /* Clear the mib counters and save them in the stats */
808    ar9300_update_mib_mac_stats(ah);
809
810    ani_state->ofdm_phy_err_count = 0;
811    ani_state->cck_phy_err_count = 0;
812}
813
814static void
815ar9300_ani_ofdm_err_trigger(struct ath_hal *ah)
816{
817    struct ath_hal_9300 *ahp = AH9300(ah);
818    struct ar9300_ani_state *ani_state;
819
820    if (!DO_ANI(ah)) {
821        return;
822    }
823
824    ani_state = ahp->ah_curani;
825
826    if (ani_state->ofdm_noise_immunity_level < HAL_ANI_OFDM_MAX_LEVEL) {
827        ar9300_ani_set_odfm_noise_immunity_level(
828            ah, ani_state->ofdm_noise_immunity_level + 1);
829    }
830}
831
832static void
833ar9300_ani_cck_err_trigger(struct ath_hal *ah)
834{
835    struct ath_hal_9300 *ahp = AH9300(ah);
836    struct ar9300_ani_state *ani_state;
837
838    if (!DO_ANI(ah)) {
839        return;
840    }
841
842    ani_state = ahp->ah_curani;
843
844    if (ani_state->cck_noise_immunity_level < HAL_ANI_CCK_MAX_LEVEL) {
845        ar9300_ani_set_cck_noise_immunity_level(
846            ah, ani_state->cck_noise_immunity_level + 1);
847    }
848}
849
850/*
851 * Restore the ANI parameters in the HAL and reset the statistics.
852 * This routine should be called for every hardware reset and for
853 * every channel change.
854 */
855void
856ar9300_ani_reset(struct ath_hal *ah, HAL_BOOL is_scanning)
857{
858    struct ath_hal_9300 *ahp = AH9300(ah);
859    struct ar9300_ani_state *ani_state;
860    HAL_CHANNEL_INTERNAL *chan = AH_PRIVATE(ah)->ah_curchan;
861    int index;
862
863    HALASSERT(chan != AH_NULL);
864
865    if (!DO_ANI(ah)) {
866        return;
867    }
868
869    /*
870     * we need to re-point to the correct ANI state since the channel
871     * may have changed due to a fast channel change
872    */
873    index = ar9300_get_ani_channel_index(ah, chan);
874    ani_state = &ahp->ah_ani[index];
875    HALASSERT(ani_state != AH_NULL);
876    ahp->ah_curani = ani_state;
877
878    ahp->ah_stats.ast_ani_reset++;
879
880    ani_state->phy_noise_spur = 0;
881
882    /* only allow a subset of functions in AP mode */
883    if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) {
884        if (IS_CHAN_2GHZ(chan)) {
885            ahp->ah_ani_function = (HAL_ANI_SPUR_IMMUNITY_LEVEL |
886                                    HAL_ANI_FIRSTEP_LEVEL |
887                                    HAL_ANI_MRC_CCK);
888        } else {
889            ahp->ah_ani_function = 0;
890        }
891    }
892    /* always allow mode (on/off) to be controlled */
893    ahp->ah_ani_function |= HAL_ANI_MODE;
894
895    if (is_scanning ||
896        (AH_PRIVATE(ah)->ah_opmode != HAL_M_STA &&
897         AH_PRIVATE(ah)->ah_opmode != HAL_M_IBSS))
898    {
899        /*
900         * If we're scanning or in AP mode, the defaults (ini) should be
901         * in place.
902         * For an AP we assume the historical levels for this channel are
903         * probably outdated so start from defaults instead.
904         */
905        if (ani_state->ofdm_noise_immunity_level != HAL_ANI_OFDM_DEF_LEVEL ||
906            ani_state->cck_noise_immunity_level != HAL_ANI_CCK_DEF_LEVEL)
907        {
908            HALDEBUG(ah, HAL_DEBUG_ANI,
909                "%s: Restore defaults: opmode %u chan %d Mhz/0x%x "
910                "is_scanning=%d restore=%d ofdm:%d cck:%d\n",
911                __func__, AH_PRIVATE(ah)->ah_opmode, chan->channel,
912                chan->channel_flags, is_scanning, ani_state->must_restore,
913                ani_state->ofdm_noise_immunity_level,
914                ani_state->cck_noise_immunity_level);
915            /*
916             * for STA/IBSS, we want to restore the historical values later
917             * (when we're not scanning)
918             */
919            if (AH_PRIVATE(ah)->ah_opmode == HAL_M_STA ||
920                AH_PRIVATE(ah)->ah_opmode == HAL_M_IBSS)
921            {
922                ar9300_ani_control(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
923                    HAL_ANI_DEF_SPUR_IMMUNE_LVL);
924                ar9300_ani_control(
925                    ah, HAL_ANI_FIRSTEP_LEVEL, HAL_ANI_DEF_FIRSTEP_LVL);
926                ar9300_ani_control(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
927                    HAL_ANI_USE_OFDM_WEAK_SIG);
928                ar9300_ani_control(ah, HAL_ANI_MRC_CCK, HAL_ANI_ENABLE_MRC_CCK);
929                ani_state->must_restore = AH_TRUE;
930            } else {
931                ar9300_ani_set_odfm_noise_immunity_level(
932                    ah, HAL_ANI_OFDM_DEF_LEVEL);
933                ar9300_ani_set_cck_noise_immunity_level(
934                    ah, HAL_ANI_CCK_DEF_LEVEL);
935            }
936        }
937    } else {
938        /*
939         * restore historical levels for this channel
940         */
941        HALDEBUG(ah, HAL_DEBUG_ANI,
942            "%s: Restore history: opmode %u chan %d Mhz/0x%x is_scanning=%d "
943            "restore=%d ofdm:%d cck:%d\n",
944            __func__, AH_PRIVATE(ah)->ah_opmode, chan->channel,
945            chan->channel_flags, is_scanning, ani_state->must_restore,
946            ani_state->ofdm_noise_immunity_level,
947            ani_state->cck_noise_immunity_level);
948        ar9300_ani_set_odfm_noise_immunity_level(
949            ah, ani_state->ofdm_noise_immunity_level);
950        ar9300_ani_set_cck_noise_immunity_level(
951            ah, ani_state->cck_noise_immunity_level);
952        ani_state->must_restore = AH_FALSE;
953    }
954
955    /* enable phy counters */
956    ar9300_ani_restart(ah);
957    OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
958    OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
959}
960
961/*
962 * Process a MIB interrupt.  We may potentially be invoked because
963 * any of the MIB counters overflow/trigger so don't assume we're
964 * here because a PHY error counter triggered.
965 */
966void
967ar9300_process_mib_intr(struct ath_hal *ah, const HAL_NODE_STATS *stats)
968{
969    struct ath_hal_9300 *ahp = AH9300(ah);
970    u_int32_t phy_cnt1, phy_cnt2;
971
972#if 0
973    HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Processing Mib Intr\n", __func__);
974#endif
975
976    /* Reset these counters regardless */
977    OS_REG_WRITE(ah, AR_FILT_OFDM, 0);
978    OS_REG_WRITE(ah, AR_FILT_CCK, 0);
979    if (!(OS_REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING)) {
980        OS_REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);
981    }
982
983    /* Clear the mib counters and save them in the stats */
984    ar9300_update_mib_mac_stats(ah);
985    ahp->ah_stats.ast_nodestats = *stats;
986
987    if (!DO_ANI(ah)) {
988        /*
989         * We must always clear the interrupt cause by resetting
990         * the phy error regs.
991         */
992        OS_REG_WRITE(ah, AR_PHY_ERR_1, 0);
993        OS_REG_WRITE(ah, AR_PHY_ERR_2, 0);
994        return;
995    }
996
997    /* NB: these are not reset-on-read */
998    phy_cnt1 = OS_REG_READ(ah, AR_PHY_ERR_1);
999    phy_cnt2 = OS_REG_READ(ah, AR_PHY_ERR_2);
1000#if HAL_ANI_DEBUG
1001    HALDEBUG(ah, HAL_DEBUG_ANI,
1002        "%s: Errors: OFDM=0x%08x-0x0=%d   CCK=0x%08x-0x0=%d\n",
1003        __func__, phy_cnt1, phy_cnt1, phy_cnt2, phy_cnt2);
1004#endif
1005    if (((phy_cnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
1006        ((phy_cnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
1007        /* NB: always restart to insure the h/w counters are reset */
1008        ar9300_ani_restart(ah);
1009    }
1010}
1011
1012
1013static void
1014ar9300_ani_lower_immunity(struct ath_hal *ah)
1015{
1016    struct ath_hal_9300 *ahp = AH9300(ah);
1017    struct ar9300_ani_state *ani_state = ahp->ah_curani;
1018
1019    if (ani_state->ofdm_noise_immunity_level > 0 &&
1020        (ani_state->ofdms_turn || ani_state->cck_noise_immunity_level == 0)) {
1021        /*
1022         * lower OFDM noise immunity
1023         */
1024        ar9300_ani_set_odfm_noise_immunity_level(
1025            ah, ani_state->ofdm_noise_immunity_level - 1);
1026
1027        /*
1028         * only lower either OFDM or CCK errors per turn
1029         * we lower the other one next time
1030         */
1031        return;
1032    }
1033
1034    if (ani_state->cck_noise_immunity_level > 0) {
1035        /*
1036         * lower CCK noise immunity
1037         */
1038        ar9300_ani_set_cck_noise_immunity_level(
1039            ah, ani_state->cck_noise_immunity_level - 1);
1040    }
1041}
1042
1043/* convert HW counter values to ms using mode specifix clock rate */
1044#define CLOCK_RATE(_ah)  (ath_hal_chan_2_clock_rate_mhz(_ah) * 1000)
1045
1046/*
1047 * Return an approximation of the time spent ``listening'' by
1048 * deducting the cycles spent tx'ing and rx'ing from the total
1049 * cycle count since our last call.  A return value <0 indicates
1050 * an invalid/inconsistent time.
1051 */
1052static int32_t
1053ar9300_ani_get_listen_time(struct ath_hal *ah, HAL_ANISTATS *ani_stats)
1054{
1055    struct ath_hal_9300 *ahp = AH9300(ah);
1056    struct ar9300_ani_state *ani_state;
1057    u_int32_t tx_frame_count, rx_frame_count, cycle_count;
1058    int32_t listen_time;
1059
1060    tx_frame_count = OS_REG_READ(ah, AR_TFCNT);
1061    rx_frame_count = OS_REG_READ(ah, AR_RFCNT);
1062    cycle_count = OS_REG_READ(ah, AR_CCCNT);
1063
1064    ani_state = ahp->ah_curani;
1065    if (ani_state->cycle_count == 0 || ani_state->cycle_count > cycle_count) {
1066        /*
1067         * Cycle counter wrap (or initial call); it's not possible
1068         * to accurately calculate a value because the registers
1069         * right shift rather than wrap--so punt and return 0.
1070         */
1071        listen_time = 0;
1072        ahp->ah_stats.ast_ani_lzero++;
1073#if HAL_ANI_DEBUG
1074        HALDEBUG(ah, HAL_DEBUG_ANI,
1075            "%s: 1st call: ani_state->cycle_count=%d\n",
1076            __func__, ani_state->cycle_count);
1077#endif
1078    } else {
1079        int32_t ccdelta = cycle_count - ani_state->cycle_count;
1080        int32_t rfdelta = rx_frame_count - ani_state->rx_frame_count;
1081        int32_t tfdelta = tx_frame_count - ani_state->tx_frame_count;
1082        listen_time = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE(ah);
1083#if HAL_ANI_DEBUG
1084        HALDEBUG(ah, HAL_DEBUG_ANI,
1085            "%s: cyclecount=%d, rfcount=%d, tfcount=%d, listen_time=%d "
1086            "CLOCK_RATE=%d\n",
1087            __func__, ccdelta, rfdelta, tfdelta, listen_time, CLOCK_RATE(ah));
1088#endif
1089    }
1090    ani_state->cycle_count = cycle_count;
1091    ani_state->tx_frame_count = tx_frame_count;
1092    ani_state->rx_frame_count = rx_frame_count;
1093    return listen_time;
1094}
1095
1096/*
1097 * Do periodic processing.  This routine is called from a timer
1098 */
1099void
1100ar9300_ani_ar_poll(struct ath_hal *ah, const HAL_NODE_STATS *stats,
1101                HAL_CHANNEL *chan, HAL_ANISTATS *ani_stats)
1102{
1103    struct ath_hal_9300 *ahp = AH9300(ah);
1104    struct ar9300_ani_state *ani_state;
1105    int32_t listen_time;
1106    u_int32_t ofdm_phy_err_rate, cck_phy_err_rate;
1107    u_int32_t ofdm_phy_err_cnt, cck_phy_err_cnt;
1108    HAL_BOOL old_phy_noise_spur;
1109
1110    ani_state = ahp->ah_curani;
1111    ahp->ah_stats.ast_nodestats = *stats;        /* XXX optimize? */
1112
1113    if (ani_state == NULL) {
1114        /* should not happen */
1115        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
1116            "%s: can't poll - no ANI not initialized for this channel\n",
1117            __func__);
1118        return;
1119    }
1120
1121    /*
1122     * ar9300_ani_ar_poll is never called while scanning but we may have been
1123     * scanning and now just restarted polling.  In this case we need to
1124     * restore historical values.
1125     */
1126    if (ani_state->must_restore) {
1127        HALDEBUG(ah, HAL_DEBUG_ANI,
1128            "%s: must restore - calling ar9300_ani_restart\n", __func__);
1129        ar9300_ani_reset(ah, AH_FALSE);
1130        return;
1131    }
1132
1133    listen_time = ar9300_ani_get_listen_time(ah, ani_stats);
1134    if (listen_time <= 0) {
1135        ahp->ah_stats.ast_ani_lneg++;
1136        /* restart ANI period if listen_time is invalid */
1137        HALDEBUG(ah, HAL_DEBUG_ANI,
1138            "%s: listen_time=%d - calling ar9300_ani_restart\n",
1139            __func__, listen_time);
1140        ar9300_ani_restart(ah);
1141        return;
1142    }
1143    /* XXX beware of overflow? */
1144    ani_state->listen_time += listen_time;
1145
1146    /* Clear the mib counters and save them in the stats */
1147    ar9300_update_mib_mac_stats(ah);
1148    /* NB: these are not reset-on-read */
1149    ofdm_phy_err_cnt = OS_REG_READ(ah, AR_PHY_ERR_1);
1150    cck_phy_err_cnt = OS_REG_READ(ah, AR_PHY_ERR_2);
1151
1152
1153
1154    /* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */
1155    ahp->ah_stats.ast_ani_ofdmerrs +=
1156        ofdm_phy_err_cnt - ani_state->ofdm_phy_err_count;
1157    ani_state->ofdm_phy_err_count = ofdm_phy_err_cnt;
1158
1159    ahp->ah_stats.ast_ani_cckerrs +=
1160        cck_phy_err_cnt - ani_state->cck_phy_err_count;
1161    ani_state->cck_phy_err_count = cck_phy_err_cnt;
1162
1163#if HAL_ANI_DEBUG
1164    HALDEBUG(ah, HAL_DEBUG_ANI,
1165        "%s: Errors: OFDM=0x%08x-0x0=%d   CCK=0x%08x-0x0=%d\n",
1166        __func__, ofdm_phy_err_cnt, ofdm_phy_err_cnt,
1167        cck_phy_err_cnt, cck_phy_err_cnt);
1168#endif
1169
1170    /*
1171     * If ani is not enabled, return after we've collected
1172     * statistics
1173     */
1174    if (!DO_ANI(ah)) {
1175        return;
1176    }
1177
1178    ofdm_phy_err_rate =
1179        ani_state->ofdm_phy_err_count * 1000 / ani_state->listen_time;
1180    cck_phy_err_rate =
1181        ani_state->cck_phy_err_count * 1000 / ani_state->listen_time;
1182
1183    HALDEBUG(ah, HAL_DEBUG_ANI,
1184        "%s: listen_time=%d OFDM:%d errs=%d/s CCK:%d errs=%d/s ofdm_turn=%d\n",
1185        __func__, listen_time,
1186        ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate,
1187        ani_state->cck_noise_immunity_level, cck_phy_err_rate,
1188        ani_state->ofdms_turn);
1189
1190    if (ani_state->listen_time >= HAL_NOISE_DETECT_PERIOD) {
1191        old_phy_noise_spur = ani_state->phy_noise_spur;
1192        if (ofdm_phy_err_rate <= ani_state->ofdm_trig_low &&
1193            cck_phy_err_rate <= ani_state->cck_trig_low) {
1194            if (ani_state->listen_time >= HAL_NOISE_RECOVER_PERIOD) {
1195                ani_state->phy_noise_spur = 0;
1196            }
1197        } else {
1198            ani_state->phy_noise_spur = 1;
1199        }
1200        if (old_phy_noise_spur != ani_state->phy_noise_spur) {
1201            HALDEBUG(ah, HAL_DEBUG_ANI,
1202                     "%s: enviroment change from %d to %d\n",
1203                     __func__, old_phy_noise_spur, ani_state->phy_noise_spur);
1204        }
1205    }
1206
1207    if (ani_state->listen_time > 5 * ahp->ah_ani_period) {
1208        /*
1209         * Check to see if need to lower immunity if
1210         * 5 ani_periods have passed
1211         */
1212        if (ofdm_phy_err_rate <= ani_state->ofdm_trig_low &&
1213            cck_phy_err_rate <= ani_state->cck_trig_low)
1214        {
1215            HALDEBUG(ah, HAL_DEBUG_ANI,
1216                "%s: 1. listen_time=%d OFDM:%d errs=%d/s(<%d)  "
1217                "CCK:%d errs=%d/s(<%d) -> ar9300_ani_lower_immunity\n",
1218                __func__, ani_state->listen_time,
1219                ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate,
1220                ani_state->ofdm_trig_low, ani_state->cck_noise_immunity_level,
1221                cck_phy_err_rate, ani_state->cck_trig_low);
1222            ar9300_ani_lower_immunity(ah);
1223            ani_state->ofdms_turn = !ani_state->ofdms_turn;
1224        }
1225        HALDEBUG(ah, HAL_DEBUG_ANI,
1226            "%s: 1 listen_time=%d ofdm=%d/s cck=%d/s - "
1227            "calling ar9300_ani_restart\n",
1228            __func__, ani_state->listen_time,
1229            ofdm_phy_err_rate, cck_phy_err_rate);
1230        ar9300_ani_restart(ah);
1231     } else if (ani_state->listen_time > ahp->ah_ani_period) {
1232        /* check to see if need to raise immunity */
1233        if (ofdm_phy_err_rate > ani_state->ofdm_trig_high &&
1234            (cck_phy_err_rate <= ani_state->cck_trig_high ||
1235             ani_state->ofdms_turn))
1236        {
1237            HALDEBUG(ah, HAL_DEBUG_ANI,
1238                "%s: 2 listen_time=%d OFDM:%d errs=%d/s(>%d) -> "
1239                "ar9300_ani_ofdm_err_trigger\n",
1240                __func__, ani_state->listen_time,
1241                ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate,
1242                ani_state->ofdm_trig_high);
1243            ar9300_ani_ofdm_err_trigger(ah);
1244            ar9300_ani_restart(ah);
1245            ani_state->ofdms_turn = AH_FALSE;
1246        } else if (cck_phy_err_rate > ani_state->cck_trig_high) {
1247            HALDEBUG(ah, HAL_DEBUG_ANI,
1248                "%s: 3 listen_time=%d CCK:%d errs=%d/s(>%d) -> "
1249                "ar9300_ani_cck_err_trigger\n",
1250                __func__, ani_state->listen_time,
1251                ani_state->cck_noise_immunity_level, cck_phy_err_rate,
1252                ani_state->cck_trig_high);
1253            ar9300_ani_cck_err_trigger(ah);
1254            ar9300_ani_restart(ah);
1255            ani_state->ofdms_turn = AH_TRUE;
1256        }
1257    }
1258}
1259
1260/*
1261 * The poll function above calculates short noise spurs, caused by non-80211
1262 * devices, based on OFDM/CCK Phy errs.
1263 * If the noise is short enough, we don't want our ratectrl Algo to stop probing
1264 * higher rates, due to bad PER.
1265 */
1266HAL_BOOL
1267ar9300_is_ani_noise_spur(struct ath_hal *ah)
1268{
1269    struct ath_hal_9300 *ahp = AH9300(ah);
1270    struct ar9300_ani_state *ani_state;
1271
1272    ani_state = ahp->ah_curani;
1273
1274    return ani_state->phy_noise_spur;
1275}
1276
1277#endif /* AH_SUPPORT_AR9300 */
1278