18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2010 Bruno Randolf <br1@einfach.org> 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 118c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 148c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "ath5k.h" 208c2ecf20Sopenharmony_ci#include "reg.h" 218c2ecf20Sopenharmony_ci#include "debug.h" 228c2ecf20Sopenharmony_ci#include "ani.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/** 258c2ecf20Sopenharmony_ci * DOC: Basic ANI Operation 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * Adaptive Noise Immunity (ANI) controls five noise immunity parameters 288c2ecf20Sopenharmony_ci * depending on the amount of interference in the environment, increasing 298c2ecf20Sopenharmony_ci * or reducing sensitivity as necessary. 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * The parameters are: 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * - "noise immunity" 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * - "spur immunity" 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * - "firstep level" 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * - "OFDM weak signal detection" 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * - "CCK weak signal detection" 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * Basically we look at the amount of ODFM and CCK timing errors we get and then 448c2ecf20Sopenharmony_ci * raise or lower immunity accordingly by setting one or more of these 458c2ecf20Sopenharmony_ci * parameters. 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * Newer chipsets have PHY error counters in hardware which will generate a MIB 488c2ecf20Sopenharmony_ci * interrupt when they overflow. Older hardware has too enable PHY error frames 498c2ecf20Sopenharmony_ci * by setting a RX flag and then count every single PHY error. When a specified 508c2ecf20Sopenharmony_ci * threshold of errors has been reached we will raise immunity. 518c2ecf20Sopenharmony_ci * Also we regularly check the amount of errors and lower or raise immunity as 528c2ecf20Sopenharmony_ci * necessary. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/***********************\ 578c2ecf20Sopenharmony_ci* ANI parameter control * 588c2ecf20Sopenharmony_ci\***********************/ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/** 618c2ecf20Sopenharmony_ci * ath5k_ani_set_noise_immunity_level() - Set noise immunity level 628c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 638c2ecf20Sopenharmony_ci * @level: level between 0 and @ATH5K_ANI_MAX_NOISE_IMM_LVL 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_civoid 668c2ecf20Sopenharmony_ciath5k_ani_set_noise_immunity_level(struct ath5k_hw *ah, int level) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci /* TODO: 698c2ecf20Sopenharmony_ci * ANI documents suggest the following five levels to use, but the HAL 708c2ecf20Sopenharmony_ci * and ath9k use only the last two levels, making this 718c2ecf20Sopenharmony_ci * essentially an on/off option. There *may* be a reason for this (???), 728c2ecf20Sopenharmony_ci * so i stick with the HAL version for now... 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci#if 0 758c2ecf20Sopenharmony_ci static const s8 lo[] = { -52, -56, -60, -64, -70 }; 768c2ecf20Sopenharmony_ci static const s8 hi[] = { -18, -18, -16, -14, -12 }; 778c2ecf20Sopenharmony_ci static const s8 sz[] = { -34, -41, -48, -55, -62 }; 788c2ecf20Sopenharmony_ci static const s8 fr[] = { -70, -72, -75, -78, -80 }; 798c2ecf20Sopenharmony_ci#else 808c2ecf20Sopenharmony_ci static const s8 lo[] = { -64, -70 }; 818c2ecf20Sopenharmony_ci static const s8 hi[] = { -14, -12 }; 828c2ecf20Sopenharmony_ci static const s8 sz[] = { -55, -62 }; 838c2ecf20Sopenharmony_ci static const s8 fr[] = { -78, -80 }; 848c2ecf20Sopenharmony_ci#endif 858c2ecf20Sopenharmony_ci if (level < 0 || level >= ARRAY_SIZE(sz)) { 868c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "noise immunity level %d out of range", 878c2ecf20Sopenharmony_ci level); 888c2ecf20Sopenharmony_ci return; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE, 928c2ecf20Sopenharmony_ci AR5K_PHY_DESIRED_SIZE_TOT, sz[level]); 938c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_AGCCOARSE, 948c2ecf20Sopenharmony_ci AR5K_PHY_AGCCOARSE_LO, lo[level]); 958c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_AGCCOARSE, 968c2ecf20Sopenharmony_ci AR5K_PHY_AGCCOARSE_HI, hi[level]); 978c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG, 988c2ecf20Sopenharmony_ci AR5K_PHY_SIG_FIRPWR, fr[level]); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci ah->ani_state.noise_imm_level = level; 1018c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/** 1058c2ecf20Sopenharmony_ci * ath5k_ani_set_spur_immunity_level() - Set spur immunity level 1068c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 1078c2ecf20Sopenharmony_ci * @level: level between 0 and @max_spur_level (the maximum level is dependent 1088c2ecf20Sopenharmony_ci * on the chip revision). 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_civoid 1118c2ecf20Sopenharmony_ciath5k_ani_set_spur_immunity_level(struct ath5k_hw *ah, int level) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci static const int val[] = { 2, 4, 6, 8, 10, 12, 14, 16 }; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (level < 0 || level >= ARRAY_SIZE(val) || 1168c2ecf20Sopenharmony_ci level > ah->ani_state.max_spur_level) { 1178c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "spur immunity level %d out of range", 1188c2ecf20Sopenharmony_ci level); 1198c2ecf20Sopenharmony_ci return; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR, 1238c2ecf20Sopenharmony_ci AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1, val[level]); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci ah->ani_state.spur_level = level; 1268c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/** 1308c2ecf20Sopenharmony_ci * ath5k_ani_set_firstep_level() - Set "firstep" level 1318c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 1328c2ecf20Sopenharmony_ci * @level: level between 0 and @ATH5K_ANI_MAX_FIRSTEP_LVL 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_civoid 1358c2ecf20Sopenharmony_ciath5k_ani_set_firstep_level(struct ath5k_hw *ah, int level) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci static const int val[] = { 0, 4, 8 }; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (level < 0 || level >= ARRAY_SIZE(val)) { 1408c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "firstep level %d out of range", level); 1418c2ecf20Sopenharmony_ci return; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG, 1458c2ecf20Sopenharmony_ci AR5K_PHY_SIG_FIRSTEP, val[level]); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci ah->ani_state.firstep_level = level; 1488c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/** 1528c2ecf20Sopenharmony_ci * ath5k_ani_set_ofdm_weak_signal_detection() - Set OFDM weak signal detection 1538c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 1548c2ecf20Sopenharmony_ci * @on: turn on or off 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_civoid 1578c2ecf20Sopenharmony_ciath5k_ani_set_ofdm_weak_signal_detection(struct ath5k_hw *ah, bool on) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci static const int m1l[] = { 127, 50 }; 1608c2ecf20Sopenharmony_ci static const int m2l[] = { 127, 40 }; 1618c2ecf20Sopenharmony_ci static const int m1[] = { 127, 0x4d }; 1628c2ecf20Sopenharmony_ci static const int m2[] = { 127, 0x40 }; 1638c2ecf20Sopenharmony_ci static const int m2cnt[] = { 31, 16 }; 1648c2ecf20Sopenharmony_ci static const int m2lcnt[] = { 63, 48 }; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, 1678c2ecf20Sopenharmony_ci AR5K_PHY_WEAK_OFDM_LOW_THR_M1, m1l[on]); 1688c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, 1698c2ecf20Sopenharmony_ci AR5K_PHY_WEAK_OFDM_LOW_THR_M2, m2l[on]); 1708c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR, 1718c2ecf20Sopenharmony_ci AR5K_PHY_WEAK_OFDM_HIGH_THR_M1, m1[on]); 1728c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR, 1738c2ecf20Sopenharmony_ci AR5K_PHY_WEAK_OFDM_HIGH_THR_M2, m2[on]); 1748c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR, 1758c2ecf20Sopenharmony_ci AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT, m2cnt[on]); 1768c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, 1778c2ecf20Sopenharmony_ci AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT, m2lcnt[on]); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (on) 1808c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, 1818c2ecf20Sopenharmony_ci AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN); 1828c2ecf20Sopenharmony_ci else 1838c2ecf20Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, 1848c2ecf20Sopenharmony_ci AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci ah->ani_state.ofdm_weak_sig = on; 1878c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s", 1888c2ecf20Sopenharmony_ci on ? "on" : "off"); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/** 1928c2ecf20Sopenharmony_ci * ath5k_ani_set_cck_weak_signal_detection() - Set CCK weak signal detection 1938c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 1948c2ecf20Sopenharmony_ci * @on: turn on or off 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_civoid 1978c2ecf20Sopenharmony_ciath5k_ani_set_cck_weak_signal_detection(struct ath5k_hw *ah, bool on) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci static const int val[] = { 8, 6 }; 2008c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_CCK_CROSSCORR, 2018c2ecf20Sopenharmony_ci AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR, val[on]); 2028c2ecf20Sopenharmony_ci ah->ani_state.cck_weak_sig = on; 2038c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s", 2048c2ecf20Sopenharmony_ci on ? "on" : "off"); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci/***************\ 2098c2ecf20Sopenharmony_ci* ANI algorithm * 2108c2ecf20Sopenharmony_ci\***************/ 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/** 2138c2ecf20Sopenharmony_ci * ath5k_ani_raise_immunity() - Increase noise immunity 2148c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 2158c2ecf20Sopenharmony_ci * @as: The &struct ath5k_ani_state 2168c2ecf20Sopenharmony_ci * @ofdm_trigger: If this is true we are called because of too many OFDM errors, 2178c2ecf20Sopenharmony_ci * the algorithm will tune more parameters then. 2188c2ecf20Sopenharmony_ci * 2198c2ecf20Sopenharmony_ci * Try to raise noise immunity (=decrease sensitivity) in several steps 2208c2ecf20Sopenharmony_ci * depending on the average RSSI of the beacons we received. 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_cistatic void 2238c2ecf20Sopenharmony_ciath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as, 2248c2ecf20Sopenharmony_ci bool ofdm_trigger) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci int rssi = ewma_beacon_rssi_read(&ah->ah_beacon_rssi_avg); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "raise immunity (%s)", 2298c2ecf20Sopenharmony_ci ofdm_trigger ? "ODFM" : "CCK"); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* first: raise noise immunity */ 2328c2ecf20Sopenharmony_ci if (as->noise_imm_level < ATH5K_ANI_MAX_NOISE_IMM_LVL) { 2338c2ecf20Sopenharmony_ci ath5k_ani_set_noise_immunity_level(ah, as->noise_imm_level + 1); 2348c2ecf20Sopenharmony_ci return; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* only OFDM: raise spur immunity level */ 2388c2ecf20Sopenharmony_ci if (ofdm_trigger && 2398c2ecf20Sopenharmony_ci as->spur_level < ah->ani_state.max_spur_level) { 2408c2ecf20Sopenharmony_ci ath5k_ani_set_spur_immunity_level(ah, as->spur_level + 1); 2418c2ecf20Sopenharmony_ci return; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* AP mode */ 2458c2ecf20Sopenharmony_ci if (ah->opmode == NL80211_IFTYPE_AP) { 2468c2ecf20Sopenharmony_ci if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) 2478c2ecf20Sopenharmony_ci ath5k_ani_set_firstep_level(ah, as->firstep_level + 1); 2488c2ecf20Sopenharmony_ci return; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* STA and IBSS mode */ 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* TODO: for IBSS mode it would be better to keep a beacon RSSI average 2548c2ecf20Sopenharmony_ci * per each neighbour node and use the minimum of these, to make sure we 2558c2ecf20Sopenharmony_ci * don't shut out a remote node by raising immunity too high. */ 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (rssi > ATH5K_ANI_RSSI_THR_HIGH) { 2588c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 2598c2ecf20Sopenharmony_ci "beacon RSSI high"); 2608c2ecf20Sopenharmony_ci /* only OFDM: beacon RSSI is high, we can disable ODFM weak 2618c2ecf20Sopenharmony_ci * signal detection */ 2628c2ecf20Sopenharmony_ci if (ofdm_trigger && as->ofdm_weak_sig) { 2638c2ecf20Sopenharmony_ci ath5k_ani_set_ofdm_weak_signal_detection(ah, false); 2648c2ecf20Sopenharmony_ci ath5k_ani_set_spur_immunity_level(ah, 0); 2658c2ecf20Sopenharmony_ci return; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci /* as a last resort or CCK: raise firstep level */ 2688c2ecf20Sopenharmony_ci if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) { 2698c2ecf20Sopenharmony_ci ath5k_ani_set_firstep_level(ah, as->firstep_level + 1); 2708c2ecf20Sopenharmony_ci return; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) { 2738c2ecf20Sopenharmony_ci /* beacon RSSI in mid range, we need OFDM weak signal detect, 2748c2ecf20Sopenharmony_ci * but can raise firstep level */ 2758c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 2768c2ecf20Sopenharmony_ci "beacon RSSI mid"); 2778c2ecf20Sopenharmony_ci if (ofdm_trigger && !as->ofdm_weak_sig) 2788c2ecf20Sopenharmony_ci ath5k_ani_set_ofdm_weak_signal_detection(ah, true); 2798c2ecf20Sopenharmony_ci if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) 2808c2ecf20Sopenharmony_ci ath5k_ani_set_firstep_level(ah, as->firstep_level + 1); 2818c2ecf20Sopenharmony_ci return; 2828c2ecf20Sopenharmony_ci } else if (ah->ah_current_channel->band == NL80211_BAND_2GHZ) { 2838c2ecf20Sopenharmony_ci /* beacon RSSI is low. in B/G mode turn of OFDM weak signal 2848c2ecf20Sopenharmony_ci * detect and zero firstep level to maximize CCK sensitivity */ 2858c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 2868c2ecf20Sopenharmony_ci "beacon RSSI low, 2GHz"); 2878c2ecf20Sopenharmony_ci if (ofdm_trigger && as->ofdm_weak_sig) 2888c2ecf20Sopenharmony_ci ath5k_ani_set_ofdm_weak_signal_detection(ah, false); 2898c2ecf20Sopenharmony_ci if (as->firstep_level > 0) 2908c2ecf20Sopenharmony_ci ath5k_ani_set_firstep_level(ah, 0); 2918c2ecf20Sopenharmony_ci return; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* TODO: why not?: 2958c2ecf20Sopenharmony_ci if (as->cck_weak_sig == true) { 2968c2ecf20Sopenharmony_ci ath5k_ani_set_cck_weak_signal_detection(ah, false); 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci */ 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/** 3028c2ecf20Sopenharmony_ci * ath5k_ani_lower_immunity() - Decrease noise immunity 3038c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 3048c2ecf20Sopenharmony_ci * @as: The &struct ath5k_ani_state 3058c2ecf20Sopenharmony_ci * 3068c2ecf20Sopenharmony_ci * Try to lower noise immunity (=increase sensitivity) in several steps 3078c2ecf20Sopenharmony_ci * depending on the average RSSI of the beacons we received. 3088c2ecf20Sopenharmony_ci */ 3098c2ecf20Sopenharmony_cistatic void 3108c2ecf20Sopenharmony_ciath5k_ani_lower_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci int rssi = ewma_beacon_rssi_read(&ah->ah_beacon_rssi_avg); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "lower immunity"); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (ah->opmode == NL80211_IFTYPE_AP) { 3178c2ecf20Sopenharmony_ci /* AP mode */ 3188c2ecf20Sopenharmony_ci if (as->firstep_level > 0) { 3198c2ecf20Sopenharmony_ci ath5k_ani_set_firstep_level(ah, as->firstep_level - 1); 3208c2ecf20Sopenharmony_ci return; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci } else { 3238c2ecf20Sopenharmony_ci /* STA and IBSS mode (see TODO above) */ 3248c2ecf20Sopenharmony_ci if (rssi > ATH5K_ANI_RSSI_THR_HIGH) { 3258c2ecf20Sopenharmony_ci /* beacon signal is high, leave OFDM weak signal 3268c2ecf20Sopenharmony_ci * detection off or it may oscillate 3278c2ecf20Sopenharmony_ci * TODO: who said it's off??? */ 3288c2ecf20Sopenharmony_ci } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) { 3298c2ecf20Sopenharmony_ci /* beacon RSSI is mid-range: turn on ODFM weak signal 3308c2ecf20Sopenharmony_ci * detection and next, lower firstep level */ 3318c2ecf20Sopenharmony_ci if (!as->ofdm_weak_sig) { 3328c2ecf20Sopenharmony_ci ath5k_ani_set_ofdm_weak_signal_detection(ah, 3338c2ecf20Sopenharmony_ci true); 3348c2ecf20Sopenharmony_ci return; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci if (as->firstep_level > 0) { 3378c2ecf20Sopenharmony_ci ath5k_ani_set_firstep_level(ah, 3388c2ecf20Sopenharmony_ci as->firstep_level - 1); 3398c2ecf20Sopenharmony_ci return; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci } else { 3428c2ecf20Sopenharmony_ci /* beacon signal is low: only reduce firstep level */ 3438c2ecf20Sopenharmony_ci if (as->firstep_level > 0) { 3448c2ecf20Sopenharmony_ci ath5k_ani_set_firstep_level(ah, 3458c2ecf20Sopenharmony_ci as->firstep_level - 1); 3468c2ecf20Sopenharmony_ci return; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* all modes */ 3528c2ecf20Sopenharmony_ci if (as->spur_level > 0) { 3538c2ecf20Sopenharmony_ci ath5k_ani_set_spur_immunity_level(ah, as->spur_level - 1); 3548c2ecf20Sopenharmony_ci return; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* finally, reduce noise immunity */ 3588c2ecf20Sopenharmony_ci if (as->noise_imm_level > 0) { 3598c2ecf20Sopenharmony_ci ath5k_ani_set_noise_immunity_level(ah, as->noise_imm_level - 1); 3608c2ecf20Sopenharmony_ci return; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci/** 3658c2ecf20Sopenharmony_ci * ath5k_hw_ani_get_listen_time() - Update counters and return listening time 3668c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 3678c2ecf20Sopenharmony_ci * @as: The &struct ath5k_ani_state 3688c2ecf20Sopenharmony_ci * 3698c2ecf20Sopenharmony_ci * Return an approximation of the time spent "listening" in milliseconds (ms) 3708c2ecf20Sopenharmony_ci * since the last call of this function. 3718c2ecf20Sopenharmony_ci * Save a snapshot of the counter values for debugging/statistics. 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_cistatic int 3748c2ecf20Sopenharmony_ciath5k_hw_ani_get_listen_time(struct ath5k_hw *ah, struct ath5k_ani_state *as) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct ath_common *common = ath5k_hw_common(ah); 3778c2ecf20Sopenharmony_ci int listen; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci spin_lock_bh(&common->cc_lock); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci ath_hw_cycle_counters_update(common); 3828c2ecf20Sopenharmony_ci memcpy(&as->last_cc, &common->cc_ani, sizeof(as->last_cc)); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* clears common->cc_ani */ 3858c2ecf20Sopenharmony_ci listen = ath_hw_get_listen_time(common); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci spin_unlock_bh(&common->cc_lock); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci return listen; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci/** 3938c2ecf20Sopenharmony_ci * ath5k_ani_save_and_clear_phy_errors() - Clear and save PHY error counters 3948c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 3958c2ecf20Sopenharmony_ci * @as: The &struct ath5k_ani_state 3968c2ecf20Sopenharmony_ci * 3978c2ecf20Sopenharmony_ci * Clear the PHY error counters as soon as possible, since this might be called 3988c2ecf20Sopenharmony_ci * from a MIB interrupt and we want to make sure we don't get interrupted again. 3998c2ecf20Sopenharmony_ci * Add the count of CCK and OFDM errors to our internal state, so it can be used 4008c2ecf20Sopenharmony_ci * by the algorithm later. 4018c2ecf20Sopenharmony_ci * 4028c2ecf20Sopenharmony_ci * Will be called from interrupt and tasklet context. 4038c2ecf20Sopenharmony_ci * Returns 0 if both counters are zero. 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_cistatic int 4068c2ecf20Sopenharmony_ciath5k_ani_save_and_clear_phy_errors(struct ath5k_hw *ah, 4078c2ecf20Sopenharmony_ci struct ath5k_ani_state *as) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci unsigned int ofdm_err, cck_err; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (!ah->ah_capabilities.cap_has_phyerr_counters) 4128c2ecf20Sopenharmony_ci return 0; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci ofdm_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1); 4158c2ecf20Sopenharmony_ci cck_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* reset counters first, we might be in a hurry (interrupt) */ 4188c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH, 4198c2ecf20Sopenharmony_ci AR5K_PHYERR_CNT1); 4208c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH, 4218c2ecf20Sopenharmony_ci AR5K_PHYERR_CNT2); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci ofdm_err = ATH5K_ANI_OFDM_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - ofdm_err); 4248c2ecf20Sopenharmony_ci cck_err = ATH5K_ANI_CCK_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - cck_err); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* sometimes both can be zero, especially when there is a superfluous 4278c2ecf20Sopenharmony_ci * second interrupt. detect that here and return an error. */ 4288c2ecf20Sopenharmony_ci if (ofdm_err <= 0 && cck_err <= 0) 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* avoid negative values should one of the registers overflow */ 4328c2ecf20Sopenharmony_ci if (ofdm_err > 0) { 4338c2ecf20Sopenharmony_ci as->ofdm_errors += ofdm_err; 4348c2ecf20Sopenharmony_ci as->sum_ofdm_errors += ofdm_err; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci if (cck_err > 0) { 4378c2ecf20Sopenharmony_ci as->cck_errors += cck_err; 4388c2ecf20Sopenharmony_ci as->sum_cck_errors += cck_err; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci return 1; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci/** 4448c2ecf20Sopenharmony_ci * ath5k_ani_period_restart() - Restart ANI period 4458c2ecf20Sopenharmony_ci * @as: The &struct ath5k_ani_state 4468c2ecf20Sopenharmony_ci * 4478c2ecf20Sopenharmony_ci * Just reset counters, so they are clear for the next "ani period". 4488c2ecf20Sopenharmony_ci */ 4498c2ecf20Sopenharmony_cistatic void 4508c2ecf20Sopenharmony_ciath5k_ani_period_restart(struct ath5k_ani_state *as) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci /* keep last values for debugging */ 4538c2ecf20Sopenharmony_ci as->last_ofdm_errors = as->ofdm_errors; 4548c2ecf20Sopenharmony_ci as->last_cck_errors = as->cck_errors; 4558c2ecf20Sopenharmony_ci as->last_listen = as->listen_time; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci as->ofdm_errors = 0; 4588c2ecf20Sopenharmony_ci as->cck_errors = 0; 4598c2ecf20Sopenharmony_ci as->listen_time = 0; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci/** 4638c2ecf20Sopenharmony_ci * ath5k_ani_calibration() - The main ANI calibration function 4648c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 4658c2ecf20Sopenharmony_ci * 4668c2ecf20Sopenharmony_ci * We count OFDM and CCK errors relative to the time where we did not send or 4678c2ecf20Sopenharmony_ci * receive ("listen" time) and raise or lower immunity accordingly. 4688c2ecf20Sopenharmony_ci * This is called regularly (every second) from the calibration timer, but also 4698c2ecf20Sopenharmony_ci * when an error threshold has been reached. 4708c2ecf20Sopenharmony_ci * 4718c2ecf20Sopenharmony_ci * In order to synchronize access from different contexts, this should be 4728c2ecf20Sopenharmony_ci * called only indirectly by scheduling the ANI tasklet! 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_civoid 4758c2ecf20Sopenharmony_ciath5k_ani_calibration(struct ath5k_hw *ah) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct ath5k_ani_state *as = &ah->ani_state; 4788c2ecf20Sopenharmony_ci int listen, ofdm_high, ofdm_low, cck_high, cck_low; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* get listen time since last call and add it to the counter because we 4818c2ecf20Sopenharmony_ci * might not have restarted the "ani period" last time. 4828c2ecf20Sopenharmony_ci * always do this to calculate the busy time also in manual mode */ 4838c2ecf20Sopenharmony_ci listen = ath5k_hw_ani_get_listen_time(ah, as); 4848c2ecf20Sopenharmony_ci as->listen_time += listen; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (as->ani_mode != ATH5K_ANI_MODE_AUTO) 4878c2ecf20Sopenharmony_ci return; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci ath5k_ani_save_and_clear_phy_errors(ah, as); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci ofdm_high = as->listen_time * ATH5K_ANI_OFDM_TRIG_HIGH / 1000; 4928c2ecf20Sopenharmony_ci cck_high = as->listen_time * ATH5K_ANI_CCK_TRIG_HIGH / 1000; 4938c2ecf20Sopenharmony_ci ofdm_low = as->listen_time * ATH5K_ANI_OFDM_TRIG_LOW / 1000; 4948c2ecf20Sopenharmony_ci cck_low = as->listen_time * ATH5K_ANI_CCK_TRIG_LOW / 1000; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 4978c2ecf20Sopenharmony_ci "listen %d (now %d)", as->listen_time, listen); 4988c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 4998c2ecf20Sopenharmony_ci "check high ofdm %d/%d cck %d/%d", 5008c2ecf20Sopenharmony_ci as->ofdm_errors, ofdm_high, as->cck_errors, cck_high); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (as->ofdm_errors > ofdm_high || as->cck_errors > cck_high) { 5038c2ecf20Sopenharmony_ci /* too many PHY errors - we have to raise immunity */ 5048c2ecf20Sopenharmony_ci bool ofdm_flag = as->ofdm_errors > ofdm_high; 5058c2ecf20Sopenharmony_ci ath5k_ani_raise_immunity(ah, as, ofdm_flag); 5068c2ecf20Sopenharmony_ci ath5k_ani_period_restart(as); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci } else if (as->listen_time > 5 * ATH5K_ANI_LISTEN_PERIOD) { 5098c2ecf20Sopenharmony_ci /* If more than 5 (TODO: why 5?) periods have passed and we got 5108c2ecf20Sopenharmony_ci * relatively little errors we can try to lower immunity */ 5118c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 5128c2ecf20Sopenharmony_ci "check low ofdm %d/%d cck %d/%d", 5138c2ecf20Sopenharmony_ci as->ofdm_errors, ofdm_low, as->cck_errors, cck_low); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (as->ofdm_errors <= ofdm_low && as->cck_errors <= cck_low) 5168c2ecf20Sopenharmony_ci ath5k_ani_lower_immunity(ah, as); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci ath5k_ani_period_restart(as); 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci/*******************\ 5248c2ecf20Sopenharmony_ci* Interrupt handler * 5258c2ecf20Sopenharmony_ci\*******************/ 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci/** 5288c2ecf20Sopenharmony_ci * ath5k_ani_mib_intr() - Interrupt handler for ANI MIB counters 5298c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 5308c2ecf20Sopenharmony_ci * 5318c2ecf20Sopenharmony_ci * Just read & reset the registers quickly, so they don't generate more 5328c2ecf20Sopenharmony_ci * interrupts, save the counters and schedule the tasklet to decide whether 5338c2ecf20Sopenharmony_ci * to raise immunity or not. 5348c2ecf20Sopenharmony_ci * 5358c2ecf20Sopenharmony_ci * We just need to handle PHY error counters, ath5k_hw_update_mib_counters() 5368c2ecf20Sopenharmony_ci * should take care of all "normal" MIB interrupts. 5378c2ecf20Sopenharmony_ci */ 5388c2ecf20Sopenharmony_civoid 5398c2ecf20Sopenharmony_ciath5k_ani_mib_intr(struct ath5k_hw *ah) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci struct ath5k_ani_state *as = &ah->ani_state; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* nothing to do here if HW does not have PHY error counters - they 5448c2ecf20Sopenharmony_ci * can't be the reason for the MIB interrupt then */ 5458c2ecf20Sopenharmony_ci if (!ah->ah_capabilities.cap_has_phyerr_counters) 5468c2ecf20Sopenharmony_ci return; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* not in use but clear anyways */ 5498c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT); 5508c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (ah->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO) 5538c2ecf20Sopenharmony_ci return; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* If one of the errors triggered, we can get a superfluous second 5568c2ecf20Sopenharmony_ci * interrupt, even though we have already reset the register. The 5578c2ecf20Sopenharmony_ci * function detects that so we can return early. */ 5588c2ecf20Sopenharmony_ci if (ath5k_ani_save_and_clear_phy_errors(ah, as) == 0) 5598c2ecf20Sopenharmony_ci return; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH || 5628c2ecf20Sopenharmony_ci as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH) 5638c2ecf20Sopenharmony_ci tasklet_schedule(&ah->ani_tasklet); 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci/** 5678c2ecf20Sopenharmony_ci * ath5k_ani_phy_error_report - Used by older HW to report PHY errors 5688c2ecf20Sopenharmony_ci * 5698c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 5708c2ecf20Sopenharmony_ci * @phyerr: One of enum ath5k_phy_error_code 5718c2ecf20Sopenharmony_ci * 5728c2ecf20Sopenharmony_ci * This is used by hardware without PHY error counters to report PHY errors 5738c2ecf20Sopenharmony_ci * on a frame-by-frame basis, instead of the interrupt. 5748c2ecf20Sopenharmony_ci */ 5758c2ecf20Sopenharmony_civoid 5768c2ecf20Sopenharmony_ciath5k_ani_phy_error_report(struct ath5k_hw *ah, 5778c2ecf20Sopenharmony_ci enum ath5k_phy_error_code phyerr) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct ath5k_ani_state *as = &ah->ani_state; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (phyerr == AR5K_RX_PHY_ERROR_OFDM_TIMING) { 5828c2ecf20Sopenharmony_ci as->ofdm_errors++; 5838c2ecf20Sopenharmony_ci if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH) 5848c2ecf20Sopenharmony_ci tasklet_schedule(&ah->ani_tasklet); 5858c2ecf20Sopenharmony_ci } else if (phyerr == AR5K_RX_PHY_ERROR_CCK_TIMING) { 5868c2ecf20Sopenharmony_ci as->cck_errors++; 5878c2ecf20Sopenharmony_ci if (as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH) 5888c2ecf20Sopenharmony_ci tasklet_schedule(&ah->ani_tasklet); 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci/****************\ 5948c2ecf20Sopenharmony_ci* Initialization * 5958c2ecf20Sopenharmony_ci\****************/ 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci/** 5988c2ecf20Sopenharmony_ci * ath5k_enable_phy_err_counters() - Enable PHY error counters 5998c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 6008c2ecf20Sopenharmony_ci * 6018c2ecf20Sopenharmony_ci * Enable PHY error counters for OFDM and CCK timing errors. 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_cistatic void 6048c2ecf20Sopenharmony_ciath5k_enable_phy_err_counters(struct ath5k_hw *ah) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH, 6078c2ecf20Sopenharmony_ci AR5K_PHYERR_CNT1); 6088c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH, 6098c2ecf20Sopenharmony_ci AR5K_PHYERR_CNT2); 6108c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_OFDM, AR5K_PHYERR_CNT1_MASK); 6118c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_CCK, AR5K_PHYERR_CNT2_MASK); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* not in use */ 6148c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT); 6158c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT); 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci/** 6198c2ecf20Sopenharmony_ci * ath5k_disable_phy_err_counters() - Disable PHY error counters 6208c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 6218c2ecf20Sopenharmony_ci * 6228c2ecf20Sopenharmony_ci * Disable PHY error counters for OFDM and CCK timing errors. 6238c2ecf20Sopenharmony_ci */ 6248c2ecf20Sopenharmony_cistatic void 6258c2ecf20Sopenharmony_ciath5k_disable_phy_err_counters(struct ath5k_hw *ah) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1); 6288c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2); 6298c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1_MASK); 6308c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2_MASK); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci /* not in use */ 6338c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT); 6348c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT); 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci/** 6388c2ecf20Sopenharmony_ci * ath5k_ani_init() - Initialize ANI 6398c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 6408c2ecf20Sopenharmony_ci * @mode: One of enum ath5k_ani_mode 6418c2ecf20Sopenharmony_ci * 6428c2ecf20Sopenharmony_ci * Initialize ANI according to mode. 6438c2ecf20Sopenharmony_ci */ 6448c2ecf20Sopenharmony_civoid 6458c2ecf20Sopenharmony_ciath5k_ani_init(struct ath5k_hw *ah, enum ath5k_ani_mode mode) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci /* ANI is only possible on 5212 and newer */ 6488c2ecf20Sopenharmony_ci if (ah->ah_version < AR5K_AR5212) 6498c2ecf20Sopenharmony_ci return; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (mode < ATH5K_ANI_MODE_OFF || mode > ATH5K_ANI_MODE_AUTO) { 6528c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "ANI mode %d out of range", mode); 6538c2ecf20Sopenharmony_ci return; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* clear old state information */ 6578c2ecf20Sopenharmony_ci memset(&ah->ani_state, 0, sizeof(ah->ani_state)); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* older hardware has more spur levels than newer */ 6608c2ecf20Sopenharmony_ci if (ah->ah_mac_srev < AR5K_SREV_AR2414) 6618c2ecf20Sopenharmony_ci ah->ani_state.max_spur_level = 7; 6628c2ecf20Sopenharmony_ci else 6638c2ecf20Sopenharmony_ci ah->ani_state.max_spur_level = 2; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* initial values for our ani parameters */ 6668c2ecf20Sopenharmony_ci if (mode == ATH5K_ANI_MODE_OFF) { 6678c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI off\n"); 6688c2ecf20Sopenharmony_ci } else if (mode == ATH5K_ANI_MODE_MANUAL_LOW) { 6698c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 6708c2ecf20Sopenharmony_ci "ANI manual low -> high sensitivity\n"); 6718c2ecf20Sopenharmony_ci ath5k_ani_set_noise_immunity_level(ah, 0); 6728c2ecf20Sopenharmony_ci ath5k_ani_set_spur_immunity_level(ah, 0); 6738c2ecf20Sopenharmony_ci ath5k_ani_set_firstep_level(ah, 0); 6748c2ecf20Sopenharmony_ci ath5k_ani_set_ofdm_weak_signal_detection(ah, true); 6758c2ecf20Sopenharmony_ci ath5k_ani_set_cck_weak_signal_detection(ah, true); 6768c2ecf20Sopenharmony_ci } else if (mode == ATH5K_ANI_MODE_MANUAL_HIGH) { 6778c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 6788c2ecf20Sopenharmony_ci "ANI manual high -> low sensitivity\n"); 6798c2ecf20Sopenharmony_ci ath5k_ani_set_noise_immunity_level(ah, 6808c2ecf20Sopenharmony_ci ATH5K_ANI_MAX_NOISE_IMM_LVL); 6818c2ecf20Sopenharmony_ci ath5k_ani_set_spur_immunity_level(ah, 6828c2ecf20Sopenharmony_ci ah->ani_state.max_spur_level); 6838c2ecf20Sopenharmony_ci ath5k_ani_set_firstep_level(ah, ATH5K_ANI_MAX_FIRSTEP_LVL); 6848c2ecf20Sopenharmony_ci ath5k_ani_set_ofdm_weak_signal_detection(ah, false); 6858c2ecf20Sopenharmony_ci ath5k_ani_set_cck_weak_signal_detection(ah, false); 6868c2ecf20Sopenharmony_ci } else if (mode == ATH5K_ANI_MODE_AUTO) { 6878c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI auto\n"); 6888c2ecf20Sopenharmony_ci ath5k_ani_set_noise_immunity_level(ah, 0); 6898c2ecf20Sopenharmony_ci ath5k_ani_set_spur_immunity_level(ah, 0); 6908c2ecf20Sopenharmony_ci ath5k_ani_set_firstep_level(ah, 0); 6918c2ecf20Sopenharmony_ci ath5k_ani_set_ofdm_weak_signal_detection(ah, true); 6928c2ecf20Sopenharmony_ci ath5k_ani_set_cck_weak_signal_detection(ah, false); 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci /* newer hardware has PHY error counter registers which we can use to 6968c2ecf20Sopenharmony_ci * get OFDM and CCK error counts. older hardware has to set rxfilter and 6978c2ecf20Sopenharmony_ci * report every single PHY error by calling ath5k_ani_phy_error_report() 6988c2ecf20Sopenharmony_ci */ 6998c2ecf20Sopenharmony_ci if (mode == ATH5K_ANI_MODE_AUTO) { 7008c2ecf20Sopenharmony_ci if (ah->ah_capabilities.cap_has_phyerr_counters) 7018c2ecf20Sopenharmony_ci ath5k_enable_phy_err_counters(ah); 7028c2ecf20Sopenharmony_ci else 7038c2ecf20Sopenharmony_ci ath5k_hw_set_rx_filter(ah, ath5k_hw_get_rx_filter(ah) | 7048c2ecf20Sopenharmony_ci AR5K_RX_FILTER_PHYERR); 7058c2ecf20Sopenharmony_ci } else { 7068c2ecf20Sopenharmony_ci if (ah->ah_capabilities.cap_has_phyerr_counters) 7078c2ecf20Sopenharmony_ci ath5k_disable_phy_err_counters(ah); 7088c2ecf20Sopenharmony_ci else 7098c2ecf20Sopenharmony_ci ath5k_hw_set_rx_filter(ah, ath5k_hw_get_rx_filter(ah) & 7108c2ecf20Sopenharmony_ci ~AR5K_RX_FILTER_PHYERR); 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci ah->ani_state.ani_mode = mode; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci/**************\ 7188c2ecf20Sopenharmony_ci* Debug output * 7198c2ecf20Sopenharmony_ci\**************/ 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci#ifdef CONFIG_ATH5K_DEBUG 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci/** 7248c2ecf20Sopenharmony_ci * ath5k_ani_print_counters() - Print ANI counters 7258c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 7268c2ecf20Sopenharmony_ci * 7278c2ecf20Sopenharmony_ci * Used for debugging ANI 7288c2ecf20Sopenharmony_ci */ 7298c2ecf20Sopenharmony_civoid 7308c2ecf20Sopenharmony_ciath5k_ani_print_counters(struct ath5k_hw *ah) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci /* clears too */ 7338c2ecf20Sopenharmony_ci pr_notice("ACK fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_ACK_FAIL)); 7348c2ecf20Sopenharmony_ci pr_notice("RTS fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_FAIL)); 7358c2ecf20Sopenharmony_ci pr_notice("RTS success\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_OK)); 7368c2ecf20Sopenharmony_ci pr_notice("FCS error\t%d\n", ath5k_hw_reg_read(ah, AR5K_FCS_FAIL)); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* no clear */ 7398c2ecf20Sopenharmony_ci pr_notice("tx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_TX)); 7408c2ecf20Sopenharmony_ci pr_notice("rx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RX)); 7418c2ecf20Sopenharmony_ci pr_notice("busy\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RXCLR)); 7428c2ecf20Sopenharmony_ci pr_notice("cycles\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_CYCLE)); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci pr_notice("AR5K_PHYERR_CNT1\t%d\n", 7458c2ecf20Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1)); 7468c2ecf20Sopenharmony_ci pr_notice("AR5K_PHYERR_CNT2\t%d\n", 7478c2ecf20Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2)); 7488c2ecf20Sopenharmony_ci pr_notice("AR5K_OFDM_FIL_CNT\t%d\n", 7498c2ecf20Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_OFDM_FIL_CNT)); 7508c2ecf20Sopenharmony_ci pr_notice("AR5K_CCK_FIL_CNT\t%d\n", 7518c2ecf20Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_CCK_FIL_CNT)); 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci#endif 755