162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (C) 2010 Bruno Randolf <br1@einfach.org> 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1162306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1362306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1462306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "ath5k.h" 2062306a36Sopenharmony_ci#include "reg.h" 2162306a36Sopenharmony_ci#include "debug.h" 2262306a36Sopenharmony_ci#include "ani.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/** 2562306a36Sopenharmony_ci * DOC: Basic ANI Operation 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Adaptive Noise Immunity (ANI) controls five noise immunity parameters 2862306a36Sopenharmony_ci * depending on the amount of interference in the environment, increasing 2962306a36Sopenharmony_ci * or reducing sensitivity as necessary. 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * The parameters are: 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * - "noise immunity" 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * - "spur immunity" 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * - "firstep level" 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * - "OFDM weak signal detection" 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * - "CCK weak signal detection" 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * Basically we look at the amount of ODFM and CCK timing errors we get and then 4462306a36Sopenharmony_ci * raise or lower immunity accordingly by setting one or more of these 4562306a36Sopenharmony_ci * parameters. 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * Newer chipsets have PHY error counters in hardware which will generate a MIB 4862306a36Sopenharmony_ci * interrupt when they overflow. Older hardware has too enable PHY error frames 4962306a36Sopenharmony_ci * by setting a RX flag and then count every single PHY error. When a specified 5062306a36Sopenharmony_ci * threshold of errors has been reached we will raise immunity. 5162306a36Sopenharmony_ci * Also we regularly check the amount of errors and lower or raise immunity as 5262306a36Sopenharmony_ci * necessary. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/***********************\ 5762306a36Sopenharmony_ci* ANI parameter control * 5862306a36Sopenharmony_ci\***********************/ 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/** 6162306a36Sopenharmony_ci * ath5k_ani_set_noise_immunity_level() - Set noise immunity level 6262306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 6362306a36Sopenharmony_ci * @level: level between 0 and @ATH5K_ANI_MAX_NOISE_IMM_LVL 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_civoid 6662306a36Sopenharmony_ciath5k_ani_set_noise_immunity_level(struct ath5k_hw *ah, int level) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci /* TODO: 6962306a36Sopenharmony_ci * ANI documents suggest the following five levels to use, but the HAL 7062306a36Sopenharmony_ci * and ath9k use only the last two levels, making this 7162306a36Sopenharmony_ci * essentially an on/off option. There *may* be a reason for this (???), 7262306a36Sopenharmony_ci * so i stick with the HAL version for now... 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ci#if 0 7562306a36Sopenharmony_ci static const s8 lo[] = { -52, -56, -60, -64, -70 }; 7662306a36Sopenharmony_ci static const s8 hi[] = { -18, -18, -16, -14, -12 }; 7762306a36Sopenharmony_ci static const s8 sz[] = { -34, -41, -48, -55, -62 }; 7862306a36Sopenharmony_ci static const s8 fr[] = { -70, -72, -75, -78, -80 }; 7962306a36Sopenharmony_ci#else 8062306a36Sopenharmony_ci static const s8 lo[] = { -64, -70 }; 8162306a36Sopenharmony_ci static const s8 hi[] = { -14, -12 }; 8262306a36Sopenharmony_ci static const s8 sz[] = { -55, -62 }; 8362306a36Sopenharmony_ci static const s8 fr[] = { -78, -80 }; 8462306a36Sopenharmony_ci#endif 8562306a36Sopenharmony_ci if (level < 0 || level >= ARRAY_SIZE(sz)) { 8662306a36Sopenharmony_ci ATH5K_ERR(ah, "noise immunity level %d out of range", 8762306a36Sopenharmony_ci level); 8862306a36Sopenharmony_ci return; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE, 9262306a36Sopenharmony_ci AR5K_PHY_DESIRED_SIZE_TOT, sz[level]); 9362306a36Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_AGCCOARSE, 9462306a36Sopenharmony_ci AR5K_PHY_AGCCOARSE_LO, lo[level]); 9562306a36Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_AGCCOARSE, 9662306a36Sopenharmony_ci AR5K_PHY_AGCCOARSE_HI, hi[level]); 9762306a36Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG, 9862306a36Sopenharmony_ci AR5K_PHY_SIG_FIRPWR, fr[level]); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci ah->ani_state.noise_imm_level = level; 10162306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/** 10562306a36Sopenharmony_ci * ath5k_ani_set_spur_immunity_level() - Set spur immunity level 10662306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 10762306a36Sopenharmony_ci * @level: level between 0 and @max_spur_level (the maximum level is dependent 10862306a36Sopenharmony_ci * on the chip revision). 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_civoid 11162306a36Sopenharmony_ciath5k_ani_set_spur_immunity_level(struct ath5k_hw *ah, int level) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci static const int val[] = { 2, 4, 6, 8, 10, 12, 14, 16 }; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (level < 0 || level >= ARRAY_SIZE(val) || 11662306a36Sopenharmony_ci level > ah->ani_state.max_spur_level) { 11762306a36Sopenharmony_ci ATH5K_ERR(ah, "spur immunity level %d out of range", 11862306a36Sopenharmony_ci level); 11962306a36Sopenharmony_ci return; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR, 12362306a36Sopenharmony_ci AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1, val[level]); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci ah->ani_state.spur_level = level; 12662306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/** 13062306a36Sopenharmony_ci * ath5k_ani_set_firstep_level() - Set "firstep" level 13162306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 13262306a36Sopenharmony_ci * @level: level between 0 and @ATH5K_ANI_MAX_FIRSTEP_LVL 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_civoid 13562306a36Sopenharmony_ciath5k_ani_set_firstep_level(struct ath5k_hw *ah, int level) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci static const int val[] = { 0, 4, 8 }; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (level < 0 || level >= ARRAY_SIZE(val)) { 14062306a36Sopenharmony_ci ATH5K_ERR(ah, "firstep level %d out of range", level); 14162306a36Sopenharmony_ci return; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG, 14562306a36Sopenharmony_ci AR5K_PHY_SIG_FIRSTEP, val[level]); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ah->ani_state.firstep_level = level; 14862306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/** 15262306a36Sopenharmony_ci * ath5k_ani_set_ofdm_weak_signal_detection() - Set OFDM weak signal detection 15362306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 15462306a36Sopenharmony_ci * @on: turn on or off 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_civoid 15762306a36Sopenharmony_ciath5k_ani_set_ofdm_weak_signal_detection(struct ath5k_hw *ah, bool on) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci static const int m1l[] = { 127, 50 }; 16062306a36Sopenharmony_ci static const int m2l[] = { 127, 40 }; 16162306a36Sopenharmony_ci static const int m1[] = { 127, 0x4d }; 16262306a36Sopenharmony_ci static const int m2[] = { 127, 0x40 }; 16362306a36Sopenharmony_ci static const int m2cnt[] = { 31, 16 }; 16462306a36Sopenharmony_ci static const int m2lcnt[] = { 63, 48 }; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, 16762306a36Sopenharmony_ci AR5K_PHY_WEAK_OFDM_LOW_THR_M1, m1l[on]); 16862306a36Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, 16962306a36Sopenharmony_ci AR5K_PHY_WEAK_OFDM_LOW_THR_M2, m2l[on]); 17062306a36Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR, 17162306a36Sopenharmony_ci AR5K_PHY_WEAK_OFDM_HIGH_THR_M1, m1[on]); 17262306a36Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR, 17362306a36Sopenharmony_ci AR5K_PHY_WEAK_OFDM_HIGH_THR_M2, m2[on]); 17462306a36Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR, 17562306a36Sopenharmony_ci AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT, m2cnt[on]); 17662306a36Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, 17762306a36Sopenharmony_ci AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT, m2lcnt[on]); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (on) 18062306a36Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, 18162306a36Sopenharmony_ci AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN); 18262306a36Sopenharmony_ci else 18362306a36Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, 18462306a36Sopenharmony_ci AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci ah->ani_state.ofdm_weak_sig = on; 18762306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s", 18862306a36Sopenharmony_ci on ? "on" : "off"); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/** 19262306a36Sopenharmony_ci * ath5k_ani_set_cck_weak_signal_detection() - Set CCK weak signal detection 19362306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 19462306a36Sopenharmony_ci * @on: turn on or off 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_civoid 19762306a36Sopenharmony_ciath5k_ani_set_cck_weak_signal_detection(struct ath5k_hw *ah, bool on) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci static const int val[] = { 8, 6 }; 20062306a36Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_CCK_CROSSCORR, 20162306a36Sopenharmony_ci AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR, val[on]); 20262306a36Sopenharmony_ci ah->ani_state.cck_weak_sig = on; 20362306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s", 20462306a36Sopenharmony_ci on ? "on" : "off"); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/***************\ 20962306a36Sopenharmony_ci* ANI algorithm * 21062306a36Sopenharmony_ci\***************/ 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci/** 21362306a36Sopenharmony_ci * ath5k_ani_raise_immunity() - Increase noise immunity 21462306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 21562306a36Sopenharmony_ci * @as: The &struct ath5k_ani_state 21662306a36Sopenharmony_ci * @ofdm_trigger: If this is true we are called because of too many OFDM errors, 21762306a36Sopenharmony_ci * the algorithm will tune more parameters then. 21862306a36Sopenharmony_ci * 21962306a36Sopenharmony_ci * Try to raise noise immunity (=decrease sensitivity) in several steps 22062306a36Sopenharmony_ci * depending on the average RSSI of the beacons we received. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_cistatic void 22362306a36Sopenharmony_ciath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as, 22462306a36Sopenharmony_ci bool ofdm_trigger) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci int rssi = ewma_beacon_rssi_read(&ah->ah_beacon_rssi_avg); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "raise immunity (%s)", 22962306a36Sopenharmony_ci ofdm_trigger ? "ODFM" : "CCK"); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* first: raise noise immunity */ 23262306a36Sopenharmony_ci if (as->noise_imm_level < ATH5K_ANI_MAX_NOISE_IMM_LVL) { 23362306a36Sopenharmony_ci ath5k_ani_set_noise_immunity_level(ah, as->noise_imm_level + 1); 23462306a36Sopenharmony_ci return; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* only OFDM: raise spur immunity level */ 23862306a36Sopenharmony_ci if (ofdm_trigger && 23962306a36Sopenharmony_ci as->spur_level < ah->ani_state.max_spur_level) { 24062306a36Sopenharmony_ci ath5k_ani_set_spur_immunity_level(ah, as->spur_level + 1); 24162306a36Sopenharmony_ci return; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* AP mode */ 24562306a36Sopenharmony_ci if (ah->opmode == NL80211_IFTYPE_AP) { 24662306a36Sopenharmony_ci if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) 24762306a36Sopenharmony_ci ath5k_ani_set_firstep_level(ah, as->firstep_level + 1); 24862306a36Sopenharmony_ci return; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* STA and IBSS mode */ 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* TODO: for IBSS mode it would be better to keep a beacon RSSI average 25462306a36Sopenharmony_ci * per each neighbour node and use the minimum of these, to make sure we 25562306a36Sopenharmony_ci * don't shut out a remote node by raising immunity too high. */ 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (rssi > ATH5K_ANI_RSSI_THR_HIGH) { 25862306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 25962306a36Sopenharmony_ci "beacon RSSI high"); 26062306a36Sopenharmony_ci /* only OFDM: beacon RSSI is high, we can disable ODFM weak 26162306a36Sopenharmony_ci * signal detection */ 26262306a36Sopenharmony_ci if (ofdm_trigger && as->ofdm_weak_sig) { 26362306a36Sopenharmony_ci ath5k_ani_set_ofdm_weak_signal_detection(ah, false); 26462306a36Sopenharmony_ci ath5k_ani_set_spur_immunity_level(ah, 0); 26562306a36Sopenharmony_ci return; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci /* as a last resort or CCK: raise firstep level */ 26862306a36Sopenharmony_ci if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) { 26962306a36Sopenharmony_ci ath5k_ani_set_firstep_level(ah, as->firstep_level + 1); 27062306a36Sopenharmony_ci return; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) { 27362306a36Sopenharmony_ci /* beacon RSSI in mid range, we need OFDM weak signal detect, 27462306a36Sopenharmony_ci * but can raise firstep level */ 27562306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 27662306a36Sopenharmony_ci "beacon RSSI mid"); 27762306a36Sopenharmony_ci if (ofdm_trigger && !as->ofdm_weak_sig) 27862306a36Sopenharmony_ci ath5k_ani_set_ofdm_weak_signal_detection(ah, true); 27962306a36Sopenharmony_ci if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) 28062306a36Sopenharmony_ci ath5k_ani_set_firstep_level(ah, as->firstep_level + 1); 28162306a36Sopenharmony_ci return; 28262306a36Sopenharmony_ci } else if (ah->ah_current_channel->band == NL80211_BAND_2GHZ) { 28362306a36Sopenharmony_ci /* beacon RSSI is low. in B/G mode turn of OFDM weak signal 28462306a36Sopenharmony_ci * detect and zero firstep level to maximize CCK sensitivity */ 28562306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 28662306a36Sopenharmony_ci "beacon RSSI low, 2GHz"); 28762306a36Sopenharmony_ci if (ofdm_trigger && as->ofdm_weak_sig) 28862306a36Sopenharmony_ci ath5k_ani_set_ofdm_weak_signal_detection(ah, false); 28962306a36Sopenharmony_ci if (as->firstep_level > 0) 29062306a36Sopenharmony_ci ath5k_ani_set_firstep_level(ah, 0); 29162306a36Sopenharmony_ci return; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* TODO: why not?: 29562306a36Sopenharmony_ci if (as->cck_weak_sig == true) { 29662306a36Sopenharmony_ci ath5k_ani_set_cck_weak_signal_detection(ah, false); 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/** 30262306a36Sopenharmony_ci * ath5k_ani_lower_immunity() - Decrease noise immunity 30362306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 30462306a36Sopenharmony_ci * @as: The &struct ath5k_ani_state 30562306a36Sopenharmony_ci * 30662306a36Sopenharmony_ci * Try to lower noise immunity (=increase sensitivity) in several steps 30762306a36Sopenharmony_ci * depending on the average RSSI of the beacons we received. 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_cistatic void 31062306a36Sopenharmony_ciath5k_ani_lower_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci int rssi = ewma_beacon_rssi_read(&ah->ah_beacon_rssi_avg); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "lower immunity"); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (ah->opmode == NL80211_IFTYPE_AP) { 31762306a36Sopenharmony_ci /* AP mode */ 31862306a36Sopenharmony_ci if (as->firstep_level > 0) { 31962306a36Sopenharmony_ci ath5k_ani_set_firstep_level(ah, as->firstep_level - 1); 32062306a36Sopenharmony_ci return; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci } else { 32362306a36Sopenharmony_ci /* STA and IBSS mode (see TODO above) */ 32462306a36Sopenharmony_ci if (rssi > ATH5K_ANI_RSSI_THR_HIGH) { 32562306a36Sopenharmony_ci /* beacon signal is high, leave OFDM weak signal 32662306a36Sopenharmony_ci * detection off or it may oscillate 32762306a36Sopenharmony_ci * TODO: who said it's off??? */ 32862306a36Sopenharmony_ci } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) { 32962306a36Sopenharmony_ci /* beacon RSSI is mid-range: turn on ODFM weak signal 33062306a36Sopenharmony_ci * detection and next, lower firstep level */ 33162306a36Sopenharmony_ci if (!as->ofdm_weak_sig) { 33262306a36Sopenharmony_ci ath5k_ani_set_ofdm_weak_signal_detection(ah, 33362306a36Sopenharmony_ci true); 33462306a36Sopenharmony_ci return; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci if (as->firstep_level > 0) { 33762306a36Sopenharmony_ci ath5k_ani_set_firstep_level(ah, 33862306a36Sopenharmony_ci as->firstep_level - 1); 33962306a36Sopenharmony_ci return; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci } else { 34262306a36Sopenharmony_ci /* beacon signal is low: only reduce firstep level */ 34362306a36Sopenharmony_ci if (as->firstep_level > 0) { 34462306a36Sopenharmony_ci ath5k_ani_set_firstep_level(ah, 34562306a36Sopenharmony_ci as->firstep_level - 1); 34662306a36Sopenharmony_ci return; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* all modes */ 35262306a36Sopenharmony_ci if (as->spur_level > 0) { 35362306a36Sopenharmony_ci ath5k_ani_set_spur_immunity_level(ah, as->spur_level - 1); 35462306a36Sopenharmony_ci return; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* finally, reduce noise immunity */ 35862306a36Sopenharmony_ci if (as->noise_imm_level > 0) { 35962306a36Sopenharmony_ci ath5k_ani_set_noise_immunity_level(ah, as->noise_imm_level - 1); 36062306a36Sopenharmony_ci return; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci/** 36562306a36Sopenharmony_ci * ath5k_hw_ani_get_listen_time() - Update counters and return listening time 36662306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 36762306a36Sopenharmony_ci * @as: The &struct ath5k_ani_state 36862306a36Sopenharmony_ci * 36962306a36Sopenharmony_ci * Return an approximation of the time spent "listening" in milliseconds (ms) 37062306a36Sopenharmony_ci * since the last call of this function. 37162306a36Sopenharmony_ci * Save a snapshot of the counter values for debugging/statistics. 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_cistatic int 37462306a36Sopenharmony_ciath5k_hw_ani_get_listen_time(struct ath5k_hw *ah, struct ath5k_ani_state *as) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct ath_common *common = ath5k_hw_common(ah); 37762306a36Sopenharmony_ci int listen; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci spin_lock_bh(&common->cc_lock); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci ath_hw_cycle_counters_update(common); 38262306a36Sopenharmony_ci memcpy(&as->last_cc, &common->cc_ani, sizeof(as->last_cc)); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* clears common->cc_ani */ 38562306a36Sopenharmony_ci listen = ath_hw_get_listen_time(common); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci spin_unlock_bh(&common->cc_lock); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return listen; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/** 39362306a36Sopenharmony_ci * ath5k_ani_save_and_clear_phy_errors() - Clear and save PHY error counters 39462306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 39562306a36Sopenharmony_ci * @as: The &struct ath5k_ani_state 39662306a36Sopenharmony_ci * 39762306a36Sopenharmony_ci * Clear the PHY error counters as soon as possible, since this might be called 39862306a36Sopenharmony_ci * from a MIB interrupt and we want to make sure we don't get interrupted again. 39962306a36Sopenharmony_ci * Add the count of CCK and OFDM errors to our internal state, so it can be used 40062306a36Sopenharmony_ci * by the algorithm later. 40162306a36Sopenharmony_ci * 40262306a36Sopenharmony_ci * Will be called from interrupt and tasklet context. 40362306a36Sopenharmony_ci * Returns 0 if both counters are zero. 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_cistatic int 40662306a36Sopenharmony_ciath5k_ani_save_and_clear_phy_errors(struct ath5k_hw *ah, 40762306a36Sopenharmony_ci struct ath5k_ani_state *as) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci unsigned int ofdm_err, cck_err; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (!ah->ah_capabilities.cap_has_phyerr_counters) 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci ofdm_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1); 41562306a36Sopenharmony_ci cck_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* reset counters first, we might be in a hurry (interrupt) */ 41862306a36Sopenharmony_ci ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH, 41962306a36Sopenharmony_ci AR5K_PHYERR_CNT1); 42062306a36Sopenharmony_ci ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH, 42162306a36Sopenharmony_ci AR5K_PHYERR_CNT2); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci ofdm_err = ATH5K_ANI_OFDM_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - ofdm_err); 42462306a36Sopenharmony_ci cck_err = ATH5K_ANI_CCK_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - cck_err); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* sometimes both can be zero, especially when there is a superfluous 42762306a36Sopenharmony_ci * second interrupt. detect that here and return an error. */ 42862306a36Sopenharmony_ci if (ofdm_err <= 0 && cck_err <= 0) 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* avoid negative values should one of the registers overflow */ 43262306a36Sopenharmony_ci if (ofdm_err > 0) { 43362306a36Sopenharmony_ci as->ofdm_errors += ofdm_err; 43462306a36Sopenharmony_ci as->sum_ofdm_errors += ofdm_err; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci if (cck_err > 0) { 43762306a36Sopenharmony_ci as->cck_errors += cck_err; 43862306a36Sopenharmony_ci as->sum_cck_errors += cck_err; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci return 1; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci/** 44462306a36Sopenharmony_ci * ath5k_ani_period_restart() - Restart ANI period 44562306a36Sopenharmony_ci * @as: The &struct ath5k_ani_state 44662306a36Sopenharmony_ci * 44762306a36Sopenharmony_ci * Just reset counters, so they are clear for the next "ani period". 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_cistatic void 45062306a36Sopenharmony_ciath5k_ani_period_restart(struct ath5k_ani_state *as) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci /* keep last values for debugging */ 45362306a36Sopenharmony_ci as->last_ofdm_errors = as->ofdm_errors; 45462306a36Sopenharmony_ci as->last_cck_errors = as->cck_errors; 45562306a36Sopenharmony_ci as->last_listen = as->listen_time; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci as->ofdm_errors = 0; 45862306a36Sopenharmony_ci as->cck_errors = 0; 45962306a36Sopenharmony_ci as->listen_time = 0; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci/** 46362306a36Sopenharmony_ci * ath5k_ani_calibration() - The main ANI calibration function 46462306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 46562306a36Sopenharmony_ci * 46662306a36Sopenharmony_ci * We count OFDM and CCK errors relative to the time where we did not send or 46762306a36Sopenharmony_ci * receive ("listen" time) and raise or lower immunity accordingly. 46862306a36Sopenharmony_ci * This is called regularly (every second) from the calibration timer, but also 46962306a36Sopenharmony_ci * when an error threshold has been reached. 47062306a36Sopenharmony_ci * 47162306a36Sopenharmony_ci * In order to synchronize access from different contexts, this should be 47262306a36Sopenharmony_ci * called only indirectly by scheduling the ANI tasklet! 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_civoid 47562306a36Sopenharmony_ciath5k_ani_calibration(struct ath5k_hw *ah) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct ath5k_ani_state *as = &ah->ani_state; 47862306a36Sopenharmony_ci int listen, ofdm_high, ofdm_low, cck_high, cck_low; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* get listen time since last call and add it to the counter because we 48162306a36Sopenharmony_ci * might not have restarted the "ani period" last time. 48262306a36Sopenharmony_ci * always do this to calculate the busy time also in manual mode */ 48362306a36Sopenharmony_ci listen = ath5k_hw_ani_get_listen_time(ah, as); 48462306a36Sopenharmony_ci as->listen_time += listen; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (as->ani_mode != ATH5K_ANI_MODE_AUTO) 48762306a36Sopenharmony_ci return; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci ath5k_ani_save_and_clear_phy_errors(ah, as); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci ofdm_high = as->listen_time * ATH5K_ANI_OFDM_TRIG_HIGH / 1000; 49262306a36Sopenharmony_ci cck_high = as->listen_time * ATH5K_ANI_CCK_TRIG_HIGH / 1000; 49362306a36Sopenharmony_ci ofdm_low = as->listen_time * ATH5K_ANI_OFDM_TRIG_LOW / 1000; 49462306a36Sopenharmony_ci cck_low = as->listen_time * ATH5K_ANI_CCK_TRIG_LOW / 1000; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 49762306a36Sopenharmony_ci "listen %d (now %d)", as->listen_time, listen); 49862306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 49962306a36Sopenharmony_ci "check high ofdm %d/%d cck %d/%d", 50062306a36Sopenharmony_ci as->ofdm_errors, ofdm_high, as->cck_errors, cck_high); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (as->ofdm_errors > ofdm_high || as->cck_errors > cck_high) { 50362306a36Sopenharmony_ci /* too many PHY errors - we have to raise immunity */ 50462306a36Sopenharmony_ci bool ofdm_flag = as->ofdm_errors > ofdm_high; 50562306a36Sopenharmony_ci ath5k_ani_raise_immunity(ah, as, ofdm_flag); 50662306a36Sopenharmony_ci ath5k_ani_period_restart(as); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci } else if (as->listen_time > 5 * ATH5K_ANI_LISTEN_PERIOD) { 50962306a36Sopenharmony_ci /* If more than 5 (TODO: why 5?) periods have passed and we got 51062306a36Sopenharmony_ci * relatively little errors we can try to lower immunity */ 51162306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 51262306a36Sopenharmony_ci "check low ofdm %d/%d cck %d/%d", 51362306a36Sopenharmony_ci as->ofdm_errors, ofdm_low, as->cck_errors, cck_low); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (as->ofdm_errors <= ofdm_low && as->cck_errors <= cck_low) 51662306a36Sopenharmony_ci ath5k_ani_lower_immunity(ah, as); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci ath5k_ani_period_restart(as); 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci/*******************\ 52462306a36Sopenharmony_ci* Interrupt handler * 52562306a36Sopenharmony_ci\*******************/ 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/** 52862306a36Sopenharmony_ci * ath5k_ani_mib_intr() - Interrupt handler for ANI MIB counters 52962306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 53062306a36Sopenharmony_ci * 53162306a36Sopenharmony_ci * Just read & reset the registers quickly, so they don't generate more 53262306a36Sopenharmony_ci * interrupts, save the counters and schedule the tasklet to decide whether 53362306a36Sopenharmony_ci * to raise immunity or not. 53462306a36Sopenharmony_ci * 53562306a36Sopenharmony_ci * We just need to handle PHY error counters, ath5k_hw_update_mib_counters() 53662306a36Sopenharmony_ci * should take care of all "normal" MIB interrupts. 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_civoid 53962306a36Sopenharmony_ciath5k_ani_mib_intr(struct ath5k_hw *ah) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci struct ath5k_ani_state *as = &ah->ani_state; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* nothing to do here if HW does not have PHY error counters - they 54462306a36Sopenharmony_ci * can't be the reason for the MIB interrupt then */ 54562306a36Sopenharmony_ci if (!ah->ah_capabilities.cap_has_phyerr_counters) 54662306a36Sopenharmony_ci return; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* not in use but clear anyways */ 54962306a36Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT); 55062306a36Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (ah->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO) 55362306a36Sopenharmony_ci return; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* If one of the errors triggered, we can get a superfluous second 55662306a36Sopenharmony_ci * interrupt, even though we have already reset the register. The 55762306a36Sopenharmony_ci * function detects that so we can return early. */ 55862306a36Sopenharmony_ci if (ath5k_ani_save_and_clear_phy_errors(ah, as) == 0) 55962306a36Sopenharmony_ci return; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH || 56262306a36Sopenharmony_ci as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH) 56362306a36Sopenharmony_ci tasklet_schedule(&ah->ani_tasklet); 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci/** 56762306a36Sopenharmony_ci * ath5k_ani_phy_error_report - Used by older HW to report PHY errors 56862306a36Sopenharmony_ci * 56962306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 57062306a36Sopenharmony_ci * @phyerr: One of enum ath5k_phy_error_code 57162306a36Sopenharmony_ci * 57262306a36Sopenharmony_ci * This is used by hardware without PHY error counters to report PHY errors 57362306a36Sopenharmony_ci * on a frame-by-frame basis, instead of the interrupt. 57462306a36Sopenharmony_ci */ 57562306a36Sopenharmony_civoid 57662306a36Sopenharmony_ciath5k_ani_phy_error_report(struct ath5k_hw *ah, 57762306a36Sopenharmony_ci enum ath5k_phy_error_code phyerr) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct ath5k_ani_state *as = &ah->ani_state; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (phyerr == AR5K_RX_PHY_ERROR_OFDM_TIMING) { 58262306a36Sopenharmony_ci as->ofdm_errors++; 58362306a36Sopenharmony_ci if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH) 58462306a36Sopenharmony_ci tasklet_schedule(&ah->ani_tasklet); 58562306a36Sopenharmony_ci } else if (phyerr == AR5K_RX_PHY_ERROR_CCK_TIMING) { 58662306a36Sopenharmony_ci as->cck_errors++; 58762306a36Sopenharmony_ci if (as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH) 58862306a36Sopenharmony_ci tasklet_schedule(&ah->ani_tasklet); 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci/****************\ 59462306a36Sopenharmony_ci* Initialization * 59562306a36Sopenharmony_ci\****************/ 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci/** 59862306a36Sopenharmony_ci * ath5k_enable_phy_err_counters() - Enable PHY error counters 59962306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 60062306a36Sopenharmony_ci * 60162306a36Sopenharmony_ci * Enable PHY error counters for OFDM and CCK timing errors. 60262306a36Sopenharmony_ci */ 60362306a36Sopenharmony_cistatic void 60462306a36Sopenharmony_ciath5k_enable_phy_err_counters(struct ath5k_hw *ah) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH, 60762306a36Sopenharmony_ci AR5K_PHYERR_CNT1); 60862306a36Sopenharmony_ci ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH, 60962306a36Sopenharmony_ci AR5K_PHYERR_CNT2); 61062306a36Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_OFDM, AR5K_PHYERR_CNT1_MASK); 61162306a36Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_CCK, AR5K_PHYERR_CNT2_MASK); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* not in use */ 61462306a36Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT); 61562306a36Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT); 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci/** 61962306a36Sopenharmony_ci * ath5k_disable_phy_err_counters() - Disable PHY error counters 62062306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 62162306a36Sopenharmony_ci * 62262306a36Sopenharmony_ci * Disable PHY error counters for OFDM and CCK timing errors. 62362306a36Sopenharmony_ci */ 62462306a36Sopenharmony_cistatic void 62562306a36Sopenharmony_ciath5k_disable_phy_err_counters(struct ath5k_hw *ah) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1); 62862306a36Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2); 62962306a36Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1_MASK); 63062306a36Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2_MASK); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* not in use */ 63362306a36Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT); 63462306a36Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT); 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci/** 63862306a36Sopenharmony_ci * ath5k_ani_init() - Initialize ANI 63962306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 64062306a36Sopenharmony_ci * @mode: One of enum ath5k_ani_mode 64162306a36Sopenharmony_ci * 64262306a36Sopenharmony_ci * Initialize ANI according to mode. 64362306a36Sopenharmony_ci */ 64462306a36Sopenharmony_civoid 64562306a36Sopenharmony_ciath5k_ani_init(struct ath5k_hw *ah, enum ath5k_ani_mode mode) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci /* ANI is only possible on 5212 and newer */ 64862306a36Sopenharmony_ci if (ah->ah_version < AR5K_AR5212) 64962306a36Sopenharmony_ci return; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (mode < ATH5K_ANI_MODE_OFF || mode > ATH5K_ANI_MODE_AUTO) { 65262306a36Sopenharmony_ci ATH5K_ERR(ah, "ANI mode %d out of range", mode); 65362306a36Sopenharmony_ci return; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* clear old state information */ 65762306a36Sopenharmony_ci memset(&ah->ani_state, 0, sizeof(ah->ani_state)); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* older hardware has more spur levels than newer */ 66062306a36Sopenharmony_ci if (ah->ah_mac_srev < AR5K_SREV_AR2414) 66162306a36Sopenharmony_ci ah->ani_state.max_spur_level = 7; 66262306a36Sopenharmony_ci else 66362306a36Sopenharmony_ci ah->ani_state.max_spur_level = 2; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* initial values for our ani parameters */ 66662306a36Sopenharmony_ci if (mode == ATH5K_ANI_MODE_OFF) { 66762306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI off\n"); 66862306a36Sopenharmony_ci } else if (mode == ATH5K_ANI_MODE_MANUAL_LOW) { 66962306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 67062306a36Sopenharmony_ci "ANI manual low -> high sensitivity\n"); 67162306a36Sopenharmony_ci ath5k_ani_set_noise_immunity_level(ah, 0); 67262306a36Sopenharmony_ci ath5k_ani_set_spur_immunity_level(ah, 0); 67362306a36Sopenharmony_ci ath5k_ani_set_firstep_level(ah, 0); 67462306a36Sopenharmony_ci ath5k_ani_set_ofdm_weak_signal_detection(ah, true); 67562306a36Sopenharmony_ci ath5k_ani_set_cck_weak_signal_detection(ah, true); 67662306a36Sopenharmony_ci } else if (mode == ATH5K_ANI_MODE_MANUAL_HIGH) { 67762306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 67862306a36Sopenharmony_ci "ANI manual high -> low sensitivity\n"); 67962306a36Sopenharmony_ci ath5k_ani_set_noise_immunity_level(ah, 68062306a36Sopenharmony_ci ATH5K_ANI_MAX_NOISE_IMM_LVL); 68162306a36Sopenharmony_ci ath5k_ani_set_spur_immunity_level(ah, 68262306a36Sopenharmony_ci ah->ani_state.max_spur_level); 68362306a36Sopenharmony_ci ath5k_ani_set_firstep_level(ah, ATH5K_ANI_MAX_FIRSTEP_LVL); 68462306a36Sopenharmony_ci ath5k_ani_set_ofdm_weak_signal_detection(ah, false); 68562306a36Sopenharmony_ci ath5k_ani_set_cck_weak_signal_detection(ah, false); 68662306a36Sopenharmony_ci } else if (mode == ATH5K_ANI_MODE_AUTO) { 68762306a36Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI auto\n"); 68862306a36Sopenharmony_ci ath5k_ani_set_noise_immunity_level(ah, 0); 68962306a36Sopenharmony_ci ath5k_ani_set_spur_immunity_level(ah, 0); 69062306a36Sopenharmony_ci ath5k_ani_set_firstep_level(ah, 0); 69162306a36Sopenharmony_ci ath5k_ani_set_ofdm_weak_signal_detection(ah, true); 69262306a36Sopenharmony_ci ath5k_ani_set_cck_weak_signal_detection(ah, false); 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* newer hardware has PHY error counter registers which we can use to 69662306a36Sopenharmony_ci * get OFDM and CCK error counts. older hardware has to set rxfilter and 69762306a36Sopenharmony_ci * report every single PHY error by calling ath5k_ani_phy_error_report() 69862306a36Sopenharmony_ci */ 69962306a36Sopenharmony_ci if (mode == ATH5K_ANI_MODE_AUTO) { 70062306a36Sopenharmony_ci if (ah->ah_capabilities.cap_has_phyerr_counters) 70162306a36Sopenharmony_ci ath5k_enable_phy_err_counters(ah); 70262306a36Sopenharmony_ci else 70362306a36Sopenharmony_ci ath5k_hw_set_rx_filter(ah, ath5k_hw_get_rx_filter(ah) | 70462306a36Sopenharmony_ci AR5K_RX_FILTER_PHYERR); 70562306a36Sopenharmony_ci } else { 70662306a36Sopenharmony_ci if (ah->ah_capabilities.cap_has_phyerr_counters) 70762306a36Sopenharmony_ci ath5k_disable_phy_err_counters(ah); 70862306a36Sopenharmony_ci else 70962306a36Sopenharmony_ci ath5k_hw_set_rx_filter(ah, ath5k_hw_get_rx_filter(ah) & 71062306a36Sopenharmony_ci ~AR5K_RX_FILTER_PHYERR); 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci ah->ani_state.ani_mode = mode; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci/**************\ 71862306a36Sopenharmony_ci* Debug output * 71962306a36Sopenharmony_ci\**************/ 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci#ifdef CONFIG_ATH5K_DEBUG 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci/** 72462306a36Sopenharmony_ci * ath5k_ani_print_counters() - Print ANI counters 72562306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 72662306a36Sopenharmony_ci * 72762306a36Sopenharmony_ci * Used for debugging ANI 72862306a36Sopenharmony_ci */ 72962306a36Sopenharmony_civoid 73062306a36Sopenharmony_ciath5k_ani_print_counters(struct ath5k_hw *ah) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci /* clears too */ 73362306a36Sopenharmony_ci pr_notice("ACK fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_ACK_FAIL)); 73462306a36Sopenharmony_ci pr_notice("RTS fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_FAIL)); 73562306a36Sopenharmony_ci pr_notice("RTS success\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_OK)); 73662306a36Sopenharmony_ci pr_notice("FCS error\t%d\n", ath5k_hw_reg_read(ah, AR5K_FCS_FAIL)); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* no clear */ 73962306a36Sopenharmony_ci pr_notice("tx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_TX)); 74062306a36Sopenharmony_ci pr_notice("rx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RX)); 74162306a36Sopenharmony_ci pr_notice("busy\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RXCLR)); 74262306a36Sopenharmony_ci pr_notice("cycles\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_CYCLE)); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci pr_notice("AR5K_PHYERR_CNT1\t%d\n", 74562306a36Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1)); 74662306a36Sopenharmony_ci pr_notice("AR5K_PHYERR_CNT2\t%d\n", 74762306a36Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2)); 74862306a36Sopenharmony_ci pr_notice("AR5K_OFDM_FIL_CNT\t%d\n", 74962306a36Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_OFDM_FIL_CNT)); 75062306a36Sopenharmony_ci pr_notice("AR5K_CCK_FIL_CNT\t%d\n", 75162306a36Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_CCK_FIL_CNT)); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci#endif 755