diff options
author | Sujith Manoharan <c_manoha@qca.qualcomm.com> | 2014-02-07 10:29:53 +0530 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-02-12 15:36:07 -0500 |
commit | 97fe6420c9a362ac9d0749db44b7b6629583813b (patch) | |
tree | b7fd07af7b1436ace4046baece39f277ba7b1987 | |
parent | adddc0d20bf4476380da94bfda8c591c49bb6cde (diff) |
ath9k: Modify IQ calibration for AR955x
IQ calibration post-processing for AR955x is different
from other chips - instead of just doing it as part
of AGC calibration once, it is triggered 3 times and
a median is determined. This patch adds initial support
for changing the calibration behavior for AR955x.
Also, to simplify things, a helper routine to issue/poll
AGC calibration is used.
For non-AR955x chips, the iqcal_idx (which will be used
in subsequent patches) is set to zero.
Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ar9003_calib.c | 81 |
1 files changed, 59 insertions, 22 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 12310e19275d..327befa06699 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -23,6 +23,7 @@ #define MAX_MEASUREMENT MAX_IQCAL_MEASUREMENT #define MAX_MAG_DELTA 11 #define MAX_PHS_DELTA 10 +#define MAXIQCAL 3 struct coeff { int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT]; @@ -797,7 +798,7 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, if (q_q_coff > 63) q_q_coff = 63; - iqc_coeff[0] = (q_q_coff * 128) + q_i_coff; + iqc_coeff[0] = (q_q_coff * 128) + (0x7f & q_i_coff); ath_dbg(common, CALIBRATE, "tx chain %d: iq corr coeff=%x\n", chain_idx, iqc_coeff[0]); @@ -828,7 +829,7 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, if (q_q_coff > 63) q_q_coff = 63; - iqc_coeff[1] = (q_q_coff * 128) + q_i_coff; + iqc_coeff[1] = (q_q_coff * 128) + (0x7f & q_i_coff); ath_dbg(common, CALIBRATE, "rx chain %d: iq corr coeff=%x\n", chain_idx, iqc_coeff[1]); @@ -991,7 +992,9 @@ static bool ar9003_hw_tx_iq_cal_run(struct ath_hw *ah) return true; } -static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable) +static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, + int iqcal_idx, + bool is_reusable) { struct ath_common *common = ath9k_hw_common(ah); const u32 txiqcal_status[AR9300_MAX_CHAINS] = { @@ -1410,7 +1413,7 @@ skip_tx_iqcal: } if (txiqcal_done) - ar9003_hw_tx_iq_cal_post_proc(ah, is_reusable); + ar9003_hw_tx_iq_cal_post_proc(ah, 0, is_reusable); else if (caldata && test_bit(TXIQCAL_DONE, &caldata->cal_flags)) ar9003_hw_tx_iq_cal_reload(ah); @@ -1456,6 +1459,29 @@ skip_tx_iqcal: return true; } +static bool do_ar9003_agc_cal(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + bool status; + + REG_WRITE(ah, AR_PHY_AGC_CONTROL, + REG_READ(ah, AR_PHY_AGC_CONTROL) | + AR_PHY_AGC_CONTROL_CAL); + + status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_CAL, + 0, AH_WAIT_TIMEOUT); + if (!status) { + ath_dbg(common, CALIBRATE, + "offset calibration failed to complete in %d ms," + "noisy environment?\n", + AH_WAIT_TIMEOUT / 1000); + return false; + } + + return true; +} + static bool ar9003_hw_init_cal_soc(struct ath_hw *ah, struct ath9k_channel *chan) { @@ -1464,6 +1490,7 @@ static bool ar9003_hw_init_cal_soc(struct ath_hw *ah, bool txiqcal_done = false; bool status = true; bool run_agc_cal = false, sep_iq_cal = false; + int i = 0; /* Use chip chainmask only for calibration */ ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask); @@ -1518,27 +1545,37 @@ skip_tx_iqcal: if (AR_SREV_9330_11(ah)) ar9003_hw_manual_peak_cal(ah, 0, IS_CHAN_2GHZ(chan)); - /* Calibrate the AGC */ - REG_WRITE(ah, AR_PHY_AGC_CONTROL, - REG_READ(ah, AR_PHY_AGC_CONTROL) | - AR_PHY_AGC_CONTROL_CAL); - - /* Poll for offset calibration complete */ - status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, - AR_PHY_AGC_CONTROL_CAL, - 0, AH_WAIT_TIMEOUT); - } + /* + * For non-AR9550 chips, we just trigger AGC calibration + * in the HW, poll for completion and then process + * the results. + * + * For AR955x, we run it multiple times and use + * median IQ correction. + */ + if (!AR_SREV_9550(ah)) { + status = do_ar9003_agc_cal(ah); + if (!status) + return false; - if (!status) { - ath_dbg(common, CALIBRATE, - "offset calibration failed to complete in %d ms; noisy environment?\n", - AH_WAIT_TIMEOUT / 1000); - return false; + if (txiqcal_done) + ar9003_hw_tx_iq_cal_post_proc(ah, 0, false); + } else { + if (!txiqcal_done) { + status = do_ar9003_agc_cal(ah); + if (!status) + return false; + } else { + for (i = 0; i < MAXIQCAL; i++) { + status = do_ar9003_agc_cal(ah); + if (!status) + return false; + ar9003_hw_tx_iq_cal_post_proc(ah, i, false); + } + } + } } - if (txiqcal_done) - ar9003_hw_tx_iq_cal_post_proc(ah, false); - /* Revert chainmask to runtime parameters */ ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask); |