ath9k: adjust WLAN and BT concurrent transmission
authorRajkumar Manoharan <rmanohar@qca.qualcomm.com>
Fri, 12 Oct 2012 08:37:25 +0000 (14:07 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 29 Oct 2012 19:19:27 +0000 (15:19 -0400)
The simulataneous transmission of both WLAN and BT might cause
increase in power levels. To avoid regulatory violation, WLAN tx
power will be adjusted according to BT power index based on avaliability
of BT scheduling messages. WLAN tx power reduction might affect its
performance. So WLAN tx power is only be lowered when the signal strength
is good enough. Otherwise concurrent tx will be disabled and WLAN uses
it default power levels. Also concurrent tx is disabled whenever WLAN is
moving to off-channel which might be used by BT.

Signed-off-by: Rajkumar Manoharan <rmanohar@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_mci.c
drivers/net/wireless/ath/ath9k/ar9003_mci.h
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/btcoex.h
drivers/net/wireless/ath/ath9k/gpio.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/mci.c
drivers/net/wireless/ath/ath9k/mci.h

index 189aeb22f5551b3e3e9ae9ea64e500be296b8a99..d31399871e8d87560597e42d3eeec5e3314cf85f 100644 (file)
@@ -18,6 +18,7 @@
 #include "hw.h"
 #include "ar9003_phy.h"
 #include "ar9003_eeprom.h"
+#include "ar9003_mci.h"
 
 #define COMP_HDR_LEN 4
 #define COMP_CKSUM_LEN 2
@@ -41,7 +42,6 @@
 static int ar9003_hw_power_interpolate(int32_t x,
                                       int32_t *px, int32_t *py, u_int16_t np);
 
-
 static const struct ar9300_eeprom ar9300_default = {
        .eepromVersion = 2,
        .templateVersion = 2,
@@ -5037,16 +5037,28 @@ static void ar9003_hw_set_power_per_rate_table(struct ath_hw *ah,
                case CTL_5GHT20:
                case CTL_2GHT20:
                        for (i = ALL_TARGET_HT20_0_8_16;
-                            i <= ALL_TARGET_HT20_23; i++)
+                            i <= ALL_TARGET_HT20_23; i++) {
                                pPwrArray[i] = (u8)min((u16)pPwrArray[i],
                                                       minCtlPower);
+                               if (ath9k_hw_mci_is_enabled(ah))
+                                       pPwrArray[i] =
+                                               (u8)min((u16)pPwrArray[i],
+                                               ar9003_mci_get_max_txpower(ah,
+                                                       pCtlMode[ctlMode]));
+                       }
                        break;
                case CTL_5GHT40:
                case CTL_2GHT40:
                        for (i = ALL_TARGET_HT40_0_8_16;
-                            i <= ALL_TARGET_HT40_23; i++)
+                            i <= ALL_TARGET_HT40_23; i++) {
                                pPwrArray[i] = (u8)min((u16)pPwrArray[i],
                                                       minCtlPower);
+                               if (ath9k_hw_mci_is_enabled(ah))
+                                       pPwrArray[i] =
+                                               (u8)min((u16)pPwrArray[i],
+                                               ar9003_mci_get_max_txpower(ah,
+                                                       pCtlMode[ctlMode]));
+                       }
                        break;
                default:
                        break;
index 44c202ce6c66bb12544ddd6673abd6405962f5cc..9aa8704eb3e4d9a3d6b93b9cd239120788227c3f 100644 (file)
@@ -818,7 +818,7 @@ int ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g,
 {
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
-       u32 regval;
+       u32 regval, i;
 
        ath_dbg(common, MCI, "MCI Reset (full_sleep = %d, is_2g = %d)\n",
                is_full_sleep, is_2g);
@@ -868,6 +868,18 @@ int ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g,
        REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2, AR_BTCOEX_CTRL2_RX_DEWEIGHT, 1);
        REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0);
 
+       /* concurrent tx priority */
+       if (mci->config & ATH_MCI_CONFIG_CONCUR_TX) {
+               REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2,
+                             AR_BTCOEX_CTRL2_DESC_BASED_TXPWR_ENABLE, 0);
+               REG_RMW_FIELD(ah, AR_BTCOEX_CTRL2,
+                             AR_BTCOEX_CTRL2_TXPWR_THRESH, 0x7f);
+               REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
+                             AR_BTCOEX_CTRL_REDUCE_TXPWR, 0);
+               for (i = 0; i < 8; i++)
+                       REG_WRITE(ah, AR_BTCOEX_MAX_TXPWR(i), 0x7f7f7f7f);
+       }
+
        regval = MS(mci->config, ATH_MCI_CONFIG_CLK_DIV);
        REG_RMW_FIELD(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_CLK_DIV, regval);
        REG_SET_BIT(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_MCI_MODE_EN);
@@ -1426,3 +1438,17 @@ void ar9003_mci_send_wlan_channels(struct ath_hw *ah)
        ar9003_mci_send_coex_wlan_channels(ah, true);
 }
 EXPORT_SYMBOL(ar9003_mci_send_wlan_channels);
+
+u16 ar9003_mci_get_max_txpower(struct ath_hw *ah, u8 ctlmode)
+{
+       if (!ah->btcoex_hw.mci.concur_tx)
+               goto out;
+
+       if (ctlmode == CTL_2GHT20)
+               return ATH_BTCOEX_HT20_MAX_TXPOWER;
+       else if (ctlmode == CTL_2GHT40)
+               return ATH_BTCOEX_HT40_MAX_TXPOWER;
+
+out:
+       return -1;
+}
index 29282348eb4dd6d3e1307d8f5b53474f0b99f93e..0910310ae834ef974aa2938a495fd94f07fd6ca9 100644 (file)
@@ -277,6 +277,7 @@ void ar9003_mci_get_isr(struct ath_hw *ah, enum ath9k_int *masked);
 void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah);
 void ar9003_mci_set_power_awake(struct ath_hw *ah);
 void ar9003_mci_check_gpm_offset(struct ath_hw *ah);
+u16 ar9003_mci_get_max_txpower(struct ath_hw *ah, u8 ctlmode);
 
 #else
 
@@ -323,6 +324,10 @@ static inline void ar9003_mci_set_power_awake(struct ath_hw *ah)
 static inline void ar9003_mci_check_gpm_offset(struct ath_hw *ah)
 {
 }
+static inline u16 ar9003_mci_get_max_txpower(struct ath_hw *ah, u8 ctlmode)
+{
+       return -1;
+}
 #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */
 
 #endif
index 77c2c16b69610e6186497a0e2cbfc6fb8ca12aa8..18dfb764eed5f1d6ebbf1129d906d7cf82eaf7ea 100644 (file)
@@ -479,6 +479,7 @@ struct ath_btcoex {
        u32 btscan_no_stomp; /* in usec */
        u32 duty_cycle;
        u32 bt_wait_time;
+       int rssi_count;
        struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */
        struct ath_mci_profile mci;
 };
index a260fcb99d13979638f8070318ef5ff86a6b6a6b..94e921147e4ce6d9da09394d778d0beeb125e8ed 100644 (file)
@@ -39,6 +39,9 @@
 #define ATH_BTCOEX_RX_WAIT_TIME       100
 #define ATH_BTCOEX_STOMP_FTP_THRESH   5
 
+#define ATH_BTCOEX_HT20_MAX_TXPOWER   0x14
+#define ATH_BTCOEX_HT40_MAX_TXPOWER   0x10
+
 #define AR9300_NUM_BT_WEIGHTS   4
 #define AR9300_NUM_WLAN_WEIGHTS 4
 /* Defines the BT AR_BT_COEX_WGHT used */
index 9e63a03330cb631e05be6c8b2874cc9ced1992d9..64dde1cd20e88463b23b3e33e7cf446fb107b47d 100644 (file)
@@ -227,6 +227,8 @@ static void ath_btcoex_period_timer(unsigned long data)
        }
        spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 
+       ath9k_mci_update_rssi(sc);
+
        ath9k_ps_wakeup(sc);
 
        if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI))
index 515e184ab47fb00e3b567872f3823eb0c909db8a..578a7234aa56dd6f10343c6564c9ae3f430c3646 100644 (file)
@@ -293,6 +293,10 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan,
                goto out;
        }
 
+       if (ath9k_hw_mci_is_enabled(sc->sc_ah) &&
+           (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
+               ath9k_mci_set_txpower(sc, true, false);
+
        if (!ath_complete_reset(sc, true))
                r = -EIO;
 
index b37c8af6e02c9aef9122b3aeb73e5e31d8492635..19dbac42f3d956601452c0ad7557d711e6016d13 100644 (file)
@@ -710,3 +710,62 @@ send_wlan_chan:
        ar9003_mci_send_wlan_channels(ah);
        ar9003_mci_state(ah, MCI_STATE_SEND_VERSION_QUERY);
 }
+
+void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
+                          bool concur_tx)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci;
+       bool old_concur_tx = mci_hw->concur_tx;
+
+       if (!(mci_hw->config & ATH_MCI_CONFIG_CONCUR_TX)) {
+               mci_hw->concur_tx = false;
+               return;
+       }
+
+       if (!IS_CHAN_2GHZ(ah->curchan))
+               return;
+
+       if (setchannel) {
+               struct ath9k_hw_cal_data *caldata = &sc->caldata;
+               if ((caldata->chanmode == CHANNEL_G_HT40PLUS) &&
+                   (ah->curchan->channel > caldata->channel) &&
+                   (ah->curchan->channel <= caldata->channel + 20))
+                       return;
+               if ((caldata->chanmode == CHANNEL_G_HT40MINUS) &&
+                   (ah->curchan->channel < caldata->channel) &&
+                   (ah->curchan->channel >= caldata->channel - 20))
+                       return;
+               mci_hw->concur_tx = false;
+       } else
+               mci_hw->concur_tx = concur_tx;
+
+       if (old_concur_tx != mci_hw->concur_tx)
+               ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false);
+}
+
+void ath9k_mci_update_rssi(struct ath_softc *sc)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_btcoex *btcoex = &sc->btcoex;
+       struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci;
+
+       if (!(mci_hw->config & ATH_MCI_CONFIG_CONCUR_TX))
+               return;
+
+       if (ah->stats.avgbrssi >= 40) {
+               if (btcoex->rssi_count < 0)
+                       btcoex->rssi_count = 0;
+               if (++btcoex->rssi_count >= ATH_MCI_CONCUR_TX_SWITCH) {
+                       btcoex->rssi_count = 0;
+                       ath9k_mci_set_txpower(sc, false, true);
+               }
+       } else {
+               if (btcoex->rssi_count > 0)
+                       btcoex->rssi_count = 0;
+               if (--btcoex->rssi_count <= -ATH_MCI_CONCUR_TX_SWITCH) {
+                       btcoex->rssi_count = 0;
+                       ath9k_mci_set_txpower(sc, false, false);
+               }
+       }
+}
index e85a0e9506fad8ebcb83dab0913de5983eaff998..e5f170a4d6629e2e1f5c6c8435a8862e17c9aaeb 100644 (file)
@@ -35,6 +35,7 @@
 #define ATH_MCI_INQUIRY_PRIO         62
 #define ATH_MCI_HI_PRIO              60
 #define ATH_MCI_NUM_BT_CHANNELS      79
+#define ATH_MCI_CONCUR_TX_SWITCH      5
 
 #define MCI_GPM_SET_CHANNEL_BIT(_p_gpm, _bt_chan)                        \
        do {                                                              \
@@ -151,10 +152,13 @@ void ath_mci_flush_profile(struct ath_mci_profile *mci);
 int ath_mci_setup(struct ath_softc *sc);
 void ath_mci_cleanup(struct ath_softc *sc);
 void ath_mci_intr(struct ath_softc *sc);
+void ath9k_mci_update_rssi(struct ath_softc *sc);
 
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
 void ath_mci_enable(struct ath_softc *sc);
 void ath9k_mci_update_wlan_channels(struct ath_softc *sc, bool allow_all);
+void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
+                          bool concur_tx);
 #else
 static inline void ath_mci_enable(struct ath_softc *sc)
 {
@@ -163,6 +167,10 @@ static inline void ath9k_mci_update_wlan_channels(struct ath_softc *sc,
                                                  bool allow_all)
 {
 }
+static inline void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
+                                        bool concur_tx)
+{
+}
 #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */
 
 #endif /* MCI_H*/