ath9k_hw: Improve fast channel change for AR9003 chips
authorRajkumar Manoharan <rmanohar@qca.qualcomm.com>
Thu, 13 Oct 2011 05:30:35 +0000 (11:00 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 14 Oct 2011 18:48:21 +0000 (14:48 -0400)
In order to reduce the overall scan time, fast channel change
should be implemented properly. This patch adds fast channel
change support across band switch or channel mode switch
instead of doing full chip reset. During the fastcc, tx iqcal
measurements are preserved and will be reloaded after successful
the channel change.

This patch also addressed fast channel issue where the STA can not
see APs in higher than operating channel on 5GHz band after
the association.

Cc: Paul Stewart <pstew@google.com>
Signed-off-by: Rajkumar Manoharan <rmanohar@qca.qualcomm.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.c
drivers/net/wireless/ath/ath9k/ar9003_phy.h
drivers/net/wireless/ath/ath9k/hw-ops.h
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h

index e4b1a83008547cbe053b18af6f2119bd95486e58..026aa5b833d2e14a8581b9f55d1d51cb7fda1c3d 100644 (file)
@@ -18,7 +18,7 @@
 #include "hw-ops.h"
 #include "ar9003_phy.h"
 
-#define MAX_MEASUREMENT        8
+#define MAX_MEASUREMENT        MAX_IQCAL_MEASUREMENT
 #define MAX_MAG_DELTA  11
 #define MAX_PHS_DELTA  10
 
@@ -663,6 +663,7 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
 {
        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++) {
@@ -712,13 +713,21 @@ 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,
                      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);
+       if (caldata)
+               caldata->done_txiqcal_once = true;
 
        return;
 
@@ -845,10 +854,55 @@ 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_init_cal(struct ath_hw *ah,
                               struct ath9k_channel *chan)
 {
        struct ath_common *common = ath9k_hw_common(ah);
+       struct ath9k_hw_cal_data *caldata = ah->caldata;
        bool txiqcal_done = false;
 
        /* Do Tx IQ Calibration */
@@ -860,9 +914,15 @@ 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))
+       if (AR_SREV_9485_OR_LATER(ah) && !AR_SREV_9340(ah)) {
+               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 = true;
-       else {
+       else {
                txiqcal_done = ar9003_hw_tx_iq_cal_run(ah);
                REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
                udelay(5);
@@ -884,6 +944,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
 
        if (txiqcal_done)
                ar9003_hw_tx_iq_cal_post_proc(ah);
+       else if (caldata && caldata->done_txiqcal_once)
+               ar9003_hw_tx_iq_cal_reload(ah);
 
        ath9k_hw_loadnf(ah, chan);
        ath9k_hw_start_nfcal(ah, true);
@@ -912,8 +974,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;
 }
index 779f407222ed1b3171e75b91cb5b35d1a7626507..4e31d655c4ea1babddd667e62022f29332d69900 100644 (file)
@@ -688,6 +688,7 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
        if (AR_SREV_9480(ah))
                ar9003_hw_prog_ini(ah, &ah->ini_BTCOEX_MAX_TXPWR, 1);
 
+       ah->modes_index = modesIndex;
        ar9003_hw_override_ini(ah);
        ar9003_hw_set_channel_regs(ah, chan);
        ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
@@ -1247,6 +1248,73 @@ static void ar9003_hw_antdiv_comb_conf_set(struct ath_hw *ah,
        REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
 }
 
+static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
+                                     struct ath9k_channel *chan,
+                                     u8 *ini_reloaded)
+{
+       unsigned int regWrites = 0;
+       u32 modesIndex;
+
+       switch (chan->chanmode) {
+       case CHANNEL_A:
+       case CHANNEL_A_HT20:
+               modesIndex = 1;
+               break;
+       case CHANNEL_A_HT40PLUS:
+       case CHANNEL_A_HT40MINUS:
+               modesIndex = 2;
+               break;
+       case CHANNEL_G:
+       case CHANNEL_G_HT20:
+       case CHANNEL_B:
+               modesIndex = 4;
+               break;
+       case CHANNEL_G_HT40PLUS:
+       case CHANNEL_G_HT40MINUS:
+               modesIndex = 3;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       if (modesIndex == ah->modes_index) {
+               *ini_reloaded = false;
+               goto set_rfmode;
+       }
+
+       ar9003_hw_prog_ini(ah, &ah->iniSOC[ATH_INI_POST], modesIndex);
+       ar9003_hw_prog_ini(ah, &ah->iniMac[ATH_INI_POST], modesIndex);
+       ar9003_hw_prog_ini(ah, &ah->iniBB[ATH_INI_POST], modesIndex);
+       ar9003_hw_prog_ini(ah, &ah->iniRadio[ATH_INI_POST], modesIndex);
+       if (AR_SREV_9480_20(ah))
+               ar9003_hw_prog_ini(ah,
+                               &ah->ini_radio_post_sys2ant,
+                               modesIndex);
+
+       REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites);
+
+       /*
+        * For 5GHz channels requiring Fast Clock, apply
+        * different modal values.
+        */
+       if (IS_CHAN_A_FAST_CLOCK(ah, chan))
+               REG_WRITE_ARRAY(&ah->iniModesAdditional, modesIndex, regWrites);
+
+       if (AR_SREV_9330(ah))
+               REG_WRITE_ARRAY(&ah->iniModesAdditional, 1, regWrites);
+
+       if (AR_SREV_9340(ah) && !ah->is_clk_25mhz)
+               REG_WRITE_ARRAY(&ah->iniModesAdditional_40M, 1, regWrites);
+
+       ah->modes_index = modesIndex;
+       *ini_reloaded = true;
+
+set_rfmode:
+       ar9003_hw_set_rfmode(ah, chan);
+       return 0;
+}
+
 void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
 {
        struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
@@ -1275,6 +1343,7 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
        priv_ops->do_getnf = ar9003_hw_do_getnf;
        priv_ops->ani_cache_ini_regs = ar9003_hw_ani_cache_ini_regs;
        priv_ops->set_radar_params = ar9003_hw_set_radar_params;
+       priv_ops->fast_chan_change = ar9003_hw_fast_chan_change;
 
        ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get;
        ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set;
index 6cea546a15076be97fc7788f9ee3eecf6c813974..c6ed51fb6858f647735d84caf558bf724beafe39 100644 (file)
 
 #define AR_PHY_TXGAIN_TABLE      (AR_SM_BASE + 0x300)
 
+#define AR_PHY_TX_IQCAL_CONTROL_0   (AR_SM_BASE + AR_SREV_9485(ah) ? \
+                                                0x3c4 : 0x444)
 #define AR_PHY_TX_IQCAL_CONTROL_1   (AR_SM_BASE + AR_SREV_9485(ah) ? \
                                                 0x3c8 : 0x448)
 #define AR_PHY_TX_IQCAL_START       (AR_SM_BASE + AR_SREV_9485(ah) ? \
 #define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT   0x01000000
 #define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S 24
 #define AR_PHY_CHANNEL_STATUS_RX_CLEAR      0x00000004
+#define AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL                   0x80000000
 #define AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT             0x01fc0000
 #define AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT_S                   18
 #define AR_PHY_TX_IQCAL_START_DO_CAL       0x00000001
index e9782d164962ab450474a4a9475d8f9a757534b6..e74c233757a2d85da633f81b958dfd3a65e6dc8e 100644 (file)
@@ -205,4 +205,11 @@ static inline void ath9k_hw_setup_calibration(struct ath_hw *ah,
        ath9k_hw_private_ops(ah)->setup_calibration(ah, currCal);
 }
 
+static inline int ath9k_hw_fast_chan_change(struct ath_hw *ah,
+                                           struct ath9k_channel *chan,
+                                           u8 *ini_reloaded)
+{
+       return ath9k_hw_private_ops(ah)->fast_chan_change(ah, chan,
+                                                         ini_reloaded);
+}
 #endif /* ATH9K_HW_OPS_H */
index 58794a4e40ad1a280b49eea7eaab98e751ec4231..e51d93d33d5fdfcf85bff528f5e92220b7804c27 100644 (file)
@@ -1394,6 +1394,14 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
        struct ath_common *common = ath9k_hw_common(ah);
        u32 qnum;
        int r;
+       bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
+       bool band_switch, mode_diff;
+       u8 ini_reloaded;
+
+       band_switch = (chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ)) !=
+                     (ah->curchan->channelFlags & (CHANNEL_2GHZ |
+                                                   CHANNEL_5GHZ));
+       mode_diff = (chan->chanmode != ah->curchan->chanmode);
 
        for (qnum = 0; qnum < AR_NUM_QCU; qnum++) {
                if (ath9k_hw_numtxpending(ah, qnum)) {
@@ -1408,6 +1416,18 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
                return false;
        }
 
+       if (edma && (band_switch || mode_diff)) {
+               ath9k_hw_mark_phy_inactive(ah);
+               udelay(5);
+
+               ath9k_hw_init_pll(ah, NULL);
+
+               if (ath9k_hw_fast_chan_change(ah, chan, &ini_reloaded)) {
+                       ath_err(common, "Failed to do fast channel change\n");
+                       return false;
+               }
+       }
+
        ath9k_hw_set_channel_regs(ah, chan);
 
        r = ath9k_hw_rf_set_freq(ah, chan);
@@ -1424,6 +1444,16 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
 
        ath9k_hw_spur_mitigate_freq(ah, chan);
 
+       if (edma && (band_switch || mode_diff)) {
+               if (band_switch || ini_reloaded)
+                       ah->eep_ops->set_board_values(ah, chan);
+
+               ath9k_hw_init_bb(ah, chan);
+
+               if (band_switch || ini_reloaded)
+                       ath9k_hw_init_cal(ah, chan);
+       }
+
        return true;
 }
 
@@ -1677,6 +1707,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 
        ath9k_hw_init_bb(ah, chan);
 
+       if (caldata)
+               caldata->done_txiqcal_once = false;
        if (!ath9k_hw_init_cal(ah, chan))
                return -EIO;
 
index 684c33c4897c9656a5e7757f3d4de1e10e53af35..e5c458d22c82c80f16e176f25b663b2630377419 100644 (file)
@@ -337,6 +337,8 @@ enum ath9k_int {
         CHANNEL_HT40PLUS |                     \
         CHANNEL_HT40MINUS)
 
+#define MAX_IQCAL_MEASUREMENT  8
+
 struct ath9k_hw_cal_data {
        u16 channel;
        u32 channelFlags;
@@ -346,8 +348,11 @@ struct ath9k_hw_cal_data {
        bool paprd_done;
        bool nfcal_pending;
        bool nfcal_interference;
+       bool done_txiqcal_once;
        u16 small_signal_gain[AR9300_MAX_CHAINS];
        u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ];
+       u32 num_measures[AR9300_MAX_CHAINS];
+       int tx_corr_coeff[MAX_IQCAL_MEASUREMENT][AR9300_MAX_CHAINS];
        struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
 };
 
@@ -583,6 +588,8 @@ struct ath_hw_private_ops {
        void (*do_getnf)(struct ath_hw *ah, int16_t nfarray[NUM_NF_READINGS]);
        void (*set_radar_params)(struct ath_hw *ah,
                                 struct ath_hw_radar_conf *conf);
+       int (*fast_chan_change)(struct ath_hw *ah, struct ath9k_channel *chan,
+                               u8 *ini_reloaded);
 
        /* ANI */
        void (*ani_cache_ini_regs)(struct ath_hw *ah);
@@ -684,6 +691,7 @@ struct ath_hw {
        atomic_t intr_ref_cnt;
        bool chip_fullsleep;
        u32 atim_window;
+       u32 modes_index;
 
        /* Calibration */
        u32 supp_cals;