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 2a2d018..2928234 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 419e9a3..05d9be5 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 385197a..a260fcb 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 1733a5a..b37c8af 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 a3df314..e85a0e9 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 {