ath9k: Add concurrent WLAN and BT tx support for MCI based chips

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>
This commit is contained in:
Rajkumar Manoharan 2012-10-12 14:07:23 +05:30 committed by John W. Linville
parent 50072ebca3
commit db60428b1a
5 changed files with 110 additions and 17 deletions

View 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,

View 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);

View 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

View 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;

View 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 {