18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org> 38c2ecf20Sopenharmony_ci * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com> 48c2ecf20Sopenharmony_ci * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com> 58c2ecf20Sopenharmony_ci * Copyright (c) 2008-2009 Felix Fietkau <nbd@openwrt.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and distribute this software for any 88c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 98c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 128c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 138c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 148c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 158c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 168c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 178c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/***********************\ 228c2ecf20Sopenharmony_ci* PHY related functions * 238c2ecf20Sopenharmony_ci\***********************/ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/delay.h> 288c2ecf20Sopenharmony_ci#include <linux/slab.h> 298c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include "ath5k.h" 328c2ecf20Sopenharmony_ci#include "reg.h" 338c2ecf20Sopenharmony_ci#include "rfbuffer.h" 348c2ecf20Sopenharmony_ci#include "rfgain.h" 358c2ecf20Sopenharmony_ci#include "../regd.h" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/** 398c2ecf20Sopenharmony_ci * DOC: PHY related functions 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * Here we handle the low-level functions related to baseband 428c2ecf20Sopenharmony_ci * and analog frontend (RF) parts. This is by far the most complex 438c2ecf20Sopenharmony_ci * part of the hw code so make sure you know what you are doing. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * Here is a list of what this is all about: 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * - Channel setting/switching 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * - Automatic Gain Control (AGC) calibration 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * - Noise Floor calibration 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * - I/Q imbalance calibration (QAM correction) 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * - Calibration due to thermal changes (gain_F) 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * - Spur noise mitigation 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * - RF/PHY initialization for the various operating modes and bwmodes 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * - Antenna control 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * - TX power control per channel/rate/packet type 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * Also have in mind we never got documentation for most of these 668c2ecf20Sopenharmony_ci * functions, what we have comes mostly from Atheros's code, reverse 678c2ecf20Sopenharmony_ci * engineering and patent docs/presentations etc. 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/******************\ 728c2ecf20Sopenharmony_ci* Helper functions * 738c2ecf20Sopenharmony_ci\******************/ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/** 768c2ecf20Sopenharmony_ci * ath5k_hw_radio_revision() - Get the PHY Chip revision 778c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 788c2ecf20Sopenharmony_ci * @band: One of enum nl80211_band 798c2ecf20Sopenharmony_ci * 808c2ecf20Sopenharmony_ci * Returns the revision number of a 2GHz, 5GHz or single chip 818c2ecf20Sopenharmony_ci * radio. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ciu16 848c2ecf20Sopenharmony_ciath5k_hw_radio_revision(struct ath5k_hw *ah, enum nl80211_band band) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci unsigned int i; 878c2ecf20Sopenharmony_ci u32 srev; 888c2ecf20Sopenharmony_ci u16 ret; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* 918c2ecf20Sopenharmony_ci * Set the radio chip access register 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ci switch (band) { 948c2ecf20Sopenharmony_ci case NL80211_BAND_2GHZ: 958c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_2GHZ, AR5K_PHY(0)); 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci case NL80211_BAND_5GHZ: 988c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0)); 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci default: 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci usleep_range(2000, 2500); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* ...wait until PHY is ready and read the selected radio revision */ 1078c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0x00001c16, AR5K_PHY(0x34)); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 1108c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0x00010000, AR5K_PHY(0x20)); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (ah->ah_version == AR5K_AR5210) { 1138c2ecf20Sopenharmony_ci srev = (ath5k_hw_reg_read(ah, AR5K_PHY(256)) >> 28) & 0xf; 1148c2ecf20Sopenharmony_ci ret = (u16)ath5k_hw_bitswap(srev, 4) + 1; 1158c2ecf20Sopenharmony_ci } else { 1168c2ecf20Sopenharmony_ci srev = (ath5k_hw_reg_read(ah, AR5K_PHY(0x100)) >> 24) & 0xff; 1178c2ecf20Sopenharmony_ci ret = (u16)ath5k_hw_bitswap(((srev & 0xf0) >> 4) | 1188c2ecf20Sopenharmony_ci ((srev & 0x0f) << 4), 8); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* Reset to the 5GHz mode */ 1228c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0)); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return ret; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/** 1288c2ecf20Sopenharmony_ci * ath5k_channel_ok() - Check if a channel is supported by the hw 1298c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 1308c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * Note: We don't do any regulatory domain checks here, it's just 1338c2ecf20Sopenharmony_ci * a sanity check. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_cibool 1368c2ecf20Sopenharmony_ciath5k_channel_ok(struct ath5k_hw *ah, struct ieee80211_channel *channel) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci u16 freq = channel->center_freq; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Check if the channel is in our supported range */ 1418c2ecf20Sopenharmony_ci if (channel->band == NL80211_BAND_2GHZ) { 1428c2ecf20Sopenharmony_ci if ((freq >= ah->ah_capabilities.cap_range.range_2ghz_min) && 1438c2ecf20Sopenharmony_ci (freq <= ah->ah_capabilities.cap_range.range_2ghz_max)) 1448c2ecf20Sopenharmony_ci return true; 1458c2ecf20Sopenharmony_ci } else if (channel->band == NL80211_BAND_5GHZ) 1468c2ecf20Sopenharmony_ci if ((freq >= ah->ah_capabilities.cap_range.range_5ghz_min) && 1478c2ecf20Sopenharmony_ci (freq <= ah->ah_capabilities.cap_range.range_5ghz_max)) 1488c2ecf20Sopenharmony_ci return true; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return false; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/** 1548c2ecf20Sopenharmony_ci * ath5k_hw_chan_has_spur_noise() - Check if channel is sensitive to spur noise 1558c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 1568c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_cibool 1598c2ecf20Sopenharmony_ciath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah, 1608c2ecf20Sopenharmony_ci struct ieee80211_channel *channel) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci u8 refclk_freq; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if ((ah->ah_radio == AR5K_RF5112) || 1658c2ecf20Sopenharmony_ci (ah->ah_radio == AR5K_RF5413) || 1668c2ecf20Sopenharmony_ci (ah->ah_radio == AR5K_RF2413) || 1678c2ecf20Sopenharmony_ci (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) 1688c2ecf20Sopenharmony_ci refclk_freq = 40; 1698c2ecf20Sopenharmony_ci else 1708c2ecf20Sopenharmony_ci refclk_freq = 32; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if ((channel->center_freq % refclk_freq != 0) && 1738c2ecf20Sopenharmony_ci ((channel->center_freq % refclk_freq < 10) || 1748c2ecf20Sopenharmony_ci (channel->center_freq % refclk_freq > 22))) 1758c2ecf20Sopenharmony_ci return true; 1768c2ecf20Sopenharmony_ci else 1778c2ecf20Sopenharmony_ci return false; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/** 1818c2ecf20Sopenharmony_ci * ath5k_hw_rfb_op() - Perform an operation on the given RF Buffer 1828c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 1838c2ecf20Sopenharmony_ci * @rf_regs: The struct ath5k_rf_reg 1848c2ecf20Sopenharmony_ci * @val: New value 1858c2ecf20Sopenharmony_ci * @reg_id: RF register ID 1868c2ecf20Sopenharmony_ci * @set: Indicate we need to swap data 1878c2ecf20Sopenharmony_ci * 1888c2ecf20Sopenharmony_ci * This is an internal function used to modify RF Banks before 1898c2ecf20Sopenharmony_ci * writing them to AR5K_RF_BUFFER. Check out rfbuffer.h for more 1908c2ecf20Sopenharmony_ci * infos. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_cistatic unsigned int 1938c2ecf20Sopenharmony_ciath5k_hw_rfb_op(struct ath5k_hw *ah, const struct ath5k_rf_reg *rf_regs, 1948c2ecf20Sopenharmony_ci u32 val, u8 reg_id, bool set) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci const struct ath5k_rf_reg *rfreg = NULL; 1978c2ecf20Sopenharmony_ci u8 offset, bank, num_bits, col, position; 1988c2ecf20Sopenharmony_ci u16 entry; 1998c2ecf20Sopenharmony_ci u32 mask, data, last_bit, bits_shifted, first_bit; 2008c2ecf20Sopenharmony_ci u32 *rfb; 2018c2ecf20Sopenharmony_ci s32 bits_left; 2028c2ecf20Sopenharmony_ci int i; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci data = 0; 2058c2ecf20Sopenharmony_ci rfb = ah->ah_rf_banks; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci for (i = 0; i < ah->ah_rf_regs_count; i++) { 2088c2ecf20Sopenharmony_ci if (rf_regs[i].index == reg_id) { 2098c2ecf20Sopenharmony_ci rfreg = &rf_regs[i]; 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (rfb == NULL || rfreg == NULL) { 2158c2ecf20Sopenharmony_ci ATH5K_PRINTF("Rf register not found!\n"); 2168c2ecf20Sopenharmony_ci /* should not happen */ 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci bank = rfreg->bank; 2218c2ecf20Sopenharmony_ci num_bits = rfreg->field.len; 2228c2ecf20Sopenharmony_ci first_bit = rfreg->field.pos; 2238c2ecf20Sopenharmony_ci col = rfreg->field.col; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* first_bit is an offset from bank's 2268c2ecf20Sopenharmony_ci * start. Since we have all banks on 2278c2ecf20Sopenharmony_ci * the same array, we use this offset 2288c2ecf20Sopenharmony_ci * to mark each bank's start */ 2298c2ecf20Sopenharmony_ci offset = ah->ah_offset[bank]; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* Boundary check */ 2328c2ecf20Sopenharmony_ci if (!(col <= 3 && num_bits <= 32 && first_bit + num_bits <= 319)) { 2338c2ecf20Sopenharmony_ci ATH5K_PRINTF("invalid values at offset %u\n", offset); 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci entry = ((first_bit - 1) / 8) + offset; 2388c2ecf20Sopenharmony_ci position = (first_bit - 1) % 8; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (set) 2418c2ecf20Sopenharmony_ci data = ath5k_hw_bitswap(val, num_bits); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci for (bits_shifted = 0, bits_left = num_bits; bits_left > 0; 2448c2ecf20Sopenharmony_ci position = 0, entry++) { 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci last_bit = (position + bits_left > 8) ? 8 : 2478c2ecf20Sopenharmony_ci position + bits_left; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci mask = (((1 << last_bit) - 1) ^ ((1 << position) - 1)) << 2508c2ecf20Sopenharmony_ci (col * 8); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (set) { 2538c2ecf20Sopenharmony_ci rfb[entry] &= ~mask; 2548c2ecf20Sopenharmony_ci rfb[entry] |= ((data << position) << (col * 8)) & mask; 2558c2ecf20Sopenharmony_ci data >>= (8 - position); 2568c2ecf20Sopenharmony_ci } else { 2578c2ecf20Sopenharmony_ci data |= (((rfb[entry] & mask) >> (col * 8)) >> position) 2588c2ecf20Sopenharmony_ci << bits_shifted; 2598c2ecf20Sopenharmony_ci bits_shifted += last_bit - position; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci bits_left -= 8 - position; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci data = set ? 1 : ath5k_hw_bitswap(data, num_bits); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return data; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/** 2718c2ecf20Sopenharmony_ci * ath5k_hw_write_ofdm_timings() - set OFDM timings on AR5212 2728c2ecf20Sopenharmony_ci * @ah: the &struct ath5k_hw 2738c2ecf20Sopenharmony_ci * @channel: the currently set channel upon reset 2748c2ecf20Sopenharmony_ci * 2758c2ecf20Sopenharmony_ci * Write the delta slope coefficient (used on pilot tracking ?) for OFDM 2768c2ecf20Sopenharmony_ci * operation on the AR5212 upon reset. This is a helper for ath5k_hw_phy_init. 2778c2ecf20Sopenharmony_ci * 2788c2ecf20Sopenharmony_ci * Since delta slope is floating point we split it on its exponent and 2798c2ecf20Sopenharmony_ci * mantissa and provide these values on hw. 2808c2ecf20Sopenharmony_ci * 2818c2ecf20Sopenharmony_ci * For more infos i think this patent is related 2828c2ecf20Sopenharmony_ci * "http://www.freepatentsonline.com/7184495.html" 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_cistatic inline int 2858c2ecf20Sopenharmony_ciath5k_hw_write_ofdm_timings(struct ath5k_hw *ah, 2868c2ecf20Sopenharmony_ci struct ieee80211_channel *channel) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci /* Get exponent and mantissa and set it */ 2898c2ecf20Sopenharmony_ci u32 coef_scaled, coef_exp, coef_man, 2908c2ecf20Sopenharmony_ci ds_coef_exp, ds_coef_man, clock; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci BUG_ON(!(ah->ah_version == AR5K_AR5212) || 2938c2ecf20Sopenharmony_ci (channel->hw_value == AR5K_MODE_11B)); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* Get coefficient 2968c2ecf20Sopenharmony_ci * ALGO: coef = (5 * clock / carrier_freq) / 2 2978c2ecf20Sopenharmony_ci * we scale coef by shifting clock value by 24 for 2988c2ecf20Sopenharmony_ci * better precision since we use integers */ 2998c2ecf20Sopenharmony_ci switch (ah->ah_bwmode) { 3008c2ecf20Sopenharmony_ci case AR5K_BWMODE_40MHZ: 3018c2ecf20Sopenharmony_ci clock = 40 * 2; 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci case AR5K_BWMODE_10MHZ: 3048c2ecf20Sopenharmony_ci clock = 40 / 2; 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci case AR5K_BWMODE_5MHZ: 3078c2ecf20Sopenharmony_ci clock = 40 / 4; 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci default: 3108c2ecf20Sopenharmony_ci clock = 40; 3118c2ecf20Sopenharmony_ci break; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci coef_scaled = ((5 * (clock << 24)) / 2) / channel->center_freq; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* Get exponent 3168c2ecf20Sopenharmony_ci * ALGO: coef_exp = 14 - highest set bit position */ 3178c2ecf20Sopenharmony_ci coef_exp = ilog2(coef_scaled); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* Doesn't make sense if it's zero*/ 3208c2ecf20Sopenharmony_ci if (!coef_scaled || !coef_exp) 3218c2ecf20Sopenharmony_ci return -EINVAL; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* Note: we've shifted coef_scaled by 24 */ 3248c2ecf20Sopenharmony_ci coef_exp = 14 - (coef_exp - 24); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* Get mantissa (significant digits) 3288c2ecf20Sopenharmony_ci * ALGO: coef_mant = floor(coef_scaled* 2^coef_exp+0.5) */ 3298c2ecf20Sopenharmony_ci coef_man = coef_scaled + 3308c2ecf20Sopenharmony_ci (1 << (24 - coef_exp - 1)); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* Calculate delta slope coefficient exponent 3338c2ecf20Sopenharmony_ci * and mantissa (remove scaling) and set them on hw */ 3348c2ecf20Sopenharmony_ci ds_coef_man = coef_man >> (24 - coef_exp); 3358c2ecf20Sopenharmony_ci ds_coef_exp = coef_exp - 16; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3, 3388c2ecf20Sopenharmony_ci AR5K_PHY_TIMING_3_DSC_MAN, ds_coef_man); 3398c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3, 3408c2ecf20Sopenharmony_ci AR5K_PHY_TIMING_3_DSC_EXP, ds_coef_exp); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci/** 3468c2ecf20Sopenharmony_ci * ath5k_hw_phy_disable() - Disable PHY 3478c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_ciint ath5k_hw_phy_disable(struct ath5k_hw *ah) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci /*Just a try M.F.*/ 3528c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/** 3588c2ecf20Sopenharmony_ci * ath5k_hw_wait_for_synth() - Wait for synth to settle 3598c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 3608c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_cistatic void 3638c2ecf20Sopenharmony_ciath5k_hw_wait_for_synth(struct ath5k_hw *ah, 3648c2ecf20Sopenharmony_ci struct ieee80211_channel *channel) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci /* 3678c2ecf20Sopenharmony_ci * On 5211+ read activation -> rx delay 3688c2ecf20Sopenharmony_ci * and use it (100ns steps). 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci if (ah->ah_version != AR5K_AR5210) { 3718c2ecf20Sopenharmony_ci u32 delay; 3728c2ecf20Sopenharmony_ci delay = ath5k_hw_reg_read(ah, AR5K_PHY_RX_DELAY) & 3738c2ecf20Sopenharmony_ci AR5K_PHY_RX_DELAY_M; 3748c2ecf20Sopenharmony_ci delay = (channel->hw_value == AR5K_MODE_11B) ? 3758c2ecf20Sopenharmony_ci ((delay << 2) / 22) : (delay / 10); 3768c2ecf20Sopenharmony_ci if (ah->ah_bwmode == AR5K_BWMODE_10MHZ) 3778c2ecf20Sopenharmony_ci delay = delay << 1; 3788c2ecf20Sopenharmony_ci if (ah->ah_bwmode == AR5K_BWMODE_5MHZ) 3798c2ecf20Sopenharmony_ci delay = delay << 2; 3808c2ecf20Sopenharmony_ci /* XXX: /2 on turbo ? Let's be safe 3818c2ecf20Sopenharmony_ci * for now */ 3828c2ecf20Sopenharmony_ci usleep_range(100 + delay, 100 + (2 * delay)); 3838c2ecf20Sopenharmony_ci } else { 3848c2ecf20Sopenharmony_ci usleep_range(1000, 1500); 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/**********************\ 3908c2ecf20Sopenharmony_ci* RF Gain optimization * 3918c2ecf20Sopenharmony_ci\**********************/ 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/** 3948c2ecf20Sopenharmony_ci * DOC: RF Gain optimization 3958c2ecf20Sopenharmony_ci * 3968c2ecf20Sopenharmony_ci * This code is used to optimize RF gain on different environments 3978c2ecf20Sopenharmony_ci * (temperature mostly) based on feedback from a power detector. 3988c2ecf20Sopenharmony_ci * 3998c2ecf20Sopenharmony_ci * It's only used on RF5111 and RF5112, later RF chips seem to have 4008c2ecf20Sopenharmony_ci * auto adjustment on hw -notice they have a much smaller BANK 7 and 4018c2ecf20Sopenharmony_ci * no gain optimization ladder-. 4028c2ecf20Sopenharmony_ci * 4038c2ecf20Sopenharmony_ci * For more infos check out this patent doc 4048c2ecf20Sopenharmony_ci * "http://www.freepatentsonline.com/7400691.html" 4058c2ecf20Sopenharmony_ci * 4068c2ecf20Sopenharmony_ci * This paper describes power drops as seen on the receiver due to 4078c2ecf20Sopenharmony_ci * probe packets 4088c2ecf20Sopenharmony_ci * "http://www.cnri.dit.ie/publications/ICT08%20-%20Practical%20Issues 4098c2ecf20Sopenharmony_ci * %20of%20Power%20Control.pdf" 4108c2ecf20Sopenharmony_ci * 4118c2ecf20Sopenharmony_ci * And this is the MadWiFi bug entry related to the above 4128c2ecf20Sopenharmony_ci * "http://madwifi-project.org/ticket/1659" 4138c2ecf20Sopenharmony_ci * with various measurements and diagrams 4148c2ecf20Sopenharmony_ci */ 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci/** 4178c2ecf20Sopenharmony_ci * ath5k_hw_rfgain_opt_init() - Initialize ah_gain during attach 4188c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_ciint ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci /* Initialize the gain optimization values */ 4238c2ecf20Sopenharmony_ci switch (ah->ah_radio) { 4248c2ecf20Sopenharmony_ci case AR5K_RF5111: 4258c2ecf20Sopenharmony_ci ah->ah_gain.g_step_idx = rfgain_opt_5111.go_default; 4268c2ecf20Sopenharmony_ci ah->ah_gain.g_low = 20; 4278c2ecf20Sopenharmony_ci ah->ah_gain.g_high = 35; 4288c2ecf20Sopenharmony_ci ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE; 4298c2ecf20Sopenharmony_ci break; 4308c2ecf20Sopenharmony_ci case AR5K_RF5112: 4318c2ecf20Sopenharmony_ci ah->ah_gain.g_step_idx = rfgain_opt_5112.go_default; 4328c2ecf20Sopenharmony_ci ah->ah_gain.g_low = 20; 4338c2ecf20Sopenharmony_ci ah->ah_gain.g_high = 85; 4348c2ecf20Sopenharmony_ci ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE; 4358c2ecf20Sopenharmony_ci break; 4368c2ecf20Sopenharmony_ci default: 4378c2ecf20Sopenharmony_ci return -EINVAL; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return 0; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci/** 4448c2ecf20Sopenharmony_ci * ath5k_hw_request_rfgain_probe() - Request a PAPD probe packet 4458c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 4468c2ecf20Sopenharmony_ci * 4478c2ecf20Sopenharmony_ci * Schedules a gain probe check on the next transmitted packet. 4488c2ecf20Sopenharmony_ci * That means our next packet is going to be sent with lower 4498c2ecf20Sopenharmony_ci * tx power and a Peak to Average Power Detector (PAPD) will try 4508c2ecf20Sopenharmony_ci * to measure the gain. 4518c2ecf20Sopenharmony_ci * 4528c2ecf20Sopenharmony_ci * TODO: Force a tx packet (bypassing PCU arbitrator etc) 4538c2ecf20Sopenharmony_ci * just after we enable the probe so that we don't mess with 4548c2ecf20Sopenharmony_ci * standard traffic. 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_cistatic void 4578c2ecf20Sopenharmony_ciath5k_hw_request_rfgain_probe(struct ath5k_hw *ah) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* Skip if gain calibration is inactive or 4618c2ecf20Sopenharmony_ci * we already handle a probe request */ 4628c2ecf20Sopenharmony_ci if (ah->ah_gain.g_state != AR5K_RFGAIN_ACTIVE) 4638c2ecf20Sopenharmony_ci return; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* Send the packet with 2dB below max power as 4668c2ecf20Sopenharmony_ci * patent doc suggest */ 4678c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_ofdm - 4, 4688c2ecf20Sopenharmony_ci AR5K_PHY_PAPD_PROBE_TXPOWER) | 4698c2ecf20Sopenharmony_ci AR5K_PHY_PAPD_PROBE_TX_NEXT, AR5K_PHY_PAPD_PROBE); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci ah->ah_gain.g_state = AR5K_RFGAIN_READ_REQUESTED; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci/** 4768c2ecf20Sopenharmony_ci * ath5k_hw_rf_gainf_corr() - Calculate Gain_F measurement correction 4778c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 4788c2ecf20Sopenharmony_ci * 4798c2ecf20Sopenharmony_ci * Calculate Gain_F measurement correction 4808c2ecf20Sopenharmony_ci * based on the current step for RF5112 rev. 2 4818c2ecf20Sopenharmony_ci */ 4828c2ecf20Sopenharmony_cistatic u32 4838c2ecf20Sopenharmony_ciath5k_hw_rf_gainf_corr(struct ath5k_hw *ah) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci u32 mix, step; 4868c2ecf20Sopenharmony_ci const struct ath5k_gain_opt *go; 4878c2ecf20Sopenharmony_ci const struct ath5k_gain_opt_step *g_step; 4888c2ecf20Sopenharmony_ci const struct ath5k_rf_reg *rf_regs; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* Only RF5112 Rev. 2 supports it */ 4918c2ecf20Sopenharmony_ci if ((ah->ah_radio != AR5K_RF5112) || 4928c2ecf20Sopenharmony_ci (ah->ah_radio_5ghz_revision <= AR5K_SREV_RAD_5112A)) 4938c2ecf20Sopenharmony_ci return 0; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci go = &rfgain_opt_5112; 4968c2ecf20Sopenharmony_ci rf_regs = rf_regs_5112a; 4978c2ecf20Sopenharmony_ci ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112a); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci g_step = &go->go_step[ah->ah_gain.g_step_idx]; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (ah->ah_rf_banks == NULL) 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci ah->ah_gain.g_f_corr = 0; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* No VGA (Variable Gain Amplifier) override, skip */ 5078c2ecf20Sopenharmony_ci if (ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXVGA_OVR, false) != 1) 5088c2ecf20Sopenharmony_ci return 0; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* Mix gain stepping */ 5118c2ecf20Sopenharmony_ci step = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXGAIN_STEP, false); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* Mix gain override */ 5148c2ecf20Sopenharmony_ci mix = g_step->gos_param[0]; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci switch (mix) { 5178c2ecf20Sopenharmony_ci case 3: 5188c2ecf20Sopenharmony_ci ah->ah_gain.g_f_corr = step * 2; 5198c2ecf20Sopenharmony_ci break; 5208c2ecf20Sopenharmony_ci case 2: 5218c2ecf20Sopenharmony_ci ah->ah_gain.g_f_corr = (step - 5) * 2; 5228c2ecf20Sopenharmony_ci break; 5238c2ecf20Sopenharmony_ci case 1: 5248c2ecf20Sopenharmony_ci ah->ah_gain.g_f_corr = step; 5258c2ecf20Sopenharmony_ci break; 5268c2ecf20Sopenharmony_ci default: 5278c2ecf20Sopenharmony_ci ah->ah_gain.g_f_corr = 0; 5288c2ecf20Sopenharmony_ci break; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return ah->ah_gain.g_f_corr; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci/** 5358c2ecf20Sopenharmony_ci * ath5k_hw_rf_check_gainf_readback() - Validate Gain_F feedback from detector 5368c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 5378c2ecf20Sopenharmony_ci * 5388c2ecf20Sopenharmony_ci * Check if current gain_F measurement is in the range of our 5398c2ecf20Sopenharmony_ci * power detector windows. If we get a measurement outside range 5408c2ecf20Sopenharmony_ci * we know it's not accurate (detectors can't measure anything outside 5418c2ecf20Sopenharmony_ci * their detection window) so we must ignore it. 5428c2ecf20Sopenharmony_ci * 5438c2ecf20Sopenharmony_ci * Returns true if readback was O.K. or false on failure 5448c2ecf20Sopenharmony_ci */ 5458c2ecf20Sopenharmony_cistatic bool 5468c2ecf20Sopenharmony_ciath5k_hw_rf_check_gainf_readback(struct ath5k_hw *ah) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci const struct ath5k_rf_reg *rf_regs; 5498c2ecf20Sopenharmony_ci u32 step, mix_ovr, level[4]; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (ah->ah_rf_banks == NULL) 5528c2ecf20Sopenharmony_ci return false; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (ah->ah_radio == AR5K_RF5111) { 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci rf_regs = rf_regs_5111; 5578c2ecf20Sopenharmony_ci ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5111); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci step = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_RFGAIN_STEP, 5608c2ecf20Sopenharmony_ci false); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci level[0] = 0; 5638c2ecf20Sopenharmony_ci level[1] = (step == 63) ? 50 : step + 4; 5648c2ecf20Sopenharmony_ci level[2] = (step != 63) ? 64 : level[0]; 5658c2ecf20Sopenharmony_ci level[3] = level[2] + 50; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci ah->ah_gain.g_high = level[3] - 5688c2ecf20Sopenharmony_ci (step == 63 ? AR5K_GAIN_DYN_ADJUST_HI_MARGIN : -5); 5698c2ecf20Sopenharmony_ci ah->ah_gain.g_low = level[0] + 5708c2ecf20Sopenharmony_ci (step == 63 ? AR5K_GAIN_DYN_ADJUST_LO_MARGIN : 0); 5718c2ecf20Sopenharmony_ci } else { 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci rf_regs = rf_regs_5112; 5748c2ecf20Sopenharmony_ci ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci mix_ovr = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXVGA_OVR, 5778c2ecf20Sopenharmony_ci false); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci level[0] = level[2] = 0; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (mix_ovr == 1) { 5828c2ecf20Sopenharmony_ci level[1] = level[3] = 83; 5838c2ecf20Sopenharmony_ci } else { 5848c2ecf20Sopenharmony_ci level[1] = level[3] = 107; 5858c2ecf20Sopenharmony_ci ah->ah_gain.g_high = 55; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci return (ah->ah_gain.g_current >= level[0] && 5908c2ecf20Sopenharmony_ci ah->ah_gain.g_current <= level[1]) || 5918c2ecf20Sopenharmony_ci (ah->ah_gain.g_current >= level[2] && 5928c2ecf20Sopenharmony_ci ah->ah_gain.g_current <= level[3]); 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci/** 5968c2ecf20Sopenharmony_ci * ath5k_hw_rf_gainf_adjust() - Perform Gain_F adjustment 5978c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 5988c2ecf20Sopenharmony_ci * 5998c2ecf20Sopenharmony_ci * Choose the right target gain based on current gain 6008c2ecf20Sopenharmony_ci * and RF gain optimization ladder 6018c2ecf20Sopenharmony_ci */ 6028c2ecf20Sopenharmony_cistatic s8 6038c2ecf20Sopenharmony_ciath5k_hw_rf_gainf_adjust(struct ath5k_hw *ah) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci const struct ath5k_gain_opt *go; 6068c2ecf20Sopenharmony_ci const struct ath5k_gain_opt_step *g_step; 6078c2ecf20Sopenharmony_ci int ret = 0; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci switch (ah->ah_radio) { 6108c2ecf20Sopenharmony_ci case AR5K_RF5111: 6118c2ecf20Sopenharmony_ci go = &rfgain_opt_5111; 6128c2ecf20Sopenharmony_ci break; 6138c2ecf20Sopenharmony_ci case AR5K_RF5112: 6148c2ecf20Sopenharmony_ci go = &rfgain_opt_5112; 6158c2ecf20Sopenharmony_ci break; 6168c2ecf20Sopenharmony_ci default: 6178c2ecf20Sopenharmony_ci return 0; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci g_step = &go->go_step[ah->ah_gain.g_step_idx]; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci if (ah->ah_gain.g_current >= ah->ah_gain.g_high) { 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci /* Reached maximum */ 6258c2ecf20Sopenharmony_ci if (ah->ah_gain.g_step_idx == 0) 6268c2ecf20Sopenharmony_ci return -1; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci for (ah->ah_gain.g_target = ah->ah_gain.g_current; 6298c2ecf20Sopenharmony_ci ah->ah_gain.g_target >= ah->ah_gain.g_high && 6308c2ecf20Sopenharmony_ci ah->ah_gain.g_step_idx > 0; 6318c2ecf20Sopenharmony_ci g_step = &go->go_step[ah->ah_gain.g_step_idx]) 6328c2ecf20Sopenharmony_ci ah->ah_gain.g_target -= 2 * 6338c2ecf20Sopenharmony_ci (go->go_step[--(ah->ah_gain.g_step_idx)].gos_gain - 6348c2ecf20Sopenharmony_ci g_step->gos_gain); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci ret = 1; 6378c2ecf20Sopenharmony_ci goto done; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (ah->ah_gain.g_current <= ah->ah_gain.g_low) { 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* Reached minimum */ 6438c2ecf20Sopenharmony_ci if (ah->ah_gain.g_step_idx == (go->go_steps_count - 1)) 6448c2ecf20Sopenharmony_ci return -2; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci for (ah->ah_gain.g_target = ah->ah_gain.g_current; 6478c2ecf20Sopenharmony_ci ah->ah_gain.g_target <= ah->ah_gain.g_low && 6488c2ecf20Sopenharmony_ci ah->ah_gain.g_step_idx < go->go_steps_count - 1; 6498c2ecf20Sopenharmony_ci g_step = &go->go_step[ah->ah_gain.g_step_idx]) 6508c2ecf20Sopenharmony_ci ah->ah_gain.g_target -= 2 * 6518c2ecf20Sopenharmony_ci (go->go_step[++ah->ah_gain.g_step_idx].gos_gain - 6528c2ecf20Sopenharmony_ci g_step->gos_gain); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci ret = 2; 6558c2ecf20Sopenharmony_ci goto done; 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cidone: 6598c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, 6608c2ecf20Sopenharmony_ci "ret %d, gain step %u, current gain %u, target gain %u\n", 6618c2ecf20Sopenharmony_ci ret, ah->ah_gain.g_step_idx, ah->ah_gain.g_current, 6628c2ecf20Sopenharmony_ci ah->ah_gain.g_target); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci return ret; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci/** 6688c2ecf20Sopenharmony_ci * ath5k_hw_gainf_calibrate() - Do a gain_F calibration 6698c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 6708c2ecf20Sopenharmony_ci * 6718c2ecf20Sopenharmony_ci * Main callback for thermal RF gain calibration engine 6728c2ecf20Sopenharmony_ci * Check for a new gain reading and schedule an adjustment 6738c2ecf20Sopenharmony_ci * if needed. 6748c2ecf20Sopenharmony_ci * 6758c2ecf20Sopenharmony_ci * Returns one of enum ath5k_rfgain codes 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_cienum ath5k_rfgain 6788c2ecf20Sopenharmony_ciath5k_hw_gainf_calibrate(struct ath5k_hw *ah) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci u32 data, type; 6818c2ecf20Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (ah->ah_rf_banks == NULL || 6848c2ecf20Sopenharmony_ci ah->ah_gain.g_state == AR5K_RFGAIN_INACTIVE) 6858c2ecf20Sopenharmony_ci return AR5K_RFGAIN_INACTIVE; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci /* No check requested, either engine is inactive 6888c2ecf20Sopenharmony_ci * or an adjustment is already requested */ 6898c2ecf20Sopenharmony_ci if (ah->ah_gain.g_state != AR5K_RFGAIN_READ_REQUESTED) 6908c2ecf20Sopenharmony_ci goto done; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci /* Read the PAPD (Peak to Average Power Detector) 6938c2ecf20Sopenharmony_ci * register */ 6948c2ecf20Sopenharmony_ci data = ath5k_hw_reg_read(ah, AR5K_PHY_PAPD_PROBE); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci /* No probe is scheduled, read gain_F measurement */ 6978c2ecf20Sopenharmony_ci if (!(data & AR5K_PHY_PAPD_PROBE_TX_NEXT)) { 6988c2ecf20Sopenharmony_ci ah->ah_gain.g_current = data >> AR5K_PHY_PAPD_PROBE_GAINF_S; 6998c2ecf20Sopenharmony_ci type = AR5K_REG_MS(data, AR5K_PHY_PAPD_PROBE_TYPE); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci /* If tx packet is CCK correct the gain_F measurement 7028c2ecf20Sopenharmony_ci * by cck ofdm gain delta */ 7038c2ecf20Sopenharmony_ci if (type == AR5K_PHY_PAPD_PROBE_TYPE_CCK) { 7048c2ecf20Sopenharmony_ci if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) 7058c2ecf20Sopenharmony_ci ah->ah_gain.g_current += 7068c2ecf20Sopenharmony_ci ee->ee_cck_ofdm_gain_delta; 7078c2ecf20Sopenharmony_ci else 7088c2ecf20Sopenharmony_ci ah->ah_gain.g_current += 7098c2ecf20Sopenharmony_ci AR5K_GAIN_CCK_PROBE_CORR; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci /* Further correct gain_F measurement for 7138c2ecf20Sopenharmony_ci * RF5112A radios */ 7148c2ecf20Sopenharmony_ci if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) { 7158c2ecf20Sopenharmony_ci ath5k_hw_rf_gainf_corr(ah); 7168c2ecf20Sopenharmony_ci ah->ah_gain.g_current = 7178c2ecf20Sopenharmony_ci ah->ah_gain.g_current >= ah->ah_gain.g_f_corr ? 7188c2ecf20Sopenharmony_ci (ah->ah_gain.g_current - ah->ah_gain.g_f_corr) : 7198c2ecf20Sopenharmony_ci 0; 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci /* Check if measurement is ok and if we need 7238c2ecf20Sopenharmony_ci * to adjust gain, schedule a gain adjustment, 7248c2ecf20Sopenharmony_ci * else switch back to the active state */ 7258c2ecf20Sopenharmony_ci if (ath5k_hw_rf_check_gainf_readback(ah) && 7268c2ecf20Sopenharmony_ci AR5K_GAIN_CHECK_ADJUST(&ah->ah_gain) && 7278c2ecf20Sopenharmony_ci ath5k_hw_rf_gainf_adjust(ah)) { 7288c2ecf20Sopenharmony_ci ah->ah_gain.g_state = AR5K_RFGAIN_NEED_CHANGE; 7298c2ecf20Sopenharmony_ci } else { 7308c2ecf20Sopenharmony_ci ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_cidone: 7358c2ecf20Sopenharmony_ci return ah->ah_gain.g_state; 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci/** 7398c2ecf20Sopenharmony_ci * ath5k_hw_rfgain_init() - Write initial RF gain settings to hw 7408c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 7418c2ecf20Sopenharmony_ci * @band: One of enum nl80211_band 7428c2ecf20Sopenharmony_ci * 7438c2ecf20Sopenharmony_ci * Write initial RF gain table to set the RF sensitivity. 7448c2ecf20Sopenharmony_ci * 7458c2ecf20Sopenharmony_ci * NOTE: This one works on all RF chips and has nothing to do 7468c2ecf20Sopenharmony_ci * with Gain_F calibration 7478c2ecf20Sopenharmony_ci */ 7488c2ecf20Sopenharmony_cistatic int 7498c2ecf20Sopenharmony_ciath5k_hw_rfgain_init(struct ath5k_hw *ah, enum nl80211_band band) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci const struct ath5k_ini_rfgain *ath5k_rfg; 7528c2ecf20Sopenharmony_ci unsigned int i, size, index; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci switch (ah->ah_radio) { 7558c2ecf20Sopenharmony_ci case AR5K_RF5111: 7568c2ecf20Sopenharmony_ci ath5k_rfg = rfgain_5111; 7578c2ecf20Sopenharmony_ci size = ARRAY_SIZE(rfgain_5111); 7588c2ecf20Sopenharmony_ci break; 7598c2ecf20Sopenharmony_ci case AR5K_RF5112: 7608c2ecf20Sopenharmony_ci ath5k_rfg = rfgain_5112; 7618c2ecf20Sopenharmony_ci size = ARRAY_SIZE(rfgain_5112); 7628c2ecf20Sopenharmony_ci break; 7638c2ecf20Sopenharmony_ci case AR5K_RF2413: 7648c2ecf20Sopenharmony_ci ath5k_rfg = rfgain_2413; 7658c2ecf20Sopenharmony_ci size = ARRAY_SIZE(rfgain_2413); 7668c2ecf20Sopenharmony_ci break; 7678c2ecf20Sopenharmony_ci case AR5K_RF2316: 7688c2ecf20Sopenharmony_ci ath5k_rfg = rfgain_2316; 7698c2ecf20Sopenharmony_ci size = ARRAY_SIZE(rfgain_2316); 7708c2ecf20Sopenharmony_ci break; 7718c2ecf20Sopenharmony_ci case AR5K_RF5413: 7728c2ecf20Sopenharmony_ci ath5k_rfg = rfgain_5413; 7738c2ecf20Sopenharmony_ci size = ARRAY_SIZE(rfgain_5413); 7748c2ecf20Sopenharmony_ci break; 7758c2ecf20Sopenharmony_ci case AR5K_RF2317: 7768c2ecf20Sopenharmony_ci case AR5K_RF2425: 7778c2ecf20Sopenharmony_ci ath5k_rfg = rfgain_2425; 7788c2ecf20Sopenharmony_ci size = ARRAY_SIZE(rfgain_2425); 7798c2ecf20Sopenharmony_ci break; 7808c2ecf20Sopenharmony_ci default: 7818c2ecf20Sopenharmony_ci return -EINVAL; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci index = (band == NL80211_BAND_2GHZ) ? 1 : 0; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 7878c2ecf20Sopenharmony_ci AR5K_REG_WAIT(i); 7888c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, ath5k_rfg[i].rfg_value[index], 7898c2ecf20Sopenharmony_ci (u32)ath5k_rfg[i].rfg_register); 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci return 0; 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci/********************\ 7978c2ecf20Sopenharmony_ci* RF Registers setup * 7988c2ecf20Sopenharmony_ci\********************/ 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci/** 8018c2ecf20Sopenharmony_ci * ath5k_hw_rfregs_init() - Initialize RF register settings 8028c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 8038c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 8048c2ecf20Sopenharmony_ci * @mode: One of enum ath5k_driver_mode 8058c2ecf20Sopenharmony_ci * 8068c2ecf20Sopenharmony_ci * Setup RF registers by writing RF buffer on hw. For 8078c2ecf20Sopenharmony_ci * more infos on this, check out rfbuffer.h 8088c2ecf20Sopenharmony_ci */ 8098c2ecf20Sopenharmony_cistatic int 8108c2ecf20Sopenharmony_ciath5k_hw_rfregs_init(struct ath5k_hw *ah, 8118c2ecf20Sopenharmony_ci struct ieee80211_channel *channel, 8128c2ecf20Sopenharmony_ci unsigned int mode) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci const struct ath5k_rf_reg *rf_regs; 8158c2ecf20Sopenharmony_ci const struct ath5k_ini_rfbuffer *ini_rfb; 8168c2ecf20Sopenharmony_ci const struct ath5k_gain_opt *go = NULL; 8178c2ecf20Sopenharmony_ci const struct ath5k_gain_opt_step *g_step; 8188c2ecf20Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 8198c2ecf20Sopenharmony_ci u8 ee_mode = 0; 8208c2ecf20Sopenharmony_ci u32 *rfb; 8218c2ecf20Sopenharmony_ci int i, obdb = -1, bank = -1; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci switch (ah->ah_radio) { 8248c2ecf20Sopenharmony_ci case AR5K_RF5111: 8258c2ecf20Sopenharmony_ci rf_regs = rf_regs_5111; 8268c2ecf20Sopenharmony_ci ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5111); 8278c2ecf20Sopenharmony_ci ini_rfb = rfb_5111; 8288c2ecf20Sopenharmony_ci ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5111); 8298c2ecf20Sopenharmony_ci go = &rfgain_opt_5111; 8308c2ecf20Sopenharmony_ci break; 8318c2ecf20Sopenharmony_ci case AR5K_RF5112: 8328c2ecf20Sopenharmony_ci if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) { 8338c2ecf20Sopenharmony_ci rf_regs = rf_regs_5112a; 8348c2ecf20Sopenharmony_ci ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112a); 8358c2ecf20Sopenharmony_ci ini_rfb = rfb_5112a; 8368c2ecf20Sopenharmony_ci ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5112a); 8378c2ecf20Sopenharmony_ci } else { 8388c2ecf20Sopenharmony_ci rf_regs = rf_regs_5112; 8398c2ecf20Sopenharmony_ci ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112); 8408c2ecf20Sopenharmony_ci ini_rfb = rfb_5112; 8418c2ecf20Sopenharmony_ci ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5112); 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci go = &rfgain_opt_5112; 8448c2ecf20Sopenharmony_ci break; 8458c2ecf20Sopenharmony_ci case AR5K_RF2413: 8468c2ecf20Sopenharmony_ci rf_regs = rf_regs_2413; 8478c2ecf20Sopenharmony_ci ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2413); 8488c2ecf20Sopenharmony_ci ini_rfb = rfb_2413; 8498c2ecf20Sopenharmony_ci ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2413); 8508c2ecf20Sopenharmony_ci break; 8518c2ecf20Sopenharmony_ci case AR5K_RF2316: 8528c2ecf20Sopenharmony_ci rf_regs = rf_regs_2316; 8538c2ecf20Sopenharmony_ci ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2316); 8548c2ecf20Sopenharmony_ci ini_rfb = rfb_2316; 8558c2ecf20Sopenharmony_ci ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2316); 8568c2ecf20Sopenharmony_ci break; 8578c2ecf20Sopenharmony_ci case AR5K_RF5413: 8588c2ecf20Sopenharmony_ci rf_regs = rf_regs_5413; 8598c2ecf20Sopenharmony_ci ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5413); 8608c2ecf20Sopenharmony_ci ini_rfb = rfb_5413; 8618c2ecf20Sopenharmony_ci ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5413); 8628c2ecf20Sopenharmony_ci break; 8638c2ecf20Sopenharmony_ci case AR5K_RF2317: 8648c2ecf20Sopenharmony_ci rf_regs = rf_regs_2425; 8658c2ecf20Sopenharmony_ci ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2425); 8668c2ecf20Sopenharmony_ci ini_rfb = rfb_2317; 8678c2ecf20Sopenharmony_ci ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2317); 8688c2ecf20Sopenharmony_ci break; 8698c2ecf20Sopenharmony_ci case AR5K_RF2425: 8708c2ecf20Sopenharmony_ci rf_regs = rf_regs_2425; 8718c2ecf20Sopenharmony_ci ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2425); 8728c2ecf20Sopenharmony_ci if (ah->ah_mac_srev < AR5K_SREV_AR2417) { 8738c2ecf20Sopenharmony_ci ini_rfb = rfb_2425; 8748c2ecf20Sopenharmony_ci ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2425); 8758c2ecf20Sopenharmony_ci } else { 8768c2ecf20Sopenharmony_ci ini_rfb = rfb_2417; 8778c2ecf20Sopenharmony_ci ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2417); 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci break; 8808c2ecf20Sopenharmony_ci default: 8818c2ecf20Sopenharmony_ci return -EINVAL; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci /* If it's the first time we set RF buffer, allocate 8858c2ecf20Sopenharmony_ci * ah->ah_rf_banks based on ah->ah_rf_banks_size 8868c2ecf20Sopenharmony_ci * we set above */ 8878c2ecf20Sopenharmony_ci if (ah->ah_rf_banks == NULL) { 8888c2ecf20Sopenharmony_ci ah->ah_rf_banks = kmalloc_array(ah->ah_rf_banks_size, 8898c2ecf20Sopenharmony_ci sizeof(u32), 8908c2ecf20Sopenharmony_ci GFP_KERNEL); 8918c2ecf20Sopenharmony_ci if (ah->ah_rf_banks == NULL) { 8928c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "out of memory\n"); 8938c2ecf20Sopenharmony_ci return -ENOMEM; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci /* Copy values to modify them */ 8988c2ecf20Sopenharmony_ci rfb = ah->ah_rf_banks; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci for (i = 0; i < ah->ah_rf_banks_size; i++) { 9018c2ecf20Sopenharmony_ci if (ini_rfb[i].rfb_bank >= AR5K_MAX_RF_BANKS) { 9028c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "invalid bank\n"); 9038c2ecf20Sopenharmony_ci return -EINVAL; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci /* Bank changed, write down the offset */ 9078c2ecf20Sopenharmony_ci if (bank != ini_rfb[i].rfb_bank) { 9088c2ecf20Sopenharmony_ci bank = ini_rfb[i].rfb_bank; 9098c2ecf20Sopenharmony_ci ah->ah_offset[bank] = i; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci rfb[i] = ini_rfb[i].rfb_mode_data[mode]; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci /* Set Output and Driver bias current (OB/DB) */ 9168c2ecf20Sopenharmony_ci if (channel->band == NL80211_BAND_2GHZ) { 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (channel->hw_value == AR5K_MODE_11B) 9198c2ecf20Sopenharmony_ci ee_mode = AR5K_EEPROM_MODE_11B; 9208c2ecf20Sopenharmony_ci else 9218c2ecf20Sopenharmony_ci ee_mode = AR5K_EEPROM_MODE_11G; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci /* For RF511X/RF211X combination we 9248c2ecf20Sopenharmony_ci * use b_OB and b_DB parameters stored 9258c2ecf20Sopenharmony_ci * in eeprom on ee->ee_ob[ee_mode][0] 9268c2ecf20Sopenharmony_ci * 9278c2ecf20Sopenharmony_ci * For all other chips we use OB/DB for 2GHz 9288c2ecf20Sopenharmony_ci * stored in the b/g modal section just like 9298c2ecf20Sopenharmony_ci * 802.11a on ee->ee_ob[ee_mode][1] */ 9308c2ecf20Sopenharmony_ci if ((ah->ah_radio == AR5K_RF5111) || 9318c2ecf20Sopenharmony_ci (ah->ah_radio == AR5K_RF5112)) 9328c2ecf20Sopenharmony_ci obdb = 0; 9338c2ecf20Sopenharmony_ci else 9348c2ecf20Sopenharmony_ci obdb = 1; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, ee->ee_ob[ee_mode][obdb], 9378c2ecf20Sopenharmony_ci AR5K_RF_OB_2GHZ, true); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, ee->ee_db[ee_mode][obdb], 9408c2ecf20Sopenharmony_ci AR5K_RF_DB_2GHZ, true); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci /* RF5111 always needs OB/DB for 5GHz, even if we use 2GHz */ 9438c2ecf20Sopenharmony_ci } else if ((channel->band == NL80211_BAND_5GHZ) || 9448c2ecf20Sopenharmony_ci (ah->ah_radio == AR5K_RF5111)) { 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci /* For 11a, Turbo and XR we need to choose 9478c2ecf20Sopenharmony_ci * OB/DB based on frequency range */ 9488c2ecf20Sopenharmony_ci ee_mode = AR5K_EEPROM_MODE_11A; 9498c2ecf20Sopenharmony_ci obdb = channel->center_freq >= 5725 ? 3 : 9508c2ecf20Sopenharmony_ci (channel->center_freq >= 5500 ? 2 : 9518c2ecf20Sopenharmony_ci (channel->center_freq >= 5260 ? 1 : 9528c2ecf20Sopenharmony_ci (channel->center_freq > 4000 ? 0 : -1))); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci if (obdb < 0) 9558c2ecf20Sopenharmony_ci return -EINVAL; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, ee->ee_ob[ee_mode][obdb], 9588c2ecf20Sopenharmony_ci AR5K_RF_OB_5GHZ, true); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, ee->ee_db[ee_mode][obdb], 9618c2ecf20Sopenharmony_ci AR5K_RF_DB_5GHZ, true); 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci g_step = &go->go_step[ah->ah_gain.g_step_idx]; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci /* Set turbo mode (N/A on RF5413) */ 9678c2ecf20Sopenharmony_ci if ((ah->ah_bwmode == AR5K_BWMODE_40MHZ) && 9688c2ecf20Sopenharmony_ci (ah->ah_radio != AR5K_RF5413)) 9698c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_TURBO, false); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci /* Bank Modifications (chip-specific) */ 9728c2ecf20Sopenharmony_ci if (ah->ah_radio == AR5K_RF5111) { 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci /* Set gain_F settings according to current step */ 9758c2ecf20Sopenharmony_ci if (channel->hw_value != AR5K_MODE_11B) { 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_FRAME_CTL, 9788c2ecf20Sopenharmony_ci AR5K_PHY_FRAME_CTL_TX_CLIP, 9798c2ecf20Sopenharmony_ci g_step->gos_param[0]); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[1], 9828c2ecf20Sopenharmony_ci AR5K_RF_PWD_90, true); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[2], 9858c2ecf20Sopenharmony_ci AR5K_RF_PWD_84, true); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[3], 9888c2ecf20Sopenharmony_ci AR5K_RF_RFGAIN_SEL, true); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci /* We programmed gain_F parameters, switch back 9918c2ecf20Sopenharmony_ci * to active state */ 9928c2ecf20Sopenharmony_ci ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci /* Bank 6/7 setup */ 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, !ee->ee_xpd[ee_mode], 9998c2ecf20Sopenharmony_ci AR5K_RF_PWD_XPD, true); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, ee->ee_x_gain[ee_mode], 10028c2ecf20Sopenharmony_ci AR5K_RF_XPD_GAIN, true); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, ee->ee_i_gain[ee_mode], 10058c2ecf20Sopenharmony_ci AR5K_RF_GAIN_I, true); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, ee->ee_xpd[ee_mode], 10088c2ecf20Sopenharmony_ci AR5K_RF_PLO_SEL, true); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci /* Tweak power detectors for half/quarter rate support */ 10118c2ecf20Sopenharmony_ci if (ah->ah_bwmode == AR5K_BWMODE_5MHZ || 10128c2ecf20Sopenharmony_ci ah->ah_bwmode == AR5K_BWMODE_10MHZ) { 10138c2ecf20Sopenharmony_ci u8 wait_i; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 0x1f, 10168c2ecf20Sopenharmony_ci AR5K_RF_WAIT_S, true); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci wait_i = (ah->ah_bwmode == AR5K_BWMODE_5MHZ) ? 10198c2ecf20Sopenharmony_ci 0x1f : 0x10; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, wait_i, 10228c2ecf20Sopenharmony_ci AR5K_RF_WAIT_I, true); 10238c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 3, 10248c2ecf20Sopenharmony_ci AR5K_RF_MAX_TIME, true); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci if (ah->ah_radio == AR5K_RF5112) { 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci /* Set gain_F settings according to current step */ 10328c2ecf20Sopenharmony_ci if (channel->hw_value != AR5K_MODE_11B) { 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[0], 10358c2ecf20Sopenharmony_ci AR5K_RF_MIXGAIN_OVR, true); 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[1], 10388c2ecf20Sopenharmony_ci AR5K_RF_PWD_138, true); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[2], 10418c2ecf20Sopenharmony_ci AR5K_RF_PWD_137, true); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[3], 10448c2ecf20Sopenharmony_ci AR5K_RF_PWD_136, true); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[4], 10478c2ecf20Sopenharmony_ci AR5K_RF_PWD_132, true); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[5], 10508c2ecf20Sopenharmony_ci AR5K_RF_PWD_131, true); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[6], 10538c2ecf20Sopenharmony_ci AR5K_RF_PWD_130, true); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci /* We programmed gain_F parameters, switch back 10568c2ecf20Sopenharmony_ci * to active state */ 10578c2ecf20Sopenharmony_ci ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE; 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci /* Bank 6/7 setup */ 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, ee->ee_xpd[ee_mode], 10638c2ecf20Sopenharmony_ci AR5K_RF_XPD_SEL, true); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112A) { 10668c2ecf20Sopenharmony_ci /* Rev. 1 supports only one xpd */ 10678c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 10688c2ecf20Sopenharmony_ci ee->ee_x_gain[ee_mode], 10698c2ecf20Sopenharmony_ci AR5K_RF_XPD_GAIN, true); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci } else { 10728c2ecf20Sopenharmony_ci u8 *pdg_curve_to_idx = ee->ee_pdc_to_idx[ee_mode]; 10738c2ecf20Sopenharmony_ci if (ee->ee_pd_gains[ee_mode] > 1) { 10748c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 10758c2ecf20Sopenharmony_ci pdg_curve_to_idx[0], 10768c2ecf20Sopenharmony_ci AR5K_RF_PD_GAIN_LO, true); 10778c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 10788c2ecf20Sopenharmony_ci pdg_curve_to_idx[1], 10798c2ecf20Sopenharmony_ci AR5K_RF_PD_GAIN_HI, true); 10808c2ecf20Sopenharmony_ci } else { 10818c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 10828c2ecf20Sopenharmony_ci pdg_curve_to_idx[0], 10838c2ecf20Sopenharmony_ci AR5K_RF_PD_GAIN_LO, true); 10848c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 10858c2ecf20Sopenharmony_ci pdg_curve_to_idx[0], 10868c2ecf20Sopenharmony_ci AR5K_RF_PD_GAIN_HI, true); 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci /* Lower synth voltage on Rev 2 */ 10908c2ecf20Sopenharmony_ci if (ah->ah_radio == AR5K_RF5112 && 10918c2ecf20Sopenharmony_ci (ah->ah_radio_5ghz_revision & AR5K_SREV_REV) > 0) { 10928c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 2, 10938c2ecf20Sopenharmony_ci AR5K_RF_HIGH_VC_CP, true); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 2, 10968c2ecf20Sopenharmony_ci AR5K_RF_MID_VC_CP, true); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 2, 10998c2ecf20Sopenharmony_ci AR5K_RF_LOW_VC_CP, true); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 2, 11028c2ecf20Sopenharmony_ci AR5K_RF_PUSH_UP, true); 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci /* Decrease power consumption on 5213+ BaseBand */ 11068c2ecf20Sopenharmony_ci if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) { 11078c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 1, 11088c2ecf20Sopenharmony_ci AR5K_RF_PAD2GND, true); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 1, 11118c2ecf20Sopenharmony_ci AR5K_RF_XB2_LVL, true); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 1, 11148c2ecf20Sopenharmony_ci AR5K_RF_XB5_LVL, true); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 1, 11178c2ecf20Sopenharmony_ci AR5K_RF_PWD_167, true); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 1, 11208c2ecf20Sopenharmony_ci AR5K_RF_PWD_166, true); 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, ee->ee_i_gain[ee_mode], 11258c2ecf20Sopenharmony_ci AR5K_RF_GAIN_I, true); 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci /* Tweak power detector for half/quarter rates */ 11288c2ecf20Sopenharmony_ci if (ah->ah_bwmode == AR5K_BWMODE_5MHZ || 11298c2ecf20Sopenharmony_ci ah->ah_bwmode == AR5K_BWMODE_10MHZ) { 11308c2ecf20Sopenharmony_ci u8 pd_delay; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci pd_delay = (ah->ah_bwmode == AR5K_BWMODE_5MHZ) ? 11338c2ecf20Sopenharmony_ci 0xf : 0x8; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, pd_delay, 11368c2ecf20Sopenharmony_ci AR5K_RF_PD_PERIOD_A, true); 11378c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 0xf, 11388c2ecf20Sopenharmony_ci AR5K_RF_PD_DELAY_A, true); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci if (ah->ah_radio == AR5K_RF5413 && 11448c2ecf20Sopenharmony_ci channel->band == NL80211_BAND_2GHZ) { 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_DERBY_CHAN_SEL_MODE, 11478c2ecf20Sopenharmony_ci true); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci /* Set optimum value for early revisions (on pci-e chips) */ 11508c2ecf20Sopenharmony_ci if (ah->ah_mac_srev >= AR5K_SREV_AR5424 && 11518c2ecf20Sopenharmony_ci ah->ah_mac_srev < AR5K_SREV_AR5413) 11528c2ecf20Sopenharmony_ci ath5k_hw_rfb_op(ah, rf_regs, ath5k_hw_bitswap(6, 3), 11538c2ecf20Sopenharmony_ci AR5K_RF_PWD_ICLOBUF_2G, true); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci } 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci /* Write RF banks on hw */ 11588c2ecf20Sopenharmony_ci for (i = 0; i < ah->ah_rf_banks_size; i++) { 11598c2ecf20Sopenharmony_ci AR5K_REG_WAIT(i); 11608c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, rfb[i], ini_rfb[i].rfb_ctrl_register); 11618c2ecf20Sopenharmony_ci } 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci return 0; 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci/**************************\ 11688c2ecf20Sopenharmony_ci PHY/RF channel functions 11698c2ecf20Sopenharmony_ci\**************************/ 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci/** 11728c2ecf20Sopenharmony_ci * ath5k_hw_rf5110_chan2athchan() - Convert channel freq on RF5110 11738c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 11748c2ecf20Sopenharmony_ci * 11758c2ecf20Sopenharmony_ci * Map channel frequency to IEEE channel number and convert it 11768c2ecf20Sopenharmony_ci * to an internal channel value used by the RF5110 chipset. 11778c2ecf20Sopenharmony_ci */ 11788c2ecf20Sopenharmony_cistatic u32 11798c2ecf20Sopenharmony_ciath5k_hw_rf5110_chan2athchan(struct ieee80211_channel *channel) 11808c2ecf20Sopenharmony_ci{ 11818c2ecf20Sopenharmony_ci u32 athchan; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci athchan = (ath5k_hw_bitswap( 11848c2ecf20Sopenharmony_ci (ieee80211_frequency_to_channel( 11858c2ecf20Sopenharmony_ci channel->center_freq) - 24) / 2, 5) 11868c2ecf20Sopenharmony_ci << 1) | (1 << 6) | 0x1; 11878c2ecf20Sopenharmony_ci return athchan; 11888c2ecf20Sopenharmony_ci} 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci/** 11918c2ecf20Sopenharmony_ci * ath5k_hw_rf5110_channel() - Set channel frequency on RF5110 11928c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 11938c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 11948c2ecf20Sopenharmony_ci */ 11958c2ecf20Sopenharmony_cistatic int 11968c2ecf20Sopenharmony_ciath5k_hw_rf5110_channel(struct ath5k_hw *ah, 11978c2ecf20Sopenharmony_ci struct ieee80211_channel *channel) 11988c2ecf20Sopenharmony_ci{ 11998c2ecf20Sopenharmony_ci u32 data; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci /* 12028c2ecf20Sopenharmony_ci * Set the channel and wait 12038c2ecf20Sopenharmony_ci */ 12048c2ecf20Sopenharmony_ci data = ath5k_hw_rf5110_chan2athchan(channel); 12058c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, data, AR5K_RF_BUFFER); 12068c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_RF_BUFFER_CONTROL_0); 12078c2ecf20Sopenharmony_ci usleep_range(1000, 1500); 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci return 0; 12108c2ecf20Sopenharmony_ci} 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci/** 12138c2ecf20Sopenharmony_ci * ath5k_hw_rf5111_chan2athchan() - Handle 2GHz channels on RF5111/2111 12148c2ecf20Sopenharmony_ci * @ieee: IEEE channel number 12158c2ecf20Sopenharmony_ci * @athchan: The &struct ath5k_athchan_2ghz 12168c2ecf20Sopenharmony_ci * 12178c2ecf20Sopenharmony_ci * In order to enable the RF2111 frequency converter on RF5111/2111 setups 12188c2ecf20Sopenharmony_ci * we need to add some offsets and extra flags to the data values we pass 12198c2ecf20Sopenharmony_ci * on to the PHY. So for every 2GHz channel this function gets called 12208c2ecf20Sopenharmony_ci * to do the conversion. 12218c2ecf20Sopenharmony_ci */ 12228c2ecf20Sopenharmony_cistatic int 12238c2ecf20Sopenharmony_ciath5k_hw_rf5111_chan2athchan(unsigned int ieee, 12248c2ecf20Sopenharmony_ci struct ath5k_athchan_2ghz *athchan) 12258c2ecf20Sopenharmony_ci{ 12268c2ecf20Sopenharmony_ci int channel; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci /* Cast this value to catch negative channel numbers (>= -19) */ 12298c2ecf20Sopenharmony_ci channel = (int)ieee; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci /* 12328c2ecf20Sopenharmony_ci * Map 2GHz IEEE channel to 5GHz Atheros channel 12338c2ecf20Sopenharmony_ci */ 12348c2ecf20Sopenharmony_ci if (channel <= 13) { 12358c2ecf20Sopenharmony_ci athchan->a2_athchan = 115 + channel; 12368c2ecf20Sopenharmony_ci athchan->a2_flags = 0x46; 12378c2ecf20Sopenharmony_ci } else if (channel == 14) { 12388c2ecf20Sopenharmony_ci athchan->a2_athchan = 124; 12398c2ecf20Sopenharmony_ci athchan->a2_flags = 0x44; 12408c2ecf20Sopenharmony_ci } else if (channel >= 15 && channel <= 26) { 12418c2ecf20Sopenharmony_ci athchan->a2_athchan = ((channel - 14) * 4) + 132; 12428c2ecf20Sopenharmony_ci athchan->a2_flags = 0x46; 12438c2ecf20Sopenharmony_ci } else 12448c2ecf20Sopenharmony_ci return -EINVAL; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci return 0; 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci/** 12508c2ecf20Sopenharmony_ci * ath5k_hw_rf5111_channel() - Set channel frequency on RF5111/2111 12518c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 12528c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 12538c2ecf20Sopenharmony_ci */ 12548c2ecf20Sopenharmony_cistatic int 12558c2ecf20Sopenharmony_ciath5k_hw_rf5111_channel(struct ath5k_hw *ah, 12568c2ecf20Sopenharmony_ci struct ieee80211_channel *channel) 12578c2ecf20Sopenharmony_ci{ 12588c2ecf20Sopenharmony_ci struct ath5k_athchan_2ghz ath5k_channel_2ghz; 12598c2ecf20Sopenharmony_ci unsigned int ath5k_channel = 12608c2ecf20Sopenharmony_ci ieee80211_frequency_to_channel(channel->center_freq); 12618c2ecf20Sopenharmony_ci u32 data0, data1, clock; 12628c2ecf20Sopenharmony_ci int ret; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci /* 12658c2ecf20Sopenharmony_ci * Set the channel on the RF5111 radio 12668c2ecf20Sopenharmony_ci */ 12678c2ecf20Sopenharmony_ci data0 = data1 = 0; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci if (channel->band == NL80211_BAND_2GHZ) { 12708c2ecf20Sopenharmony_ci /* Map 2GHz channel to 5GHz Atheros channel ID */ 12718c2ecf20Sopenharmony_ci ret = ath5k_hw_rf5111_chan2athchan( 12728c2ecf20Sopenharmony_ci ieee80211_frequency_to_channel(channel->center_freq), 12738c2ecf20Sopenharmony_ci &ath5k_channel_2ghz); 12748c2ecf20Sopenharmony_ci if (ret) 12758c2ecf20Sopenharmony_ci return ret; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci ath5k_channel = ath5k_channel_2ghz.a2_athchan; 12788c2ecf20Sopenharmony_ci data0 = ((ath5k_hw_bitswap(ath5k_channel_2ghz.a2_flags, 8) & 0xff) 12798c2ecf20Sopenharmony_ci << 5) | (1 << 4); 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci if (ath5k_channel < 145 || !(ath5k_channel & 1)) { 12838c2ecf20Sopenharmony_ci clock = 1; 12848c2ecf20Sopenharmony_ci data1 = ((ath5k_hw_bitswap(ath5k_channel - 24, 8) & 0xff) << 2) | 12858c2ecf20Sopenharmony_ci (clock << 1) | (1 << 10) | 1; 12868c2ecf20Sopenharmony_ci } else { 12878c2ecf20Sopenharmony_ci clock = 0; 12888c2ecf20Sopenharmony_ci data1 = ((ath5k_hw_bitswap((ath5k_channel - 24) / 2, 8) & 0xff) 12898c2ecf20Sopenharmony_ci << 2) | (clock << 1) | (1 << 10) | 1; 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, (data1 & 0xff) | ((data0 & 0xff) << 8), 12938c2ecf20Sopenharmony_ci AR5K_RF_BUFFER); 12948c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, ((data1 >> 8) & 0xff) | (data0 & 0xff00), 12958c2ecf20Sopenharmony_ci AR5K_RF_BUFFER_CONTROL_3); 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci return 0; 12988c2ecf20Sopenharmony_ci} 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci/** 13018c2ecf20Sopenharmony_ci * ath5k_hw_rf5112_channel() - Set channel frequency on 5112 and newer 13028c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 13038c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 13048c2ecf20Sopenharmony_ci * 13058c2ecf20Sopenharmony_ci * On RF5112/2112 and newer we don't need to do any conversion. 13068c2ecf20Sopenharmony_ci * We pass the frequency value after a few modifications to the 13078c2ecf20Sopenharmony_ci * chip directly. 13088c2ecf20Sopenharmony_ci * 13098c2ecf20Sopenharmony_ci * NOTE: Make sure channel frequency given is within our range or else 13108c2ecf20Sopenharmony_ci * we might damage the chip ! Use ath5k_channel_ok before calling this one. 13118c2ecf20Sopenharmony_ci */ 13128c2ecf20Sopenharmony_cistatic int 13138c2ecf20Sopenharmony_ciath5k_hw_rf5112_channel(struct ath5k_hw *ah, 13148c2ecf20Sopenharmony_ci struct ieee80211_channel *channel) 13158c2ecf20Sopenharmony_ci{ 13168c2ecf20Sopenharmony_ci u32 data, data0, data1, data2; 13178c2ecf20Sopenharmony_ci u16 c; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci data = data0 = data1 = data2 = 0; 13208c2ecf20Sopenharmony_ci c = channel->center_freq; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci /* My guess based on code: 13238c2ecf20Sopenharmony_ci * 2GHz RF has 2 synth modes, one with a Local Oscillator 13248c2ecf20Sopenharmony_ci * at 2224Hz and one with a LO at 2192Hz. IF is 1520Hz 13258c2ecf20Sopenharmony_ci * (3040/2). data0 is used to set the PLL divider and data1 13268c2ecf20Sopenharmony_ci * selects synth mode. */ 13278c2ecf20Sopenharmony_ci if (c < 4800) { 13288c2ecf20Sopenharmony_ci /* Channel 14 and all frequencies with 2Hz spacing 13298c2ecf20Sopenharmony_ci * below/above (non-standard channels) */ 13308c2ecf20Sopenharmony_ci if (!((c - 2224) % 5)) { 13318c2ecf20Sopenharmony_ci /* Same as (c - 2224) / 5 */ 13328c2ecf20Sopenharmony_ci data0 = ((2 * (c - 704)) - 3040) / 10; 13338c2ecf20Sopenharmony_ci data1 = 1; 13348c2ecf20Sopenharmony_ci /* Channel 1 and all frequencies with 5Hz spacing 13358c2ecf20Sopenharmony_ci * below/above (standard channels without channel 14) */ 13368c2ecf20Sopenharmony_ci } else if (!((c - 2192) % 5)) { 13378c2ecf20Sopenharmony_ci /* Same as (c - 2192) / 5 */ 13388c2ecf20Sopenharmony_ci data0 = ((2 * (c - 672)) - 3040) / 10; 13398c2ecf20Sopenharmony_ci data1 = 0; 13408c2ecf20Sopenharmony_ci } else 13418c2ecf20Sopenharmony_ci return -EINVAL; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci data0 = ath5k_hw_bitswap((data0 << 2) & 0xff, 8); 13448c2ecf20Sopenharmony_ci /* This is more complex, we have a single synthesizer with 13458c2ecf20Sopenharmony_ci * 4 reference clock settings (?) based on frequency spacing 13468c2ecf20Sopenharmony_ci * and set using data2. LO is at 4800Hz and data0 is again used 13478c2ecf20Sopenharmony_ci * to set some divider. 13488c2ecf20Sopenharmony_ci * 13498c2ecf20Sopenharmony_ci * NOTE: There is an old atheros presentation at Stanford 13508c2ecf20Sopenharmony_ci * that mentions a method called dual direct conversion 13518c2ecf20Sopenharmony_ci * with 1GHz sliding IF for RF5110. Maybe that's what we 13528c2ecf20Sopenharmony_ci * have here, or an updated version. */ 13538c2ecf20Sopenharmony_ci } else if ((c % 5) != 2 || c > 5435) { 13548c2ecf20Sopenharmony_ci if (!(c % 20) && c >= 5120) { 13558c2ecf20Sopenharmony_ci data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8); 13568c2ecf20Sopenharmony_ci data2 = ath5k_hw_bitswap(3, 2); 13578c2ecf20Sopenharmony_ci } else if (!(c % 10)) { 13588c2ecf20Sopenharmony_ci data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8); 13598c2ecf20Sopenharmony_ci data2 = ath5k_hw_bitswap(2, 2); 13608c2ecf20Sopenharmony_ci } else if (!(c % 5)) { 13618c2ecf20Sopenharmony_ci data0 = ath5k_hw_bitswap((c - 4800) / 5, 8); 13628c2ecf20Sopenharmony_ci data2 = ath5k_hw_bitswap(1, 2); 13638c2ecf20Sopenharmony_ci } else 13648c2ecf20Sopenharmony_ci return -EINVAL; 13658c2ecf20Sopenharmony_ci } else { 13668c2ecf20Sopenharmony_ci data0 = ath5k_hw_bitswap((10 * (c - 2 - 4800)) / 25 + 1, 8); 13678c2ecf20Sopenharmony_ci data2 = ath5k_hw_bitswap(0, 2); 13688c2ecf20Sopenharmony_ci } 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci data = (data0 << 4) | (data1 << 1) | (data2 << 2) | 0x1001; 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, data & 0xff, AR5K_RF_BUFFER); 13738c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5); 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci return 0; 13768c2ecf20Sopenharmony_ci} 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci/** 13798c2ecf20Sopenharmony_ci * ath5k_hw_rf2425_channel() - Set channel frequency on RF2425 13808c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 13818c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 13828c2ecf20Sopenharmony_ci * 13838c2ecf20Sopenharmony_ci * AR2425/2417 have a different 2GHz RF so code changes 13848c2ecf20Sopenharmony_ci * a little bit from RF5112. 13858c2ecf20Sopenharmony_ci */ 13868c2ecf20Sopenharmony_cistatic int 13878c2ecf20Sopenharmony_ciath5k_hw_rf2425_channel(struct ath5k_hw *ah, 13888c2ecf20Sopenharmony_ci struct ieee80211_channel *channel) 13898c2ecf20Sopenharmony_ci{ 13908c2ecf20Sopenharmony_ci u32 data, data0, data2; 13918c2ecf20Sopenharmony_ci u16 c; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci data = data0 = data2 = 0; 13948c2ecf20Sopenharmony_ci c = channel->center_freq; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci if (c < 4800) { 13978c2ecf20Sopenharmony_ci data0 = ath5k_hw_bitswap((c - 2272), 8); 13988c2ecf20Sopenharmony_ci data2 = 0; 13998c2ecf20Sopenharmony_ci /* ? 5GHz ? */ 14008c2ecf20Sopenharmony_ci } else if ((c % 5) != 2 || c > 5435) { 14018c2ecf20Sopenharmony_ci if (!(c % 20) && c < 5120) 14028c2ecf20Sopenharmony_ci data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8); 14038c2ecf20Sopenharmony_ci else if (!(c % 10)) 14048c2ecf20Sopenharmony_ci data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8); 14058c2ecf20Sopenharmony_ci else if (!(c % 5)) 14068c2ecf20Sopenharmony_ci data0 = ath5k_hw_bitswap((c - 4800) / 5, 8); 14078c2ecf20Sopenharmony_ci else 14088c2ecf20Sopenharmony_ci return -EINVAL; 14098c2ecf20Sopenharmony_ci data2 = ath5k_hw_bitswap(1, 2); 14108c2ecf20Sopenharmony_ci } else { 14118c2ecf20Sopenharmony_ci data0 = ath5k_hw_bitswap((10 * (c - 2 - 4800)) / 25 + 1, 8); 14128c2ecf20Sopenharmony_ci data2 = ath5k_hw_bitswap(0, 2); 14138c2ecf20Sopenharmony_ci } 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci data = (data0 << 4) | data2 << 2 | 0x1001; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, data & 0xff, AR5K_RF_BUFFER); 14188c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5); 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci return 0; 14218c2ecf20Sopenharmony_ci} 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci/** 14248c2ecf20Sopenharmony_ci * ath5k_hw_channel() - Set a channel on the radio chip 14258c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 14268c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 14278c2ecf20Sopenharmony_ci * 14288c2ecf20Sopenharmony_ci * This is the main function called to set a channel on the 14298c2ecf20Sopenharmony_ci * radio chip based on the radio chip version. 14308c2ecf20Sopenharmony_ci */ 14318c2ecf20Sopenharmony_cistatic int 14328c2ecf20Sopenharmony_ciath5k_hw_channel(struct ath5k_hw *ah, 14338c2ecf20Sopenharmony_ci struct ieee80211_channel *channel) 14348c2ecf20Sopenharmony_ci{ 14358c2ecf20Sopenharmony_ci int ret; 14368c2ecf20Sopenharmony_ci /* 14378c2ecf20Sopenharmony_ci * Check bounds supported by the PHY (we don't care about regulatory 14388c2ecf20Sopenharmony_ci * restrictions at this point). 14398c2ecf20Sopenharmony_ci */ 14408c2ecf20Sopenharmony_ci if (!ath5k_channel_ok(ah, channel)) { 14418c2ecf20Sopenharmony_ci ATH5K_ERR(ah, 14428c2ecf20Sopenharmony_ci "channel frequency (%u MHz) out of supported " 14438c2ecf20Sopenharmony_ci "band range\n", 14448c2ecf20Sopenharmony_ci channel->center_freq); 14458c2ecf20Sopenharmony_ci return -EINVAL; 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci /* 14498c2ecf20Sopenharmony_ci * Set the channel and wait 14508c2ecf20Sopenharmony_ci */ 14518c2ecf20Sopenharmony_ci switch (ah->ah_radio) { 14528c2ecf20Sopenharmony_ci case AR5K_RF5110: 14538c2ecf20Sopenharmony_ci ret = ath5k_hw_rf5110_channel(ah, channel); 14548c2ecf20Sopenharmony_ci break; 14558c2ecf20Sopenharmony_ci case AR5K_RF5111: 14568c2ecf20Sopenharmony_ci ret = ath5k_hw_rf5111_channel(ah, channel); 14578c2ecf20Sopenharmony_ci break; 14588c2ecf20Sopenharmony_ci case AR5K_RF2317: 14598c2ecf20Sopenharmony_ci case AR5K_RF2425: 14608c2ecf20Sopenharmony_ci ret = ath5k_hw_rf2425_channel(ah, channel); 14618c2ecf20Sopenharmony_ci break; 14628c2ecf20Sopenharmony_ci default: 14638c2ecf20Sopenharmony_ci ret = ath5k_hw_rf5112_channel(ah, channel); 14648c2ecf20Sopenharmony_ci break; 14658c2ecf20Sopenharmony_ci } 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci if (ret) 14688c2ecf20Sopenharmony_ci return ret; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci /* Set JAPAN setting for channel 14 */ 14718c2ecf20Sopenharmony_ci if (channel->center_freq == 2484) { 14728c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL, 14738c2ecf20Sopenharmony_ci AR5K_PHY_CCKTXCTL_JAPAN); 14748c2ecf20Sopenharmony_ci } else { 14758c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL, 14768c2ecf20Sopenharmony_ci AR5K_PHY_CCKTXCTL_WORLD); 14778c2ecf20Sopenharmony_ci } 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci ah->ah_current_channel = channel; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci return 0; 14828c2ecf20Sopenharmony_ci} 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci/*****************\ 14868c2ecf20Sopenharmony_ci PHY calibration 14878c2ecf20Sopenharmony_ci\*****************/ 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci/** 14908c2ecf20Sopenharmony_ci * DOC: PHY Calibration routines 14918c2ecf20Sopenharmony_ci * 14928c2ecf20Sopenharmony_ci * Noise floor calibration: When we tell the hardware to 14938c2ecf20Sopenharmony_ci * perform a noise floor calibration by setting the 14948c2ecf20Sopenharmony_ci * AR5K_PHY_AGCCTL_NF bit on AR5K_PHY_AGCCTL, it will periodically 14958c2ecf20Sopenharmony_ci * sample-and-hold the minimum noise level seen at the antennas. 14968c2ecf20Sopenharmony_ci * This value is then stored in a ring buffer of recently measured 14978c2ecf20Sopenharmony_ci * noise floor values so we have a moving window of the last few 14988c2ecf20Sopenharmony_ci * samples. The median of the values in the history is then loaded 14998c2ecf20Sopenharmony_ci * into the hardware for its own use for RSSI and CCA measurements. 15008c2ecf20Sopenharmony_ci * This type of calibration doesn't interfere with traffic. 15018c2ecf20Sopenharmony_ci * 15028c2ecf20Sopenharmony_ci * AGC calibration: When we tell the hardware to perform 15038c2ecf20Sopenharmony_ci * an AGC (Automatic Gain Control) calibration by setting the 15048c2ecf20Sopenharmony_ci * AR5K_PHY_AGCCTL_CAL, hw disconnects the antennas and does 15058c2ecf20Sopenharmony_ci * a calibration on the DC offsets of ADCs. During this period 15068c2ecf20Sopenharmony_ci * rx/tx gets disabled so we have to deal with it on the driver 15078c2ecf20Sopenharmony_ci * part. 15088c2ecf20Sopenharmony_ci * 15098c2ecf20Sopenharmony_ci * I/Q calibration: When we tell the hardware to perform 15108c2ecf20Sopenharmony_ci * an I/Q calibration, it tries to correct I/Q imbalance and 15118c2ecf20Sopenharmony_ci * fix QAM constellation by sampling data from rxed frames. 15128c2ecf20Sopenharmony_ci * It doesn't interfere with traffic. 15138c2ecf20Sopenharmony_ci * 15148c2ecf20Sopenharmony_ci * For more infos on AGC and I/Q calibration check out patent doc 15158c2ecf20Sopenharmony_ci * #03/094463. 15168c2ecf20Sopenharmony_ci */ 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci/** 15198c2ecf20Sopenharmony_ci * ath5k_hw_read_measured_noise_floor() - Read measured NF from hw 15208c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 15218c2ecf20Sopenharmony_ci */ 15228c2ecf20Sopenharmony_cistatic s32 15238c2ecf20Sopenharmony_ciath5k_hw_read_measured_noise_floor(struct ath5k_hw *ah) 15248c2ecf20Sopenharmony_ci{ 15258c2ecf20Sopenharmony_ci s32 val; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci val = ath5k_hw_reg_read(ah, AR5K_PHY_NF); 15288c2ecf20Sopenharmony_ci return sign_extend32(AR5K_REG_MS(val, AR5K_PHY_NF_MINCCA_PWR), 8); 15298c2ecf20Sopenharmony_ci} 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci/** 15328c2ecf20Sopenharmony_ci * ath5k_hw_init_nfcal_hist() - Initialize NF calibration history buffer 15338c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 15348c2ecf20Sopenharmony_ci */ 15358c2ecf20Sopenharmony_civoid 15368c2ecf20Sopenharmony_ciath5k_hw_init_nfcal_hist(struct ath5k_hw *ah) 15378c2ecf20Sopenharmony_ci{ 15388c2ecf20Sopenharmony_ci int i; 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci ah->ah_nfcal_hist.index = 0; 15418c2ecf20Sopenharmony_ci for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++) 15428c2ecf20Sopenharmony_ci ah->ah_nfcal_hist.nfval[i] = AR5K_TUNE_CCA_MAX_GOOD_VALUE; 15438c2ecf20Sopenharmony_ci} 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci/** 15468c2ecf20Sopenharmony_ci * ath5k_hw_update_nfcal_hist() - Update NF calibration history buffer 15478c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 15488c2ecf20Sopenharmony_ci * @noise_floor: The NF we got from hw 15498c2ecf20Sopenharmony_ci */ 15508c2ecf20Sopenharmony_cistatic void ath5k_hw_update_nfcal_hist(struct ath5k_hw *ah, s16 noise_floor) 15518c2ecf20Sopenharmony_ci{ 15528c2ecf20Sopenharmony_ci struct ath5k_nfcal_hist *hist = &ah->ah_nfcal_hist; 15538c2ecf20Sopenharmony_ci hist->index = (hist->index + 1) & (ATH5K_NF_CAL_HIST_MAX - 1); 15548c2ecf20Sopenharmony_ci hist->nfval[hist->index] = noise_floor; 15558c2ecf20Sopenharmony_ci} 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci/** 15588c2ecf20Sopenharmony_ci * ath5k_hw_get_median_noise_floor() - Get median NF from history buffer 15598c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 15608c2ecf20Sopenharmony_ci */ 15618c2ecf20Sopenharmony_cistatic s16 15628c2ecf20Sopenharmony_ciath5k_hw_get_median_noise_floor(struct ath5k_hw *ah) 15638c2ecf20Sopenharmony_ci{ 15648c2ecf20Sopenharmony_ci s16 sort[ATH5K_NF_CAL_HIST_MAX]; 15658c2ecf20Sopenharmony_ci s16 tmp; 15668c2ecf20Sopenharmony_ci int i, j; 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci memcpy(sort, ah->ah_nfcal_hist.nfval, sizeof(sort)); 15698c2ecf20Sopenharmony_ci for (i = 0; i < ATH5K_NF_CAL_HIST_MAX - 1; i++) { 15708c2ecf20Sopenharmony_ci for (j = 1; j < ATH5K_NF_CAL_HIST_MAX - i; j++) { 15718c2ecf20Sopenharmony_ci if (sort[j] > sort[j - 1]) { 15728c2ecf20Sopenharmony_ci tmp = sort[j]; 15738c2ecf20Sopenharmony_ci sort[j] = sort[j - 1]; 15748c2ecf20Sopenharmony_ci sort[j - 1] = tmp; 15758c2ecf20Sopenharmony_ci } 15768c2ecf20Sopenharmony_ci } 15778c2ecf20Sopenharmony_ci } 15788c2ecf20Sopenharmony_ci for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++) { 15798c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, 15808c2ecf20Sopenharmony_ci "cal %d:%d\n", i, sort[i]); 15818c2ecf20Sopenharmony_ci } 15828c2ecf20Sopenharmony_ci return sort[(ATH5K_NF_CAL_HIST_MAX - 1) / 2]; 15838c2ecf20Sopenharmony_ci} 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci/** 15868c2ecf20Sopenharmony_ci * ath5k_hw_update_noise_floor() - Update NF on hardware 15878c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 15888c2ecf20Sopenharmony_ci * 15898c2ecf20Sopenharmony_ci * This is the main function we call to perform a NF calibration, 15908c2ecf20Sopenharmony_ci * it reads NF from hardware, calculates the median and updates 15918c2ecf20Sopenharmony_ci * NF on hw. 15928c2ecf20Sopenharmony_ci */ 15938c2ecf20Sopenharmony_civoid 15948c2ecf20Sopenharmony_ciath5k_hw_update_noise_floor(struct ath5k_hw *ah) 15958c2ecf20Sopenharmony_ci{ 15968c2ecf20Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 15978c2ecf20Sopenharmony_ci u32 val; 15988c2ecf20Sopenharmony_ci s16 nf, threshold; 15998c2ecf20Sopenharmony_ci u8 ee_mode; 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci /* keep last value if calibration hasn't completed */ 16028c2ecf20Sopenharmony_ci if (ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL) & AR5K_PHY_AGCCTL_NF) { 16038c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, 16048c2ecf20Sopenharmony_ci "NF did not complete in calibration window\n"); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci return; 16078c2ecf20Sopenharmony_ci } 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci ah->ah_cal_mask |= AR5K_CALIBRATION_NF; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci ee_mode = ath5k_eeprom_mode_from_channel(ah, ah->ah_current_channel); 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci /* completed NF calibration, test threshold */ 16148c2ecf20Sopenharmony_ci nf = ath5k_hw_read_measured_noise_floor(ah); 16158c2ecf20Sopenharmony_ci threshold = ee->ee_noise_floor_thr[ee_mode]; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci if (nf > threshold) { 16188c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, 16198c2ecf20Sopenharmony_ci "noise floor failure detected; " 16208c2ecf20Sopenharmony_ci "read %d, threshold %d\n", 16218c2ecf20Sopenharmony_ci nf, threshold); 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci nf = AR5K_TUNE_CCA_MAX_GOOD_VALUE; 16248c2ecf20Sopenharmony_ci } 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci ath5k_hw_update_nfcal_hist(ah, nf); 16278c2ecf20Sopenharmony_ci nf = ath5k_hw_get_median_noise_floor(ah); 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci /* load noise floor (in .5 dBm) so the hardware will use it */ 16308c2ecf20Sopenharmony_ci val = ath5k_hw_reg_read(ah, AR5K_PHY_NF) & ~AR5K_PHY_NF_M; 16318c2ecf20Sopenharmony_ci val |= (nf * 2) & AR5K_PHY_NF_M; 16328c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, val, AR5K_PHY_NF); 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci AR5K_REG_MASKED_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF, 16358c2ecf20Sopenharmony_ci ~(AR5K_PHY_AGCCTL_NF_EN | AR5K_PHY_AGCCTL_NF_NOUPDATE)); 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF, 16388c2ecf20Sopenharmony_ci 0, false); 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci /* 16418c2ecf20Sopenharmony_ci * Load a high max CCA Power value (-50 dBm in .5 dBm units) 16428c2ecf20Sopenharmony_ci * so that we're not capped by the median we just loaded. 16438c2ecf20Sopenharmony_ci * This will be used as the initial value for the next noise 16448c2ecf20Sopenharmony_ci * floor calibration. 16458c2ecf20Sopenharmony_ci */ 16468c2ecf20Sopenharmony_ci val = (val & ~AR5K_PHY_NF_M) | ((-50 * 2) & AR5K_PHY_NF_M); 16478c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, val, AR5K_PHY_NF); 16488c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, 16498c2ecf20Sopenharmony_ci AR5K_PHY_AGCCTL_NF_EN | 16508c2ecf20Sopenharmony_ci AR5K_PHY_AGCCTL_NF_NOUPDATE | 16518c2ecf20Sopenharmony_ci AR5K_PHY_AGCCTL_NF); 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci ah->ah_noise_floor = nf; 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci ah->ah_cal_mask &= ~AR5K_CALIBRATION_NF; 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, 16588c2ecf20Sopenharmony_ci "noise floor calibrated: %d\n", nf); 16598c2ecf20Sopenharmony_ci} 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci/** 16628c2ecf20Sopenharmony_ci * ath5k_hw_rf5110_calibrate() - Perform a PHY calibration on RF5110 16638c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 16648c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 16658c2ecf20Sopenharmony_ci * 16668c2ecf20Sopenharmony_ci * Do a complete PHY calibration (AGC + NF + I/Q) on RF5110 16678c2ecf20Sopenharmony_ci */ 16688c2ecf20Sopenharmony_cistatic int 16698c2ecf20Sopenharmony_ciath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, 16708c2ecf20Sopenharmony_ci struct ieee80211_channel *channel) 16718c2ecf20Sopenharmony_ci{ 16728c2ecf20Sopenharmony_ci u32 phy_sig, phy_agc, phy_sat, beacon; 16738c2ecf20Sopenharmony_ci int ret; 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci if (!(ah->ah_cal_mask & AR5K_CALIBRATION_FULL)) 16768c2ecf20Sopenharmony_ci return 0; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci /* 16798c2ecf20Sopenharmony_ci * Disable beacons and RX/TX queues, wait 16808c2ecf20Sopenharmony_ci */ 16818c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5210, 16828c2ecf20Sopenharmony_ci AR5K_DIAG_SW_DIS_TX_5210 | AR5K_DIAG_SW_DIS_RX_5210); 16838c2ecf20Sopenharmony_ci beacon = ath5k_hw_reg_read(ah, AR5K_BEACON_5210); 16848c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, beacon & ~AR5K_BEACON_ENABLE, AR5K_BEACON_5210); 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci usleep_range(2000, 2500); 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci /* 16898c2ecf20Sopenharmony_ci * Set the channel (with AGC turned off) 16908c2ecf20Sopenharmony_ci */ 16918c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); 16928c2ecf20Sopenharmony_ci udelay(10); 16938c2ecf20Sopenharmony_ci ret = ath5k_hw_channel(ah, channel); 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci /* 16968c2ecf20Sopenharmony_ci * Activate PHY and wait 16978c2ecf20Sopenharmony_ci */ 16988c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT); 16998c2ecf20Sopenharmony_ci usleep_range(1000, 1500); 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci if (ret) 17048c2ecf20Sopenharmony_ci return ret; 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci /* 17078c2ecf20Sopenharmony_ci * Calibrate the radio chip 17088c2ecf20Sopenharmony_ci */ 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci /* Remember normal state */ 17118c2ecf20Sopenharmony_ci phy_sig = ath5k_hw_reg_read(ah, AR5K_PHY_SIG); 17128c2ecf20Sopenharmony_ci phy_agc = ath5k_hw_reg_read(ah, AR5K_PHY_AGCCOARSE); 17138c2ecf20Sopenharmony_ci phy_sat = ath5k_hw_reg_read(ah, AR5K_PHY_ADCSAT); 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci /* Update radio registers */ 17168c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, (phy_sig & ~(AR5K_PHY_SIG_FIRPWR)) | 17178c2ecf20Sopenharmony_ci AR5K_REG_SM(-1, AR5K_PHY_SIG_FIRPWR), AR5K_PHY_SIG); 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, (phy_agc & ~(AR5K_PHY_AGCCOARSE_HI | 17208c2ecf20Sopenharmony_ci AR5K_PHY_AGCCOARSE_LO)) | 17218c2ecf20Sopenharmony_ci AR5K_REG_SM(-1, AR5K_PHY_AGCCOARSE_HI) | 17228c2ecf20Sopenharmony_ci AR5K_REG_SM(-127, AR5K_PHY_AGCCOARSE_LO), AR5K_PHY_AGCCOARSE); 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, (phy_sat & ~(AR5K_PHY_ADCSAT_ICNT | 17258c2ecf20Sopenharmony_ci AR5K_PHY_ADCSAT_THR)) | 17268c2ecf20Sopenharmony_ci AR5K_REG_SM(2, AR5K_PHY_ADCSAT_ICNT) | 17278c2ecf20Sopenharmony_ci AR5K_REG_SM(12, AR5K_PHY_ADCSAT_THR), AR5K_PHY_ADCSAT); 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci udelay(20); 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); 17328c2ecf20Sopenharmony_ci udelay(10); 17338c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_PHY_RFSTG_DISABLE, AR5K_PHY_RFSTG); 17348c2ecf20Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci usleep_range(1000, 1500); 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci /* 17398c2ecf20Sopenharmony_ci * Enable calibration and wait until completion 17408c2ecf20Sopenharmony_ci */ 17418c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_CAL); 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, 17448c2ecf20Sopenharmony_ci AR5K_PHY_AGCCTL_CAL, 0, false); 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci /* Reset to normal state */ 17478c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, phy_sig, AR5K_PHY_SIG); 17488c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, phy_agc, AR5K_PHY_AGCCOARSE); 17498c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, phy_sat, AR5K_PHY_ADCSAT); 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci if (ret) { 17528c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "calibration timeout (%uMHz)\n", 17538c2ecf20Sopenharmony_ci channel->center_freq); 17548c2ecf20Sopenharmony_ci return ret; 17558c2ecf20Sopenharmony_ci } 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci /* 17588c2ecf20Sopenharmony_ci * Re-enable RX/TX and beacons 17598c2ecf20Sopenharmony_ci */ 17608c2ecf20Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5210, 17618c2ecf20Sopenharmony_ci AR5K_DIAG_SW_DIS_TX_5210 | AR5K_DIAG_SW_DIS_RX_5210); 17628c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, beacon, AR5K_BEACON_5210); 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci return 0; 17658c2ecf20Sopenharmony_ci} 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci/** 17688c2ecf20Sopenharmony_ci * ath5k_hw_rf511x_iq_calibrate() - Perform I/Q calibration on RF5111 and newer 17698c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 17708c2ecf20Sopenharmony_ci */ 17718c2ecf20Sopenharmony_cistatic int 17728c2ecf20Sopenharmony_ciath5k_hw_rf511x_iq_calibrate(struct ath5k_hw *ah) 17738c2ecf20Sopenharmony_ci{ 17748c2ecf20Sopenharmony_ci u32 i_pwr, q_pwr; 17758c2ecf20Sopenharmony_ci s32 iq_corr, i_coff, i_coffd, q_coff, q_coffd; 17768c2ecf20Sopenharmony_ci int i; 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci /* Skip if I/Q calibration is not needed or if it's still running */ 17798c2ecf20Sopenharmony_ci if (!ah->ah_iq_cal_needed) 17808c2ecf20Sopenharmony_ci return -EINVAL; 17818c2ecf20Sopenharmony_ci else if (ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & AR5K_PHY_IQ_RUN) { 17828c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE, 17838c2ecf20Sopenharmony_ci "I/Q calibration still running"); 17848c2ecf20Sopenharmony_ci return -EBUSY; 17858c2ecf20Sopenharmony_ci } 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci /* Calibration has finished, get the results and re-run */ 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci /* Work around for empty results which can apparently happen on 5212: 17908c2ecf20Sopenharmony_ci * Read registers up to 10 times until we get both i_pr and q_pwr */ 17918c2ecf20Sopenharmony_ci for (i = 0; i <= 10; i++) { 17928c2ecf20Sopenharmony_ci iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR); 17938c2ecf20Sopenharmony_ci i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I); 17948c2ecf20Sopenharmony_ci q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q); 17958c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE, 17968c2ecf20Sopenharmony_ci "iq_corr:%x i_pwr:%x q_pwr:%x", iq_corr, i_pwr, q_pwr); 17978c2ecf20Sopenharmony_ci if (i_pwr && q_pwr) 17988c2ecf20Sopenharmony_ci break; 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7; 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci if (ah->ah_version == AR5K_AR5211) 18048c2ecf20Sopenharmony_ci q_coffd = q_pwr >> 6; 18058c2ecf20Sopenharmony_ci else 18068c2ecf20Sopenharmony_ci q_coffd = q_pwr >> 7; 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci /* In case i_coffd became zero, cancel calibration 18098c2ecf20Sopenharmony_ci * not only it's too small, it'll also result a divide 18108c2ecf20Sopenharmony_ci * by zero later on. */ 18118c2ecf20Sopenharmony_ci if (i_coffd == 0 || q_coffd < 2) 18128c2ecf20Sopenharmony_ci return -ECANCELED; 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci /* Protect against loss of sign bits */ 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci i_coff = (-iq_corr) / i_coffd; 18178c2ecf20Sopenharmony_ci i_coff = clamp(i_coff, -32, 31); /* signed 6 bit */ 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci if (ah->ah_version == AR5K_AR5211) 18208c2ecf20Sopenharmony_ci q_coff = (i_pwr / q_coffd) - 64; 18218c2ecf20Sopenharmony_ci else 18228c2ecf20Sopenharmony_ci q_coff = (i_pwr / q_coffd) - 128; 18238c2ecf20Sopenharmony_ci q_coff = clamp(q_coff, -16, 15); /* signed 5 bit */ 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE, 18268c2ecf20Sopenharmony_ci "new I:%d Q:%d (i_coffd:%x q_coffd:%x)", 18278c2ecf20Sopenharmony_ci i_coff, q_coff, i_coffd, q_coffd); 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci /* Commit new I/Q values (set enable bit last to match HAL sources) */ 18308c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_I_COFF, i_coff); 18318c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_Q_COFF, q_coff); 18328c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE); 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci /* Re-enable calibration -if we don't we'll commit 18358c2ecf20Sopenharmony_ci * the same values again and again */ 18368c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, 18378c2ecf20Sopenharmony_ci AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15); 18388c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_RUN); 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci return 0; 18418c2ecf20Sopenharmony_ci} 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci/** 18448c2ecf20Sopenharmony_ci * ath5k_hw_phy_calibrate() - Perform a PHY calibration 18458c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 18468c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 18478c2ecf20Sopenharmony_ci * 18488c2ecf20Sopenharmony_ci * The main function we call from above to perform 18498c2ecf20Sopenharmony_ci * a short or full PHY calibration based on RF chip 18508c2ecf20Sopenharmony_ci * and current channel 18518c2ecf20Sopenharmony_ci */ 18528c2ecf20Sopenharmony_ciint 18538c2ecf20Sopenharmony_ciath5k_hw_phy_calibrate(struct ath5k_hw *ah, 18548c2ecf20Sopenharmony_ci struct ieee80211_channel *channel) 18558c2ecf20Sopenharmony_ci{ 18568c2ecf20Sopenharmony_ci int ret; 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci if (ah->ah_radio == AR5K_RF5110) 18598c2ecf20Sopenharmony_ci return ath5k_hw_rf5110_calibrate(ah, channel); 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci ret = ath5k_hw_rf511x_iq_calibrate(ah); 18628c2ecf20Sopenharmony_ci if (ret) { 18638c2ecf20Sopenharmony_ci ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE, 18648c2ecf20Sopenharmony_ci "No I/Q correction performed (%uMHz)\n", 18658c2ecf20Sopenharmony_ci channel->center_freq); 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci /* Happens all the time if there is not much 18688c2ecf20Sopenharmony_ci * traffic, consider it normal behaviour. */ 18698c2ecf20Sopenharmony_ci ret = 0; 18708c2ecf20Sopenharmony_ci } 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci /* On full calibration request a PAPD probe for 18738c2ecf20Sopenharmony_ci * gainf calibration if needed */ 18748c2ecf20Sopenharmony_ci if ((ah->ah_cal_mask & AR5K_CALIBRATION_FULL) && 18758c2ecf20Sopenharmony_ci (ah->ah_radio == AR5K_RF5111 || 18768c2ecf20Sopenharmony_ci ah->ah_radio == AR5K_RF5112) && 18778c2ecf20Sopenharmony_ci channel->hw_value != AR5K_MODE_11B) 18788c2ecf20Sopenharmony_ci ath5k_hw_request_rfgain_probe(ah); 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci /* Update noise floor */ 18818c2ecf20Sopenharmony_ci if (!(ah->ah_cal_mask & AR5K_CALIBRATION_NF)) 18828c2ecf20Sopenharmony_ci ath5k_hw_update_noise_floor(ah); 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci return ret; 18858c2ecf20Sopenharmony_ci} 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci/***************************\ 18898c2ecf20Sopenharmony_ci* Spur mitigation functions * 18908c2ecf20Sopenharmony_ci\***************************/ 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci/** 18938c2ecf20Sopenharmony_ci * ath5k_hw_set_spur_mitigation_filter() - Configure SPUR filter 18948c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 18958c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 18968c2ecf20Sopenharmony_ci * 18978c2ecf20Sopenharmony_ci * This function gets called during PHY initialization to 18988c2ecf20Sopenharmony_ci * configure the spur filter for the given channel. Spur is noise 18998c2ecf20Sopenharmony_ci * generated due to "reflection" effects, for more information on this 19008c2ecf20Sopenharmony_ci * method check out patent US7643810 19018c2ecf20Sopenharmony_ci */ 19028c2ecf20Sopenharmony_cistatic void 19038c2ecf20Sopenharmony_ciath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah, 19048c2ecf20Sopenharmony_ci struct ieee80211_channel *channel) 19058c2ecf20Sopenharmony_ci{ 19068c2ecf20Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 19078c2ecf20Sopenharmony_ci u32 mag_mask[4] = {0, 0, 0, 0}; 19088c2ecf20Sopenharmony_ci u32 pilot_mask[2] = {0, 0}; 19098c2ecf20Sopenharmony_ci /* Note: fbin values are scaled up by 2 */ 19108c2ecf20Sopenharmony_ci u16 spur_chan_fbin, chan_fbin, symbol_width, spur_detection_window; 19118c2ecf20Sopenharmony_ci s32 spur_delta_phase, spur_freq_sigma_delta; 19128c2ecf20Sopenharmony_ci s32 spur_offset, num_symbols_x16; 19138c2ecf20Sopenharmony_ci u8 num_symbol_offsets, i, freq_band; 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci /* Convert current frequency to fbin value (the same way channels 19168c2ecf20Sopenharmony_ci * are stored on EEPROM, check out ath5k_eeprom_bin2freq) and scale 19178c2ecf20Sopenharmony_ci * up by 2 so we can compare it later */ 19188c2ecf20Sopenharmony_ci if (channel->band == NL80211_BAND_2GHZ) { 19198c2ecf20Sopenharmony_ci chan_fbin = (channel->center_freq - 2300) * 10; 19208c2ecf20Sopenharmony_ci freq_band = AR5K_EEPROM_BAND_2GHZ; 19218c2ecf20Sopenharmony_ci } else { 19228c2ecf20Sopenharmony_ci chan_fbin = (channel->center_freq - 4900) * 10; 19238c2ecf20Sopenharmony_ci freq_band = AR5K_EEPROM_BAND_5GHZ; 19248c2ecf20Sopenharmony_ci } 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci /* Check if any spur_chan_fbin from EEPROM is 19278c2ecf20Sopenharmony_ci * within our current channel's spur detection range */ 19288c2ecf20Sopenharmony_ci spur_chan_fbin = AR5K_EEPROM_NO_SPUR; 19298c2ecf20Sopenharmony_ci spur_detection_window = AR5K_SPUR_CHAN_WIDTH; 19308c2ecf20Sopenharmony_ci /* XXX: Half/Quarter channels ?*/ 19318c2ecf20Sopenharmony_ci if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) 19328c2ecf20Sopenharmony_ci spur_detection_window *= 2; 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci for (i = 0; i < AR5K_EEPROM_N_SPUR_CHANS; i++) { 19358c2ecf20Sopenharmony_ci spur_chan_fbin = ee->ee_spur_chans[i][freq_band]; 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci /* Note: mask cleans AR5K_EEPROM_NO_SPUR flag 19388c2ecf20Sopenharmony_ci * so it's zero if we got nothing from EEPROM */ 19398c2ecf20Sopenharmony_ci if (spur_chan_fbin == AR5K_EEPROM_NO_SPUR) { 19408c2ecf20Sopenharmony_ci spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK; 19418c2ecf20Sopenharmony_ci break; 19428c2ecf20Sopenharmony_ci } 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci if ((chan_fbin - spur_detection_window <= 19458c2ecf20Sopenharmony_ci (spur_chan_fbin & AR5K_EEPROM_SPUR_CHAN_MASK)) && 19468c2ecf20Sopenharmony_ci (chan_fbin + spur_detection_window >= 19478c2ecf20Sopenharmony_ci (spur_chan_fbin & AR5K_EEPROM_SPUR_CHAN_MASK))) { 19488c2ecf20Sopenharmony_ci spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK; 19498c2ecf20Sopenharmony_ci break; 19508c2ecf20Sopenharmony_ci } 19518c2ecf20Sopenharmony_ci } 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci /* We need to enable spur filter for this channel */ 19548c2ecf20Sopenharmony_ci if (spur_chan_fbin) { 19558c2ecf20Sopenharmony_ci spur_offset = spur_chan_fbin - chan_fbin; 19568c2ecf20Sopenharmony_ci /* 19578c2ecf20Sopenharmony_ci * Calculate deltas: 19588c2ecf20Sopenharmony_ci * spur_freq_sigma_delta -> spur_offset / sample_freq << 21 19598c2ecf20Sopenharmony_ci * spur_delta_phase -> spur_offset / chip_freq << 11 19608c2ecf20Sopenharmony_ci * Note: Both values have 100Hz resolution 19618c2ecf20Sopenharmony_ci */ 19628c2ecf20Sopenharmony_ci switch (ah->ah_bwmode) { 19638c2ecf20Sopenharmony_ci case AR5K_BWMODE_40MHZ: 19648c2ecf20Sopenharmony_ci /* Both sample_freq and chip_freq are 80MHz */ 19658c2ecf20Sopenharmony_ci spur_delta_phase = (spur_offset << 16) / 25; 19668c2ecf20Sopenharmony_ci spur_freq_sigma_delta = (spur_delta_phase >> 10); 19678c2ecf20Sopenharmony_ci symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz * 2; 19688c2ecf20Sopenharmony_ci break; 19698c2ecf20Sopenharmony_ci case AR5K_BWMODE_10MHZ: 19708c2ecf20Sopenharmony_ci /* Both sample_freq and chip_freq are 20MHz (?) */ 19718c2ecf20Sopenharmony_ci spur_delta_phase = (spur_offset << 18) / 25; 19728c2ecf20Sopenharmony_ci spur_freq_sigma_delta = (spur_delta_phase >> 10); 19738c2ecf20Sopenharmony_ci symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 2; 19748c2ecf20Sopenharmony_ci break; 19758c2ecf20Sopenharmony_ci case AR5K_BWMODE_5MHZ: 19768c2ecf20Sopenharmony_ci /* Both sample_freq and chip_freq are 10MHz (?) */ 19778c2ecf20Sopenharmony_ci spur_delta_phase = (spur_offset << 19) / 25; 19788c2ecf20Sopenharmony_ci spur_freq_sigma_delta = (spur_delta_phase >> 10); 19798c2ecf20Sopenharmony_ci symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 4; 19808c2ecf20Sopenharmony_ci break; 19818c2ecf20Sopenharmony_ci default: 19828c2ecf20Sopenharmony_ci if (channel->band == NL80211_BAND_5GHZ) { 19838c2ecf20Sopenharmony_ci /* Both sample_freq and chip_freq are 40MHz */ 19848c2ecf20Sopenharmony_ci spur_delta_phase = (spur_offset << 17) / 25; 19858c2ecf20Sopenharmony_ci spur_freq_sigma_delta = 19868c2ecf20Sopenharmony_ci (spur_delta_phase >> 10); 19878c2ecf20Sopenharmony_ci symbol_width = 19888c2ecf20Sopenharmony_ci AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz; 19898c2ecf20Sopenharmony_ci } else { 19908c2ecf20Sopenharmony_ci /* sample_freq -> 40MHz chip_freq -> 44MHz 19918c2ecf20Sopenharmony_ci * (for b compatibility) */ 19928c2ecf20Sopenharmony_ci spur_delta_phase = (spur_offset << 17) / 25; 19938c2ecf20Sopenharmony_ci spur_freq_sigma_delta = 19948c2ecf20Sopenharmony_ci (spur_offset << 8) / 55; 19958c2ecf20Sopenharmony_ci symbol_width = 19968c2ecf20Sopenharmony_ci AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz; 19978c2ecf20Sopenharmony_ci } 19988c2ecf20Sopenharmony_ci break; 19998c2ecf20Sopenharmony_ci } 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci /* Calculate pilot and magnitude masks */ 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci /* Scale up spur_offset by 1000 to switch to 100HZ resolution 20048c2ecf20Sopenharmony_ci * and divide by symbol_width to find how many symbols we have 20058c2ecf20Sopenharmony_ci * Note: number of symbols is scaled up by 16 */ 20068c2ecf20Sopenharmony_ci num_symbols_x16 = ((spur_offset * 1000) << 4) / symbol_width; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci /* Spur is on a symbol if num_symbols_x16 % 16 is zero */ 20098c2ecf20Sopenharmony_ci if (!(num_symbols_x16 & 0xF)) 20108c2ecf20Sopenharmony_ci /* _X_ */ 20118c2ecf20Sopenharmony_ci num_symbol_offsets = 3; 20128c2ecf20Sopenharmony_ci else 20138c2ecf20Sopenharmony_ci /* _xx_ */ 20148c2ecf20Sopenharmony_ci num_symbol_offsets = 4; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci for (i = 0; i < num_symbol_offsets; i++) { 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci /* Calculate pilot mask */ 20198c2ecf20Sopenharmony_ci s32 curr_sym_off = 20208c2ecf20Sopenharmony_ci (num_symbols_x16 / 16) + i + 25; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci /* Pilot magnitude mask seems to be a way to 20238c2ecf20Sopenharmony_ci * declare the boundaries for our detection 20248c2ecf20Sopenharmony_ci * window or something, it's 2 for the middle 20258c2ecf20Sopenharmony_ci * value(s) where the symbol is expected to be 20268c2ecf20Sopenharmony_ci * and 1 on the boundary values */ 20278c2ecf20Sopenharmony_ci u8 plt_mag_map = 20288c2ecf20Sopenharmony_ci (i == 0 || i == (num_symbol_offsets - 1)) 20298c2ecf20Sopenharmony_ci ? 1 : 2; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci if (curr_sym_off >= 0 && curr_sym_off <= 32) { 20328c2ecf20Sopenharmony_ci if (curr_sym_off <= 25) 20338c2ecf20Sopenharmony_ci pilot_mask[0] |= 1 << curr_sym_off; 20348c2ecf20Sopenharmony_ci else if (curr_sym_off >= 27) 20358c2ecf20Sopenharmony_ci pilot_mask[0] |= 1 << (curr_sym_off - 1); 20368c2ecf20Sopenharmony_ci } else if (curr_sym_off >= 33 && curr_sym_off <= 52) 20378c2ecf20Sopenharmony_ci pilot_mask[1] |= 1 << (curr_sym_off - 33); 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci /* Calculate magnitude mask (for viterbi decoder) */ 20408c2ecf20Sopenharmony_ci if (curr_sym_off >= -1 && curr_sym_off <= 14) 20418c2ecf20Sopenharmony_ci mag_mask[0] |= 20428c2ecf20Sopenharmony_ci plt_mag_map << (curr_sym_off + 1) * 2; 20438c2ecf20Sopenharmony_ci else if (curr_sym_off >= 15 && curr_sym_off <= 30) 20448c2ecf20Sopenharmony_ci mag_mask[1] |= 20458c2ecf20Sopenharmony_ci plt_mag_map << (curr_sym_off - 15) * 2; 20468c2ecf20Sopenharmony_ci else if (curr_sym_off >= 31 && curr_sym_off <= 46) 20478c2ecf20Sopenharmony_ci mag_mask[2] |= 20488c2ecf20Sopenharmony_ci plt_mag_map << (curr_sym_off - 31) * 2; 20498c2ecf20Sopenharmony_ci else if (curr_sym_off >= 47 && curr_sym_off <= 53) 20508c2ecf20Sopenharmony_ci mag_mask[3] |= 20518c2ecf20Sopenharmony_ci plt_mag_map << (curr_sym_off - 47) * 2; 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci } 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci /* Write settings on hw to enable spur filter */ 20568c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL, 20578c2ecf20Sopenharmony_ci AR5K_PHY_BIN_MASK_CTL_RATE, 0xff); 20588c2ecf20Sopenharmony_ci /* XXX: Self correlator also ? */ 20598c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, 20608c2ecf20Sopenharmony_ci AR5K_PHY_IQ_PILOT_MASK_EN | 20618c2ecf20Sopenharmony_ci AR5K_PHY_IQ_CHAN_MASK_EN | 20628c2ecf20Sopenharmony_ci AR5K_PHY_IQ_SPUR_FILT_EN); 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci /* Set delta phase and freq sigma delta */ 20658c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 20668c2ecf20Sopenharmony_ci AR5K_REG_SM(spur_delta_phase, 20678c2ecf20Sopenharmony_ci AR5K_PHY_TIMING_11_SPUR_DELTA_PHASE) | 20688c2ecf20Sopenharmony_ci AR5K_REG_SM(spur_freq_sigma_delta, 20698c2ecf20Sopenharmony_ci AR5K_PHY_TIMING_11_SPUR_FREQ_SD) | 20708c2ecf20Sopenharmony_ci AR5K_PHY_TIMING_11_USE_SPUR_IN_AGC, 20718c2ecf20Sopenharmony_ci AR5K_PHY_TIMING_11); 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci /* Write pilot masks */ 20748c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, pilot_mask[0], AR5K_PHY_TIMING_7); 20758c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_8, 20768c2ecf20Sopenharmony_ci AR5K_PHY_TIMING_8_PILOT_MASK_2, 20778c2ecf20Sopenharmony_ci pilot_mask[1]); 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, pilot_mask[0], AR5K_PHY_TIMING_9); 20808c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_10, 20818c2ecf20Sopenharmony_ci AR5K_PHY_TIMING_10_PILOT_MASK_2, 20828c2ecf20Sopenharmony_ci pilot_mask[1]); 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci /* Write magnitude masks */ 20858c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, mag_mask[0], AR5K_PHY_BIN_MASK_1); 20868c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, mag_mask[1], AR5K_PHY_BIN_MASK_2); 20878c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, mag_mask[2], AR5K_PHY_BIN_MASK_3); 20888c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL, 20898c2ecf20Sopenharmony_ci AR5K_PHY_BIN_MASK_CTL_MASK_4, 20908c2ecf20Sopenharmony_ci mag_mask[3]); 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, mag_mask[0], AR5K_PHY_BIN_MASK2_1); 20938c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, mag_mask[1], AR5K_PHY_BIN_MASK2_2); 20948c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, mag_mask[2], AR5K_PHY_BIN_MASK2_3); 20958c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK2_4, 20968c2ecf20Sopenharmony_ci AR5K_PHY_BIN_MASK2_4_MASK_4, 20978c2ecf20Sopenharmony_ci mag_mask[3]); 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci } else if (ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & 21008c2ecf20Sopenharmony_ci AR5K_PHY_IQ_SPUR_FILT_EN) { 21018c2ecf20Sopenharmony_ci /* Clean up spur mitigation settings and disable filter */ 21028c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL, 21038c2ecf20Sopenharmony_ci AR5K_PHY_BIN_MASK_CTL_RATE, 0); 21048c2ecf20Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_IQ, 21058c2ecf20Sopenharmony_ci AR5K_PHY_IQ_PILOT_MASK_EN | 21068c2ecf20Sopenharmony_ci AR5K_PHY_IQ_CHAN_MASK_EN | 21078c2ecf20Sopenharmony_ci AR5K_PHY_IQ_SPUR_FILT_EN); 21088c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_11); 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci /* Clear pilot masks */ 21118c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_7); 21128c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_8, 21138c2ecf20Sopenharmony_ci AR5K_PHY_TIMING_8_PILOT_MASK_2, 21148c2ecf20Sopenharmony_ci 0); 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_9); 21178c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_10, 21188c2ecf20Sopenharmony_ci AR5K_PHY_TIMING_10_PILOT_MASK_2, 21198c2ecf20Sopenharmony_ci 0); 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci /* Clear magnitude masks */ 21228c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_1); 21238c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_2); 21248c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_3); 21258c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL, 21268c2ecf20Sopenharmony_ci AR5K_PHY_BIN_MASK_CTL_MASK_4, 21278c2ecf20Sopenharmony_ci 0); 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_1); 21308c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_2); 21318c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_3); 21328c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK2_4, 21338c2ecf20Sopenharmony_ci AR5K_PHY_BIN_MASK2_4_MASK_4, 21348c2ecf20Sopenharmony_ci 0); 21358c2ecf20Sopenharmony_ci } 21368c2ecf20Sopenharmony_ci} 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci/*****************\ 21408c2ecf20Sopenharmony_ci* Antenna control * 21418c2ecf20Sopenharmony_ci\*****************/ 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci/** 21448c2ecf20Sopenharmony_ci * DOC: Antenna control 21458c2ecf20Sopenharmony_ci * 21468c2ecf20Sopenharmony_ci * Hw supports up to 14 antennas ! I haven't found any card that implements 21478c2ecf20Sopenharmony_ci * that. The maximum number of antennas I've seen is up to 4 (2 for 2GHz and 2 21488c2ecf20Sopenharmony_ci * for 5GHz). Antenna 1 (MAIN) should be omnidirectional, 2 (AUX) 21498c2ecf20Sopenharmony_ci * omnidirectional or sectorial and antennas 3-14 sectorial (or directional). 21508c2ecf20Sopenharmony_ci * 21518c2ecf20Sopenharmony_ci * We can have a single antenna for RX and multiple antennas for TX. 21528c2ecf20Sopenharmony_ci * RX antenna is our "default" antenna (usually antenna 1) set on 21538c2ecf20Sopenharmony_ci * DEFAULT_ANTENNA register and TX antenna is set on each TX control descriptor 21548c2ecf20Sopenharmony_ci * (0 for automatic selection, 1 - 14 antenna number). 21558c2ecf20Sopenharmony_ci * 21568c2ecf20Sopenharmony_ci * We can let hw do all the work doing fast antenna diversity for both 21578c2ecf20Sopenharmony_ci * tx and rx or we can do things manually. Here are the options we have 21588c2ecf20Sopenharmony_ci * (all are bits of STA_ID1 register): 21598c2ecf20Sopenharmony_ci * 21608c2ecf20Sopenharmony_ci * AR5K_STA_ID1_DEFAULT_ANTENNA -> When 0 is set as the TX antenna on TX 21618c2ecf20Sopenharmony_ci * control descriptor, use the default antenna to transmit or else use the last 21628c2ecf20Sopenharmony_ci * antenna on which we received an ACK. 21638c2ecf20Sopenharmony_ci * 21648c2ecf20Sopenharmony_ci * AR5K_STA_ID1_DESC_ANTENNA -> Update default antenna after each TX frame to 21658c2ecf20Sopenharmony_ci * the antenna on which we got the ACK for that frame. 21668c2ecf20Sopenharmony_ci * 21678c2ecf20Sopenharmony_ci * AR5K_STA_ID1_RTS_DEF_ANTENNA -> Use default antenna for RTS or else use the 21688c2ecf20Sopenharmony_ci * one on the TX descriptor. 21698c2ecf20Sopenharmony_ci * 21708c2ecf20Sopenharmony_ci * AR5K_STA_ID1_SELFGEN_DEF_ANT -> Use default antenna for self generated frames 21718c2ecf20Sopenharmony_ci * (ACKs etc), or else use current antenna (the one we just used for TX). 21728c2ecf20Sopenharmony_ci * 21738c2ecf20Sopenharmony_ci * Using the above we support the following scenarios: 21748c2ecf20Sopenharmony_ci * 21758c2ecf20Sopenharmony_ci * AR5K_ANTMODE_DEFAULT -> Hw handles antenna diversity etc automatically 21768c2ecf20Sopenharmony_ci * 21778c2ecf20Sopenharmony_ci * AR5K_ANTMODE_FIXED_A -> Only antenna A (MAIN) is present 21788c2ecf20Sopenharmony_ci * 21798c2ecf20Sopenharmony_ci * AR5K_ANTMODE_FIXED_B -> Only antenna B (AUX) is present 21808c2ecf20Sopenharmony_ci * 21818c2ecf20Sopenharmony_ci * AR5K_ANTMODE_SINGLE_AP -> Sta locked on a single ap 21828c2ecf20Sopenharmony_ci * 21838c2ecf20Sopenharmony_ci * AR5K_ANTMODE_SECTOR_AP -> AP with tx antenna set on tx desc 21848c2ecf20Sopenharmony_ci * 21858c2ecf20Sopenharmony_ci * AR5K_ANTMODE_SECTOR_STA -> STA with tx antenna set on tx desc 21868c2ecf20Sopenharmony_ci * 21878c2ecf20Sopenharmony_ci * AR5K_ANTMODE_DEBUG Debug mode -A -> Rx, B-> Tx- 21888c2ecf20Sopenharmony_ci * 21898c2ecf20Sopenharmony_ci * Also note that when setting antenna to F on tx descriptor card inverts 21908c2ecf20Sopenharmony_ci * current tx antenna. 21918c2ecf20Sopenharmony_ci */ 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci/** 21948c2ecf20Sopenharmony_ci * ath5k_hw_set_def_antenna() - Set default rx antenna on AR5211/5212 and newer 21958c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 21968c2ecf20Sopenharmony_ci * @ant: Antenna number 21978c2ecf20Sopenharmony_ci */ 21988c2ecf20Sopenharmony_cistatic void 21998c2ecf20Sopenharmony_ciath5k_hw_set_def_antenna(struct ath5k_hw *ah, u8 ant) 22008c2ecf20Sopenharmony_ci{ 22018c2ecf20Sopenharmony_ci if (ah->ah_version != AR5K_AR5210) 22028c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, ant & 0x7, AR5K_DEFAULT_ANTENNA); 22038c2ecf20Sopenharmony_ci} 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci/** 22068c2ecf20Sopenharmony_ci * ath5k_hw_set_fast_div() - Enable/disable fast rx antenna diversity 22078c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 22088c2ecf20Sopenharmony_ci * @ee_mode: One of enum ath5k_driver_mode 22098c2ecf20Sopenharmony_ci * @enable: True to enable, false to disable 22108c2ecf20Sopenharmony_ci */ 22118c2ecf20Sopenharmony_cistatic void 22128c2ecf20Sopenharmony_ciath5k_hw_set_fast_div(struct ath5k_hw *ah, u8 ee_mode, bool enable) 22138c2ecf20Sopenharmony_ci{ 22148c2ecf20Sopenharmony_ci switch (ee_mode) { 22158c2ecf20Sopenharmony_ci case AR5K_EEPROM_MODE_11G: 22168c2ecf20Sopenharmony_ci /* XXX: This is set to 22178c2ecf20Sopenharmony_ci * disabled on initvals !!! */ 22188c2ecf20Sopenharmony_ci case AR5K_EEPROM_MODE_11A: 22198c2ecf20Sopenharmony_ci if (enable) 22208c2ecf20Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGCCTL, 22218c2ecf20Sopenharmony_ci AR5K_PHY_AGCCTL_OFDM_DIV_DIS); 22228c2ecf20Sopenharmony_ci else 22238c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, 22248c2ecf20Sopenharmony_ci AR5K_PHY_AGCCTL_OFDM_DIV_DIS); 22258c2ecf20Sopenharmony_ci break; 22268c2ecf20Sopenharmony_ci case AR5K_EEPROM_MODE_11B: 22278c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, 22288c2ecf20Sopenharmony_ci AR5K_PHY_AGCCTL_OFDM_DIV_DIS); 22298c2ecf20Sopenharmony_ci break; 22308c2ecf20Sopenharmony_ci default: 22318c2ecf20Sopenharmony_ci return; 22328c2ecf20Sopenharmony_ci } 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci if (enable) { 22358c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RESTART, 22368c2ecf20Sopenharmony_ci AR5K_PHY_RESTART_DIV_GC, 4); 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_FAST_ANT_DIV, 22398c2ecf20Sopenharmony_ci AR5K_PHY_FAST_ANT_DIV_EN); 22408c2ecf20Sopenharmony_ci } else { 22418c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RESTART, 22428c2ecf20Sopenharmony_ci AR5K_PHY_RESTART_DIV_GC, 0); 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_FAST_ANT_DIV, 22458c2ecf20Sopenharmony_ci AR5K_PHY_FAST_ANT_DIV_EN); 22468c2ecf20Sopenharmony_ci } 22478c2ecf20Sopenharmony_ci} 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci/** 22508c2ecf20Sopenharmony_ci * ath5k_hw_set_antenna_switch() - Set up antenna switch table 22518c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 22528c2ecf20Sopenharmony_ci * @ee_mode: One of enum ath5k_driver_mode 22538c2ecf20Sopenharmony_ci * 22548c2ecf20Sopenharmony_ci * Switch table comes from EEPROM and includes information on controlling 22558c2ecf20Sopenharmony_ci * the 2 antenna RX attenuators 22568c2ecf20Sopenharmony_ci */ 22578c2ecf20Sopenharmony_civoid 22588c2ecf20Sopenharmony_ciath5k_hw_set_antenna_switch(struct ath5k_hw *ah, u8 ee_mode) 22598c2ecf20Sopenharmony_ci{ 22608c2ecf20Sopenharmony_ci u8 ant0, ant1; 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci /* 22638c2ecf20Sopenharmony_ci * In case a fixed antenna was set as default 22648c2ecf20Sopenharmony_ci * use the same switch table twice. 22658c2ecf20Sopenharmony_ci */ 22668c2ecf20Sopenharmony_ci if (ah->ah_ant_mode == AR5K_ANTMODE_FIXED_A) 22678c2ecf20Sopenharmony_ci ant0 = ant1 = AR5K_ANT_SWTABLE_A; 22688c2ecf20Sopenharmony_ci else if (ah->ah_ant_mode == AR5K_ANTMODE_FIXED_B) 22698c2ecf20Sopenharmony_ci ant0 = ant1 = AR5K_ANT_SWTABLE_B; 22708c2ecf20Sopenharmony_ci else { 22718c2ecf20Sopenharmony_ci ant0 = AR5K_ANT_SWTABLE_A; 22728c2ecf20Sopenharmony_ci ant1 = AR5K_ANT_SWTABLE_B; 22738c2ecf20Sopenharmony_ci } 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_ci /* Set antenna idle switch table */ 22768c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_ANT_CTL, 22778c2ecf20Sopenharmony_ci AR5K_PHY_ANT_CTL_SWTABLE_IDLE, 22788c2ecf20Sopenharmony_ci (ah->ah_ant_ctl[ee_mode][AR5K_ANT_CTL] | 22798c2ecf20Sopenharmony_ci AR5K_PHY_ANT_CTL_TXRX_EN)); 22808c2ecf20Sopenharmony_ci 22818c2ecf20Sopenharmony_ci /* Set antenna switch tables */ 22828c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, ah->ah_ant_ctl[ee_mode][ant0], 22838c2ecf20Sopenharmony_ci AR5K_PHY_ANT_SWITCH_TABLE_0); 22848c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, ah->ah_ant_ctl[ee_mode][ant1], 22858c2ecf20Sopenharmony_ci AR5K_PHY_ANT_SWITCH_TABLE_1); 22868c2ecf20Sopenharmony_ci} 22878c2ecf20Sopenharmony_ci 22888c2ecf20Sopenharmony_ci/** 22898c2ecf20Sopenharmony_ci * ath5k_hw_set_antenna_mode() - Set antenna operating mode 22908c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 22918c2ecf20Sopenharmony_ci * @ant_mode: One of enum ath5k_ant_mode 22928c2ecf20Sopenharmony_ci */ 22938c2ecf20Sopenharmony_civoid 22948c2ecf20Sopenharmony_ciath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode) 22958c2ecf20Sopenharmony_ci{ 22968c2ecf20Sopenharmony_ci struct ieee80211_channel *channel = ah->ah_current_channel; 22978c2ecf20Sopenharmony_ci bool use_def_for_tx, update_def_on_tx, use_def_for_rts, fast_div; 22988c2ecf20Sopenharmony_ci bool use_def_for_sg; 22998c2ecf20Sopenharmony_ci int ee_mode; 23008c2ecf20Sopenharmony_ci u8 def_ant, tx_ant; 23018c2ecf20Sopenharmony_ci u32 sta_id1 = 0; 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ci /* if channel is not initialized yet we can't set the antennas 23048c2ecf20Sopenharmony_ci * so just store the mode. it will be set on the next reset */ 23058c2ecf20Sopenharmony_ci if (channel == NULL) { 23068c2ecf20Sopenharmony_ci ah->ah_ant_mode = ant_mode; 23078c2ecf20Sopenharmony_ci return; 23088c2ecf20Sopenharmony_ci } 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ci def_ant = ah->ah_def_ant; 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_ci ee_mode = ath5k_eeprom_mode_from_channel(ah, channel); 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci switch (ant_mode) { 23158c2ecf20Sopenharmony_ci case AR5K_ANTMODE_DEFAULT: 23168c2ecf20Sopenharmony_ci tx_ant = 0; 23178c2ecf20Sopenharmony_ci use_def_for_tx = false; 23188c2ecf20Sopenharmony_ci update_def_on_tx = false; 23198c2ecf20Sopenharmony_ci use_def_for_rts = false; 23208c2ecf20Sopenharmony_ci use_def_for_sg = false; 23218c2ecf20Sopenharmony_ci fast_div = true; 23228c2ecf20Sopenharmony_ci break; 23238c2ecf20Sopenharmony_ci case AR5K_ANTMODE_FIXED_A: 23248c2ecf20Sopenharmony_ci def_ant = 1; 23258c2ecf20Sopenharmony_ci tx_ant = 1; 23268c2ecf20Sopenharmony_ci use_def_for_tx = true; 23278c2ecf20Sopenharmony_ci update_def_on_tx = false; 23288c2ecf20Sopenharmony_ci use_def_for_rts = true; 23298c2ecf20Sopenharmony_ci use_def_for_sg = true; 23308c2ecf20Sopenharmony_ci fast_div = false; 23318c2ecf20Sopenharmony_ci break; 23328c2ecf20Sopenharmony_ci case AR5K_ANTMODE_FIXED_B: 23338c2ecf20Sopenharmony_ci def_ant = 2; 23348c2ecf20Sopenharmony_ci tx_ant = 2; 23358c2ecf20Sopenharmony_ci use_def_for_tx = true; 23368c2ecf20Sopenharmony_ci update_def_on_tx = false; 23378c2ecf20Sopenharmony_ci use_def_for_rts = true; 23388c2ecf20Sopenharmony_ci use_def_for_sg = true; 23398c2ecf20Sopenharmony_ci fast_div = false; 23408c2ecf20Sopenharmony_ci break; 23418c2ecf20Sopenharmony_ci case AR5K_ANTMODE_SINGLE_AP: 23428c2ecf20Sopenharmony_ci def_ant = 1; /* updated on tx */ 23438c2ecf20Sopenharmony_ci tx_ant = 0; 23448c2ecf20Sopenharmony_ci use_def_for_tx = true; 23458c2ecf20Sopenharmony_ci update_def_on_tx = true; 23468c2ecf20Sopenharmony_ci use_def_for_rts = true; 23478c2ecf20Sopenharmony_ci use_def_for_sg = true; 23488c2ecf20Sopenharmony_ci fast_div = true; 23498c2ecf20Sopenharmony_ci break; 23508c2ecf20Sopenharmony_ci case AR5K_ANTMODE_SECTOR_AP: 23518c2ecf20Sopenharmony_ci tx_ant = 1; /* variable */ 23528c2ecf20Sopenharmony_ci use_def_for_tx = false; 23538c2ecf20Sopenharmony_ci update_def_on_tx = false; 23548c2ecf20Sopenharmony_ci use_def_for_rts = true; 23558c2ecf20Sopenharmony_ci use_def_for_sg = false; 23568c2ecf20Sopenharmony_ci fast_div = false; 23578c2ecf20Sopenharmony_ci break; 23588c2ecf20Sopenharmony_ci case AR5K_ANTMODE_SECTOR_STA: 23598c2ecf20Sopenharmony_ci tx_ant = 1; /* variable */ 23608c2ecf20Sopenharmony_ci use_def_for_tx = true; 23618c2ecf20Sopenharmony_ci update_def_on_tx = false; 23628c2ecf20Sopenharmony_ci use_def_for_rts = true; 23638c2ecf20Sopenharmony_ci use_def_for_sg = false; 23648c2ecf20Sopenharmony_ci fast_div = true; 23658c2ecf20Sopenharmony_ci break; 23668c2ecf20Sopenharmony_ci case AR5K_ANTMODE_DEBUG: 23678c2ecf20Sopenharmony_ci def_ant = 1; 23688c2ecf20Sopenharmony_ci tx_ant = 2; 23698c2ecf20Sopenharmony_ci use_def_for_tx = false; 23708c2ecf20Sopenharmony_ci update_def_on_tx = false; 23718c2ecf20Sopenharmony_ci use_def_for_rts = false; 23728c2ecf20Sopenharmony_ci use_def_for_sg = false; 23738c2ecf20Sopenharmony_ci fast_div = false; 23748c2ecf20Sopenharmony_ci break; 23758c2ecf20Sopenharmony_ci default: 23768c2ecf20Sopenharmony_ci return; 23778c2ecf20Sopenharmony_ci } 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci ah->ah_tx_ant = tx_ant; 23808c2ecf20Sopenharmony_ci ah->ah_ant_mode = ant_mode; 23818c2ecf20Sopenharmony_ci ah->ah_def_ant = def_ant; 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_ci sta_id1 |= use_def_for_tx ? AR5K_STA_ID1_DEFAULT_ANTENNA : 0; 23848c2ecf20Sopenharmony_ci sta_id1 |= update_def_on_tx ? AR5K_STA_ID1_DESC_ANTENNA : 0; 23858c2ecf20Sopenharmony_ci sta_id1 |= use_def_for_rts ? AR5K_STA_ID1_RTS_DEF_ANTENNA : 0; 23868c2ecf20Sopenharmony_ci sta_id1 |= use_def_for_sg ? AR5K_STA_ID1_SELFGEN_DEF_ANT : 0; 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_ANTENNA_SETTINGS); 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci if (sta_id1) 23918c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, sta_id1); 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ci ath5k_hw_set_antenna_switch(ah, ee_mode); 23948c2ecf20Sopenharmony_ci /* Note: set diversity before default antenna 23958c2ecf20Sopenharmony_ci * because it won't work correctly */ 23968c2ecf20Sopenharmony_ci ath5k_hw_set_fast_div(ah, ee_mode, fast_div); 23978c2ecf20Sopenharmony_ci ath5k_hw_set_def_antenna(ah, def_ant); 23988c2ecf20Sopenharmony_ci} 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci/****************\ 24028c2ecf20Sopenharmony_ci* TX power setup * 24038c2ecf20Sopenharmony_ci\****************/ 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci/* 24068c2ecf20Sopenharmony_ci * Helper functions 24078c2ecf20Sopenharmony_ci */ 24088c2ecf20Sopenharmony_ci 24098c2ecf20Sopenharmony_ci/** 24108c2ecf20Sopenharmony_ci * ath5k_get_interpolated_value() - Get interpolated Y val between two points 24118c2ecf20Sopenharmony_ci * @target: X value of the middle point 24128c2ecf20Sopenharmony_ci * @x_left: X value of the left point 24138c2ecf20Sopenharmony_ci * @x_right: X value of the right point 24148c2ecf20Sopenharmony_ci * @y_left: Y value of the left point 24158c2ecf20Sopenharmony_ci * @y_right: Y value of the right point 24168c2ecf20Sopenharmony_ci */ 24178c2ecf20Sopenharmony_cistatic s16 24188c2ecf20Sopenharmony_ciath5k_get_interpolated_value(s16 target, s16 x_left, s16 x_right, 24198c2ecf20Sopenharmony_ci s16 y_left, s16 y_right) 24208c2ecf20Sopenharmony_ci{ 24218c2ecf20Sopenharmony_ci s16 ratio, result; 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci /* Avoid divide by zero and skip interpolation 24248c2ecf20Sopenharmony_ci * if we have the same point */ 24258c2ecf20Sopenharmony_ci if ((x_left == x_right) || (y_left == y_right)) 24268c2ecf20Sopenharmony_ci return y_left; 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_ci /* 24298c2ecf20Sopenharmony_ci * Since we use ints and not fps, we need to scale up in 24308c2ecf20Sopenharmony_ci * order to get a sane ratio value (or else we 'll eg. get 24318c2ecf20Sopenharmony_ci * always 1 instead of 1.25, 1.75 etc). We scale up by 100 24328c2ecf20Sopenharmony_ci * to have some accuracy both for 0.5 and 0.25 steps. 24338c2ecf20Sopenharmony_ci */ 24348c2ecf20Sopenharmony_ci ratio = ((100 * y_right - 100 * y_left) / (x_right - x_left)); 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci /* Now scale down to be in range */ 24378c2ecf20Sopenharmony_ci result = y_left + (ratio * (target - x_left) / 100); 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci return result; 24408c2ecf20Sopenharmony_ci} 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_ci/** 24438c2ecf20Sopenharmony_ci * ath5k_get_linear_pcdac_min() - Find vertical boundary (min pwr) for the 24448c2ecf20Sopenharmony_ci * linear PCDAC curve 24458c2ecf20Sopenharmony_ci * @stepL: Left array with y values (pcdac steps) 24468c2ecf20Sopenharmony_ci * @stepR: Right array with y values (pcdac steps) 24478c2ecf20Sopenharmony_ci * @pwrL: Left array with x values (power steps) 24488c2ecf20Sopenharmony_ci * @pwrR: Right array with x values (power steps) 24498c2ecf20Sopenharmony_ci * 24508c2ecf20Sopenharmony_ci * Since we have the top of the curve and we draw the line below 24518c2ecf20Sopenharmony_ci * until we reach 1 (1 pcdac step) we need to know which point 24528c2ecf20Sopenharmony_ci * (x value) that is so that we don't go below x axis and have negative 24538c2ecf20Sopenharmony_ci * pcdac values when creating the curve, or fill the table with zeros. 24548c2ecf20Sopenharmony_ci */ 24558c2ecf20Sopenharmony_cistatic s16 24568c2ecf20Sopenharmony_ciath5k_get_linear_pcdac_min(const u8 *stepL, const u8 *stepR, 24578c2ecf20Sopenharmony_ci const s16 *pwrL, const s16 *pwrR) 24588c2ecf20Sopenharmony_ci{ 24598c2ecf20Sopenharmony_ci s8 tmp; 24608c2ecf20Sopenharmony_ci s16 min_pwrL, min_pwrR; 24618c2ecf20Sopenharmony_ci s16 pwr_i; 24628c2ecf20Sopenharmony_ci 24638c2ecf20Sopenharmony_ci /* Some vendors write the same pcdac value twice !!! */ 24648c2ecf20Sopenharmony_ci if (stepL[0] == stepL[1] || stepR[0] == stepR[1]) 24658c2ecf20Sopenharmony_ci return max(pwrL[0], pwrR[0]); 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_ci if (pwrL[0] == pwrL[1]) 24688c2ecf20Sopenharmony_ci min_pwrL = pwrL[0]; 24698c2ecf20Sopenharmony_ci else { 24708c2ecf20Sopenharmony_ci pwr_i = pwrL[0]; 24718c2ecf20Sopenharmony_ci do { 24728c2ecf20Sopenharmony_ci pwr_i--; 24738c2ecf20Sopenharmony_ci tmp = (s8) ath5k_get_interpolated_value(pwr_i, 24748c2ecf20Sopenharmony_ci pwrL[0], pwrL[1], 24758c2ecf20Sopenharmony_ci stepL[0], stepL[1]); 24768c2ecf20Sopenharmony_ci } while (tmp > 1); 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_ci min_pwrL = pwr_i; 24798c2ecf20Sopenharmony_ci } 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_ci if (pwrR[0] == pwrR[1]) 24828c2ecf20Sopenharmony_ci min_pwrR = pwrR[0]; 24838c2ecf20Sopenharmony_ci else { 24848c2ecf20Sopenharmony_ci pwr_i = pwrR[0]; 24858c2ecf20Sopenharmony_ci do { 24868c2ecf20Sopenharmony_ci pwr_i--; 24878c2ecf20Sopenharmony_ci tmp = (s8) ath5k_get_interpolated_value(pwr_i, 24888c2ecf20Sopenharmony_ci pwrR[0], pwrR[1], 24898c2ecf20Sopenharmony_ci stepR[0], stepR[1]); 24908c2ecf20Sopenharmony_ci } while (tmp > 1); 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_ci min_pwrR = pwr_i; 24938c2ecf20Sopenharmony_ci } 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci /* Keep the right boundary so that it works for both curves */ 24968c2ecf20Sopenharmony_ci return max(min_pwrL, min_pwrR); 24978c2ecf20Sopenharmony_ci} 24988c2ecf20Sopenharmony_ci 24998c2ecf20Sopenharmony_ci/** 25008c2ecf20Sopenharmony_ci * ath5k_create_power_curve() - Create a Power to PDADC or PCDAC curve 25018c2ecf20Sopenharmony_ci * @pmin: Minimum power value (xmin) 25028c2ecf20Sopenharmony_ci * @pmax: Maximum power value (xmax) 25038c2ecf20Sopenharmony_ci * @pwr: Array of power steps (x values) 25048c2ecf20Sopenharmony_ci * @vpd: Array of matching PCDAC/PDADC steps (y values) 25058c2ecf20Sopenharmony_ci * @num_points: Number of provided points 25068c2ecf20Sopenharmony_ci * @vpd_table: Array to fill with the full PCDAC/PDADC values (y values) 25078c2ecf20Sopenharmony_ci * @type: One of enum ath5k_powertable_type (eeprom.h) 25088c2ecf20Sopenharmony_ci * 25098c2ecf20Sopenharmony_ci * Interpolate (pwr,vpd) points to create a Power to PDADC or a 25108c2ecf20Sopenharmony_ci * Power to PCDAC curve. 25118c2ecf20Sopenharmony_ci * 25128c2ecf20Sopenharmony_ci * Each curve has power on x axis (in 0.5dB units) and PCDAC/PDADC 25138c2ecf20Sopenharmony_ci * steps (offsets) on y axis. Power can go up to 31.5dB and max 25148c2ecf20Sopenharmony_ci * PCDAC/PDADC step for each curve is 64 but we can write more than 25158c2ecf20Sopenharmony_ci * one curves on hw so we can go up to 128 (which is the max step we 25168c2ecf20Sopenharmony_ci * can write on the final table). 25178c2ecf20Sopenharmony_ci * 25188c2ecf20Sopenharmony_ci * We write y values (PCDAC/PDADC steps) on hw. 25198c2ecf20Sopenharmony_ci */ 25208c2ecf20Sopenharmony_cistatic void 25218c2ecf20Sopenharmony_ciath5k_create_power_curve(s16 pmin, s16 pmax, 25228c2ecf20Sopenharmony_ci const s16 *pwr, const u8 *vpd, 25238c2ecf20Sopenharmony_ci u8 num_points, 25248c2ecf20Sopenharmony_ci u8 *vpd_table, u8 type) 25258c2ecf20Sopenharmony_ci{ 25268c2ecf20Sopenharmony_ci u8 idx[2] = { 0, 1 }; 25278c2ecf20Sopenharmony_ci s16 pwr_i = 2 * pmin; 25288c2ecf20Sopenharmony_ci int i; 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_ci if (num_points < 2) 25318c2ecf20Sopenharmony_ci return; 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_ci /* We want the whole line, so adjust boundaries 25348c2ecf20Sopenharmony_ci * to cover the entire power range. Note that 25358c2ecf20Sopenharmony_ci * power values are already 0.25dB so no need 25368c2ecf20Sopenharmony_ci * to multiply pwr_i by 2 */ 25378c2ecf20Sopenharmony_ci if (type == AR5K_PWRTABLE_LINEAR_PCDAC) { 25388c2ecf20Sopenharmony_ci pwr_i = pmin; 25398c2ecf20Sopenharmony_ci pmin = 0; 25408c2ecf20Sopenharmony_ci pmax = 63; 25418c2ecf20Sopenharmony_ci } 25428c2ecf20Sopenharmony_ci 25438c2ecf20Sopenharmony_ci /* Find surrounding turning points (TPs) 25448c2ecf20Sopenharmony_ci * and interpolate between them */ 25458c2ecf20Sopenharmony_ci for (i = 0; (i <= (u16) (pmax - pmin)) && 25468c2ecf20Sopenharmony_ci (i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) { 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_ci /* We passed the right TP, move to the next set of TPs 25498c2ecf20Sopenharmony_ci * if we pass the last TP, extrapolate above using the last 25508c2ecf20Sopenharmony_ci * two TPs for ratio */ 25518c2ecf20Sopenharmony_ci if ((pwr_i > pwr[idx[1]]) && (idx[1] < num_points - 1)) { 25528c2ecf20Sopenharmony_ci idx[0]++; 25538c2ecf20Sopenharmony_ci idx[1]++; 25548c2ecf20Sopenharmony_ci } 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci vpd_table[i] = (u8) ath5k_get_interpolated_value(pwr_i, 25578c2ecf20Sopenharmony_ci pwr[idx[0]], pwr[idx[1]], 25588c2ecf20Sopenharmony_ci vpd[idx[0]], vpd[idx[1]]); 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_ci /* Increase by 0.5dB 25618c2ecf20Sopenharmony_ci * (0.25 dB units) */ 25628c2ecf20Sopenharmony_ci pwr_i += 2; 25638c2ecf20Sopenharmony_ci } 25648c2ecf20Sopenharmony_ci} 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci/** 25678c2ecf20Sopenharmony_ci * ath5k_get_chan_pcal_surrounding_piers() - Get surrounding calibration piers 25688c2ecf20Sopenharmony_ci * for a given channel. 25698c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 25708c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 25718c2ecf20Sopenharmony_ci * @pcinfo_l: The &struct ath5k_chan_pcal_info to put the left cal. pier 25728c2ecf20Sopenharmony_ci * @pcinfo_r: The &struct ath5k_chan_pcal_info to put the right cal. pier 25738c2ecf20Sopenharmony_ci * 25748c2ecf20Sopenharmony_ci * Get the surrounding per-channel power calibration piers 25758c2ecf20Sopenharmony_ci * for a given frequency so that we can interpolate between 25768c2ecf20Sopenharmony_ci * them and come up with an appropriate dataset for our current 25778c2ecf20Sopenharmony_ci * channel. 25788c2ecf20Sopenharmony_ci */ 25798c2ecf20Sopenharmony_cistatic void 25808c2ecf20Sopenharmony_ciath5k_get_chan_pcal_surrounding_piers(struct ath5k_hw *ah, 25818c2ecf20Sopenharmony_ci struct ieee80211_channel *channel, 25828c2ecf20Sopenharmony_ci struct ath5k_chan_pcal_info **pcinfo_l, 25838c2ecf20Sopenharmony_ci struct ath5k_chan_pcal_info **pcinfo_r) 25848c2ecf20Sopenharmony_ci{ 25858c2ecf20Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 25868c2ecf20Sopenharmony_ci struct ath5k_chan_pcal_info *pcinfo; 25878c2ecf20Sopenharmony_ci u8 idx_l, idx_r; 25888c2ecf20Sopenharmony_ci u8 mode, max, i; 25898c2ecf20Sopenharmony_ci u32 target = channel->center_freq; 25908c2ecf20Sopenharmony_ci 25918c2ecf20Sopenharmony_ci idx_l = 0; 25928c2ecf20Sopenharmony_ci idx_r = 0; 25938c2ecf20Sopenharmony_ci 25948c2ecf20Sopenharmony_ci switch (channel->hw_value) { 25958c2ecf20Sopenharmony_ci case AR5K_EEPROM_MODE_11A: 25968c2ecf20Sopenharmony_ci pcinfo = ee->ee_pwr_cal_a; 25978c2ecf20Sopenharmony_ci mode = AR5K_EEPROM_MODE_11A; 25988c2ecf20Sopenharmony_ci break; 25998c2ecf20Sopenharmony_ci case AR5K_EEPROM_MODE_11B: 26008c2ecf20Sopenharmony_ci pcinfo = ee->ee_pwr_cal_b; 26018c2ecf20Sopenharmony_ci mode = AR5K_EEPROM_MODE_11B; 26028c2ecf20Sopenharmony_ci break; 26038c2ecf20Sopenharmony_ci case AR5K_EEPROM_MODE_11G: 26048c2ecf20Sopenharmony_ci default: 26058c2ecf20Sopenharmony_ci pcinfo = ee->ee_pwr_cal_g; 26068c2ecf20Sopenharmony_ci mode = AR5K_EEPROM_MODE_11G; 26078c2ecf20Sopenharmony_ci break; 26088c2ecf20Sopenharmony_ci } 26098c2ecf20Sopenharmony_ci max = ee->ee_n_piers[mode] - 1; 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_ci /* Frequency is below our calibrated 26128c2ecf20Sopenharmony_ci * range. Use the lowest power curve 26138c2ecf20Sopenharmony_ci * we have */ 26148c2ecf20Sopenharmony_ci if (target < pcinfo[0].freq) { 26158c2ecf20Sopenharmony_ci idx_l = idx_r = 0; 26168c2ecf20Sopenharmony_ci goto done; 26178c2ecf20Sopenharmony_ci } 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci /* Frequency is above our calibrated 26208c2ecf20Sopenharmony_ci * range. Use the highest power curve 26218c2ecf20Sopenharmony_ci * we have */ 26228c2ecf20Sopenharmony_ci if (target > pcinfo[max].freq) { 26238c2ecf20Sopenharmony_ci idx_l = idx_r = max; 26248c2ecf20Sopenharmony_ci goto done; 26258c2ecf20Sopenharmony_ci } 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_ci /* Frequency is inside our calibrated 26288c2ecf20Sopenharmony_ci * channel range. Pick the surrounding 26298c2ecf20Sopenharmony_ci * calibration piers so that we can 26308c2ecf20Sopenharmony_ci * interpolate */ 26318c2ecf20Sopenharmony_ci for (i = 0; i <= max; i++) { 26328c2ecf20Sopenharmony_ci 26338c2ecf20Sopenharmony_ci /* Frequency matches one of our calibration 26348c2ecf20Sopenharmony_ci * piers, no need to interpolate, just use 26358c2ecf20Sopenharmony_ci * that calibration pier */ 26368c2ecf20Sopenharmony_ci if (pcinfo[i].freq == target) { 26378c2ecf20Sopenharmony_ci idx_l = idx_r = i; 26388c2ecf20Sopenharmony_ci goto done; 26398c2ecf20Sopenharmony_ci } 26408c2ecf20Sopenharmony_ci 26418c2ecf20Sopenharmony_ci /* We found a calibration pier that's above 26428c2ecf20Sopenharmony_ci * frequency, use this pier and the previous 26438c2ecf20Sopenharmony_ci * one to interpolate */ 26448c2ecf20Sopenharmony_ci if (target < pcinfo[i].freq) { 26458c2ecf20Sopenharmony_ci idx_r = i; 26468c2ecf20Sopenharmony_ci idx_l = idx_r - 1; 26478c2ecf20Sopenharmony_ci goto done; 26488c2ecf20Sopenharmony_ci } 26498c2ecf20Sopenharmony_ci } 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_cidone: 26528c2ecf20Sopenharmony_ci *pcinfo_l = &pcinfo[idx_l]; 26538c2ecf20Sopenharmony_ci *pcinfo_r = &pcinfo[idx_r]; 26548c2ecf20Sopenharmony_ci} 26558c2ecf20Sopenharmony_ci 26568c2ecf20Sopenharmony_ci/** 26578c2ecf20Sopenharmony_ci * ath5k_get_rate_pcal_data() - Get the interpolated per-rate power 26588c2ecf20Sopenharmony_ci * calibration data 26598c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw *ah, 26608c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 26618c2ecf20Sopenharmony_ci * @rates: The &struct ath5k_rate_pcal_info to fill 26628c2ecf20Sopenharmony_ci * 26638c2ecf20Sopenharmony_ci * Get the surrounding per-rate power calibration data 26648c2ecf20Sopenharmony_ci * for a given frequency and interpolate between power 26658c2ecf20Sopenharmony_ci * values to set max target power supported by hw for 26668c2ecf20Sopenharmony_ci * each rate on this frequency. 26678c2ecf20Sopenharmony_ci */ 26688c2ecf20Sopenharmony_cistatic void 26698c2ecf20Sopenharmony_ciath5k_get_rate_pcal_data(struct ath5k_hw *ah, 26708c2ecf20Sopenharmony_ci struct ieee80211_channel *channel, 26718c2ecf20Sopenharmony_ci struct ath5k_rate_pcal_info *rates) 26728c2ecf20Sopenharmony_ci{ 26738c2ecf20Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 26748c2ecf20Sopenharmony_ci struct ath5k_rate_pcal_info *rpinfo; 26758c2ecf20Sopenharmony_ci u8 idx_l, idx_r; 26768c2ecf20Sopenharmony_ci u8 mode, max, i; 26778c2ecf20Sopenharmony_ci u32 target = channel->center_freq; 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci idx_l = 0; 26808c2ecf20Sopenharmony_ci idx_r = 0; 26818c2ecf20Sopenharmony_ci 26828c2ecf20Sopenharmony_ci switch (channel->hw_value) { 26838c2ecf20Sopenharmony_ci case AR5K_MODE_11A: 26848c2ecf20Sopenharmony_ci rpinfo = ee->ee_rate_tpwr_a; 26858c2ecf20Sopenharmony_ci mode = AR5K_EEPROM_MODE_11A; 26868c2ecf20Sopenharmony_ci break; 26878c2ecf20Sopenharmony_ci case AR5K_MODE_11B: 26888c2ecf20Sopenharmony_ci rpinfo = ee->ee_rate_tpwr_b; 26898c2ecf20Sopenharmony_ci mode = AR5K_EEPROM_MODE_11B; 26908c2ecf20Sopenharmony_ci break; 26918c2ecf20Sopenharmony_ci case AR5K_MODE_11G: 26928c2ecf20Sopenharmony_ci default: 26938c2ecf20Sopenharmony_ci rpinfo = ee->ee_rate_tpwr_g; 26948c2ecf20Sopenharmony_ci mode = AR5K_EEPROM_MODE_11G; 26958c2ecf20Sopenharmony_ci break; 26968c2ecf20Sopenharmony_ci } 26978c2ecf20Sopenharmony_ci max = ee->ee_rate_target_pwr_num[mode] - 1; 26988c2ecf20Sopenharmony_ci 26998c2ecf20Sopenharmony_ci /* Get the surrounding calibration 27008c2ecf20Sopenharmony_ci * piers - same as above */ 27018c2ecf20Sopenharmony_ci if (target < rpinfo[0].freq) { 27028c2ecf20Sopenharmony_ci idx_l = idx_r = 0; 27038c2ecf20Sopenharmony_ci goto done; 27048c2ecf20Sopenharmony_ci } 27058c2ecf20Sopenharmony_ci 27068c2ecf20Sopenharmony_ci if (target > rpinfo[max].freq) { 27078c2ecf20Sopenharmony_ci idx_l = idx_r = max; 27088c2ecf20Sopenharmony_ci goto done; 27098c2ecf20Sopenharmony_ci } 27108c2ecf20Sopenharmony_ci 27118c2ecf20Sopenharmony_ci for (i = 0; i <= max; i++) { 27128c2ecf20Sopenharmony_ci 27138c2ecf20Sopenharmony_ci if (rpinfo[i].freq == target) { 27148c2ecf20Sopenharmony_ci idx_l = idx_r = i; 27158c2ecf20Sopenharmony_ci goto done; 27168c2ecf20Sopenharmony_ci } 27178c2ecf20Sopenharmony_ci 27188c2ecf20Sopenharmony_ci if (target < rpinfo[i].freq) { 27198c2ecf20Sopenharmony_ci idx_r = i; 27208c2ecf20Sopenharmony_ci idx_l = idx_r - 1; 27218c2ecf20Sopenharmony_ci goto done; 27228c2ecf20Sopenharmony_ci } 27238c2ecf20Sopenharmony_ci } 27248c2ecf20Sopenharmony_ci 27258c2ecf20Sopenharmony_cidone: 27268c2ecf20Sopenharmony_ci /* Now interpolate power value, based on the frequency */ 27278c2ecf20Sopenharmony_ci rates->freq = target; 27288c2ecf20Sopenharmony_ci 27298c2ecf20Sopenharmony_ci rates->target_power_6to24 = 27308c2ecf20Sopenharmony_ci ath5k_get_interpolated_value(target, rpinfo[idx_l].freq, 27318c2ecf20Sopenharmony_ci rpinfo[idx_r].freq, 27328c2ecf20Sopenharmony_ci rpinfo[idx_l].target_power_6to24, 27338c2ecf20Sopenharmony_ci rpinfo[idx_r].target_power_6to24); 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci rates->target_power_36 = 27368c2ecf20Sopenharmony_ci ath5k_get_interpolated_value(target, rpinfo[idx_l].freq, 27378c2ecf20Sopenharmony_ci rpinfo[idx_r].freq, 27388c2ecf20Sopenharmony_ci rpinfo[idx_l].target_power_36, 27398c2ecf20Sopenharmony_ci rpinfo[idx_r].target_power_36); 27408c2ecf20Sopenharmony_ci 27418c2ecf20Sopenharmony_ci rates->target_power_48 = 27428c2ecf20Sopenharmony_ci ath5k_get_interpolated_value(target, rpinfo[idx_l].freq, 27438c2ecf20Sopenharmony_ci rpinfo[idx_r].freq, 27448c2ecf20Sopenharmony_ci rpinfo[idx_l].target_power_48, 27458c2ecf20Sopenharmony_ci rpinfo[idx_r].target_power_48); 27468c2ecf20Sopenharmony_ci 27478c2ecf20Sopenharmony_ci rates->target_power_54 = 27488c2ecf20Sopenharmony_ci ath5k_get_interpolated_value(target, rpinfo[idx_l].freq, 27498c2ecf20Sopenharmony_ci rpinfo[idx_r].freq, 27508c2ecf20Sopenharmony_ci rpinfo[idx_l].target_power_54, 27518c2ecf20Sopenharmony_ci rpinfo[idx_r].target_power_54); 27528c2ecf20Sopenharmony_ci} 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_ci/** 27558c2ecf20Sopenharmony_ci * ath5k_get_max_ctl_power() - Get max edge power for a given frequency 27568c2ecf20Sopenharmony_ci * @ah: the &struct ath5k_hw 27578c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 27588c2ecf20Sopenharmony_ci * 27598c2ecf20Sopenharmony_ci * Get the max edge power for this channel if 27608c2ecf20Sopenharmony_ci * we have such data from EEPROM's Conformance Test 27618c2ecf20Sopenharmony_ci * Limits (CTL), and limit max power if needed. 27628c2ecf20Sopenharmony_ci */ 27638c2ecf20Sopenharmony_cistatic void 27648c2ecf20Sopenharmony_ciath5k_get_max_ctl_power(struct ath5k_hw *ah, 27658c2ecf20Sopenharmony_ci struct ieee80211_channel *channel) 27668c2ecf20Sopenharmony_ci{ 27678c2ecf20Sopenharmony_ci struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah); 27688c2ecf20Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 27698c2ecf20Sopenharmony_ci struct ath5k_edge_power *rep = ee->ee_ctl_pwr; 27708c2ecf20Sopenharmony_ci u8 *ctl_val = ee->ee_ctl; 27718c2ecf20Sopenharmony_ci s16 max_chan_pwr = ah->ah_txpower.txp_max_pwr / 4; 27728c2ecf20Sopenharmony_ci s16 edge_pwr = 0; 27738c2ecf20Sopenharmony_ci u8 rep_idx; 27748c2ecf20Sopenharmony_ci u8 i, ctl_mode; 27758c2ecf20Sopenharmony_ci u8 ctl_idx = 0xFF; 27768c2ecf20Sopenharmony_ci u32 target = channel->center_freq; 27778c2ecf20Sopenharmony_ci 27788c2ecf20Sopenharmony_ci ctl_mode = ath_regd_get_band_ctl(regulatory, channel->band); 27798c2ecf20Sopenharmony_ci 27808c2ecf20Sopenharmony_ci switch (channel->hw_value) { 27818c2ecf20Sopenharmony_ci case AR5K_MODE_11A: 27828c2ecf20Sopenharmony_ci if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) 27838c2ecf20Sopenharmony_ci ctl_mode |= AR5K_CTL_TURBO; 27848c2ecf20Sopenharmony_ci else 27858c2ecf20Sopenharmony_ci ctl_mode |= AR5K_CTL_11A; 27868c2ecf20Sopenharmony_ci break; 27878c2ecf20Sopenharmony_ci case AR5K_MODE_11G: 27888c2ecf20Sopenharmony_ci if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) 27898c2ecf20Sopenharmony_ci ctl_mode |= AR5K_CTL_TURBOG; 27908c2ecf20Sopenharmony_ci else 27918c2ecf20Sopenharmony_ci ctl_mode |= AR5K_CTL_11G; 27928c2ecf20Sopenharmony_ci break; 27938c2ecf20Sopenharmony_ci case AR5K_MODE_11B: 27948c2ecf20Sopenharmony_ci ctl_mode |= AR5K_CTL_11B; 27958c2ecf20Sopenharmony_ci break; 27968c2ecf20Sopenharmony_ci default: 27978c2ecf20Sopenharmony_ci return; 27988c2ecf20Sopenharmony_ci } 27998c2ecf20Sopenharmony_ci 28008c2ecf20Sopenharmony_ci for (i = 0; i < ee->ee_ctls; i++) { 28018c2ecf20Sopenharmony_ci if (ctl_val[i] == ctl_mode) { 28028c2ecf20Sopenharmony_ci ctl_idx = i; 28038c2ecf20Sopenharmony_ci break; 28048c2ecf20Sopenharmony_ci } 28058c2ecf20Sopenharmony_ci } 28068c2ecf20Sopenharmony_ci 28078c2ecf20Sopenharmony_ci /* If we have a CTL dataset available grab it and find the 28088c2ecf20Sopenharmony_ci * edge power for our frequency */ 28098c2ecf20Sopenharmony_ci if (ctl_idx == 0xFF) 28108c2ecf20Sopenharmony_ci return; 28118c2ecf20Sopenharmony_ci 28128c2ecf20Sopenharmony_ci /* Edge powers are sorted by frequency from lower 28138c2ecf20Sopenharmony_ci * to higher. Each CTL corresponds to 8 edge power 28148c2ecf20Sopenharmony_ci * measurements. */ 28158c2ecf20Sopenharmony_ci rep_idx = ctl_idx * AR5K_EEPROM_N_EDGES; 28168c2ecf20Sopenharmony_ci 28178c2ecf20Sopenharmony_ci /* Don't do boundaries check because we 28188c2ecf20Sopenharmony_ci * might have more that one bands defined 28198c2ecf20Sopenharmony_ci * for this mode */ 28208c2ecf20Sopenharmony_ci 28218c2ecf20Sopenharmony_ci /* Get the edge power that's closer to our 28228c2ecf20Sopenharmony_ci * frequency */ 28238c2ecf20Sopenharmony_ci for (i = 0; i < AR5K_EEPROM_N_EDGES; i++) { 28248c2ecf20Sopenharmony_ci rep_idx += i; 28258c2ecf20Sopenharmony_ci if (target <= rep[rep_idx].freq) 28268c2ecf20Sopenharmony_ci edge_pwr = (s16) rep[rep_idx].edge; 28278c2ecf20Sopenharmony_ci } 28288c2ecf20Sopenharmony_ci 28298c2ecf20Sopenharmony_ci if (edge_pwr) 28308c2ecf20Sopenharmony_ci ah->ah_txpower.txp_max_pwr = 4 * min(edge_pwr, max_chan_pwr); 28318c2ecf20Sopenharmony_ci} 28328c2ecf20Sopenharmony_ci 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_ci/* 28358c2ecf20Sopenharmony_ci * Power to PCDAC table functions 28368c2ecf20Sopenharmony_ci */ 28378c2ecf20Sopenharmony_ci 28388c2ecf20Sopenharmony_ci/** 28398c2ecf20Sopenharmony_ci * DOC: Power to PCDAC table functions 28408c2ecf20Sopenharmony_ci * 28418c2ecf20Sopenharmony_ci * For RF5111 we have an XPD -eXternal Power Detector- curve 28428c2ecf20Sopenharmony_ci * for each calibrated channel. Each curve has 0,5dB Power steps 28438c2ecf20Sopenharmony_ci * on x axis and PCDAC steps (offsets) on y axis and looks like an 28448c2ecf20Sopenharmony_ci * exponential function. To recreate the curve we read 11 points 28458c2ecf20Sopenharmony_ci * from eeprom (eeprom.c) and interpolate here. 28468c2ecf20Sopenharmony_ci * 28478c2ecf20Sopenharmony_ci * For RF5112 we have 4 XPD -eXternal Power Detector- curves 28488c2ecf20Sopenharmony_ci * for each calibrated channel on 0, -6, -12 and -18dBm but we only 28498c2ecf20Sopenharmony_ci * use the higher (3) and the lower (0) curves. Each curve again has 0.5dB 28508c2ecf20Sopenharmony_ci * power steps on x axis and PCDAC steps on y axis and looks like a 28518c2ecf20Sopenharmony_ci * linear function. To recreate the curve and pass the power values 28528c2ecf20Sopenharmony_ci * on hw, we get 4 points for xpd 0 (lower gain -> max power) 28538c2ecf20Sopenharmony_ci * and 3 points for xpd 3 (higher gain -> lower power) from eeprom (eeprom.c) 28548c2ecf20Sopenharmony_ci * and interpolate here. 28558c2ecf20Sopenharmony_ci * 28568c2ecf20Sopenharmony_ci * For a given channel we get the calibrated points (piers) for it or 28578c2ecf20Sopenharmony_ci * -if we don't have calibration data for this specific channel- from the 28588c2ecf20Sopenharmony_ci * available surrounding channels we have calibration data for, after we do a 28598c2ecf20Sopenharmony_ci * linear interpolation between them. Then since we have our calibrated points 28608c2ecf20Sopenharmony_ci * for this channel, we do again a linear interpolation between them to get the 28618c2ecf20Sopenharmony_ci * whole curve. 28628c2ecf20Sopenharmony_ci * 28638c2ecf20Sopenharmony_ci * We finally write the Y values of the curve(s) (the PCDAC values) on hw 28648c2ecf20Sopenharmony_ci */ 28658c2ecf20Sopenharmony_ci 28668c2ecf20Sopenharmony_ci/** 28678c2ecf20Sopenharmony_ci * ath5k_fill_pwr_to_pcdac_table() - Fill Power to PCDAC table on RF5111 28688c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 28698c2ecf20Sopenharmony_ci * @table_min: Minimum power (x min) 28708c2ecf20Sopenharmony_ci * @table_max: Maximum power (x max) 28718c2ecf20Sopenharmony_ci * 28728c2ecf20Sopenharmony_ci * No further processing is needed for RF5111, the only thing we have to 28738c2ecf20Sopenharmony_ci * do is fill the values below and above calibration range since eeprom data 28748c2ecf20Sopenharmony_ci * may not cover the entire PCDAC table. 28758c2ecf20Sopenharmony_ci */ 28768c2ecf20Sopenharmony_cistatic void 28778c2ecf20Sopenharmony_ciath5k_fill_pwr_to_pcdac_table(struct ath5k_hw *ah, s16* table_min, 28788c2ecf20Sopenharmony_ci s16 *table_max) 28798c2ecf20Sopenharmony_ci{ 28808c2ecf20Sopenharmony_ci u8 *pcdac_out = ah->ah_txpower.txp_pd_table; 28818c2ecf20Sopenharmony_ci u8 *pcdac_tmp = ah->ah_txpower.tmpL[0]; 28828c2ecf20Sopenharmony_ci u8 pcdac_0, pcdac_n, pcdac_i, pwr_idx, i; 28838c2ecf20Sopenharmony_ci s16 min_pwr, max_pwr; 28848c2ecf20Sopenharmony_ci 28858c2ecf20Sopenharmony_ci /* Get table boundaries */ 28868c2ecf20Sopenharmony_ci min_pwr = table_min[0]; 28878c2ecf20Sopenharmony_ci pcdac_0 = pcdac_tmp[0]; 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_ci max_pwr = table_max[0]; 28908c2ecf20Sopenharmony_ci pcdac_n = pcdac_tmp[table_max[0] - table_min[0]]; 28918c2ecf20Sopenharmony_ci 28928c2ecf20Sopenharmony_ci /* Extrapolate below minimum using pcdac_0 */ 28938c2ecf20Sopenharmony_ci pcdac_i = 0; 28948c2ecf20Sopenharmony_ci for (i = 0; i < min_pwr; i++) 28958c2ecf20Sopenharmony_ci pcdac_out[pcdac_i++] = pcdac_0; 28968c2ecf20Sopenharmony_ci 28978c2ecf20Sopenharmony_ci /* Copy values from pcdac_tmp */ 28988c2ecf20Sopenharmony_ci pwr_idx = min_pwr; 28998c2ecf20Sopenharmony_ci for (i = 0; pwr_idx <= max_pwr && 29008c2ecf20Sopenharmony_ci pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE; i++) { 29018c2ecf20Sopenharmony_ci pcdac_out[pcdac_i++] = pcdac_tmp[i]; 29028c2ecf20Sopenharmony_ci pwr_idx++; 29038c2ecf20Sopenharmony_ci } 29048c2ecf20Sopenharmony_ci 29058c2ecf20Sopenharmony_ci /* Extrapolate above maximum */ 29068c2ecf20Sopenharmony_ci while (pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE) 29078c2ecf20Sopenharmony_ci pcdac_out[pcdac_i++] = pcdac_n; 29088c2ecf20Sopenharmony_ci 29098c2ecf20Sopenharmony_ci} 29108c2ecf20Sopenharmony_ci 29118c2ecf20Sopenharmony_ci/** 29128c2ecf20Sopenharmony_ci * ath5k_combine_linear_pcdac_curves() - Combine available PCDAC Curves 29138c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 29148c2ecf20Sopenharmony_ci * @table_min: Minimum power (x min) 29158c2ecf20Sopenharmony_ci * @table_max: Maximum power (x max) 29168c2ecf20Sopenharmony_ci * @pdcurves: Number of pd curves 29178c2ecf20Sopenharmony_ci * 29188c2ecf20Sopenharmony_ci * Combine available XPD Curves and fill Linear Power to PCDAC table on RF5112 29198c2ecf20Sopenharmony_ci * RFX112 can have up to 2 curves (one for low txpower range and one for 29208c2ecf20Sopenharmony_ci * higher txpower range). We need to put them both on pcdac_out and place 29218c2ecf20Sopenharmony_ci * them in the correct location. In case we only have one curve available 29228c2ecf20Sopenharmony_ci * just fit it on pcdac_out (it's supposed to cover the entire range of 29238c2ecf20Sopenharmony_ci * available pwr levels since it's always the higher power curve). Extrapolate 29248c2ecf20Sopenharmony_ci * below and above final table if needed. 29258c2ecf20Sopenharmony_ci */ 29268c2ecf20Sopenharmony_cistatic void 29278c2ecf20Sopenharmony_ciath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min, 29288c2ecf20Sopenharmony_ci s16 *table_max, u8 pdcurves) 29298c2ecf20Sopenharmony_ci{ 29308c2ecf20Sopenharmony_ci u8 *pcdac_out = ah->ah_txpower.txp_pd_table; 29318c2ecf20Sopenharmony_ci u8 *pcdac_low_pwr; 29328c2ecf20Sopenharmony_ci u8 *pcdac_high_pwr; 29338c2ecf20Sopenharmony_ci u8 *pcdac_tmp; 29348c2ecf20Sopenharmony_ci u8 pwr; 29358c2ecf20Sopenharmony_ci s16 max_pwr_idx; 29368c2ecf20Sopenharmony_ci s16 min_pwr_idx; 29378c2ecf20Sopenharmony_ci s16 mid_pwr_idx = 0; 29388c2ecf20Sopenharmony_ci /* Edge flag turns on the 7nth bit on the PCDAC 29398c2ecf20Sopenharmony_ci * to declare the higher power curve (force values 29408c2ecf20Sopenharmony_ci * to be greater than 64). If we only have one curve 29418c2ecf20Sopenharmony_ci * we don't need to set this, if we have 2 curves and 29428c2ecf20Sopenharmony_ci * fill the table backwards this can also be used to 29438c2ecf20Sopenharmony_ci * switch from higher power curve to lower power curve */ 29448c2ecf20Sopenharmony_ci u8 edge_flag; 29458c2ecf20Sopenharmony_ci int i; 29468c2ecf20Sopenharmony_ci 29478c2ecf20Sopenharmony_ci /* When we have only one curve available 29488c2ecf20Sopenharmony_ci * that's the higher power curve. If we have 29498c2ecf20Sopenharmony_ci * two curves the first is the high power curve 29508c2ecf20Sopenharmony_ci * and the next is the low power curve. */ 29518c2ecf20Sopenharmony_ci if (pdcurves > 1) { 29528c2ecf20Sopenharmony_ci pcdac_low_pwr = ah->ah_txpower.tmpL[1]; 29538c2ecf20Sopenharmony_ci pcdac_high_pwr = ah->ah_txpower.tmpL[0]; 29548c2ecf20Sopenharmony_ci mid_pwr_idx = table_max[1] - table_min[1] - 1; 29558c2ecf20Sopenharmony_ci max_pwr_idx = (table_max[0] - table_min[0]) / 2; 29568c2ecf20Sopenharmony_ci 29578c2ecf20Sopenharmony_ci /* If table size goes beyond 31.5dB, keep the 29588c2ecf20Sopenharmony_ci * upper 31.5dB range when setting tx power. 29598c2ecf20Sopenharmony_ci * Note: 126 = 31.5 dB in quarter dB steps */ 29608c2ecf20Sopenharmony_ci if (table_max[0] - table_min[1] > 126) 29618c2ecf20Sopenharmony_ci min_pwr_idx = table_max[0] - 126; 29628c2ecf20Sopenharmony_ci else 29638c2ecf20Sopenharmony_ci min_pwr_idx = table_min[1]; 29648c2ecf20Sopenharmony_ci 29658c2ecf20Sopenharmony_ci /* Since we fill table backwards 29668c2ecf20Sopenharmony_ci * start from high power curve */ 29678c2ecf20Sopenharmony_ci pcdac_tmp = pcdac_high_pwr; 29688c2ecf20Sopenharmony_ci 29698c2ecf20Sopenharmony_ci edge_flag = 0x40; 29708c2ecf20Sopenharmony_ci } else { 29718c2ecf20Sopenharmony_ci pcdac_low_pwr = ah->ah_txpower.tmpL[1]; /* Zeroed */ 29728c2ecf20Sopenharmony_ci pcdac_high_pwr = ah->ah_txpower.tmpL[0]; 29738c2ecf20Sopenharmony_ci min_pwr_idx = table_min[0]; 29748c2ecf20Sopenharmony_ci max_pwr_idx = (table_max[0] - table_min[0]) / 2; 29758c2ecf20Sopenharmony_ci pcdac_tmp = pcdac_high_pwr; 29768c2ecf20Sopenharmony_ci edge_flag = 0; 29778c2ecf20Sopenharmony_ci } 29788c2ecf20Sopenharmony_ci 29798c2ecf20Sopenharmony_ci /* This is used when setting tx power*/ 29808c2ecf20Sopenharmony_ci ah->ah_txpower.txp_min_idx = min_pwr_idx / 2; 29818c2ecf20Sopenharmony_ci 29828c2ecf20Sopenharmony_ci /* Fill Power to PCDAC table backwards */ 29838c2ecf20Sopenharmony_ci pwr = max_pwr_idx; 29848c2ecf20Sopenharmony_ci for (i = 63; i >= 0; i--) { 29858c2ecf20Sopenharmony_ci /* Entering lower power range, reset 29868c2ecf20Sopenharmony_ci * edge flag and set pcdac_tmp to lower 29878c2ecf20Sopenharmony_ci * power curve.*/ 29888c2ecf20Sopenharmony_ci if (edge_flag == 0x40 && 29898c2ecf20Sopenharmony_ci (2 * pwr <= (table_max[1] - table_min[0]) || pwr == 0)) { 29908c2ecf20Sopenharmony_ci edge_flag = 0x00; 29918c2ecf20Sopenharmony_ci pcdac_tmp = pcdac_low_pwr; 29928c2ecf20Sopenharmony_ci pwr = mid_pwr_idx / 2; 29938c2ecf20Sopenharmony_ci } 29948c2ecf20Sopenharmony_ci 29958c2ecf20Sopenharmony_ci /* Don't go below 1, extrapolate below if we have 29968c2ecf20Sopenharmony_ci * already switched to the lower power curve -or 29978c2ecf20Sopenharmony_ci * we only have one curve and edge_flag is zero 29988c2ecf20Sopenharmony_ci * anyway */ 29998c2ecf20Sopenharmony_ci if (pcdac_tmp[pwr] < 1 && (edge_flag == 0x00)) { 30008c2ecf20Sopenharmony_ci while (i >= 0) { 30018c2ecf20Sopenharmony_ci pcdac_out[i] = pcdac_out[i + 1]; 30028c2ecf20Sopenharmony_ci i--; 30038c2ecf20Sopenharmony_ci } 30048c2ecf20Sopenharmony_ci break; 30058c2ecf20Sopenharmony_ci } 30068c2ecf20Sopenharmony_ci 30078c2ecf20Sopenharmony_ci pcdac_out[i] = pcdac_tmp[pwr] | edge_flag; 30088c2ecf20Sopenharmony_ci 30098c2ecf20Sopenharmony_ci /* Extrapolate above if pcdac is greater than 30108c2ecf20Sopenharmony_ci * 126 -this can happen because we OR pcdac_out 30118c2ecf20Sopenharmony_ci * value with edge_flag on high power curve */ 30128c2ecf20Sopenharmony_ci if (pcdac_out[i] > 126) 30138c2ecf20Sopenharmony_ci pcdac_out[i] = 126; 30148c2ecf20Sopenharmony_ci 30158c2ecf20Sopenharmony_ci /* Decrease by a 0.5dB step */ 30168c2ecf20Sopenharmony_ci pwr--; 30178c2ecf20Sopenharmony_ci } 30188c2ecf20Sopenharmony_ci} 30198c2ecf20Sopenharmony_ci 30208c2ecf20Sopenharmony_ci/** 30218c2ecf20Sopenharmony_ci * ath5k_write_pcdac_table() - Write the PCDAC values on hw 30228c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 30238c2ecf20Sopenharmony_ci */ 30248c2ecf20Sopenharmony_cistatic void 30258c2ecf20Sopenharmony_ciath5k_write_pcdac_table(struct ath5k_hw *ah) 30268c2ecf20Sopenharmony_ci{ 30278c2ecf20Sopenharmony_ci u8 *pcdac_out = ah->ah_txpower.txp_pd_table; 30288c2ecf20Sopenharmony_ci int i; 30298c2ecf20Sopenharmony_ci 30308c2ecf20Sopenharmony_ci /* 30318c2ecf20Sopenharmony_ci * Write TX power values 30328c2ecf20Sopenharmony_ci */ 30338c2ecf20Sopenharmony_ci for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) { 30348c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 30358c2ecf20Sopenharmony_ci (((pcdac_out[2 * i + 0] << 8 | 0xff) & 0xffff) << 0) | 30368c2ecf20Sopenharmony_ci (((pcdac_out[2 * i + 1] << 8 | 0xff) & 0xffff) << 16), 30378c2ecf20Sopenharmony_ci AR5K_PHY_PCDAC_TXPOWER(i)); 30388c2ecf20Sopenharmony_ci } 30398c2ecf20Sopenharmony_ci} 30408c2ecf20Sopenharmony_ci 30418c2ecf20Sopenharmony_ci 30428c2ecf20Sopenharmony_ci/* 30438c2ecf20Sopenharmony_ci * Power to PDADC table functions 30448c2ecf20Sopenharmony_ci */ 30458c2ecf20Sopenharmony_ci 30468c2ecf20Sopenharmony_ci/** 30478c2ecf20Sopenharmony_ci * DOC: Power to PDADC table functions 30488c2ecf20Sopenharmony_ci * 30498c2ecf20Sopenharmony_ci * For RF2413 and later we have a Power to PDADC table (Power Detector) 30508c2ecf20Sopenharmony_ci * instead of a PCDAC (Power Control) and 4 pd gain curves for each 30518c2ecf20Sopenharmony_ci * calibrated channel. Each curve has power on x axis in 0.5 db steps and 30528c2ecf20Sopenharmony_ci * PDADC steps on y axis and looks like an exponential function like the 30538c2ecf20Sopenharmony_ci * RF5111 curve. 30548c2ecf20Sopenharmony_ci * 30558c2ecf20Sopenharmony_ci * To recreate the curves we read the points from eeprom (eeprom.c) 30568c2ecf20Sopenharmony_ci * and interpolate here. Note that in most cases only 2 (higher and lower) 30578c2ecf20Sopenharmony_ci * curves are used (like RF5112) but vendors have the opportunity to include 30588c2ecf20Sopenharmony_ci * all 4 curves on eeprom. The final curve (higher power) has an extra 30598c2ecf20Sopenharmony_ci * point for better accuracy like RF5112. 30608c2ecf20Sopenharmony_ci * 30618c2ecf20Sopenharmony_ci * The process is similar to what we do above for RF5111/5112 30628c2ecf20Sopenharmony_ci */ 30638c2ecf20Sopenharmony_ci 30648c2ecf20Sopenharmony_ci/** 30658c2ecf20Sopenharmony_ci * ath5k_combine_pwr_to_pdadc_curves() - Combine the various PDADC curves 30668c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 30678c2ecf20Sopenharmony_ci * @pwr_min: Minimum power (x min) 30688c2ecf20Sopenharmony_ci * @pwr_max: Maximum power (x max) 30698c2ecf20Sopenharmony_ci * @pdcurves: Number of available curves 30708c2ecf20Sopenharmony_ci * 30718c2ecf20Sopenharmony_ci * Combine the various pd curves and create the final Power to PDADC table 30728c2ecf20Sopenharmony_ci * We can have up to 4 pd curves, we need to do a similar process 30738c2ecf20Sopenharmony_ci * as we do for RF5112. This time we don't have an edge_flag but we 30748c2ecf20Sopenharmony_ci * set the gain boundaries on a separate register. 30758c2ecf20Sopenharmony_ci */ 30768c2ecf20Sopenharmony_cistatic void 30778c2ecf20Sopenharmony_ciath5k_combine_pwr_to_pdadc_curves(struct ath5k_hw *ah, 30788c2ecf20Sopenharmony_ci s16 *pwr_min, s16 *pwr_max, u8 pdcurves) 30798c2ecf20Sopenharmony_ci{ 30808c2ecf20Sopenharmony_ci u8 gain_boundaries[AR5K_EEPROM_N_PD_GAINS]; 30818c2ecf20Sopenharmony_ci u8 *pdadc_out = ah->ah_txpower.txp_pd_table; 30828c2ecf20Sopenharmony_ci u8 *pdadc_tmp; 30838c2ecf20Sopenharmony_ci s16 pdadc_0; 30848c2ecf20Sopenharmony_ci u8 pdadc_i, pdadc_n, pwr_step, pdg, max_idx, table_size; 30858c2ecf20Sopenharmony_ci u8 pd_gain_overlap; 30868c2ecf20Sopenharmony_ci 30878c2ecf20Sopenharmony_ci /* Note: Register value is initialized on initvals 30888c2ecf20Sopenharmony_ci * there is no feedback from hw. 30898c2ecf20Sopenharmony_ci * XXX: What about pd_gain_overlap from EEPROM ? */ 30908c2ecf20Sopenharmony_ci pd_gain_overlap = (u8) ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG5) & 30918c2ecf20Sopenharmony_ci AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP; 30928c2ecf20Sopenharmony_ci 30938c2ecf20Sopenharmony_ci /* Create final PDADC table */ 30948c2ecf20Sopenharmony_ci for (pdg = 0, pdadc_i = 0; pdg < pdcurves; pdg++) { 30958c2ecf20Sopenharmony_ci pdadc_tmp = ah->ah_txpower.tmpL[pdg]; 30968c2ecf20Sopenharmony_ci 30978c2ecf20Sopenharmony_ci if (pdg == pdcurves - 1) 30988c2ecf20Sopenharmony_ci /* 2 dB boundary stretch for last 30998c2ecf20Sopenharmony_ci * (higher power) curve */ 31008c2ecf20Sopenharmony_ci gain_boundaries[pdg] = pwr_max[pdg] + 4; 31018c2ecf20Sopenharmony_ci else 31028c2ecf20Sopenharmony_ci /* Set gain boundary in the middle 31038c2ecf20Sopenharmony_ci * between this curve and the next one */ 31048c2ecf20Sopenharmony_ci gain_boundaries[pdg] = 31058c2ecf20Sopenharmony_ci (pwr_max[pdg] + pwr_min[pdg + 1]) / 2; 31068c2ecf20Sopenharmony_ci 31078c2ecf20Sopenharmony_ci /* Sanity check in case our 2 db stretch got out of 31088c2ecf20Sopenharmony_ci * range. */ 31098c2ecf20Sopenharmony_ci if (gain_boundaries[pdg] > AR5K_TUNE_MAX_TXPOWER) 31108c2ecf20Sopenharmony_ci gain_boundaries[pdg] = AR5K_TUNE_MAX_TXPOWER; 31118c2ecf20Sopenharmony_ci 31128c2ecf20Sopenharmony_ci /* For the first curve (lower power) 31138c2ecf20Sopenharmony_ci * start from 0 dB */ 31148c2ecf20Sopenharmony_ci if (pdg == 0) 31158c2ecf20Sopenharmony_ci pdadc_0 = 0; 31168c2ecf20Sopenharmony_ci else 31178c2ecf20Sopenharmony_ci /* For the other curves use the gain overlap */ 31188c2ecf20Sopenharmony_ci pdadc_0 = (gain_boundaries[pdg - 1] - pwr_min[pdg]) - 31198c2ecf20Sopenharmony_ci pd_gain_overlap; 31208c2ecf20Sopenharmony_ci 31218c2ecf20Sopenharmony_ci /* Force each power step to be at least 0.5 dB */ 31228c2ecf20Sopenharmony_ci if ((pdadc_tmp[1] - pdadc_tmp[0]) > 1) 31238c2ecf20Sopenharmony_ci pwr_step = pdadc_tmp[1] - pdadc_tmp[0]; 31248c2ecf20Sopenharmony_ci else 31258c2ecf20Sopenharmony_ci pwr_step = 1; 31268c2ecf20Sopenharmony_ci 31278c2ecf20Sopenharmony_ci /* If pdadc_0 is negative, we need to extrapolate 31288c2ecf20Sopenharmony_ci * below this pdgain by a number of pwr_steps */ 31298c2ecf20Sopenharmony_ci while ((pdadc_0 < 0) && (pdadc_i < 128)) { 31308c2ecf20Sopenharmony_ci s16 tmp = pdadc_tmp[0] + pdadc_0 * pwr_step; 31318c2ecf20Sopenharmony_ci pdadc_out[pdadc_i++] = (tmp < 0) ? 0 : (u8) tmp; 31328c2ecf20Sopenharmony_ci pdadc_0++; 31338c2ecf20Sopenharmony_ci } 31348c2ecf20Sopenharmony_ci 31358c2ecf20Sopenharmony_ci /* Set last pwr level, using gain boundaries */ 31368c2ecf20Sopenharmony_ci pdadc_n = gain_boundaries[pdg] + pd_gain_overlap - pwr_min[pdg]; 31378c2ecf20Sopenharmony_ci /* Limit it to be inside pwr range */ 31388c2ecf20Sopenharmony_ci table_size = pwr_max[pdg] - pwr_min[pdg]; 31398c2ecf20Sopenharmony_ci max_idx = (pdadc_n < table_size) ? pdadc_n : table_size; 31408c2ecf20Sopenharmony_ci 31418c2ecf20Sopenharmony_ci /* Fill pdadc_out table */ 31428c2ecf20Sopenharmony_ci while (pdadc_0 < max_idx && pdadc_i < 128) 31438c2ecf20Sopenharmony_ci pdadc_out[pdadc_i++] = pdadc_tmp[pdadc_0++]; 31448c2ecf20Sopenharmony_ci 31458c2ecf20Sopenharmony_ci /* Need to extrapolate above this pdgain? */ 31468c2ecf20Sopenharmony_ci if (pdadc_n <= max_idx) 31478c2ecf20Sopenharmony_ci continue; 31488c2ecf20Sopenharmony_ci 31498c2ecf20Sopenharmony_ci /* Force each power step to be at least 0.5 dB */ 31508c2ecf20Sopenharmony_ci if ((pdadc_tmp[table_size - 1] - pdadc_tmp[table_size - 2]) > 1) 31518c2ecf20Sopenharmony_ci pwr_step = pdadc_tmp[table_size - 1] - 31528c2ecf20Sopenharmony_ci pdadc_tmp[table_size - 2]; 31538c2ecf20Sopenharmony_ci else 31548c2ecf20Sopenharmony_ci pwr_step = 1; 31558c2ecf20Sopenharmony_ci 31568c2ecf20Sopenharmony_ci /* Extrapolate above */ 31578c2ecf20Sopenharmony_ci while ((pdadc_0 < (s16) pdadc_n) && 31588c2ecf20Sopenharmony_ci (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2)) { 31598c2ecf20Sopenharmony_ci s16 tmp = pdadc_tmp[table_size - 1] + 31608c2ecf20Sopenharmony_ci (pdadc_0 - max_idx) * pwr_step; 31618c2ecf20Sopenharmony_ci pdadc_out[pdadc_i++] = (tmp > 127) ? 127 : (u8) tmp; 31628c2ecf20Sopenharmony_ci pdadc_0++; 31638c2ecf20Sopenharmony_ci } 31648c2ecf20Sopenharmony_ci } 31658c2ecf20Sopenharmony_ci 31668c2ecf20Sopenharmony_ci while (pdg < AR5K_EEPROM_N_PD_GAINS) { 31678c2ecf20Sopenharmony_ci gain_boundaries[pdg] = gain_boundaries[pdg - 1]; 31688c2ecf20Sopenharmony_ci pdg++; 31698c2ecf20Sopenharmony_ci } 31708c2ecf20Sopenharmony_ci 31718c2ecf20Sopenharmony_ci while (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2) { 31728c2ecf20Sopenharmony_ci pdadc_out[pdadc_i] = pdadc_out[pdadc_i - 1]; 31738c2ecf20Sopenharmony_ci pdadc_i++; 31748c2ecf20Sopenharmony_ci } 31758c2ecf20Sopenharmony_ci 31768c2ecf20Sopenharmony_ci /* Set gain boundaries */ 31778c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 31788c2ecf20Sopenharmony_ci AR5K_REG_SM(pd_gain_overlap, 31798c2ecf20Sopenharmony_ci AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP) | 31808c2ecf20Sopenharmony_ci AR5K_REG_SM(gain_boundaries[0], 31818c2ecf20Sopenharmony_ci AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1) | 31828c2ecf20Sopenharmony_ci AR5K_REG_SM(gain_boundaries[1], 31838c2ecf20Sopenharmony_ci AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2) | 31848c2ecf20Sopenharmony_ci AR5K_REG_SM(gain_boundaries[2], 31858c2ecf20Sopenharmony_ci AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3) | 31868c2ecf20Sopenharmony_ci AR5K_REG_SM(gain_boundaries[3], 31878c2ecf20Sopenharmony_ci AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4), 31888c2ecf20Sopenharmony_ci AR5K_PHY_TPC_RG5); 31898c2ecf20Sopenharmony_ci 31908c2ecf20Sopenharmony_ci /* Used for setting rate power table */ 31918c2ecf20Sopenharmony_ci ah->ah_txpower.txp_min_idx = pwr_min[0]; 31928c2ecf20Sopenharmony_ci 31938c2ecf20Sopenharmony_ci} 31948c2ecf20Sopenharmony_ci 31958c2ecf20Sopenharmony_ci/** 31968c2ecf20Sopenharmony_ci * ath5k_write_pwr_to_pdadc_table() - Write the PDADC values on hw 31978c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 31988c2ecf20Sopenharmony_ci * @ee_mode: One of enum ath5k_driver_mode 31998c2ecf20Sopenharmony_ci */ 32008c2ecf20Sopenharmony_cistatic void 32018c2ecf20Sopenharmony_ciath5k_write_pwr_to_pdadc_table(struct ath5k_hw *ah, u8 ee_mode) 32028c2ecf20Sopenharmony_ci{ 32038c2ecf20Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 32048c2ecf20Sopenharmony_ci u8 *pdadc_out = ah->ah_txpower.txp_pd_table; 32058c2ecf20Sopenharmony_ci u8 *pdg_to_idx = ee->ee_pdc_to_idx[ee_mode]; 32068c2ecf20Sopenharmony_ci u8 pdcurves = ee->ee_pd_gains[ee_mode]; 32078c2ecf20Sopenharmony_ci u32 reg; 32088c2ecf20Sopenharmony_ci u8 i; 32098c2ecf20Sopenharmony_ci 32108c2ecf20Sopenharmony_ci /* Select the right pdgain curves */ 32118c2ecf20Sopenharmony_ci 32128c2ecf20Sopenharmony_ci /* Clear current settings */ 32138c2ecf20Sopenharmony_ci reg = ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG1); 32148c2ecf20Sopenharmony_ci reg &= ~(AR5K_PHY_TPC_RG1_PDGAIN_1 | 32158c2ecf20Sopenharmony_ci AR5K_PHY_TPC_RG1_PDGAIN_2 | 32168c2ecf20Sopenharmony_ci AR5K_PHY_TPC_RG1_PDGAIN_3 | 32178c2ecf20Sopenharmony_ci AR5K_PHY_TPC_RG1_NUM_PD_GAIN); 32188c2ecf20Sopenharmony_ci 32198c2ecf20Sopenharmony_ci /* 32208c2ecf20Sopenharmony_ci * Use pd_gains curve from eeprom 32218c2ecf20Sopenharmony_ci * 32228c2ecf20Sopenharmony_ci * This overrides the default setting from initvals 32238c2ecf20Sopenharmony_ci * in case some vendors (e.g. Zcomax) don't use the default 32248c2ecf20Sopenharmony_ci * curves. If we don't honor their settings we 'll get a 32258c2ecf20Sopenharmony_ci * 5dB (1 * gain overlap ?) drop. 32268c2ecf20Sopenharmony_ci */ 32278c2ecf20Sopenharmony_ci reg |= AR5K_REG_SM(pdcurves, AR5K_PHY_TPC_RG1_NUM_PD_GAIN); 32288c2ecf20Sopenharmony_ci 32298c2ecf20Sopenharmony_ci switch (pdcurves) { 32308c2ecf20Sopenharmony_ci case 3: 32318c2ecf20Sopenharmony_ci reg |= AR5K_REG_SM(pdg_to_idx[2], AR5K_PHY_TPC_RG1_PDGAIN_3); 32328c2ecf20Sopenharmony_ci fallthrough; 32338c2ecf20Sopenharmony_ci case 2: 32348c2ecf20Sopenharmony_ci reg |= AR5K_REG_SM(pdg_to_idx[1], AR5K_PHY_TPC_RG1_PDGAIN_2); 32358c2ecf20Sopenharmony_ci fallthrough; 32368c2ecf20Sopenharmony_ci case 1: 32378c2ecf20Sopenharmony_ci reg |= AR5K_REG_SM(pdg_to_idx[0], AR5K_PHY_TPC_RG1_PDGAIN_1); 32388c2ecf20Sopenharmony_ci break; 32398c2ecf20Sopenharmony_ci } 32408c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, reg, AR5K_PHY_TPC_RG1); 32418c2ecf20Sopenharmony_ci 32428c2ecf20Sopenharmony_ci /* 32438c2ecf20Sopenharmony_ci * Write TX power values 32448c2ecf20Sopenharmony_ci */ 32458c2ecf20Sopenharmony_ci for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) { 32468c2ecf20Sopenharmony_ci u32 val = get_unaligned_le32(&pdadc_out[4 * i]); 32478c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, val, AR5K_PHY_PDADC_TXPOWER(i)); 32488c2ecf20Sopenharmony_ci } 32498c2ecf20Sopenharmony_ci} 32508c2ecf20Sopenharmony_ci 32518c2ecf20Sopenharmony_ci 32528c2ecf20Sopenharmony_ci/* 32538c2ecf20Sopenharmony_ci * Common code for PCDAC/PDADC tables 32548c2ecf20Sopenharmony_ci */ 32558c2ecf20Sopenharmony_ci 32568c2ecf20Sopenharmony_ci/** 32578c2ecf20Sopenharmony_ci * ath5k_setup_channel_powertable() - Set up power table for this channel 32588c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 32598c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 32608c2ecf20Sopenharmony_ci * @ee_mode: One of enum ath5k_driver_mode 32618c2ecf20Sopenharmony_ci * @type: One of enum ath5k_powertable_type (eeprom.h) 32628c2ecf20Sopenharmony_ci * 32638c2ecf20Sopenharmony_ci * This is the main function that uses all of the above 32648c2ecf20Sopenharmony_ci * to set PCDAC/PDADC table on hw for the current channel. 32658c2ecf20Sopenharmony_ci * This table is used for tx power calibration on the baseband, 32668c2ecf20Sopenharmony_ci * without it we get weird tx power levels and in some cases 32678c2ecf20Sopenharmony_ci * distorted spectral mask 32688c2ecf20Sopenharmony_ci */ 32698c2ecf20Sopenharmony_cistatic int 32708c2ecf20Sopenharmony_ciath5k_setup_channel_powertable(struct ath5k_hw *ah, 32718c2ecf20Sopenharmony_ci struct ieee80211_channel *channel, 32728c2ecf20Sopenharmony_ci u8 ee_mode, u8 type) 32738c2ecf20Sopenharmony_ci{ 32748c2ecf20Sopenharmony_ci struct ath5k_pdgain_info *pdg_L, *pdg_R; 32758c2ecf20Sopenharmony_ci struct ath5k_chan_pcal_info *pcinfo_L; 32768c2ecf20Sopenharmony_ci struct ath5k_chan_pcal_info *pcinfo_R; 32778c2ecf20Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 32788c2ecf20Sopenharmony_ci u8 *pdg_curve_to_idx = ee->ee_pdc_to_idx[ee_mode]; 32798c2ecf20Sopenharmony_ci s16 table_min[AR5K_EEPROM_N_PD_GAINS]; 32808c2ecf20Sopenharmony_ci s16 table_max[AR5K_EEPROM_N_PD_GAINS]; 32818c2ecf20Sopenharmony_ci u8 *tmpL; 32828c2ecf20Sopenharmony_ci u8 *tmpR; 32838c2ecf20Sopenharmony_ci u32 target = channel->center_freq; 32848c2ecf20Sopenharmony_ci int pdg, i; 32858c2ecf20Sopenharmony_ci 32868c2ecf20Sopenharmony_ci /* Get surrounding freq piers for this channel */ 32878c2ecf20Sopenharmony_ci ath5k_get_chan_pcal_surrounding_piers(ah, channel, 32888c2ecf20Sopenharmony_ci &pcinfo_L, 32898c2ecf20Sopenharmony_ci &pcinfo_R); 32908c2ecf20Sopenharmony_ci 32918c2ecf20Sopenharmony_ci /* Loop over pd gain curves on 32928c2ecf20Sopenharmony_ci * surrounding freq piers by index */ 32938c2ecf20Sopenharmony_ci for (pdg = 0; pdg < ee->ee_pd_gains[ee_mode]; pdg++) { 32948c2ecf20Sopenharmony_ci 32958c2ecf20Sopenharmony_ci /* Fill curves in reverse order 32968c2ecf20Sopenharmony_ci * from lower power (max gain) 32978c2ecf20Sopenharmony_ci * to higher power. Use curve -> idx 32988c2ecf20Sopenharmony_ci * backmapping we did on eeprom init */ 32998c2ecf20Sopenharmony_ci u8 idx = pdg_curve_to_idx[pdg]; 33008c2ecf20Sopenharmony_ci 33018c2ecf20Sopenharmony_ci /* Grab the needed curves by index */ 33028c2ecf20Sopenharmony_ci pdg_L = &pcinfo_L->pd_curves[idx]; 33038c2ecf20Sopenharmony_ci pdg_R = &pcinfo_R->pd_curves[idx]; 33048c2ecf20Sopenharmony_ci 33058c2ecf20Sopenharmony_ci /* Initialize the temp tables */ 33068c2ecf20Sopenharmony_ci tmpL = ah->ah_txpower.tmpL[pdg]; 33078c2ecf20Sopenharmony_ci tmpR = ah->ah_txpower.tmpR[pdg]; 33088c2ecf20Sopenharmony_ci 33098c2ecf20Sopenharmony_ci /* Set curve's x boundaries and create 33108c2ecf20Sopenharmony_ci * curves so that they cover the same 33118c2ecf20Sopenharmony_ci * range (if we don't do that one table 33128c2ecf20Sopenharmony_ci * will have values on some range and the 33138c2ecf20Sopenharmony_ci * other one won't have any so interpolation 33148c2ecf20Sopenharmony_ci * will fail) */ 33158c2ecf20Sopenharmony_ci table_min[pdg] = min(pdg_L->pd_pwr[0], 33168c2ecf20Sopenharmony_ci pdg_R->pd_pwr[0]) / 2; 33178c2ecf20Sopenharmony_ci 33188c2ecf20Sopenharmony_ci table_max[pdg] = max(pdg_L->pd_pwr[pdg_L->pd_points - 1], 33198c2ecf20Sopenharmony_ci pdg_R->pd_pwr[pdg_R->pd_points - 1]) / 2; 33208c2ecf20Sopenharmony_ci 33218c2ecf20Sopenharmony_ci /* Now create the curves on surrounding channels 33228c2ecf20Sopenharmony_ci * and interpolate if needed to get the final 33238c2ecf20Sopenharmony_ci * curve for this gain on this channel */ 33248c2ecf20Sopenharmony_ci switch (type) { 33258c2ecf20Sopenharmony_ci case AR5K_PWRTABLE_LINEAR_PCDAC: 33268c2ecf20Sopenharmony_ci /* Override min/max so that we don't loose 33278c2ecf20Sopenharmony_ci * accuracy (don't divide by 2) */ 33288c2ecf20Sopenharmony_ci table_min[pdg] = min(pdg_L->pd_pwr[0], 33298c2ecf20Sopenharmony_ci pdg_R->pd_pwr[0]); 33308c2ecf20Sopenharmony_ci 33318c2ecf20Sopenharmony_ci table_max[pdg] = 33328c2ecf20Sopenharmony_ci max(pdg_L->pd_pwr[pdg_L->pd_points - 1], 33338c2ecf20Sopenharmony_ci pdg_R->pd_pwr[pdg_R->pd_points - 1]); 33348c2ecf20Sopenharmony_ci 33358c2ecf20Sopenharmony_ci /* Override minimum so that we don't get 33368c2ecf20Sopenharmony_ci * out of bounds while extrapolating 33378c2ecf20Sopenharmony_ci * below. Don't do this when we have 2 33388c2ecf20Sopenharmony_ci * curves and we are on the high power curve 33398c2ecf20Sopenharmony_ci * because table_min is ok in this case */ 33408c2ecf20Sopenharmony_ci if (!(ee->ee_pd_gains[ee_mode] > 1 && pdg == 0)) { 33418c2ecf20Sopenharmony_ci 33428c2ecf20Sopenharmony_ci table_min[pdg] = 33438c2ecf20Sopenharmony_ci ath5k_get_linear_pcdac_min(pdg_L->pd_step, 33448c2ecf20Sopenharmony_ci pdg_R->pd_step, 33458c2ecf20Sopenharmony_ci pdg_L->pd_pwr, 33468c2ecf20Sopenharmony_ci pdg_R->pd_pwr); 33478c2ecf20Sopenharmony_ci 33488c2ecf20Sopenharmony_ci /* Don't go too low because we will 33498c2ecf20Sopenharmony_ci * miss the upper part of the curve. 33508c2ecf20Sopenharmony_ci * Note: 126 = 31.5dB (max power supported) 33518c2ecf20Sopenharmony_ci * in 0.25dB units */ 33528c2ecf20Sopenharmony_ci if (table_max[pdg] - table_min[pdg] > 126) 33538c2ecf20Sopenharmony_ci table_min[pdg] = table_max[pdg] - 126; 33548c2ecf20Sopenharmony_ci } 33558c2ecf20Sopenharmony_ci 33568c2ecf20Sopenharmony_ci fallthrough; 33578c2ecf20Sopenharmony_ci case AR5K_PWRTABLE_PWR_TO_PCDAC: 33588c2ecf20Sopenharmony_ci case AR5K_PWRTABLE_PWR_TO_PDADC: 33598c2ecf20Sopenharmony_ci 33608c2ecf20Sopenharmony_ci ath5k_create_power_curve(table_min[pdg], 33618c2ecf20Sopenharmony_ci table_max[pdg], 33628c2ecf20Sopenharmony_ci pdg_L->pd_pwr, 33638c2ecf20Sopenharmony_ci pdg_L->pd_step, 33648c2ecf20Sopenharmony_ci pdg_L->pd_points, tmpL, type); 33658c2ecf20Sopenharmony_ci 33668c2ecf20Sopenharmony_ci /* We are in a calibration 33678c2ecf20Sopenharmony_ci * pier, no need to interpolate 33688c2ecf20Sopenharmony_ci * between freq piers */ 33698c2ecf20Sopenharmony_ci if (pcinfo_L == pcinfo_R) 33708c2ecf20Sopenharmony_ci continue; 33718c2ecf20Sopenharmony_ci 33728c2ecf20Sopenharmony_ci ath5k_create_power_curve(table_min[pdg], 33738c2ecf20Sopenharmony_ci table_max[pdg], 33748c2ecf20Sopenharmony_ci pdg_R->pd_pwr, 33758c2ecf20Sopenharmony_ci pdg_R->pd_step, 33768c2ecf20Sopenharmony_ci pdg_R->pd_points, tmpR, type); 33778c2ecf20Sopenharmony_ci break; 33788c2ecf20Sopenharmony_ci default: 33798c2ecf20Sopenharmony_ci return -EINVAL; 33808c2ecf20Sopenharmony_ci } 33818c2ecf20Sopenharmony_ci 33828c2ecf20Sopenharmony_ci /* Interpolate between curves 33838c2ecf20Sopenharmony_ci * of surrounding freq piers to 33848c2ecf20Sopenharmony_ci * get the final curve for this 33858c2ecf20Sopenharmony_ci * pd gain. Re-use tmpL for interpolation 33868c2ecf20Sopenharmony_ci * output */ 33878c2ecf20Sopenharmony_ci for (i = 0; (i < (u16) (table_max[pdg] - table_min[pdg])) && 33888c2ecf20Sopenharmony_ci (i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) { 33898c2ecf20Sopenharmony_ci tmpL[i] = (u8) ath5k_get_interpolated_value(target, 33908c2ecf20Sopenharmony_ci (s16) pcinfo_L->freq, 33918c2ecf20Sopenharmony_ci (s16) pcinfo_R->freq, 33928c2ecf20Sopenharmony_ci (s16) tmpL[i], 33938c2ecf20Sopenharmony_ci (s16) tmpR[i]); 33948c2ecf20Sopenharmony_ci } 33958c2ecf20Sopenharmony_ci } 33968c2ecf20Sopenharmony_ci 33978c2ecf20Sopenharmony_ci /* Now we have a set of curves for this 33988c2ecf20Sopenharmony_ci * channel on tmpL (x range is table_max - table_min 33998c2ecf20Sopenharmony_ci * and y values are tmpL[pdg][]) sorted in the same 34008c2ecf20Sopenharmony_ci * order as EEPROM (because we've used the backmapping). 34018c2ecf20Sopenharmony_ci * So for RF5112 it's from higher power to lower power 34028c2ecf20Sopenharmony_ci * and for RF2413 it's from lower power to higher power. 34038c2ecf20Sopenharmony_ci * For RF5111 we only have one curve. */ 34048c2ecf20Sopenharmony_ci 34058c2ecf20Sopenharmony_ci /* Fill min and max power levels for this 34068c2ecf20Sopenharmony_ci * channel by interpolating the values on 34078c2ecf20Sopenharmony_ci * surrounding channels to complete the dataset */ 34088c2ecf20Sopenharmony_ci ah->ah_txpower.txp_min_pwr = ath5k_get_interpolated_value(target, 34098c2ecf20Sopenharmony_ci (s16) pcinfo_L->freq, 34108c2ecf20Sopenharmony_ci (s16) pcinfo_R->freq, 34118c2ecf20Sopenharmony_ci pcinfo_L->min_pwr, pcinfo_R->min_pwr); 34128c2ecf20Sopenharmony_ci 34138c2ecf20Sopenharmony_ci ah->ah_txpower.txp_max_pwr = ath5k_get_interpolated_value(target, 34148c2ecf20Sopenharmony_ci (s16) pcinfo_L->freq, 34158c2ecf20Sopenharmony_ci (s16) pcinfo_R->freq, 34168c2ecf20Sopenharmony_ci pcinfo_L->max_pwr, pcinfo_R->max_pwr); 34178c2ecf20Sopenharmony_ci 34188c2ecf20Sopenharmony_ci /* Fill PCDAC/PDADC table */ 34198c2ecf20Sopenharmony_ci switch (type) { 34208c2ecf20Sopenharmony_ci case AR5K_PWRTABLE_LINEAR_PCDAC: 34218c2ecf20Sopenharmony_ci /* For RF5112 we can have one or two curves 34228c2ecf20Sopenharmony_ci * and each curve covers a certain power lvl 34238c2ecf20Sopenharmony_ci * range so we need to do some more processing */ 34248c2ecf20Sopenharmony_ci ath5k_combine_linear_pcdac_curves(ah, table_min, table_max, 34258c2ecf20Sopenharmony_ci ee->ee_pd_gains[ee_mode]); 34268c2ecf20Sopenharmony_ci 34278c2ecf20Sopenharmony_ci /* Set txp.offset so that we can 34288c2ecf20Sopenharmony_ci * match max power value with max 34298c2ecf20Sopenharmony_ci * table index */ 34308c2ecf20Sopenharmony_ci ah->ah_txpower.txp_offset = 64 - (table_max[0] / 2); 34318c2ecf20Sopenharmony_ci break; 34328c2ecf20Sopenharmony_ci case AR5K_PWRTABLE_PWR_TO_PCDAC: 34338c2ecf20Sopenharmony_ci /* We are done for RF5111 since it has only 34348c2ecf20Sopenharmony_ci * one curve, just fit the curve on the table */ 34358c2ecf20Sopenharmony_ci ath5k_fill_pwr_to_pcdac_table(ah, table_min, table_max); 34368c2ecf20Sopenharmony_ci 34378c2ecf20Sopenharmony_ci /* No rate powertable adjustment for RF5111 */ 34388c2ecf20Sopenharmony_ci ah->ah_txpower.txp_min_idx = 0; 34398c2ecf20Sopenharmony_ci ah->ah_txpower.txp_offset = 0; 34408c2ecf20Sopenharmony_ci break; 34418c2ecf20Sopenharmony_ci case AR5K_PWRTABLE_PWR_TO_PDADC: 34428c2ecf20Sopenharmony_ci /* Set PDADC boundaries and fill 34438c2ecf20Sopenharmony_ci * final PDADC table */ 34448c2ecf20Sopenharmony_ci ath5k_combine_pwr_to_pdadc_curves(ah, table_min, table_max, 34458c2ecf20Sopenharmony_ci ee->ee_pd_gains[ee_mode]); 34468c2ecf20Sopenharmony_ci 34478c2ecf20Sopenharmony_ci /* Set txp.offset, note that table_min 34488c2ecf20Sopenharmony_ci * can be negative */ 34498c2ecf20Sopenharmony_ci ah->ah_txpower.txp_offset = table_min[0]; 34508c2ecf20Sopenharmony_ci break; 34518c2ecf20Sopenharmony_ci default: 34528c2ecf20Sopenharmony_ci return -EINVAL; 34538c2ecf20Sopenharmony_ci } 34548c2ecf20Sopenharmony_ci 34558c2ecf20Sopenharmony_ci ah->ah_txpower.txp_setup = true; 34568c2ecf20Sopenharmony_ci 34578c2ecf20Sopenharmony_ci return 0; 34588c2ecf20Sopenharmony_ci} 34598c2ecf20Sopenharmony_ci 34608c2ecf20Sopenharmony_ci/** 34618c2ecf20Sopenharmony_ci * ath5k_write_channel_powertable() - Set power table for current channel on hw 34628c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 34638c2ecf20Sopenharmony_ci * @ee_mode: One of enum ath5k_driver_mode 34648c2ecf20Sopenharmony_ci * @type: One of enum ath5k_powertable_type (eeprom.h) 34658c2ecf20Sopenharmony_ci */ 34668c2ecf20Sopenharmony_cistatic void 34678c2ecf20Sopenharmony_ciath5k_write_channel_powertable(struct ath5k_hw *ah, u8 ee_mode, u8 type) 34688c2ecf20Sopenharmony_ci{ 34698c2ecf20Sopenharmony_ci if (type == AR5K_PWRTABLE_PWR_TO_PDADC) 34708c2ecf20Sopenharmony_ci ath5k_write_pwr_to_pdadc_table(ah, ee_mode); 34718c2ecf20Sopenharmony_ci else 34728c2ecf20Sopenharmony_ci ath5k_write_pcdac_table(ah); 34738c2ecf20Sopenharmony_ci} 34748c2ecf20Sopenharmony_ci 34758c2ecf20Sopenharmony_ci 34768c2ecf20Sopenharmony_ci/** 34778c2ecf20Sopenharmony_ci * DOC: Per-rate tx power setting 34788c2ecf20Sopenharmony_ci * 34798c2ecf20Sopenharmony_ci * This is the code that sets the desired tx power limit (below 34808c2ecf20Sopenharmony_ci * maximum) on hw for each rate (we also have TPC that sets 34818c2ecf20Sopenharmony_ci * power per packet type). We do that by providing an index on the 34828c2ecf20Sopenharmony_ci * PCDAC/PDADC table we set up above, for each rate. 34838c2ecf20Sopenharmony_ci * 34848c2ecf20Sopenharmony_ci * For now we only limit txpower based on maximum tx power 34858c2ecf20Sopenharmony_ci * supported by hw (what's inside rate_info) + conformance test 34868c2ecf20Sopenharmony_ci * limits. We need to limit this even more, based on regulatory domain 34878c2ecf20Sopenharmony_ci * etc to be safe. Normally this is done from above so we don't care 34888c2ecf20Sopenharmony_ci * here, all we care is that the tx power we set will be O.K. 34898c2ecf20Sopenharmony_ci * for the hw (e.g. won't create noise on PA etc). 34908c2ecf20Sopenharmony_ci * 34918c2ecf20Sopenharmony_ci * Rate power table contains indices to PCDAC/PDADC table (0.5dB steps - 34928c2ecf20Sopenharmony_ci * x values) and is indexed as follows: 34938c2ecf20Sopenharmony_ci * rates[0] - rates[7] -> OFDM rates 34948c2ecf20Sopenharmony_ci * rates[8] - rates[14] -> CCK rates 34958c2ecf20Sopenharmony_ci * rates[15] -> XR rates (they all have the same power) 34968c2ecf20Sopenharmony_ci */ 34978c2ecf20Sopenharmony_ci 34988c2ecf20Sopenharmony_ci/** 34998c2ecf20Sopenharmony_ci * ath5k_setup_rate_powertable() - Set up rate power table for a given tx power 35008c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 35018c2ecf20Sopenharmony_ci * @max_pwr: The maximum tx power requested in 0.5dB steps 35028c2ecf20Sopenharmony_ci * @rate_info: The &struct ath5k_rate_pcal_info to fill 35038c2ecf20Sopenharmony_ci * @ee_mode: One of enum ath5k_driver_mode 35048c2ecf20Sopenharmony_ci */ 35058c2ecf20Sopenharmony_cistatic void 35068c2ecf20Sopenharmony_ciath5k_setup_rate_powertable(struct ath5k_hw *ah, u16 max_pwr, 35078c2ecf20Sopenharmony_ci struct ath5k_rate_pcal_info *rate_info, 35088c2ecf20Sopenharmony_ci u8 ee_mode) 35098c2ecf20Sopenharmony_ci{ 35108c2ecf20Sopenharmony_ci unsigned int i; 35118c2ecf20Sopenharmony_ci u16 *rates; 35128c2ecf20Sopenharmony_ci s16 rate_idx_scaled = 0; 35138c2ecf20Sopenharmony_ci 35148c2ecf20Sopenharmony_ci /* max_pwr is power level we got from driver/user in 0.5dB 35158c2ecf20Sopenharmony_ci * units, switch to 0.25dB units so we can compare */ 35168c2ecf20Sopenharmony_ci max_pwr *= 2; 35178c2ecf20Sopenharmony_ci max_pwr = min(max_pwr, (u16) ah->ah_txpower.txp_max_pwr) / 2; 35188c2ecf20Sopenharmony_ci 35198c2ecf20Sopenharmony_ci /* apply rate limits */ 35208c2ecf20Sopenharmony_ci rates = ah->ah_txpower.txp_rates_power_table; 35218c2ecf20Sopenharmony_ci 35228c2ecf20Sopenharmony_ci /* OFDM rates 6 to 24Mb/s */ 35238c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) 35248c2ecf20Sopenharmony_ci rates[i] = min(max_pwr, rate_info->target_power_6to24); 35258c2ecf20Sopenharmony_ci 35268c2ecf20Sopenharmony_ci /* Rest OFDM rates */ 35278c2ecf20Sopenharmony_ci rates[5] = min(rates[0], rate_info->target_power_36); 35288c2ecf20Sopenharmony_ci rates[6] = min(rates[0], rate_info->target_power_48); 35298c2ecf20Sopenharmony_ci rates[7] = min(rates[0], rate_info->target_power_54); 35308c2ecf20Sopenharmony_ci 35318c2ecf20Sopenharmony_ci /* CCK rates */ 35328c2ecf20Sopenharmony_ci /* 1L */ 35338c2ecf20Sopenharmony_ci rates[8] = min(rates[0], rate_info->target_power_6to24); 35348c2ecf20Sopenharmony_ci /* 2L */ 35358c2ecf20Sopenharmony_ci rates[9] = min(rates[0], rate_info->target_power_36); 35368c2ecf20Sopenharmony_ci /* 2S */ 35378c2ecf20Sopenharmony_ci rates[10] = min(rates[0], rate_info->target_power_36); 35388c2ecf20Sopenharmony_ci /* 5L */ 35398c2ecf20Sopenharmony_ci rates[11] = min(rates[0], rate_info->target_power_48); 35408c2ecf20Sopenharmony_ci /* 5S */ 35418c2ecf20Sopenharmony_ci rates[12] = min(rates[0], rate_info->target_power_48); 35428c2ecf20Sopenharmony_ci /* 11L */ 35438c2ecf20Sopenharmony_ci rates[13] = min(rates[0], rate_info->target_power_54); 35448c2ecf20Sopenharmony_ci /* 11S */ 35458c2ecf20Sopenharmony_ci rates[14] = min(rates[0], rate_info->target_power_54); 35468c2ecf20Sopenharmony_ci 35478c2ecf20Sopenharmony_ci /* XR rates */ 35488c2ecf20Sopenharmony_ci rates[15] = min(rates[0], rate_info->target_power_6to24); 35498c2ecf20Sopenharmony_ci 35508c2ecf20Sopenharmony_ci /* CCK rates have different peak to average ratio 35518c2ecf20Sopenharmony_ci * so we have to tweak their power so that gainf 35528c2ecf20Sopenharmony_ci * correction works ok. For this we use OFDM to 35538c2ecf20Sopenharmony_ci * CCK delta from eeprom */ 35548c2ecf20Sopenharmony_ci if ((ee_mode == AR5K_EEPROM_MODE_11G) && 35558c2ecf20Sopenharmony_ci (ah->ah_phy_revision < AR5K_SREV_PHY_5212A)) 35568c2ecf20Sopenharmony_ci for (i = 8; i <= 15; i++) 35578c2ecf20Sopenharmony_ci rates[i] -= ah->ah_txpower.txp_cck_ofdm_gainf_delta; 35588c2ecf20Sopenharmony_ci 35598c2ecf20Sopenharmony_ci /* Save min/max and current tx power for this channel 35608c2ecf20Sopenharmony_ci * in 0.25dB units. 35618c2ecf20Sopenharmony_ci * 35628c2ecf20Sopenharmony_ci * Note: We use rates[0] for current tx power because 35638c2ecf20Sopenharmony_ci * it covers most of the rates, in most cases. It's our 35648c2ecf20Sopenharmony_ci * tx power limit and what the user expects to see. */ 35658c2ecf20Sopenharmony_ci ah->ah_txpower.txp_min_pwr = 2 * rates[7]; 35668c2ecf20Sopenharmony_ci ah->ah_txpower.txp_cur_pwr = 2 * rates[0]; 35678c2ecf20Sopenharmony_ci 35688c2ecf20Sopenharmony_ci /* Set max txpower for correct OFDM operation on all rates 35698c2ecf20Sopenharmony_ci * -that is the txpower for 54Mbit-, it's used for the PAPD 35708c2ecf20Sopenharmony_ci * gain probe and it's in 0.5dB units */ 35718c2ecf20Sopenharmony_ci ah->ah_txpower.txp_ofdm = rates[7]; 35728c2ecf20Sopenharmony_ci 35738c2ecf20Sopenharmony_ci /* Now that we have all rates setup use table offset to 35748c2ecf20Sopenharmony_ci * match the power range set by user with the power indices 35758c2ecf20Sopenharmony_ci * on PCDAC/PDADC table */ 35768c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) { 35778c2ecf20Sopenharmony_ci rate_idx_scaled = rates[i] + ah->ah_txpower.txp_offset; 35788c2ecf20Sopenharmony_ci /* Don't get out of bounds */ 35798c2ecf20Sopenharmony_ci if (rate_idx_scaled > 63) 35808c2ecf20Sopenharmony_ci rate_idx_scaled = 63; 35818c2ecf20Sopenharmony_ci if (rate_idx_scaled < 0) 35828c2ecf20Sopenharmony_ci rate_idx_scaled = 0; 35838c2ecf20Sopenharmony_ci rates[i] = rate_idx_scaled; 35848c2ecf20Sopenharmony_ci } 35858c2ecf20Sopenharmony_ci} 35868c2ecf20Sopenharmony_ci 35878c2ecf20Sopenharmony_ci 35888c2ecf20Sopenharmony_ci/** 35898c2ecf20Sopenharmony_ci * ath5k_hw_txpower() - Set transmission power limit for a given channel 35908c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 35918c2ecf20Sopenharmony_ci * @channel: The &struct ieee80211_channel 35928c2ecf20Sopenharmony_ci * @txpower: Requested tx power in 0.5dB steps 35938c2ecf20Sopenharmony_ci * 35948c2ecf20Sopenharmony_ci * Combines all of the above to set the requested tx power limit 35958c2ecf20Sopenharmony_ci * on hw. 35968c2ecf20Sopenharmony_ci */ 35978c2ecf20Sopenharmony_cistatic int 35988c2ecf20Sopenharmony_ciath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, 35998c2ecf20Sopenharmony_ci u8 txpower) 36008c2ecf20Sopenharmony_ci{ 36018c2ecf20Sopenharmony_ci struct ath5k_rate_pcal_info rate_info; 36028c2ecf20Sopenharmony_ci struct ieee80211_channel *curr_channel = ah->ah_current_channel; 36038c2ecf20Sopenharmony_ci int ee_mode; 36048c2ecf20Sopenharmony_ci u8 type; 36058c2ecf20Sopenharmony_ci int ret; 36068c2ecf20Sopenharmony_ci 36078c2ecf20Sopenharmony_ci if (txpower > AR5K_TUNE_MAX_TXPOWER) { 36088c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "invalid tx power: %u\n", txpower); 36098c2ecf20Sopenharmony_ci return -EINVAL; 36108c2ecf20Sopenharmony_ci } 36118c2ecf20Sopenharmony_ci 36128c2ecf20Sopenharmony_ci ee_mode = ath5k_eeprom_mode_from_channel(ah, channel); 36138c2ecf20Sopenharmony_ci 36148c2ecf20Sopenharmony_ci /* Initialize TX power table */ 36158c2ecf20Sopenharmony_ci switch (ah->ah_radio) { 36168c2ecf20Sopenharmony_ci case AR5K_RF5110: 36178c2ecf20Sopenharmony_ci /* TODO */ 36188c2ecf20Sopenharmony_ci return 0; 36198c2ecf20Sopenharmony_ci case AR5K_RF5111: 36208c2ecf20Sopenharmony_ci type = AR5K_PWRTABLE_PWR_TO_PCDAC; 36218c2ecf20Sopenharmony_ci break; 36228c2ecf20Sopenharmony_ci case AR5K_RF5112: 36238c2ecf20Sopenharmony_ci type = AR5K_PWRTABLE_LINEAR_PCDAC; 36248c2ecf20Sopenharmony_ci break; 36258c2ecf20Sopenharmony_ci case AR5K_RF2413: 36268c2ecf20Sopenharmony_ci case AR5K_RF5413: 36278c2ecf20Sopenharmony_ci case AR5K_RF2316: 36288c2ecf20Sopenharmony_ci case AR5K_RF2317: 36298c2ecf20Sopenharmony_ci case AR5K_RF2425: 36308c2ecf20Sopenharmony_ci type = AR5K_PWRTABLE_PWR_TO_PDADC; 36318c2ecf20Sopenharmony_ci break; 36328c2ecf20Sopenharmony_ci default: 36338c2ecf20Sopenharmony_ci return -EINVAL; 36348c2ecf20Sopenharmony_ci } 36358c2ecf20Sopenharmony_ci 36368c2ecf20Sopenharmony_ci /* 36378c2ecf20Sopenharmony_ci * If we don't change channel/mode skip tx powertable calculation 36388c2ecf20Sopenharmony_ci * and use the cached one. 36398c2ecf20Sopenharmony_ci */ 36408c2ecf20Sopenharmony_ci if (!ah->ah_txpower.txp_setup || 36418c2ecf20Sopenharmony_ci (channel->hw_value != curr_channel->hw_value) || 36428c2ecf20Sopenharmony_ci (channel->center_freq != curr_channel->center_freq)) { 36438c2ecf20Sopenharmony_ci /* Reset TX power values but preserve requested 36448c2ecf20Sopenharmony_ci * tx power from above */ 36458c2ecf20Sopenharmony_ci int requested_txpower = ah->ah_txpower.txp_requested; 36468c2ecf20Sopenharmony_ci 36478c2ecf20Sopenharmony_ci memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower)); 36488c2ecf20Sopenharmony_ci 36498c2ecf20Sopenharmony_ci /* Restore TPC setting and requested tx power */ 36508c2ecf20Sopenharmony_ci ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER; 36518c2ecf20Sopenharmony_ci 36528c2ecf20Sopenharmony_ci ah->ah_txpower.txp_requested = requested_txpower; 36538c2ecf20Sopenharmony_ci 36548c2ecf20Sopenharmony_ci /* Calculate the powertable */ 36558c2ecf20Sopenharmony_ci ret = ath5k_setup_channel_powertable(ah, channel, 36568c2ecf20Sopenharmony_ci ee_mode, type); 36578c2ecf20Sopenharmony_ci if (ret) 36588c2ecf20Sopenharmony_ci return ret; 36598c2ecf20Sopenharmony_ci } 36608c2ecf20Sopenharmony_ci 36618c2ecf20Sopenharmony_ci /* Write table on hw */ 36628c2ecf20Sopenharmony_ci ath5k_write_channel_powertable(ah, ee_mode, type); 36638c2ecf20Sopenharmony_ci 36648c2ecf20Sopenharmony_ci /* Limit max power if we have a CTL available */ 36658c2ecf20Sopenharmony_ci ath5k_get_max_ctl_power(ah, channel); 36668c2ecf20Sopenharmony_ci 36678c2ecf20Sopenharmony_ci /* FIXME: Antenna reduction stuff */ 36688c2ecf20Sopenharmony_ci 36698c2ecf20Sopenharmony_ci /* FIXME: Limit power on turbo modes */ 36708c2ecf20Sopenharmony_ci 36718c2ecf20Sopenharmony_ci /* FIXME: TPC scale reduction */ 36728c2ecf20Sopenharmony_ci 36738c2ecf20Sopenharmony_ci /* Get surrounding channels for per-rate power table 36748c2ecf20Sopenharmony_ci * calibration */ 36758c2ecf20Sopenharmony_ci ath5k_get_rate_pcal_data(ah, channel, &rate_info); 36768c2ecf20Sopenharmony_ci 36778c2ecf20Sopenharmony_ci /* Setup rate power table */ 36788c2ecf20Sopenharmony_ci ath5k_setup_rate_powertable(ah, txpower, &rate_info, ee_mode); 36798c2ecf20Sopenharmony_ci 36808c2ecf20Sopenharmony_ci /* Write rate power table on hw */ 36818c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(3, 24) | 36828c2ecf20Sopenharmony_ci AR5K_TXPOWER_OFDM(2, 16) | AR5K_TXPOWER_OFDM(1, 8) | 36838c2ecf20Sopenharmony_ci AR5K_TXPOWER_OFDM(0, 0), AR5K_PHY_TXPOWER_RATE1); 36848c2ecf20Sopenharmony_ci 36858c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(7, 24) | 36868c2ecf20Sopenharmony_ci AR5K_TXPOWER_OFDM(6, 16) | AR5K_TXPOWER_OFDM(5, 8) | 36878c2ecf20Sopenharmony_ci AR5K_TXPOWER_OFDM(4, 0), AR5K_PHY_TXPOWER_RATE2); 36888c2ecf20Sopenharmony_ci 36898c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_TXPOWER_CCK(10, 24) | 36908c2ecf20Sopenharmony_ci AR5K_TXPOWER_CCK(9, 16) | AR5K_TXPOWER_CCK(15, 8) | 36918c2ecf20Sopenharmony_ci AR5K_TXPOWER_CCK(8, 0), AR5K_PHY_TXPOWER_RATE3); 36928c2ecf20Sopenharmony_ci 36938c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_TXPOWER_CCK(14, 24) | 36948c2ecf20Sopenharmony_ci AR5K_TXPOWER_CCK(13, 16) | AR5K_TXPOWER_CCK(12, 8) | 36958c2ecf20Sopenharmony_ci AR5K_TXPOWER_CCK(11, 0), AR5K_PHY_TXPOWER_RATE4); 36968c2ecf20Sopenharmony_ci 36978c2ecf20Sopenharmony_ci /* FIXME: TPC support */ 36988c2ecf20Sopenharmony_ci if (ah->ah_txpower.txp_tpc) { 36998c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE | 37008c2ecf20Sopenharmony_ci AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX); 37018c2ecf20Sopenharmony_ci 37028c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 37038c2ecf20Sopenharmony_ci AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_ACK) | 37048c2ecf20Sopenharmony_ci AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CTS) | 37058c2ecf20Sopenharmony_ci AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP), 37068c2ecf20Sopenharmony_ci AR5K_TPC); 37078c2ecf20Sopenharmony_ci } else { 37088c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_TUNE_MAX_TXPOWER, 37098c2ecf20Sopenharmony_ci AR5K_PHY_TXPOWER_RATE_MAX); 37108c2ecf20Sopenharmony_ci } 37118c2ecf20Sopenharmony_ci 37128c2ecf20Sopenharmony_ci return 0; 37138c2ecf20Sopenharmony_ci} 37148c2ecf20Sopenharmony_ci 37158c2ecf20Sopenharmony_ci/** 37168c2ecf20Sopenharmony_ci * ath5k_hw_set_txpower_limit() - Set txpower limit for the current channel 37178c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 37188c2ecf20Sopenharmony_ci * @txpower: The requested tx power limit in 0.5dB steps 37198c2ecf20Sopenharmony_ci * 37208c2ecf20Sopenharmony_ci * This function provides access to ath5k_hw_txpower to the driver in 37218c2ecf20Sopenharmony_ci * case user or an application changes it while PHY is running. 37228c2ecf20Sopenharmony_ci */ 37238c2ecf20Sopenharmony_ciint 37248c2ecf20Sopenharmony_ciath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower) 37258c2ecf20Sopenharmony_ci{ 37268c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_TXPOWER, 37278c2ecf20Sopenharmony_ci "changing txpower to %d\n", txpower); 37288c2ecf20Sopenharmony_ci 37298c2ecf20Sopenharmony_ci return ath5k_hw_txpower(ah, ah->ah_current_channel, txpower); 37308c2ecf20Sopenharmony_ci} 37318c2ecf20Sopenharmony_ci 37328c2ecf20Sopenharmony_ci 37338c2ecf20Sopenharmony_ci/*************\ 37348c2ecf20Sopenharmony_ci Init function 37358c2ecf20Sopenharmony_ci\*************/ 37368c2ecf20Sopenharmony_ci 37378c2ecf20Sopenharmony_ci/** 37388c2ecf20Sopenharmony_ci * ath5k_hw_phy_init() - Initialize PHY 37398c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 37408c2ecf20Sopenharmony_ci * @channel: The @struct ieee80211_channel 37418c2ecf20Sopenharmony_ci * @mode: One of enum ath5k_driver_mode 37428c2ecf20Sopenharmony_ci * @fast: Try a fast channel switch instead 37438c2ecf20Sopenharmony_ci * 37448c2ecf20Sopenharmony_ci * This is the main function used during reset to initialize PHY 37458c2ecf20Sopenharmony_ci * or do a fast channel change if possible. 37468c2ecf20Sopenharmony_ci * 37478c2ecf20Sopenharmony_ci * NOTE: Do not call this one from the driver, it assumes PHY is in a 37488c2ecf20Sopenharmony_ci * warm reset state ! 37498c2ecf20Sopenharmony_ci */ 37508c2ecf20Sopenharmony_ciint 37518c2ecf20Sopenharmony_ciath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel, 37528c2ecf20Sopenharmony_ci u8 mode, bool fast) 37538c2ecf20Sopenharmony_ci{ 37548c2ecf20Sopenharmony_ci struct ieee80211_channel *curr_channel; 37558c2ecf20Sopenharmony_ci int ret, i; 37568c2ecf20Sopenharmony_ci u32 phy_tst1; 37578c2ecf20Sopenharmony_ci ret = 0; 37588c2ecf20Sopenharmony_ci 37598c2ecf20Sopenharmony_ci /* 37608c2ecf20Sopenharmony_ci * Sanity check for fast flag 37618c2ecf20Sopenharmony_ci * Don't try fast channel change when changing modulation 37628c2ecf20Sopenharmony_ci * mode/band. We check for chip compatibility on 37638c2ecf20Sopenharmony_ci * ath5k_hw_reset. 37648c2ecf20Sopenharmony_ci */ 37658c2ecf20Sopenharmony_ci curr_channel = ah->ah_current_channel; 37668c2ecf20Sopenharmony_ci if (fast && (channel->hw_value != curr_channel->hw_value)) 37678c2ecf20Sopenharmony_ci return -EINVAL; 37688c2ecf20Sopenharmony_ci 37698c2ecf20Sopenharmony_ci /* 37708c2ecf20Sopenharmony_ci * On fast channel change we only set the synth parameters 37718c2ecf20Sopenharmony_ci * while PHY is running, enable calibration and skip the rest. 37728c2ecf20Sopenharmony_ci */ 37738c2ecf20Sopenharmony_ci if (fast) { 37748c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_RFBUS_REQ, 37758c2ecf20Sopenharmony_ci AR5K_PHY_RFBUS_REQ_REQUEST); 37768c2ecf20Sopenharmony_ci for (i = 0; i < 100; i++) { 37778c2ecf20Sopenharmony_ci if (ath5k_hw_reg_read(ah, AR5K_PHY_RFBUS_GRANT)) 37788c2ecf20Sopenharmony_ci break; 37798c2ecf20Sopenharmony_ci udelay(5); 37808c2ecf20Sopenharmony_ci } 37818c2ecf20Sopenharmony_ci /* Failed */ 37828c2ecf20Sopenharmony_ci if (i >= 100) 37838c2ecf20Sopenharmony_ci return -EIO; 37848c2ecf20Sopenharmony_ci 37858c2ecf20Sopenharmony_ci /* Set channel and wait for synth */ 37868c2ecf20Sopenharmony_ci ret = ath5k_hw_channel(ah, channel); 37878c2ecf20Sopenharmony_ci if (ret) 37888c2ecf20Sopenharmony_ci return ret; 37898c2ecf20Sopenharmony_ci 37908c2ecf20Sopenharmony_ci ath5k_hw_wait_for_synth(ah, channel); 37918c2ecf20Sopenharmony_ci } 37928c2ecf20Sopenharmony_ci 37938c2ecf20Sopenharmony_ci /* 37948c2ecf20Sopenharmony_ci * Set TX power 37958c2ecf20Sopenharmony_ci * 37968c2ecf20Sopenharmony_ci * Note: We need to do that before we set 37978c2ecf20Sopenharmony_ci * RF buffer settings on 5211/5212+ so that we 37988c2ecf20Sopenharmony_ci * properly set curve indices. 37998c2ecf20Sopenharmony_ci */ 38008c2ecf20Sopenharmony_ci ret = ath5k_hw_txpower(ah, channel, ah->ah_txpower.txp_requested ? 38018c2ecf20Sopenharmony_ci ah->ah_txpower.txp_requested * 2 : 38028c2ecf20Sopenharmony_ci AR5K_TUNE_MAX_TXPOWER); 38038c2ecf20Sopenharmony_ci if (ret) 38048c2ecf20Sopenharmony_ci return ret; 38058c2ecf20Sopenharmony_ci 38068c2ecf20Sopenharmony_ci /* Write OFDM timings on 5212*/ 38078c2ecf20Sopenharmony_ci if (ah->ah_version == AR5K_AR5212 && 38088c2ecf20Sopenharmony_ci channel->hw_value != AR5K_MODE_11B) { 38098c2ecf20Sopenharmony_ci 38108c2ecf20Sopenharmony_ci ret = ath5k_hw_write_ofdm_timings(ah, channel); 38118c2ecf20Sopenharmony_ci if (ret) 38128c2ecf20Sopenharmony_ci return ret; 38138c2ecf20Sopenharmony_ci 38148c2ecf20Sopenharmony_ci /* Spur info is available only from EEPROM versions 38158c2ecf20Sopenharmony_ci * greater than 5.3, but the EEPROM routines will use 38168c2ecf20Sopenharmony_ci * static values for older versions */ 38178c2ecf20Sopenharmony_ci if (ah->ah_mac_srev >= AR5K_SREV_AR5424) 38188c2ecf20Sopenharmony_ci ath5k_hw_set_spur_mitigation_filter(ah, 38198c2ecf20Sopenharmony_ci channel); 38208c2ecf20Sopenharmony_ci } 38218c2ecf20Sopenharmony_ci 38228c2ecf20Sopenharmony_ci /* If we used fast channel switching 38238c2ecf20Sopenharmony_ci * we are done, release RF bus and 38248c2ecf20Sopenharmony_ci * fire up NF calibration. 38258c2ecf20Sopenharmony_ci * 38268c2ecf20Sopenharmony_ci * Note: Only NF calibration due to 38278c2ecf20Sopenharmony_ci * channel change, not AGC calibration 38288c2ecf20Sopenharmony_ci * since AGC is still running ! 38298c2ecf20Sopenharmony_ci */ 38308c2ecf20Sopenharmony_ci if (fast) { 38318c2ecf20Sopenharmony_ci /* 38328c2ecf20Sopenharmony_ci * Release RF Bus grant 38338c2ecf20Sopenharmony_ci */ 38348c2ecf20Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_RFBUS_REQ, 38358c2ecf20Sopenharmony_ci AR5K_PHY_RFBUS_REQ_REQUEST); 38368c2ecf20Sopenharmony_ci 38378c2ecf20Sopenharmony_ci /* 38388c2ecf20Sopenharmony_ci * Start NF calibration 38398c2ecf20Sopenharmony_ci */ 38408c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, 38418c2ecf20Sopenharmony_ci AR5K_PHY_AGCCTL_NF); 38428c2ecf20Sopenharmony_ci 38438c2ecf20Sopenharmony_ci return ret; 38448c2ecf20Sopenharmony_ci } 38458c2ecf20Sopenharmony_ci 38468c2ecf20Sopenharmony_ci /* 38478c2ecf20Sopenharmony_ci * For 5210 we do all initialization using 38488c2ecf20Sopenharmony_ci * initvals, so we don't have to modify 38498c2ecf20Sopenharmony_ci * any settings (5210 also only supports 38508c2ecf20Sopenharmony_ci * a/aturbo modes) 38518c2ecf20Sopenharmony_ci */ 38528c2ecf20Sopenharmony_ci if (ah->ah_version != AR5K_AR5210) { 38538c2ecf20Sopenharmony_ci 38548c2ecf20Sopenharmony_ci /* 38558c2ecf20Sopenharmony_ci * Write initial RF gain settings 38568c2ecf20Sopenharmony_ci * This should work for both 5111/5112 38578c2ecf20Sopenharmony_ci */ 38588c2ecf20Sopenharmony_ci ret = ath5k_hw_rfgain_init(ah, channel->band); 38598c2ecf20Sopenharmony_ci if (ret) 38608c2ecf20Sopenharmony_ci return ret; 38618c2ecf20Sopenharmony_ci 38628c2ecf20Sopenharmony_ci usleep_range(1000, 1500); 38638c2ecf20Sopenharmony_ci 38648c2ecf20Sopenharmony_ci /* 38658c2ecf20Sopenharmony_ci * Write RF buffer 38668c2ecf20Sopenharmony_ci */ 38678c2ecf20Sopenharmony_ci ret = ath5k_hw_rfregs_init(ah, channel, mode); 38688c2ecf20Sopenharmony_ci if (ret) 38698c2ecf20Sopenharmony_ci return ret; 38708c2ecf20Sopenharmony_ci 38718c2ecf20Sopenharmony_ci /*Enable/disable 802.11b mode on 5111 38728c2ecf20Sopenharmony_ci (enable 2111 frequency converter + CCK)*/ 38738c2ecf20Sopenharmony_ci if (ah->ah_radio == AR5K_RF5111) { 38748c2ecf20Sopenharmony_ci if (mode == AR5K_MODE_11B) 38758c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG, 38768c2ecf20Sopenharmony_ci AR5K_TXCFG_B_MODE); 38778c2ecf20Sopenharmony_ci else 38788c2ecf20Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_TXCFG, 38798c2ecf20Sopenharmony_ci AR5K_TXCFG_B_MODE); 38808c2ecf20Sopenharmony_ci } 38818c2ecf20Sopenharmony_ci 38828c2ecf20Sopenharmony_ci } else if (ah->ah_version == AR5K_AR5210) { 38838c2ecf20Sopenharmony_ci usleep_range(1000, 1500); 38848c2ecf20Sopenharmony_ci /* Disable phy and wait */ 38858c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT); 38868c2ecf20Sopenharmony_ci usleep_range(1000, 1500); 38878c2ecf20Sopenharmony_ci } 38888c2ecf20Sopenharmony_ci 38898c2ecf20Sopenharmony_ci /* Set channel on PHY */ 38908c2ecf20Sopenharmony_ci ret = ath5k_hw_channel(ah, channel); 38918c2ecf20Sopenharmony_ci if (ret) 38928c2ecf20Sopenharmony_ci return ret; 38938c2ecf20Sopenharmony_ci 38948c2ecf20Sopenharmony_ci /* 38958c2ecf20Sopenharmony_ci * Enable the PHY and wait until completion 38968c2ecf20Sopenharmony_ci * This includes BaseBand and Synthesizer 38978c2ecf20Sopenharmony_ci * activation. 38988c2ecf20Sopenharmony_ci */ 38998c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT); 39008c2ecf20Sopenharmony_ci 39018c2ecf20Sopenharmony_ci ath5k_hw_wait_for_synth(ah, channel); 39028c2ecf20Sopenharmony_ci 39038c2ecf20Sopenharmony_ci /* 39048c2ecf20Sopenharmony_ci * Perform ADC test to see if baseband is ready 39058c2ecf20Sopenharmony_ci * Set tx hold and check adc test register 39068c2ecf20Sopenharmony_ci */ 39078c2ecf20Sopenharmony_ci phy_tst1 = ath5k_hw_reg_read(ah, AR5K_PHY_TST1); 39088c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_PHY_TST1_TXHOLD, AR5K_PHY_TST1); 39098c2ecf20Sopenharmony_ci for (i = 0; i <= 20; i++) { 39108c2ecf20Sopenharmony_ci if (!(ath5k_hw_reg_read(ah, AR5K_PHY_ADC_TEST) & 0x10)) 39118c2ecf20Sopenharmony_ci break; 39128c2ecf20Sopenharmony_ci usleep_range(200, 250); 39138c2ecf20Sopenharmony_ci } 39148c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, phy_tst1, AR5K_PHY_TST1); 39158c2ecf20Sopenharmony_ci 39168c2ecf20Sopenharmony_ci /* 39178c2ecf20Sopenharmony_ci * Start automatic gain control calibration 39188c2ecf20Sopenharmony_ci * 39198c2ecf20Sopenharmony_ci * During AGC calibration RX path is re-routed to 39208c2ecf20Sopenharmony_ci * a power detector so we don't receive anything. 39218c2ecf20Sopenharmony_ci * 39228c2ecf20Sopenharmony_ci * This method is used to calibrate some static offsets 39238c2ecf20Sopenharmony_ci * used together with on-the fly I/Q calibration (the 39248c2ecf20Sopenharmony_ci * one performed via ath5k_hw_phy_calibrate), which doesn't 39258c2ecf20Sopenharmony_ci * interrupt rx path. 39268c2ecf20Sopenharmony_ci * 39278c2ecf20Sopenharmony_ci * While rx path is re-routed to the power detector we also 39288c2ecf20Sopenharmony_ci * start a noise floor calibration to measure the 39298c2ecf20Sopenharmony_ci * card's noise floor (the noise we measure when we are not 39308c2ecf20Sopenharmony_ci * transmitting or receiving anything). 39318c2ecf20Sopenharmony_ci * 39328c2ecf20Sopenharmony_ci * If we are in a noisy environment, AGC calibration may time 39338c2ecf20Sopenharmony_ci * out and/or noise floor calibration might timeout. 39348c2ecf20Sopenharmony_ci */ 39358c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, 39368c2ecf20Sopenharmony_ci AR5K_PHY_AGCCTL_CAL | AR5K_PHY_AGCCTL_NF); 39378c2ecf20Sopenharmony_ci 39388c2ecf20Sopenharmony_ci /* At the same time start I/Q calibration for QAM constellation 39398c2ecf20Sopenharmony_ci * -no need for CCK- */ 39408c2ecf20Sopenharmony_ci ah->ah_iq_cal_needed = false; 39418c2ecf20Sopenharmony_ci if (!(mode == AR5K_MODE_11B)) { 39428c2ecf20Sopenharmony_ci ah->ah_iq_cal_needed = true; 39438c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, 39448c2ecf20Sopenharmony_ci AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15); 39458c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, 39468c2ecf20Sopenharmony_ci AR5K_PHY_IQ_RUN); 39478c2ecf20Sopenharmony_ci } 39488c2ecf20Sopenharmony_ci 39498c2ecf20Sopenharmony_ci /* Wait for gain calibration to finish (we check for I/Q calibration 39508c2ecf20Sopenharmony_ci * during ath5k_phy_calibrate) */ 39518c2ecf20Sopenharmony_ci if (ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, 39528c2ecf20Sopenharmony_ci AR5K_PHY_AGCCTL_CAL, 0, false)) { 39538c2ecf20Sopenharmony_ci ATH5K_ERR(ah, "gain calibration timeout (%uMHz)\n", 39548c2ecf20Sopenharmony_ci channel->center_freq); 39558c2ecf20Sopenharmony_ci } 39568c2ecf20Sopenharmony_ci 39578c2ecf20Sopenharmony_ci /* Restore antenna mode */ 39588c2ecf20Sopenharmony_ci ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode); 39598c2ecf20Sopenharmony_ci 39608c2ecf20Sopenharmony_ci return ret; 39618c2ecf20Sopenharmony_ci} 3962