diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/ar9003_calib.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ar9003_calib.c | 280 |
1 files changed, 236 insertions, 44 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index f48051c50092..16851cb109a6 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -17,8 +17,9 @@ #include "hw.h" #include "hw-ops.h" #include "ar9003_phy.h" +#include "ar9003_rtt.h" -#define MAX_MEASUREMENT 8 +#define MAX_MEASUREMENT MAX_IQCAL_MEASUREMENT #define MAX_MAG_DELTA 11 #define MAX_PHS_DELTA 10 @@ -615,11 +616,10 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement, { int mp_max = -64, max_idx = 0; int mp_min = 63, min_idx = 0; - int mp_avg = 0, i, outlier_idx = 0; + int mp_avg = 0, i, outlier_idx = 0, mp_count = 0; /* find min/max mismatch across all calibrated gains */ for (i = 0; i < nmeasurement; i++) { - mp_avg += mp_coeff[i]; if (mp_coeff[i] > mp_max) { mp_max = mp_coeff[i]; max_idx = i; @@ -632,10 +632,20 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement, /* find average (exclude max abs value) */ for (i = 0; i < nmeasurement; i++) { if ((abs(mp_coeff[i]) < abs(mp_max)) || - (abs(mp_coeff[i]) < abs(mp_min))) + (abs(mp_coeff[i]) < abs(mp_min))) { mp_avg += mp_coeff[i]; + mp_count++; + } } - mp_avg /= (nmeasurement - 1); + + /* + * finding mean magnitude/phase if possible, otherwise + * just use the last value as the mean + */ + if (mp_count) + mp_avg /= mp_count; + else + mp_avg = mp_coeff[nmeasurement - 1]; /* detect outlier */ if (abs(mp_max - mp_min) > max_delta) { @@ -643,16 +653,19 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement, outlier_idx = max_idx; else outlier_idx = min_idx; + + mp_coeff[outlier_idx] = mp_avg; } - mp_coeff[outlier_idx] = mp_avg; } static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah, u8 num_chains, - struct coeff *coeff) + struct coeff *coeff, + bool is_reusable) { int i, im, nmeasurement; u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS]; + struct ath9k_hw_cal_data *caldata = ah->caldata; memset(tx_corr_coeff, 0, sizeof(tx_corr_coeff)); for (i = 0; i < MAX_MEASUREMENT / 2; i++) { @@ -702,7 +715,13 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah, REG_RMW_FIELD(ah, tx_corr_coeff[im][i], AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE, coeff->iqc_coeff[0]); + + if (caldata) + caldata->tx_corr_coeff[im][i] = + coeff->iqc_coeff[0]; } + if (caldata) + caldata->num_measures[i] = nmeasurement; } REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3, @@ -710,8 +729,10 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah, REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); - return; + if (caldata) + caldata->done_txiqcal_once = is_reusable; + return; } static bool ar9003_hw_tx_iq_cal_run(struct ath_hw *ah) @@ -738,7 +759,7 @@ 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) +static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable) { struct ath_common *common = ath9k_hw_common(ah); const u32 txiqcal_status[AR9300_MAX_CHAINS] = { @@ -827,7 +848,8 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah) coeff.phs_coeff[i][im] -= 128; } } - ar9003_hw_tx_iqcal_load_avg_2_passes(ah, num_chains, &coeff); + ar9003_hw_tx_iqcal_load_avg_2_passes(ah, num_chains, + &coeff, is_reusable); return; @@ -835,23 +857,129 @@ tx_iqcal_fail: ath_dbg(common, ATH_DBG_CALIBRATE, "Tx IQ Cal failed\n"); return; } + +static void ar9003_hw_tx_iq_cal_reload(struct ath_hw *ah) +{ + struct ath9k_hw_cal_data *caldata = ah->caldata; + u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS]; + int i, im; + + memset(tx_corr_coeff, 0, sizeof(tx_corr_coeff)); + for (i = 0; i < MAX_MEASUREMENT / 2; i++) { + tx_corr_coeff[i * 2][0] = tx_corr_coeff[(i * 2) + 1][0] = + AR_PHY_TX_IQCAL_CORR_COEFF_B0(i); + if (!AR_SREV_9485(ah)) { + tx_corr_coeff[i * 2][1] = + tx_corr_coeff[(i * 2) + 1][1] = + AR_PHY_TX_IQCAL_CORR_COEFF_B1(i); + + tx_corr_coeff[i * 2][2] = + tx_corr_coeff[(i * 2) + 1][2] = + AR_PHY_TX_IQCAL_CORR_COEFF_B2(i); + } + } + + for (i = 0; i < AR9300_MAX_CHAINS; i++) { + if (!(ah->txchainmask & (1 << i))) + continue; + + for (im = 0; im < caldata->num_measures[i]; im++) { + if ((im % 2) == 0) + REG_RMW_FIELD(ah, tx_corr_coeff[im][i], + AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE, + caldata->tx_corr_coeff[im][i]); + else + REG_RMW_FIELD(ah, tx_corr_coeff[im][i], + AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE, + caldata->tx_corr_coeff[im][i]); + } + } + + REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3, + AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1); + REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0, + AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); +} + +static bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan) +{ + struct ath9k_rtt_hist *hist; + u32 *table; + int i; + bool restore; + + if (!(ah->caps.hw_caps & ATH9K_HW_CAP_RTT) || !ah->caldata) + return false; + + hist = &ah->caldata->rtt_hist; + ar9003_hw_rtt_enable(ah); + ar9003_hw_rtt_set_mask(ah, 0x10); + for (i = 0; i < AR9300_MAX_CHAINS; i++) { + if (!(ah->rxchainmask & (1 << i))) + continue; + table = &hist->table[i][hist->num_readings][0]; + ar9003_hw_rtt_load_hist(ah, i, table); + } + restore = ar9003_hw_rtt_force_restore(ah); + ar9003_hw_rtt_disable(ah); + + return restore; +} + static bool ar9003_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); - struct ath9k_hw_capabilities *pCap = &ah->caps; - int val; - bool txiqcal_done = false; + struct ath9k_hw_cal_data *caldata = ah->caldata; + bool txiqcal_done = false, txclcal_done = false; + bool is_reusable = true, status = true; + bool run_rtt_cal = false, run_agc_cal; + bool rtt = !!(ah->caps.hw_caps & ATH9K_HW_CAP_RTT); + u32 agc_ctrl = 0, agc_supp_cals = AR_PHY_AGC_CONTROL_OFFSET_CAL | + AR_PHY_AGC_CONTROL_FLTR_CAL | + AR_PHY_AGC_CONTROL_PKDET_CAL; + int i, j; + u32 cl_idx[AR9300_MAX_CHAINS] = { AR_PHY_CL_TAB_0, + AR_PHY_CL_TAB_1, + AR_PHY_CL_TAB_2 }; + + if (rtt) { + if (!ar9003_hw_rtt_restore(ah, chan)) + run_rtt_cal = true; + + ath_dbg(common, ATH_DBG_CALIBRATE, "RTT restore %s\n", + run_rtt_cal ? "failed" : "succeed"); + } + run_agc_cal = run_rtt_cal; - val = REG_READ(ah, AR_ENT_OTP); - ath_dbg(common, ATH_DBG_CALIBRATE, "ath9k: AR_ENT_OTP 0x%x\n", val); + if (run_rtt_cal) { + ar9003_hw_rtt_enable(ah); + ar9003_hw_rtt_set_mask(ah, 0x00); + ar9003_hw_rtt_clear_hist(ah); + } - /* Configure rx/tx chains before running AGC/TxiQ cals */ - if (val & AR_ENT_OTP_CHAIN2_DISABLE) - ar9003_hw_set_chain_masks(ah, 0x3, 0x3); - else - ar9003_hw_set_chain_masks(ah, pCap->rx_chainmask, - pCap->tx_chainmask); + if (rtt && !run_rtt_cal) { + agc_ctrl = REG_READ(ah, AR_PHY_AGC_CONTROL); + agc_supp_cals &= agc_ctrl; + agc_ctrl &= ~(AR_PHY_AGC_CONTROL_OFFSET_CAL | + AR_PHY_AGC_CONTROL_FLTR_CAL | + AR_PHY_AGC_CONTROL_PKDET_CAL); + REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl); + } + + if (ah->enabled_cals & TX_CL_CAL) { + if (caldata && caldata->done_txclcal_once) + REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, + AR_PHY_CL_CAL_ENABLE); + else { + REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, + AR_PHY_CL_CAL_ENABLE); + run_agc_cal = true; + } + } + + if (!(ah->enabled_cals & TX_IQ_CAL)) + goto skip_tx_iqcal; /* Do Tx IQ Calibration */ REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1, @@ -862,34 +990,98 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, * For AR9485 or later chips, TxIQ cal runs as part of * AGC calibration */ - if (AR_SREV_9485_OR_LATER(ah)) - txiqcal_done = true; - else { - txiqcal_done = ar9003_hw_tx_iq_cal_run(ah); - REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); - udelay(5); - REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); - } - - /* 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 */ - if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, - 0, AH_WAIT_TIMEOUT)) { + if (ah->enabled_cals & TX_IQ_ON_AGC_CAL) { + if (caldata && !caldata->done_txiqcal_once) + REG_SET_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, + AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); + else + REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, + AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); + txiqcal_done = run_agc_cal = true; + goto skip_tx_iqcal; + } else if (caldata && !caldata->done_txiqcal_once) + run_agc_cal = true; + + txiqcal_done = ar9003_hw_tx_iq_cal_run(ah); + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); + udelay(5); + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + +skip_tx_iqcal: + if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) { + /* 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); + } + if (rtt && !run_rtt_cal) { + agc_ctrl |= agc_supp_cals; + REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl); + } + + if (!status) { + if (run_rtt_cal) + ar9003_hw_rtt_disable(ah); + ath_dbg(common, ATH_DBG_CALIBRATE, - "offset calibration failed to complete in 1ms; noisy environment?\n"); + "offset calibration failed to complete in 1ms;" + "noisy environment?\n"); return false; } if (txiqcal_done) - ar9003_hw_tx_iq_cal_post_proc(ah); + ar9003_hw_tx_iq_cal_post_proc(ah, is_reusable); + else if (caldata && caldata->done_txiqcal_once) + ar9003_hw_tx_iq_cal_reload(ah); + +#define CL_TAB_ENTRY(reg_base) (reg_base + (4 * j)) + if (caldata && (ah->enabled_cals & TX_CL_CAL)) { + txclcal_done = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & + AR_PHY_AGC_CONTROL_CLC_SUCCESS); + if (caldata->done_txclcal_once) { + for (i = 0; i < AR9300_MAX_CHAINS; i++) { + if (!(ah->txchainmask & (1 << i))) + continue; + for (j = 0; j < MAX_CL_TAB_ENTRY; j++) + REG_WRITE(ah, CL_TAB_ENTRY(cl_idx[i]), + caldata->tx_clcal[i][j]); + } + } else if (is_reusable && txclcal_done) { + for (i = 0; i < AR9300_MAX_CHAINS; i++) { + if (!(ah->txchainmask & (1 << i))) + continue; + for (j = 0; j < MAX_CL_TAB_ENTRY; j++) + caldata->tx_clcal[i][j] = + REG_READ(ah, + CL_TAB_ENTRY(cl_idx[i])); + } + caldata->done_txclcal_once = true; + } + } +#undef CL_TAB_ENTRY + + if (run_rtt_cal && caldata) { + struct ath9k_rtt_hist *hist = &caldata->rtt_hist; + if (is_reusable && (hist->num_readings < RTT_HIST_MAX)) { + u32 *table; + + for (i = 0; i < AR9300_MAX_CHAINS; i++) { + if (!(ah->rxchainmask & (1 << i))) + continue; + table = &hist->table[i][hist->num_readings][0]; + ar9003_hw_rtt_fill_hist(ah, i, table); + } + } - /* Revert chainmasks to their original values before NF cal */ - ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask); + ar9003_hw_rtt_disable(ah); + } + ath9k_hw_loadnf(ah, chan); ath9k_hw_start_nfcal(ah, true); /* Initialize list pointers */ @@ -916,8 +1108,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, if (ah->cal_list_curr) ath9k_hw_reset_calibration(ah, ah->cal_list_curr); - if (ah->caldata) - ah->caldata->CalValid = 0; + if (caldata) + caldata->CalValid = 0; return true; } |