xref: /linux/drivers/net/wireless/ath/ath9k/ar9003_paprd.c (revision 7952ca5b20e1d54a3bfdfd976086c6f9df5873a2)
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