diff options
author | Luis R. Rodriguez <lrodriguez@atheros.com> | 2010-04-15 17:39:11 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-04-16 15:43:35 -0400 |
commit | df23acaa5d3239745805650e2f27a4252182c063 (patch) | |
tree | 045e59bc9d7bd70ddbf4e2b9c0f8e623157a5f41 /drivers/net/wireless | |
parent | 4b01931e3a3ca5ec49604e2b279b8b9dd42fbe4c (diff) |
ath9k_hw: complete AR9003 calibration
This goes with some new shiny TX IQ calibration that AR9003
hardware family supports.
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ar9003_calib.c | 577 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ar9003_phy.c | 100 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/eeprom.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/hw.h | 1 |
4 files changed, 667 insertions, 12 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index f0e8f639ecf..5e20b4860c7 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c | |||
@@ -58,20 +58,108 @@ static void ar9003_hw_setup_calibration(struct ath_hw *ah, | |||
58 | } | 58 | } |
59 | } | 59 | } |
60 | 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 | |||
61 | static bool ar9003_hw_calibrate(struct ath_hw *ah, | 117 | static bool ar9003_hw_calibrate(struct ath_hw *ah, |
62 | struct ath9k_channel *chan, | 118 | struct ath9k_channel *chan, |
63 | u8 rxchainmask, | 119 | u8 rxchainmask, |
64 | bool longcal) | 120 | bool longcal) |
65 | { | 121 | { |
66 | /* TODO */ | 122 | bool iscaldone = true; |
67 | return false; | 123 | struct ath9k_cal_list *currCal = ah->cal_list_curr; |
68 | } | 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 | } | ||
69 | 148 | ||
70 | static bool ar9003_hw_init_cal(struct ath_hw *ah, | 149 | /* Do NF cal only at longer intervals */ |
71 | struct ath9k_channel *chan) | 150 | if (longcal) { |
72 | { | 151 | /* |
73 | /* TODO */ | 152 | * Load the NF from history buffer of the current channel. |
74 | return false; | 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; | ||
75 | } | 163 | } |
76 | 164 | ||
77 | static void ar9003_hw_iqcal_collect(struct ath_hw *ah) | 165 | static void ar9003_hw_iqcal_collect(struct ath_hw *ah) |
@@ -225,13 +313,479 @@ static void ar9003_hw_init_cal_settings(struct ath_hw *ah) | |||
225 | static bool ar9003_hw_iscal_supported(struct ath_hw *ah, | 313 | static bool ar9003_hw_iscal_supported(struct ath_hw *ah, |
226 | enum ath9k_cal_types calType) | 314 | enum ath9k_cal_types calType) |
227 | { | 315 | { |
228 | /* TODO */ | 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 | |||
229 | return false; | 330 | return false; |
230 | } | 331 | } |
231 | 332 | ||
232 | static void ar9003_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) | 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) | ||
233 | { | 385 | { |
234 | /* TODO */ | 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 | return; | ||
730 | } | ||
731 | |||
732 | static bool ar9003_hw_init_cal(struct ath_hw *ah, | ||
733 | struct ath9k_channel *chan) | ||
734 | { | ||
735 | struct ath_common *common = ath9k_hw_common(ah); | ||
736 | |||
737 | /* | ||
738 | * 0x7 = 0b111 , AR9003 needs to be configured for 3-chain mode before | ||
739 | * running AGC/TxIQ cals | ||
740 | */ | ||
741 | ar9003_hw_set_chain_masks(ah, 0x7, 0x7); | ||
742 | |||
743 | /* Calibrate the AGC */ | ||
744 | REG_WRITE(ah, AR_PHY_AGC_CONTROL, | ||
745 | REG_READ(ah, AR_PHY_AGC_CONTROL) | | ||
746 | AR_PHY_AGC_CONTROL_CAL); | ||
747 | |||
748 | /* Poll for offset calibration complete */ | ||
749 | if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, | ||
750 | 0, AH_WAIT_TIMEOUT)) { | ||
751 | ath_print(common, ATH_DBG_CALIBRATE, | ||
752 | "offset calibration failed to " | ||
753 | "complete in 1ms; noisy environment?\n"); | ||
754 | return false; | ||
755 | } | ||
756 | |||
757 | /* Do 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; | ||
235 | } | 789 | } |
236 | 790 | ||
237 | void ar9003_hw_attach_calib_ops(struct ath_hw *ah) | 791 | void ar9003_hw_attach_calib_ops(struct ath_hw *ah) |
@@ -243,7 +797,6 @@ void ar9003_hw_attach_calib_ops(struct ath_hw *ah) | |||
243 | priv_ops->init_cal = ar9003_hw_init_cal; | 797 | priv_ops->init_cal = ar9003_hw_init_cal; |
244 | priv_ops->setup_calibration = ar9003_hw_setup_calibration; | 798 | priv_ops->setup_calibration = ar9003_hw_setup_calibration; |
245 | priv_ops->iscal_supported = ar9003_hw_iscal_supported; | 799 | priv_ops->iscal_supported = ar9003_hw_iscal_supported; |
246 | priv_ops->loadnf = ar9003_hw_loadnf; | ||
247 | 800 | ||
248 | ops->calibrate = ar9003_hw_calibrate; | 801 | ops->calibrate = ar9003_hw_calibrate; |
249 | } | 802 | } |
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 67b3b651843..fee07fd7a59 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c | |||
@@ -814,6 +814,105 @@ void ar9003_hw_set_nf_limits(struct ath_hw *ah) | |||
814 | ah->nf_5g_min = AR_PHY_CCA_MIN_GOOD_VAL_9300_5GHZ; | 814 | ah->nf_5g_min = AR_PHY_CCA_MIN_GOOD_VAL_9300_5GHZ; |
815 | } | 815 | } |
816 | 816 | ||
817 | /* | ||
818 | * Find out which of the RX chains are enabled | ||
819 | */ | ||
820 | static u32 ar9003_hw_get_rx_chainmask(struct ath_hw *ah) | ||
821 | { | ||
822 | u32 chain = REG_READ(ah, AR_PHY_RX_CHAINMASK); | ||
823 | /* | ||
824 | * The bits [2:0] indicate the rx chain mask and are to be | ||
825 | * interpreted as follows: | ||
826 | * 00x => Only chain 0 is enabled | ||
827 | * 01x => Chain 1 and 0 enabled | ||
828 | * 1xx => Chain 2,1 and 0 enabled | ||
829 | */ | ||
830 | return chain & 0x7; | ||
831 | } | ||
832 | |||
833 | static void ar9003_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) | ||
834 | { | ||
835 | struct ath9k_nfcal_hist *h; | ||
836 | unsigned i, j; | ||
837 | int32_t val; | ||
838 | const u32 ar9300_cca_regs[6] = { | ||
839 | AR_PHY_CCA_0, | ||
840 | AR_PHY_CCA_1, | ||
841 | AR_PHY_CCA_2, | ||
842 | AR_PHY_EXT_CCA, | ||
843 | AR_PHY_EXT_CCA_1, | ||
844 | AR_PHY_EXT_CCA_2, | ||
845 | }; | ||
846 | u8 chainmask, rx_chain_status; | ||
847 | struct ath_common *common = ath9k_hw_common(ah); | ||
848 | |||
849 | rx_chain_status = ar9003_hw_get_rx_chainmask(ah); | ||
850 | |||
851 | chainmask = 0x3F; | ||
852 | h = ah->nfCalHist; | ||
853 | |||
854 | for (i = 0; i < NUM_NF_READINGS; i++) { | ||
855 | if (chainmask & (1 << i)) { | ||
856 | val = REG_READ(ah, ar9300_cca_regs[i]); | ||
857 | val &= 0xFFFFFE00; | ||
858 | val |= (((u32) (h[i].privNF) << 1) & 0x1ff); | ||
859 | REG_WRITE(ah, ar9300_cca_regs[i], val); | ||
860 | } | ||
861 | } | ||
862 | |||
863 | /* | ||
864 | * Load software filtered NF value into baseband internal minCCApwr | ||
865 | * variable. | ||
866 | */ | ||
867 | REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, | ||
868 | AR_PHY_AGC_CONTROL_ENABLE_NF); | ||
869 | REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, | ||
870 | AR_PHY_AGC_CONTROL_NO_UPDATE_NF); | ||
871 | REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); | ||
872 | |||
873 | /* | ||
874 | * Wait for load to complete, should be fast, a few 10s of us. | ||
875 | * The max delay was changed from an original 250us to 10000us | ||
876 | * since 250us often results in NF load timeout and causes deaf | ||
877 | * condition during stress testing 12/12/2009 | ||
878 | */ | ||
879 | for (j = 0; j < 1000; j++) { | ||
880 | if ((REG_READ(ah, AR_PHY_AGC_CONTROL) & | ||
881 | AR_PHY_AGC_CONTROL_NF) == 0) | ||
882 | break; | ||
883 | udelay(10); | ||
884 | } | ||
885 | |||
886 | /* | ||
887 | * We timed out waiting for the noisefloor to load, probably due to an | ||
888 | * in-progress rx. Simply return here and allow the load plenty of time | ||
889 | * to complete before the next calibration interval. We need to avoid | ||
890 | * trying to load -50 (which happens below) while the previous load is | ||
891 | * still in progress as this can cause rx deafness. Instead by returning | ||
892 | * here, the baseband nf cal will just be capped by our present | ||
893 | * noisefloor until the next calibration timer. | ||
894 | */ | ||
895 | if (j == 1000) { | ||
896 | ath_print(common, ATH_DBG_ANY, "Timeout while waiting for nf " | ||
897 | "to load: AR_PHY_AGC_CONTROL=0x%x\n", | ||
898 | REG_READ(ah, AR_PHY_AGC_CONTROL)); | ||
899 | } | ||
900 | |||
901 | /* | ||
902 | * Restore maxCCAPower register parameter again so that we're not capped | ||
903 | * by the median we just loaded. This will be initial (and max) value | ||
904 | * of next noise floor calibration the baseband does. | ||
905 | */ | ||
906 | for (i = 0; i < NUM_NF_READINGS; i++) { | ||
907 | if (chainmask & (1 << i)) { | ||
908 | val = REG_READ(ah, ar9300_cca_regs[i]); | ||
909 | val &= 0xFFFFFE00; | ||
910 | val |= (((u32) (-50) << 1) & 0x1ff); | ||
911 | REG_WRITE(ah, ar9300_cca_regs[i], val); | ||
912 | } | ||
913 | } | ||
914 | } | ||
915 | |||
817 | void ar9003_hw_attach_phy_ops(struct ath_hw *ah) | 916 | void ar9003_hw_attach_phy_ops(struct ath_hw *ah) |
818 | { | 917 | { |
819 | struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); | 918 | struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); |
@@ -833,4 +932,5 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah) | |||
833 | priv_ops->set_diversity = ar9003_hw_set_diversity; | 932 | priv_ops->set_diversity = ar9003_hw_set_diversity; |
834 | priv_ops->ani_control = ar9003_hw_ani_control; | 933 | priv_ops->ani_control = ar9003_hw_ani_control; |
835 | priv_ops->do_getnf = ar9003_hw_do_getnf; | 934 | priv_ops->do_getnf = ar9003_hw_do_getnf; |
935 | priv_ops->loadnf = ar9003_hw_loadnf; | ||
836 | } | 936 | } |
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h index e087e2de606..c0cd717738c 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.h +++ b/drivers/net/wireless/ath/ath9k/eeprom.h | |||
@@ -155,6 +155,7 @@ | |||
155 | #define AR5416_BCHAN_UNUSED 0xFF | 155 | #define AR5416_BCHAN_UNUSED 0xFF |
156 | #define AR5416_MAX_PWR_RANGE_IN_HALF_DB 64 | 156 | #define AR5416_MAX_PWR_RANGE_IN_HALF_DB 64 |
157 | #define AR5416_MAX_CHAINS 3 | 157 | #define AR5416_MAX_CHAINS 3 |
158 | #define AR9300_MAX_CHAINS 3 | ||
158 | #define AR5416_PWR_TABLE_OFFSET_DB -5 | 159 | #define AR5416_PWR_TABLE_OFFSET_DB -5 |
159 | 160 | ||
160 | /* Rx gain type values */ | 161 | /* Rx gain type values */ |
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index d28a904606b..cb0421a910d 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h | |||
@@ -611,6 +611,7 @@ struct ath_hw { | |||
611 | struct ath9k_cal_list adcgain_caldata; | 611 | struct ath9k_cal_list adcgain_caldata; |
612 | struct ath9k_cal_list adcdc_calinitdata; | 612 | struct ath9k_cal_list adcdc_calinitdata; |
613 | struct ath9k_cal_list adcdc_caldata; | 613 | struct ath9k_cal_list adcdc_caldata; |
614 | struct ath9k_cal_list tempCompCalData; | ||
614 | struct ath9k_cal_list *cal_list; | 615 | struct ath9k_cal_list *cal_list; |
615 | struct ath9k_cal_list *cal_list_last; | 616 | struct ath9k_cal_list *cal_list_last; |
616 | struct ath9k_cal_list *cal_list_curr; | 617 | struct ath9k_cal_list *cal_list_curr; |