Path: blob/main/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_ani.c
48526 views
/*1* Copyright (c) 2013 Qualcomm Atheros, Inc.2*3* Permission to use, copy, modify, and/or distribute this software for any4* purpose with or without fee is hereby granted, provided that the above5* copyright notice and this permission notice appear in all copies.6*7* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH8* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY9* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,10* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM11* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR12* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR13* PERFORMANCE OF THIS SOFTWARE.14*/1516#include "opt_ah.h"1718#include "ah.h"19#include "ah_internal.h"20#include "ah_desc.h"21//#include "ah_pktlog.h"2223#include "ar9300/ar9300.h"24#include "ar9300/ar9300reg.h"25#include "ar9300/ar9300phy.h"2627extern void ar9300_set_rx_filter(struct ath_hal *ah, u_int32_t bits);28extern u_int32_t ar9300_get_rx_filter(struct ath_hal *ah);2930#define HAL_ANI_DEBUG 13132/*33* Anti noise immunity support. We track phy errors and react34* to excessive errors by adjusting the noise immunity parameters.35*/3637/******************************************************************************38*39* New Ani Algorithm for Station side only40*41*****************************************************************************/4243#define HAL_ANI_OFDM_TRIG_HIGH 1000 /* units are errors per second */44#define HAL_ANI_OFDM_TRIG_LOW 400 /* units are errors per second */45#define HAL_ANI_CCK_TRIG_HIGH 600 /* units are errors per second */46#define HAL_ANI_CCK_TRIG_LOW 300 /* units are errors per second */47#define HAL_ANI_USE_OFDM_WEAK_SIG AH_TRUE48#define HAL_ANI_ENABLE_MRC_CCK AH_TRUE /* default is enabled */49#define HAL_ANI_DEF_SPUR_IMMUNE_LVL 350#define HAL_ANI_DEF_FIRSTEP_LVL 251#define HAL_ANI_RSSI_THR_HIGH 4052#define HAL_ANI_RSSI_THR_LOW 753#define HAL_ANI_PERIOD 10005455#define HAL_NOISE_DETECT_PERIOD 10056#define HAL_NOISE_RECOVER_PERIOD 50005758#define HAL_SIG_FIRSTEP_SETTING_MIN 059#define HAL_SIG_FIRSTEP_SETTING_MAX 2060#define HAL_SIG_SPUR_IMM_SETTING_MIN 061#define HAL_SIG_SPUR_IMM_SETTING_MAX 226263#define HAL_EP_RND(x, mul) \64((((x) % (mul)) >= ((mul) / 2)) ? ((x) + ((mul) - 1)) / (mul) : (x) / (mul))65#define BEACON_RSSI(ahp) \66HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \67HAL_RSSI_EP_MULTIPLIER)6869typedef int TABLE[];70/*71* level: 0 1 2 3 4 5 6 7 872* firstep_table: lvl 0-8, default 273*/74static const TABLE firstep_table = { -4, -2, 0, 2, 4, 6, 8, 10, 12};75/* cycpwr_thr1_table: lvl 0-7, default 3 */76static const TABLE cycpwr_thr1_table = { -6, -4, -2, 0, 2, 4, 6, 8 };77/* values here are relative to the INI */7879typedef struct _HAL_ANI_OFDM_LEVEL_ENTRY {80int spur_immunity_level;81int fir_step_level;82int ofdm_weak_signal_on;83} HAL_ANI_OFDM_LEVEL_ENTRY;84static const HAL_ANI_OFDM_LEVEL_ENTRY ofdm_level_table[] = {85/* SI FS WS */86{ 0, 0, 1 }, /* lvl 0 */87{ 1, 1, 1 }, /* lvl 1 */88{ 2, 2, 1 }, /* lvl 2 */89{ 3, 2, 1 }, /* lvl 3 (default) */90{ 4, 3, 1 }, /* lvl 4 */91{ 5, 4, 1 }, /* lvl 5 */92{ 6, 5, 1 }, /* lvl 6 */93{ 7, 6, 1 }, /* lvl 7 */94{ 7, 7, 1 }, /* lvl 8 */95{ 7, 8, 0 } /* lvl 9 */96};97#define HAL_ANI_OFDM_NUM_LEVEL \98(sizeof(ofdm_level_table) / sizeof(ofdm_level_table[0]))99#define HAL_ANI_OFDM_MAX_LEVEL (HAL_ANI_OFDM_NUM_LEVEL - 1)100#define HAL_ANI_OFDM_DEF_LEVEL 3 /* default level - matches the INI settings */101102typedef struct _HAL_ANI_CCK_LEVEL_ENTRY {103int fir_step_level;104int mrc_cck_on;105} HAL_ANI_CCK_LEVEL_ENTRY;106107static const HAL_ANI_CCK_LEVEL_ENTRY cck_level_table[] = {108/* FS MRC-CCK */109{ 0, 1 }, /* lvl 0 */110{ 1, 1 }, /* lvl 1 */111{ 2, 1 }, /* lvl 2 (default) */112{ 3, 1 }, /* lvl 3 */113{ 4, 0 }, /* lvl 4 */114{ 5, 0 }, /* lvl 5 */115{ 6, 0 }, /* lvl 6 */116{ 7, 0 }, /* lvl 7 (only for high rssi) */117{ 8, 0 } /* lvl 8 (only for high rssi) */118};119#define HAL_ANI_CCK_NUM_LEVEL \120(sizeof(cck_level_table) / sizeof(cck_level_table[0]))121#define HAL_ANI_CCK_MAX_LEVEL (HAL_ANI_CCK_NUM_LEVEL - 1)122#define HAL_ANI_CCK_MAX_LEVEL_LOW_RSSI (HAL_ANI_CCK_NUM_LEVEL - 3)123#define HAL_ANI_CCK_DEF_LEVEL 2 /* default level - matches the INI settings */124125/*126* register values to turn OFDM weak signal detection OFF127*/128static const int m1_thresh_low_off = 127;129static const int m2_thresh_low_off = 127;130static const int m1_thresh_off = 127;131static const int m2_thresh_off = 127;132static const int m2_count_thr_off = 31;133static const int m2_count_thr_low_off = 63;134static const int m1_thresh_low_ext_off = 127;135static const int m2_thresh_low_ext_off = 127;136static const int m1_thresh_ext_off = 127;137static const int m2_thresh_ext_off = 127;138139void140ar9300_enable_mib_counters(struct ath_hal *ah)141{142HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Enable MIB counters\n", __func__);143/* Clear the mib counters and save them in the stats */144ar9300_update_mib_mac_stats(ah);145146OS_REG_WRITE(ah, AR_FILT_OFDM, 0);147OS_REG_WRITE(ah, AR_FILT_CCK, 0);148OS_REG_WRITE(ah, AR_MIBC,149~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS) & 0x0f);150OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);151OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);152153}154155void156ar9300_disable_mib_counters(struct ath_hal *ah)157{158HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Disabling MIB counters\n", __func__);159160OS_REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC | AR_MIBC_CMC);161162/* Clear the mib counters and save them in the stats */163ar9300_update_mib_mac_stats(ah);164165OS_REG_WRITE(ah, AR_FILT_OFDM, 0);166OS_REG_WRITE(ah, AR_FILT_CCK, 0);167}168169/*170* This routine returns the index into the ani_state array that171* corresponds to the channel in *chan. If no match is found and the172* array is still not fully utilized, a new entry is created for the173* channel. We assume the attach function has already initialized the174* ah_ani values and only the channel field needs to be set.175*/176static int177ar9300_get_ani_channel_index(struct ath_hal *ah,178const struct ieee80211_channel *chan)179{180struct ath_hal_9300 *ahp = AH9300(ah);181int i;182183for (i = 0; i < ARRAY_LENGTH(ahp->ah_ani); i++) {184/* XXX this doesn't distinguish between 20/40 channels */185if (ahp->ah_ani[i].c.ic_freq == chan->ic_freq) {186return i;187}188if (ahp->ah_ani[i].c.ic_freq == 0) {189ahp->ah_ani[i].c.ic_freq = chan->ic_freq;190ahp->ah_ani[i].c.ic_flags = chan->ic_flags;191return i;192}193}194/* XXX statistic */195HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,196"%s: No more channel states left. Using channel 0\n", __func__);197return 0; /* XXX gotta return something valid */198}199200/*201* Return the current ANI state of the channel we're on202*/203struct ar9300_ani_state *204ar9300_ani_get_current_state(struct ath_hal *ah)205{206return AH9300(ah)->ah_curani;207}208209/*210* Return the current statistics.211*/212HAL_ANI_STATS *213ar9300_ani_get_current_stats(struct ath_hal *ah)214{215return &AH9300(ah)->ah_stats;216}217218/*219* Setup ANI handling. Sets all thresholds and levels to default level AND220* resets the channel statistics221*/222223void224ar9300_ani_attach(struct ath_hal *ah)225{226struct ath_hal_9300 *ahp = AH9300(ah);227int i;228229OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani));230for (i = 0; i < ARRAY_LENGTH(ahp->ah_ani); i++) {231ahp->ah_ani[i].ofdm_trig_high = HAL_ANI_OFDM_TRIG_HIGH;232ahp->ah_ani[i].ofdm_trig_low = HAL_ANI_OFDM_TRIG_LOW;233ahp->ah_ani[i].cck_trig_high = HAL_ANI_CCK_TRIG_HIGH;234ahp->ah_ani[i].cck_trig_low = HAL_ANI_CCK_TRIG_LOW;235ahp->ah_ani[i].rssi_thr_high = HAL_ANI_RSSI_THR_HIGH;236ahp->ah_ani[i].rssi_thr_low = HAL_ANI_RSSI_THR_LOW;237ahp->ah_ani[i].ofdm_noise_immunity_level = HAL_ANI_OFDM_DEF_LEVEL;238ahp->ah_ani[i].cck_noise_immunity_level = HAL_ANI_CCK_DEF_LEVEL;239ahp->ah_ani[i].ofdm_weak_sig_detect_off = !HAL_ANI_USE_OFDM_WEAK_SIG;240ahp->ah_ani[i].spur_immunity_level = HAL_ANI_DEF_SPUR_IMMUNE_LVL;241ahp->ah_ani[i].firstep_level = HAL_ANI_DEF_FIRSTEP_LVL;242ahp->ah_ani[i].mrc_cck_off = !HAL_ANI_ENABLE_MRC_CCK;243ahp->ah_ani[i].ofdms_turn = AH_TRUE;244ahp->ah_ani[i].must_restore = AH_FALSE;245}246247/*248* Since we expect some ongoing maintenance on the tables,249* let's sanity check here.250* The default level should not modify INI setting.251*/252HALASSERT(firstep_table[HAL_ANI_DEF_FIRSTEP_LVL] == 0);253HALASSERT(cycpwr_thr1_table[HAL_ANI_DEF_SPUR_IMMUNE_LVL] == 0);254HALASSERT(255ofdm_level_table[HAL_ANI_OFDM_DEF_LEVEL].fir_step_level ==256HAL_ANI_DEF_FIRSTEP_LVL);257HALASSERT(258ofdm_level_table[HAL_ANI_OFDM_DEF_LEVEL].spur_immunity_level ==259HAL_ANI_DEF_SPUR_IMMUNE_LVL);260HALASSERT(261cck_level_table[HAL_ANI_CCK_DEF_LEVEL].fir_step_level ==262HAL_ANI_DEF_FIRSTEP_LVL);263264/* Initialize and enable MIB Counters */265OS_REG_WRITE(ah, AR_PHY_ERR_1, 0);266OS_REG_WRITE(ah, AR_PHY_ERR_2, 0);267ar9300_enable_mib_counters(ah);268269ahp->ah_ani_period = HAL_ANI_PERIOD;270if (ah->ah_config.ath_hal_enable_ani) {271ahp->ah_proc_phy_err |= HAL_PROCESS_ANI;272}273}274275/*276* Cleanup any ANI state setup.277*/278void279ar9300_ani_detach(struct ath_hal *ah)280{281HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Detaching Ani\n", __func__);282ar9300_disable_mib_counters(ah);283OS_REG_WRITE(ah, AR_PHY_ERR_1, 0);284OS_REG_WRITE(ah, AR_PHY_ERR_2, 0);285}286287/*288* Initialize the ANI register values with default (ini) values.289* This routine is called during a (full) hardware reset after290* all the registers are initialised from the INI.291*/292void293ar9300_ani_init_defaults(struct ath_hal *ah, HAL_HT_MACMODE macmode)294{295struct ath_hal_9300 *ahp = AH9300(ah);296struct ar9300_ani_state *ani_state;297const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;298int index;299u_int32_t val;300301HALASSERT(chan != AH_NULL);302index = ar9300_get_ani_channel_index(ah, chan);303ani_state = &ahp->ah_ani[index];304ahp->ah_curani = ani_state;305306HALDEBUG(ah, HAL_DEBUG_ANI,307"%s: ver %d.%d opmode %u chan %d Mhz/0x%x macmode %d\n",308__func__, AH_PRIVATE(ah)->ah_macVersion, AH_PRIVATE(ah)->ah_macRev,309AH_PRIVATE(ah)->ah_opmode, chan->ic_freq, chan->ic_flags, macmode);310311val = OS_REG_READ(ah, AR_PHY_SFCORR);312ani_state->ini_def.m1_thresh = MS(val, AR_PHY_SFCORR_M1_THRESH);313ani_state->ini_def.m2_thresh = MS(val, AR_PHY_SFCORR_M2_THRESH);314ani_state->ini_def.m2_count_thr = MS(val, AR_PHY_SFCORR_M2COUNT_THR);315316val = OS_REG_READ(ah, AR_PHY_SFCORR_LOW);317ani_state->ini_def.m1_thresh_low =318MS(val, AR_PHY_SFCORR_LOW_M1_THRESH_LOW);319ani_state->ini_def.m2_thresh_low =320MS(val, AR_PHY_SFCORR_LOW_M2_THRESH_LOW);321ani_state->ini_def.m2_count_thr_low =322MS(val, AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW);323324val = OS_REG_READ(ah, AR_PHY_SFCORR_EXT);325ani_state->ini_def.m1_thresh_ext = MS(val, AR_PHY_SFCORR_EXT_M1_THRESH);326ani_state->ini_def.m2_thresh_ext = MS(val, AR_PHY_SFCORR_EXT_M2_THRESH);327ani_state->ini_def.m1_thresh_low_ext =328MS(val, AR_PHY_SFCORR_EXT_M1_THRESH_LOW);329ani_state->ini_def.m2_thresh_low_ext =330MS(val, AR_PHY_SFCORR_EXT_M2_THRESH_LOW);331332ani_state->ini_def.firstep =333OS_REG_READ_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRSTEP);334ani_state->ini_def.firstep_low =335OS_REG_READ_FIELD(336ah, AR_PHY_FIND_SIG_LOW, AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW);337ani_state->ini_def.cycpwr_thr1 =338OS_REG_READ_FIELD(ah, AR_PHY_TIMING5, AR_PHY_TIMING5_CYCPWR_THR1);339ani_state->ini_def.cycpwr_thr1_ext =340OS_REG_READ_FIELD(ah, AR_PHY_EXT_CCA, AR_PHY_EXT_CYCPWR_THR1);341342/* these levels just got reset to defaults by the INI */343ani_state->spur_immunity_level = HAL_ANI_DEF_SPUR_IMMUNE_LVL;344ani_state->firstep_level = HAL_ANI_DEF_FIRSTEP_LVL;345ani_state->ofdm_weak_sig_detect_off = !HAL_ANI_USE_OFDM_WEAK_SIG;346ani_state->mrc_cck_off = !HAL_ANI_ENABLE_MRC_CCK;347348ani_state->cycle_count = 0;349}350351/*352* Set the ANI settings to match an OFDM level.353*/354static void355ar9300_ani_set_odfm_noise_immunity_level(struct ath_hal *ah,356u_int8_t ofdm_noise_immunity_level)357{358struct ath_hal_9300 *ahp = AH9300(ah);359struct ar9300_ani_state *ani_state = ahp->ah_curani;360361ani_state->rssi = BEACON_RSSI(ahp);362HALDEBUG(ah, HAL_DEBUG_ANI,363"**** %s: ofdmlevel %d=>%d, rssi=%d[lo=%d hi=%d]\n", __func__,364ani_state->ofdm_noise_immunity_level, ofdm_noise_immunity_level,365ani_state->rssi, ani_state->rssi_thr_low, ani_state->rssi_thr_high);366367ani_state->ofdm_noise_immunity_level = ofdm_noise_immunity_level;368369if (ani_state->spur_immunity_level !=370ofdm_level_table[ofdm_noise_immunity_level].spur_immunity_level)371{372ar9300_ani_control(373ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,374ofdm_level_table[ofdm_noise_immunity_level].spur_immunity_level);375}376377if (ani_state->firstep_level !=378ofdm_level_table[ofdm_noise_immunity_level].fir_step_level &&379ofdm_level_table[ofdm_noise_immunity_level].fir_step_level >=380cck_level_table[ani_state->cck_noise_immunity_level].fir_step_level)381{382ar9300_ani_control(383ah, HAL_ANI_FIRSTEP_LEVEL,384ofdm_level_table[ofdm_noise_immunity_level].fir_step_level);385}386387if ((AH_PRIVATE(ah)->ah_opmode != HAL_M_STA ||388ani_state->rssi <= ani_state->rssi_thr_high))389{390if (ani_state->ofdm_weak_sig_detect_off) {391/*392* force on ofdm weak sig detect.393*/394ar9300_ani_control(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_TRUE);395}396} else if (ani_state->ofdm_weak_sig_detect_off ==397ofdm_level_table[ofdm_noise_immunity_level].ofdm_weak_signal_on)398{399ar9300_ani_control(400ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,401ofdm_level_table[ofdm_noise_immunity_level].ofdm_weak_signal_on);402}403}404405/*406* Set the ANI settings to match a CCK level.407*/408static void409ar9300_ani_set_cck_noise_immunity_level(struct ath_hal *ah,410u_int8_t cck_noise_immunity_level)411{412struct ath_hal_9300 *ahp = AH9300(ah);413struct ar9300_ani_state *ani_state = ahp->ah_curani;414int level;415416ani_state->rssi = BEACON_RSSI(ahp);417HALDEBUG(ah, HAL_DEBUG_ANI,418"**** %s: ccklevel %d=>%d, rssi=%d[lo=%d hi=%d]\n",419__func__, ani_state->cck_noise_immunity_level, cck_noise_immunity_level,420ani_state->rssi, ani_state->rssi_thr_low, ani_state->rssi_thr_high);421422if (AH_PRIVATE(ah)->ah_opmode == HAL_M_STA &&423ani_state->rssi <= ani_state->rssi_thr_low &&424cck_noise_immunity_level > HAL_ANI_CCK_MAX_LEVEL_LOW_RSSI)425{426cck_noise_immunity_level = HAL_ANI_CCK_MAX_LEVEL_LOW_RSSI;427}428429ani_state->cck_noise_immunity_level = cck_noise_immunity_level;430431level = ani_state->ofdm_noise_immunity_level;432if (ani_state->firstep_level !=433cck_level_table[cck_noise_immunity_level].fir_step_level &&434cck_level_table[cck_noise_immunity_level].fir_step_level >=435ofdm_level_table[level].fir_step_level)436{437ar9300_ani_control(438ah, HAL_ANI_FIRSTEP_LEVEL,439cck_level_table[cck_noise_immunity_level].fir_step_level);440}441442if (ani_state->mrc_cck_off ==443cck_level_table[cck_noise_immunity_level].mrc_cck_on)444{445ar9300_ani_control(446ah, HAL_ANI_MRC_CCK,447cck_level_table[cck_noise_immunity_level].mrc_cck_on);448}449}450451/*452* Control Adaptive Noise Immunity Parameters453*/454HAL_BOOL455ar9300_ani_control(struct ath_hal *ah, HAL_ANI_CMD cmd, int param)456{457struct ath_hal_9300 *ahp = AH9300(ah);458struct ar9300_ani_state *ani_state = ahp->ah_curani;459const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;460int32_t value, value2;461u_int level = param;462u_int is_on;463464HALDEBUG(ah, HAL_DEBUG_ANI, "%s: cmd=%d, param=%d, chan=%p, funcmask=0x%08x\n",465__func__,466cmd,467param,468chan,469ahp->ah_ani_function);470471472if (chan == NULL && cmd != HAL_ANI_MODE) {473HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,474"%s: ignoring cmd 0x%02x - no channel\n", __func__, cmd);475return AH_FALSE;476}477478/*479* These two control the top-level cck/ofdm immunity levels and will480* program the rest of the values.481*/482if (cmd == HAL_ANI_NOISE_IMMUNITY_LEVEL) {483if (param > HAL_ANI_OFDM_NUM_LEVEL)484return AH_FALSE;485ar9300_ani_set_odfm_noise_immunity_level(ah, param);486return AH_TRUE;487}488489if (cmd == HAL_ANI_CCK_NOISE_IMMUNITY_LEVEL) {490if (param > HAL_ANI_CCK_NUM_LEVEL)491return AH_FALSE;492ar9300_ani_set_cck_noise_immunity_level(ah, param);493return AH_TRUE;494}495496/*497* Check to see if this command is available in the498* current operating mode.499*/500if (((1 << cmd) & ahp->ah_ani_function) == 0) {501HALDEBUG(ah, HAL_DEBUG_ANI,502"%s: early check: invalid cmd 0x%02x (allowed=0x%02x)\n",503__func__, cmd, ahp->ah_ani_function);504return AH_FALSE;505}506507/*508* The rest of these program in the requested parameter values509* into the PHY.510*/511switch (cmd) {512513case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION:514{515int m1_thresh_low, m2_thresh_low;516int m1_thresh, m2_thresh;517int m2_count_thr, m2_count_thr_low;518int m1_thresh_low_ext, m2_thresh_low_ext;519int m1_thresh_ext, m2_thresh_ext;520/*521* is_on == 1 means ofdm weak signal detection is ON522* (default, less noise imm)523* is_on == 0 means ofdm weak signal detection is OFF524* (more noise imm)525*/526is_on = param ? 1 : 0;527528if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah))529goto skip_ws_det;530531/*532* make register setting for default (weak sig detect ON)533* come from INI file534*/535m1_thresh_low = is_on ?536ani_state->ini_def.m1_thresh_low : m1_thresh_low_off;537m2_thresh_low = is_on ?538ani_state->ini_def.m2_thresh_low : m2_thresh_low_off;539m1_thresh = is_on ?540ani_state->ini_def.m1_thresh : m1_thresh_off;541m2_thresh = is_on ?542ani_state->ini_def.m2_thresh : m2_thresh_off;543m2_count_thr = is_on ?544ani_state->ini_def.m2_count_thr : m2_count_thr_off;545m2_count_thr_low = is_on ?546ani_state->ini_def.m2_count_thr_low : m2_count_thr_low_off;547m1_thresh_low_ext = is_on ?548ani_state->ini_def.m1_thresh_low_ext : m1_thresh_low_ext_off;549m2_thresh_low_ext = is_on ?550ani_state->ini_def.m2_thresh_low_ext : m2_thresh_low_ext_off;551m1_thresh_ext = is_on ?552ani_state->ini_def.m1_thresh_ext : m1_thresh_ext_off;553m2_thresh_ext = is_on ?554ani_state->ini_def.m2_thresh_ext : m2_thresh_ext_off;555OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,556AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1_thresh_low);557OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,558AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2_thresh_low);559OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M1_THRESH,560m1_thresh);561OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2_THRESH,562m2_thresh);563OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2COUNT_THR,564m2_count_thr);565OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,566AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2_count_thr_low);567OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,568AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1_thresh_low_ext);569OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,570AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2_thresh_low_ext);571OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M1_THRESH,572m1_thresh_ext);573OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M2_THRESH,574m2_thresh_ext);575skip_ws_det:576if (is_on) {577OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,578AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);579} else {580OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,581AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);582}583if ((!is_on) != ani_state->ofdm_weak_sig_detect_off) {584HALDEBUG(ah, HAL_DEBUG_ANI,585"%s: ** ch %d: ofdm weak signal: %s=>%s\n",586__func__, chan->ic_freq,587!ani_state->ofdm_weak_sig_detect_off ? "on" : "off",588is_on ? "on" : "off");589if (is_on) {590ahp->ah_stats.ast_ani_ofdmon++;591} else {592ahp->ah_stats.ast_ani_ofdmoff++;593}594ani_state->ofdm_weak_sig_detect_off = !is_on;595}596break;597}598case HAL_ANI_FIRSTEP_LEVEL:599if (level >= ARRAY_LENGTH(firstep_table)) {600HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,601"%s: HAL_ANI_FIRSTEP_LEVEL level out of range (%u > %u)\n",602__func__, level, (unsigned) ARRAY_LENGTH(firstep_table));603return AH_FALSE;604}605/*606* make register setting relative to default607* from INI file & cap value608*/609value =610firstep_table[level] -611firstep_table[HAL_ANI_DEF_FIRSTEP_LVL] +612ani_state->ini_def.firstep;613if (value < HAL_SIG_FIRSTEP_SETTING_MIN) {614value = HAL_SIG_FIRSTEP_SETTING_MIN;615}616if (value > HAL_SIG_FIRSTEP_SETTING_MAX) {617value = HAL_SIG_FIRSTEP_SETTING_MAX;618}619OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRSTEP, value);620/*621* we need to set first step low register too622* make register setting relative to default from INI file & cap value623*/624value2 =625firstep_table[level] -626firstep_table[HAL_ANI_DEF_FIRSTEP_LVL] +627ani_state->ini_def.firstep_low;628if (value2 < HAL_SIG_FIRSTEP_SETTING_MIN) {629value2 = HAL_SIG_FIRSTEP_SETTING_MIN;630}631if (value2 > HAL_SIG_FIRSTEP_SETTING_MAX) {632value2 = HAL_SIG_FIRSTEP_SETTING_MAX;633}634OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG_LOW,635AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW, value2);636637if (level != ani_state->firstep_level) {638HALDEBUG(ah, HAL_DEBUG_ANI,639"%s: ** ch %d: level %d=>%d[def:%d] firstep[level]=%d ini=%d\n",640__func__, chan->ic_freq, ani_state->firstep_level, level,641HAL_ANI_DEF_FIRSTEP_LVL, value, ani_state->ini_def.firstep);642HALDEBUG(ah, HAL_DEBUG_ANI,643"%s: ** ch %d: level %d=>%d[def:%d] "644"firstep_low[level]=%d ini=%d\n",645__func__, chan->ic_freq, ani_state->firstep_level, level,646HAL_ANI_DEF_FIRSTEP_LVL, value2,647ani_state->ini_def.firstep_low);648if (level > ani_state->firstep_level) {649ahp->ah_stats.ast_ani_stepup++;650} else if (level < ani_state->firstep_level) {651ahp->ah_stats.ast_ani_stepdown++;652}653ani_state->firstep_level = level;654}655break;656case HAL_ANI_SPUR_IMMUNITY_LEVEL:657if (level >= ARRAY_LENGTH(cycpwr_thr1_table)) {658HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,659"%s: HAL_ANI_SPUR_IMMUNITY_LEVEL level "660"out of range (%u > %u)\n",661__func__, level, (unsigned) ARRAY_LENGTH(cycpwr_thr1_table));662return AH_FALSE;663}664/*665* make register setting relative to default from INI file & cap value666*/667value =668cycpwr_thr1_table[level] -669cycpwr_thr1_table[HAL_ANI_DEF_SPUR_IMMUNE_LVL] +670ani_state->ini_def.cycpwr_thr1;671if (value < HAL_SIG_SPUR_IMM_SETTING_MIN) {672value = HAL_SIG_SPUR_IMM_SETTING_MIN;673}674if (value > HAL_SIG_SPUR_IMM_SETTING_MAX) {675value = HAL_SIG_SPUR_IMM_SETTING_MAX;676}677OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5, AR_PHY_TIMING5_CYCPWR_THR1, value);678679/*680* set AR_PHY_EXT_CCA for extension channel681* make register setting relative to default from INI file & cap value682*/683value2 =684cycpwr_thr1_table[level] -685cycpwr_thr1_table[HAL_ANI_DEF_SPUR_IMMUNE_LVL] +686ani_state->ini_def.cycpwr_thr1_ext;687if (value2 < HAL_SIG_SPUR_IMM_SETTING_MIN) {688value2 = HAL_SIG_SPUR_IMM_SETTING_MIN;689}690if (value2 > HAL_SIG_SPUR_IMM_SETTING_MAX) {691value2 = HAL_SIG_SPUR_IMM_SETTING_MAX;692}693OS_REG_RMW_FIELD(ah, AR_PHY_EXT_CCA, AR_PHY_EXT_CYCPWR_THR1, value2);694695if (level != ani_state->spur_immunity_level) {696HALDEBUG(ah, HAL_DEBUG_ANI,697"%s: ** ch %d: level %d=>%d[def:%d] "698"cycpwr_thr1[level]=%d ini=%d\n",699__func__, chan->ic_freq, ani_state->spur_immunity_level, level,700HAL_ANI_DEF_SPUR_IMMUNE_LVL, value,701ani_state->ini_def.cycpwr_thr1);702HALDEBUG(ah, HAL_DEBUG_ANI,703"%s: ** ch %d: level %d=>%d[def:%d] "704"cycpwr_thr1_ext[level]=%d ini=%d\n",705__func__, chan->ic_freq, ani_state->spur_immunity_level, level,706HAL_ANI_DEF_SPUR_IMMUNE_LVL, value2,707ani_state->ini_def.cycpwr_thr1_ext);708if (level > ani_state->spur_immunity_level) {709ahp->ah_stats.ast_ani_spurup++;710} else if (level < ani_state->spur_immunity_level) {711ahp->ah_stats.ast_ani_spurdown++;712}713ani_state->spur_immunity_level = level;714}715break;716case HAL_ANI_MRC_CCK:717/*718* is_on == 1 means MRC CCK ON (default, less noise imm)719* is_on == 0 means MRC CCK is OFF (more noise imm)720*/721is_on = param ? 1 : 0;722if (!AR_SREV_POSEIDON(ah)) {723OS_REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL,724AR_PHY_MRC_CCK_ENABLE, is_on);725OS_REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL,726AR_PHY_MRC_CCK_MUX_REG, is_on);727}728if ((!is_on) != ani_state->mrc_cck_off) {729HALDEBUG(ah, HAL_DEBUG_ANI,730"%s: ** ch %d: MRC CCK: %s=>%s\n", __func__, chan->ic_freq,731!ani_state->mrc_cck_off ? "on" : "off", is_on ? "on" : "off");732if (is_on) {733ahp->ah_stats.ast_ani_ccklow++;734} else {735ahp->ah_stats.ast_ani_cckhigh++;736}737ani_state->mrc_cck_off = !is_on;738}739break;740case HAL_ANI_PRESENT:741break;742#ifdef AH_PRIVATE_DIAG743case HAL_ANI_MODE:744if (param == 0) {745ahp->ah_proc_phy_err &= ~HAL_PROCESS_ANI;746/* Turn off HW counters if we have them */747ar9300_ani_detach(ah);748if (AH_PRIVATE(ah)->ah_curchan == NULL) {749return AH_TRUE;750}751/* if we're turning off ANI, reset regs back to INI settings */752if (ah->ah_config.ath_hal_enable_ani) {753HAL_ANI_CMD savefunc = ahp->ah_ani_function;754/* temporarly allow all functions so we can reset */755ahp->ah_ani_function = HAL_ANI_ALL;756HALDEBUG(ah, HAL_DEBUG_ANI,757"%s: disable all ANI functions\n", __func__);758ar9300_ani_set_odfm_noise_immunity_level(759ah, HAL_ANI_OFDM_DEF_LEVEL);760ar9300_ani_set_cck_noise_immunity_level(761ah, HAL_ANI_CCK_DEF_LEVEL);762ahp->ah_ani_function = savefunc;763}764} else { /* normal/auto mode */765HALDEBUG(ah, HAL_DEBUG_ANI, "%s: enabled\n", __func__);766ahp->ah_proc_phy_err |= HAL_PROCESS_ANI;767if (AH_PRIVATE(ah)->ah_curchan == NULL) {768return AH_TRUE;769}770ar9300_enable_mib_counters(ah);771ar9300_ani_reset(ah, AH_FALSE);772ani_state = ahp->ah_curani;773}774HALDEBUG(ah, HAL_DEBUG_ANI, "5 ANC: ahp->ah_proc_phy_err %x \n",775ahp->ah_proc_phy_err);776break;777case HAL_ANI_PHYERR_RESET:778ahp->ah_stats.ast_ani_ofdmerrs = 0;779ahp->ah_stats.ast_ani_cckerrs = 0;780break;781#endif /* AH_PRIVATE_DIAG */782default:783#if HAL_ANI_DEBUG784HALDEBUG(ah, HAL_DEBUG_ANI,785"%s: invalid cmd 0x%02x (allowed=0x%02x)\n",786__func__, cmd, ahp->ah_ani_function);787#endif788return AH_FALSE;789}790791#if HAL_ANI_DEBUG792HALDEBUG(ah, HAL_DEBUG_ANI,793"%s: ANI parameters: SI=%d, ofdm_ws=%s FS=%d MRCcck=%s listen_time=%d "794"CC=%d listen=%d ofdm_errs=%d cck_errs=%d\n",795__func__, ani_state->spur_immunity_level,796!ani_state->ofdm_weak_sig_detect_off ? "on" : "off",797ani_state->firstep_level, !ani_state->mrc_cck_off ? "on" : "off",798ani_state->listen_time, ani_state->cycle_count,799ani_state->listen_time, ani_state->ofdm_phy_err_count,800ani_state->cck_phy_err_count);801#endif802803#ifndef REMOVE_PKT_LOG804/* do pktlog */805{806struct log_ani log_data;807808/* Populate the ani log record */809log_data.phy_stats_disable = DO_ANI(ah);810log_data.noise_immun_lvl = ani_state->ofdm_noise_immunity_level;811log_data.spur_immun_lvl = ani_state->spur_immunity_level;812log_data.ofdm_weak_det = ani_state->ofdm_weak_sig_detect_off;813log_data.cck_weak_thr = ani_state->cck_noise_immunity_level;814log_data.fir_lvl = ani_state->firstep_level;815log_data.listen_time = ani_state->listen_time;816log_data.cycle_count = ani_state->cycle_count;817/* express ofdm_phy_err_count as errors/second */818log_data.ofdm_phy_err_count = ani_state->listen_time ?819ani_state->ofdm_phy_err_count * 1000 / ani_state->listen_time : 0;820/* express cck_phy_err_count as errors/second */821log_data.cck_phy_err_count = ani_state->listen_time ?822ani_state->cck_phy_err_count * 1000 / ani_state->listen_time : 0;823log_data.rssi = ani_state->rssi;824825/* clear interrupt context flag */826ath_hal_log_ani(AH_PRIVATE(ah)->ah_sc, &log_data, 0);827}828#endif829830return AH_TRUE;831}832833static void834ar9300_ani_restart(struct ath_hal *ah)835{836struct ath_hal_9300 *ahp = AH9300(ah);837struct ar9300_ani_state *ani_state;838839if (!DO_ANI(ah)) {840return;841}842843ani_state = ahp->ah_curani;844845ani_state->listen_time = 0;846847OS_REG_WRITE(ah, AR_PHY_ERR_1, 0);848OS_REG_WRITE(ah, AR_PHY_ERR_2, 0);849OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);850OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);851852/* Clear the mib counters and save them in the stats */853ar9300_update_mib_mac_stats(ah);854855ani_state->ofdm_phy_err_count = 0;856ani_state->cck_phy_err_count = 0;857}858859static void860ar9300_ani_ofdm_err_trigger(struct ath_hal *ah)861{862struct ath_hal_9300 *ahp = AH9300(ah);863struct ar9300_ani_state *ani_state;864865if (!DO_ANI(ah)) {866return;867}868869ani_state = ahp->ah_curani;870871if (ani_state->ofdm_noise_immunity_level < HAL_ANI_OFDM_MAX_LEVEL) {872ar9300_ani_set_odfm_noise_immunity_level(873ah, ani_state->ofdm_noise_immunity_level + 1);874}875}876877static void878ar9300_ani_cck_err_trigger(struct ath_hal *ah)879{880struct ath_hal_9300 *ahp = AH9300(ah);881struct ar9300_ani_state *ani_state;882883if (!DO_ANI(ah)) {884return;885}886887ani_state = ahp->ah_curani;888889if (ani_state->cck_noise_immunity_level < HAL_ANI_CCK_MAX_LEVEL) {890ar9300_ani_set_cck_noise_immunity_level(891ah, ani_state->cck_noise_immunity_level + 1);892}893}894895/*896* Restore the ANI parameters in the HAL and reset the statistics.897* This routine should be called for every hardware reset and for898* every channel change.899*/900void901ar9300_ani_reset(struct ath_hal *ah, HAL_BOOL is_scanning)902{903struct ath_hal_9300 *ahp = AH9300(ah);904struct ar9300_ani_state *ani_state;905const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;906HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);907int index;908909HALASSERT(chan != AH_NULL);910911if (!DO_ANI(ah)) {912return;913}914915/*916* we need to re-point to the correct ANI state since the channel917* may have changed due to a fast channel change918*/919index = ar9300_get_ani_channel_index(ah, chan);920ani_state = &ahp->ah_ani[index];921HALASSERT(ani_state != AH_NULL);922ahp->ah_curani = ani_state;923924ahp->ah_stats.ast_ani_reset++;925926ani_state->phy_noise_spur = 0;927928/* only allow a subset of functions in AP mode */929if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) {930if (IS_CHAN_2GHZ(ichan)) {931ahp->ah_ani_function = (1 << HAL_ANI_SPUR_IMMUNITY_LEVEL) |932(1 << HAL_ANI_FIRSTEP_LEVEL) |933(1 << HAL_ANI_MRC_CCK);934} else {935ahp->ah_ani_function = 0;936}937} else {938ahp->ah_ani_function = HAL_ANI_ALL;939}940941/* always allow mode (on/off) to be controlled */942ahp->ah_ani_function |= HAL_ANI_MODE;943944if (is_scanning ||945(AH_PRIVATE(ah)->ah_opmode != HAL_M_STA &&946AH_PRIVATE(ah)->ah_opmode != HAL_M_IBSS))947{948/*949* If we're scanning or in AP mode, the defaults (ini) should be950* in place.951* For an AP we assume the historical levels for this channel are952* probably outdated so start from defaults instead.953*/954if (ani_state->ofdm_noise_immunity_level != HAL_ANI_OFDM_DEF_LEVEL ||955ani_state->cck_noise_immunity_level != HAL_ANI_CCK_DEF_LEVEL)956{957HALDEBUG(ah, HAL_DEBUG_ANI,958"%s: Restore defaults: opmode %u chan %d Mhz/0x%x "959"is_scanning=%d restore=%d ofdm:%d cck:%d\n",960__func__, AH_PRIVATE(ah)->ah_opmode, chan->ic_freq,961chan->ic_flags, is_scanning, ani_state->must_restore,962ani_state->ofdm_noise_immunity_level,963ani_state->cck_noise_immunity_level);964/*965* for STA/IBSS, we want to restore the historical values later966* (when we're not scanning)967*/968if (AH_PRIVATE(ah)->ah_opmode == HAL_M_STA ||969AH_PRIVATE(ah)->ah_opmode == HAL_M_IBSS)970{971ar9300_ani_control(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,972HAL_ANI_DEF_SPUR_IMMUNE_LVL);973ar9300_ani_control(974ah, HAL_ANI_FIRSTEP_LEVEL, HAL_ANI_DEF_FIRSTEP_LVL);975ar9300_ani_control(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,976HAL_ANI_USE_OFDM_WEAK_SIG);977ar9300_ani_control(ah, HAL_ANI_MRC_CCK, HAL_ANI_ENABLE_MRC_CCK);978ani_state->must_restore = AH_TRUE;979} else {980ar9300_ani_set_odfm_noise_immunity_level(981ah, HAL_ANI_OFDM_DEF_LEVEL);982ar9300_ani_set_cck_noise_immunity_level(983ah, HAL_ANI_CCK_DEF_LEVEL);984}985}986} else {987/*988* restore historical levels for this channel989*/990HALDEBUG(ah, HAL_DEBUG_ANI,991"%s: Restore history: opmode %u chan %d Mhz/0x%x is_scanning=%d "992"restore=%d ofdm:%d cck:%d\n",993__func__, AH_PRIVATE(ah)->ah_opmode, chan->ic_freq,994chan->ic_flags, is_scanning, ani_state->must_restore,995ani_state->ofdm_noise_immunity_level,996ani_state->cck_noise_immunity_level);997ar9300_ani_set_odfm_noise_immunity_level(998ah, ani_state->ofdm_noise_immunity_level);999ar9300_ani_set_cck_noise_immunity_level(1000ah, ani_state->cck_noise_immunity_level);1001ani_state->must_restore = AH_FALSE;1002}10031004/* enable phy counters */1005ar9300_ani_restart(ah);1006OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);1007OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);1008}10091010/*1011* Process a MIB interrupt. We may potentially be invoked because1012* any of the MIB counters overflow/trigger so don't assume we're1013* here because a PHY error counter triggered.1014*/1015void1016ar9300_process_mib_intr(struct ath_hal *ah, const HAL_NODE_STATS *stats)1017{1018struct ath_hal_9300 *ahp = AH9300(ah);1019u_int32_t phy_cnt1, phy_cnt2;10201021#if 01022HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Processing Mib Intr\n", __func__);1023#endif10241025/* Reset these counters regardless */1026OS_REG_WRITE(ah, AR_FILT_OFDM, 0);1027OS_REG_WRITE(ah, AR_FILT_CCK, 0);1028if (!(OS_REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING)) {1029OS_REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);1030}10311032/* Clear the mib counters and save them in the stats */1033ar9300_update_mib_mac_stats(ah);1034ahp->ah_stats.ast_nodestats = *stats;10351036if (!DO_ANI(ah)) {1037/*1038* We must always clear the interrupt cause by resetting1039* the phy error regs.1040*/1041OS_REG_WRITE(ah, AR_PHY_ERR_1, 0);1042OS_REG_WRITE(ah, AR_PHY_ERR_2, 0);1043return;1044}10451046/* NB: these are not reset-on-read */1047phy_cnt1 = OS_REG_READ(ah, AR_PHY_ERR_1);1048phy_cnt2 = OS_REG_READ(ah, AR_PHY_ERR_2);1049#if HAL_ANI_DEBUG1050HALDEBUG(ah, HAL_DEBUG_ANI,1051"%s: Errors: OFDM=0x%08x-0x0=%d CCK=0x%08x-0x0=%d\n",1052__func__, phy_cnt1, phy_cnt1, phy_cnt2, phy_cnt2);1053#endif1054if (((phy_cnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||1055((phy_cnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {1056/* NB: always restart to insure the h/w counters are reset */1057ar9300_ani_restart(ah);1058}1059}106010611062static void1063ar9300_ani_lower_immunity(struct ath_hal *ah)1064{1065struct ath_hal_9300 *ahp = AH9300(ah);1066struct ar9300_ani_state *ani_state = ahp->ah_curani;10671068if (ani_state->ofdm_noise_immunity_level > 0 &&1069(ani_state->ofdms_turn || ani_state->cck_noise_immunity_level == 0)) {1070/*1071* lower OFDM noise immunity1072*/1073ar9300_ani_set_odfm_noise_immunity_level(1074ah, ani_state->ofdm_noise_immunity_level - 1);10751076/*1077* only lower either OFDM or CCK errors per turn1078* we lower the other one next time1079*/1080return;1081}10821083if (ani_state->cck_noise_immunity_level > 0) {1084/*1085* lower CCK noise immunity1086*/1087ar9300_ani_set_cck_noise_immunity_level(1088ah, ani_state->cck_noise_immunity_level - 1);1089}1090}10911092/* convert HW counter values to ms using mode specifix clock rate */1093//#define CLOCK_RATE(_ah) (ath_hal_chan_2_clock_rate_mhz(_ah) * 1000)1094#define CLOCK_RATE(_ah) (ath_hal_mac_clks(ah, 1000))10951096/*1097* Return an approximation of the time spent ``listening'' by1098* deducting the cycles spent tx'ing and rx'ing from the total1099* cycle count since our last call. A return value <0 indicates1100* an invalid/inconsistent time.1101*/1102static int32_t1103ar9300_ani_get_listen_time(struct ath_hal *ah, HAL_ANISTATS *ani_stats)1104{1105struct ath_hal_9300 *ahp = AH9300(ah);1106struct ar9300_ani_state *ani_state;1107u_int32_t tx_frame_count, rx_frame_count, cycle_count;1108u_int32_t rx_busy_count, rx_ext_busy_count;1109int32_t listen_time;11101111tx_frame_count = OS_REG_READ(ah, AR_TFCNT);1112rx_frame_count = OS_REG_READ(ah, AR_RFCNT);1113rx_busy_count = OS_REG_READ(ah, AR_RCCNT);1114rx_ext_busy_count = OS_REG_READ(ah, AR_EXTRCCNT);1115cycle_count = OS_REG_READ(ah, AR_CCCNT);11161117ani_state = ahp->ah_curani;1118if (ani_state->cycle_count == 0 || ani_state->cycle_count > cycle_count) {1119/*1120* Cycle counter wrap (or initial call); it's not possible1121* to accurately calculate a value because the registers1122* right shift rather than wrap--so punt and return 0.1123*/1124listen_time = 0;1125ahp->ah_stats.ast_ani_lzero++;1126#if HAL_ANI_DEBUG1127HALDEBUG(ah, HAL_DEBUG_ANI,1128"%s: 1st call: ani_state->cycle_count=%d\n",1129__func__, ani_state->cycle_count);1130#endif1131} else {1132int32_t ccdelta = cycle_count - ani_state->cycle_count;1133int32_t rfdelta = rx_frame_count - ani_state->rx_frame_count;1134int32_t tfdelta = tx_frame_count - ani_state->tx_frame_count;1135int32_t rcdelta = rx_busy_count - ani_state->rx_busy_count;1136int32_t extrcdelta = rx_ext_busy_count - ani_state->rx_ext_busy_count;1137listen_time = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE(ah);1138//#if HAL_ANI_DEBUG1139HALDEBUG(ah, HAL_DEBUG_ANI,1140"%s: cyclecount=%d, rfcount=%d, tfcount=%d, rcdelta=%d, extrcdelta=%d, listen_time=%d "1141"CLOCK_RATE=%d\n",1142__func__, ccdelta, rfdelta, tfdelta, rcdelta, extrcdelta,1143listen_time, CLOCK_RATE(ah));1144//#endif1145/* Populate as appropriate */1146ani_stats->cyclecnt_diff = ccdelta;1147ani_stats->rxclr_cnt = rcdelta;1148ani_stats->txframecnt_diff = tfdelta;1149ani_stats->rxframecnt_diff = rfdelta;1150ani_stats->extrxclr_cnt = extrcdelta;1151ani_stats->listen_time = listen_time;1152ani_stats->valid = AH_TRUE;1153}1154ani_state->cycle_count = cycle_count;1155ani_state->tx_frame_count = tx_frame_count;1156ani_state->rx_frame_count = rx_frame_count;1157ani_state->rx_busy_count = rx_busy_count;1158ani_state->rx_ext_busy_count = rx_ext_busy_count;1159return listen_time;1160}11611162/*1163* Do periodic processing. This routine is called from a timer1164*/1165void1166ar9300_ani_ar_poll(struct ath_hal *ah, const HAL_NODE_STATS *stats,1167const struct ieee80211_channel *chan, HAL_ANISTATS *ani_stats)1168{1169struct ath_hal_9300 *ahp = AH9300(ah);1170struct ar9300_ani_state *ani_state;1171int32_t listen_time;1172u_int32_t ofdm_phy_err_rate, cck_phy_err_rate;1173u_int32_t ofdm_phy_err_cnt, cck_phy_err_cnt;1174HAL_BOOL old_phy_noise_spur;11751176ani_state = ahp->ah_curani;1177ahp->ah_stats.ast_nodestats = *stats; /* XXX optimize? */11781179if (ani_state == NULL) {1180/* should not happen */1181HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,1182"%s: can't poll - no ANI not initialized for this channel\n",1183__func__);1184return;1185}11861187/*1188* ar9300_ani_ar_poll is never called while scanning but we may have been1189* scanning and now just restarted polling. In this case we need to1190* restore historical values.1191*/1192if (ani_state->must_restore) {1193HALDEBUG(ah, HAL_DEBUG_ANI,1194"%s: must restore - calling ar9300_ani_restart\n", __func__);1195ar9300_ani_reset(ah, AH_FALSE);1196return;1197}11981199listen_time = ar9300_ani_get_listen_time(ah, ani_stats);1200if (listen_time <= 0) {1201ahp->ah_stats.ast_ani_lneg++;1202/* restart ANI period if listen_time is invalid */1203HALDEBUG(ah, HAL_DEBUG_ANI,1204"%s: listen_time=%d - calling ar9300_ani_restart\n",1205__func__, listen_time);1206ar9300_ani_restart(ah);1207return;1208}1209/* XXX beware of overflow? */1210ani_state->listen_time += listen_time;12111212/* Clear the mib counters and save them in the stats */1213ar9300_update_mib_mac_stats(ah);1214/* NB: these are not reset-on-read */1215ofdm_phy_err_cnt = OS_REG_READ(ah, AR_PHY_ERR_1);1216cck_phy_err_cnt = OS_REG_READ(ah, AR_PHY_ERR_2);12171218/* Populate HAL_ANISTATS */1219/* XXX TODO: are these correct? */1220if (ani_stats) {1221ani_stats->cckphyerr_cnt =1222cck_phy_err_cnt - ani_state->cck_phy_err_count;1223ani_stats->ofdmphyerrcnt_diff =1224ofdm_phy_err_cnt - ani_state->ofdm_phy_err_count;1225}12261227/* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */1228ahp->ah_stats.ast_ani_ofdmerrs +=1229ofdm_phy_err_cnt - ani_state->ofdm_phy_err_count;1230ani_state->ofdm_phy_err_count = ofdm_phy_err_cnt;12311232ahp->ah_stats.ast_ani_cckerrs +=1233cck_phy_err_cnt - ani_state->cck_phy_err_count;1234ani_state->cck_phy_err_count = cck_phy_err_cnt;12351236/*1237* Note - the ANI code is using the aggregate listen time.1238* The AR_PHY_CNT1/AR_PHY_CNT2 registers here are also1239* free running, not clear-on-read and are free-running.1240*1241* So, ofdm_phy_err_rate / cck_phy_err_rate are accumulating1242* the same as listenTime is accumulating.1243*/12441245#if HAL_ANI_DEBUG1246HALDEBUG(ah, HAL_DEBUG_ANI,1247"%s: Errors: OFDM=0x%08x-0x0=%d CCK=0x%08x-0x0=%d\n",1248__func__, ofdm_phy_err_cnt, ofdm_phy_err_cnt,1249cck_phy_err_cnt, cck_phy_err_cnt);1250#endif12511252/*1253* If ani is not enabled, return after we've collected1254* statistics1255*/1256if (!DO_ANI(ah)) {1257return;1258}12591260/*1261* Calculate the OFDM/CCK phy error rate over the listen time interval.1262* This is used in subsequent math to see if the OFDM/CCK phy error rate1263* is above or below the threshold checks.1264*/12651266ofdm_phy_err_rate =1267ani_state->ofdm_phy_err_count * 1000 / ani_state->listen_time;1268cck_phy_err_rate =1269ani_state->cck_phy_err_count * 1000 / ani_state->listen_time;12701271HALDEBUG(ah, HAL_DEBUG_ANI,1272"%s: listen_time=%d (total: %d) OFDM:%d errs=%d/s CCK:%d errs=%d/s ofdm_turn=%d\n",1273__func__, listen_time,1274ani_state->listen_time,1275ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate,1276ani_state->cck_noise_immunity_level, cck_phy_err_rate,1277ani_state->ofdms_turn);12781279/*1280* Check for temporary noise spurs. This is intended to be used by1281* rate control to check if we should try higher packet rates or not.1282* If the noise period is short enough then we shouldn't avoid trying1283* higher rates but if the noise is high/sustained then it's likely1284* not a great idea to try the higher MCS rates.1285*/1286if (ani_state->listen_time >= HAL_NOISE_DETECT_PERIOD) {1287old_phy_noise_spur = ani_state->phy_noise_spur;1288if (ofdm_phy_err_rate <= ani_state->ofdm_trig_low &&1289cck_phy_err_rate <= ani_state->cck_trig_low) {1290if (ani_state->listen_time >= HAL_NOISE_RECOVER_PERIOD) {1291ani_state->phy_noise_spur = 0;1292}1293} else {1294ani_state->phy_noise_spur = 1;1295}1296if (old_phy_noise_spur != ani_state->phy_noise_spur) {1297HALDEBUG(ah, HAL_DEBUG_ANI,1298"%s: environment change from %d to %d\n",1299__func__, old_phy_noise_spur, ani_state->phy_noise_spur);1300}1301}13021303if (ani_state->listen_time > 5 * ahp->ah_ani_period) {1304/*1305* Check to see if need to lower immunity if1306* 5 ani_periods have passed1307*/1308if (ofdm_phy_err_rate <= ani_state->ofdm_trig_low &&1309cck_phy_err_rate <= ani_state->cck_trig_low)1310{1311HALDEBUG(ah, HAL_DEBUG_ANI,1312"%s: 1. listen_time=%d OFDM:%d errs=%d/s(<%d) "1313"CCK:%d errs=%d/s(<%d) -> ar9300_ani_lower_immunity\n",1314__func__, ani_state->listen_time,1315ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate,1316ani_state->ofdm_trig_low, ani_state->cck_noise_immunity_level,1317cck_phy_err_rate, ani_state->cck_trig_low);1318ar9300_ani_lower_immunity(ah);1319ani_state->ofdms_turn = !ani_state->ofdms_turn;1320}1321/*1322* Force an ANI restart regardless of whether the lower immunity1323* level was met.1324*/1325HALDEBUG(ah, HAL_DEBUG_ANI,1326"%s: 1 listen_time=%d ofdm=%d/s cck=%d/s - "1327"calling ar9300_ani_restart\n",1328__func__, ani_state->listen_time,1329ofdm_phy_err_rate, cck_phy_err_rate);1330ar9300_ani_restart(ah);1331} else if (ani_state->listen_time > ahp->ah_ani_period) {1332/* check to see if need to raise immunity */1333if (ofdm_phy_err_rate > ani_state->ofdm_trig_high &&1334(cck_phy_err_rate <= ani_state->cck_trig_high ||1335ani_state->ofdms_turn))1336{1337HALDEBUG(ah, HAL_DEBUG_ANI,1338"%s: 2 listen_time=%d OFDM:%d errs=%d/s(>%d) -> "1339"ar9300_ani_ofdm_err_trigger\n",1340__func__, ani_state->listen_time,1341ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate,1342ani_state->ofdm_trig_high);1343ar9300_ani_ofdm_err_trigger(ah);1344ar9300_ani_restart(ah);1345ani_state->ofdms_turn = AH_FALSE;1346} else if (cck_phy_err_rate > ani_state->cck_trig_high) {1347HALDEBUG(ah, HAL_DEBUG_ANI,1348"%s: 3 listen_time=%d CCK:%d errs=%d/s(>%d) -> "1349"ar9300_ani_cck_err_trigger\n",1350__func__, ani_state->listen_time,1351ani_state->cck_noise_immunity_level, cck_phy_err_rate,1352ani_state->cck_trig_high);1353ar9300_ani_cck_err_trigger(ah);1354ar9300_ani_restart(ah);1355ani_state->ofdms_turn = AH_TRUE;1356}1357}13581359/*1360* Note that currently this poll function doesn't reset the listen1361* time after it accumulates a second worth of error samples.1362* It will continue to accumulate samples until a counter overflows,1363* or a raise threshold is met, or 5 seconds passes.1364*/1365}13661367/*1368* The poll function above calculates short noise spurs, caused by non-802111369* devices, based on OFDM/CCK Phy errs.1370* If the noise is short enough, we don't want our ratectrl Algo to stop probing1371* higher rates, due to bad PER.1372*/1373HAL_BOOL1374ar9300_is_ani_noise_spur(struct ath_hal *ah)1375{1376struct ath_hal_9300 *ahp = AH9300(ah);1377struct ar9300_ani_state *ani_state;13781379ani_state = ahp->ah_curani;13801381return ani_state->phy_noise_spur;1382}1383138413851386