ath9k: Add concurrent WLAN and BT tx support for MCI based chips
authorRajkumar Manoharan <rmanohar@qca.qualcomm.com>
Fri, 12 Oct 2012 08:37:23 +0000 (14:07 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 29 Oct 2012 19:18:53 +0000 (15:18 -0400)
This feature enables both WLAN and BT can transmit simultaneously
by setting WLAN and BT to equal priorities. Whenever both are
transmitting, it might violate regulatory power limits. To avoid
regulatory violation, WLAN tx power will be adjusted according to BT
power index based on avaliability of BT scheduling message. If the
combined power exceeds threshold, BT transmission will be held off.

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

index 2a2d01889613a610a8cb6084e6f0705234867123..29282348eb4dd6d3e1307d8f5b53474f0b99f93e 100644 (file)
@@ -196,7 +196,6 @@ enum mci_state_type {
        MCI_STATE_SEND_WLAN_COEX_VERSION,
        MCI_STATE_SEND_VERSION_QUERY,
        MCI_STATE_SEND_STATUS_QUERY,
-       MCI_STATE_SET_CONCUR_TX_PRI,
        MCI_STATE_RECOVER_RX,
        MCI_STATE_NEED_FTP_STOMP,
        MCI_STATE_DEBUG,
index 419e9a3f2feda6c20fc7a504120f26b0820bb9ca..05d9be5be52eb435ac0edfa789f0c635938aed81 100644 (file)
@@ -218,27 +218,45 @@ void ath9k_hw_btcoex_set_weight(struct ath_hw *ah,
                                enum ath_stomp_type stomp_type)
 {
        struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
+       struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci;
+       u8 txprio_shift[] = { 24, 16, 16, 0 }; /* tx priority weight */
+       bool concur_tx = (mci_hw->concur_tx && btcoex_hw->tx_prio[stomp_type]);
+       const u32 *weight = ar9003_wlan_weights[stomp_type];
+       int i;
 
-       if (AR_SREV_9300_20_OR_LATER(ah)) {
-               const u32 *weight = ar9003_wlan_weights[stomp_type];
-               int i;
-
-               if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
-                       if ((stomp_type == ATH_BTCOEX_STOMP_LOW) &&
-                           btcoex_hw->mci.stomp_ftp)
-                               stomp_type = ATH_BTCOEX_STOMP_LOW_FTP;
-                       weight = mci_wlan_weights[stomp_type];
-               }
-
-               for (i = 0; i < AR9300_NUM_WLAN_WEIGHTS; i++) {
-                       btcoex_hw->bt_weight[i] = AR9300_BT_WGHT;
-                       btcoex_hw->wlan_weight[i] = weight[i];
-               }
-       } else {
+       if (!AR_SREV_9300_20_OR_LATER(ah)) {
                btcoex_hw->bt_coex_weights =
                        SM(bt_weight, AR_BTCOEX_BT_WGHT) |
                        SM(wlan_weight, AR_BTCOEX_WL_WGHT);
+               return;
+       }
+
+       if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
+               enum ath_stomp_type stype =
+                       ((stomp_type == ATH_BTCOEX_STOMP_LOW) &&
+                        btcoex_hw->mci.stomp_ftp) ?
+                       ATH_BTCOEX_STOMP_LOW_FTP : stomp_type;
+               weight = mci_wlan_weights[stype];
        }
+
+       for (i = 0; i < AR9300_NUM_WLAN_WEIGHTS; i++) {
+               btcoex_hw->bt_weight[i] = AR9300_BT_WGHT;
+               btcoex_hw->wlan_weight[i] = weight[i];
+               if (concur_tx && i) {
+                       btcoex_hw->wlan_weight[i] &=
+                               ~(0xff << txprio_shift[i-1]);
+                       btcoex_hw->wlan_weight[i] |=
+                               (btcoex_hw->tx_prio[stomp_type] <<
+                                txprio_shift[i-1]);
+               }
+       }
+       /* Last WLAN weight has to be adjusted wrt tx priority */
+       if (concur_tx) {
+               btcoex_hw->wlan_weight[i-1] &= ~(0xff << txprio_shift[i-1]);
+               btcoex_hw->wlan_weight[i-1] |= (btcoex_hw->tx_prio[stomp_type]
+                                                     << txprio_shift[i-1]);
+       }
+
 }
 EXPORT_SYMBOL(ath9k_hw_btcoex_set_weight);
 
@@ -385,3 +403,13 @@ void ath9k_hw_btcoex_bt_stomp(struct ath_hw *ah,
        }
 }
 EXPORT_SYMBOL(ath9k_hw_btcoex_bt_stomp);
+
+void ath9k_hw_btcoex_set_concur_txprio(struct ath_hw *ah, u8 *stomp_txprio)
+{
+       struct ath_btcoex_hw *btcoex = &ah->btcoex_hw;
+       int i;
+
+       for (i = 0; i < ATH_BTCOEX_STOMP_MAX; i++)
+               btcoex->tx_prio[i] = stomp_txprio[i];
+}
+EXPORT_SYMBOL(ath9k_hw_btcoex_set_concur_txprio);
index 385197ad79b006f494c3659dc88efb343038c184..a260fcb99d13979638f8070318ef5ff86a6b6a6b 100644 (file)
@@ -84,6 +84,7 @@ struct ath9k_hw_mci {
        u8 bt_ver_minor;
        u8 bt_state;
        u8 stomp_ftp;
+       bool concur_tx;
 };
 
 struct ath_btcoex_hw {
@@ -98,6 +99,7 @@ struct ath_btcoex_hw {
        u32 bt_coex_mode2;      /* Register setting for AR_BT_COEX_MODE2 */
        u32 bt_weight[AR9300_NUM_BT_WEIGHTS];
        u32 wlan_weight[AR9300_NUM_WLAN_WEIGHTS];
+       u8 tx_prio[ATH_BTCOEX_STOMP_MAX];
 };
 
 void ath9k_hw_btcoex_init_scheme(struct ath_hw *ah);
@@ -112,5 +114,6 @@ void ath9k_hw_btcoex_set_weight(struct ath_hw *ah,
 void ath9k_hw_btcoex_disable(struct ath_hw *ah);
 void ath9k_hw_btcoex_bt_stomp(struct ath_hw *ah,
                              enum ath_stomp_type stomp_type);
+void ath9k_hw_btcoex_set_concur_txprio(struct ath_hw *ah, u8 *stomp_txprio);
 
 #endif
index 1733a5ac3279d6d152a1f1a575a316af0ca72ada..b37c8af6e02c9aef9122b3aeb73e5e31d8492635 100644 (file)
@@ -43,6 +43,7 @@ static bool ath_mci_add_profile(struct ath_common *common,
                                struct ath_mci_profile_info *info)
 {
        struct ath_mci_profile_info *entry;
+       u8 voice_priority[] = { 110, 110, 110, 112, 110, 110, 114, 116, 118 };
 
        if ((mci->num_sco == ATH_MCI_MAX_SCO_PROFILE) &&
            (info->type == MCI_GPM_COEX_PROFILE_VOICE))
@@ -59,6 +60,12 @@ static bool ath_mci_add_profile(struct ath_common *common,
        memcpy(entry, info, 10);
        INC_PROF(mci, info);
        list_add_tail(&entry->list, &mci->info);
+       if (info->type == MCI_GPM_COEX_PROFILE_VOICE) {
+               if (info->voice_type < sizeof(voice_priority))
+                       mci->voice_priority = voice_priority[info->voice_type];
+               else
+                       mci->voice_priority = 110;
+       }
 
        return true;
 }
@@ -250,6 +257,57 @@ static void ath9k_mci_work(struct work_struct *work)
        ath_mci_update_scheme(sc);
 }
 
+static void ath_mci_update_stomp_txprio(u8 cur_txprio, u8 *stomp_prio)
+{
+       if (cur_txprio < stomp_prio[ATH_BTCOEX_STOMP_NONE])
+               stomp_prio[ATH_BTCOEX_STOMP_NONE] = cur_txprio;
+
+       if (cur_txprio > stomp_prio[ATH_BTCOEX_STOMP_ALL])
+               stomp_prio[ATH_BTCOEX_STOMP_ALL] = cur_txprio;
+
+       if ((cur_txprio > ATH_MCI_HI_PRIO) &&
+           (cur_txprio < stomp_prio[ATH_BTCOEX_STOMP_LOW]))
+               stomp_prio[ATH_BTCOEX_STOMP_LOW] = cur_txprio;
+}
+
+static void ath_mci_set_concur_txprio(struct ath_softc *sc)
+{
+       struct ath_btcoex *btcoex = &sc->btcoex;
+       struct ath_mci_profile *mci = &btcoex->mci;
+       u8 stomp_txprio[] = { 0, 0, 0, 0 }; /* all, low, none, low_ftp */
+
+       if (mci->num_mgmt) {
+               stomp_txprio[ATH_BTCOEX_STOMP_ALL] = ATH_MCI_INQUIRY_PRIO;
+               if (!mci->num_pan && !mci->num_other_acl)
+                       stomp_txprio[ATH_BTCOEX_STOMP_NONE] =
+                               ATH_MCI_INQUIRY_PRIO;
+       } else {
+               u8 prof_prio[] = { 50, 90, 94, 52 };/* RFCOMM, A2DP, HID, PAN */
+
+               stomp_txprio[ATH_BTCOEX_STOMP_LOW] =
+               stomp_txprio[ATH_BTCOEX_STOMP_NONE] = 0xff;
+
+               if (mci->num_sco)
+                       ath_mci_update_stomp_txprio(mci->voice_priority,
+                                                   stomp_txprio);
+               if (mci->num_other_acl)
+                       ath_mci_update_stomp_txprio(prof_prio[0], stomp_txprio);
+               if (mci->num_a2dp)
+                       ath_mci_update_stomp_txprio(prof_prio[1], stomp_txprio);
+               if (mci->num_hid)
+                       ath_mci_update_stomp_txprio(prof_prio[2], stomp_txprio);
+               if (mci->num_pan)
+                       ath_mci_update_stomp_txprio(prof_prio[3], stomp_txprio);
+
+               if (stomp_txprio[ATH_BTCOEX_STOMP_NONE] == 0xff)
+                       stomp_txprio[ATH_BTCOEX_STOMP_NONE] = 0;
+
+               if (stomp_txprio[ATH_BTCOEX_STOMP_LOW] == 0xff)
+                       stomp_txprio[ATH_BTCOEX_STOMP_LOW] = 0;
+       }
+       ath9k_hw_btcoex_set_concur_txprio(sc->sc_ah, stomp_txprio);
+}
+
 static u8 ath_mci_process_profile(struct ath_softc *sc,
                                  struct ath_mci_profile_info *info)
 {
@@ -281,6 +339,7 @@ static u8 ath_mci_process_profile(struct ath_softc *sc,
        } else
                ath_mci_del_profile(common, mci, entry);
 
+       ath_mci_set_concur_txprio(sc);
        return 1;
 }
 
@@ -314,6 +373,7 @@ static u8 ath_mci_process_status(struct ath_softc *sc,
                        mci->num_mgmt++;
        } while (++i < ATH_MCI_MAX_PROFILE);
 
+       ath_mci_set_concur_txprio(sc);
        if (old_num_mgmt != mci->num_mgmt)
                return 1;
 
index a3df314c1e704f9f537fcec4ff37b7e3f79754a9..e85a0e9506fad8ebcb83dab0913de5983eaff998 100644 (file)
@@ -32,6 +32,8 @@
 #define ATH_MCI_MAX_PROFILE            (ATH_MCI_MAX_ACL_PROFILE +\
                                         ATH_MCI_MAX_SCO_PROFILE)
 
+#define ATH_MCI_INQUIRY_PRIO         62
+#define ATH_MCI_HI_PRIO              60
 #define ATH_MCI_NUM_BT_CHANNELS      79
 
 #define MCI_GPM_SET_CHANNEL_BIT(_p_gpm, _bt_chan)                        \
@@ -131,6 +133,7 @@ struct ath_mci_profile {
        u8 num_pan;
        u8 num_other_acl;
        u8 num_bdr;
+       u8 voice_priority;
 };
 
 struct ath_mci_buf {