diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/ar9003_calib.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ar9003_calib.c | 252 |
1 files changed, 242 insertions, 10 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index aa0127265746..8c145cd98c1c 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -326,6 +326,224 @@ static void ar9003_hw_init_cal_settings(struct ath_hw *ah) ah->supp_cals = IQ_MISMATCH_CAL; } +#define OFF_UPPER_LT 24 +#define OFF_LOWER_LT 7 + +static bool ar9003_hw_dynamic_osdac_selection(struct ath_hw *ah, + bool txiqcal_done) +{ + struct ath_common *common = ath9k_hw_common(ah); + int ch0_done, osdac_ch0, dc_off_ch0_i1, dc_off_ch0_q1, dc_off_ch0_i2, + dc_off_ch0_q2, dc_off_ch0_i3, dc_off_ch0_q3; + int ch1_done, osdac_ch1, dc_off_ch1_i1, dc_off_ch1_q1, dc_off_ch1_i2, + dc_off_ch1_q2, dc_off_ch1_i3, dc_off_ch1_q3; + int ch2_done, osdac_ch2, dc_off_ch2_i1, dc_off_ch2_q1, dc_off_ch2_i2, + dc_off_ch2_q2, dc_off_ch2_i3, dc_off_ch2_q3; + bool status; + u32 temp, val; + + /* + * Clear offset and IQ calibration, run AGC cal. + */ + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_OFFSET_CAL); + REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, + AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); + 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, + "AGC cal without offset cal failed to complete in 1ms"); + return false; + } + + /* + * Allow only offset calibration and disable the others + * (Carrier Leak calibration, TX Filter calibration and + * Peak Detector offset calibration). + */ + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_OFFSET_CAL); + REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, + AR_PHY_CL_CAL_ENABLE); + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_FLTR_CAL); + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_PKDET_CAL); + + ch0_done = 0; + ch1_done = 0; + ch2_done = 0; + + while ((ch0_done == 0) || (ch1_done == 0) || (ch2_done == 0)) { + osdac_ch0 = (REG_READ(ah, AR_PHY_65NM_CH0_BB1) >> 30) & 0x3; + osdac_ch1 = (REG_READ(ah, AR_PHY_65NM_CH1_BB1) >> 30) & 0x3; + osdac_ch2 = (REG_READ(ah, AR_PHY_65NM_CH2_BB1) >> 30) & 0x3; + + REG_SET_BIT(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + + 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, + "DC offset cal failed to complete in 1ms"); + return false; + } + + REG_CLR_BIT(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + + /* + * High gain. + */ + REG_WRITE(ah, AR_PHY_65NM_CH0_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH0_BB3) & 0xfffffcff) | (1 << 8))); + REG_WRITE(ah, AR_PHY_65NM_CH1_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH1_BB3) & 0xfffffcff) | (1 << 8))); + REG_WRITE(ah, AR_PHY_65NM_CH2_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH2_BB3) & 0xfffffcff) | (1 << 8))); + + temp = REG_READ(ah, AR_PHY_65NM_CH0_BB3); + dc_off_ch0_i1 = (temp >> 26) & 0x1f; + dc_off_ch0_q1 = (temp >> 21) & 0x1f; + + temp = REG_READ(ah, AR_PHY_65NM_CH1_BB3); + dc_off_ch1_i1 = (temp >> 26) & 0x1f; + dc_off_ch1_q1 = (temp >> 21) & 0x1f; + + temp = REG_READ(ah, AR_PHY_65NM_CH2_BB3); + dc_off_ch2_i1 = (temp >> 26) & 0x1f; + dc_off_ch2_q1 = (temp >> 21) & 0x1f; + + /* + * Low gain. + */ + REG_WRITE(ah, AR_PHY_65NM_CH0_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH0_BB3) & 0xfffffcff) | (2 << 8))); + REG_WRITE(ah, AR_PHY_65NM_CH1_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH1_BB3) & 0xfffffcff) | (2 << 8))); + REG_WRITE(ah, AR_PHY_65NM_CH2_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH2_BB3) & 0xfffffcff) | (2 << 8))); + + temp = REG_READ(ah, AR_PHY_65NM_CH0_BB3); + dc_off_ch0_i2 = (temp >> 26) & 0x1f; + dc_off_ch0_q2 = (temp >> 21) & 0x1f; + + temp = REG_READ(ah, AR_PHY_65NM_CH1_BB3); + dc_off_ch1_i2 = (temp >> 26) & 0x1f; + dc_off_ch1_q2 = (temp >> 21) & 0x1f; + + temp = REG_READ(ah, AR_PHY_65NM_CH2_BB3); + dc_off_ch2_i2 = (temp >> 26) & 0x1f; + dc_off_ch2_q2 = (temp >> 21) & 0x1f; + + /* + * Loopback. + */ + REG_WRITE(ah, AR_PHY_65NM_CH0_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH0_BB3) & 0xfffffcff) | (3 << 8))); + REG_WRITE(ah, AR_PHY_65NM_CH1_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH1_BB3) & 0xfffffcff) | (3 << 8))); + REG_WRITE(ah, AR_PHY_65NM_CH2_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH2_BB3) & 0xfffffcff) | (3 << 8))); + + temp = REG_READ(ah, AR_PHY_65NM_CH0_BB3); + dc_off_ch0_i3 = (temp >> 26) & 0x1f; + dc_off_ch0_q3 = (temp >> 21) & 0x1f; + + temp = REG_READ(ah, AR_PHY_65NM_CH1_BB3); + dc_off_ch1_i3 = (temp >> 26) & 0x1f; + dc_off_ch1_q3 = (temp >> 21) & 0x1f; + + temp = REG_READ(ah, AR_PHY_65NM_CH2_BB3); + dc_off_ch2_i3 = (temp >> 26) & 0x1f; + dc_off_ch2_q3 = (temp >> 21) & 0x1f; + + if ((dc_off_ch0_i1 > OFF_UPPER_LT) || (dc_off_ch0_i1 < OFF_LOWER_LT) || + (dc_off_ch0_i2 > OFF_UPPER_LT) || (dc_off_ch0_i2 < OFF_LOWER_LT) || + (dc_off_ch0_i3 > OFF_UPPER_LT) || (dc_off_ch0_i3 < OFF_LOWER_LT) || + (dc_off_ch0_q1 > OFF_UPPER_LT) || (dc_off_ch0_q1 < OFF_LOWER_LT) || + (dc_off_ch0_q2 > OFF_UPPER_LT) || (dc_off_ch0_q2 < OFF_LOWER_LT) || + (dc_off_ch0_q3 > OFF_UPPER_LT) || (dc_off_ch0_q3 < OFF_LOWER_LT)) { + if (osdac_ch0 == 3) { + ch0_done = 1; + } else { + osdac_ch0++; + + val = REG_READ(ah, AR_PHY_65NM_CH0_BB1) & 0x3fffffff; + val |= (osdac_ch0 << 30); + REG_WRITE(ah, AR_PHY_65NM_CH0_BB1, val); + + ch0_done = 0; + } + } else { + ch0_done = 1; + } + + if ((dc_off_ch1_i1 > OFF_UPPER_LT) || (dc_off_ch1_i1 < OFF_LOWER_LT) || + (dc_off_ch1_i2 > OFF_UPPER_LT) || (dc_off_ch1_i2 < OFF_LOWER_LT) || + (dc_off_ch1_i3 > OFF_UPPER_LT) || (dc_off_ch1_i3 < OFF_LOWER_LT) || + (dc_off_ch1_q1 > OFF_UPPER_LT) || (dc_off_ch1_q1 < OFF_LOWER_LT) || + (dc_off_ch1_q2 > OFF_UPPER_LT) || (dc_off_ch1_q2 < OFF_LOWER_LT) || + (dc_off_ch1_q3 > OFF_UPPER_LT) || (dc_off_ch1_q3 < OFF_LOWER_LT)) { + if (osdac_ch1 == 3) { + ch1_done = 1; + } else { + osdac_ch1++; + + val = REG_READ(ah, AR_PHY_65NM_CH1_BB1) & 0x3fffffff; + val |= (osdac_ch1 << 30); + REG_WRITE(ah, AR_PHY_65NM_CH1_BB1, val); + + ch1_done = 0; + } + } else { + ch1_done = 1; + } + + if ((dc_off_ch2_i1 > OFF_UPPER_LT) || (dc_off_ch2_i1 < OFF_LOWER_LT) || + (dc_off_ch2_i2 > OFF_UPPER_LT) || (dc_off_ch2_i2 < OFF_LOWER_LT) || + (dc_off_ch2_i3 > OFF_UPPER_LT) || (dc_off_ch2_i3 < OFF_LOWER_LT) || + (dc_off_ch2_q1 > OFF_UPPER_LT) || (dc_off_ch2_q1 < OFF_LOWER_LT) || + (dc_off_ch2_q2 > OFF_UPPER_LT) || (dc_off_ch2_q2 < OFF_LOWER_LT) || + (dc_off_ch2_q3 > OFF_UPPER_LT) || (dc_off_ch2_q3 < OFF_LOWER_LT)) { + if (osdac_ch2 == 3) { + ch2_done = 1; + } else { + osdac_ch2++; + + val = REG_READ(ah, AR_PHY_65NM_CH2_BB1) & 0x3fffffff; + val |= (osdac_ch2 << 30); + REG_WRITE(ah, AR_PHY_65NM_CH2_BB1, val); + + ch2_done = 0; + } + } else { + ch2_done = 1; + } + } + + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_OFFSET_CAL); + REG_SET_BIT(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + + /* + * We don't need to check txiqcal_done here since it is always + * set for AR9550. + */ + REG_SET_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, + AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); + + return true; +} + /* * solve 4x4 linear equation used in loopback iq cal. */ @@ -898,7 +1116,7 @@ static void ar9003_hw_tx_iq_cal_reload(struct ath_hw *ah) static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g) { - int offset[8], total = 0, test; + int offset[8] = {0}, total = 0, test; int agc_out, i; REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), @@ -923,12 +1141,18 @@ static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g) AR_PHY_65NM_RXRF_AGC_AGC_ON_OVR, 0x1); REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0x1); - if (is_2g) - REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), - AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR, 0x0); - else + + if (AR_SREV_9330_11(ah)) { REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), - AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR, 0x0); + AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, 0x0); + } else { + if (is_2g) + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR, 0x0); + else + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR, 0x0); + } for (i = 6; i > 0; i--) { offset[i] = BIT(i - 1); @@ -964,9 +1188,9 @@ static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g) AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0); } -static void ar9003_hw_do_manual_peak_cal(struct ath_hw *ah, - struct ath9k_channel *chan, - bool run_rtt_cal) +static void ar9003_hw_do_pcoem_manual_peak_cal(struct ath_hw *ah, + struct ath9k_channel *chan, + bool run_rtt_cal) { struct ath9k_hw_cal_data *caldata = ah->caldata; int i; @@ -1145,7 +1369,7 @@ skip_tx_iqcal: AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT); - ar9003_hw_do_manual_peak_cal(ah, chan, run_rtt_cal); + ar9003_hw_do_pcoem_manual_peak_cal(ah, chan, run_rtt_cal); } if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) { @@ -1265,8 +1489,16 @@ static bool ar9003_hw_init_cal_soc(struct ath_hw *ah, REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); } + if (AR_SREV_9550(ah) && IS_CHAN_2GHZ(chan)) { + if (!ar9003_hw_dynamic_osdac_selection(ah, txiqcal_done)) + return false; + } + skip_tx_iqcal: if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) { + 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) | |