2 * Copyright (c) 2010 Atheros Communications Inc.
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.
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.
19 #include "ar9003_phy.h"
21 static void ar9003_hw_setup_calibration(struct ath_hw
*ah
,
22 struct ath9k_cal_list
*currCal
)
24 struct ath_common
*common
= ath9k_hw_common(ah
);
26 /* Select calibration to run */
27 switch (currCal
->calData
->calType
) {
30 * Start calibration with
31 * 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples
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
);
38 ath_print(common
, ATH_DBG_CALIBRATE
,
39 "starting IQ Mismatch Calibration\n");
42 REG_SET_BIT(ah
, AR_PHY_TIMING4
, AR_PHY_TIMING4_DO_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);
50 ath_print(common
, ATH_DBG_CALIBRATE
,
51 "starting Temperature Compensation Calibration\n");
62 * Generic calibration routine.
63 * Recalibrate the lower PHY chips to account for temperature/environment
66 static bool ar9003_hw_per_calibration(struct ath_hw
*ah
,
67 struct ath9k_channel
*ichan
,
69 struct ath9k_cal_list
*currCal
)
71 struct ath9k_hw_cal_data
*caldata
= ah
->caldata
;
72 /* Cal is assumed not done until explicitly set below */
73 bool iscaldone
= false;
75 /* Calibration in progress. */
76 if (currCal
->calState
== CAL_RUNNING
) {
77 /* Check to see if it has finished. */
78 if (!(REG_READ(ah
, AR_PHY_TIMING4
) & AR_PHY_TIMING4_DO_CAL
)) {
80 * Accumulate cal measures for active chains
82 currCal
->calData
->calCollect(ah
);
85 if (ah
->cal_samples
>=
86 currCal
->calData
->calNumSamples
) {
87 unsigned int i
, numChains
= 0;
88 for (i
= 0; i
< AR9300_MAX_CHAINS
; i
++) {
89 if (rxchainmask
& (1 << i
))
94 * Process accumulated data
96 currCal
->calData
->calPostProc(ah
, numChains
);
98 /* Calibration has finished. */
99 caldata
->CalValid
|= currCal
->calData
->calType
;
100 currCal
->calState
= CAL_DONE
;
104 * Set-up collection of another sub-sample until we
107 ar9003_hw_setup_calibration(ah
, currCal
);
110 } else if (!(caldata
->CalValid
& currCal
->calData
->calType
)) {
111 /* If current cal is marked invalid in channel, kick it off */
112 ath9k_hw_reset_calibration(ah
, currCal
);
118 static bool ar9003_hw_calibrate(struct ath_hw
*ah
,
119 struct ath9k_channel
*chan
,
123 bool iscaldone
= true;
124 struct ath9k_cal_list
*currCal
= ah
->cal_list_curr
;
127 * For given calibration:
128 * 1. Call generic cal routine
129 * 2. When this cal is done (isCalDone) if we have more cals waiting
130 * (eg after reset), mask this to upper layers by not propagating
131 * isCalDone if it is set to TRUE.
132 * Instead, change isCalDone to FALSE and setup the waiting cal(s)
136 (currCal
->calState
== CAL_RUNNING
||
137 currCal
->calState
== CAL_WAITING
)) {
138 iscaldone
= ar9003_hw_per_calibration(ah
, chan
,
139 rxchainmask
, currCal
);
141 ah
->cal_list_curr
= currCal
= currCal
->calNext
;
143 if (currCal
->calState
== CAL_WAITING
) {
145 ath9k_hw_reset_calibration(ah
, currCal
);
150 /* Do NF cal only at longer intervals */
153 * Get the value from the previous NF cal and update
156 ath9k_hw_getnf(ah
, chan
);
159 * Load the NF from history buffer of the current channel.
160 * NF is slow time-variant, so it is OK to use a historical
163 ath9k_hw_loadnf(ah
, ah
->curchan
);
165 /* start NF calibration, without updating BB NF register */
166 ath9k_hw_start_nfcal(ah
, false);
172 static void ar9003_hw_iqcal_collect(struct ath_hw
*ah
)
176 /* Accumulate IQ cal measures for active chains */
177 for (i
= 0; i
< AR5416_MAX_CHAINS
; i
++) {
178 ah
->totalPowerMeasI
[i
] +=
179 REG_READ(ah
, AR_PHY_CAL_MEAS_0(i
));
180 ah
->totalPowerMeasQ
[i
] +=
181 REG_READ(ah
, AR_PHY_CAL_MEAS_1(i
));
182 ah
->totalIqCorrMeas
[i
] +=
183 (int32_t) REG_READ(ah
, AR_PHY_CAL_MEAS_2(i
));
184 ath_print(ath9k_hw_common(ah
), ATH_DBG_CALIBRATE
,
185 "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n",
186 ah
->cal_samples
, i
, ah
->totalPowerMeasI
[i
],
187 ah
->totalPowerMeasQ
[i
],
188 ah
->totalIqCorrMeas
[i
]);
192 static void ar9003_hw_iqcalibrate(struct ath_hw
*ah
, u8 numChains
)
194 struct ath_common
*common
= ath9k_hw_common(ah
);
195 u32 powerMeasQ
, powerMeasI
, iqCorrMeas
;
196 u32 qCoffDenom
, iCoffDenom
;
197 int32_t qCoff
, iCoff
;
199 const u_int32_t offset_array
[3] = {
200 AR_PHY_RX_IQCAL_CORR_B0
,
201 AR_PHY_RX_IQCAL_CORR_B1
,
202 AR_PHY_RX_IQCAL_CORR_B2
,
205 for (i
= 0; i
< numChains
; i
++) {
206 powerMeasI
= ah
->totalPowerMeasI
[i
];
207 powerMeasQ
= ah
->totalPowerMeasQ
[i
];
208 iqCorrMeas
= ah
->totalIqCorrMeas
[i
];
210 ath_print(common
, ATH_DBG_CALIBRATE
,
211 "Starting IQ Cal and Correction for Chain %d\n",
214 ath_print(common
, ATH_DBG_CALIBRATE
,
215 "Orignal: Chn %diq_corr_meas = 0x%08x\n",
216 i
, ah
->totalIqCorrMeas
[i
]);
220 if (iqCorrMeas
> 0x80000000) {
221 iqCorrMeas
= (0xffffffff - iqCorrMeas
) + 1;
225 ath_print(common
, ATH_DBG_CALIBRATE
,
226 "Chn %d pwr_meas_i = 0x%08x\n", i
, powerMeasI
);
227 ath_print(common
, ATH_DBG_CALIBRATE
,
228 "Chn %d pwr_meas_q = 0x%08x\n", i
, powerMeasQ
);
229 ath_print(common
, ATH_DBG_CALIBRATE
, "iqCorrNeg is 0x%08x\n",
232 iCoffDenom
= (powerMeasI
/ 2 + powerMeasQ
/ 2) / 256;
233 qCoffDenom
= powerMeasQ
/ 64;
235 if ((iCoffDenom
!= 0) && (qCoffDenom
!= 0)) {
236 iCoff
= iqCorrMeas
/ iCoffDenom
;
237 qCoff
= powerMeasI
/ qCoffDenom
- 64;
238 ath_print(common
, ATH_DBG_CALIBRATE
,
239 "Chn %d iCoff = 0x%08x\n", i
, iCoff
);
240 ath_print(common
, ATH_DBG_CALIBRATE
,
241 "Chn %d qCoff = 0x%08x\n", i
, qCoff
);
243 /* Force bounds on iCoff */
246 else if (iCoff
<= -63)
249 /* Negate iCoff if iqCorrNeg == 0 */
250 if (iqCorrNeg
== 0x0)
253 /* Force bounds on qCoff */
256 else if (qCoff
<= -63)
259 iCoff
= iCoff
& 0x7f;
260 qCoff
= qCoff
& 0x7f;
262 ath_print(common
, ATH_DBG_CALIBRATE
,
263 "Chn %d : iCoff = 0x%x qCoff = 0x%x\n",
265 ath_print(common
, ATH_DBG_CALIBRATE
,
266 "Register offset (0x%04x) "
267 "before update = 0x%x\n",
269 REG_READ(ah
, offset_array
[i
]));
271 REG_RMW_FIELD(ah
, offset_array
[i
],
272 AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF
,
274 REG_RMW_FIELD(ah
, offset_array
[i
],
275 AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF
,
277 ath_print(common
, ATH_DBG_CALIBRATE
,
278 "Register offset (0x%04x) QI COFF "
279 "(bitfields 0x%08x) after update = 0x%x\n",
281 AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF
,
282 REG_READ(ah
, offset_array
[i
]));
283 ath_print(common
, ATH_DBG_CALIBRATE
,
284 "Register offset (0x%04x) QQ COFF "
285 "(bitfields 0x%08x) after update = 0x%x\n",
287 AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF
,
288 REG_READ(ah
, offset_array
[i
]));
290 ath_print(common
, ATH_DBG_CALIBRATE
,
291 "IQ Cal and Correction done for Chain %d\n",
296 REG_SET_BIT(ah
, AR_PHY_RX_IQCAL_CORR_B0
,
297 AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE
);
298 ath_print(common
, ATH_DBG_CALIBRATE
,
299 "IQ Cal and Correction (offset 0x%04x) enabled "
300 "(bit position 0x%08x). New Value 0x%08x\n",
301 (unsigned) (AR_PHY_RX_IQCAL_CORR_B0
),
302 AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE
,
303 REG_READ(ah
, AR_PHY_RX_IQCAL_CORR_B0
));
306 static const struct ath9k_percal_data iq_cal_single_sample
= {
310 ar9003_hw_iqcal_collect
,
311 ar9003_hw_iqcalibrate
314 static void ar9003_hw_init_cal_settings(struct ath_hw
*ah
)
316 ah
->iq_caldata
.calData
= &iq_cal_single_sample
;
317 ah
->supp_cals
= IQ_MISMATCH_CAL
;
320 static bool ar9003_hw_iscal_supported(struct ath_hw
*ah
,
321 enum ath9k_cal_types calType
)
323 switch (calType
& ah
->supp_cals
) {
324 case IQ_MISMATCH_CAL
:
337 * solve 4x4 linear equation used in loopback iq cal.
339 static bool ar9003_hw_solve_iq_cal(struct ath_hw
*ah
,
350 s32 f1
= cos_2phi_1
- cos_2phi_2
,
351 f3
= sin_2phi_1
- sin_2phi_2
,
353 s32 mag_tx
, phs_tx
, mag_rx
, phs_rx
;
354 const s32 result_shift
= 1 << 15;
355 struct ath_common
*common
= ath9k_hw_common(ah
);
357 f2
= (f1
* f1
+ f3
* f3
) / result_shift
;
360 ath_print(common
, ATH_DBG_CALIBRATE
, "Divide by 0\n");
364 /* mag mismatch, tx */
365 mag_tx
= f1
* (mag_a0_d0
- mag_a1_d0
) + f3
* (phs_a0_d0
- phs_a1_d0
);
366 /* phs mismatch, tx */
367 phs_tx
= f3
* (-mag_a0_d0
+ mag_a1_d0
) + f1
* (phs_a0_d0
- phs_a1_d0
);
369 mag_tx
= (mag_tx
/ f2
);
370 phs_tx
= (phs_tx
/ f2
);
372 /* mag mismatch, rx */
373 mag_rx
= mag_a0_d0
- (cos_2phi_1
* mag_tx
+ sin_2phi_1
* phs_tx
) /
375 /* phs mismatch, rx */
376 phs_rx
= phs_a0_d0
+ (sin_2phi_1
* mag_tx
- cos_2phi_1
* phs_tx
) /
379 solved_eq
[0] = mag_tx
;
380 solved_eq
[1] = phs_tx
;
381 solved_eq
[2] = mag_rx
;
382 solved_eq
[3] = phs_rx
;
387 static s32
ar9003_hw_find_mag_approx(struct ath_hw
*ah
, s32 in_re
, s32 in_im
)
389 s32 abs_i
= abs(in_re
),
401 return max_abs
- (max_abs
/ 32) + (min_abs
/ 8) + (min_abs
/ 4);
406 static bool ar9003_hw_calc_iq_corr(struct ath_hw
*ah
,
411 s32 i2_m_q2_a0_d0
, i2_p_q2_a0_d0
, iq_corr_a0_d0
,
412 i2_m_q2_a0_d1
, i2_p_q2_a0_d1
, iq_corr_a0_d1
,
413 i2_m_q2_a1_d0
, i2_p_q2_a1_d0
, iq_corr_a1_d0
,
414 i2_m_q2_a1_d1
, i2_p_q2_a1_d1
, iq_corr_a1_d1
;
415 s32 mag_a0_d0
, mag_a1_d0
, mag_a0_d1
, mag_a1_d1
,
416 phs_a0_d0
, phs_a1_d0
, phs_a0_d1
, phs_a1_d1
,
417 sin_2phi_1
, cos_2phi_1
,
418 sin_2phi_2
, cos_2phi_2
;
419 s32 mag_tx
, phs_tx
, mag_rx
, phs_rx
;
420 s32 solved_eq
[4], mag_corr_tx
, phs_corr_tx
, mag_corr_rx
, phs_corr_rx
,
422 const s32 res_scale
= 1 << 15;
423 const s32 delpt_shift
= 1 << 8;
425 struct ath_common
*common
= ath9k_hw_common(ah
);
427 i2_m_q2_a0_d0
= iq_res
[0] & 0xfff;
428 i2_p_q2_a0_d0
= (iq_res
[0] >> 12) & 0xfff;
429 iq_corr_a0_d0
= ((iq_res
[0] >> 24) & 0xff) + ((iq_res
[1] & 0xf) << 8);
431 if (i2_m_q2_a0_d0
> 0x800)
432 i2_m_q2_a0_d0
= -((0xfff - i2_m_q2_a0_d0
) + 1);
434 if (i2_p_q2_a0_d0
> 0x800)
435 i2_p_q2_a0_d0
= -((0xfff - i2_p_q2_a0_d0
) + 1);
437 if (iq_corr_a0_d0
> 0x800)
438 iq_corr_a0_d0
= -((0xfff - iq_corr_a0_d0
) + 1);
440 i2_m_q2_a0_d1
= (iq_res
[1] >> 4) & 0xfff;
441 i2_p_q2_a0_d1
= (iq_res
[2] & 0xfff);
442 iq_corr_a0_d1
= (iq_res
[2] >> 12) & 0xfff;
444 if (i2_m_q2_a0_d1
> 0x800)
445 i2_m_q2_a0_d1
= -((0xfff - i2_m_q2_a0_d1
) + 1);
447 if (i2_p_q2_a0_d1
> 0x800)
448 i2_p_q2_a0_d1
= -((0xfff - i2_p_q2_a0_d1
) + 1);
450 if (iq_corr_a0_d1
> 0x800)
451 iq_corr_a0_d1
= -((0xfff - iq_corr_a0_d1
) + 1);
453 i2_m_q2_a1_d0
= ((iq_res
[2] >> 24) & 0xff) + ((iq_res
[3] & 0xf) << 8);
454 i2_p_q2_a1_d0
= (iq_res
[3] >> 4) & 0xfff;
455 iq_corr_a1_d0
= iq_res
[4] & 0xfff;
457 if (i2_m_q2_a1_d0
> 0x800)
458 i2_m_q2_a1_d0
= -((0xfff - i2_m_q2_a1_d0
) + 1);
460 if (i2_p_q2_a1_d0
> 0x800)
461 i2_p_q2_a1_d0
= -((0xfff - i2_p_q2_a1_d0
) + 1);
463 if (iq_corr_a1_d0
> 0x800)
464 iq_corr_a1_d0
= -((0xfff - iq_corr_a1_d0
) + 1);
466 i2_m_q2_a1_d1
= (iq_res
[4] >> 12) & 0xfff;
467 i2_p_q2_a1_d1
= ((iq_res
[4] >> 24) & 0xff) + ((iq_res
[5] & 0xf) << 8);
468 iq_corr_a1_d1
= (iq_res
[5] >> 4) & 0xfff;
470 if (i2_m_q2_a1_d1
> 0x800)
471 i2_m_q2_a1_d1
= -((0xfff - i2_m_q2_a1_d1
) + 1);
473 if (i2_p_q2_a1_d1
> 0x800)
474 i2_p_q2_a1_d1
= -((0xfff - i2_p_q2_a1_d1
) + 1);
476 if (iq_corr_a1_d1
> 0x800)
477 iq_corr_a1_d1
= -((0xfff - iq_corr_a1_d1
) + 1);
479 if ((i2_p_q2_a0_d0
== 0) || (i2_p_q2_a0_d1
== 0) ||
480 (i2_p_q2_a1_d0
== 0) || (i2_p_q2_a1_d1
== 0)) {
481 ath_print(common
, ATH_DBG_CALIBRATE
,
482 "Divide by 0:\na0_d0=%d\n"
483 "a0_d1=%d\na2_d0=%d\na1_d1=%d\n",
484 i2_p_q2_a0_d0
, i2_p_q2_a0_d1
,
485 i2_p_q2_a1_d0
, i2_p_q2_a1_d1
);
489 mag_a0_d0
= (i2_m_q2_a0_d0
* res_scale
) / i2_p_q2_a0_d0
;
490 phs_a0_d0
= (iq_corr_a0_d0
* res_scale
) / i2_p_q2_a0_d0
;
492 mag_a0_d1
= (i2_m_q2_a0_d1
* res_scale
) / i2_p_q2_a0_d1
;
493 phs_a0_d1
= (iq_corr_a0_d1
* res_scale
) / i2_p_q2_a0_d1
;
495 mag_a1_d0
= (i2_m_q2_a1_d0
* res_scale
) / i2_p_q2_a1_d0
;
496 phs_a1_d0
= (iq_corr_a1_d0
* res_scale
) / i2_p_q2_a1_d0
;
498 mag_a1_d1
= (i2_m_q2_a1_d1
* res_scale
) / i2_p_q2_a1_d1
;
499 phs_a1_d1
= (iq_corr_a1_d1
* res_scale
) / i2_p_q2_a1_d1
;
501 /* w/o analog phase shift */
502 sin_2phi_1
= (((mag_a0_d0
- mag_a0_d1
) * delpt_shift
) / DELPT
);
503 /* w/o analog phase shift */
504 cos_2phi_1
= (((phs_a0_d1
- phs_a0_d0
) * delpt_shift
) / DELPT
);
505 /* w/ analog phase shift */
506 sin_2phi_2
= (((mag_a1_d0
- mag_a1_d1
) * delpt_shift
) / DELPT
);
507 /* w/ analog phase shift */
508 cos_2phi_2
= (((phs_a1_d1
- phs_a1_d0
) * delpt_shift
) / DELPT
);
511 * force sin^2 + cos^2 = 1;
512 * find magnitude by approximation
514 mag1
= ar9003_hw_find_mag_approx(ah
, cos_2phi_1
, sin_2phi_1
);
515 mag2
= ar9003_hw_find_mag_approx(ah
, cos_2phi_2
, sin_2phi_2
);
517 if ((mag1
== 0) || (mag2
== 0)) {
518 ath_print(common
, ATH_DBG_CALIBRATE
,
519 "Divide by 0: mag1=%d, mag2=%d\n",
524 /* normalization sin and cos by mag */
525 sin_2phi_1
= (sin_2phi_1
* res_scale
/ mag1
);
526 cos_2phi_1
= (cos_2phi_1
* res_scale
/ mag1
);
527 sin_2phi_2
= (sin_2phi_2
* res_scale
/ mag2
);
528 cos_2phi_2
= (cos_2phi_2
* res_scale
/ mag2
);
530 /* calculate IQ mismatch */
531 if (!ar9003_hw_solve_iq_cal(ah
,
532 sin_2phi_1
, cos_2phi_1
,
533 sin_2phi_2
, cos_2phi_2
,
534 mag_a0_d0
, phs_a0_d0
,
536 phs_a1_d0
, solved_eq
)) {
537 ath_print(common
, ATH_DBG_CALIBRATE
,
538 "Call to ar9003_hw_solve_iq_cal() failed.\n");
542 mag_tx
= solved_eq
[0];
543 phs_tx
= solved_eq
[1];
544 mag_rx
= solved_eq
[2];
545 phs_rx
= solved_eq
[3];
547 ath_print(common
, ATH_DBG_CALIBRATE
,
548 "chain %d: mag mismatch=%d phase mismatch=%d\n",
549 chain_idx
, mag_tx
/res_scale
, phs_tx
/res_scale
);
551 if (res_scale
== mag_tx
) {
552 ath_print(common
, ATH_DBG_CALIBRATE
,
553 "Divide by 0: mag_tx=%d, res_scale=%d\n",
558 /* calculate and quantize Tx IQ correction factor */
559 mag_corr_tx
= (mag_tx
* res_scale
) / (res_scale
- mag_tx
);
560 phs_corr_tx
= -phs_tx
;
562 q_q_coff
= (mag_corr_tx
* 128 / res_scale
);
563 q_i_coff
= (phs_corr_tx
* 256 / res_scale
);
565 ath_print(common
, ATH_DBG_CALIBRATE
,
566 "tx chain %d: mag corr=%d phase corr=%d\n",
567 chain_idx
, q_q_coff
, q_i_coff
);
578 iqc_coeff
[0] = (q_q_coff
* 128) + q_i_coff
;
580 ath_print(common
, ATH_DBG_CALIBRATE
,
581 "tx chain %d: iq corr coeff=%x\n",
582 chain_idx
, iqc_coeff
[0]);
584 if (-mag_rx
== res_scale
) {
585 ath_print(common
, ATH_DBG_CALIBRATE
,
586 "Divide by 0: mag_rx=%d, res_scale=%d\n",
591 /* calculate and quantize Rx IQ correction factors */
592 mag_corr_rx
= (-mag_rx
* res_scale
) / (res_scale
+ mag_rx
);
593 phs_corr_rx
= -phs_rx
;
595 q_q_coff
= (mag_corr_rx
* 128 / res_scale
);
596 q_i_coff
= (phs_corr_rx
* 256 / res_scale
);
598 ath_print(common
, ATH_DBG_CALIBRATE
,
599 "rx chain %d: mag corr=%d phase corr=%d\n",
600 chain_idx
, q_q_coff
, q_i_coff
);
611 iqc_coeff
[1] = (q_q_coff
* 128) + q_i_coff
;
613 ath_print(common
, ATH_DBG_CALIBRATE
,
614 "rx chain %d: iq corr coeff=%x\n",
615 chain_idx
, iqc_coeff
[1]);
620 static void ar9003_hw_tx_iq_cal(struct ath_hw
*ah
)
622 struct ath_common
*common
= ath9k_hw_common(ah
);
623 const u32 txiqcal_status
[AR9300_MAX_CHAINS
] = {
624 AR_PHY_TX_IQCAL_STATUS_B0
,
625 AR_PHY_TX_IQCAL_STATUS_B1
,
626 AR_PHY_TX_IQCAL_STATUS_B2
,
628 const u32 tx_corr_coeff
[AR9300_MAX_CHAINS
] = {
629 AR_PHY_TX_IQCAL_CORR_COEFF_01_B0
,
630 AR_PHY_TX_IQCAL_CORR_COEFF_01_B1
,
631 AR_PHY_TX_IQCAL_CORR_COEFF_01_B2
,
633 const u32 rx_corr
[AR9300_MAX_CHAINS
] = {
634 AR_PHY_RX_IQCAL_CORR_B0
,
635 AR_PHY_RX_IQCAL_CORR_B1
,
636 AR_PHY_RX_IQCAL_CORR_B2
,
638 const u_int32_t chan_info_tab
[] = {
639 AR_PHY_CHAN_INFO_TAB_0
,
640 AR_PHY_CHAN_INFO_TAB_1
,
641 AR_PHY_CHAN_INFO_TAB_2
,
648 for (i
= 0; i
< AR9300_MAX_CHAINS
; i
++) {
649 if (ah
->txchainmask
& (1 << i
))
653 REG_RMW_FIELD(ah
, AR_PHY_TX_IQCAL_CONTROL_1
,
654 AR_PHY_TX_IQCAQL_CONTROL_1_IQCORR_I_Q_COFF_DELPT
,
656 REG_RMW_FIELD(ah
, AR_PHY_TX_IQCAL_START
,
657 AR_PHY_TX_IQCAL_START_DO_CAL
,
658 AR_PHY_TX_IQCAL_START_DO_CAL
);
660 if (!ath9k_hw_wait(ah
, AR_PHY_TX_IQCAL_START
,
661 AR_PHY_TX_IQCAL_START_DO_CAL
,
662 0, AH_WAIT_TIMEOUT
)) {
663 ath_print(common
, ATH_DBG_CALIBRATE
,
664 "Tx IQ Cal not complete.\n");
665 goto TX_IQ_CAL_FAILED
;
668 for (i
= 0; i
< num_chains
; i
++) {
669 ath_print(common
, ATH_DBG_CALIBRATE
,
670 "Doing Tx IQ Cal for chain %d.\n", i
);
672 if (REG_READ(ah
, txiqcal_status
[i
]) &
673 AR_PHY_TX_IQCAL_STATUS_FAILED
) {
674 ath_print(common
, ATH_DBG_CALIBRATE
,
675 "Tx IQ Cal failed for chain %d.\n", i
);
676 goto TX_IQ_CAL_FAILED
;
679 for (j
= 0; j
< 3; j
++) {
680 u_int8_t idx
= 2 * j
,
683 REG_RMW_FIELD(ah
, AR_PHY_CHAN_INFO_MEMORY
,
684 AR_PHY_CHAN_INFO_TAB_S2_READ
, 0);
687 iq_res
[idx
] = REG_READ(ah
, chan_info_tab
[i
] + offset
);
689 REG_RMW_FIELD(ah
, AR_PHY_CHAN_INFO_MEMORY
,
690 AR_PHY_CHAN_INFO_TAB_S2_READ
, 1);
693 iq_res
[idx
+1] = 0xffff & REG_READ(ah
,
697 ath_print(common
, ATH_DBG_CALIBRATE
,
698 "IQ RES[%d]=0x%x IQ_RES[%d]=0x%x\n",
699 idx
, iq_res
[idx
], idx
+1, iq_res
[idx
+1]);
702 if (!ar9003_hw_calc_iq_corr(ah
, i
, iq_res
, iqc_coeff
)) {
703 ath_print(common
, ATH_DBG_CALIBRATE
,
704 "Failed in calculation of IQ correction.\n");
705 goto TX_IQ_CAL_FAILED
;
708 ath_print(common
, ATH_DBG_CALIBRATE
,
709 "IQ_COEFF[0] = 0x%x IQ_COEFF[1] = 0x%x\n",
710 iqc_coeff
[0], iqc_coeff
[1]);
712 REG_RMW_FIELD(ah
, tx_corr_coeff
[i
],
713 AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE
,
715 REG_RMW_FIELD(ah
, rx_corr
[i
],
716 AR_PHY_RX_IQCAL_CORR_LOOPBACK_IQCORR_Q_Q_COFF
,
718 REG_RMW_FIELD(ah
, rx_corr
[i
],
719 AR_PHY_RX_IQCAL_CORR_LOOPBACK_IQCORR_Q_I_COFF
,
723 REG_RMW_FIELD(ah
, AR_PHY_TX_IQCAL_CONTROL_3
,
724 AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN
, 0x1);
725 REG_RMW_FIELD(ah
, AR_PHY_RX_IQCAL_CORR_B0
,
726 AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN
, 0x1);
731 ath_print(common
, ATH_DBG_CALIBRATE
, "Tx IQ Cal failed\n");
734 static bool ar9003_hw_init_cal(struct ath_hw
*ah
,
735 struct ath9k_channel
*chan
)
737 struct ath_common
*common
= ath9k_hw_common(ah
);
740 * 0x7 = 0b111 , AR9003 needs to be configured for 3-chain mode before
741 * running AGC/TxIQ cals
743 ar9003_hw_set_chain_masks(ah
, 0x7, 0x7);
745 /* Do Tx IQ Calibration */
746 ar9003_hw_tx_iq_cal(ah
);
747 REG_WRITE(ah
, AR_PHY_ACTIVE
, AR_PHY_ACTIVE_DIS
);
749 REG_WRITE(ah
, AR_PHY_ACTIVE
, AR_PHY_ACTIVE_EN
);
751 /* Calibrate the AGC */
752 REG_WRITE(ah
, AR_PHY_AGC_CONTROL
,
753 REG_READ(ah
, AR_PHY_AGC_CONTROL
) |
754 AR_PHY_AGC_CONTROL_CAL
);
756 /* Poll for offset calibration complete */
757 if (!ath9k_hw_wait(ah
, AR_PHY_AGC_CONTROL
, AR_PHY_AGC_CONTROL_CAL
,
758 0, AH_WAIT_TIMEOUT
)) {
759 ath_print(common
, ATH_DBG_CALIBRATE
,
760 "offset calibration failed to "
761 "complete in 1ms; noisy environment?\n");
765 /* Revert chainmasks to their original values before NF cal */
766 ar9003_hw_set_chain_masks(ah
, ah
->rxchainmask
, ah
->txchainmask
);
768 ath9k_hw_start_nfcal(ah
, true);
770 /* Initialize list pointers */
771 ah
->cal_list
= ah
->cal_list_last
= ah
->cal_list_curr
= NULL
;
773 if (ar9003_hw_iscal_supported(ah
, IQ_MISMATCH_CAL
)) {
774 INIT_CAL(&ah
->iq_caldata
);
775 INSERT_CAL(ah
, &ah
->iq_caldata
);
776 ath_print(common
, ATH_DBG_CALIBRATE
,
777 "enabling IQ Calibration.\n");
780 if (ar9003_hw_iscal_supported(ah
, TEMP_COMP_CAL
)) {
781 INIT_CAL(&ah
->tempCompCalData
);
782 INSERT_CAL(ah
, &ah
->tempCompCalData
);
783 ath_print(common
, ATH_DBG_CALIBRATE
,
784 "enabling Temperature Compensation Calibration.\n");
787 /* Initialize current pointer to first element in list */
788 ah
->cal_list_curr
= ah
->cal_list
;
790 if (ah
->cal_list_curr
)
791 ath9k_hw_reset_calibration(ah
, ah
->cal_list_curr
);
794 ah
->caldata
->CalValid
= 0;
799 void ar9003_hw_attach_calib_ops(struct ath_hw
*ah
)
801 struct ath_hw_private_ops
*priv_ops
= ath9k_hw_private_ops(ah
);
802 struct ath_hw_ops
*ops
= ath9k_hw_ops(ah
);
804 priv_ops
->init_cal_settings
= ar9003_hw_init_cal_settings
;
805 priv_ops
->init_cal
= ar9003_hw_init_cal
;
806 priv_ops
->setup_calibration
= ar9003_hw_setup_calibration
;
807 priv_ops
->iscal_supported
= ar9003_hw_iscal_supported
;
809 ops
->calibrate
= ar9003_hw_calibrate
;