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 86*7952ca5bSMohammed Shafi Shajakhan switch (get_streams(common->tx_chainmask)) { 87*7952ca5bSMohammed Shafi Shajakhan case 1: 88*7952ca5bSMohammed Shafi Shajakhan delta = 6; 89*7952ca5bSMohammed Shafi Shajakhan break; 90*7952ca5bSMohammed Shafi Shajakhan case 2: 91*7952ca5bSMohammed Shafi Shajakhan delta = 4; 92*7952ca5bSMohammed Shafi Shajakhan break; 93*7952ca5bSMohammed Shafi Shajakhan case 3: 94*7952ca5bSMohammed Shafi Shajakhan delta = 2; 95*7952ca5bSMohammed Shafi Shajakhan break; 96*7952ca5bSMohammed Shafi Shajakhan default: 97*7952ca5bSMohammed Shafi Shajakhan delta = 0; 98*7952ca5bSMohammed Shafi Shajakhan ath_dbg(common, ATH_DBG_CALIBRATE, 99*7952ca5bSMohammed Shafi Shajakhan "Invalid tx-chainmask: %u\n", common->tx_chainmask); 100*7952ca5bSMohammed Shafi Shajakhan } 101*7952ca5bSMohammed Shafi Shajakhan 102*7952ca5bSMohammed Shafi Shajakhan power += delta; 1031bf38661SFelix Fietkau return power; 1041bf38661SFelix Fietkau } 1051bf38661SFelix Fietkau 1061bf38661SFelix Fietkau static int ar9003_paprd_setup_single_table(struct ath_hw *ah) 1071bf38661SFelix Fietkau { 1081bf38661SFelix Fietkau struct ath_common *common = ath9k_hw_common(ah); 10907b2fa5aSJoe Perches static const u32 ctrl0[3] = { 110717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL0_B0, 111717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL0_B1, 112717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL0_B2 113717f6bedSFelix Fietkau }; 11407b2fa5aSJoe Perches static const u32 ctrl1[3] = { 115717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_B0, 116717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_B1, 117717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_B2 118717f6bedSFelix Fietkau }; 1191bf38661SFelix Fietkau int training_power; 120717f6bedSFelix Fietkau int i; 121717f6bedSFelix Fietkau 1221bf38661SFelix Fietkau if (IS_CHAN_2GHZ(ah->curchan)) 1231bf38661SFelix Fietkau training_power = ar9003_get_training_power_2g(ah); 1241bf38661SFelix Fietkau else 1251bf38661SFelix Fietkau training_power = ar9003_get_training_power_5g(ah); 1261bf38661SFelix Fietkau 12784288044SMohammed Shafi Shajakhan ath_dbg(common, ATH_DBG_CALIBRATE, 12884288044SMohammed Shafi Shajakhan "Training power: %d, Target power: %d\n", 12984288044SMohammed Shafi Shajakhan training_power, ah->paprd_target_power); 13084288044SMohammed Shafi Shajakhan 1311bf38661SFelix Fietkau if (training_power < 0) { 1321bf38661SFelix Fietkau ath_dbg(common, ATH_DBG_CALIBRATE, 1331bf38661SFelix Fietkau "PAPRD target power delta out of range"); 1341bf38661SFelix Fietkau return -ERANGE; 1351bf38661SFelix Fietkau } 1361bf38661SFelix Fietkau ah->paprd_training_power = training_power; 1371bf38661SFelix Fietkau 1387072bf62SVasanthakumar Thiagarajan REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2AM, AR_PHY_PAPRD_AM2AM_MASK, 1397072bf62SVasanthakumar Thiagarajan ah->paprd_ratemask); 1407072bf62SVasanthakumar Thiagarajan REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2PM, AR_PHY_PAPRD_AM2PM_MASK, 1417072bf62SVasanthakumar Thiagarajan ah->paprd_ratemask); 1427072bf62SVasanthakumar Thiagarajan REG_RMW_FIELD(ah, AR_PHY_PAPRD_HT40, AR_PHY_PAPRD_HT40_MASK, 143f1a8abb0SFelix Fietkau ah->paprd_ratemask_ht40); 144717f6bedSFelix Fietkau 14511441fb8SVasanthakumar Thiagarajan for (i = 0; i < ah->caps.max_txchains; i++) { 146717f6bedSFelix Fietkau REG_RMW_FIELD(ah, ctrl0[i], 147717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL0_USE_SINGLE_TABLE_MASK, 1); 148717f6bedSFelix Fietkau REG_RMW_FIELD(ah, ctrl1[i], 149717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_ADAPTIVE_AM2PM_ENABLE, 1); 150717f6bedSFelix Fietkau REG_RMW_FIELD(ah, ctrl1[i], 151717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_ADAPTIVE_AM2AM_ENABLE, 1); 152717f6bedSFelix Fietkau REG_RMW_FIELD(ah, ctrl1[i], 153717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_ADAPTIVE_SCALING_ENA, 0); 154717f6bedSFelix Fietkau REG_RMW_FIELD(ah, ctrl1[i], 155717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_PA_GAIN_SCALE_FACT_MASK, 181); 156717f6bedSFelix Fietkau REG_RMW_FIELD(ah, ctrl1[i], 157717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_PAPRD_MAG_SCALE_FACT, 361); 158717f6bedSFelix Fietkau REG_RMW_FIELD(ah, ctrl1[i], 159717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_ADAPTIVE_SCALING_ENA, 0); 160717f6bedSFelix Fietkau REG_RMW_FIELD(ah, ctrl0[i], 161717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL0_PAPRD_MAG_THRSH, 3); 162717f6bedSFelix Fietkau } 163717f6bedSFelix Fietkau 164717f6bedSFelix Fietkau ar9003_paprd_enable(ah, false); 165717f6bedSFelix Fietkau 166717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, 167717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_LB_SKIP, 0x30); 168717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, 169717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_LB_ENABLE, 1); 170717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, 171717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_TX_GAIN_FORCE, 1); 172717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, 173717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_RX_BB_GAIN_FORCE, 0); 174717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, 175717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_IQCORR_ENABLE, 0); 176717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, 177717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_AGC2_SETTLING, 28); 178717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1, 179717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL1_CF_CF_PAPRD_TRAIN_ENABLE, 1); 180717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL2, 181717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL2_CF_PAPRD_INIT_RX_BB_GAIN, 147); 182717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, 183717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_FINE_CORR_LEN, 4); 184717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, 185717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_COARSE_CORR_LEN, 4); 186717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, 187717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_NUM_CORR_STAGES, 7); 188717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, 189717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_MIN_LOOPBACK_DEL, 1); 19011441fb8SVasanthakumar Thiagarajan if (AR_SREV_9485(ah)) 191717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, 19211441fb8SVasanthakumar Thiagarajan AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP, 19311441fb8SVasanthakumar Thiagarajan -3); 19411441fb8SVasanthakumar Thiagarajan else 19511441fb8SVasanthakumar Thiagarajan REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, 19611441fb8SVasanthakumar Thiagarajan AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP, 19711441fb8SVasanthakumar Thiagarajan -6); 198717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, 199717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_ADC_DESIRED_SIZE, 200717f6bedSFelix Fietkau -15); 201717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3, 202717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_BBTXMIX_DISABLE, 1); 203717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL4, 204717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_SAFETY_DELTA, 0); 205717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL4, 206717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_MIN_CORR, 400); 207717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL4, 208717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_NUM_TRAIN_SAMPLES, 209717f6bedSFelix Fietkau 100); 210717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_0_B0, 211717f6bedSFelix Fietkau AR_PHY_PAPRD_PRE_POST_SCALING, 261376); 212717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_1_B0, 213717f6bedSFelix Fietkau AR_PHY_PAPRD_PRE_POST_SCALING, 248079); 214717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_2_B0, 215717f6bedSFelix Fietkau AR_PHY_PAPRD_PRE_POST_SCALING, 233759); 216717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_3_B0, 217717f6bedSFelix Fietkau AR_PHY_PAPRD_PRE_POST_SCALING, 220464); 218717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_4_B0, 219717f6bedSFelix Fietkau AR_PHY_PAPRD_PRE_POST_SCALING, 208194); 220717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_5_B0, 221717f6bedSFelix Fietkau AR_PHY_PAPRD_PRE_POST_SCALING, 196949); 222717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_6_B0, 223717f6bedSFelix Fietkau AR_PHY_PAPRD_PRE_POST_SCALING, 185706); 224717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_7_B0, 225717f6bedSFelix Fietkau AR_PHY_PAPRD_PRE_POST_SCALING, 175487); 2261bf38661SFelix Fietkau return 0; 227717f6bedSFelix Fietkau } 228717f6bedSFelix Fietkau 229717f6bedSFelix Fietkau static void ar9003_paprd_get_gain_table(struct ath_hw *ah) 230717f6bedSFelix Fietkau { 231717f6bedSFelix Fietkau u32 *entry = ah->paprd_gain_table_entries; 232717f6bedSFelix Fietkau u8 *index = ah->paprd_gain_table_index; 233717f6bedSFelix Fietkau u32 reg = AR_PHY_TXGAIN_TABLE; 234717f6bedSFelix Fietkau int i; 235717f6bedSFelix Fietkau 236717f6bedSFelix Fietkau memset(entry, 0, sizeof(ah->paprd_gain_table_entries)); 237717f6bedSFelix Fietkau memset(index, 0, sizeof(ah->paprd_gain_table_index)); 238717f6bedSFelix Fietkau 239717f6bedSFelix Fietkau for (i = 0; i < 32; i++) { 240717f6bedSFelix Fietkau entry[i] = REG_READ(ah, reg); 241717f6bedSFelix Fietkau index[i] = (entry[i] >> 24) & 0xff; 242717f6bedSFelix Fietkau reg += 4; 243717f6bedSFelix Fietkau } 244717f6bedSFelix Fietkau } 245717f6bedSFelix Fietkau 246717f6bedSFelix Fietkau static unsigned int ar9003_get_desired_gain(struct ath_hw *ah, int chain, 247717f6bedSFelix Fietkau int target_power) 248717f6bedSFelix Fietkau { 249717f6bedSFelix Fietkau int olpc_gain_delta = 0; 250717f6bedSFelix Fietkau int alpha_therm, alpha_volt; 251717f6bedSFelix Fietkau int therm_cal_value, volt_cal_value; 252717f6bedSFelix Fietkau int therm_value, volt_value; 253717f6bedSFelix Fietkau int thermal_gain_corr, voltage_gain_corr; 254717f6bedSFelix Fietkau int desired_scale, desired_gain = 0; 255717f6bedSFelix Fietkau u32 reg; 256717f6bedSFelix Fietkau 257717f6bedSFelix Fietkau REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1, 258717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE); 259717f6bedSFelix Fietkau desired_scale = REG_READ_FIELD(ah, AR_PHY_TPC_12, 260717f6bedSFelix Fietkau AR_PHY_TPC_12_DESIRED_SCALE_HT40_5); 261717f6bedSFelix Fietkau alpha_therm = REG_READ_FIELD(ah, AR_PHY_TPC_19, 262717f6bedSFelix Fietkau AR_PHY_TPC_19_ALPHA_THERM); 263717f6bedSFelix Fietkau alpha_volt = REG_READ_FIELD(ah, AR_PHY_TPC_19, 264717f6bedSFelix Fietkau AR_PHY_TPC_19_ALPHA_VOLT); 265717f6bedSFelix Fietkau therm_cal_value = REG_READ_FIELD(ah, AR_PHY_TPC_18, 266717f6bedSFelix Fietkau AR_PHY_TPC_18_THERM_CAL_VALUE); 267717f6bedSFelix Fietkau volt_cal_value = REG_READ_FIELD(ah, AR_PHY_TPC_18, 268717f6bedSFelix Fietkau AR_PHY_TPC_18_VOLT_CAL_VALUE); 269717f6bedSFelix Fietkau therm_value = REG_READ_FIELD(ah, AR_PHY_BB_THERM_ADC_4, 270717f6bedSFelix Fietkau AR_PHY_BB_THERM_ADC_4_LATEST_THERM_VALUE); 271717f6bedSFelix Fietkau volt_value = REG_READ_FIELD(ah, AR_PHY_BB_THERM_ADC_4, 272717f6bedSFelix Fietkau AR_PHY_BB_THERM_ADC_4_LATEST_VOLT_VALUE); 273717f6bedSFelix Fietkau 274717f6bedSFelix Fietkau if (chain == 0) 275717f6bedSFelix Fietkau reg = AR_PHY_TPC_11_B0; 276717f6bedSFelix Fietkau else if (chain == 1) 277717f6bedSFelix Fietkau reg = AR_PHY_TPC_11_B1; 278717f6bedSFelix Fietkau else 279717f6bedSFelix Fietkau reg = AR_PHY_TPC_11_B2; 280717f6bedSFelix Fietkau 281717f6bedSFelix Fietkau olpc_gain_delta = REG_READ_FIELD(ah, reg, 282717f6bedSFelix Fietkau AR_PHY_TPC_11_OLPC_GAIN_DELTA); 283717f6bedSFelix Fietkau 284717f6bedSFelix Fietkau if (olpc_gain_delta >= 128) 285717f6bedSFelix Fietkau olpc_gain_delta = olpc_gain_delta - 256; 286717f6bedSFelix Fietkau 287717f6bedSFelix Fietkau thermal_gain_corr = (alpha_therm * (therm_value - therm_cal_value) + 288717f6bedSFelix Fietkau (256 / 2)) / 256; 289717f6bedSFelix Fietkau voltage_gain_corr = (alpha_volt * (volt_value - volt_cal_value) + 290717f6bedSFelix Fietkau (128 / 2)) / 128; 291717f6bedSFelix Fietkau desired_gain = target_power - olpc_gain_delta - thermal_gain_corr - 292717f6bedSFelix Fietkau voltage_gain_corr + desired_scale; 293717f6bedSFelix Fietkau 294717f6bedSFelix Fietkau return desired_gain; 295717f6bedSFelix Fietkau } 296717f6bedSFelix Fietkau 297717f6bedSFelix Fietkau static void ar9003_tx_force_gain(struct ath_hw *ah, unsigned int gain_index) 298717f6bedSFelix Fietkau { 299717f6bedSFelix Fietkau int selected_gain_entry, txbb1dbgain, txbb6dbgain, txmxrgain; 300717f6bedSFelix Fietkau int padrvgnA, padrvgnB, padrvgnC, padrvgnD; 301717f6bedSFelix Fietkau u32 *gain_table_entries = ah->paprd_gain_table_entries; 302717f6bedSFelix Fietkau 303717f6bedSFelix Fietkau selected_gain_entry = gain_table_entries[gain_index]; 304717f6bedSFelix Fietkau txbb1dbgain = selected_gain_entry & 0x7; 305717f6bedSFelix Fietkau txbb6dbgain = (selected_gain_entry >> 3) & 0x3; 306717f6bedSFelix Fietkau txmxrgain = (selected_gain_entry >> 5) & 0xf; 307717f6bedSFelix Fietkau padrvgnA = (selected_gain_entry >> 9) & 0xf; 308717f6bedSFelix Fietkau padrvgnB = (selected_gain_entry >> 13) & 0xf; 309717f6bedSFelix Fietkau padrvgnC = (selected_gain_entry >> 17) & 0xf; 310717f6bedSFelix Fietkau padrvgnD = (selected_gain_entry >> 21) & 0x3; 311717f6bedSFelix Fietkau 312717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 313717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCED_TXBB1DBGAIN, txbb1dbgain); 314717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 315717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCED_TXBB6DBGAIN, txbb6dbgain); 316717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 317717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCED_TXMXRGAIN, txmxrgain); 318717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 319717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNA, padrvgnA); 320717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 321717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNB, padrvgnB); 322717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 323717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNC, padrvgnC); 324717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 325717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGND, padrvgnD); 326717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 327717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCED_ENABLE_PAL, 0); 328717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 329717f6bedSFelix Fietkau AR_PHY_TX_FORCED_GAIN_FORCE_TX_GAIN, 0); 330717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TPC_1, AR_PHY_TPC_1_FORCED_DAC_GAIN, 0); 331717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_TPC_1, AR_PHY_TPC_1_FORCE_DAC_GAIN, 0); 332717f6bedSFelix Fietkau } 333717f6bedSFelix Fietkau 334717f6bedSFelix Fietkau static inline int find_expn(int num) 335717f6bedSFelix Fietkau { 336717f6bedSFelix Fietkau return fls(num) - 1; 337717f6bedSFelix Fietkau } 338717f6bedSFelix Fietkau 339717f6bedSFelix Fietkau static inline int find_proper_scale(int expn, int N) 340717f6bedSFelix Fietkau { 341717f6bedSFelix Fietkau return (expn > N) ? expn - 10 : 0; 342717f6bedSFelix Fietkau } 343717f6bedSFelix Fietkau 344717f6bedSFelix Fietkau #define NUM_BIN 23 345717f6bedSFelix Fietkau 346717f6bedSFelix Fietkau static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain) 347717f6bedSFelix Fietkau { 348717f6bedSFelix Fietkau unsigned int thresh_accum_cnt; 349717f6bedSFelix Fietkau int x_est[NUM_BIN + 1], Y[NUM_BIN + 1], theta[NUM_BIN + 1]; 350717f6bedSFelix Fietkau int PA_in[NUM_BIN + 1]; 351717f6bedSFelix Fietkau int B1_tmp[NUM_BIN + 1], B2_tmp[NUM_BIN + 1]; 352717f6bedSFelix Fietkau unsigned int B1_abs_max, B2_abs_max; 353717f6bedSFelix Fietkau int max_index, scale_factor; 354717f6bedSFelix Fietkau int y_est[NUM_BIN + 1]; 355717f6bedSFelix Fietkau int x_est_fxp1_nonlin, x_tilde[NUM_BIN + 1]; 356717f6bedSFelix Fietkau unsigned int x_tilde_abs; 357717f6bedSFelix Fietkau int G_fxp, Y_intercept, order_x_by_y, M, I, L, sum_y_sqr, sum_y_quad; 358717f6bedSFelix Fietkau int Q_x, Q_B1, Q_B2, beta_raw, alpha_raw, scale_B; 359717f6bedSFelix Fietkau int Q_scale_B, Q_beta, Q_alpha, alpha, beta, order_1, order_2; 360717f6bedSFelix Fietkau int order1_5x, order2_3x, order1_5x_rem, order2_3x_rem; 361717f6bedSFelix Fietkau int y5, y3, tmp; 362717f6bedSFelix Fietkau int theta_low_bin = 0; 363717f6bedSFelix Fietkau int i; 364717f6bedSFelix Fietkau 365717f6bedSFelix Fietkau /* disregard any bin that contains <= 16 samples */ 366717f6bedSFelix Fietkau thresh_accum_cnt = 16; 367717f6bedSFelix Fietkau scale_factor = 5; 368717f6bedSFelix Fietkau max_index = 0; 369717f6bedSFelix Fietkau memset(theta, 0, sizeof(theta)); 370717f6bedSFelix Fietkau memset(x_est, 0, sizeof(x_est)); 371717f6bedSFelix Fietkau memset(Y, 0, sizeof(Y)); 372717f6bedSFelix Fietkau memset(y_est, 0, sizeof(y_est)); 373717f6bedSFelix Fietkau memset(x_tilde, 0, sizeof(x_tilde)); 374717f6bedSFelix Fietkau 375717f6bedSFelix Fietkau for (i = 0; i < NUM_BIN; i++) { 376717f6bedSFelix Fietkau s32 accum_cnt, accum_tx, accum_rx, accum_ang; 377717f6bedSFelix Fietkau 378717f6bedSFelix Fietkau /* number of samples */ 379717f6bedSFelix Fietkau accum_cnt = data_L[i] & 0xffff; 380717f6bedSFelix Fietkau 381717f6bedSFelix Fietkau if (accum_cnt <= thresh_accum_cnt) 382717f6bedSFelix Fietkau continue; 383717f6bedSFelix Fietkau 384717f6bedSFelix Fietkau /* sum(tx amplitude) */ 385717f6bedSFelix Fietkau accum_tx = ((data_L[i] >> 16) & 0xffff) | 386717f6bedSFelix Fietkau ((data_U[i] & 0x7ff) << 16); 387717f6bedSFelix Fietkau 388717f6bedSFelix Fietkau /* sum(rx amplitude distance to lower bin edge) */ 389717f6bedSFelix Fietkau accum_rx = ((data_U[i] >> 11) & 0x1f) | 390717f6bedSFelix Fietkau ((data_L[i + 23] & 0xffff) << 5); 391717f6bedSFelix Fietkau 392717f6bedSFelix Fietkau /* sum(angles) */ 393717f6bedSFelix Fietkau accum_ang = ((data_L[i + 23] >> 16) & 0xffff) | 394717f6bedSFelix Fietkau ((data_U[i + 23] & 0x7ff) << 16); 395717f6bedSFelix Fietkau 396717f6bedSFelix Fietkau accum_tx <<= scale_factor; 397717f6bedSFelix Fietkau accum_rx <<= scale_factor; 398717f6bedSFelix Fietkau x_est[i + 1] = (((accum_tx + accum_cnt) / accum_cnt) + 32) >> 399717f6bedSFelix Fietkau scale_factor; 400717f6bedSFelix Fietkau 401717f6bedSFelix Fietkau Y[i + 1] = ((((accum_rx + accum_cnt) / accum_cnt) + 32) >> 402717f6bedSFelix Fietkau scale_factor) + 403717f6bedSFelix Fietkau (1 << scale_factor) * max_index + 16; 404717f6bedSFelix Fietkau 405717f6bedSFelix Fietkau if (accum_ang >= (1 << 26)) 406717f6bedSFelix Fietkau accum_ang -= 1 << 27; 407717f6bedSFelix Fietkau 408717f6bedSFelix Fietkau theta[i + 1] = ((accum_ang * (1 << scale_factor)) + accum_cnt) / 409717f6bedSFelix Fietkau accum_cnt; 410717f6bedSFelix Fietkau 411717f6bedSFelix Fietkau max_index++; 412717f6bedSFelix Fietkau } 413717f6bedSFelix Fietkau 414717f6bedSFelix Fietkau /* 415717f6bedSFelix Fietkau * Find average theta of first 5 bin and all of those to same value. 416717f6bedSFelix Fietkau * Curve is linear at that range. 417717f6bedSFelix Fietkau */ 418717f6bedSFelix Fietkau for (i = 1; i < 6; i++) 419717f6bedSFelix Fietkau theta_low_bin += theta[i]; 420717f6bedSFelix Fietkau 421717f6bedSFelix Fietkau theta_low_bin = theta_low_bin / 5; 422717f6bedSFelix Fietkau for (i = 1; i < 6; i++) 423717f6bedSFelix Fietkau theta[i] = theta_low_bin; 424717f6bedSFelix Fietkau 425717f6bedSFelix Fietkau /* Set values at origin */ 426717f6bedSFelix Fietkau theta[0] = theta_low_bin; 427717f6bedSFelix Fietkau for (i = 0; i <= max_index; i++) 428717f6bedSFelix Fietkau theta[i] -= theta_low_bin; 429717f6bedSFelix Fietkau 430717f6bedSFelix Fietkau x_est[0] = 0; 431717f6bedSFelix Fietkau Y[0] = 0; 432717f6bedSFelix Fietkau scale_factor = 8; 433717f6bedSFelix Fietkau 434717f6bedSFelix Fietkau /* low signal gain */ 435717f6bedSFelix Fietkau if (x_est[6] == x_est[3]) 436717f6bedSFelix Fietkau return false; 437717f6bedSFelix Fietkau 438717f6bedSFelix Fietkau G_fxp = 439717f6bedSFelix Fietkau (((Y[6] - Y[3]) * 1 << scale_factor) + 440717f6bedSFelix Fietkau (x_est[6] - x_est[3])) / (x_est[6] - x_est[3]); 441717f6bedSFelix Fietkau 4422d3fca18SSenthil Balasubramanian /* prevent division by zero */ 4432d3fca18SSenthil Balasubramanian if (G_fxp == 0) 4442d3fca18SSenthil Balasubramanian return false; 4452d3fca18SSenthil Balasubramanian 446717f6bedSFelix Fietkau Y_intercept = 447717f6bedSFelix Fietkau (G_fxp * (x_est[0] - x_est[3]) + 448717f6bedSFelix Fietkau (1 << scale_factor)) / (1 << scale_factor) + Y[3]; 449717f6bedSFelix Fietkau 450717f6bedSFelix Fietkau for (i = 0; i <= max_index; i++) 451717f6bedSFelix Fietkau y_est[i] = Y[i] - Y_intercept; 452717f6bedSFelix Fietkau 453717f6bedSFelix Fietkau for (i = 0; i <= 3; i++) { 454717f6bedSFelix Fietkau y_est[i] = i * 32; 455717f6bedSFelix Fietkau x_est[i] = ((y_est[i] * 1 << scale_factor) + G_fxp) / G_fxp; 456717f6bedSFelix Fietkau } 457717f6bedSFelix Fietkau 4582d3fca18SSenthil Balasubramanian if (y_est[max_index] == 0) 4592d3fca18SSenthil Balasubramanian return false; 4602d3fca18SSenthil Balasubramanian 461717f6bedSFelix Fietkau x_est_fxp1_nonlin = 462717f6bedSFelix Fietkau x_est[max_index] - ((1 << scale_factor) * y_est[max_index] + 463717f6bedSFelix Fietkau G_fxp) / G_fxp; 464717f6bedSFelix Fietkau 465717f6bedSFelix Fietkau order_x_by_y = 466717f6bedSFelix Fietkau (x_est_fxp1_nonlin + y_est[max_index]) / y_est[max_index]; 467717f6bedSFelix Fietkau 468717f6bedSFelix Fietkau if (order_x_by_y == 0) 469717f6bedSFelix Fietkau M = 10; 470717f6bedSFelix Fietkau else if (order_x_by_y == 1) 471717f6bedSFelix Fietkau M = 9; 472717f6bedSFelix Fietkau else 473717f6bedSFelix Fietkau M = 8; 474717f6bedSFelix Fietkau 475717f6bedSFelix Fietkau I = (max_index > 15) ? 7 : max_index >> 1; 476717f6bedSFelix Fietkau L = max_index - I; 477717f6bedSFelix Fietkau scale_factor = 8; 478717f6bedSFelix Fietkau sum_y_sqr = 0; 479717f6bedSFelix Fietkau sum_y_quad = 0; 480717f6bedSFelix Fietkau x_tilde_abs = 0; 481717f6bedSFelix Fietkau 482717f6bedSFelix Fietkau for (i = 0; i <= L; i++) { 483717f6bedSFelix Fietkau unsigned int y_sqr; 484717f6bedSFelix Fietkau unsigned int y_quad; 485717f6bedSFelix Fietkau unsigned int tmp_abs; 486717f6bedSFelix Fietkau 487717f6bedSFelix Fietkau /* prevent division by zero */ 488717f6bedSFelix Fietkau if (y_est[i + I] == 0) 489717f6bedSFelix Fietkau return false; 490717f6bedSFelix Fietkau 491717f6bedSFelix Fietkau x_est_fxp1_nonlin = 492717f6bedSFelix Fietkau x_est[i + I] - ((1 << scale_factor) * y_est[i + I] + 493717f6bedSFelix Fietkau G_fxp) / G_fxp; 494717f6bedSFelix Fietkau 495717f6bedSFelix Fietkau x_tilde[i] = 496717f6bedSFelix Fietkau (x_est_fxp1_nonlin * (1 << M) + y_est[i + I]) / y_est[i + 497717f6bedSFelix Fietkau I]; 498717f6bedSFelix Fietkau x_tilde[i] = 499717f6bedSFelix Fietkau (x_tilde[i] * (1 << M) + y_est[i + I]) / y_est[i + I]; 500717f6bedSFelix Fietkau x_tilde[i] = 501717f6bedSFelix Fietkau (x_tilde[i] * (1 << M) + y_est[i + I]) / y_est[i + I]; 502717f6bedSFelix Fietkau y_sqr = 503717f6bedSFelix Fietkau (y_est[i + I] * y_est[i + I] + 504717f6bedSFelix Fietkau (scale_factor * scale_factor)) / (scale_factor * 505717f6bedSFelix Fietkau scale_factor); 506717f6bedSFelix Fietkau tmp_abs = abs(x_tilde[i]); 507717f6bedSFelix Fietkau if (tmp_abs > x_tilde_abs) 508717f6bedSFelix Fietkau x_tilde_abs = tmp_abs; 509717f6bedSFelix Fietkau 510717f6bedSFelix Fietkau y_quad = y_sqr * y_sqr; 511717f6bedSFelix Fietkau sum_y_sqr = sum_y_sqr + y_sqr; 512717f6bedSFelix Fietkau sum_y_quad = sum_y_quad + y_quad; 513717f6bedSFelix Fietkau B1_tmp[i] = y_sqr * (L + 1); 514717f6bedSFelix Fietkau B2_tmp[i] = y_sqr; 515717f6bedSFelix Fietkau } 516717f6bedSFelix Fietkau 517717f6bedSFelix Fietkau B1_abs_max = 0; 518717f6bedSFelix Fietkau B2_abs_max = 0; 519717f6bedSFelix Fietkau for (i = 0; i <= L; i++) { 520717f6bedSFelix Fietkau int abs_val; 521717f6bedSFelix Fietkau 522717f6bedSFelix Fietkau B1_tmp[i] -= sum_y_sqr; 523717f6bedSFelix Fietkau B2_tmp[i] = sum_y_quad - sum_y_sqr * B2_tmp[i]; 524717f6bedSFelix Fietkau 525717f6bedSFelix Fietkau abs_val = abs(B1_tmp[i]); 526717f6bedSFelix Fietkau if (abs_val > B1_abs_max) 527717f6bedSFelix Fietkau B1_abs_max = abs_val; 528717f6bedSFelix Fietkau 529717f6bedSFelix Fietkau abs_val = abs(B2_tmp[i]); 530717f6bedSFelix Fietkau if (abs_val > B2_abs_max) 531717f6bedSFelix Fietkau B2_abs_max = abs_val; 532717f6bedSFelix Fietkau } 533717f6bedSFelix Fietkau 534717f6bedSFelix Fietkau Q_x = find_proper_scale(find_expn(x_tilde_abs), 10); 535717f6bedSFelix Fietkau Q_B1 = find_proper_scale(find_expn(B1_abs_max), 10); 536717f6bedSFelix Fietkau Q_B2 = find_proper_scale(find_expn(B2_abs_max), 10); 537717f6bedSFelix Fietkau 538717f6bedSFelix Fietkau beta_raw = 0; 539717f6bedSFelix Fietkau alpha_raw = 0; 540717f6bedSFelix Fietkau for (i = 0; i <= L; i++) { 541717f6bedSFelix Fietkau x_tilde[i] = x_tilde[i] / (1 << Q_x); 542717f6bedSFelix Fietkau B1_tmp[i] = B1_tmp[i] / (1 << Q_B1); 543717f6bedSFelix Fietkau B2_tmp[i] = B2_tmp[i] / (1 << Q_B2); 544717f6bedSFelix Fietkau beta_raw = beta_raw + B1_tmp[i] * x_tilde[i]; 545717f6bedSFelix Fietkau alpha_raw = alpha_raw + B2_tmp[i] * x_tilde[i]; 546717f6bedSFelix Fietkau } 547717f6bedSFelix Fietkau 548717f6bedSFelix Fietkau scale_B = 549717f6bedSFelix Fietkau ((sum_y_quad / scale_factor) * (L + 1) - 550717f6bedSFelix Fietkau (sum_y_sqr / scale_factor) * sum_y_sqr) * scale_factor; 551717f6bedSFelix Fietkau 552717f6bedSFelix Fietkau Q_scale_B = find_proper_scale(find_expn(abs(scale_B)), 10); 553717f6bedSFelix Fietkau scale_B = scale_B / (1 << Q_scale_B); 5542d3fca18SSenthil Balasubramanian if (scale_B == 0) 5552d3fca18SSenthil Balasubramanian return false; 556717f6bedSFelix Fietkau Q_beta = find_proper_scale(find_expn(abs(beta_raw)), 10); 557717f6bedSFelix Fietkau Q_alpha = find_proper_scale(find_expn(abs(alpha_raw)), 10); 558717f6bedSFelix Fietkau beta_raw = beta_raw / (1 << Q_beta); 559717f6bedSFelix Fietkau alpha_raw = alpha_raw / (1 << Q_alpha); 560717f6bedSFelix Fietkau alpha = (alpha_raw << 10) / scale_B; 561717f6bedSFelix Fietkau beta = (beta_raw << 10) / scale_B; 562717f6bedSFelix Fietkau order_1 = 3 * M - Q_x - Q_B1 - Q_beta + 10 + Q_scale_B; 563717f6bedSFelix Fietkau order_2 = 3 * M - Q_x - Q_B2 - Q_alpha + 10 + Q_scale_B; 564717f6bedSFelix Fietkau order1_5x = order_1 / 5; 565717f6bedSFelix Fietkau order2_3x = order_2 / 3; 566717f6bedSFelix Fietkau order1_5x_rem = order_1 - 5 * order1_5x; 567717f6bedSFelix Fietkau order2_3x_rem = order_2 - 3 * order2_3x; 568717f6bedSFelix Fietkau 569717f6bedSFelix Fietkau for (i = 0; i < PAPRD_TABLE_SZ; i++) { 570717f6bedSFelix Fietkau tmp = i * 32; 571717f6bedSFelix Fietkau y5 = ((beta * tmp) >> 6) >> order1_5x; 572717f6bedSFelix Fietkau y5 = (y5 * tmp) >> order1_5x; 573717f6bedSFelix Fietkau y5 = (y5 * tmp) >> order1_5x; 574717f6bedSFelix Fietkau y5 = (y5 * tmp) >> order1_5x; 575717f6bedSFelix Fietkau y5 = (y5 * tmp) >> order1_5x; 576717f6bedSFelix Fietkau y5 = y5 >> order1_5x_rem; 577717f6bedSFelix Fietkau y3 = (alpha * tmp) >> order2_3x; 578717f6bedSFelix Fietkau y3 = (y3 * tmp) >> order2_3x; 579717f6bedSFelix Fietkau y3 = (y3 * tmp) >> order2_3x; 580717f6bedSFelix Fietkau y3 = y3 >> order2_3x_rem; 581717f6bedSFelix Fietkau PA_in[i] = y5 + y3 + (256 * tmp) / G_fxp; 582717f6bedSFelix Fietkau 583717f6bedSFelix Fietkau if (i >= 2) { 584717f6bedSFelix Fietkau tmp = PA_in[i] - PA_in[i - 1]; 585717f6bedSFelix Fietkau if (tmp < 0) 586717f6bedSFelix Fietkau PA_in[i] = 587717f6bedSFelix Fietkau PA_in[i - 1] + (PA_in[i - 1] - 588717f6bedSFelix Fietkau PA_in[i - 2]); 589717f6bedSFelix Fietkau } 590717f6bedSFelix Fietkau 591717f6bedSFelix Fietkau PA_in[i] = (PA_in[i] < 1400) ? PA_in[i] : 1400; 592717f6bedSFelix Fietkau } 593717f6bedSFelix Fietkau 594717f6bedSFelix Fietkau beta_raw = 0; 595717f6bedSFelix Fietkau alpha_raw = 0; 596717f6bedSFelix Fietkau 597717f6bedSFelix Fietkau for (i = 0; i <= L; i++) { 598717f6bedSFelix Fietkau int theta_tilde = 599717f6bedSFelix Fietkau ((theta[i + I] << M) + y_est[i + I]) / y_est[i + I]; 600717f6bedSFelix Fietkau theta_tilde = 601717f6bedSFelix Fietkau ((theta_tilde << M) + y_est[i + I]) / y_est[i + I]; 602717f6bedSFelix Fietkau theta_tilde = 603717f6bedSFelix Fietkau ((theta_tilde << M) + y_est[i + I]) / y_est[i + I]; 604717f6bedSFelix Fietkau beta_raw = beta_raw + B1_tmp[i] * theta_tilde; 605717f6bedSFelix Fietkau alpha_raw = alpha_raw + B2_tmp[i] * theta_tilde; 606717f6bedSFelix Fietkau } 607717f6bedSFelix Fietkau 608717f6bedSFelix Fietkau Q_beta = find_proper_scale(find_expn(abs(beta_raw)), 10); 609717f6bedSFelix Fietkau Q_alpha = find_proper_scale(find_expn(abs(alpha_raw)), 10); 610717f6bedSFelix Fietkau beta_raw = beta_raw / (1 << Q_beta); 611717f6bedSFelix Fietkau alpha_raw = alpha_raw / (1 << Q_alpha); 612717f6bedSFelix Fietkau 613717f6bedSFelix Fietkau alpha = (alpha_raw << 10) / scale_B; 614717f6bedSFelix Fietkau beta = (beta_raw << 10) / scale_B; 615717f6bedSFelix Fietkau order_1 = 3 * M - Q_x - Q_B1 - Q_beta + 10 + Q_scale_B + 5; 616717f6bedSFelix Fietkau order_2 = 3 * M - Q_x - Q_B2 - Q_alpha + 10 + Q_scale_B + 5; 617717f6bedSFelix Fietkau order1_5x = order_1 / 5; 618717f6bedSFelix Fietkau order2_3x = order_2 / 3; 619717f6bedSFelix Fietkau order1_5x_rem = order_1 - 5 * order1_5x; 620717f6bedSFelix Fietkau order2_3x_rem = order_2 - 3 * order2_3x; 621717f6bedSFelix Fietkau 622717f6bedSFelix Fietkau for (i = 0; i < PAPRD_TABLE_SZ; i++) { 623717f6bedSFelix Fietkau int PA_angle; 624717f6bedSFelix Fietkau 625717f6bedSFelix Fietkau /* pa_table[4] is calculated from PA_angle for i=5 */ 626717f6bedSFelix Fietkau if (i == 4) 627717f6bedSFelix Fietkau continue; 628717f6bedSFelix Fietkau 629717f6bedSFelix Fietkau tmp = i * 32; 630717f6bedSFelix Fietkau if (beta > 0) 631717f6bedSFelix Fietkau y5 = (((beta * tmp - 64) >> 6) - 632717f6bedSFelix Fietkau (1 << order1_5x)) / (1 << order1_5x); 633717f6bedSFelix Fietkau else 634717f6bedSFelix Fietkau y5 = ((((beta * tmp - 64) >> 6) + 635717f6bedSFelix Fietkau (1 << order1_5x)) / (1 << order1_5x)); 636717f6bedSFelix Fietkau 637717f6bedSFelix Fietkau y5 = (y5 * tmp) / (1 << order1_5x); 638717f6bedSFelix Fietkau y5 = (y5 * tmp) / (1 << order1_5x); 639717f6bedSFelix Fietkau y5 = (y5 * tmp) / (1 << order1_5x); 640717f6bedSFelix Fietkau y5 = (y5 * tmp) / (1 << order1_5x); 641717f6bedSFelix Fietkau y5 = y5 / (1 << order1_5x_rem); 642717f6bedSFelix Fietkau 643717f6bedSFelix Fietkau if (beta > 0) 644717f6bedSFelix Fietkau y3 = (alpha * tmp - 645717f6bedSFelix Fietkau (1 << order2_3x)) / (1 << order2_3x); 646717f6bedSFelix Fietkau else 647717f6bedSFelix Fietkau y3 = (alpha * tmp + 648717f6bedSFelix Fietkau (1 << order2_3x)) / (1 << order2_3x); 649717f6bedSFelix Fietkau y3 = (y3 * tmp) / (1 << order2_3x); 650717f6bedSFelix Fietkau y3 = (y3 * tmp) / (1 << order2_3x); 651717f6bedSFelix Fietkau y3 = y3 / (1 << order2_3x_rem); 652717f6bedSFelix Fietkau 653717f6bedSFelix Fietkau if (i < 4) { 654717f6bedSFelix Fietkau PA_angle = 0; 655717f6bedSFelix Fietkau } else { 656717f6bedSFelix Fietkau PA_angle = y5 + y3; 657717f6bedSFelix Fietkau if (PA_angle < -150) 658717f6bedSFelix Fietkau PA_angle = -150; 659717f6bedSFelix Fietkau else if (PA_angle > 150) 660717f6bedSFelix Fietkau PA_angle = 150; 661717f6bedSFelix Fietkau } 662717f6bedSFelix Fietkau 663717f6bedSFelix Fietkau pa_table[i] = ((PA_in[i] & 0x7ff) << 11) + (PA_angle & 0x7ff); 664717f6bedSFelix Fietkau if (i == 5) { 665717f6bedSFelix Fietkau PA_angle = (PA_angle + 2) >> 1; 666717f6bedSFelix Fietkau pa_table[i - 1] = ((PA_in[i - 1] & 0x7ff) << 11) + 667717f6bedSFelix Fietkau (PA_angle & 0x7ff); 668717f6bedSFelix Fietkau } 669717f6bedSFelix Fietkau } 670717f6bedSFelix Fietkau 671717f6bedSFelix Fietkau *gain = G_fxp; 672717f6bedSFelix Fietkau return true; 673717f6bedSFelix Fietkau } 674717f6bedSFelix Fietkau 675717f6bedSFelix Fietkau void ar9003_paprd_populate_single_table(struct ath_hw *ah, 67620bd2a09SFelix Fietkau struct ath9k_hw_cal_data *caldata, 67720bd2a09SFelix Fietkau int chain) 678717f6bedSFelix Fietkau { 67920bd2a09SFelix Fietkau u32 *paprd_table_val = caldata->pa_table[chain]; 68020bd2a09SFelix Fietkau u32 small_signal_gain = caldata->small_signal_gain[chain]; 6811bf38661SFelix Fietkau u32 training_power = ah->paprd_training_power; 682717f6bedSFelix Fietkau u32 reg = 0; 683717f6bedSFelix Fietkau int i; 684717f6bedSFelix Fietkau 685717f6bedSFelix Fietkau if (chain == 0) 686717f6bedSFelix Fietkau reg = AR_PHY_PAPRD_MEM_TAB_B0; 687717f6bedSFelix Fietkau else if (chain == 1) 688717f6bedSFelix Fietkau reg = AR_PHY_PAPRD_MEM_TAB_B1; 689717f6bedSFelix Fietkau else if (chain == 2) 690717f6bedSFelix Fietkau reg = AR_PHY_PAPRD_MEM_TAB_B2; 691717f6bedSFelix Fietkau 692717f6bedSFelix Fietkau for (i = 0; i < PAPRD_TABLE_SZ; i++) { 693717f6bedSFelix Fietkau REG_WRITE(ah, reg, paprd_table_val[i]); 694717f6bedSFelix Fietkau reg = reg + 4; 695717f6bedSFelix Fietkau } 696717f6bedSFelix Fietkau 697717f6bedSFelix Fietkau if (chain == 0) 698717f6bedSFelix Fietkau reg = AR_PHY_PA_GAIN123_B0; 699717f6bedSFelix Fietkau else if (chain == 1) 700717f6bedSFelix Fietkau reg = AR_PHY_PA_GAIN123_B1; 701717f6bedSFelix Fietkau else 702717f6bedSFelix Fietkau reg = AR_PHY_PA_GAIN123_B2; 703717f6bedSFelix Fietkau 704717f6bedSFelix Fietkau REG_RMW_FIELD(ah, reg, AR_PHY_PA_GAIN123_PA_GAIN1, small_signal_gain); 705717f6bedSFelix Fietkau 706717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL1_B0, 707717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL, 708717f6bedSFelix Fietkau training_power); 709717f6bedSFelix Fietkau 71011441fb8SVasanthakumar Thiagarajan if (ah->caps.tx_chainmask & BIT(1)) 711717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL1_B1, 712717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL, 713717f6bedSFelix Fietkau training_power); 714717f6bedSFelix Fietkau 71511441fb8SVasanthakumar Thiagarajan if (ah->caps.tx_chainmask & BIT(2)) 716717f6bedSFelix Fietkau REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL1_B2, 717717f6bedSFelix Fietkau AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL, 718717f6bedSFelix Fietkau training_power); 719717f6bedSFelix Fietkau } 720717f6bedSFelix Fietkau EXPORT_SYMBOL(ar9003_paprd_populate_single_table); 721717f6bedSFelix Fietkau 722717f6bedSFelix Fietkau int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain) 723717f6bedSFelix Fietkau { 724717f6bedSFelix Fietkau unsigned int i, desired_gain, gain_index; 7251bf38661SFelix Fietkau unsigned int train_power = ah->paprd_training_power; 726717f6bedSFelix Fietkau 727717f6bedSFelix Fietkau desired_gain = ar9003_get_desired_gain(ah, chain, train_power); 728717f6bedSFelix Fietkau 729717f6bedSFelix Fietkau gain_index = 0; 730717f6bedSFelix Fietkau for (i = 0; i < 32; i++) { 731717f6bedSFelix Fietkau if (ah->paprd_gain_table_index[i] >= desired_gain) 732717f6bedSFelix Fietkau break; 733717f6bedSFelix Fietkau gain_index++; 734717f6bedSFelix Fietkau } 735717f6bedSFelix Fietkau 736717f6bedSFelix Fietkau ar9003_tx_force_gain(ah, gain_index); 737717f6bedSFelix Fietkau 738717f6bedSFelix Fietkau REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1, 739717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE); 740717f6bedSFelix Fietkau 741717f6bedSFelix Fietkau return 0; 742717f6bedSFelix Fietkau } 743717f6bedSFelix Fietkau EXPORT_SYMBOL(ar9003_paprd_setup_gain_table); 744717f6bedSFelix Fietkau 74520bd2a09SFelix Fietkau int ar9003_paprd_create_curve(struct ath_hw *ah, 74620bd2a09SFelix Fietkau struct ath9k_hw_cal_data *caldata, int chain) 747717f6bedSFelix Fietkau { 74820bd2a09SFelix Fietkau u16 *small_signal_gain = &caldata->small_signal_gain[chain]; 74920bd2a09SFelix Fietkau u32 *pa_table = caldata->pa_table[chain]; 750717f6bedSFelix Fietkau u32 *data_L, *data_U; 751717f6bedSFelix Fietkau int i, status = 0; 752717f6bedSFelix Fietkau u32 *buf; 753717f6bedSFelix Fietkau u32 reg; 754717f6bedSFelix Fietkau 75520bd2a09SFelix Fietkau memset(caldata->pa_table[chain], 0, sizeof(caldata->pa_table[chain])); 756717f6bedSFelix Fietkau 757717f6bedSFelix Fietkau buf = kmalloc(2 * 48 * sizeof(u32), GFP_ATOMIC); 758717f6bedSFelix Fietkau if (!buf) 759717f6bedSFelix Fietkau return -ENOMEM; 760717f6bedSFelix Fietkau 761717f6bedSFelix Fietkau data_L = &buf[0]; 762717f6bedSFelix Fietkau data_U = &buf[48]; 763717f6bedSFelix Fietkau 764717f6bedSFelix Fietkau REG_CLR_BIT(ah, AR_PHY_CHAN_INFO_MEMORY, 765717f6bedSFelix Fietkau AR_PHY_CHAN_INFO_MEMORY_CHANINFOMEM_S2_READ); 766717f6bedSFelix Fietkau 767717f6bedSFelix Fietkau reg = AR_PHY_CHAN_INFO_TAB_0; 768717f6bedSFelix Fietkau for (i = 0; i < 48; i++) 769717f6bedSFelix Fietkau data_L[i] = REG_READ(ah, reg + (i << 2)); 770717f6bedSFelix Fietkau 771717f6bedSFelix Fietkau REG_SET_BIT(ah, AR_PHY_CHAN_INFO_MEMORY, 772717f6bedSFelix Fietkau AR_PHY_CHAN_INFO_MEMORY_CHANINFOMEM_S2_READ); 773717f6bedSFelix Fietkau 774717f6bedSFelix Fietkau for (i = 0; i < 48; i++) 775717f6bedSFelix Fietkau data_U[i] = REG_READ(ah, reg + (i << 2)); 776717f6bedSFelix Fietkau 777717f6bedSFelix Fietkau if (!create_pa_curve(data_L, data_U, pa_table, small_signal_gain)) 778717f6bedSFelix Fietkau status = -2; 779717f6bedSFelix Fietkau 780717f6bedSFelix Fietkau REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1, 781717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE); 782717f6bedSFelix Fietkau 783717f6bedSFelix Fietkau kfree(buf); 784717f6bedSFelix Fietkau 785717f6bedSFelix Fietkau return status; 786717f6bedSFelix Fietkau } 787717f6bedSFelix Fietkau EXPORT_SYMBOL(ar9003_paprd_create_curve); 788717f6bedSFelix Fietkau 789717f6bedSFelix Fietkau int ar9003_paprd_init_table(struct ath_hw *ah) 790717f6bedSFelix Fietkau { 7911bf38661SFelix Fietkau int ret; 7921bf38661SFelix Fietkau 7931bf38661SFelix Fietkau ret = ar9003_paprd_setup_single_table(ah); 7941bf38661SFelix Fietkau if (ret < 0) 7951bf38661SFelix Fietkau return ret; 7961bf38661SFelix Fietkau 797717f6bedSFelix Fietkau ar9003_paprd_get_gain_table(ah); 798717f6bedSFelix Fietkau return 0; 799717f6bedSFelix Fietkau } 800717f6bedSFelix Fietkau EXPORT_SYMBOL(ar9003_paprd_init_table); 801717f6bedSFelix Fietkau 802717f6bedSFelix Fietkau bool ar9003_paprd_is_done(struct ath_hw *ah) 803717f6bedSFelix Fietkau { 8040e44d48cSMohammed Shafi Shajakhan int paprd_done, agc2_pwr; 8050e44d48cSMohammed Shafi Shajakhan paprd_done = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_STAT1, 806717f6bedSFelix Fietkau AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE); 8070e44d48cSMohammed Shafi Shajakhan 8080e44d48cSMohammed Shafi Shajakhan if (paprd_done == 0x1) { 8090e44d48cSMohammed Shafi Shajakhan agc2_pwr = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_STAT1, 8100e44d48cSMohammed Shafi Shajakhan AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_AGC2_PWR); 8110e44d48cSMohammed Shafi Shajakhan 8120e44d48cSMohammed Shafi Shajakhan ath_dbg(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, 8130e44d48cSMohammed Shafi Shajakhan "AGC2_PWR = 0x%x training done = 0x%x\n", 8140e44d48cSMohammed Shafi Shajakhan agc2_pwr, paprd_done); 8150e44d48cSMohammed Shafi Shajakhan /* 8160e44d48cSMohammed Shafi Shajakhan * agc2_pwr range should not be less than 'IDEAL_AGC2_PWR_CHANGE' 8170e44d48cSMohammed Shafi Shajakhan * when the training is completely done, otherwise retraining is 8180e44d48cSMohammed Shafi Shajakhan * done to make sure the value is in ideal range 8190e44d48cSMohammed Shafi Shajakhan */ 8200e44d48cSMohammed Shafi Shajakhan if (agc2_pwr <= PAPRD_IDEAL_AGC2_PWR_RANGE) 8210e44d48cSMohammed Shafi Shajakhan paprd_done = 0; 8220e44d48cSMohammed Shafi Shajakhan } 8230e44d48cSMohammed Shafi Shajakhan 8240e44d48cSMohammed Shafi Shajakhan return !!paprd_done; 825717f6bedSFelix Fietkau } 826717f6bedSFelix Fietkau EXPORT_SYMBOL(ar9003_paprd_is_done); 827