1717f6bedSFelix Fietkau /* 25b68138eSSujith Manoharan * Copyright (c) 2010-2011 Atheros Communications Inc. 3717f6bedSFelix Fietkau * 4717f6bedSFelix Fietkau * Permission to use, copy, modify, and/or distribute this software for any 5717f6bedSFelix Fietkau * purpose with or without fee is hereby granted, provided that the above 6717f6bedSFelix Fietkau * copyright notice and this permission notice appear in all copies. 7717f6bedSFelix Fietkau * 8717f6bedSFelix Fietkau * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9717f6bedSFelix Fietkau * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10717f6bedSFelix Fietkau * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11717f6bedSFelix Fietkau * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12717f6bedSFelix Fietkau * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13717f6bedSFelix Fietkau * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14717f6bedSFelix Fietkau * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15717f6bedSFelix Fietkau */ 16717f6bedSFelix Fietkau 17717f6bedSFelix Fietkau #include "hw.h" 18717f6bedSFelix Fietkau #include "ar9003_phy.h" 19717f6bedSFelix Fietkau 20717f6bedSFelix Fietkau void ar9003_paprd_enable(struct ath_hw *ah, bool val) 21717f6bedSFelix Fietkau { 2245ef6a0bSVasanthakumar Thiagarajan struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); 2345ef6a0bSVasanthakumar Thiagarajan struct ath9k_channel *chan = ah->curchan; 2445ef6a0bSVasanthakumar Thiagarajan 2545ef6a0bSVasanthakumar Thiagarajan if (val) { 2645ef6a0bSVasanthakumar Thiagarajan ah->paprd_table_write_done = true; 2745ef6a0bSVasanthakumar Thiagarajan 2845ef6a0bSVasanthakumar Thiagarajan ah->eep_ops->set_txpower(ah, chan, 2945ef6a0bSVasanthakumar Thiagarajan ath9k_regd_get_ctl(regulatory, chan), 3045ef6a0bSVasanthakumar Thiagarajan chan->chan->max_antenna_gain * 2, 3145ef6a0bSVasanthakumar Thiagarajan chan->chan->max_power * 2, 3245ef6a0bSVasanthakumar Thiagarajan min((u32) MAX_RATE_POWER, 3345ef6a0bSVasanthakumar Thiagarajan (u32) regulatory->power_limit), false); 3445ef6a0bSVasanthakumar Thiagarajan } 3545ef6a0bSVasanthakumar Thiagarajan 36717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL0_B0, 37717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL0_PAPRD_ENABLE, !!val); 3811441fb8SVasanthakumar Thiagarajan if (ah->caps.tx_chainmask & BIT(1)) 39717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL0_B1, 40717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL0_PAPRD_ENABLE, !!val); 4111441fb8SVasanthakumar Thiagarajan if (ah->caps.tx_chainmask & BIT(2)) 42717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL0_B2, 43717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL0_PAPRD_ENABLE, !!val); 44717f6bedSFelix Fietkau } 45717f6bedSFelix Fietkau EXPORT_SYMBOL(ar9003_paprd_enable); 46717f6bedSFelix Fietkau 471bf38661SFelix Fietkau static int ar9003_get_training_power_2g(struct ath_hw *ah) 48717f6bedSFelix Fietkau { 49931749bfSMohammed Shafi Shajakhan struct ath9k_channel *chan = ah->curchan; 501bf38661SFelix Fietkau unsigned int power, scale, delta; 511bf38661SFelix Fietkau 52931749bfSMohammed Shafi Shajakhan scale = ar9003_get_paprd_scale_factor(ah, chan); 531bf38661SFelix Fietkau power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE5, 541bf38661SFelix Fietkau AR_PHY_POWERTX_RATE5_POWERTXHT20_0); 551bf38661SFelix Fietkau 561bf38661SFelix Fietkau delta = abs((int) ah->paprd_target_power - (int) power); 571bf38661SFelix Fietkau if (delta > scale) 581bf38661SFelix Fietkau return -1; 591bf38661SFelix Fietkau 601bf38661SFelix Fietkau if (delta < 4) 611bf38661SFelix Fietkau power -= 4 - delta; 621bf38661SFelix Fietkau 631bf38661SFelix Fietkau return power; 641bf38661SFelix Fietkau } 651bf38661SFelix Fietkau 661bf38661SFelix Fietkau static int ar9003_get_training_power_5g(struct ath_hw *ah) 671bf38661SFelix Fietkau { 681bf38661SFelix Fietkau struct ath_common *common = ath9k_hw_common(ah); 691bf38661SFelix Fietkau struct ath9k_channel *chan = ah->curchan; 701bf38661SFelix Fietkau unsigned int power, scale, delta; 711bf38661SFelix Fietkau 72931749bfSMohammed Shafi Shajakhan scale = ar9003_get_paprd_scale_factor(ah, chan); 731bf38661SFelix Fietkau 741bf38661SFelix Fietkau if (IS_CHAN_HT40(chan)) 751bf38661SFelix Fietkau power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE8, 761bf38661SFelix Fietkau AR_PHY_POWERTX_RATE8_POWERTXHT40_5); 771bf38661SFelix Fietkau else 781bf38661SFelix Fietkau power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE6, 791bf38661SFelix Fietkau AR_PHY_POWERTX_RATE6_POWERTXHT20_5); 801bf38661SFelix Fietkau 811bf38661SFelix Fietkau power += scale; 821bf38661SFelix Fietkau delta = abs((int) ah->paprd_target_power - (int) power); 831bf38661SFelix Fietkau if (delta > scale) 841bf38661SFelix Fietkau return -1; 851bf38661SFelix Fietkau 861bf38661SFelix Fietkau power += 2 * get_streams(common->tx_chainmask); 871bf38661SFelix Fietkau return power; 881bf38661SFelix Fietkau } 891bf38661SFelix Fietkau 901bf38661SFelix Fietkau static int ar9003_paprd_setup_single_table(struct ath_hw *ah) 911bf38661SFelix Fietkau { 921bf38661SFelix Fietkau struct ath_common *common = ath9k_hw_common(ah); 9307b2fa5aSJoe Perches static const u32 ctrl0[3] = { 94717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL0_B0, 95717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL0_B1, 96717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL0_B2 97717f6bedSFelix Fietkau }; 9807b2fa5aSJoe Perches static const u32 ctrl1[3] = { 99717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_B0, 100717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_B1, 101717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_B2 102717f6bedSFelix Fietkau }; 1031bf38661SFelix Fietkau int training_power; 104717f6bedSFelix Fietkau int i; 105717f6bedSFelix Fietkau 1061bf38661SFelix Fietkau if (IS_CHAN_2GHZ(ah->curchan)) 1071bf38661SFelix Fietkau training_power = ar9003_get_training_power_2g(ah); 1081bf38661SFelix Fietkau else 1091bf38661SFelix Fietkau training_power = ar9003_get_training_power_5g(ah); 1101bf38661SFelix Fietkau 11184288044SMohammed Shafi Shajakhan ath_dbg(common, ATH_DBG_CALIBRATE, 11284288044SMohammed Shafi Shajakhan "Training power: %d, Target power: %d\n", 11384288044SMohammed Shafi Shajakhan training_power, ah->paprd_target_power); 11484288044SMohammed Shafi Shajakhan 1151bf38661SFelix Fietkau if (training_power < 0) { 1161bf38661SFelix Fietkau ath_dbg(common, ATH_DBG_CALIBRATE, 1171bf38661SFelix Fietkau "PAPRD target power delta out of range"); 1181bf38661SFelix Fietkau return -ERANGE; 1191bf38661SFelix Fietkau } 1201bf38661SFelix Fietkau ah->paprd_training_power = training_power; 1211bf38661SFelix Fietkau 1227072bf62SVasanthakumar Thiagarajan REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2AM, AR_PHY_PAPRD_AM2AM_MASK, 1237072bf62SVasanthakumar Thiagarajan ah->paprd_ratemask); 1247072bf62SVasanthakumar Thiagarajan REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2PM, AR_PHY_PAPRD_AM2PM_MASK, 1257072bf62SVasanthakumar Thiagarajan ah->paprd_ratemask); 1267072bf62SVasanthakumar Thiagarajan REG_RMW_FIELD(ah, AR_PHY_PAPRD_HT40, AR_PHY_PAPRD_HT40_MASK, 127f1a8abb0SFelix Fietkau ah->paprd_ratemask_ht40); 128717f6bedSFelix Fietkau 12911441fb8SVasanthakumar Thiagarajan for (i = 0; i < ah->caps.max_txchains; i++) { 130717f6bedSFelix Fietkau REG_RMW_FIELD(ah, ctrl0[i], 131717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL0_USE_SINGLE_TABLE_MASK, 1); 132717f6bedSFelix Fietkau REG_RMW_FIELD(ah, ctrl1[i], 133717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_ADAPTIVE_AM2PM_ENABLE, 1); 134717f6bedSFelix Fietkau REG_RMW_FIELD(ah, ctrl1[i], 135717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_ADAPTIVE_AM2AM_ENABLE, 1); 136717f6bedSFelix Fietkau REG_RMW_FIELD(ah, ctrl1[i], 137717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_ADAPTIVE_SCALING_ENA, 0); 138717f6bedSFelix Fietkau REG_RMW_FIELD(ah, ctrl1[i], 139717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_PA_GAIN_SCALE_FACT_MASK, 181); 140717f6bedSFelix Fietkau REG_RMW_FIELD(ah, ctrl1[i], 141717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_PAPRD_MAG_SCALE_FACT, 361); 142717f6bedSFelix Fietkau REG_RMW_FIELD(ah, ctrl1[i], 143717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_ADAPTIVE_SCALING_ENA, 0); 144717f6bedSFelix Fietkau REG_RMW_FIELD(ah, ctrl0[i], 145717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL0_PAPRD_MAG_THRSH, 3); 146717f6bedSFelix Fietkau } 147717f6bedSFelix Fietkau 148717f6bedSFelix Fietkau ar9003_paprd_enable(ah, false); 149717f6bedSFelix Fietkau 150717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, 151717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_LB_SKIP, 0x30); 152717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, 153717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_LB_ENABLE, 1); 154717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, 155717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_TX_GAIN_FORCE, 1); 156717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, 157717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_RX_BB_GAIN_FORCE, 0); 158717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, 159717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_IQCORR_ENABLE, 0); 160717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, 161717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_AGC2_SETTLING, 28); 162717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, 163717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL1_CF_CF_PAPRD_TRAIN_ENABLE, 1); 164717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL2, 165717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL2_CF_PAPRD_INIT_RX_BB_GAIN, 147); 166717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, 167717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_FINE_CORR_LEN, 4); 168717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, 169717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_COARSE_CORR_LEN, 4); 170717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, 171717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_NUM_CORR_STAGES, 7); 172717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, 173717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_MIN_LOOPBACK_DEL, 1); 17411441fb8SVasanthakumar Thiagarajan if (AR_SREV_9485(ah)) 175717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, 17611441fb8SVasanthakumar Thiagarajan AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP, 17711441fb8SVasanthakumar Thiagarajan -3); 17811441fb8SVasanthakumar Thiagarajan else 17911441fb8SVasanthakumar Thiagarajan REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, 18011441fb8SVasanthakumar Thiagarajan AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP, 18111441fb8SVasanthakumar Thiagarajan -6); 182717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, 183717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_ADC_DESIRED_SIZE, 184717f6bedSFelix Fietkau -15); 185717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, 186717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_BBTXMIX_DISABLE, 1); 187717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL4, 188717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_SAFETY_DELTA, 0); 189717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL4, 190717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_MIN_CORR, 400); 191717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL4, 192717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_NUM_TRAIN_SAMPLES, 193717f6bedSFelix Fietkau 100); 194717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_0_B0, 195717f6bedSFelix Fietkau AR_PHY_PAPRD_PRE_POST_SCALING, 261376); 196717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_1_B0, 197717f6bedSFelix Fietkau AR_PHY_PAPRD_PRE_POST_SCALING, 248079); 198717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_2_B0, 199717f6bedSFelix Fietkau AR_PHY_PAPRD_PRE_POST_SCALING, 233759); 200717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_3_B0, 201717f6bedSFelix Fietkau AR_PHY_PAPRD_PRE_POST_SCALING, 220464); 202717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_4_B0, 203717f6bedSFelix Fietkau AR_PHY_PAPRD_PRE_POST_SCALING, 208194); 204717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_5_B0, 205717f6bedSFelix Fietkau AR_PHY_PAPRD_PRE_POST_SCALING, 196949); 206717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_6_B0, 207717f6bedSFelix Fietkau AR_PHY_PAPRD_PRE_POST_SCALING, 185706); 208717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_7_B0, 209717f6bedSFelix Fietkau AR_PHY_PAPRD_PRE_POST_SCALING, 175487); 2101bf38661SFelix Fietkau return 0; 211717f6bedSFelix Fietkau } 212717f6bedSFelix Fietkau 213717f6bedSFelix Fietkau static void ar9003_paprd_get_gain_table(struct ath_hw *ah) 214717f6bedSFelix Fietkau { 215717f6bedSFelix Fietkau u32 *entry = ah->paprd_gain_table_entries; 216717f6bedSFelix Fietkau u8 *index = ah->paprd_gain_table_index; 217717f6bedSFelix Fietkau u32 reg = AR_PHY_TXGAIN_TABLE; 218717f6bedSFelix Fietkau int i; 219717f6bedSFelix Fietkau 220717f6bedSFelix Fietkau memset(entry, 0, sizeof(ah->paprd_gain_table_entries)); 221717f6bedSFelix Fietkau memset(index, 0, sizeof(ah->paprd_gain_table_index)); 222717f6bedSFelix Fietkau 223717f6bedSFelix Fietkau for (i = 0; i < 32; i++) { 224717f6bedSFelix Fietkau entry[i] = REG_READ(ah, reg); 225717f6bedSFelix Fietkau index[i] = (entry[i] >> 24) & 0xff; 226717f6bedSFelix Fietkau reg += 4; 227717f6bedSFelix Fietkau } 228717f6bedSFelix Fietkau } 229717f6bedSFelix Fietkau 230717f6bedSFelix Fietkau static unsigned int ar9003_get_desired_gain(struct ath_hw *ah, int chain, 231717f6bedSFelix Fietkau int target_power) 232717f6bedSFelix Fietkau { 233717f6bedSFelix Fietkau int olpc_gain_delta = 0; 234717f6bedSFelix Fietkau int alpha_therm, alpha_volt; 235717f6bedSFelix Fietkau int therm_cal_value, volt_cal_value; 236717f6bedSFelix Fietkau int therm_value, volt_value; 237717f6bedSFelix Fietkau int thermal_gain_corr, voltage_gain_corr; 238717f6bedSFelix Fietkau int desired_scale, desired_gain = 0; 239717f6bedSFelix Fietkau u32 reg; 240717f6bedSFelix Fietkau 241717f6bedSFelix Fietkau REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1, 242717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE); 243717f6bedSFelix Fietkau desired_scale = REG_READ_FIELD(ah, AR_PHY_TPC_12, 244717f6bedSFelix Fietkau AR_PHY_TPC_12_DESIRED_SCALE_HT40_5); 245717f6bedSFelix Fietkau alpha_therm = REG_READ_FIELD(ah, AR_PHY_TPC_19, 246717f6bedSFelix Fietkau AR_PHY_TPC_19_ALPHA_THERM); 247717f6bedSFelix Fietkau alpha_volt = REG_READ_FIELD(ah, AR_PHY_TPC_19, 248717f6bedSFelix Fietkau AR_PHY_TPC_19_ALPHA_VOLT); 249717f6bedSFelix Fietkau therm_cal_value = REG_READ_FIELD(ah, AR_PHY_TPC_18, 250717f6bedSFelix Fietkau AR_PHY_TPC_18_THERM_CAL_VALUE); 251717f6bedSFelix Fietkau volt_cal_value = REG_READ_FIELD(ah, AR_PHY_TPC_18, 252717f6bedSFelix Fietkau AR_PHY_TPC_18_VOLT_CAL_VALUE); 253717f6bedSFelix Fietkau therm_value = REG_READ_FIELD(ah, AR_PHY_BB_THERM_ADC_4, 254717f6bedSFelix Fietkau AR_PHY_BB_THERM_ADC_4_LATEST_THERM_VALUE); 255717f6bedSFelix Fietkau volt_value = REG_READ_FIELD(ah, AR_PHY_BB_THERM_ADC_4, 256717f6bedSFelix Fietkau AR_PHY_BB_THERM_ADC_4_LATEST_VOLT_VALUE); 257717f6bedSFelix Fietkau 258717f6bedSFelix Fietkau if (chain == 0) 259717f6bedSFelix Fietkau reg = AR_PHY_TPC_11_B0; 260717f6bedSFelix Fietkau else if (chain == 1) 261717f6bedSFelix Fietkau reg = AR_PHY_TPC_11_B1; 262717f6bedSFelix Fietkau else 263717f6bedSFelix Fietkau reg = AR_PHY_TPC_11_B2; 264717f6bedSFelix Fietkau 265717f6bedSFelix Fietkau olpc_gain_delta = REG_READ_FIELD(ah, reg, 266717f6bedSFelix Fietkau AR_PHY_TPC_11_OLPC_GAIN_DELTA); 267717f6bedSFelix Fietkau 268717f6bedSFelix Fietkau if (olpc_gain_delta >= 128) 269717f6bedSFelix Fietkau olpc_gain_delta = olpc_gain_delta - 256; 270717f6bedSFelix Fietkau 271717f6bedSFelix Fietkau thermal_gain_corr = (alpha_therm * (therm_value - therm_cal_value) + 272717f6bedSFelix Fietkau (256 / 2)) / 256; 273717f6bedSFelix Fietkau voltage_gain_corr = (alpha_volt * (volt_value - volt_cal_value) + 274717f6bedSFelix Fietkau (128 / 2)) / 128; 275717f6bedSFelix Fietkau desired_gain = target_power - olpc_gain_delta - thermal_gain_corr - 276717f6bedSFelix Fietkau voltage_gain_corr + desired_scale; 277717f6bedSFelix Fietkau 278717f6bedSFelix Fietkau return desired_gain; 279717f6bedSFelix Fietkau } 280717f6bedSFelix Fietkau 281717f6bedSFelix Fietkau static void ar9003_tx_force_gain(struct ath_hw *ah, unsigned int gain_index) 282717f6bedSFelix Fietkau { 283717f6bedSFelix Fietkau int selected_gain_entry, txbb1dbgain, txbb6dbgain, txmxrgain; 284717f6bedSFelix Fietkau int padrvgnA, padrvgnB, padrvgnC, padrvgnD; 285717f6bedSFelix Fietkau u32 *gain_table_entries = ah->paprd_gain_table_entries; 286717f6bedSFelix Fietkau 287717f6bedSFelix Fietkau selected_gain_entry = gain_table_entries[gain_index]; 288717f6bedSFelix Fietkau txbb1dbgain = selected_gain_entry & 0x7; 289717f6bedSFelix Fietkau txbb6dbgain = (selected_gain_entry >> 3) & 0x3; 290717f6bedSFelix Fietkau txmxrgain = (selected_gain_entry >> 5) & 0xf; 291717f6bedSFelix Fietkau padrvgnA = (selected_gain_entry >> 9) & 0xf; 292717f6bedSFelix Fietkau padrvgnB = (selected_gain_entry >> 13) & 0xf; 293717f6bedSFelix Fietkau padrvgnC = (selected_gain_entry >> 17) & 0xf; 294717f6bedSFelix Fietkau padrvgnD = (selected_gain_entry >> 21) & 0x3; 295717f6bedSFelix Fietkau 296717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 297717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCED_TXBB1DBGAIN, txbb1dbgain); 298717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 299717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCED_TXBB6DBGAIN, txbb6dbgain); 300717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 301717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCED_TXMXRGAIN, txmxrgain); 302717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 303717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNA, padrvgnA); 304717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 305717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNB, padrvgnB); 306717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 307717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNC, padrvgnC); 308717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 309717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGND, padrvgnD); 310717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 311717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCED_ENABLE_PAL, 0); 312717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 313717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCE_TX_GAIN, 0); 314717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TPC_1, AR_PHY_TPC_1_FORCED_DAC_GAIN, 0); 315717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TPC_1, AR_PHY_TPC_1_FORCE_DAC_GAIN, 0); 316717f6bedSFelix Fietkau } 317717f6bedSFelix Fietkau 318717f6bedSFelix Fietkau static inline int find_expn(int num) 319717f6bedSFelix Fietkau { 320717f6bedSFelix Fietkau return fls(num) - 1; 321717f6bedSFelix Fietkau } 322717f6bedSFelix Fietkau 323717f6bedSFelix Fietkau static inline int find_proper_scale(int expn, int N) 324717f6bedSFelix Fietkau { 325717f6bedSFelix Fietkau return (expn > N) ? expn - 10 : 0; 326717f6bedSFelix Fietkau } 327717f6bedSFelix Fietkau 328717f6bedSFelix Fietkau #define NUM_BIN 23 329717f6bedSFelix Fietkau 330717f6bedSFelix Fietkau static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain) 331717f6bedSFelix Fietkau { 332717f6bedSFelix Fietkau unsigned int thresh_accum_cnt; 333717f6bedSFelix Fietkau int x_est[NUM_BIN + 1], Y[NUM_BIN + 1], theta[NUM_BIN + 1]; 334717f6bedSFelix Fietkau int PA_in[NUM_BIN + 1]; 335717f6bedSFelix Fietkau int B1_tmp[NUM_BIN + 1], B2_tmp[NUM_BIN + 1]; 336717f6bedSFelix Fietkau unsigned int B1_abs_max, B2_abs_max; 337717f6bedSFelix Fietkau int max_index, scale_factor; 338717f6bedSFelix Fietkau int y_est[NUM_BIN + 1]; 339717f6bedSFelix Fietkau int x_est_fxp1_nonlin, x_tilde[NUM_BIN + 1]; 340717f6bedSFelix Fietkau unsigned int x_tilde_abs; 341717f6bedSFelix Fietkau int G_fxp, Y_intercept, order_x_by_y, M, I, L, sum_y_sqr, sum_y_quad; 342717f6bedSFelix Fietkau int Q_x, Q_B1, Q_B2, beta_raw, alpha_raw, scale_B; 343717f6bedSFelix Fietkau int Q_scale_B, Q_beta, Q_alpha, alpha, beta, order_1, order_2; 344717f6bedSFelix Fietkau int order1_5x, order2_3x, order1_5x_rem, order2_3x_rem; 345717f6bedSFelix Fietkau int y5, y3, tmp; 346717f6bedSFelix Fietkau int theta_low_bin = 0; 347717f6bedSFelix Fietkau int i; 348717f6bedSFelix Fietkau 349717f6bedSFelix Fietkau /* disregard any bin that contains <= 16 samples */ 350717f6bedSFelix Fietkau thresh_accum_cnt = 16; 351717f6bedSFelix Fietkau scale_factor = 5; 352717f6bedSFelix Fietkau max_index = 0; 353717f6bedSFelix Fietkau memset(theta, 0, sizeof(theta)); 354717f6bedSFelix Fietkau memset(x_est, 0, sizeof(x_est)); 355717f6bedSFelix Fietkau memset(Y, 0, sizeof(Y)); 356717f6bedSFelix Fietkau memset(y_est, 0, sizeof(y_est)); 357717f6bedSFelix Fietkau memset(x_tilde, 0, sizeof(x_tilde)); 358717f6bedSFelix Fietkau 359717f6bedSFelix Fietkau for (i = 0; i < NUM_BIN; i++) { 360717f6bedSFelix Fietkau s32 accum_cnt, accum_tx, accum_rx, accum_ang; 361717f6bedSFelix Fietkau 362717f6bedSFelix Fietkau /* number of samples */ 363717f6bedSFelix Fietkau accum_cnt = data_L[i] & 0xffff; 364717f6bedSFelix Fietkau 365717f6bedSFelix Fietkau if (accum_cnt <= thresh_accum_cnt) 366717f6bedSFelix Fietkau continue; 367717f6bedSFelix Fietkau 368717f6bedSFelix Fietkau /* sum(tx amplitude) */ 369717f6bedSFelix Fietkau accum_tx = ((data_L[i] >> 16) & 0xffff) | 370717f6bedSFelix Fietkau ((data_U[i] & 0x7ff) << 16); 371717f6bedSFelix Fietkau 372717f6bedSFelix Fietkau /* sum(rx amplitude distance to lower bin edge) */ 373717f6bedSFelix Fietkau accum_rx = ((data_U[i] >> 11) & 0x1f) | 374717f6bedSFelix Fietkau ((data_L[i + 23] & 0xffff) << 5); 375717f6bedSFelix Fietkau 376717f6bedSFelix Fietkau /* sum(angles) */ 377717f6bedSFelix Fietkau accum_ang = ((data_L[i + 23] >> 16) & 0xffff) | 378717f6bedSFelix Fietkau ((data_U[i + 23] & 0x7ff) << 16); 379717f6bedSFelix Fietkau 380717f6bedSFelix Fietkau accum_tx <<= scale_factor; 381717f6bedSFelix Fietkau accum_rx <<= scale_factor; 382717f6bedSFelix Fietkau x_est[i + 1] = (((accum_tx + accum_cnt) / accum_cnt) + 32) >> 383717f6bedSFelix Fietkau scale_factor; 384717f6bedSFelix Fietkau 385717f6bedSFelix Fietkau Y[i + 1] = ((((accum_rx + accum_cnt) / accum_cnt) + 32) >> 386717f6bedSFelix Fietkau scale_factor) + 387717f6bedSFelix Fietkau (1 << scale_factor) * max_index + 16; 388717f6bedSFelix Fietkau 389717f6bedSFelix Fietkau if (accum_ang >= (1 << 26)) 390717f6bedSFelix Fietkau accum_ang -= 1 << 27; 391717f6bedSFelix Fietkau 392717f6bedSFelix Fietkau theta[i + 1] = ((accum_ang * (1 << scale_factor)) + accum_cnt) / 393717f6bedSFelix Fietkau accum_cnt; 394717f6bedSFelix Fietkau 395717f6bedSFelix Fietkau max_index++; 396717f6bedSFelix Fietkau } 397717f6bedSFelix Fietkau 398717f6bedSFelix Fietkau /* 399717f6bedSFelix Fietkau * Find average theta of first 5 bin and all of those to same value. 400717f6bedSFelix Fietkau * Curve is linear at that range. 401717f6bedSFelix Fietkau */ 402717f6bedSFelix Fietkau for (i = 1; i < 6; i++) 403717f6bedSFelix Fietkau theta_low_bin += theta[i]; 404717f6bedSFelix Fietkau 405717f6bedSFelix Fietkau theta_low_bin = theta_low_bin / 5; 406717f6bedSFelix Fietkau for (i = 1; i < 6; i++) 407717f6bedSFelix Fietkau theta[i] = theta_low_bin; 408717f6bedSFelix Fietkau 409717f6bedSFelix Fietkau /* Set values at origin */ 410717f6bedSFelix Fietkau theta[0] = theta_low_bin; 411717f6bedSFelix Fietkau for (i = 0; i <= max_index; i++) 412717f6bedSFelix Fietkau theta[i] -= theta_low_bin; 413717f6bedSFelix Fietkau 414717f6bedSFelix Fietkau x_est[0] = 0; 415717f6bedSFelix Fietkau Y[0] = 0; 416717f6bedSFelix Fietkau scale_factor = 8; 417717f6bedSFelix Fietkau 418717f6bedSFelix Fietkau /* low signal gain */ 419717f6bedSFelix Fietkau if (x_est[6] == x_est[3]) 420717f6bedSFelix Fietkau return false; 421717f6bedSFelix Fietkau 422717f6bedSFelix Fietkau G_fxp = 423717f6bedSFelix Fietkau (((Y[6] - Y[3]) * 1 << scale_factor) + 424717f6bedSFelix Fietkau (x_est[6] - x_est[3])) / (x_est[6] - x_est[3]); 425717f6bedSFelix Fietkau 4262d3fca18SSenthil Balasubramanian /* prevent division by zero */ 4272d3fca18SSenthil Balasubramanian if (G_fxp == 0) 4282d3fca18SSenthil Balasubramanian return false; 4292d3fca18SSenthil Balasubramanian 430717f6bedSFelix Fietkau Y_intercept = 431717f6bedSFelix Fietkau (G_fxp * (x_est[0] - x_est[3]) + 432717f6bedSFelix Fietkau (1 << scale_factor)) / (1 << scale_factor) + Y[3]; 433717f6bedSFelix Fietkau 434717f6bedSFelix Fietkau for (i = 0; i <= max_index; i++) 435717f6bedSFelix Fietkau y_est[i] = Y[i] - Y_intercept; 436717f6bedSFelix Fietkau 437717f6bedSFelix Fietkau for (i = 0; i <= 3; i++) { 438717f6bedSFelix Fietkau y_est[i] = i * 32; 439717f6bedSFelix Fietkau x_est[i] = ((y_est[i] * 1 << scale_factor) + G_fxp) / G_fxp; 440717f6bedSFelix Fietkau } 441717f6bedSFelix Fietkau 4422d3fca18SSenthil Balasubramanian if (y_est[max_index] == 0) 4432d3fca18SSenthil Balasubramanian return false; 4442d3fca18SSenthil Balasubramanian 445717f6bedSFelix Fietkau x_est_fxp1_nonlin = 446717f6bedSFelix Fietkau x_est[max_index] - ((1 << scale_factor) * y_est[max_index] + 447717f6bedSFelix Fietkau G_fxp) / G_fxp; 448717f6bedSFelix Fietkau 449717f6bedSFelix Fietkau order_x_by_y = 450717f6bedSFelix Fietkau (x_est_fxp1_nonlin + y_est[max_index]) / y_est[max_index]; 451717f6bedSFelix Fietkau 452717f6bedSFelix Fietkau if (order_x_by_y == 0) 453717f6bedSFelix Fietkau M = 10; 454717f6bedSFelix Fietkau else if (order_x_by_y == 1) 455717f6bedSFelix Fietkau M = 9; 456717f6bedSFelix Fietkau else 457717f6bedSFelix Fietkau M = 8; 458717f6bedSFelix Fietkau 459717f6bedSFelix Fietkau I = (max_index > 15) ? 7 : max_index >> 1; 460717f6bedSFelix Fietkau L = max_index - I; 461717f6bedSFelix Fietkau scale_factor = 8; 462717f6bedSFelix Fietkau sum_y_sqr = 0; 463717f6bedSFelix Fietkau sum_y_quad = 0; 464717f6bedSFelix Fietkau x_tilde_abs = 0; 465717f6bedSFelix Fietkau 466717f6bedSFelix Fietkau for (i = 0; i <= L; i++) { 467717f6bedSFelix Fietkau unsigned int y_sqr; 468717f6bedSFelix Fietkau unsigned int y_quad; 469717f6bedSFelix Fietkau unsigned int tmp_abs; 470717f6bedSFelix Fietkau 471717f6bedSFelix Fietkau /* prevent division by zero */ 472717f6bedSFelix Fietkau if (y_est[i + I] == 0) 473717f6bedSFelix Fietkau return false; 474717f6bedSFelix Fietkau 475717f6bedSFelix Fietkau x_est_fxp1_nonlin = 476717f6bedSFelix Fietkau x_est[i + I] - ((1 << scale_factor) * y_est[i + I] + 477717f6bedSFelix Fietkau G_fxp) / G_fxp; 478717f6bedSFelix Fietkau 479717f6bedSFelix Fietkau x_tilde[i] = 480717f6bedSFelix Fietkau (x_est_fxp1_nonlin * (1 << M) + y_est[i + I]) / y_est[i + 481717f6bedSFelix Fietkau I]; 482717f6bedSFelix Fietkau x_tilde[i] = 483717f6bedSFelix Fietkau (x_tilde[i] * (1 << M) + y_est[i + I]) / y_est[i + I]; 484717f6bedSFelix Fietkau x_tilde[i] = 485717f6bedSFelix Fietkau (x_tilde[i] * (1 << M) + y_est[i + I]) / y_est[i + I]; 486717f6bedSFelix Fietkau y_sqr = 487717f6bedSFelix Fietkau (y_est[i + I] * y_est[i + I] + 488717f6bedSFelix Fietkau (scale_factor * scale_factor)) / (scale_factor * 489717f6bedSFelix Fietkau scale_factor); 490717f6bedSFelix Fietkau tmp_abs = abs(x_tilde[i]); 491717f6bedSFelix Fietkau if (tmp_abs > x_tilde_abs) 492717f6bedSFelix Fietkau x_tilde_abs = tmp_abs; 493717f6bedSFelix Fietkau 494717f6bedSFelix Fietkau y_quad = y_sqr * y_sqr; 495717f6bedSFelix Fietkau sum_y_sqr = sum_y_sqr + y_sqr; 496717f6bedSFelix Fietkau sum_y_quad = sum_y_quad + y_quad; 497717f6bedSFelix Fietkau B1_tmp[i] = y_sqr * (L + 1); 498717f6bedSFelix Fietkau B2_tmp[i] = y_sqr; 499717f6bedSFelix Fietkau } 500717f6bedSFelix Fietkau 501717f6bedSFelix Fietkau B1_abs_max = 0; 502717f6bedSFelix Fietkau B2_abs_max = 0; 503717f6bedSFelix Fietkau for (i = 0; i <= L; i++) { 504717f6bedSFelix Fietkau int abs_val; 505717f6bedSFelix Fietkau 506717f6bedSFelix Fietkau B1_tmp[i] -= sum_y_sqr; 507717f6bedSFelix Fietkau B2_tmp[i] = sum_y_quad - sum_y_sqr * B2_tmp[i]; 508717f6bedSFelix Fietkau 509717f6bedSFelix Fietkau abs_val = abs(B1_tmp[i]); 510717f6bedSFelix Fietkau if (abs_val > B1_abs_max) 511717f6bedSFelix Fietkau B1_abs_max = abs_val; 512717f6bedSFelix Fietkau 513717f6bedSFelix Fietkau abs_val = abs(B2_tmp[i]); 514717f6bedSFelix Fietkau if (abs_val > B2_abs_max) 515717f6bedSFelix Fietkau B2_abs_max = abs_val; 516717f6bedSFelix Fietkau } 517717f6bedSFelix Fietkau 518717f6bedSFelix Fietkau Q_x = find_proper_scale(find_expn(x_tilde_abs), 10); 519717f6bedSFelix Fietkau Q_B1 = find_proper_scale(find_expn(B1_abs_max), 10); 520717f6bedSFelix Fietkau Q_B2 = find_proper_scale(find_expn(B2_abs_max), 10); 521717f6bedSFelix Fietkau 522717f6bedSFelix Fietkau beta_raw = 0; 523717f6bedSFelix Fietkau alpha_raw = 0; 524717f6bedSFelix Fietkau for (i = 0; i <= L; i++) { 525717f6bedSFelix Fietkau x_tilde[i] = x_tilde[i] / (1 << Q_x); 526717f6bedSFelix Fietkau B1_tmp[i] = B1_tmp[i] / (1 << Q_B1); 527717f6bedSFelix Fietkau B2_tmp[i] = B2_tmp[i] / (1 << Q_B2); 528717f6bedSFelix Fietkau beta_raw = beta_raw + B1_tmp[i] * x_tilde[i]; 529717f6bedSFelix Fietkau alpha_raw = alpha_raw + B2_tmp[i] * x_tilde[i]; 530717f6bedSFelix Fietkau } 531717f6bedSFelix Fietkau 532717f6bedSFelix Fietkau scale_B = 533717f6bedSFelix Fietkau ((sum_y_quad / scale_factor) * (L + 1) - 534717f6bedSFelix Fietkau (sum_y_sqr / scale_factor) * sum_y_sqr) * scale_factor; 535717f6bedSFelix Fietkau 536717f6bedSFelix Fietkau Q_scale_B = find_proper_scale(find_expn(abs(scale_B)), 10); 537717f6bedSFelix Fietkau scale_B = scale_B / (1 << Q_scale_B); 5382d3fca18SSenthil Balasubramanian if (scale_B == 0) 5392d3fca18SSenthil Balasubramanian return false; 540717f6bedSFelix Fietkau Q_beta = find_proper_scale(find_expn(abs(beta_raw)), 10); 541717f6bedSFelix Fietkau Q_alpha = find_proper_scale(find_expn(abs(alpha_raw)), 10); 542717f6bedSFelix Fietkau beta_raw = beta_raw / (1 << Q_beta); 543717f6bedSFelix Fietkau alpha_raw = alpha_raw / (1 << Q_alpha); 544717f6bedSFelix Fietkau alpha = (alpha_raw << 10) / scale_B; 545717f6bedSFelix Fietkau beta = (beta_raw << 10) / scale_B; 546717f6bedSFelix Fietkau order_1 = 3 * M - Q_x - Q_B1 - Q_beta + 10 + Q_scale_B; 547717f6bedSFelix Fietkau order_2 = 3 * M - Q_x - Q_B2 - Q_alpha + 10 + Q_scale_B; 548717f6bedSFelix Fietkau order1_5x = order_1 / 5; 549717f6bedSFelix Fietkau order2_3x = order_2 / 3; 550717f6bedSFelix Fietkau order1_5x_rem = order_1 - 5 * order1_5x; 551717f6bedSFelix Fietkau order2_3x_rem = order_2 - 3 * order2_3x; 552717f6bedSFelix Fietkau 553717f6bedSFelix Fietkau for (i = 0; i < PAPRD_TABLE_SZ; i++) { 554717f6bedSFelix Fietkau tmp = i * 32; 555717f6bedSFelix Fietkau y5 = ((beta * tmp) >> 6) >> order1_5x; 556717f6bedSFelix Fietkau y5 = (y5 * tmp) >> order1_5x; 557717f6bedSFelix Fietkau y5 = (y5 * tmp) >> order1_5x; 558717f6bedSFelix Fietkau y5 = (y5 * tmp) >> order1_5x; 559717f6bedSFelix Fietkau y5 = (y5 * tmp) >> order1_5x; 560717f6bedSFelix Fietkau y5 = y5 >> order1_5x_rem; 561717f6bedSFelix Fietkau y3 = (alpha * tmp) >> order2_3x; 562717f6bedSFelix Fietkau y3 = (y3 * tmp) >> order2_3x; 563717f6bedSFelix Fietkau y3 = (y3 * tmp) >> order2_3x; 564717f6bedSFelix Fietkau y3 = y3 >> order2_3x_rem; 565717f6bedSFelix Fietkau PA_in[i] = y5 + y3 + (256 * tmp) / G_fxp; 566717f6bedSFelix Fietkau 567717f6bedSFelix Fietkau if (i >= 2) { 568717f6bedSFelix Fietkau tmp = PA_in[i] - PA_in[i - 1]; 569717f6bedSFelix Fietkau if (tmp < 0) 570717f6bedSFelix Fietkau PA_in[i] = 571717f6bedSFelix Fietkau PA_in[i - 1] + (PA_in[i - 1] - 572717f6bedSFelix Fietkau PA_in[i - 2]); 573717f6bedSFelix Fietkau } 574717f6bedSFelix Fietkau 575717f6bedSFelix Fietkau PA_in[i] = (PA_in[i] < 1400) ? PA_in[i] : 1400; 576717f6bedSFelix Fietkau } 577717f6bedSFelix Fietkau 578717f6bedSFelix Fietkau beta_raw = 0; 579717f6bedSFelix Fietkau alpha_raw = 0; 580717f6bedSFelix Fietkau 581717f6bedSFelix Fietkau for (i = 0; i <= L; i++) { 582717f6bedSFelix Fietkau int theta_tilde = 583717f6bedSFelix Fietkau ((theta[i + I] << M) + y_est[i + I]) / y_est[i + I]; 584717f6bedSFelix Fietkau theta_tilde = 585717f6bedSFelix Fietkau ((theta_tilde << M) + y_est[i + I]) / y_est[i + I]; 586717f6bedSFelix Fietkau theta_tilde = 587717f6bedSFelix Fietkau ((theta_tilde << M) + y_est[i + I]) / y_est[i + I]; 588717f6bedSFelix Fietkau beta_raw = beta_raw + B1_tmp[i] * theta_tilde; 589717f6bedSFelix Fietkau alpha_raw = alpha_raw + B2_tmp[i] * theta_tilde; 590717f6bedSFelix Fietkau } 591717f6bedSFelix Fietkau 592717f6bedSFelix Fietkau Q_beta = find_proper_scale(find_expn(abs(beta_raw)), 10); 593717f6bedSFelix Fietkau Q_alpha = find_proper_scale(find_expn(abs(alpha_raw)), 10); 594717f6bedSFelix Fietkau beta_raw = beta_raw / (1 << Q_beta); 595717f6bedSFelix Fietkau alpha_raw = alpha_raw / (1 << Q_alpha); 596717f6bedSFelix Fietkau 597717f6bedSFelix Fietkau alpha = (alpha_raw << 10) / scale_B; 598717f6bedSFelix Fietkau beta = (beta_raw << 10) / scale_B; 599717f6bedSFelix Fietkau order_1 = 3 * M - Q_x - Q_B1 - Q_beta + 10 + Q_scale_B + 5; 600717f6bedSFelix Fietkau order_2 = 3 * M - Q_x - Q_B2 - Q_alpha + 10 + Q_scale_B + 5; 601717f6bedSFelix Fietkau order1_5x = order_1 / 5; 602717f6bedSFelix Fietkau order2_3x = order_2 / 3; 603717f6bedSFelix Fietkau order1_5x_rem = order_1 - 5 * order1_5x; 604717f6bedSFelix Fietkau order2_3x_rem = order_2 - 3 * order2_3x; 605717f6bedSFelix Fietkau 606717f6bedSFelix Fietkau for (i = 0; i < PAPRD_TABLE_SZ; i++) { 607717f6bedSFelix Fietkau int PA_angle; 608717f6bedSFelix Fietkau 609717f6bedSFelix Fietkau /* pa_table[4] is calculated from PA_angle for i=5 */ 610717f6bedSFelix Fietkau if (i == 4) 611717f6bedSFelix Fietkau continue; 612717f6bedSFelix Fietkau 613717f6bedSFelix Fietkau tmp = i * 32; 614717f6bedSFelix Fietkau if (beta > 0) 615717f6bedSFelix Fietkau y5 = (((beta * tmp - 64) >> 6) - 616717f6bedSFelix Fietkau (1 << order1_5x)) / (1 << order1_5x); 617717f6bedSFelix Fietkau else 618717f6bedSFelix Fietkau y5 = ((((beta * tmp - 64) >> 6) + 619717f6bedSFelix Fietkau (1 << order1_5x)) / (1 << order1_5x)); 620717f6bedSFelix Fietkau 621717f6bedSFelix Fietkau y5 = (y5 * tmp) / (1 << order1_5x); 622717f6bedSFelix Fietkau y5 = (y5 * tmp) / (1 << order1_5x); 623717f6bedSFelix Fietkau y5 = (y5 * tmp) / (1 << order1_5x); 624717f6bedSFelix Fietkau y5 = (y5 * tmp) / (1 << order1_5x); 625717f6bedSFelix Fietkau y5 = y5 / (1 << order1_5x_rem); 626717f6bedSFelix Fietkau 627717f6bedSFelix Fietkau if (beta > 0) 628717f6bedSFelix Fietkau y3 = (alpha * tmp - 629717f6bedSFelix Fietkau (1 << order2_3x)) / (1 << order2_3x); 630717f6bedSFelix Fietkau else 631717f6bedSFelix Fietkau y3 = (alpha * tmp + 632717f6bedSFelix Fietkau (1 << order2_3x)) / (1 << order2_3x); 633717f6bedSFelix Fietkau y3 = (y3 * tmp) / (1 << order2_3x); 634717f6bedSFelix Fietkau y3 = (y3 * tmp) / (1 << order2_3x); 635717f6bedSFelix Fietkau y3 = y3 / (1 << order2_3x_rem); 636717f6bedSFelix Fietkau 637717f6bedSFelix Fietkau if (i < 4) { 638717f6bedSFelix Fietkau PA_angle = 0; 639717f6bedSFelix Fietkau } else { 640717f6bedSFelix Fietkau PA_angle = y5 + y3; 641717f6bedSFelix Fietkau if (PA_angle < -150) 642717f6bedSFelix Fietkau PA_angle = -150; 643717f6bedSFelix Fietkau else if (PA_angle > 150) 644717f6bedSFelix Fietkau PA_angle = 150; 645717f6bedSFelix Fietkau } 646717f6bedSFelix Fietkau 647717f6bedSFelix Fietkau pa_table[i] = ((PA_in[i] & 0x7ff) << 11) + (PA_angle & 0x7ff); 648717f6bedSFelix Fietkau if (i == 5) { 649717f6bedSFelix Fietkau PA_angle = (PA_angle + 2) >> 1; 650717f6bedSFelix Fietkau pa_table[i - 1] = ((PA_in[i - 1] & 0x7ff) << 11) + 651717f6bedSFelix Fietkau (PA_angle & 0x7ff); 652717f6bedSFelix Fietkau } 653717f6bedSFelix Fietkau } 654717f6bedSFelix Fietkau 655717f6bedSFelix Fietkau *gain = G_fxp; 656717f6bedSFelix Fietkau return true; 657717f6bedSFelix Fietkau } 658717f6bedSFelix Fietkau 659717f6bedSFelix Fietkau void ar9003_paprd_populate_single_table(struct ath_hw *ah, 66020bd2a09SFelix Fietkau struct ath9k_hw_cal_data *caldata, 66120bd2a09SFelix Fietkau int chain) 662717f6bedSFelix Fietkau { 66320bd2a09SFelix Fietkau u32 *paprd_table_val = caldata->pa_table[chain]; 66420bd2a09SFelix Fietkau u32 small_signal_gain = caldata->small_signal_gain[chain]; 6651bf38661SFelix Fietkau u32 training_power = ah->paprd_training_power; 666717f6bedSFelix Fietkau u32 reg = 0; 667717f6bedSFelix Fietkau int i; 668717f6bedSFelix Fietkau 669717f6bedSFelix Fietkau if (chain == 0) 670717f6bedSFelix Fietkau reg = AR_PHY_PAPRD_MEM_TAB_B0; 671717f6bedSFelix Fietkau else if (chain == 1) 672717f6bedSFelix Fietkau reg = AR_PHY_PAPRD_MEM_TAB_B1; 673717f6bedSFelix Fietkau else if (chain == 2) 674717f6bedSFelix Fietkau reg = AR_PHY_PAPRD_MEM_TAB_B2; 675717f6bedSFelix Fietkau 676717f6bedSFelix Fietkau for (i = 0; i < PAPRD_TABLE_SZ; i++) { 677717f6bedSFelix Fietkau REG_WRITE(ah, reg, paprd_table_val[i]); 678717f6bedSFelix Fietkau reg = reg + 4; 679717f6bedSFelix Fietkau } 680717f6bedSFelix Fietkau 681717f6bedSFelix Fietkau if (chain == 0) 682717f6bedSFelix Fietkau reg = AR_PHY_PA_GAIN123_B0; 683717f6bedSFelix Fietkau else if (chain == 1) 684717f6bedSFelix Fietkau reg = AR_PHY_PA_GAIN123_B1; 685717f6bedSFelix Fietkau else 686717f6bedSFelix Fietkau reg = AR_PHY_PA_GAIN123_B2; 687717f6bedSFelix Fietkau 688717f6bedSFelix Fietkau REG_RMW_FIELD(ah, reg, AR_PHY_PA_GAIN123_PA_GAIN1, small_signal_gain); 689717f6bedSFelix Fietkau 690717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL1_B0, 691717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL, 692717f6bedSFelix Fietkau training_power); 693717f6bedSFelix Fietkau 69411441fb8SVasanthakumar Thiagarajan if (ah->caps.tx_chainmask & BIT(1)) 695717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL1_B1, 696717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL, 697717f6bedSFelix Fietkau training_power); 698717f6bedSFelix Fietkau 69911441fb8SVasanthakumar Thiagarajan if (ah->caps.tx_chainmask & BIT(2)) 700717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL1_B2, 701717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL, 702717f6bedSFelix Fietkau training_power); 703717f6bedSFelix Fietkau } 704717f6bedSFelix Fietkau EXPORT_SYMBOL(ar9003_paprd_populate_single_table); 705717f6bedSFelix Fietkau 706717f6bedSFelix Fietkau int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain) 707717f6bedSFelix Fietkau { 708717f6bedSFelix Fietkau unsigned int i, desired_gain, gain_index; 7091bf38661SFelix Fietkau unsigned int train_power = ah->paprd_training_power; 710717f6bedSFelix Fietkau 711717f6bedSFelix Fietkau desired_gain = ar9003_get_desired_gain(ah, chain, train_power); 712717f6bedSFelix Fietkau 713717f6bedSFelix Fietkau gain_index = 0; 714717f6bedSFelix Fietkau for (i = 0; i < 32; i++) { 715717f6bedSFelix Fietkau if (ah->paprd_gain_table_index[i] >= desired_gain) 716717f6bedSFelix Fietkau break; 717717f6bedSFelix Fietkau gain_index++; 718717f6bedSFelix Fietkau } 719717f6bedSFelix Fietkau 720717f6bedSFelix Fietkau ar9003_tx_force_gain(ah, gain_index); 721717f6bedSFelix Fietkau 722717f6bedSFelix Fietkau REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1, 723717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE); 724717f6bedSFelix Fietkau 725717f6bedSFelix Fietkau return 0; 726717f6bedSFelix Fietkau } 727717f6bedSFelix Fietkau EXPORT_SYMBOL(ar9003_paprd_setup_gain_table); 728717f6bedSFelix Fietkau 72920bd2a09SFelix Fietkau int ar9003_paprd_create_curve(struct ath_hw *ah, 73020bd2a09SFelix Fietkau struct ath9k_hw_cal_data *caldata, int chain) 731717f6bedSFelix Fietkau { 73220bd2a09SFelix Fietkau u16 *small_signal_gain = &caldata->small_signal_gain[chain]; 73320bd2a09SFelix Fietkau u32 *pa_table = caldata->pa_table[chain]; 734717f6bedSFelix Fietkau u32 *data_L, *data_U; 735717f6bedSFelix Fietkau int i, status = 0; 736717f6bedSFelix Fietkau u32 *buf; 737717f6bedSFelix Fietkau u32 reg; 738717f6bedSFelix Fietkau 73920bd2a09SFelix Fietkau memset(caldata->pa_table[chain], 0, sizeof(caldata->pa_table[chain])); 740717f6bedSFelix Fietkau 741717f6bedSFelix Fietkau buf = kmalloc(2 * 48 * sizeof(u32), GFP_ATOMIC); 742717f6bedSFelix Fietkau if (!buf) 743717f6bedSFelix Fietkau return -ENOMEM; 744717f6bedSFelix Fietkau 745717f6bedSFelix Fietkau data_L = &buf[0]; 746717f6bedSFelix Fietkau data_U = &buf[48]; 747717f6bedSFelix Fietkau 748717f6bedSFelix Fietkau REG_CLR_BIT(ah, AR_PHY_CHAN_INFO_MEMORY, 749717f6bedSFelix Fietkau AR_PHY_CHAN_INFO_MEMORY_CHANINFOMEM_S2_READ); 750717f6bedSFelix Fietkau 751717f6bedSFelix Fietkau reg = AR_PHY_CHAN_INFO_TAB_0; 752717f6bedSFelix Fietkau for (i = 0; i < 48; i++) 753717f6bedSFelix Fietkau data_L[i] = REG_READ(ah, reg + (i << 2)); 754717f6bedSFelix Fietkau 755717f6bedSFelix Fietkau REG_SET_BIT(ah, AR_PHY_CHAN_INFO_MEMORY, 756717f6bedSFelix Fietkau AR_PHY_CHAN_INFO_MEMORY_CHANINFOMEM_S2_READ); 757717f6bedSFelix Fietkau 758717f6bedSFelix Fietkau for (i = 0; i < 48; i++) 759717f6bedSFelix Fietkau data_U[i] = REG_READ(ah, reg + (i << 2)); 760717f6bedSFelix Fietkau 761717f6bedSFelix Fietkau if (!create_pa_curve(data_L, data_U, pa_table, small_signal_gain)) 762717f6bedSFelix Fietkau status = -2; 763717f6bedSFelix Fietkau 764717f6bedSFelix Fietkau REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1, 765717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE); 766717f6bedSFelix Fietkau 767717f6bedSFelix Fietkau kfree(buf); 768717f6bedSFelix Fietkau 769717f6bedSFelix Fietkau return status; 770717f6bedSFelix Fietkau } 771717f6bedSFelix Fietkau EXPORT_SYMBOL(ar9003_paprd_create_curve); 772717f6bedSFelix Fietkau 773717f6bedSFelix Fietkau int ar9003_paprd_init_table(struct ath_hw *ah) 774717f6bedSFelix Fietkau { 7751bf38661SFelix Fietkau int ret; 7761bf38661SFelix Fietkau 7771bf38661SFelix Fietkau ret = ar9003_paprd_setup_single_table(ah); 7781bf38661SFelix Fietkau if (ret < 0) 7791bf38661SFelix Fietkau return ret; 7801bf38661SFelix Fietkau 781717f6bedSFelix Fietkau ar9003_paprd_get_gain_table(ah); 782717f6bedSFelix Fietkau return 0; 783717f6bedSFelix Fietkau } 784717f6bedSFelix Fietkau EXPORT_SYMBOL(ar9003_paprd_init_table); 785717f6bedSFelix Fietkau 786717f6bedSFelix Fietkau bool ar9003_paprd_is_done(struct ath_hw *ah) 787717f6bedSFelix Fietkau { 788*0e44d48cSMohammed Shafi Shajakhan int paprd_done, agc2_pwr; 789*0e44d48cSMohammed Shafi Shajakhan paprd_done = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_STAT1, 790717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE); 791*0e44d48cSMohammed Shafi Shajakhan 792*0e44d48cSMohammed Shafi Shajakhan if (paprd_done == 0x1) { 793*0e44d48cSMohammed Shafi Shajakhan agc2_pwr = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_STAT1, 794*0e44d48cSMohammed Shafi Shajakhan AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_AGC2_PWR); 795*0e44d48cSMohammed Shafi Shajakhan 796*0e44d48cSMohammed Shafi Shajakhan ath_dbg(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, 797*0e44d48cSMohammed Shafi Shajakhan "AGC2_PWR = 0x%x training done = 0x%x\n", 798*0e44d48cSMohammed Shafi Shajakhan agc2_pwr, paprd_done); 799*0e44d48cSMohammed Shafi Shajakhan /* 800*0e44d48cSMohammed Shafi Shajakhan * agc2_pwr range should not be less than 'IDEAL_AGC2_PWR_CHANGE' 801*0e44d48cSMohammed Shafi Shajakhan * when the training is completely done, otherwise retraining is 802*0e44d48cSMohammed Shafi Shajakhan * done to make sure the value is in ideal range 803*0e44d48cSMohammed Shafi Shajakhan */ 804*0e44d48cSMohammed Shafi Shajakhan if (agc2_pwr <= PAPRD_IDEAL_AGC2_PWR_RANGE) 805*0e44d48cSMohammed Shafi Shajakhan paprd_done = 0; 806*0e44d48cSMohammed Shafi Shajakhan } 807*0e44d48cSMohammed Shafi Shajakhan 808*0e44d48cSMohammed Shafi Shajakhan return !!paprd_done; 809717f6bedSFelix Fietkau } 810717f6bedSFelix Fietkau EXPORT_SYMBOL(ar9003_paprd_is_done); 811