]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/net/wireless/ath/ath9k/ar9003_calib.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / net / wireless / ath / ath9k / ar9003_calib.c
index 9e6edffe0bd125544bce11b4d1dbcea470173add..4a4cd88429c069076945e3d0c13807c8d83b2729 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),
@@ -40,8 +50,8 @@ static void ar9003_hw_setup_calibration(struct ath_hw *ah,
                currCal->calData->calCountMax);
                REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ);
 
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "starting IQ Mismatch Calibration\n");
+               ath_dbg(common, ATH_DBG_CALIBRATE,
+                       "starting IQ Mismatch Calibration\n");
 
                /* Kick-off cal */
                REG_SET_BIT(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL);
@@ -52,8 +62,8 @@ static void ar9003_hw_setup_calibration(struct ath_hw *ah,
                REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM,
                              AR_PHY_65NM_CH0_THERM_START, 1);
 
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "starting Temperature Compensation Calibration\n");
+               ath_dbg(common, ATH_DBG_CALIBRATE,
+                       "starting Temperature Compensation Calibration\n");
                break;
        }
 }
@@ -181,11 +191,11 @@ static void ar9003_hw_iqcal_collect(struct ath_hw *ah)
                        REG_READ(ah, AR_PHY_CAL_MEAS_1(i));
                ah->totalIqCorrMeas[i] +=
                        (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i));
-               ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE,
-                         "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n",
-                         ah->cal_samples, i, ah->totalPowerMeasI[i],
-                         ah->totalPowerMeasQ[i],
-                         ah->totalIqCorrMeas[i]);
+               ath_dbg(ath9k_hw_common(ah), ATH_DBG_CALIBRATE,
+                       "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n",
+                       ah->cal_samples, i, ah->totalPowerMeasI[i],
+                       ah->totalPowerMeasQ[i],
+                       ah->totalIqCorrMeas[i]);
        }
 }
 
@@ -196,7 +206,7 @@ static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
        u32 qCoffDenom, iCoffDenom;
        int32_t qCoff, iCoff;
        int iqCorrNeg, i;
-       const u_int32_t offset_array[3] = {
+       static const u_int32_t offset_array[3] = {
                AR_PHY_RX_IQCAL_CORR_B0,
                AR_PHY_RX_IQCAL_CORR_B1,
                AR_PHY_RX_IQCAL_CORR_B2,
@@ -207,13 +217,13 @@ static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
                powerMeasQ = ah->totalPowerMeasQ[i];
                iqCorrMeas = ah->totalIqCorrMeas[i];
 
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "Starting IQ Cal and Correction for Chain %d\n",
-                         i);
+               ath_dbg(common, ATH_DBG_CALIBRATE,
+                       "Starting IQ Cal and Correction for Chain %d\n",
+                       i);
 
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "Orignal: Chn %diq_corr_meas = 0x%08x\n",
-                         i, ah->totalIqCorrMeas[i]);
+               ath_dbg(common, ATH_DBG_CALIBRATE,
+                       "Orignal: Chn %diq_corr_meas = 0x%08x\n",
+                       i, ah->totalIqCorrMeas[i]);
 
                iqCorrNeg = 0;
 
@@ -222,12 +232,12 @@ static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
                        iqCorrNeg = 1;
                }
 
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI);
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ);
-               ath_print(common, ATH_DBG_CALIBRATE, "iqCorrNeg is 0x%08x\n",
-                         iqCorrNeg);
+               ath_dbg(common, ATH_DBG_CALIBRATE,
+                       "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI);
+               ath_dbg(common, ATH_DBG_CALIBRATE,
+                       "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ);
+               ath_dbg(common, ATH_DBG_CALIBRATE, "iqCorrNeg is 0x%08x\n",
+                       iqCorrNeg);
 
                iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 256;
                qCoffDenom = powerMeasQ / 64;
@@ -235,10 +245,10 @@ static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
                if ((iCoffDenom != 0) && (qCoffDenom != 0)) {
                        iCoff = iqCorrMeas / iCoffDenom;
                        qCoff = powerMeasI / qCoffDenom - 64;
-                       ath_print(common, ATH_DBG_CALIBRATE,
-                                 "Chn %d iCoff = 0x%08x\n", i, iCoff);
-                       ath_print(common, ATH_DBG_CALIBRATE,
-                                 "Chn %d qCoff = 0x%08x\n", i, qCoff);
+                       ath_dbg(common, ATH_DBG_CALIBRATE,
+                               "Chn %d iCoff = 0x%08x\n", i, iCoff);
+                       ath_dbg(common, ATH_DBG_CALIBRATE,
+                               "Chn %d qCoff = 0x%08x\n", i, qCoff);
 
                        /* Force bounds on iCoff */
                        if (iCoff >= 63)
@@ -259,14 +269,13 @@ static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
                        iCoff = iCoff & 0x7f;
                        qCoff = qCoff & 0x7f;
 
-                       ath_print(common, ATH_DBG_CALIBRATE,
-                                 "Chn %d : iCoff = 0x%x  qCoff = 0x%x\n",
-                                 i, iCoff, qCoff);
-                       ath_print(common, ATH_DBG_CALIBRATE,
-                                 "Register offset (0x%04x) "
-                                 "before update = 0x%x\n",
-                                 offset_array[i],
-                                 REG_READ(ah, offset_array[i]));
+                       ath_dbg(common, ATH_DBG_CALIBRATE,
+                               "Chn %d : iCoff = 0x%x  qCoff = 0x%x\n",
+                               i, iCoff, qCoff);
+                       ath_dbg(common, ATH_DBG_CALIBRATE,
+                               "Register offset (0x%04x) before update = 0x%x\n",
+                               offset_array[i],
+                               REG_READ(ah, offset_array[i]));
 
                        REG_RMW_FIELD(ah, offset_array[i],
                                      AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF,
@@ -274,33 +283,29 @@ static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
                        REG_RMW_FIELD(ah, offset_array[i],
                                      AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF,
                                      qCoff);
-                       ath_print(common, ATH_DBG_CALIBRATE,
-                                 "Register offset (0x%04x) QI COFF "
-                                 "(bitfields 0x%08x) after update = 0x%x\n",
-                                 offset_array[i],
-                                 AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF,
-                                 REG_READ(ah, offset_array[i]));
-                       ath_print(common, ATH_DBG_CALIBRATE,
-                                 "Register offset (0x%04x) QQ COFF "
-                                 "(bitfields 0x%08x) after update = 0x%x\n",
-                                 offset_array[i],
-                                 AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF,
-                                 REG_READ(ah, offset_array[i]));
-
-                       ath_print(common, ATH_DBG_CALIBRATE,
-                                 "IQ Cal and Correction done for Chain %d\n",
-                                 i);
+                       ath_dbg(common, ATH_DBG_CALIBRATE,
+                               "Register offset (0x%04x) QI COFF (bitfields 0x%08x) after update = 0x%x\n",
+                               offset_array[i],
+                               AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF,
+                               REG_READ(ah, offset_array[i]));
+                       ath_dbg(common, ATH_DBG_CALIBRATE,
+                               "Register offset (0x%04x) QQ COFF (bitfields 0x%08x) after update = 0x%x\n",
+                               offset_array[i],
+                               AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF,
+                               REG_READ(ah, offset_array[i]));
+
+                       ath_dbg(common, ATH_DBG_CALIBRATE,
+                               "IQ Cal and Correction done for Chain %d\n", i);
                }
        }
 
        REG_SET_BIT(ah, AR_PHY_RX_IQCAL_CORR_B0,
                    AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE);
-       ath_print(common, ATH_DBG_CALIBRATE,
-                 "IQ Cal and Correction (offset 0x%04x) enabled "
-                 "(bit position 0x%08x). New Value 0x%08x\n",
-                 (unsigned) (AR_PHY_RX_IQCAL_CORR_B0),
-                 AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE,
-                 REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B0));
+       ath_dbg(common, ATH_DBG_CALIBRATE,
+               "IQ Cal and Correction (offset 0x%04x) enabled (bit position 0x%08x). New Value 0x%08x\n",
+               (unsigned) (AR_PHY_RX_IQCAL_CORR_B0),
+               AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE,
+               REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B0));
 }
 
 static const struct ath9k_percal_data iq_cal_single_sample = {
@@ -340,7 +345,7 @@ static bool ar9003_hw_solve_iq_cal(struct ath_hw *ah,
        f2 = (f1 * f1 + f3 * f3) / result_shift;
 
        if (!f2) {
-               ath_print(common, ATH_DBG_CALIBRATE, "Divide by 0\n");
+               ath_dbg(common, ATH_DBG_CALIBRATE, "Divide by 0\n");
                return false;
        }
 
@@ -461,11 +466,14 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
 
        if ((i2_p_q2_a0_d0 == 0) || (i2_p_q2_a0_d1 == 0) ||
            (i2_p_q2_a1_d0 == 0) || (i2_p_q2_a1_d1 == 0)) {
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "Divide by 0:\na0_d0=%d\n"
-                         "a0_d1=%d\na2_d0=%d\na1_d1=%d\n",
-                         i2_p_q2_a0_d0, i2_p_q2_a0_d1,
-                         i2_p_q2_a1_d0, i2_p_q2_a1_d1);
+               ath_dbg(common, ATH_DBG_CALIBRATE,
+                       "Divide by 0:\n"
+                       "a0_d0=%d\n"
+                       "a0_d1=%d\n"
+                       "a2_d0=%d\n"
+                       "a1_d1=%d\n",
+                       i2_p_q2_a0_d0, i2_p_q2_a0_d1,
+                       i2_p_q2_a1_d0, i2_p_q2_a1_d1);
                return false;
        }
 
@@ -498,9 +506,9 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
        mag2 = ar9003_hw_find_mag_approx(ah, cos_2phi_2, sin_2phi_2);
 
        if ((mag1 == 0) || (mag2 == 0)) {
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "Divide by 0: mag1=%d, mag2=%d\n",
-                         mag1, mag2);
+               ath_dbg(common, ATH_DBG_CALIBRATE,
+                       "Divide by 0: mag1=%d, mag2=%d\n",
+                       mag1, mag2);
                return false;
        }
 
@@ -517,8 +525,8 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
                             mag_a0_d0, phs_a0_d0,
                             mag_a1_d0,
                             phs_a1_d0, solved_eq)) {
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "Call to ar9003_hw_solve_iq_cal() failed.\n");
+               ath_dbg(common, ATH_DBG_CALIBRATE,
+                       "Call to ar9003_hw_solve_iq_cal() failed.\n");
                return false;
        }
 
@@ -527,14 +535,14 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
        mag_rx = solved_eq[2];
        phs_rx = solved_eq[3];
 
-       ath_print(common, ATH_DBG_CALIBRATE,
-                 "chain %d: mag mismatch=%d phase mismatch=%d\n",
-                 chain_idx, mag_tx/res_scale, phs_tx/res_scale);
+       ath_dbg(common, ATH_DBG_CALIBRATE,
+               "chain %d: mag mismatch=%d phase mismatch=%d\n",
+               chain_idx, mag_tx/res_scale, phs_tx/res_scale);
 
        if (res_scale == mag_tx) {
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "Divide by 0: mag_tx=%d, res_scale=%d\n",
-                         mag_tx, res_scale);
+               ath_dbg(common, ATH_DBG_CALIBRATE,
+                       "Divide by 0: mag_tx=%d, res_scale=%d\n",
+                       mag_tx, res_scale);
                return false;
        }
 
@@ -545,9 +553,9 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
        q_q_coff = (mag_corr_tx * 128 / res_scale);
        q_i_coff = (phs_corr_tx * 256 / res_scale);
 
-       ath_print(common, ATH_DBG_CALIBRATE,
-                 "tx chain %d: mag corr=%d  phase corr=%d\n",
-                 chain_idx, q_q_coff, q_i_coff);
+       ath_dbg(common, ATH_DBG_CALIBRATE,
+               "tx chain %d: mag corr=%d  phase corr=%d\n",
+               chain_idx, q_q_coff, q_i_coff);
 
        if (q_i_coff < -63)
                q_i_coff = -63;
@@ -560,14 +568,14 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
 
        iqc_coeff[0] = (q_q_coff * 128) + q_i_coff;
 
-       ath_print(common, ATH_DBG_CALIBRATE,
-                 "tx chain %d: iq corr coeff=%x\n",
-                 chain_idx, iqc_coeff[0]);
+       ath_dbg(common, ATH_DBG_CALIBRATE,
+               "tx chain %d: iq corr coeff=%x\n",
+               chain_idx, iqc_coeff[0]);
 
        if (-mag_rx == res_scale) {
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "Divide by 0: mag_rx=%d, res_scale=%d\n",
-                         mag_rx, res_scale);
+               ath_dbg(common, ATH_DBG_CALIBRATE,
+                       "Divide by 0: mag_rx=%d, res_scale=%d\n",
+                       mag_rx, res_scale);
                return false;
        }
 
@@ -578,9 +586,9 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
        q_q_coff = (mag_corr_rx * 128 / res_scale);
        q_i_coff = (phs_corr_rx * 256 / res_scale);
 
-       ath_print(common, ATH_DBG_CALIBRATE,
-                 "rx chain %d: mag corr=%d  phase corr=%d\n",
-                 chain_idx, q_q_coff, q_i_coff);
+       ath_dbg(common, ATH_DBG_CALIBRATE,
+               "rx chain %d: mag corr=%d  phase corr=%d\n",
+               chain_idx, q_q_coff, q_i_coff);
 
        if (q_i_coff < -63)
                q_i_coff = -63;
@@ -593,140 +601,367 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah,
 
        iqc_coeff[1] = (q_q_coff * 128) + q_i_coff;
 
-       ath_print(common, ATH_DBG_CALIBRATE,
-                 "rx chain %d: iq corr coeff=%x\n",
-                 chain_idx, iqc_coeff[1]);
+       ath_dbg(common, ATH_DBG_CALIBRATE,
+               "rx chain %d: iq corr coeff=%x\n",
+               chain_idx, iqc_coeff[1]);
+
+       return true;
+}
+
+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_DIFFERENCE &&
+           diff[1] > MAX_DIFFERENCE &&
+           diff[2] > MAX_DIFFERENCE)
+               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(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
-       const u32 txiqcal_status[AR9300_MAX_CHAINS] = {
+       static const u32 txiqcal_status[AR9300_MAX_CHAINS] = {
                AR_PHY_TX_IQCAL_STATUS_B0,
                AR_PHY_TX_IQCAL_STATUS_B1,
                AR_PHY_TX_IQCAL_STATUS_B2,
        };
-       const u32 tx_corr_coeff[AR9300_MAX_CHAINS] = {
-               AR_PHY_TX_IQCAL_CORR_COEFF_01_B0,
-               AR_PHY_TX_IQCAL_CORR_COEFF_01_B1,
-               AR_PHY_TX_IQCAL_CORR_COEFF_01_B2,
-       };
-       const u32 rx_corr[AR9300_MAX_CHAINS] = {
-               AR_PHY_RX_IQCAL_CORR_B0,
-               AR_PHY_RX_IQCAL_CORR_B1,
-               AR_PHY_RX_IQCAL_CORR_B2,
-       };
-       const u_int32_t chan_info_tab[] = {
+       static const u32 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];
-       s32 iqc_coeff[2];
-       s32 i, j;
-       u32 num_chains = 0;
+       s32 i, j, ip, im, nmeasurement;
+       u8 nchains = get_streams(common->tx_chainmask);
+
+       for (ip = 0; ip < MPASS; ip++) {
+               REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1,
+                             AR_PHY_TX_IQCAQL_CONTROL_1_IQCORR_I_Q_COFF_DELPT,
+                             DELPT);
+               REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_START,
+                             AR_PHY_TX_IQCAL_START_DO_CAL,
+                             AR_PHY_TX_IQCAL_START_DO_CAL);
+
+               if (!ath9k_hw_wait(ah, AR_PHY_TX_IQCAL_START,
+                                  AR_PHY_TX_IQCAL_START_DO_CAL,
+                                  0, AH_WAIT_TIMEOUT)) {
+                       ath_dbg(common, ATH_DBG_CALIBRATE,
+                               "Tx IQ Cal not complete.\n");
+                       goto TX_IQ_CAL_FAILED;
+               }
 
-       for (i = 0; i < AR9300_MAX_CHAINS; i++) {
-               if (ah->txchainmask & (1 << i))
-                       num_chains++;
-       }
+               nmeasurement = REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_STATUS_B0,
+                                             AR_PHY_CALIBRATED_GAINS_0);
+                       if (nmeasurement > MAX_MEASUREMENT)
+                               nmeasurement = MAX_MEASUREMENT;
+
+               for (i = 0; i < nchains; i++) {
+                       ath_dbg(common, ATH_DBG_CALIBRATE,
+                               "Doing Tx IQ Cal for chain %d.\n", i);
+                       for (im = 0; im < nmeasurement; im++) {
+                               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_IQ_CAL_FAILED;
+                               }
 
-       REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1,
-                     AR_PHY_TX_IQCAQL_CONTROL_1_IQCORR_I_Q_COFF_DELPT,
-                     DELPT);
-       REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_START,
-                     AR_PHY_TX_IQCAL_START_DO_CAL,
-                     AR_PHY_TX_IQCAL_START_DO_CAL);
+                               for (j = 0; j < 3; j++) {
+                                       u8 idx = 2 * j,
+                                          offset = 4 * (3 * im + j);
 
-       if (!ath9k_hw_wait(ah, AR_PHY_TX_IQCAL_START,
-                          AR_PHY_TX_IQCAL_START_DO_CAL,
-                          0, AH_WAIT_TIMEOUT)) {
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "Tx IQ Cal not complete.\n");
-               goto TX_IQ_CAL_FAILED;
-       }
+                                       REG_RMW_FIELD(ah, AR_PHY_CHAN_INFO_MEMORY,
+                                                     AR_PHY_CHAN_INFO_TAB_S2_READ,
+                                                     0);
 
-       for (i = 0; i < num_chains; i++) {
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "Doing Tx IQ Cal for chain %d.\n", i);
+                                       /* 32 bits */
+                                       iq_res[idx] = REG_READ(ah,
+                                                       chan_info_tab[i] +
+                                                       offset);
 
-               if (REG_READ(ah, txiqcal_status[i]) &
-                            AR_PHY_TX_IQCAL_STATUS_FAILED) {
-                       ath_print(common, ATH_DBG_CALIBRATE,
-                                 "Tx IQ Cal failed for chain %d.\n", i);
-                       goto TX_IQ_CAL_FAILED;
-               }
+                                       REG_RMW_FIELD(ah, AR_PHY_CHAN_INFO_MEMORY,
+                                                     AR_PHY_CHAN_INFO_TAB_S2_READ,
+                                                     1);
 
-               for (j = 0; j < 3; j++) {
-                       u_int8_t idx = 2 * j,
-                       offset = 4 * j;
+                                       /* 16 bits */
+                                       iq_res[idx+1] = 0xffff & REG_READ(ah,
+                                                               chan_info_tab[i] +
+                                                               offset);
 
-                       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);
+                                       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]);
+                               }
 
-                       REG_RMW_FIELD(ah, AR_PHY_CHAN_INFO_MEMORY,
-                                     AR_PHY_CHAN_INFO_TAB_S2_READ, 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_IQ_CAL_FAILED;
+                               }
+                               coeff.mag_coeff[i][im][ip] =
+                                               coeff.iqc_coeff[0] & 0x7f;
+                               coeff.phs_coeff[i][im][ip] =
+                                               (coeff.iqc_coeff[0] >> 7) & 0x7f;
 
-                       /* 16 bits */
-                       iq_res[idx+1] = 0xffff & REG_READ(ah,
-                                                         chan_info_tab[i] +
-                                                         offset);
+                               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;
 
-                       ath_print(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, iqc_coeff)) {
-                       ath_print(common, ATH_DBG_CALIBRATE,
-                                 "Failed in calculation of IQ correction.\n");
-                       goto TX_IQ_CAL_FAILED;
+                       }
                }
-
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "IQ_COEFF[0] = 0x%x IQ_COEFF[1] = 0x%x\n",
-                         iqc_coeff[0], iqc_coeff[1]);
-
-               REG_RMW_FIELD(ah, tx_corr_coeff[i],
-                             AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE,
-                             iqc_coeff[0]);
-               REG_RMW_FIELD(ah, rx_corr[i],
-                             AR_PHY_RX_IQCAL_CORR_LOOPBACK_IQCORR_Q_Q_COFF,
-                             iqc_coeff[1] >> 7);
-               REG_RMW_FIELD(ah, rx_corr[i],
-                             AR_PHY_RX_IQCAL_CORR_LOOPBACK_IQCORR_Q_I_COFF,
-                             iqc_coeff[1]);
        }
 
-       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);
+       ar9003_hw_tx_iqcal_load_avg_2_passes(ah, nchains, &coeff);
 
        return;
 
 TX_IQ_CAL_FAILED:
-       ath_print(common, ATH_DBG_CALIBRATE, "Tx IQ Cal failed\n");
+       ath_dbg(common, ATH_DBG_CALIBRATE, "Tx IQ Cal failed\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)
 {
        struct ath_common *common = ath9k_hw_common(ah);
+       int val;
 
-       /*
-        * 0x7 = 0b111 , AR9003 needs to be configured for 3-chain mode before
-        * running AGC/TxIQ cals
-        */
-       ar9003_hw_set_chain_masks(ah, 0x7, 0x7);
+       val = REG_READ(ah, AR_ENT_OTP);
+       ath_dbg(common, ATH_DBG_CALIBRATE, "ath9k: AR_ENT_OTP 0x%x\n", val);
+
+       if (AR_SREV_9485(ah))
+               ar9003_hw_set_chain_masks(ah, 0x1, 0x1);
+       else if (val & AR_ENT_OTP_CHAIN2_DISABLE)
+               ar9003_hw_set_chain_masks(ah, 0x3, 0x3);
+       else
+               /*
+                * 0x7 = 0b111 , AR9003 needs to be configured for 3-chain
+                * mode before running AGC/TxIQ cals
+                */
+               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);
@@ -739,12 +974,14 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
        /* Poll for offset calibration complete */
        if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL,
                           0, AH_WAIT_TIMEOUT)) {
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "offset calibration failed to "
-                         "complete in 1ms; noisy environment?\n");
+               ath_dbg(common, ATH_DBG_CALIBRATE,
+                       "offset calibration failed to complete in 1ms; noisy environment?\n");
                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);
 
@@ -757,15 +994,15 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
        if (ah->supp_cals & IQ_MISMATCH_CAL) {
                INIT_CAL(&ah->iq_caldata);
                INSERT_CAL(ah, &ah->iq_caldata);
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "enabling IQ Calibration.\n");
+               ath_dbg(common, ATH_DBG_CALIBRATE,
+                       "enabling IQ Calibration.\n");
        }
 
        if (ah->supp_cals & TEMP_COMP_CAL) {
                INIT_CAL(&ah->tempCompCalData);
                INSERT_CAL(ah, &ah->tempCompCalData);
-               ath_print(common, ATH_DBG_CALIBRATE,
-                         "enabling Temperature Compensation Calibration.\n");
+               ath_dbg(common, ATH_DBG_CALIBRATE,
+                       "enabling Temperature Compensation Calibration.\n");
        }
 
        /* Initialize current pointer to first element in list */