ath9k_hw: Add IQ cal changes for AR9485
authorVasanthakumar Thiagarajan <vasanth@atheros.com>
Mon, 6 Dec 2010 12:27:56 +0000 (04:27 -0800)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 7 Dec 2010 21:54:14 +0000 (16:54 -0500)
Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/ar9003_calib.c
drivers/net/wireless/ath/ath9k/ar9003_phy.h

index e8f7df8..7c3334b 100644 (file)
 #include "hw-ops.h"
 #include "ar9003_phy.h"
 
+#define MPASS  3
+#define MAX_MEASUREMENT        8
+#define MAX_DIFFERENCE 10
+
+struct coeff {
+       int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MPASS];
+       int phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MPASS];
+       int iqc_coeff[2];
+};
+
 enum ar9003_cal_types {
        IQ_MISMATCH_CAL = BIT(0),
        TEMP_COMP_CAL = BIT(1),
@@ -712,6 +722,229 @@ TX_IQ_CAL_FAILED:
        ath_dbg(common, ATH_DBG_CALIBRATE, "Tx IQ Cal failed\n");
 }
 
+static bool ar9003_hw_compute_closest_pass_and_avg(int *mp_coeff, int *mp_avg)
+{
+       int diff[MPASS];
+
+       diff[0] = abs(mp_coeff[0] - mp_coeff[1]);
+       diff[1] = abs(mp_coeff[1] - mp_coeff[2]);
+       diff[2] = abs(mp_coeff[2] - mp_coeff[0]);
+
+       if (diff[0] > MAX_MEASUREMENT &&
+           diff[1] > MAX_MEASUREMENT &&
+           diff[2] > MAX_MEASUREMENT)
+               return false;
+
+       if (diff[0] <= diff[1] && diff[0] <= diff[2])
+               *mp_avg = (mp_coeff[0] + mp_coeff[1]) / 2;
+       else if (diff[1] <= diff[2])
+               *mp_avg = (mp_coeff[1] + mp_coeff[2]) / 2;
+       else
+               *mp_avg = (mp_coeff[2] + mp_coeff[0]) / 2;
+
+       return true;
+}
+
+static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
+                                                u8 num_chains,
+                                                struct coeff *coeff)
+{
+       struct ath_common *common = ath9k_hw_common(ah);
+       int i, im, nmeasurement;
+       int magnitude, phase;
+       u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS];
+
+       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);
+               }
+       }
+
+       /* Load the average of 2 passes */
+       for (i = 0; i < num_chains; i++) {
+               if (AR_SREV_9485(ah))
+                       nmeasurement = REG_READ_FIELD(ah,
+                                       AR_PHY_TX_IQCAL_STATUS_B0_9485,
+                                       AR_PHY_CALIBRATED_GAINS_0);
+               else
+                       nmeasurement = REG_READ_FIELD(ah,
+                                       AR_PHY_TX_IQCAL_STATUS_B0,
+                                       AR_PHY_CALIBRATED_GAINS_0);
+
+               if (nmeasurement > MAX_MEASUREMENT)
+                       nmeasurement = MAX_MEASUREMENT;
+
+               for (im = 0; im < nmeasurement; im++) {
+                       /*
+                        * Determine which 2 passes are closest and compute avg
+                        * magnitude
+                        */
+                       if (!ar9003_hw_compute_closest_pass_and_avg(coeff->mag_coeff[i][im],
+                                                                   &magnitude))
+                               goto disable_txiqcal;
+
+                       /*
+                        * Determine which 2 passes are closest and compute avg
+                        * phase
+                        */
+                       if (!ar9003_hw_compute_closest_pass_and_avg(coeff->phs_coeff[i][im],
+                                                                   &phase))
+                               goto disable_txiqcal;
+
+                       coeff->iqc_coeff[0] = (magnitude & 0x7f) |
+                                             ((phase & 0x7f) << 7);
+
+                       if ((im % 2) == 0)
+                               REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
+                                       AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE,
+                                       coeff->iqc_coeff[0]);
+                       else
+                               REG_RMW_FIELD(ah, tx_corr_coeff[im][i],
+                                       AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE,
+                                       coeff->iqc_coeff[0]);
+               }
+       }
+
+       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);
+
+       return;
+
+disable_txiqcal:
+       REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3,
+                     AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x0);
+       REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0,
+                     AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x0);
+
+       ath_dbg(common, ATH_DBG_CALIBRATE, "TX IQ Cal disabled\n");
+}
+
+static void ar9003_hw_tx_iq_cal_run(struct ath_hw *ah)
+{
+       u8 tx_gain_forced;
+
+       REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1_9485,
+                     AR_PHY_TX_IQCAQL_CONTROL_1_IQCORR_I_Q_COFF_DELPT, DELPT);
+       tx_gain_forced = REG_READ_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
+                                       AR_PHY_TXGAIN_FORCE);
+       if (tx_gain_forced)
+               REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN,
+                             AR_PHY_TXGAIN_FORCE, 0);
+
+       REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_START_9485,
+                     AR_PHY_TX_IQCAL_START_DO_CAL_9485, 1);
+}
+
+static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah)
+{
+       struct ath_common *common = ath9k_hw_common(ah);
+       const u32 txiqcal_status[AR9300_MAX_CHAINS] = {
+               AR_PHY_TX_IQCAL_STATUS_B0_9485,
+               AR_PHY_TX_IQCAL_STATUS_B1,
+               AR_PHY_TX_IQCAL_STATUS_B2,
+       };
+       const u_int32_t chan_info_tab[] = {
+               AR_PHY_CHAN_INFO_TAB_0,
+               AR_PHY_CHAN_INFO_TAB_1,
+               AR_PHY_CHAN_INFO_TAB_2,
+       };
+       struct coeff coeff;
+       s32 iq_res[6];
+       u8 num_chains = 0;
+       int i, ip, im, j;
+       int nmeasurement;
+
+       for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+               if (ah->txchainmask & (1 << i))
+                       num_chains++;
+       }
+
+       for (ip = 0; ip < MPASS; ip++) {
+               for (i = 0; i < num_chains; i++) {
+                       nmeasurement = REG_READ_FIELD(ah,
+                                       AR_PHY_TX_IQCAL_STATUS_B0_9485,
+                                       AR_PHY_CALIBRATED_GAINS_0);
+                       if (nmeasurement > MAX_MEASUREMENT)
+                               nmeasurement = MAX_MEASUREMENT;
+
+                       for (im = 0; im < nmeasurement; im++) {
+                               ath_dbg(common, ATH_DBG_CALIBRATE,
+                                       "Doing Tx IQ Cal for chain %d.\n", i);
+
+                               if (REG_READ(ah, txiqcal_status[i]) &
+                                   AR_PHY_TX_IQCAL_STATUS_FAILED) {
+                                       ath_dbg(common, ATH_DBG_CALIBRATE,
+                                       "Tx IQ Cal failed for chain %d.\n", i);
+                                       goto tx_iqcal_fail;
+                               }
+
+                               for (j = 0; j < 3; j++) {
+                                       u32 idx = 2 * j, offset = 4 * (3 * im + j);
+
+                                       REG_RMW_FIELD(ah,
+                                               AR_PHY_CHAN_INFO_MEMORY,
+                                               AR_PHY_CHAN_INFO_TAB_S2_READ,
+                                               0);
+
+                                       /* 32 bits */
+                                       iq_res[idx] = REG_READ(ah,
+                                                       chan_info_tab[i] +
+                                                       offset);
+
+                                       REG_RMW_FIELD(ah,
+                                               AR_PHY_CHAN_INFO_MEMORY,
+                                               AR_PHY_CHAN_INFO_TAB_S2_READ,
+                                               1);
+
+                                       /* 16 bits */
+                                       iq_res[idx + 1] = 0xffff & REG_READ(ah,
+                                                         chan_info_tab[i] + offset);
+
+                                       ath_dbg(common, ATH_DBG_CALIBRATE,
+                                               "IQ RES[%d]=0x%x"
+                                               "IQ_RES[%d]=0x%x\n",
+                                               idx, iq_res[idx], idx + 1,
+                                               iq_res[idx + 1]);
+                               }
+
+                               if (!ar9003_hw_calc_iq_corr(ah, i, iq_res,
+                                                           coeff.iqc_coeff)) {
+                                       ath_dbg(common, ATH_DBG_CALIBRATE,
+                                        "Failed in calculation of IQ correction.\n");
+                                       goto tx_iqcal_fail;
+                               }
+
+                               coeff.mag_coeff[i][im][ip] =
+                                               coeff.iqc_coeff[0] & 0x7f;
+                               coeff.phs_coeff[i][im][ip] =
+                                               (coeff.iqc_coeff[0] >> 7) & 0x7f;
+
+                               if (coeff.mag_coeff[i][im][ip] > 63)
+                                       coeff.mag_coeff[i][im][ip] -= 128;
+                               if (coeff.phs_coeff[i][im][ip] > 63)
+                                       coeff.phs_coeff[i][im][ip] -= 128;
+                       }
+               }
+       }
+       ar9003_hw_tx_iqcal_load_avg_2_passes(ah, num_chains, &coeff);
+
+       return;
+
+tx_iqcal_fail:
+       ath_dbg(common, ATH_DBG_CALIBRATE, "Tx IQ Cal failed\n");
+       return;
+}
 static bool ar9003_hw_init_cal(struct ath_hw *ah,
                               struct ath9k_channel *chan)
 {
@@ -733,7 +966,11 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
                ar9003_hw_set_chain_masks(ah, 0x7, 0x7);
 
        /* Do Tx IQ Calibration */
-       ar9003_hw_tx_iq_cal(ah);
+       if (AR_SREV_9485(ah))
+               ar9003_hw_tx_iq_cal_run(ah);
+       else
+               ar9003_hw_tx_iq_cal(ah);
+
        REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
        udelay(5);
        REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
@@ -751,6 +988,9 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
                return false;
        }
 
+       if (AR_SREV_9485(ah))
+               ar9003_hw_tx_iq_cal_post_proc(ah);
+
        /* Revert chainmasks to their original values before NF cal */
        ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
 
index abb4ec1..74e0da9 100644 (file)
 
 #define AR_PHY_TXGAIN_TABLE      (AR_SM_BASE + 0x300)
 
+#define AR_PHY_TX_IQCAL_START_9485             (AR_SM_BASE + 0x3c4)
+#define AR_PHY_TX_IQCAL_START_DO_CAL_9485      0x80000000
+#define AR_PHY_TX_IQCAL_START_DO_CAL_9485_S    31
+#define AR_PHY_TX_IQCAL_CONTROL_1_9485         (AR_SM_BASE + 0x3c8)
+#define AR_PHY_TX_IQCAL_STATUS_B0_9485         (AR_SM_BASE + 0x3f0)
+
 #define AR_PHY_TX_IQCAL_CONTROL_1   (AR_SM_BASE + 0x448)
 #define AR_PHY_TX_IQCAL_START       (AR_SM_BASE + 0x440)
 #define AR_PHY_TX_IQCAL_STATUS_B0   (AR_SM_BASE + 0x48c)
 #define AR_PHY_TPCGR1_FORCED_DAC_GAIN_S 1
 #define AR_PHY_TPCGR1_FORCE_DAC_GAIN    0x00000001
 #define AR_PHY_TXGAIN_FORCE               0x00000001
+#define AR_PHY_TXGAIN_FORCE_S            0
 #define AR_PHY_TXGAIN_FORCED_PADVGNRA     0x00003c00
 #define AR_PHY_TXGAIN_FORCED_PADVGNRA_S   10
 #define AR_PHY_TXGAIN_FORCED_PADVGNRB     0x0003c000
 #define AR_PHY_TX_IQCAL_START_DO_CAL_S      0
 
 #define AR_PHY_TX_IQCAL_STATUS_FAILED    0x00000001
-#define AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE      0x00003fff
-#define AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE_S    0
+#define AR_PHY_CALIBRATED_GAINS_0       0x3e
+#define AR_PHY_CALIBRATED_GAINS_0_S     1
+
+#define AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE      0x00003fff
+#define AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE_S    0
+#define AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE      0x0fffc000
+#define AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE_S    14
 
 #define AR_PHY_65NM_CH0_RXTX4_THERM_ON          0x10000000
 #define AR_PHY_65NM_CH0_RXTX4_THERM_ON_S        28