diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/mci.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/mci.c | 171 |
1 files changed, 170 insertions, 1 deletions
diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index ec2d7c807567..0dd2cbb52d65 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c | |||
@@ -43,6 +43,7 @@ static bool ath_mci_add_profile(struct ath_common *common, | |||
43 | struct ath_mci_profile_info *info) | 43 | struct ath_mci_profile_info *info) |
44 | { | 44 | { |
45 | struct ath_mci_profile_info *entry; | 45 | struct ath_mci_profile_info *entry; |
46 | u8 voice_priority[] = { 110, 110, 110, 112, 110, 110, 114, 116, 118 }; | ||
46 | 47 | ||
47 | if ((mci->num_sco == ATH_MCI_MAX_SCO_PROFILE) && | 48 | if ((mci->num_sco == ATH_MCI_MAX_SCO_PROFILE) && |
48 | (info->type == MCI_GPM_COEX_PROFILE_VOICE)) | 49 | (info->type == MCI_GPM_COEX_PROFILE_VOICE)) |
@@ -59,6 +60,12 @@ static bool ath_mci_add_profile(struct ath_common *common, | |||
59 | memcpy(entry, info, 10); | 60 | memcpy(entry, info, 10); |
60 | INC_PROF(mci, info); | 61 | INC_PROF(mci, info); |
61 | list_add_tail(&entry->list, &mci->info); | 62 | list_add_tail(&entry->list, &mci->info); |
63 | if (info->type == MCI_GPM_COEX_PROFILE_VOICE) { | ||
64 | if (info->voice_type < sizeof(voice_priority)) | ||
65 | mci->voice_priority = voice_priority[info->voice_type]; | ||
66 | else | ||
67 | mci->voice_priority = 110; | ||
68 | } | ||
62 | 69 | ||
63 | return true; | 70 | return true; |
64 | } | 71 | } |
@@ -150,7 +157,7 @@ static void ath_mci_update_scheme(struct ath_softc *sc) | |||
150 | * For single PAN/FTP profile, allocate 35% for BT | 157 | * For single PAN/FTP profile, allocate 35% for BT |
151 | * to improve WLAN throughput. | 158 | * to improve WLAN throughput. |
152 | */ | 159 | */ |
153 | btcoex->duty_cycle = 35; | 160 | btcoex->duty_cycle = AR_SREV_9565(sc->sc_ah) ? 40 : 35; |
154 | btcoex->btcoex_period = 53; | 161 | btcoex->btcoex_period = 53; |
155 | ath_dbg(common, MCI, | 162 | ath_dbg(common, MCI, |
156 | "Single PAN/FTP bt period %d ms dutycycle %d\n", | 163 | "Single PAN/FTP bt period %d ms dutycycle %d\n", |
@@ -250,6 +257,57 @@ static void ath9k_mci_work(struct work_struct *work) | |||
250 | ath_mci_update_scheme(sc); | 257 | ath_mci_update_scheme(sc); |
251 | } | 258 | } |
252 | 259 | ||
260 | static void ath_mci_update_stomp_txprio(u8 cur_txprio, u8 *stomp_prio) | ||
261 | { | ||
262 | if (cur_txprio < stomp_prio[ATH_BTCOEX_STOMP_NONE]) | ||
263 | stomp_prio[ATH_BTCOEX_STOMP_NONE] = cur_txprio; | ||
264 | |||
265 | if (cur_txprio > stomp_prio[ATH_BTCOEX_STOMP_ALL]) | ||
266 | stomp_prio[ATH_BTCOEX_STOMP_ALL] = cur_txprio; | ||
267 | |||
268 | if ((cur_txprio > ATH_MCI_HI_PRIO) && | ||
269 | (cur_txprio < stomp_prio[ATH_BTCOEX_STOMP_LOW])) | ||
270 | stomp_prio[ATH_BTCOEX_STOMP_LOW] = cur_txprio; | ||
271 | } | ||
272 | |||
273 | static void ath_mci_set_concur_txprio(struct ath_softc *sc) | ||
274 | { | ||
275 | struct ath_btcoex *btcoex = &sc->btcoex; | ||
276 | struct ath_mci_profile *mci = &btcoex->mci; | ||
277 | u8 stomp_txprio[] = { 0, 0, 0, 0 }; /* all, low, none, low_ftp */ | ||
278 | |||
279 | if (mci->num_mgmt) { | ||
280 | stomp_txprio[ATH_BTCOEX_STOMP_ALL] = ATH_MCI_INQUIRY_PRIO; | ||
281 | if (!mci->num_pan && !mci->num_other_acl) | ||
282 | stomp_txprio[ATH_BTCOEX_STOMP_NONE] = | ||
283 | ATH_MCI_INQUIRY_PRIO; | ||
284 | } else { | ||
285 | u8 prof_prio[] = { 50, 90, 94, 52 };/* RFCOMM, A2DP, HID, PAN */ | ||
286 | |||
287 | stomp_txprio[ATH_BTCOEX_STOMP_LOW] = | ||
288 | stomp_txprio[ATH_BTCOEX_STOMP_NONE] = 0xff; | ||
289 | |||
290 | if (mci->num_sco) | ||
291 | ath_mci_update_stomp_txprio(mci->voice_priority, | ||
292 | stomp_txprio); | ||
293 | if (mci->num_other_acl) | ||
294 | ath_mci_update_stomp_txprio(prof_prio[0], stomp_txprio); | ||
295 | if (mci->num_a2dp) | ||
296 | ath_mci_update_stomp_txprio(prof_prio[1], stomp_txprio); | ||
297 | if (mci->num_hid) | ||
298 | ath_mci_update_stomp_txprio(prof_prio[2], stomp_txprio); | ||
299 | if (mci->num_pan) | ||
300 | ath_mci_update_stomp_txprio(prof_prio[3], stomp_txprio); | ||
301 | |||
302 | if (stomp_txprio[ATH_BTCOEX_STOMP_NONE] == 0xff) | ||
303 | stomp_txprio[ATH_BTCOEX_STOMP_NONE] = 0; | ||
304 | |||
305 | if (stomp_txprio[ATH_BTCOEX_STOMP_LOW] == 0xff) | ||
306 | stomp_txprio[ATH_BTCOEX_STOMP_LOW] = 0; | ||
307 | } | ||
308 | ath9k_hw_btcoex_set_concur_txprio(sc->sc_ah, stomp_txprio); | ||
309 | } | ||
310 | |||
253 | static u8 ath_mci_process_profile(struct ath_softc *sc, | 311 | static u8 ath_mci_process_profile(struct ath_softc *sc, |
254 | struct ath_mci_profile_info *info) | 312 | struct ath_mci_profile_info *info) |
255 | { | 313 | { |
@@ -281,6 +339,7 @@ static u8 ath_mci_process_profile(struct ath_softc *sc, | |||
281 | } else | 339 | } else |
282 | ath_mci_del_profile(common, mci, entry); | 340 | ath_mci_del_profile(common, mci, entry); |
283 | 341 | ||
342 | ath_mci_set_concur_txprio(sc); | ||
284 | return 1; | 343 | return 1; |
285 | } | 344 | } |
286 | 345 | ||
@@ -314,6 +373,7 @@ static u8 ath_mci_process_status(struct ath_softc *sc, | |||
314 | mci->num_mgmt++; | 373 | mci->num_mgmt++; |
315 | } while (++i < ATH_MCI_MAX_PROFILE); | 374 | } while (++i < ATH_MCI_MAX_PROFILE); |
316 | 375 | ||
376 | ath_mci_set_concur_txprio(sc); | ||
317 | if (old_num_mgmt != mci->num_mgmt) | 377 | if (old_num_mgmt != mci->num_mgmt) |
318 | return 1; | 378 | return 1; |
319 | 379 | ||
@@ -600,3 +660,112 @@ void ath_mci_enable(struct ath_softc *sc) | |||
600 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) | 660 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) |
601 | sc->sc_ah->imask |= ATH9K_INT_MCI; | 661 | sc->sc_ah->imask |= ATH9K_INT_MCI; |
602 | } | 662 | } |
663 | |||
664 | void ath9k_mci_update_wlan_channels(struct ath_softc *sc, bool allow_all) | ||
665 | { | ||
666 | struct ath_hw *ah = sc->sc_ah; | ||
667 | struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; | ||
668 | struct ath9k_channel *chan = ah->curchan; | ||
669 | u32 channelmap[] = {0x00000000, 0xffff0000, 0xffffffff, 0x7fffffff}; | ||
670 | int i; | ||
671 | s16 chan_start, chan_end; | ||
672 | u16 wlan_chan; | ||
673 | |||
674 | if (!chan || !IS_CHAN_2GHZ(chan)) | ||
675 | return; | ||
676 | |||
677 | if (allow_all) | ||
678 | goto send_wlan_chan; | ||
679 | |||
680 | wlan_chan = chan->channel - 2402; | ||
681 | |||
682 | chan_start = wlan_chan - 10; | ||
683 | chan_end = wlan_chan + 10; | ||
684 | |||
685 | if (chan->chanmode == CHANNEL_G_HT40PLUS) | ||
686 | chan_end += 20; | ||
687 | else if (chan->chanmode == CHANNEL_G_HT40MINUS) | ||
688 | chan_start -= 20; | ||
689 | |||
690 | /* adjust side band */ | ||
691 | chan_start -= 7; | ||
692 | chan_end += 7; | ||
693 | |||
694 | if (chan_start <= 0) | ||
695 | chan_start = 0; | ||
696 | if (chan_end >= ATH_MCI_NUM_BT_CHANNELS) | ||
697 | chan_end = ATH_MCI_NUM_BT_CHANNELS - 1; | ||
698 | |||
699 | ath_dbg(ath9k_hw_common(ah), MCI, | ||
700 | "WLAN current channel %d mask BT channel %d - %d\n", | ||
701 | wlan_chan, chan_start, chan_end); | ||
702 | |||
703 | for (i = chan_start; i < chan_end; i++) | ||
704 | MCI_GPM_CLR_CHANNEL_BIT(&channelmap, i); | ||
705 | |||
706 | send_wlan_chan: | ||
707 | /* update and send wlan channels info to BT */ | ||
708 | for (i = 0; i < 4; i++) | ||
709 | mci->wlan_channels[i] = channelmap[i]; | ||
710 | ar9003_mci_send_wlan_channels(ah); | ||
711 | ar9003_mci_state(ah, MCI_STATE_SEND_VERSION_QUERY); | ||
712 | } | ||
713 | |||
714 | void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel, | ||
715 | bool concur_tx) | ||
716 | { | ||
717 | struct ath_hw *ah = sc->sc_ah; | ||
718 | struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci; | ||
719 | bool old_concur_tx = mci_hw->concur_tx; | ||
720 | |||
721 | if (!(mci_hw->config & ATH_MCI_CONFIG_CONCUR_TX)) { | ||
722 | mci_hw->concur_tx = false; | ||
723 | return; | ||
724 | } | ||
725 | |||
726 | if (!IS_CHAN_2GHZ(ah->curchan)) | ||
727 | return; | ||
728 | |||
729 | if (setchannel) { | ||
730 | struct ath9k_hw_cal_data *caldata = &sc->caldata; | ||
731 | if ((caldata->chanmode == CHANNEL_G_HT40PLUS) && | ||
732 | (ah->curchan->channel > caldata->channel) && | ||
733 | (ah->curchan->channel <= caldata->channel + 20)) | ||
734 | return; | ||
735 | if ((caldata->chanmode == CHANNEL_G_HT40MINUS) && | ||
736 | (ah->curchan->channel < caldata->channel) && | ||
737 | (ah->curchan->channel >= caldata->channel - 20)) | ||
738 | return; | ||
739 | mci_hw->concur_tx = false; | ||
740 | } else | ||
741 | mci_hw->concur_tx = concur_tx; | ||
742 | |||
743 | if (old_concur_tx != mci_hw->concur_tx) | ||
744 | ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false); | ||
745 | } | ||
746 | |||
747 | void ath9k_mci_update_rssi(struct ath_softc *sc) | ||
748 | { | ||
749 | struct ath_hw *ah = sc->sc_ah; | ||
750 | struct ath_btcoex *btcoex = &sc->btcoex; | ||
751 | struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci; | ||
752 | |||
753 | if (!(mci_hw->config & ATH_MCI_CONFIG_CONCUR_TX)) | ||
754 | return; | ||
755 | |||
756 | if (ah->stats.avgbrssi >= 40) { | ||
757 | if (btcoex->rssi_count < 0) | ||
758 | btcoex->rssi_count = 0; | ||
759 | if (++btcoex->rssi_count >= ATH_MCI_CONCUR_TX_SWITCH) { | ||
760 | btcoex->rssi_count = 0; | ||
761 | ath9k_mci_set_txpower(sc, false, true); | ||
762 | } | ||
763 | } else { | ||
764 | if (btcoex->rssi_count > 0) | ||
765 | btcoex->rssi_count = 0; | ||
766 | if (--btcoex->rssi_count <= -ATH_MCI_CONCUR_TX_SWITCH) { | ||
767 | btcoex->rssi_count = 0; | ||
768 | ath9k_mci_set_txpower(sc, false, false); | ||
769 | } | ||
770 | } | ||
771 | } | ||