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 189aeb2..d313998 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 44c202c..9aa8704 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 2928234..0910310 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 77c2c16..18dfb76 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 a260fcb..94e9211 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 9e63a03..64dde1c 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 515e184..578a723 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 b37c8af..19dbac4 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 e85a0e9..e5f170a 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*/