Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2010-2011 Atheros Communications Inc.
3 : : *
4 : : * Permission to use, copy, modify, and/or distribute this software for any
5 : : * purpose with or without fee is hereby granted, provided that the above
6 : : * copyright notice and this permission notice appear in all copies.
7 : : *
8 : : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 : : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 : : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 : : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 : : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 : : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 : : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 : : */
16 : :
17 : : #include <linux/export.h>
18 : : #include "hw.h"
19 : : #include "ar9003_phy.h"
20 : :
21 : 0 : void ar9003_paprd_enable(struct ath_hw *ah, bool val)
22 : : {
23 : 0 : struct ath9k_channel *chan = ah->curchan;
24 : 0 : struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
25 : :
26 : : /*
27 : : * 3 bits for modalHeader5G.papdRateMaskHt20
28 : : * is used for sub-band disabling of PAPRD.
29 : : * 5G band is divided into 3 sub-bands -- upper,
30 : : * middle, lower.
31 : : * if bit 30 of modalHeader5G.papdRateMaskHt20 is set
32 : : * -- disable PAPRD for upper band 5GHz
33 : : * if bit 29 of modalHeader5G.papdRateMaskHt20 is set
34 : : * -- disable PAPRD for middle band 5GHz
35 : : * if bit 28 of modalHeader5G.papdRateMaskHt20 is set
36 : : * -- disable PAPRD for lower band 5GHz
37 : : */
38 : :
39 [ # # ]: 0 : if (IS_CHAN_5GHZ(chan)) {
40 [ # # ]: 0 : if (chan->channel >= UPPER_5G_SUB_BAND_START) {
41 [ # # ]: 0 : if (le32_to_cpu(eep->modalHeader5G.papdRateMaskHt20)
42 : : & BIT(30))
43 : : val = false;
44 [ # # ]: 0 : } else if (chan->channel >= MID_5G_SUB_BAND_START) {
45 [ # # ]: 0 : if (le32_to_cpu(eep->modalHeader5G.papdRateMaskHt20)
46 : : & BIT(29))
47 : : val = false;
48 : : } else {
49 [ # # ]: 0 : if (le32_to_cpu(eep->modalHeader5G.papdRateMaskHt20)
50 : : & BIT(28))
51 : : val = false;
52 : : }
53 : : }
54 : :
55 [ # # ]: 0 : if (val) {
56 : 0 : ah->paprd_table_write_done = true;
57 : 0 : ath9k_hw_apply_txpower(ah, chan, false);
58 : : }
59 : :
60 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL0_B0,
61 : : AR_PHY_PAPRD_CTRL0_PAPRD_ENABLE, !!val);
62 [ # # ]: 0 : if (ah->caps.tx_chainmask & BIT(1))
63 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL0_B1,
64 : : AR_PHY_PAPRD_CTRL0_PAPRD_ENABLE, !!val);
65 [ # # ]: 0 : if (ah->caps.tx_chainmask & BIT(2))
66 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL0_B2,
67 : : AR_PHY_PAPRD_CTRL0_PAPRD_ENABLE, !!val);
68 : 0 : }
69 : : EXPORT_SYMBOL(ar9003_paprd_enable);
70 : :
71 : 0 : static int ar9003_get_training_power_2g(struct ath_hw *ah)
72 : : {
73 : 0 : struct ath9k_channel *chan = ah->curchan;
74 : 0 : unsigned int power, scale, delta;
75 : :
76 : 0 : scale = ar9003_get_paprd_scale_factor(ah, chan);
77 : :
78 [ # # # # : 0 : if (AR_SREV_9330(ah) || AR_SREV_9340(ah) ||
# # ]
79 [ # # ]: 0 : AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
80 : 0 : power = ah->paprd_target_power + 2;
81 [ # # ]: 0 : } else if (AR_SREV_9485(ah)) {
82 : : power = 25;
83 : : } else {
84 : 0 : power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE5,
85 : : AR_PHY_POWERTX_RATE5_POWERTXHT20_0);
86 : :
87 : 0 : delta = abs((int) ah->paprd_target_power - (int) power);
88 [ # # ]: 0 : if (delta > scale)
89 : : return -1;
90 : :
91 [ # # ]: 0 : if (delta < 4)
92 : 0 : power -= 4 - delta;
93 : : }
94 : :
95 : 0 : return power;
96 : : }
97 : :
98 : 0 : static int ar9003_get_training_power_5g(struct ath_hw *ah)
99 : : {
100 : 0 : struct ath_common *common = ath9k_hw_common(ah);
101 : 0 : struct ath9k_channel *chan = ah->curchan;
102 : 0 : unsigned int power, scale, delta;
103 : :
104 : 0 : scale = ar9003_get_paprd_scale_factor(ah, chan);
105 : :
106 [ # # ]: 0 : if (IS_CHAN_HT40(chan))
107 : 0 : power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE8,
108 : : AR_PHY_POWERTX_RATE8_POWERTXHT40_5);
109 : : else
110 : 0 : power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE6,
111 : : AR_PHY_POWERTX_RATE6_POWERTXHT20_5);
112 : :
113 : 0 : power += scale;
114 : 0 : delta = abs((int) ah->paprd_target_power - (int) power);
115 [ # # ]: 0 : if (delta > scale)
116 : : return -1;
117 : :
118 [ # # ]: 0 : switch (get_streams(ah->txchainmask)) {
119 : : case 1:
120 : : delta = 6;
121 : : break;
122 : : case 2:
123 : : delta = 4;
124 : : break;
125 : : case 3:
126 : : delta = 2;
127 : : break;
128 : 0 : default:
129 : 0 : delta = 0;
130 [ # # ]: 0 : ath_dbg(common, CALIBRATE, "Invalid tx-chainmask: %u\n",
131 : : ah->txchainmask);
132 : : }
133 : :
134 : 0 : power += delta;
135 : 0 : return power;
136 : : }
137 : :
138 : 0 : static int ar9003_paprd_setup_single_table(struct ath_hw *ah)
139 : : {
140 [ # # ]: 0 : struct ath_common *common = ath9k_hw_common(ah);
141 : 0 : static const u32 ctrl0[3] = {
142 : : AR_PHY_PAPRD_CTRL0_B0,
143 : : AR_PHY_PAPRD_CTRL0_B1,
144 : : AR_PHY_PAPRD_CTRL0_B2
145 : : };
146 : 0 : static const u32 ctrl1[3] = {
147 : : AR_PHY_PAPRD_CTRL1_B0,
148 : : AR_PHY_PAPRD_CTRL1_B1,
149 : : AR_PHY_PAPRD_CTRL1_B2
150 : : };
151 : 0 : int training_power;
152 : 0 : int i, val;
153 : 0 : u32 am2pm_mask = ah->paprd_ratemask;
154 : :
155 [ # # ]: 0 : if (IS_CHAN_2GHZ(ah->curchan))
156 : 0 : training_power = ar9003_get_training_power_2g(ah);
157 : : else
158 : 0 : training_power = ar9003_get_training_power_5g(ah);
159 : :
160 [ # # ]: 0 : ath_dbg(common, CALIBRATE, "Training power: %d, Target power: %d\n",
161 : : training_power, ah->paprd_target_power);
162 : :
163 [ # # ]: 0 : if (training_power < 0) {
164 [ # # ]: 0 : ath_dbg(common, CALIBRATE,
165 : : "PAPRD target power delta out of range\n");
166 : 0 : return -ERANGE;
167 : : }
168 : 0 : ah->paprd_training_power = training_power;
169 : :
170 [ # # ]: 0 : if (AR_SREV_9330(ah))
171 : 0 : am2pm_mask = 0;
172 : :
173 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2AM, AR_PHY_PAPRD_AM2AM_MASK,
174 : : ah->paprd_ratemask);
175 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2PM, AR_PHY_PAPRD_AM2PM_MASK,
176 : : am2pm_mask);
177 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_HT40, AR_PHY_PAPRD_HT40_MASK,
178 : : ah->paprd_ratemask_ht40);
179 : :
180 [ # # ]: 0 : ath_dbg(common, CALIBRATE, "PAPRD HT20 mask: 0x%x, HT40 mask: 0x%x\n",
181 : : ah->paprd_ratemask, ah->paprd_ratemask_ht40);
182 : :
183 [ # # ]: 0 : for (i = 0; i < ah->caps.max_txchains; i++) {
184 : 0 : REG_RMW_FIELD(ah, ctrl0[i],
185 : : AR_PHY_PAPRD_CTRL0_USE_SINGLE_TABLE_MASK, 1);
186 : 0 : REG_RMW_FIELD(ah, ctrl1[i],
187 : : AR_PHY_PAPRD_CTRL1_ADAPTIVE_AM2PM_ENABLE, 1);
188 : 0 : REG_RMW_FIELD(ah, ctrl1[i],
189 : : AR_PHY_PAPRD_CTRL1_ADAPTIVE_AM2AM_ENABLE, 1);
190 : 0 : REG_RMW_FIELD(ah, ctrl1[i],
191 : : AR_PHY_PAPRD_CTRL1_ADAPTIVE_SCALING_ENA, 0);
192 : 0 : REG_RMW_FIELD(ah, ctrl1[i],
193 : : AR_PHY_PAPRD_CTRL1_PA_GAIN_SCALE_FACT_MASK, 181);
194 : 0 : REG_RMW_FIELD(ah, ctrl1[i],
195 : : AR_PHY_PAPRD_CTRL1_PAPRD_MAG_SCALE_FACT, 361);
196 : 0 : REG_RMW_FIELD(ah, ctrl1[i],
197 : : AR_PHY_PAPRD_CTRL1_ADAPTIVE_SCALING_ENA, 0);
198 : 0 : REG_RMW_FIELD(ah, ctrl0[i],
199 : : AR_PHY_PAPRD_CTRL0_PAPRD_MAG_THRSH, 3);
200 : : }
201 : :
202 : 0 : ar9003_paprd_enable(ah, false);
203 : :
204 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1,
205 : : AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_LB_SKIP, 0x30);
206 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1,
207 : : AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_LB_ENABLE, 1);
208 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1,
209 : : AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_TX_GAIN_FORCE, 1);
210 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1,
211 : : AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_RX_BB_GAIN_FORCE, 0);
212 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1,
213 : : AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_IQCORR_ENABLE, 0);
214 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1,
215 : : AR_PHY_PAPRD_TRAINER_CNTL1_CF_PAPRD_AGC2_SETTLING, 28);
216 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL1,
217 : : AR_PHY_PAPRD_TRAINER_CNTL1_CF_CF_PAPRD_TRAIN_ENABLE, 1);
218 : :
219 [ # # ]: 0 : if (AR_SREV_9485(ah)) {
220 : : val = 148;
221 : : } else {
222 [ # # ]: 0 : if (IS_CHAN_2GHZ(ah->curchan)) {
223 [ # # # # ]: 0 : if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
224 : : val = 145;
225 : : else
226 : 0 : val = 147;
227 : : } else {
228 : : val = 137;
229 : : }
230 : : }
231 : :
232 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL2,
233 : : AR_PHY_PAPRD_TRAINER_CNTL2_CF_PAPRD_INIT_RX_BB_GAIN, val);
234 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
235 : : AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_FINE_CORR_LEN, 4);
236 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
237 : : AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_COARSE_CORR_LEN, 4);
238 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
239 : : AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_NUM_CORR_STAGES, 7);
240 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
241 : : AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_MIN_LOOPBACK_DEL, 1);
242 : :
243 [ # # # # ]: 0 : if (AR_SREV_9485(ah) ||
244 [ # # ]: 0 : AR_SREV_9462(ah) ||
245 [ # # ]: 0 : AR_SREV_9565(ah) ||
246 [ # # ]: 0 : AR_SREV_9550(ah) ||
247 [ # # ]: 0 : AR_SREV_9330(ah) ||
248 : : AR_SREV_9340(ah))
249 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
250 : : AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP, -3);
251 : : else
252 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
253 : : AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP, -6);
254 : :
255 : 0 : val = -10;
256 : :
257 [ # # # # : 0 : if (IS_CHAN_2GHZ(ah->curchan) && !AR_SREV_9462(ah) && !AR_SREV_9565(ah))
# # ]
258 : 0 : val = -15;
259 : :
260 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
261 : : AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_ADC_DESIRED_SIZE,
262 : : val);
263 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
264 : : AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_BBTXMIX_DISABLE, 1);
265 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL4,
266 : : AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_SAFETY_DELTA, 0);
267 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL4,
268 : : AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_MIN_CORR, 400);
269 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL4,
270 : : AR_PHY_PAPRD_TRAINER_CNTL4_CF_PAPRD_NUM_TRAIN_SAMPLES,
271 : : 100);
272 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_0_B0,
273 : : AR_PHY_PAPRD_PRE_POST_SCALING, 261376);
274 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_1_B0,
275 : : AR_PHY_PAPRD_PRE_POST_SCALING, 248079);
276 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_2_B0,
277 : : AR_PHY_PAPRD_PRE_POST_SCALING, 233759);
278 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_3_B0,
279 : : AR_PHY_PAPRD_PRE_POST_SCALING, 220464);
280 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_4_B0,
281 : : AR_PHY_PAPRD_PRE_POST_SCALING, 208194);
282 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_5_B0,
283 : : AR_PHY_PAPRD_PRE_POST_SCALING, 196949);
284 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_6_B0,
285 : : AR_PHY_PAPRD_PRE_POST_SCALING, 185706);
286 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_7_B0,
287 : : AR_PHY_PAPRD_PRE_POST_SCALING, 175487);
288 : 0 : return 0;
289 : : }
290 : :
291 : 0 : static void ar9003_paprd_get_gain_table(struct ath_hw *ah)
292 : : {
293 : 0 : u32 *entry = ah->paprd_gain_table_entries;
294 : 0 : u8 *index = ah->paprd_gain_table_index;
295 : 0 : u32 reg = AR_PHY_TXGAIN_TABLE;
296 : 0 : int i;
297 : :
298 [ # # ]: 0 : for (i = 0; i < PAPRD_GAIN_TABLE_ENTRIES; i++) {
299 : 0 : entry[i] = REG_READ(ah, reg);
300 : 0 : index[i] = (entry[i] >> 24) & 0xff;
301 : 0 : reg += 4;
302 : : }
303 : 0 : }
304 : :
305 : 0 : static unsigned int ar9003_get_desired_gain(struct ath_hw *ah, int chain,
306 : : int target_power)
307 : : {
308 : 0 : int olpc_gain_delta = 0, cl_gain_mod;
309 : 0 : int alpha_therm, alpha_volt;
310 : 0 : int therm_cal_value, volt_cal_value;
311 : 0 : int therm_value, volt_value;
312 : 0 : int thermal_gain_corr, voltage_gain_corr;
313 : 0 : int desired_scale, desired_gain = 0;
314 : 0 : u32 reg_olpc = 0, reg_cl_gain = 0;
315 : :
316 [ # # ]: 0 : REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1,
317 : : AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE);
318 : 0 : desired_scale = REG_READ_FIELD(ah, AR_PHY_TPC_12,
319 : : AR_PHY_TPC_12_DESIRED_SCALE_HT40_5);
320 : 0 : alpha_therm = REG_READ_FIELD(ah, AR_PHY_TPC_19,
321 : : AR_PHY_TPC_19_ALPHA_THERM);
322 : 0 : alpha_volt = REG_READ_FIELD(ah, AR_PHY_TPC_19,
323 : : AR_PHY_TPC_19_ALPHA_VOLT);
324 : 0 : therm_cal_value = REG_READ_FIELD(ah, AR_PHY_TPC_18,
325 : : AR_PHY_TPC_18_THERM_CAL_VALUE);
326 : 0 : volt_cal_value = REG_READ_FIELD(ah, AR_PHY_TPC_18,
327 : : AR_PHY_TPC_18_VOLT_CAL_VALUE);
328 : 0 : therm_value = REG_READ_FIELD(ah, AR_PHY_BB_THERM_ADC_4,
329 : : AR_PHY_BB_THERM_ADC_4_LATEST_THERM_VALUE);
330 : 0 : volt_value = REG_READ_FIELD(ah, AR_PHY_BB_THERM_ADC_4,
331 : : AR_PHY_BB_THERM_ADC_4_LATEST_VOLT_VALUE);
332 : :
333 [ # # ]: 0 : switch (chain) {
334 : : case 0:
335 : : reg_olpc = AR_PHY_TPC_11_B0;
336 : : reg_cl_gain = AR_PHY_CL_TAB_0;
337 : : break;
338 : : case 1:
339 : : reg_olpc = AR_PHY_TPC_11_B1;
340 : : reg_cl_gain = AR_PHY_CL_TAB_1;
341 : : break;
342 : : case 2:
343 : : reg_olpc = AR_PHY_TPC_11_B2;
344 : : reg_cl_gain = AR_PHY_CL_TAB_2;
345 : : break;
346 : : default:
347 [ # # ]: 0 : ath_dbg(ath9k_hw_common(ah), CALIBRATE,
348 : : "Invalid chainmask: %d\n", chain);
349 : : break;
350 : : }
351 : :
352 : 0 : olpc_gain_delta = REG_READ_FIELD(ah, reg_olpc,
353 : : AR_PHY_TPC_11_OLPC_GAIN_DELTA);
354 : 0 : cl_gain_mod = REG_READ_FIELD(ah, reg_cl_gain,
355 : : AR_PHY_CL_TAB_CL_GAIN_MOD);
356 : :
357 [ # # ]: 0 : if (olpc_gain_delta >= 128)
358 : 0 : olpc_gain_delta = olpc_gain_delta - 256;
359 : :
360 : 0 : thermal_gain_corr = (alpha_therm * (therm_value - therm_cal_value) +
361 : : (256 / 2)) / 256;
362 : 0 : voltage_gain_corr = (alpha_volt * (volt_value - volt_cal_value) +
363 : : (128 / 2)) / 128;
364 : 0 : desired_gain = target_power - olpc_gain_delta - thermal_gain_corr -
365 : 0 : voltage_gain_corr + desired_scale + cl_gain_mod;
366 : :
367 : 0 : return desired_gain;
368 : : }
369 : :
370 : 0 : static void ar9003_tx_force_gain(struct ath_hw *ah, unsigned int gain_index)
371 : : {
372 : 0 : int selected_gain_entry, txbb1dbgain, txbb6dbgain, txmxrgain;
373 : 0 : int padrvgnA, padrvgnB, padrvgnC, padrvgnD;
374 : 0 : u32 *gain_table_entries = ah->paprd_gain_table_entries;
375 : :
376 : 0 : selected_gain_entry = gain_table_entries[gain_index];
377 : 0 : txbb1dbgain = selected_gain_entry & 0x7;
378 : 0 : txbb6dbgain = (selected_gain_entry >> 3) & 0x3;
379 : 0 : txmxrgain = (selected_gain_entry >> 5) & 0xf;
380 : 0 : padrvgnA = (selected_gain_entry >> 9) & 0xf;
381 : 0 : padrvgnB = (selected_gain_entry >> 13) & 0xf;
382 : 0 : padrvgnC = (selected_gain_entry >> 17) & 0xf;
383 : 0 : padrvgnD = (selected_gain_entry >> 21) & 0x3;
384 : :
385 : 0 : REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
386 : : AR_PHY_TX_FORCED_GAIN_FORCED_TXBB1DBGAIN, txbb1dbgain);
387 : 0 : REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
388 : : AR_PHY_TX_FORCED_GAIN_FORCED_TXBB6DBGAIN, txbb6dbgain);
389 : 0 : REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
390 : : AR_PHY_TX_FORCED_GAIN_FORCED_TXMXRGAIN, txmxrgain);
391 : 0 : REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
392 : : AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNA, padrvgnA);
393 : 0 : REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
394 : : AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNB, padrvgnB);
395 : 0 : REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
396 : : AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGNC, padrvgnC);
397 : 0 : REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
398 : : AR_PHY_TX_FORCED_GAIN_FORCED_PADRVGND, padrvgnD);
399 : 0 : REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
400 : : AR_PHY_TX_FORCED_GAIN_FORCED_ENABLE_PAL, 0);
401 : 0 : REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
402 : : AR_PHY_TX_FORCED_GAIN_FORCE_TX_GAIN, 0);
403 : 0 : REG_RMW_FIELD(ah, AR_PHY_TPC_1, AR_PHY_TPC_1_FORCED_DAC_GAIN, 0);
404 : 0 : REG_RMW_FIELD(ah, AR_PHY_TPC_1, AR_PHY_TPC_1_FORCE_DAC_GAIN, 0);
405 : 0 : }
406 : :
407 : 0 : static inline int find_expn(int num)
408 : : {
409 : 0 : return fls(num) - 1;
410 : : }
411 : :
412 : 0 : static inline int find_proper_scale(int expn, int N)
413 : : {
414 [ # # # # : 0 : return (expn > N) ? expn - 10 : 0;
# # # # #
# # # # #
# # ]
415 : : }
416 : :
417 : : #define NUM_BIN 23
418 : :
419 : 0 : static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain)
420 : : {
421 : 0 : unsigned int thresh_accum_cnt;
422 : 0 : int x_est[NUM_BIN + 1], Y[NUM_BIN + 1], theta[NUM_BIN + 1];
423 : 0 : int PA_in[NUM_BIN + 1];
424 : 0 : int B1_tmp[NUM_BIN + 1], B2_tmp[NUM_BIN + 1];
425 : 0 : unsigned int B1_abs_max, B2_abs_max;
426 : 0 : int max_index, scale_factor;
427 : 0 : int y_est[NUM_BIN + 1];
428 : 0 : int x_est_fxp1_nonlin, x_tilde[NUM_BIN + 1];
429 : 0 : unsigned int x_tilde_abs;
430 : 0 : int G_fxp, Y_intercept, order_x_by_y, M, I, L, sum_y_sqr, sum_y_quad;
431 : 0 : int Q_x, Q_B1, Q_B2, beta_raw, alpha_raw, scale_B;
432 : 0 : int Q_scale_B, Q_beta, Q_alpha, alpha, beta, order_1, order_2;
433 : 0 : int order1_5x, order2_3x, order1_5x_rem, order2_3x_rem;
434 : 0 : int y5, y3, tmp;
435 : 0 : int theta_low_bin = 0;
436 : 0 : int i;
437 : :
438 : : /* disregard any bin that contains <= 16 samples */
439 : 0 : thresh_accum_cnt = 16;
440 : 0 : scale_factor = 5;
441 : 0 : max_index = 0;
442 : 0 : memset(theta, 0, sizeof(theta));
443 : 0 : memset(x_est, 0, sizeof(x_est));
444 : 0 : memset(Y, 0, sizeof(Y));
445 : 0 : memset(y_est, 0, sizeof(y_est));
446 : 0 : memset(x_tilde, 0, sizeof(x_tilde));
447 : :
448 [ # # ]: 0 : for (i = 0; i < NUM_BIN; i++) {
449 : 0 : s32 accum_cnt, accum_tx, accum_rx, accum_ang;
450 : :
451 : : /* number of samples */
452 : 0 : accum_cnt = data_L[i] & 0xffff;
453 : :
454 [ # # ]: 0 : if (accum_cnt <= thresh_accum_cnt)
455 : 0 : continue;
456 : :
457 : 0 : max_index++;
458 : :
459 : : /* sum(tx amplitude) */
460 : 0 : accum_tx = ((data_L[i] >> 16) & 0xffff) |
461 : 0 : ((data_U[i] & 0x7ff) << 16);
462 : :
463 : : /* sum(rx amplitude distance to lower bin edge) */
464 : 0 : accum_rx = ((data_U[i] >> 11) & 0x1f) |
465 : 0 : ((data_L[i + 23] & 0xffff) << 5);
466 : :
467 : : /* sum(angles) */
468 : 0 : accum_ang = ((data_L[i + 23] >> 16) & 0xffff) |
469 : 0 : ((data_U[i + 23] & 0x7ff) << 16);
470 : :
471 : 0 : accum_tx <<= scale_factor;
472 : 0 : accum_rx <<= scale_factor;
473 : 0 : x_est[max_index] =
474 : 0 : (((accum_tx + accum_cnt) / accum_cnt) + 32) >>
475 : : scale_factor;
476 : :
477 : 0 : Y[max_index] =
478 : 0 : ((((accum_rx + accum_cnt) / accum_cnt) + 32) >>
479 : 0 : scale_factor) +
480 : 0 : (1 << scale_factor) * i + 16;
481 : :
482 [ # # ]: 0 : if (accum_ang >= (1 << 26))
483 : 0 : accum_ang -= 1 << 27;
484 : :
485 : 0 : theta[max_index] =
486 : 0 : ((accum_ang * (1 << scale_factor)) + accum_cnt) /
487 : : accum_cnt;
488 : : }
489 : :
490 : : /*
491 : : * Find average theta of first 5 bin and all of those to same value.
492 : : * Curve is linear at that range.
493 : : */
494 [ # # ]: 0 : for (i = 1; i < 6; i++)
495 : 0 : theta_low_bin += theta[i];
496 : :
497 : 0 : theta_low_bin = theta_low_bin / 5;
498 [ # # ]: 0 : for (i = 1; i < 6; i++)
499 : 0 : theta[i] = theta_low_bin;
500 : :
501 : : /* Set values at origin */
502 : 0 : theta[0] = theta_low_bin;
503 [ # # ]: 0 : for (i = 0; i <= max_index; i++)
504 : 0 : theta[i] -= theta_low_bin;
505 : :
506 : 0 : x_est[0] = 0;
507 : 0 : Y[0] = 0;
508 : 0 : scale_factor = 8;
509 : :
510 : : /* low signal gain */
511 [ # # ]: 0 : if (x_est[6] == x_est[3])
512 : : return false;
513 : :
514 : 0 : G_fxp =
515 : 0 : (((Y[6] - Y[3]) * 1 << scale_factor) +
516 : 0 : (x_est[6] - x_est[3])) / (x_est[6] - x_est[3]);
517 : :
518 : : /* prevent division by zero */
519 [ # # ]: 0 : if (G_fxp == 0)
520 : : return false;
521 : :
522 : 0 : Y_intercept =
523 : 0 : (G_fxp * (x_est[0] - x_est[3]) +
524 : 0 : (1 << scale_factor)) / (1 << scale_factor) + Y[3];
525 : :
526 [ # # ]: 0 : for (i = 0; i <= max_index; i++)
527 : 0 : y_est[i] = Y[i] - Y_intercept;
528 : :
529 [ # # ]: 0 : for (i = 0; i <= 3; i++) {
530 : 0 : y_est[i] = i * 32;
531 : 0 : x_est[i] = ((y_est[i] * 1 << scale_factor) + G_fxp) / G_fxp;
532 : : }
533 : :
534 [ # # ]: 0 : if (y_est[max_index] == 0)
535 : : return false;
536 : :
537 : 0 : x_est_fxp1_nonlin =
538 : 0 : x_est[max_index] - ((1 << scale_factor) * y_est[max_index] +
539 : 0 : G_fxp) / G_fxp;
540 : :
541 : 0 : order_x_by_y =
542 : 0 : (x_est_fxp1_nonlin + y_est[max_index]) / y_est[max_index];
543 : :
544 [ # # ]: 0 : if (order_x_by_y == 0)
545 : : M = 10;
546 [ # # ]: 0 : else if (order_x_by_y == 1)
547 : : M = 9;
548 : : else
549 : 0 : M = 8;
550 : :
551 [ # # ]: 0 : I = (max_index > 15) ? 7 : max_index >> 1;
552 : 0 : L = max_index - I;
553 : 0 : scale_factor = 8;
554 : 0 : sum_y_sqr = 0;
555 : 0 : sum_y_quad = 0;
556 : 0 : x_tilde_abs = 0;
557 : :
558 [ # # ]: 0 : for (i = 0; i <= L; i++) {
559 : 0 : unsigned int y_sqr;
560 : 0 : unsigned int y_quad;
561 : 0 : unsigned int tmp_abs;
562 : :
563 : : /* prevent division by zero */
564 [ # # ]: 0 : if (y_est[i + I] == 0)
565 : : return false;
566 : :
567 : 0 : x_est_fxp1_nonlin =
568 : 0 : x_est[i + I] - ((1 << scale_factor) * y_est[i + I] +
569 : 0 : G_fxp) / G_fxp;
570 : :
571 : 0 : x_tilde[i] =
572 : 0 : (x_est_fxp1_nonlin * (1 << M) + y_est[i + I]) / y_est[i +
573 : : I];
574 : 0 : x_tilde[i] =
575 : 0 : (x_tilde[i] * (1 << M) + y_est[i + I]) / y_est[i + I];
576 : 0 : x_tilde[i] =
577 : 0 : (x_tilde[i] * (1 << M) + y_est[i + I]) / y_est[i + I];
578 : 0 : y_sqr =
579 : 0 : (y_est[i + I] * y_est[i + I] +
580 : 0 : (scale_factor * scale_factor)) / (scale_factor *
581 : : scale_factor);
582 : 0 : tmp_abs = abs(x_tilde[i]);
583 : 0 : if (tmp_abs > x_tilde_abs)
584 : : x_tilde_abs = tmp_abs;
585 : :
586 : 0 : y_quad = y_sqr * y_sqr;
587 : 0 : sum_y_sqr = sum_y_sqr + y_sqr;
588 : 0 : sum_y_quad = sum_y_quad + y_quad;
589 : 0 : B1_tmp[i] = y_sqr * (L + 1);
590 : 0 : B2_tmp[i] = y_sqr;
591 : : }
592 : :
593 : : B1_abs_max = 0;
594 : : B2_abs_max = 0;
595 [ # # ]: 0 : for (i = 0; i <= L; i++) {
596 : 0 : int abs_val;
597 : :
598 : 0 : B1_tmp[i] -= sum_y_sqr;
599 : 0 : B2_tmp[i] = sum_y_quad - sum_y_sqr * B2_tmp[i];
600 : :
601 : 0 : abs_val = abs(B1_tmp[i]);
602 : 0 : if (abs_val > B1_abs_max)
603 : : B1_abs_max = abs_val;
604 : :
605 : 0 : abs_val = abs(B2_tmp[i]);
606 : 0 : if (abs_val > B2_abs_max)
607 : : B2_abs_max = abs_val;
608 : : }
609 : :
610 [ # # ]: 0 : Q_x = find_proper_scale(find_expn(x_tilde_abs), 10);
611 [ # # ]: 0 : Q_B1 = find_proper_scale(find_expn(B1_abs_max), 10);
612 [ # # ]: 0 : Q_B2 = find_proper_scale(find_expn(B2_abs_max), 10);
613 : :
614 : 0 : beta_raw = 0;
615 : 0 : alpha_raw = 0;
616 [ # # ]: 0 : for (i = 0; i <= L; i++) {
617 : 0 : x_tilde[i] = x_tilde[i] / (1 << Q_x);
618 : 0 : B1_tmp[i] = B1_tmp[i] / (1 << Q_B1);
619 : 0 : B2_tmp[i] = B2_tmp[i] / (1 << Q_B2);
620 : 0 : beta_raw = beta_raw + B1_tmp[i] * x_tilde[i];
621 : 0 : alpha_raw = alpha_raw + B2_tmp[i] * x_tilde[i];
622 : : }
623 : :
624 : 0 : scale_B =
625 : 0 : ((sum_y_quad / scale_factor) * (L + 1) -
626 : 0 : (sum_y_sqr / scale_factor) * sum_y_sqr) * scale_factor;
627 : :
628 [ # # ]: 0 : Q_scale_B = find_proper_scale(find_expn(abs(scale_B)), 10);
629 : 0 : scale_B = scale_B / (1 << Q_scale_B);
630 [ # # ]: 0 : if (scale_B == 0)
631 : : return false;
632 [ # # ]: 0 : Q_beta = find_proper_scale(find_expn(abs(beta_raw)), 10);
633 [ # # ]: 0 : Q_alpha = find_proper_scale(find_expn(abs(alpha_raw)), 10);
634 : 0 : beta_raw = beta_raw / (1 << Q_beta);
635 : 0 : alpha_raw = alpha_raw / (1 << Q_alpha);
636 : 0 : alpha = (alpha_raw << 10) / scale_B;
637 : 0 : beta = (beta_raw << 10) / scale_B;
638 : 0 : order_1 = 3 * M - Q_x - Q_B1 - Q_beta + 10 + Q_scale_B;
639 : 0 : order_2 = 3 * M - Q_x - Q_B2 - Q_alpha + 10 + Q_scale_B;
640 : 0 : order1_5x = order_1 / 5;
641 : 0 : order2_3x = order_2 / 3;
642 : 0 : order1_5x_rem = order_1 - 5 * order1_5x;
643 : 0 : order2_3x_rem = order_2 - 3 * order2_3x;
644 : :
645 [ # # ]: 0 : for (i = 0; i < PAPRD_TABLE_SZ; i++) {
646 : 0 : tmp = i * 32;
647 : 0 : y5 = ((beta * tmp) >> 6) >> order1_5x;
648 : 0 : y5 = (y5 * tmp) >> order1_5x;
649 : 0 : y5 = (y5 * tmp) >> order1_5x;
650 : 0 : y5 = (y5 * tmp) >> order1_5x;
651 : 0 : y5 = (y5 * tmp) >> order1_5x;
652 : 0 : y5 = y5 >> order1_5x_rem;
653 : 0 : y3 = (alpha * tmp) >> order2_3x;
654 : 0 : y3 = (y3 * tmp) >> order2_3x;
655 : 0 : y3 = (y3 * tmp) >> order2_3x;
656 : 0 : y3 = y3 >> order2_3x_rem;
657 : 0 : PA_in[i] = y5 + y3 + (256 * tmp) / G_fxp;
658 : :
659 [ # # ]: 0 : if (i >= 2) {
660 : 0 : tmp = PA_in[i] - PA_in[i - 1];
661 [ # # ]: 0 : if (tmp < 0)
662 : 0 : PA_in[i] =
663 : 0 : PA_in[i - 1] + (PA_in[i - 1] -
664 : 0 : PA_in[i - 2]);
665 : : }
666 : :
667 : 0 : PA_in[i] = (PA_in[i] < 1400) ? PA_in[i] : 1400;
668 : : }
669 : :
670 : : beta_raw = 0;
671 : : alpha_raw = 0;
672 : :
673 [ # # ]: 0 : for (i = 0; i <= L; i++) {
674 : 0 : int theta_tilde =
675 : 0 : ((theta[i + I] << M) + y_est[i + I]) / y_est[i + I];
676 : 0 : theta_tilde =
677 : 0 : ((theta_tilde << M) + y_est[i + I]) / y_est[i + I];
678 : 0 : theta_tilde =
679 : 0 : ((theta_tilde << M) + y_est[i + I]) / y_est[i + I];
680 : 0 : beta_raw = beta_raw + B1_tmp[i] * theta_tilde;
681 : 0 : alpha_raw = alpha_raw + B2_tmp[i] * theta_tilde;
682 : : }
683 : :
684 [ # # ]: 0 : Q_beta = find_proper_scale(find_expn(abs(beta_raw)), 10);
685 [ # # ]: 0 : Q_alpha = find_proper_scale(find_expn(abs(alpha_raw)), 10);
686 : 0 : beta_raw = beta_raw / (1 << Q_beta);
687 : 0 : alpha_raw = alpha_raw / (1 << Q_alpha);
688 : :
689 : 0 : alpha = (alpha_raw << 10) / scale_B;
690 : 0 : beta = (beta_raw << 10) / scale_B;
691 : 0 : order_1 = 3 * M - Q_x - Q_B1 - Q_beta + 10 + Q_scale_B + 5;
692 : 0 : order_2 = 3 * M - Q_x - Q_B2 - Q_alpha + 10 + Q_scale_B + 5;
693 : 0 : order1_5x = order_1 / 5;
694 : 0 : order2_3x = order_2 / 3;
695 : 0 : order1_5x_rem = order_1 - 5 * order1_5x;
696 : 0 : order2_3x_rem = order_2 - 3 * order2_3x;
697 : :
698 [ # # ]: 0 : for (i = 0; i < PAPRD_TABLE_SZ; i++) {
699 : 0 : int PA_angle;
700 : :
701 : : /* pa_table[4] is calculated from PA_angle for i=5 */
702 [ # # ]: 0 : if (i == 4)
703 : 0 : continue;
704 : :
705 : 0 : tmp = i * 32;
706 [ # # ]: 0 : if (beta > 0)
707 : 0 : y5 = (((beta * tmp - 64) >> 6) -
708 : 0 : (1 << order1_5x)) / (1 << order1_5x);
709 : : else
710 : 0 : y5 = ((((beta * tmp - 64) >> 6) +
711 : 0 : (1 << order1_5x)) / (1 << order1_5x));
712 : :
713 : 0 : y5 = (y5 * tmp) / (1 << order1_5x);
714 : 0 : y5 = (y5 * tmp) / (1 << order1_5x);
715 : 0 : y5 = (y5 * tmp) / (1 << order1_5x);
716 : 0 : y5 = (y5 * tmp) / (1 << order1_5x);
717 : 0 : y5 = y5 / (1 << order1_5x_rem);
718 : :
719 [ # # ]: 0 : if (beta > 0)
720 : 0 : y3 = (alpha * tmp -
721 : 0 : (1 << order2_3x)) / (1 << order2_3x);
722 : : else
723 : 0 : y3 = (alpha * tmp +
724 : 0 : (1 << order2_3x)) / (1 << order2_3x);
725 : 0 : y3 = (y3 * tmp) / (1 << order2_3x);
726 : 0 : y3 = (y3 * tmp) / (1 << order2_3x);
727 : 0 : y3 = y3 / (1 << order2_3x_rem);
728 : :
729 [ # # ]: 0 : if (i < 4) {
730 : : PA_angle = 0;
731 : : } else {
732 : 0 : PA_angle = y5 + y3;
733 : 0 : if (PA_angle < -150)
734 : : PA_angle = -150;
735 : : else if (PA_angle > 150)
736 : : PA_angle = 150;
737 : : }
738 : :
739 : 0 : pa_table[i] = ((PA_in[i] & 0x7ff) << 11) + (PA_angle & 0x7ff);
740 [ # # ]: 0 : if (i == 5) {
741 : 0 : PA_angle = (PA_angle + 2) >> 1;
742 : 0 : pa_table[i - 1] = ((PA_in[i - 1] & 0x7ff) << 11) +
743 : 0 : (PA_angle & 0x7ff);
744 : : }
745 : : }
746 : :
747 : 0 : *gain = G_fxp;
748 : 0 : return true;
749 : : }
750 : :
751 : 0 : void ar9003_paprd_populate_single_table(struct ath_hw *ah,
752 : : struct ath9k_hw_cal_data *caldata,
753 : : int chain)
754 : : {
755 : 0 : u32 *paprd_table_val = caldata->pa_table[chain];
756 : 0 : u32 small_signal_gain = caldata->small_signal_gain[chain];
757 : 0 : u32 training_power = ah->paprd_training_power;
758 : 0 : u32 reg = 0;
759 : 0 : int i;
760 : :
761 [ # # ]: 0 : if (chain == 0)
762 : : reg = AR_PHY_PAPRD_MEM_TAB_B0;
763 [ # # ]: 0 : else if (chain == 1)
764 : : reg = AR_PHY_PAPRD_MEM_TAB_B1;
765 [ # # ]: 0 : else if (chain == 2)
766 : 0 : reg = AR_PHY_PAPRD_MEM_TAB_B2;
767 : :
768 [ # # ]: 0 : for (i = 0; i < PAPRD_TABLE_SZ; i++) {
769 : 0 : REG_WRITE(ah, reg, paprd_table_val[i]);
770 : 0 : reg = reg + 4;
771 : : }
772 : :
773 [ # # ]: 0 : if (chain == 0)
774 : : reg = AR_PHY_PA_GAIN123_B0;
775 [ # # ]: 0 : else if (chain == 1)
776 : : reg = AR_PHY_PA_GAIN123_B1;
777 : : else
778 : 0 : reg = AR_PHY_PA_GAIN123_B2;
779 : :
780 : 0 : REG_RMW_FIELD(ah, reg, AR_PHY_PA_GAIN123_PA_GAIN1, small_signal_gain);
781 : :
782 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL1_B0,
783 : : AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL,
784 : : training_power);
785 : :
786 [ # # ]: 0 : if (ah->caps.tx_chainmask & BIT(1))
787 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL1_B1,
788 : : AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL,
789 : : training_power);
790 : :
791 [ # # ]: 0 : if (ah->caps.tx_chainmask & BIT(2))
792 : : /* val AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL correct? */
793 : 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_CTRL1_B2,
794 : : AR_PHY_PAPRD_CTRL1_PAPRD_POWER_AT_AM2AM_CAL,
795 : : training_power);
796 : 0 : }
797 : : EXPORT_SYMBOL(ar9003_paprd_populate_single_table);
798 : :
799 : 0 : void ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain)
800 : : {
801 : 0 : unsigned int i, desired_gain, gain_index;
802 : 0 : unsigned int train_power = ah->paprd_training_power;
803 : :
804 : 0 : desired_gain = ar9003_get_desired_gain(ah, chain, train_power);
805 : :
806 : 0 : gain_index = 0;
807 [ # # ]: 0 : for (i = 0; i < PAPRD_GAIN_TABLE_ENTRIES; i++) {
808 [ # # ]: 0 : if (ah->paprd_gain_table_index[i] >= desired_gain)
809 : : break;
810 : 0 : gain_index++;
811 : : }
812 : :
813 : 0 : ar9003_tx_force_gain(ah, gain_index);
814 : :
815 [ # # ]: 0 : REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1,
816 : : AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE);
817 : 0 : }
818 : : EXPORT_SYMBOL(ar9003_paprd_setup_gain_table);
819 : :
820 : 0 : static bool ar9003_paprd_retrain_pa_in(struct ath_hw *ah,
821 : : struct ath9k_hw_cal_data *caldata,
822 : : int chain)
823 : : {
824 : 0 : u32 *pa_in = caldata->pa_table[chain];
825 : 0 : int capdiv_offset, quick_drop_offset;
826 : 0 : int capdiv2g, quick_drop;
827 : 0 : int count = 0;
828 : 0 : int i;
829 : :
830 [ # # # # ]: 0 : if (!AR_SREV_9485(ah) && !AR_SREV_9330(ah))
831 : : return false;
832 : :
833 : 0 : capdiv2g = REG_READ_FIELD(ah, AR_PHY_65NM_CH0_TXRF3,
834 : : AR_PHY_65NM_CH0_TXRF3_CAPDIV2G);
835 : :
836 [ # # ]: 0 : quick_drop = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
837 : : AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP);
838 : :
839 [ # # ]: 0 : if (quick_drop)
840 : 0 : quick_drop -= 0x40;
841 : :
842 [ # # ]: 0 : for (i = 0; i < NUM_BIN + 1; i++) {
843 [ # # ]: 0 : if (pa_in[i] == 1400)
844 : 0 : count++;
845 : : }
846 : :
847 [ # # ]: 0 : if (AR_SREV_9485(ah)) {
848 [ # # ]: 0 : if (pa_in[23] < 800) {
849 : 0 : capdiv_offset = (int)((1000 - pa_in[23] + 75) / 150);
850 : 0 : capdiv2g += capdiv_offset;
851 [ # # ]: 0 : if (capdiv2g > 7) {
852 : 0 : capdiv2g = 7;
853 [ # # ]: 0 : if (pa_in[23] < 600) {
854 : 0 : quick_drop++;
855 : 0 : if (quick_drop > 0)
856 : : quick_drop = 0;
857 : : }
858 : : }
859 [ # # ]: 0 : } else if (pa_in[23] == 1400) {
860 : 0 : quick_drop_offset = min_t(int, count / 3, 2);
861 : 0 : quick_drop += quick_drop_offset;
862 : 0 : capdiv2g += quick_drop_offset / 2;
863 : :
864 : 0 : if (capdiv2g > 7)
865 : : capdiv2g = 7;
866 : :
867 [ # # ]: 0 : if (quick_drop > 0) {
868 : 0 : quick_drop = 0;
869 : 0 : capdiv2g -= quick_drop_offset;
870 : 0 : if (capdiv2g < 0)
871 : : capdiv2g = 0;
872 : : }
873 : : } else {
874 : : return false;
875 : : }
876 [ # # ]: 0 : } else if (AR_SREV_9330(ah)) {
877 [ # # ]: 0 : if (pa_in[23] < 1000) {
878 : 0 : capdiv_offset = (1000 - pa_in[23]) / 100;
879 : 0 : capdiv2g += capdiv_offset;
880 [ # # ]: 0 : if (capdiv_offset > 3) {
881 : 0 : capdiv_offset = 1;
882 : 0 : quick_drop--;
883 : : }
884 : :
885 : 0 : capdiv2g += capdiv_offset;
886 : 0 : if (capdiv2g > 6)
887 : : capdiv2g = 6;
888 : 0 : if (quick_drop < -4)
889 : : quick_drop = -4;
890 [ # # ]: 0 : } else if (pa_in[23] == 1400) {
891 [ # # ]: 0 : if (count > 3) {
892 : 0 : quick_drop++;
893 : 0 : capdiv2g -= count / 4;
894 : 0 : if (quick_drop > -2)
895 : : quick_drop = -2;
896 : : } else {
897 : 0 : capdiv2g--;
898 : : }
899 : :
900 : 0 : if (capdiv2g < 0)
901 : : capdiv2g = 0;
902 : : } else {
903 : : return false;
904 : : }
905 : : }
906 : :
907 : 0 : REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_TXRF3,
908 : : AR_PHY_65NM_CH0_TXRF3_CAPDIV2G, capdiv2g);
909 [ # # ]: 0 : REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
910 : : AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP,
911 : : quick_drop);
912 : :
913 : 0 : return true;
914 : : }
915 : :
916 : 0 : int ar9003_paprd_create_curve(struct ath_hw *ah,
917 : : struct ath9k_hw_cal_data *caldata, int chain)
918 : : {
919 : 0 : u16 *small_signal_gain = &caldata->small_signal_gain[chain];
920 : 0 : u32 *pa_table = caldata->pa_table[chain];
921 : 0 : u32 *data_L, *data_U;
922 : 0 : int i, status = 0;
923 : 0 : u32 *buf;
924 : 0 : u32 reg;
925 : :
926 : 0 : memset(caldata->pa_table[chain], 0, sizeof(caldata->pa_table[chain]));
927 : :
928 : 0 : buf = kmalloc_array(2 * 48, sizeof(u32), GFP_KERNEL);
929 [ # # ]: 0 : if (!buf)
930 : : return -ENOMEM;
931 : :
932 : 0 : data_L = &buf[0];
933 : 0 : data_U = &buf[48];
934 : :
935 [ # # ]: 0 : REG_CLR_BIT(ah, AR_PHY_CHAN_INFO_MEMORY,
936 : : AR_PHY_CHAN_INFO_MEMORY_CHANINFOMEM_S2_READ);
937 : :
938 : 0 : reg = AR_PHY_CHAN_INFO_TAB_0;
939 [ # # ]: 0 : for (i = 0; i < 48; i++)
940 : 0 : data_L[i] = REG_READ(ah, reg + (i << 2));
941 : :
942 [ # # ]: 0 : REG_SET_BIT(ah, AR_PHY_CHAN_INFO_MEMORY,
943 : : AR_PHY_CHAN_INFO_MEMORY_CHANINFOMEM_S2_READ);
944 : :
945 [ # # ]: 0 : for (i = 0; i < 48; i++)
946 : 0 : data_U[i] = REG_READ(ah, reg + (i << 2));
947 : :
948 [ # # ]: 0 : if (!create_pa_curve(data_L, data_U, pa_table, small_signal_gain))
949 : 0 : status = -2;
950 : :
951 [ # # ]: 0 : if (ar9003_paprd_retrain_pa_in(ah, caldata, chain))
952 : 0 : status = -EINPROGRESS;
953 : :
954 [ # # ]: 0 : REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1,
955 : : AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE);
956 : :
957 : 0 : kfree(buf);
958 : :
959 : 0 : return status;
960 : : }
961 : : EXPORT_SYMBOL(ar9003_paprd_create_curve);
962 : :
963 : 0 : int ar9003_paprd_init_table(struct ath_hw *ah)
964 : : {
965 : 0 : int ret;
966 : :
967 : 0 : ret = ar9003_paprd_setup_single_table(ah);
968 [ # # ]: 0 : if (ret < 0)
969 : : return ret;
970 : :
971 : 0 : ar9003_paprd_get_gain_table(ah);
972 : 0 : return 0;
973 : : }
974 : : EXPORT_SYMBOL(ar9003_paprd_init_table);
975 : :
976 : 0 : bool ar9003_paprd_is_done(struct ath_hw *ah)
977 : : {
978 : 0 : int paprd_done, agc2_pwr;
979 : :
980 [ # # ]: 0 : paprd_done = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_STAT1,
981 : : AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE);
982 : :
983 [ # # ]: 0 : if (AR_SREV_9485(ah))
984 : 0 : goto exit;
985 : :
986 [ # # ]: 0 : if (paprd_done == 0x1) {
987 : 0 : agc2_pwr = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_STAT1,
988 : : AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_AGC2_PWR);
989 : :
990 [ # # ]: 0 : ath_dbg(ath9k_hw_common(ah), CALIBRATE,
991 : : "AGC2_PWR = 0x%x training done = 0x%x\n",
992 : : agc2_pwr, paprd_done);
993 : : /*
994 : : * agc2_pwr range should not be less than 'IDEAL_AGC2_PWR_CHANGE'
995 : : * when the training is completely done, otherwise retraining is
996 : : * done to make sure the value is in ideal range
997 : : */
998 [ # # ]: 0 : if (agc2_pwr <= PAPRD_IDEAL_AGC2_PWR_RANGE)
999 : 0 : paprd_done = 0;
1000 : : }
1001 : 0 : exit:
1002 : 0 : return !!paprd_done;
1003 : : }
1004 : : EXPORT_SYMBOL(ar9003_paprd_is_done);
1005 : :
1006 : 448 : bool ar9003_is_paprd_enabled(struct ath_hw *ah)
1007 : : {
1008 [ - + - - ]: 448 : if ((ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && ah->config.enable_paprd)
1009 : 0 : return true;
1010 : :
1011 : : return false;
1012 : : }
1013 : : EXPORT_SYMBOL(ar9003_is_paprd_enabled);
|