diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/ar9003_calib.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ar9003_calib.c | 802 |
1 files changed, 802 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c new file mode 100644 index 000000000000..56a9e5fa6d66 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c | |||
@@ -0,0 +1,802 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010 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 "hw.h" | ||
18 | #include "hw-ops.h" | ||
19 | #include "ar9003_phy.h" | ||
20 | |||
21 | static void ar9003_hw_setup_calibration(struct ath_hw *ah, | ||
22 | struct ath9k_cal_list *currCal) | ||
23 | { | ||
24 | struct ath_common *common = ath9k_hw_common(ah); | ||
25 | |||
26 | /* Select calibration to run */ | ||
27 | switch (currCal->calData->calType) { | ||
28 | case IQ_MISMATCH_CAL: | ||
29 | /* | ||
30 | * Start calibration with | ||
31 | * 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples | ||
32 | */ | ||
33 | REG_RMW_FIELD(ah, AR_PHY_TIMING4, | ||
34 | AR_PHY_TIMING4_IQCAL_LOG_COUNT_MAX, | ||
35 | currCal->calData->calCountMax); | ||
36 | REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ); | ||
37 | |||
38 | ath_print(common, ATH_DBG_CALIBRATE, | ||
39 | "starting IQ Mismatch Calibration\n"); | ||
40 | |||
41 | /* Kick-off cal */ | ||
42 | REG_SET_BIT(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL); | ||
43 | break; | ||
44 | case TEMP_COMP_CAL: | ||
45 | REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM, | ||
46 | AR_PHY_65NM_CH0_THERM_LOCAL, 1); | ||
47 | REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM, | ||
48 | AR_PHY_65NM_CH0_THERM_START, 1); | ||
49 | |||
50 | ath_print(common, ATH_DBG_CALIBRATE, | ||
51 | "starting Temperature Compensation Calibration\n"); | ||
52 | break; | ||
53 | case ADC_DC_INIT_CAL: | ||
54 | case ADC_GAIN_CAL: | ||
55 | case ADC_DC_CAL: | ||
56 | /* Not yet */ | ||
57 | break; | ||
58 | } | ||
59 | } | ||
60 | |||
61 | /* | ||
62 | * Generic calibration routine. | ||
63 | * Recalibrate the lower PHY chips to account for temperature/environment | ||
64 | * changes. | ||
65 | */ | ||
66 | static bool ar9003_hw_per_calibration(struct ath_hw *ah, | ||
67 | struct ath9k_channel *ichan, | ||
68 | u8 rxchainmask, | ||
69 | struct ath9k_cal_list *currCal) | ||
70 | { | ||
71 | /* Cal is assumed not done until explicitly set below */ | ||
72 | bool iscaldone = false; | ||
73 | |||
74 | /* Calibration in progress. */ | ||
75 | if (currCal->calState == CAL_RUNNING) { | ||
76 | /* Check to see if it has finished. */ | ||
77 | if (!(REG_READ(ah, AR_PHY_TIMING4) & AR_PHY_TIMING4_DO_CAL)) { | ||
78 | /* | ||
79 | * Accumulate cal measures for active chains | ||
80 | */ | ||
81 | currCal->calData->calCollect(ah); | ||
82 | ah->cal_samples++; | ||
83 | |||
84 | if (ah->cal_samples >= | ||
85 | currCal->calData->calNumSamples) { | ||
86 | unsigned int i, numChains = 0; | ||
87 | for (i = 0; i < AR9300_MAX_CHAINS; i++) { | ||
88 | if (rxchainmask & (1 << i)) | ||
89 | numChains++; | ||
90 | } | ||
91 | |||
92 | /* | ||
93 | * Process accumulated data | ||
94 | */ | ||
95 | currCal->calData->calPostProc(ah, numChains); | ||
96 | |||
97 | /* Calibration has finished. */ | ||
98 | ichan->CalValid |= currCal->calData->calType; | ||
99 | currCal->calState = CAL_DONE; | ||
100 | iscaldone = true; | ||
101 | } else { | ||
102 | /* | ||
103 | * Set-up collection of another sub-sample until we | ||
104 | * get desired number | ||
105 | */ | ||
106 | ar9003_hw_setup_calibration(ah, currCal); | ||
107 | } | ||
108 | } | ||
109 | } else if (!(ichan->CalValid & currCal->calData->calType)) { | ||
110 | /* If current cal is marked invalid in channel, kick it off */ | ||
111 | ath9k_hw_reset_calibration(ah, currCal); | ||
112 | } | ||
113 | |||
114 | return iscaldone; | ||
115 | } | ||
116 | |||
117 | static bool ar9003_hw_calibrate(struct ath_hw *ah, | ||
118 | struct ath9k_channel *chan, | ||
119 | u8 rxchainmask, | ||
120 | bool longcal) | ||
121 | { | ||
122 | bool iscaldone = true; | ||
123 | struct ath9k_cal_list *currCal = ah->cal_list_curr; | ||
124 | |||
125 | /* | ||
126 | * For given calibration: | ||
127 | * 1. Call generic cal routine | ||
128 | * 2. When this cal is done (isCalDone) if we have more cals waiting | ||
129 | * (eg after reset), mask this to upper layers by not propagating | ||
130 | * isCalDone if it is set to TRUE. | ||
131 | * Instead, change isCalDone to FALSE and setup the waiting cal(s) | ||
132 | * to be run. | ||
133 | */ | ||
134 | if (currCal && | ||
135 | (currCal->calState == CAL_RUNNING || | ||
136 | currCal->calState == CAL_WAITING)) { | ||
137 | iscaldone = ar9003_hw_per_calibration(ah, chan, | ||
138 | rxchainmask, currCal); | ||
139 | if (iscaldone) { | ||
140 | ah->cal_list_curr = currCal = currCal->calNext; | ||
141 | |||
142 | if (currCal->calState == CAL_WAITING) { | ||
143 | iscaldone = false; | ||
144 | ath9k_hw_reset_calibration(ah, currCal); | ||
145 | } | ||
146 | } | ||
147 | } | ||
148 | |||
149 | /* Do NF cal only at longer intervals */ | ||
150 | if (longcal) { | ||
151 | /* | ||
152 | * Load the NF from history buffer of the current channel. | ||
153 | * NF is slow time-variant, so it is OK to use a historical | ||
154 | * value. | ||
155 | */ | ||
156 | ath9k_hw_loadnf(ah, ah->curchan); | ||
157 | |||
158 | /* start NF calibration, without updating BB NF register */ | ||
159 | ath9k_hw_start_nfcal(ah); | ||
160 | } | ||
161 | |||
162 | return iscaldone; | ||
163 | } | ||
164 | |||
165 | static void ar9003_hw_iqcal_collect(struct ath_hw *ah) | ||
166 | { | ||
167 | int i; | ||
168 | |||
169 | /* Accumulate IQ cal measures for active chains */ | ||
170 | for (i = 0; i < AR5416_MAX_CHAINS; i++) { | ||
171 | ah->totalPowerMeasI[i] += | ||
172 | REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); | ||
173 | ah->totalPowerMeasQ[i] += | ||
174 | REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); | ||
175 | ah->totalIqCorrMeas[i] += | ||
176 | (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); | ||
177 | ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, | ||
178 | "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n", | ||
179 | ah->cal_samples, i, ah->totalPowerMeasI[i], | ||
180 | ah->totalPowerMeasQ[i], | ||
181 | ah->totalIqCorrMeas[i]); | ||
182 | } | ||
183 | } | ||
184 | |||
185 | static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) | ||
186 | { | ||
187 | struct ath_common *common = ath9k_hw_common(ah); | ||
188 | u32 powerMeasQ, powerMeasI, iqCorrMeas; | ||
189 | u32 qCoffDenom, iCoffDenom; | ||
190 | int32_t qCoff, iCoff; | ||
191 | int iqCorrNeg, i; | ||
192 | const u_int32_t offset_array[3] = { | ||
193 | AR_PHY_RX_IQCAL_CORR_B0, | ||
194 | AR_PHY_RX_IQCAL_CORR_B1, | ||
195 | AR_PHY_RX_IQCAL_CORR_B2, | ||
196 | }; | ||
197 | |||
198 | for (i = 0; i < numChains; i++) { | ||
199 | powerMeasI = ah->totalPowerMeasI[i]; | ||
200 | powerMeasQ = ah->totalPowerMeasQ[i]; | ||
201 | iqCorrMeas = ah->totalIqCorrMeas[i]; | ||
202 | |||
203 | ath_print(common, ATH_DBG_CALIBRATE, | ||
204 | "Starting IQ Cal and Correction for Chain %d\n", | ||
205 | i); | ||
206 | |||
207 | ath_print(common, ATH_DBG_CALIBRATE, | ||
208 | "Orignal: Chn %diq_corr_meas = 0x%08x\n", | ||
209 | i, ah->totalIqCorrMeas[i]); | ||
210 | |||
211 | iqCorrNeg = 0; | ||
212 | |||
213 | if (iqCorrMeas > 0x80000000) { | ||
214 | iqCorrMeas = (0xffffffff - iqCorrMeas) + 1; | ||
215 | iqCorrNeg = 1; | ||
216 | } | ||
217 | |||
218 | ath_print(common, ATH_DBG_CALIBRATE, | ||
219 | "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI); | ||
220 | ath_print(common, ATH_DBG_CALIBRATE, | ||
221 | "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ); | ||
222 | ath_print(common, ATH_DBG_CALIBRATE, "iqCorrNeg is 0x%08x\n", | ||
223 | iqCorrNeg); | ||
224 | |||
225 | iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 256; | ||
226 | qCoffDenom = powerMeasQ / 64; | ||
227 | |||
228 | if ((iCoffDenom != 0) && (qCoffDenom != 0)) { | ||
229 | iCoff = iqCorrMeas / iCoffDenom; | ||
230 | qCoff = powerMeasI / qCoffDenom - 64; | ||
231 | ath_print(common, ATH_DBG_CALIBRATE, | ||
232 | "Chn %d iCoff = 0x%08x\n", i, iCoff); | ||
233 | ath_print(common, ATH_DBG_CALIBRATE, | ||
234 | "Chn %d qCoff = 0x%08x\n", i, qCoff); | ||
235 | |||
236 | /* Force bounds on iCoff */ | ||
237 | if (iCoff >= 63) | ||
238 | iCoff = 63; | ||
239 | else if (iCoff <= -63) | ||
240 | iCoff = -63; | ||
241 | |||
242 | /* Negate iCoff if iqCorrNeg == 0 */ | ||
243 | if (iqCorrNeg == 0x0) | ||
244 | iCoff = -iCoff; | ||
245 | |||
246 | /* Force bounds on qCoff */ | ||
247 | if (qCoff >= 63) | ||
248 | qCoff = 63; | ||
249 | else if (qCoff <= -63) | ||
250 | qCoff = -63; | ||
251 | |||
252 | iCoff = iCoff & 0x7f; | ||
253 | qCoff = qCoff & 0x7f; | ||
254 | |||
255 | ath_print(common, ATH_DBG_CALIBRATE, | ||
256 | "Chn %d : iCoff = 0x%x qCoff = 0x%x\n", | ||
257 | i, iCoff, qCoff); | ||
258 | ath_print(common, ATH_DBG_CALIBRATE, | ||
259 | "Register offset (0x%04x) " | ||
260 | "before update = 0x%x\n", | ||
261 | offset_array[i], | ||
262 | REG_READ(ah, offset_array[i])); | ||
263 | |||
264 | REG_RMW_FIELD(ah, offset_array[i], | ||
265 | AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF, | ||
266 | iCoff); | ||
267 | REG_RMW_FIELD(ah, offset_array[i], | ||
268 | AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF, | ||
269 | qCoff); | ||
270 | ath_print(common, ATH_DBG_CALIBRATE, | ||
271 | "Register offset (0x%04x) QI COFF " | ||
272 | "(bitfields 0x%08x) after update = 0x%x\n", | ||
273 | offset_array[i], | ||
274 | AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF, | ||
275 | REG_READ(ah, offset_array[i])); | ||
276 | ath_print(common, ATH_DBG_CALIBRATE, | ||
277 | "Register offset (0x%04x) QQ COFF " | ||
278 | "(bitfields 0x%08x) after update = 0x%x\n", | ||
279 | offset_array[i], | ||
280 | AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF, | ||
281 | REG_READ(ah, offset_array[i])); | ||
282 | |||
283 | ath_print(common, ATH_DBG_CALIBRATE, | ||
284 | "IQ Cal and Correction done for Chain %d\n", | ||
285 | i); | ||
286 | } | ||
287 | } | ||
288 | |||
289 | REG_SET_BIT(ah, AR_PHY_RX_IQCAL_CORR_B0, | ||
290 | AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE); | ||
291 | ath_print(common, ATH_DBG_CALIBRATE, | ||
292 | "IQ Cal and Correction (offset 0x%04x) enabled " | ||
293 | "(bit position 0x%08x). New Value 0x%08x\n", | ||
294 | (unsigned) (AR_PHY_RX_IQCAL_CORR_B0), | ||
295 | AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE, | ||
296 | REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B0)); | ||
297 | } | ||
298 | |||
299 | static const struct ath9k_percal_data iq_cal_single_sample = { | ||
300 | IQ_MISMATCH_CAL, | ||
301 | MIN_CAL_SAMPLES, | ||
302 | PER_MAX_LOG_COUNT, | ||
303 | ar9003_hw_iqcal_collect, | ||
304 | ar9003_hw_iqcalibrate | ||
305 | }; | ||
306 | |||
307 | static void ar9003_hw_init_cal_settings(struct ath_hw *ah) | ||
308 | { | ||
309 | ah->iq_caldata.calData = &iq_cal_single_sample; | ||
310 | ah->supp_cals = IQ_MISMATCH_CAL; | ||
311 | } | ||
312 | |||
313 | static bool ar9003_hw_iscal_supported(struct ath_hw *ah, | ||
314 | enum ath9k_cal_types calType) | ||
315 | { | ||
316 | switch (calType & ah->supp_cals) { | ||
317 | case IQ_MISMATCH_CAL: | ||
318 | /* | ||
319 | * XXX: Run IQ Mismatch for non-CCK only | ||
320 | * Note that CHANNEL_B is never set though. | ||
321 | */ | ||
322 | return true; | ||
323 | case ADC_GAIN_CAL: | ||
324 | case ADC_DC_CAL: | ||
325 | return false; | ||
326 | case TEMP_COMP_CAL: | ||
327 | return true; | ||
328 | } | ||
329 | |||
330 | return false; | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * solve 4x4 linear equation used in loopback iq cal. | ||
335 | */ | ||
336 | static bool ar9003_hw_solve_iq_cal(struct ath_hw *ah, | ||
337 | s32 sin_2phi_1, | ||
338 | s32 cos_2phi_1, | ||
339 | s32 sin_2phi_2, | ||
340 | s32 cos_2phi_2, | ||
341 | s32 mag_a0_d0, | ||
342 | s32 phs_a0_d0, | ||
343 | s32 mag_a1_d0, | ||
344 | s32 phs_a1_d0, | ||
345 | s32 solved_eq[]) | ||
346 | { | ||
347 | s32 f1 = cos_2phi_1 - cos_2phi_2, | ||
348 | f3 = sin_2phi_1 - sin_2phi_2, | ||
349 | f2; | ||
350 | s32 mag_tx, phs_tx, mag_rx, phs_rx; | ||
351 | const s32 result_shift = 1 << 15; | ||
352 | struct ath_common *common = ath9k_hw_common(ah); | ||
353 | |||
354 | f2 = (f1 * f1 + f3 * f3) / result_shift; | ||
355 | |||
356 | if (!f2) { | ||
357 | ath_print(common, ATH_DBG_CALIBRATE, "Divide by 0\n"); | ||
358 | return false; | ||
359 | } | ||
360 | |||
361 | /* mag mismatch, tx */ | ||
362 | mag_tx = f1 * (mag_a0_d0 - mag_a1_d0) + f3 * (phs_a0_d0 - phs_a1_d0); | ||
363 | /* phs mismatch, tx */ | ||
364 | phs_tx = f3 * (-mag_a0_d0 + mag_a1_d0) + f1 * (phs_a0_d0 - phs_a1_d0); | ||
365 | |||
366 | mag_tx = (mag_tx / f2); | ||
367 | phs_tx = (phs_tx / f2); | ||
368 | |||
369 | /* mag mismatch, rx */ | ||
370 | mag_rx = mag_a0_d0 - (cos_2phi_1 * mag_tx + sin_2phi_1 * phs_tx) / | ||
371 | result_shift; | ||
372 | /* phs mismatch, rx */ | ||
373 | phs_rx = phs_a0_d0 + (sin_2phi_1 * mag_tx - cos_2phi_1 * phs_tx) / | ||
374 | result_shift; | ||
375 | |||
376 | solved_eq[0] = mag_tx; | ||
377 | solved_eq[1] = phs_tx; | ||
378 | solved_eq[2] = mag_rx; | ||
379 | solved_eq[3] = phs_rx; | ||
380 | |||
381 | return true; | ||
382 | } | ||
383 | |||
384 | static s32 ar9003_hw_find_mag_approx(struct ath_hw *ah, s32 in_re, s32 in_im) | ||
385 | { | ||
386 | s32 abs_i = abs(in_re), | ||
387 | abs_q = abs(in_im), | ||
388 | max_abs, min_abs; | ||
389 | |||
390 | if (abs_i > abs_q) { | ||
391 | max_abs = abs_i; | ||
392 | min_abs = abs_q; | ||
393 | } else { | ||
394 | max_abs = abs_q; | ||
395 | min_abs = abs_i; | ||
396 | } | ||
397 | |||
398 | return max_abs - (max_abs / 32) + (min_abs / 8) + (min_abs / 4); | ||
399 | } | ||
400 | |||
401 | #define DELPT 32 | ||
402 | |||
403 | static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, | ||
404 | s32 chain_idx, | ||
405 | const s32 iq_res[], | ||
406 | s32 iqc_coeff[]) | ||
407 | { | ||
408 | s32 i2_m_q2_a0_d0, i2_p_q2_a0_d0, iq_corr_a0_d0, | ||
409 | i2_m_q2_a0_d1, i2_p_q2_a0_d1, iq_corr_a0_d1, | ||
410 | i2_m_q2_a1_d0, i2_p_q2_a1_d0, iq_corr_a1_d0, | ||
411 | i2_m_q2_a1_d1, i2_p_q2_a1_d1, iq_corr_a1_d1; | ||
412 | s32 mag_a0_d0, mag_a1_d0, mag_a0_d1, mag_a1_d1, | ||
413 | phs_a0_d0, phs_a1_d0, phs_a0_d1, phs_a1_d1, | ||
414 | sin_2phi_1, cos_2phi_1, | ||
415 | sin_2phi_2, cos_2phi_2; | ||
416 | s32 mag_tx, phs_tx, mag_rx, phs_rx; | ||
417 | s32 solved_eq[4], mag_corr_tx, phs_corr_tx, mag_corr_rx, phs_corr_rx, | ||
418 | q_q_coff, q_i_coff; | ||
419 | const s32 res_scale = 1 << 15; | ||
420 | const s32 delpt_shift = 1 << 8; | ||
421 | s32 mag1, mag2; | ||
422 | struct ath_common *common = ath9k_hw_common(ah); | ||
423 | |||
424 | i2_m_q2_a0_d0 = iq_res[0] & 0xfff; | ||
425 | i2_p_q2_a0_d0 = (iq_res[0] >> 12) & 0xfff; | ||
426 | iq_corr_a0_d0 = ((iq_res[0] >> 24) & 0xff) + ((iq_res[1] & 0xf) << 8); | ||
427 | |||
428 | if (i2_m_q2_a0_d0 > 0x800) | ||
429 | i2_m_q2_a0_d0 = -((0xfff - i2_m_q2_a0_d0) + 1); | ||
430 | |||
431 | if (i2_p_q2_a0_d0 > 0x800) | ||
432 | i2_p_q2_a0_d0 = -((0xfff - i2_p_q2_a0_d0) + 1); | ||
433 | |||
434 | if (iq_corr_a0_d0 > 0x800) | ||
435 | iq_corr_a0_d0 = -((0xfff - iq_corr_a0_d0) + 1); | ||
436 | |||
437 | i2_m_q2_a0_d1 = (iq_res[1] >> 4) & 0xfff; | ||
438 | i2_p_q2_a0_d1 = (iq_res[2] & 0xfff); | ||
439 | iq_corr_a0_d1 = (iq_res[2] >> 12) & 0xfff; | ||
440 | |||
441 | if (i2_m_q2_a0_d1 > 0x800) | ||
442 | i2_m_q2_a0_d1 = -((0xfff - i2_m_q2_a0_d1) + 1); | ||
443 | |||
444 | if (i2_p_q2_a0_d1 > 0x800) | ||
445 | i2_p_q2_a0_d1 = -((0xfff - i2_p_q2_a0_d1) + 1); | ||
446 | |||
447 | if (iq_corr_a0_d1 > 0x800) | ||
448 | iq_corr_a0_d1 = -((0xfff - iq_corr_a0_d1) + 1); | ||
449 | |||
450 | i2_m_q2_a1_d0 = ((iq_res[2] >> 24) & 0xff) + ((iq_res[3] & 0xf) << 8); | ||
451 | i2_p_q2_a1_d0 = (iq_res[3] >> 4) & 0xfff; | ||
452 | iq_corr_a1_d0 = iq_res[4] & 0xfff; | ||
453 | |||
454 | if (i2_m_q2_a1_d0 > 0x800) | ||
455 | i2_m_q2_a1_d0 = -((0xfff - i2_m_q2_a1_d0) + 1); | ||
456 | |||
457 | if (i2_p_q2_a1_d0 > 0x800) | ||
458 | i2_p_q2_a1_d0 = -((0xfff - i2_p_q2_a1_d0) + 1); | ||
459 | |||
460 | if (iq_corr_a1_d0 > 0x800) | ||
461 | iq_corr_a1_d0 = -((0xfff - iq_corr_a1_d0) + 1); | ||
462 | |||
463 | i2_m_q2_a1_d1 = (iq_res[4] >> 12) & 0xfff; | ||
464 | i2_p_q2_a1_d1 = ((iq_res[4] >> 24) & 0xff) + ((iq_res[5] & 0xf) << 8); | ||
465 | iq_corr_a1_d1 = (iq_res[5] >> 4) & 0xfff; | ||
466 | |||
467 | if (i2_m_q2_a1_d1 > 0x800) | ||
468 | i2_m_q2_a1_d1 = -((0xfff - i2_m_q2_a1_d1) + 1); | ||
469 | |||
470 | if (i2_p_q2_a1_d1 > 0x800) | ||
471 | i2_p_q2_a1_d1 = -((0xfff - i2_p_q2_a1_d1) + 1); | ||
472 | |||
473 | if (iq_corr_a1_d1 > 0x800) | ||
474 | iq_corr_a1_d1 = -((0xfff - iq_corr_a1_d1) + 1); | ||
475 | |||
476 | if ((i2_p_q2_a0_d0 == 0) || (i2_p_q2_a0_d1 == 0) || | ||
477 | (i2_p_q2_a1_d0 == 0) || (i2_p_q2_a1_d1 == 0)) { | ||
478 | ath_print(common, ATH_DBG_CALIBRATE, | ||
479 | "Divide by 0:\na0_d0=%d\n" | ||
480 | "a0_d1=%d\na2_d0=%d\na1_d1=%d\n", | ||
481 | i2_p_q2_a0_d0, i2_p_q2_a0_d1, | ||
482 | i2_p_q2_a1_d0, i2_p_q2_a1_d1); | ||
483 | return false; | ||
484 | } | ||
485 | |||
486 | mag_a0_d0 = (i2_m_q2_a0_d0 * res_scale) / i2_p_q2_a0_d0; | ||
487 | phs_a0_d0 = (iq_corr_a0_d0 * res_scale) / i2_p_q2_a0_d0; | ||
488 | |||
489 | mag_a0_d1 = (i2_m_q2_a0_d1 * res_scale) / i2_p_q2_a0_d1; | ||
490 | phs_a0_d1 = (iq_corr_a0_d1 * res_scale) / i2_p_q2_a0_d1; | ||
491 | |||
492 | mag_a1_d0 = (i2_m_q2_a1_d0 * res_scale) / i2_p_q2_a1_d0; | ||
493 | phs_a1_d0 = (iq_corr_a1_d0 * res_scale) / i2_p_q2_a1_d0; | ||
494 | |||
495 | mag_a1_d1 = (i2_m_q2_a1_d1 * res_scale) / i2_p_q2_a1_d1; | ||
496 | phs_a1_d1 = (iq_corr_a1_d1 * res_scale) / i2_p_q2_a1_d1; | ||
497 | |||
498 | /* w/o analog phase shift */ | ||
499 | sin_2phi_1 = (((mag_a0_d0 - mag_a0_d1) * delpt_shift) / DELPT); | ||
500 | /* w/o analog phase shift */ | ||
501 | cos_2phi_1 = (((phs_a0_d1 - phs_a0_d0) * delpt_shift) / DELPT); | ||
502 | /* w/ analog phase shift */ | ||
503 | sin_2phi_2 = (((mag_a1_d0 - mag_a1_d1) * delpt_shift) / DELPT); | ||
504 | /* w/ analog phase shift */ | ||
505 | cos_2phi_2 = (((phs_a1_d1 - phs_a1_d0) * delpt_shift) / DELPT); | ||
506 | |||
507 | /* | ||
508 | * force sin^2 + cos^2 = 1; | ||
509 | * find magnitude by approximation | ||
510 | */ | ||
511 | mag1 = ar9003_hw_find_mag_approx(ah, cos_2phi_1, sin_2phi_1); | ||
512 | mag2 = ar9003_hw_find_mag_approx(ah, cos_2phi_2, sin_2phi_2); | ||
513 | |||
514 | if ((mag1 == 0) || (mag2 == 0)) { | ||
515 | ath_print(common, ATH_DBG_CALIBRATE, | ||
516 | "Divide by 0: mag1=%d, mag2=%d\n", | ||
517 | mag1, mag2); | ||
518 | return false; | ||
519 | } | ||
520 | |||
521 | /* normalization sin and cos by mag */ | ||
522 | sin_2phi_1 = (sin_2phi_1 * res_scale / mag1); | ||
523 | cos_2phi_1 = (cos_2phi_1 * res_scale / mag1); | ||
524 | sin_2phi_2 = (sin_2phi_2 * res_scale / mag2); | ||
525 | cos_2phi_2 = (cos_2phi_2 * res_scale / mag2); | ||
526 | |||
527 | /* calculate IQ mismatch */ | ||
528 | if (!ar9003_hw_solve_iq_cal(ah, | ||
529 | sin_2phi_1, cos_2phi_1, | ||
530 | sin_2phi_2, cos_2phi_2, | ||
531 | mag_a0_d0, phs_a0_d0, | ||
532 | mag_a1_d0, | ||
533 | phs_a1_d0, solved_eq)) { | ||
534 | ath_print(common, ATH_DBG_CALIBRATE, | ||
535 | "Call to ar9003_hw_solve_iq_cal() failed.\n"); | ||
536 | return false; | ||
537 | } | ||
538 | |||
539 | mag_tx = solved_eq[0]; | ||
540 | phs_tx = solved_eq[1]; | ||
541 | mag_rx = solved_eq[2]; | ||
542 | phs_rx = solved_eq[3]; | ||
543 | |||
544 | ath_print(common, ATH_DBG_CALIBRATE, | ||
545 | "chain %d: mag mismatch=%d phase mismatch=%d\n", | ||
546 | chain_idx, mag_tx/res_scale, phs_tx/res_scale); | ||
547 | |||
548 | if (res_scale == mag_tx) { | ||
549 | ath_print(common, ATH_DBG_CALIBRATE, | ||
550 | "Divide by 0: mag_tx=%d, res_scale=%d\n", | ||
551 | mag_tx, res_scale); | ||
552 | return false; | ||
553 | } | ||
554 | |||
555 | /* calculate and quantize Tx IQ correction factor */ | ||
556 | mag_corr_tx = (mag_tx * res_scale) / (res_scale - mag_tx); | ||
557 | phs_corr_tx = -phs_tx; | ||
558 | |||
559 | q_q_coff = (mag_corr_tx * 128 / res_scale); | ||
560 | q_i_coff = (phs_corr_tx * 256 / res_scale); | ||
561 | |||
562 | ath_print(common, ATH_DBG_CALIBRATE, | ||
563 | "tx chain %d: mag corr=%d phase corr=%d\n", | ||
564 | chain_idx, q_q_coff, q_i_coff); | ||
565 | |||
566 | if (q_i_coff < -63) | ||
567 | q_i_coff = -63; | ||
568 | if (q_i_coff > 63) | ||
569 | q_i_coff = 63; | ||
570 | if (q_q_coff < -63) | ||
571 | q_q_coff = -63; | ||
572 | if (q_q_coff > 63) | ||
573 | q_q_coff = 63; | ||
574 | |||
575 | iqc_coeff[0] = (q_q_coff * 128) + q_i_coff; | ||
576 | |||
577 | ath_print(common, ATH_DBG_CALIBRATE, | ||
578 | "tx chain %d: iq corr coeff=%x\n", | ||
579 | chain_idx, iqc_coeff[0]); | ||
580 | |||
581 | if (-mag_rx == res_scale) { | ||
582 | ath_print(common, ATH_DBG_CALIBRATE, | ||
583 | "Divide by 0: mag_rx=%d, res_scale=%d\n", | ||
584 | mag_rx, res_scale); | ||
585 | return false; | ||
586 | } | ||
587 | |||
588 | /* calculate and quantize Rx IQ correction factors */ | ||
589 | mag_corr_rx = (-mag_rx * res_scale) / (res_scale + mag_rx); | ||
590 | phs_corr_rx = -phs_rx; | ||
591 | |||
592 | q_q_coff = (mag_corr_rx * 128 / res_scale); | ||
593 | q_i_coff = (phs_corr_rx * 256 / res_scale); | ||
594 | |||
595 | ath_print(common, ATH_DBG_CALIBRATE, | ||
596 | "rx chain %d: mag corr=%d phase corr=%d\n", | ||
597 | chain_idx, q_q_coff, q_i_coff); | ||
598 | |||
599 | if (q_i_coff < -63) | ||
600 | q_i_coff = -63; | ||
601 | if (q_i_coff > 63) | ||
602 | q_i_coff = 63; | ||
603 | if (q_q_coff < -63) | ||
604 | q_q_coff = -63; | ||
605 | if (q_q_coff > 63) | ||
606 | q_q_coff = 63; | ||
607 | |||
608 | iqc_coeff[1] = (q_q_coff * 128) + q_i_coff; | ||
609 | |||
610 | ath_print(common, ATH_DBG_CALIBRATE, | ||
611 | "rx chain %d: iq corr coeff=%x\n", | ||
612 | chain_idx, iqc_coeff[1]); | ||
613 | |||
614 | return true; | ||
615 | } | ||
616 | |||
617 | static void ar9003_hw_tx_iq_cal(struct ath_hw *ah) | ||
618 | { | ||
619 | struct ath_common *common = ath9k_hw_common(ah); | ||
620 | const u32 txiqcal_status[AR9300_MAX_CHAINS] = { | ||
621 | AR_PHY_TX_IQCAL_STATUS_B0, | ||
622 | AR_PHY_TX_IQCAL_STATUS_B1, | ||
623 | AR_PHY_TX_IQCAL_STATUS_B2, | ||
624 | }; | ||
625 | const u32 tx_corr_coeff[AR9300_MAX_CHAINS] = { | ||
626 | AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, | ||
627 | AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, | ||
628 | AR_PHY_TX_IQCAL_CORR_COEFF_01_B2, | ||
629 | }; | ||
630 | const u32 rx_corr[AR9300_MAX_CHAINS] = { | ||
631 | AR_PHY_RX_IQCAL_CORR_B0, | ||
632 | AR_PHY_RX_IQCAL_CORR_B1, | ||
633 | AR_PHY_RX_IQCAL_CORR_B2, | ||
634 | }; | ||
635 | const u_int32_t chan_info_tab[] = { | ||
636 | AR_PHY_CHAN_INFO_TAB_0, | ||
637 | AR_PHY_CHAN_INFO_TAB_1, | ||
638 | AR_PHY_CHAN_INFO_TAB_2, | ||
639 | }; | ||
640 | s32 iq_res[6]; | ||
641 | s32 iqc_coeff[2]; | ||
642 | s32 i, j; | ||
643 | u32 num_chains = 0; | ||
644 | |||
645 | for (i = 0; i < AR9300_MAX_CHAINS; i++) { | ||
646 | if (ah->txchainmask & (1 << i)) | ||
647 | num_chains++; | ||
648 | } | ||
649 | |||
650 | REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1, | ||
651 | AR_PHY_TX_IQCAQL_CONTROL_1_IQCORR_I_Q_COFF_DELPT, | ||
652 | DELPT); | ||
653 | REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_START, | ||
654 | AR_PHY_TX_IQCAL_START_DO_CAL, | ||
655 | AR_PHY_TX_IQCAL_START_DO_CAL); | ||
656 | |||
657 | if (!ath9k_hw_wait(ah, AR_PHY_TX_IQCAL_START, | ||
658 | AR_PHY_TX_IQCAL_START_DO_CAL, | ||
659 | 0, AH_WAIT_TIMEOUT)) { | ||
660 | ath_print(common, ATH_DBG_CALIBRATE, | ||
661 | "Tx IQ Cal not complete.\n"); | ||
662 | goto TX_IQ_CAL_FAILED; | ||
663 | } | ||
664 | |||
665 | for (i = 0; i < num_chains; i++) { | ||
666 | ath_print(common, ATH_DBG_CALIBRATE, | ||
667 | "Doing Tx IQ Cal for chain %d.\n", i); | ||
668 | |||
669 | if (REG_READ(ah, txiqcal_status[i]) & | ||
670 | AR_PHY_TX_IQCAL_STATUS_FAILED) { | ||
671 | ath_print(common, ATH_DBG_CALIBRATE, | ||
672 | "Tx IQ Cal failed for chain %d.\n", i); | ||
673 | goto TX_IQ_CAL_FAILED; | ||
674 | } | ||
675 | |||
676 | for (j = 0; j < 3; j++) { | ||
677 | u_int8_t idx = 2 * j, | ||
678 | offset = 4 * j; | ||
679 | |||
680 | REG_RMW_FIELD(ah, AR_PHY_CHAN_INFO_MEMORY, | ||
681 | AR_PHY_CHAN_INFO_TAB_S2_READ, 0); | ||
682 | |||
683 | /* 32 bits */ | ||
684 | iq_res[idx] = REG_READ(ah, chan_info_tab[i] + offset); | ||
685 | |||
686 | REG_RMW_FIELD(ah, AR_PHY_CHAN_INFO_MEMORY, | ||
687 | AR_PHY_CHAN_INFO_TAB_S2_READ, 1); | ||
688 | |||
689 | /* 16 bits */ | ||
690 | iq_res[idx+1] = 0xffff & REG_READ(ah, | ||
691 | chan_info_tab[i] + | ||
692 | offset); | ||
693 | |||
694 | ath_print(common, ATH_DBG_CALIBRATE, | ||
695 | "IQ RES[%d]=0x%x IQ_RES[%d]=0x%x\n", | ||
696 | idx, iq_res[idx], idx+1, iq_res[idx+1]); | ||
697 | } | ||
698 | |||
699 | if (!ar9003_hw_calc_iq_corr(ah, i, iq_res, iqc_coeff)) { | ||
700 | ath_print(common, ATH_DBG_CALIBRATE, | ||
701 | "Failed in calculation of IQ correction.\n"); | ||
702 | goto TX_IQ_CAL_FAILED; | ||
703 | } | ||
704 | |||
705 | ath_print(common, ATH_DBG_CALIBRATE, | ||
706 | "IQ_COEFF[0] = 0x%x IQ_COEFF[1] = 0x%x\n", | ||
707 | iqc_coeff[0], iqc_coeff[1]); | ||
708 | |||
709 | REG_RMW_FIELD(ah, tx_corr_coeff[i], | ||
710 | AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE, | ||
711 | iqc_coeff[0]); | ||
712 | REG_RMW_FIELD(ah, rx_corr[i], | ||
713 | AR_PHY_RX_IQCAL_CORR_LOOPBACK_IQCORR_Q_Q_COFF, | ||
714 | iqc_coeff[1] >> 7); | ||
715 | REG_RMW_FIELD(ah, rx_corr[i], | ||
716 | AR_PHY_RX_IQCAL_CORR_LOOPBACK_IQCORR_Q_I_COFF, | ||
717 | iqc_coeff[1]); | ||
718 | } | ||
719 | |||
720 | REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3, | ||
721 | AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1); | ||
722 | REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0, | ||
723 | AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); | ||
724 | |||
725 | return; | ||
726 | |||
727 | TX_IQ_CAL_FAILED: | ||
728 | ath_print(common, ATH_DBG_CALIBRATE, "Tx IQ Cal failed\n"); | ||
729 | } | ||
730 | |||
731 | static bool ar9003_hw_init_cal(struct ath_hw *ah, | ||
732 | struct ath9k_channel *chan) | ||
733 | { | ||
734 | struct ath_common *common = ath9k_hw_common(ah); | ||
735 | |||
736 | /* | ||
737 | * 0x7 = 0b111 , AR9003 needs to be configured for 3-chain mode before | ||
738 | * running AGC/TxIQ cals | ||
739 | */ | ||
740 | ar9003_hw_set_chain_masks(ah, 0x7, 0x7); | ||
741 | |||
742 | /* Calibrate the AGC */ | ||
743 | REG_WRITE(ah, AR_PHY_AGC_CONTROL, | ||
744 | REG_READ(ah, AR_PHY_AGC_CONTROL) | | ||
745 | AR_PHY_AGC_CONTROL_CAL); | ||
746 | |||
747 | /* Poll for offset calibration complete */ | ||
748 | if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, | ||
749 | 0, AH_WAIT_TIMEOUT)) { | ||
750 | ath_print(common, ATH_DBG_CALIBRATE, | ||
751 | "offset calibration failed to " | ||
752 | "complete in 1ms; noisy environment?\n"); | ||
753 | return false; | ||
754 | } | ||
755 | |||
756 | /* Do Tx IQ Calibration */ | ||
757 | if (ah->config.tx_iq_calibration) | ||
758 | ar9003_hw_tx_iq_cal(ah); | ||
759 | |||
760 | /* Revert chainmasks to their original values before NF cal */ | ||
761 | ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask); | ||
762 | |||
763 | /* Initialize list pointers */ | ||
764 | ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL; | ||
765 | |||
766 | if (ar9003_hw_iscal_supported(ah, IQ_MISMATCH_CAL)) { | ||
767 | INIT_CAL(&ah->iq_caldata); | ||
768 | INSERT_CAL(ah, &ah->iq_caldata); | ||
769 | ath_print(common, ATH_DBG_CALIBRATE, | ||
770 | "enabling IQ Calibration.\n"); | ||
771 | } | ||
772 | |||
773 | if (ar9003_hw_iscal_supported(ah, TEMP_COMP_CAL)) { | ||
774 | INIT_CAL(&ah->tempCompCalData); | ||
775 | INSERT_CAL(ah, &ah->tempCompCalData); | ||
776 | ath_print(common, ATH_DBG_CALIBRATE, | ||
777 | "enabling Temperature Compensation Calibration.\n"); | ||
778 | } | ||
779 | |||
780 | /* Initialize current pointer to first element in list */ | ||
781 | ah->cal_list_curr = ah->cal_list; | ||
782 | |||
783 | if (ah->cal_list_curr) | ||
784 | ath9k_hw_reset_calibration(ah, ah->cal_list_curr); | ||
785 | |||
786 | chan->CalValid = 0; | ||
787 | |||
788 | return true; | ||
789 | } | ||
790 | |||
791 | void ar9003_hw_attach_calib_ops(struct ath_hw *ah) | ||
792 | { | ||
793 | struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); | ||
794 | struct ath_hw_ops *ops = ath9k_hw_ops(ah); | ||
795 | |||
796 | priv_ops->init_cal_settings = ar9003_hw_init_cal_settings; | ||
797 | priv_ops->init_cal = ar9003_hw_init_cal; | ||
798 | priv_ops->setup_calibration = ar9003_hw_setup_calibration; | ||
799 | priv_ops->iscal_supported = ar9003_hw_iscal_supported; | ||
800 | |||
801 | ops->calibrate = ar9003_hw_calibrate; | ||
802 | } | ||