Skip to content

Commit

Permalink
ath9k: Add concurrent WLAN and BT tx support for MCI based chips
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Rajkumar Manoharan authored and John W. Linville committed Oct 29, 2012
1 parent 50072eb commit db60428
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 17 deletions.
1 change: 0 additions & 1 deletion drivers/net/wireless/ath/ath9k/ar9003_mci.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
60 changes: 44 additions & 16 deletions drivers/net/wireless/ath/ath9k/btcoex.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);
3 changes: 3 additions & 0 deletions drivers/net/wireless/ath/ath9k/btcoex.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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);
Expand All @@ -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
60 changes: 60 additions & 0 deletions drivers/net/wireless/ath/ath9k/mci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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;
}
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;

Expand Down
3 changes: 3 additions & 0 deletions drivers/net/wireless/ath/ath9k/mci.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit db60428

Please sign in to comment.