162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2010-2011 Atheros Communications Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1162306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1362306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1462306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "ath9k.h" 2162306a36Sopenharmony_ci#include "mci.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic const u8 ath_mci_duty_cycle[] = { 55, 50, 60, 70, 80, 85, 90, 95, 98 }; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic struct ath_mci_profile_info* 2662306a36Sopenharmony_ciath_mci_find_profile(struct ath_mci_profile *mci, 2762306a36Sopenharmony_ci struct ath_mci_profile_info *info) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct ath_mci_profile_info *entry; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci if (list_empty(&mci->info)) 3262306a36Sopenharmony_ci return NULL; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci list_for_each_entry(entry, &mci->info, list) { 3562306a36Sopenharmony_ci if (entry->conn_handle == info->conn_handle) 3662306a36Sopenharmony_ci return entry; 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci return NULL; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic bool ath_mci_add_profile(struct ath_common *common, 4262306a36Sopenharmony_ci struct ath_mci_profile *mci, 4362306a36Sopenharmony_ci struct ath_mci_profile_info *info) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct ath_mci_profile_info *entry; 4662306a36Sopenharmony_ci static const u8 voice_priority[] = { 110, 110, 110, 112, 110, 110, 114, 116, 118 }; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if ((mci->num_sco == ATH_MCI_MAX_SCO_PROFILE) && 4962306a36Sopenharmony_ci (info->type == MCI_GPM_COEX_PROFILE_VOICE)) 5062306a36Sopenharmony_ci return false; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (((NUM_PROF(mci) - mci->num_sco) == ATH_MCI_MAX_ACL_PROFILE) && 5362306a36Sopenharmony_ci (info->type != MCI_GPM_COEX_PROFILE_VOICE)) 5462306a36Sopenharmony_ci return false; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_ATOMIC); 5762306a36Sopenharmony_ci if (!entry) 5862306a36Sopenharmony_ci return false; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci memcpy(entry, info, 10); 6162306a36Sopenharmony_ci INC_PROF(mci, info); 6262306a36Sopenharmony_ci list_add_tail(&entry->list, &mci->info); 6362306a36Sopenharmony_ci if (info->type == MCI_GPM_COEX_PROFILE_VOICE) { 6462306a36Sopenharmony_ci if (info->voice_type < sizeof(voice_priority)) 6562306a36Sopenharmony_ci mci->voice_priority = voice_priority[info->voice_type]; 6662306a36Sopenharmony_ci else 6762306a36Sopenharmony_ci mci->voice_priority = 110; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return true; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void ath_mci_del_profile(struct ath_common *common, 7462306a36Sopenharmony_ci struct ath_mci_profile *mci, 7562306a36Sopenharmony_ci struct ath_mci_profile_info *entry) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci if (!entry) 7862306a36Sopenharmony_ci return; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci DEC_PROF(mci, entry); 8162306a36Sopenharmony_ci list_del(&entry->list); 8262306a36Sopenharmony_ci kfree(entry); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_civoid ath_mci_flush_profile(struct ath_mci_profile *mci) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct ath_mci_profile_info *info, *tinfo; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci mci->aggr_limit = 0; 9062306a36Sopenharmony_ci mci->num_mgmt = 0; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (list_empty(&mci->info)) 9362306a36Sopenharmony_ci return; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci list_for_each_entry_safe(info, tinfo, &mci->info, list) { 9662306a36Sopenharmony_ci list_del(&info->list); 9762306a36Sopenharmony_ci DEC_PROF(mci, info); 9862306a36Sopenharmony_ci kfree(info); 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void ath_mci_adjust_aggr_limit(struct ath_btcoex *btcoex) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct ath_mci_profile *mci = &btcoex->mci; 10562306a36Sopenharmony_ci u32 wlan_airtime = btcoex->btcoex_period * 10662306a36Sopenharmony_ci (100 - btcoex->duty_cycle) / 100; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* 10962306a36Sopenharmony_ci * Scale: wlan_airtime is in ms, aggr_limit is in 0.25 ms. 11062306a36Sopenharmony_ci * When wlan_airtime is less than 4ms, aggregation limit has to be 11162306a36Sopenharmony_ci * adjusted half of wlan_airtime to ensure that the aggregation can fit 11262306a36Sopenharmony_ci * without collision with BT traffic. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci if ((wlan_airtime <= 4) && 11562306a36Sopenharmony_ci (!mci->aggr_limit || (mci->aggr_limit > (2 * wlan_airtime)))) 11662306a36Sopenharmony_ci mci->aggr_limit = 2 * wlan_airtime; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void ath_mci_update_scheme(struct ath_softc *sc) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 12262306a36Sopenharmony_ci struct ath_btcoex *btcoex = &sc->btcoex; 12362306a36Sopenharmony_ci struct ath_mci_profile *mci = &btcoex->mci; 12462306a36Sopenharmony_ci struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci; 12562306a36Sopenharmony_ci struct ath_mci_profile_info *info; 12662306a36Sopenharmony_ci u32 num_profile = NUM_PROF(mci); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (mci_hw->config & ATH_MCI_CONFIG_DISABLE_TUNING) 12962306a36Sopenharmony_ci goto skip_tuning; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci mci->aggr_limit = 0; 13262306a36Sopenharmony_ci btcoex->duty_cycle = ath_mci_duty_cycle[num_profile]; 13362306a36Sopenharmony_ci btcoex->btcoex_period = ATH_MCI_DEF_BT_PERIOD; 13462306a36Sopenharmony_ci if (NUM_PROF(mci)) 13562306a36Sopenharmony_ci btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW; 13662306a36Sopenharmony_ci else 13762306a36Sopenharmony_ci btcoex->bt_stomp_type = mci->num_mgmt ? ATH_BTCOEX_STOMP_ALL : 13862306a36Sopenharmony_ci ATH_BTCOEX_STOMP_LOW; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (num_profile == 1) { 14162306a36Sopenharmony_ci info = list_first_entry(&mci->info, 14262306a36Sopenharmony_ci struct ath_mci_profile_info, 14362306a36Sopenharmony_ci list); 14462306a36Sopenharmony_ci if (mci->num_sco) { 14562306a36Sopenharmony_ci if (info->T == 12) 14662306a36Sopenharmony_ci mci->aggr_limit = 8; 14762306a36Sopenharmony_ci else if (info->T == 6) { 14862306a36Sopenharmony_ci mci->aggr_limit = 6; 14962306a36Sopenharmony_ci btcoex->duty_cycle = 30; 15062306a36Sopenharmony_ci } else 15162306a36Sopenharmony_ci mci->aggr_limit = 6; 15262306a36Sopenharmony_ci ath_dbg(common, MCI, 15362306a36Sopenharmony_ci "Single SCO, aggregation limit %d 1/4 ms\n", 15462306a36Sopenharmony_ci mci->aggr_limit); 15562306a36Sopenharmony_ci } else if (mci->num_pan || mci->num_other_acl) { 15662306a36Sopenharmony_ci /* 15762306a36Sopenharmony_ci * For single PAN/FTP profile, allocate 35% for BT 15862306a36Sopenharmony_ci * to improve WLAN throughput. 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci btcoex->duty_cycle = AR_SREV_9565(sc->sc_ah) ? 40 : 35; 16162306a36Sopenharmony_ci btcoex->btcoex_period = 53; 16262306a36Sopenharmony_ci ath_dbg(common, MCI, 16362306a36Sopenharmony_ci "Single PAN/FTP bt period %d ms dutycycle %d\n", 16462306a36Sopenharmony_ci btcoex->duty_cycle, btcoex->btcoex_period); 16562306a36Sopenharmony_ci } else if (mci->num_hid) { 16662306a36Sopenharmony_ci btcoex->duty_cycle = 30; 16762306a36Sopenharmony_ci mci->aggr_limit = 6; 16862306a36Sopenharmony_ci ath_dbg(common, MCI, 16962306a36Sopenharmony_ci "Multiple attempt/timeout single HID " 17062306a36Sopenharmony_ci "aggregation limit 1.5 ms dutycycle 30%%\n"); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci } else if (num_profile == 2) { 17362306a36Sopenharmony_ci if (mci->num_hid == 2) 17462306a36Sopenharmony_ci btcoex->duty_cycle = 30; 17562306a36Sopenharmony_ci mci->aggr_limit = 6; 17662306a36Sopenharmony_ci ath_dbg(common, MCI, 17762306a36Sopenharmony_ci "Two BT profiles aggr limit 1.5 ms dutycycle %d%%\n", 17862306a36Sopenharmony_ci btcoex->duty_cycle); 17962306a36Sopenharmony_ci } else if (num_profile >= 3) { 18062306a36Sopenharmony_ci mci->aggr_limit = 4; 18162306a36Sopenharmony_ci ath_dbg(common, MCI, 18262306a36Sopenharmony_ci "Three or more profiles aggregation limit 1 ms\n"); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ciskip_tuning: 18662306a36Sopenharmony_ci if (IS_CHAN_2GHZ(sc->sc_ah->curchan)) { 18762306a36Sopenharmony_ci if (IS_CHAN_HT(sc->sc_ah->curchan)) 18862306a36Sopenharmony_ci ath_mci_adjust_aggr_limit(btcoex); 18962306a36Sopenharmony_ci else 19062306a36Sopenharmony_ci btcoex->btcoex_period >>= 1; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ath9k_btcoex_timer_pause(sc); 19462306a36Sopenharmony_ci ath9k_hw_btcoex_disable(sc->sc_ah); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (IS_CHAN_5GHZ(sc->sc_ah->curchan)) 19762306a36Sopenharmony_ci return; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci btcoex->duty_cycle += (mci->num_bdr ? ATH_MCI_BDR_DUTY_CYCLE : 0); 20062306a36Sopenharmony_ci if (btcoex->duty_cycle > ATH_MCI_MAX_DUTY_CYCLE) 20162306a36Sopenharmony_ci btcoex->duty_cycle = ATH_MCI_MAX_DUTY_CYCLE; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci btcoex->btcoex_no_stomp = btcoex->btcoex_period * 20462306a36Sopenharmony_ci (100 - btcoex->duty_cycle) / 100; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci ath9k_hw_btcoex_enable(sc->sc_ah); 20762306a36Sopenharmony_ci ath9k_btcoex_timer_resume(sc); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic void ath_mci_cal_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 21362306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 21462306a36Sopenharmony_ci struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; 21562306a36Sopenharmony_ci u32 payload[4] = {0, 0, 0, 0}; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci switch (opcode) { 21862306a36Sopenharmony_ci case MCI_GPM_BT_CAL_REQ: 21962306a36Sopenharmony_ci if (mci_hw->bt_state == MCI_BT_AWAKE) { 22062306a36Sopenharmony_ci mci_hw->bt_state = MCI_BT_CAL_START; 22162306a36Sopenharmony_ci ath9k_queue_reset(sc, RESET_TYPE_MCI); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci ath_dbg(common, MCI, "MCI State : %d\n", mci_hw->bt_state); 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci case MCI_GPM_BT_CAL_GRANT: 22662306a36Sopenharmony_ci MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_DONE); 22762306a36Sopenharmony_ci ar9003_mci_send_message(sc->sc_ah, MCI_GPM, 0, payload, 22862306a36Sopenharmony_ci 16, false, true); 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci default: 23162306a36Sopenharmony_ci ath_dbg(common, MCI, "Unknown GPM CAL message\n"); 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void ath9k_mci_work(struct work_struct *work) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct ath_softc *sc = container_of(work, struct ath_softc, mci_work); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci ath_mci_update_scheme(sc); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic void ath_mci_update_stomp_txprio(u8 cur_txprio, u8 *stomp_prio) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci if (cur_txprio < stomp_prio[ATH_BTCOEX_STOMP_NONE]) 24662306a36Sopenharmony_ci stomp_prio[ATH_BTCOEX_STOMP_NONE] = cur_txprio; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (cur_txprio > stomp_prio[ATH_BTCOEX_STOMP_ALL]) 24962306a36Sopenharmony_ci stomp_prio[ATH_BTCOEX_STOMP_ALL] = cur_txprio; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if ((cur_txprio > ATH_MCI_HI_PRIO) && 25262306a36Sopenharmony_ci (cur_txprio < stomp_prio[ATH_BTCOEX_STOMP_LOW])) 25362306a36Sopenharmony_ci stomp_prio[ATH_BTCOEX_STOMP_LOW] = cur_txprio; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic void ath_mci_set_concur_txprio(struct ath_softc *sc) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct ath_btcoex *btcoex = &sc->btcoex; 25962306a36Sopenharmony_ci struct ath_mci_profile *mci = &btcoex->mci; 26062306a36Sopenharmony_ci u8 stomp_txprio[ATH_BTCOEX_STOMP_MAX]; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci memset(stomp_txprio, 0, sizeof(stomp_txprio)); 26362306a36Sopenharmony_ci if (mci->num_mgmt) { 26462306a36Sopenharmony_ci stomp_txprio[ATH_BTCOEX_STOMP_ALL] = ATH_MCI_INQUIRY_PRIO; 26562306a36Sopenharmony_ci if (!mci->num_pan && !mci->num_other_acl) 26662306a36Sopenharmony_ci stomp_txprio[ATH_BTCOEX_STOMP_NONE] = 26762306a36Sopenharmony_ci ATH_MCI_INQUIRY_PRIO; 26862306a36Sopenharmony_ci } else { 26962306a36Sopenharmony_ci static const u8 prof_prio[] = { 27062306a36Sopenharmony_ci 50, 90, 94, 52 27162306a36Sopenharmony_ci }; /* RFCOMM, A2DP, HID, PAN */ 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci stomp_txprio[ATH_BTCOEX_STOMP_LOW] = 27462306a36Sopenharmony_ci stomp_txprio[ATH_BTCOEX_STOMP_NONE] = 0xff; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (mci->num_sco) 27762306a36Sopenharmony_ci ath_mci_update_stomp_txprio(mci->voice_priority, 27862306a36Sopenharmony_ci stomp_txprio); 27962306a36Sopenharmony_ci if (mci->num_other_acl) 28062306a36Sopenharmony_ci ath_mci_update_stomp_txprio(prof_prio[0], stomp_txprio); 28162306a36Sopenharmony_ci if (mci->num_a2dp) 28262306a36Sopenharmony_ci ath_mci_update_stomp_txprio(prof_prio[1], stomp_txprio); 28362306a36Sopenharmony_ci if (mci->num_hid) 28462306a36Sopenharmony_ci ath_mci_update_stomp_txprio(prof_prio[2], stomp_txprio); 28562306a36Sopenharmony_ci if (mci->num_pan) 28662306a36Sopenharmony_ci ath_mci_update_stomp_txprio(prof_prio[3], stomp_txprio); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (stomp_txprio[ATH_BTCOEX_STOMP_NONE] == 0xff) 28962306a36Sopenharmony_ci stomp_txprio[ATH_BTCOEX_STOMP_NONE] = 0; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (stomp_txprio[ATH_BTCOEX_STOMP_LOW] == 0xff) 29262306a36Sopenharmony_ci stomp_txprio[ATH_BTCOEX_STOMP_LOW] = 0; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci ath9k_hw_btcoex_set_concur_txprio(sc->sc_ah, stomp_txprio); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic u8 ath_mci_process_profile(struct ath_softc *sc, 29862306a36Sopenharmony_ci struct ath_mci_profile_info *info) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 30162306a36Sopenharmony_ci struct ath_btcoex *btcoex = &sc->btcoex; 30262306a36Sopenharmony_ci struct ath_mci_profile *mci = &btcoex->mci; 30362306a36Sopenharmony_ci struct ath_mci_profile_info *entry = NULL; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci entry = ath_mci_find_profile(mci, info); 30662306a36Sopenharmony_ci if (entry) { 30762306a36Sopenharmony_ci /* 30862306a36Sopenharmony_ci * Two MCI interrupts are generated while connecting to 30962306a36Sopenharmony_ci * headset and A2DP profile, but only one MCI interrupt 31062306a36Sopenharmony_ci * is generated with last added profile type while disconnecting 31162306a36Sopenharmony_ci * both profiles. 31262306a36Sopenharmony_ci * So while adding second profile type decrement 31362306a36Sopenharmony_ci * the first one. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci if (entry->type != info->type) { 31662306a36Sopenharmony_ci DEC_PROF(mci, entry); 31762306a36Sopenharmony_ci INC_PROF(mci, info); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci memcpy(entry, info, 10); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (info->start) { 32362306a36Sopenharmony_ci if (!entry && !ath_mci_add_profile(common, mci, info)) 32462306a36Sopenharmony_ci return 0; 32562306a36Sopenharmony_ci } else 32662306a36Sopenharmony_ci ath_mci_del_profile(common, mci, entry); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci ath_mci_set_concur_txprio(sc); 32962306a36Sopenharmony_ci return 1; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic u8 ath_mci_process_status(struct ath_softc *sc, 33362306a36Sopenharmony_ci struct ath_mci_profile_status *status) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct ath_btcoex *btcoex = &sc->btcoex; 33662306a36Sopenharmony_ci struct ath_mci_profile *mci = &btcoex->mci; 33762306a36Sopenharmony_ci struct ath_mci_profile_info info; 33862306a36Sopenharmony_ci int i = 0, old_num_mgmt = mci->num_mgmt; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* Link status type are not handled */ 34162306a36Sopenharmony_ci if (status->is_link) 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci info.conn_handle = status->conn_handle; 34562306a36Sopenharmony_ci if (ath_mci_find_profile(mci, &info)) 34662306a36Sopenharmony_ci return 0; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (status->conn_handle >= ATH_MCI_MAX_PROFILE) 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (status->is_critical) 35262306a36Sopenharmony_ci __set_bit(status->conn_handle, mci->status); 35362306a36Sopenharmony_ci else 35462306a36Sopenharmony_ci __clear_bit(status->conn_handle, mci->status); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci mci->num_mgmt = 0; 35762306a36Sopenharmony_ci do { 35862306a36Sopenharmony_ci if (test_bit(i, mci->status)) 35962306a36Sopenharmony_ci mci->num_mgmt++; 36062306a36Sopenharmony_ci } while (++i < ATH_MCI_MAX_PROFILE); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci ath_mci_set_concur_txprio(sc); 36362306a36Sopenharmony_ci if (old_num_mgmt != mci->num_mgmt) 36462306a36Sopenharmony_ci return 1; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return 0; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 37262306a36Sopenharmony_ci struct ath_mci_profile_info profile_info; 37362306a36Sopenharmony_ci struct ath_mci_profile_status profile_status; 37462306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 37562306a36Sopenharmony_ci u8 major, minor, update_scheme = 0; 37662306a36Sopenharmony_ci u32 seq_num; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (ar9003_mci_state(ah, MCI_STATE_NEED_FLUSH_BT_INFO) && 37962306a36Sopenharmony_ci ar9003_mci_state(ah, MCI_STATE_ENABLE)) { 38062306a36Sopenharmony_ci ath_dbg(common, MCI, "(MCI) Need to flush BT profiles\n"); 38162306a36Sopenharmony_ci ath_mci_flush_profile(&sc->btcoex.mci); 38262306a36Sopenharmony_ci ar9003_mci_state(ah, MCI_STATE_SEND_STATUS_QUERY); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci switch (opcode) { 38662306a36Sopenharmony_ci case MCI_GPM_COEX_VERSION_QUERY: 38762306a36Sopenharmony_ci ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_COEX_VERSION); 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci case MCI_GPM_COEX_VERSION_RESPONSE: 39062306a36Sopenharmony_ci major = *(rx_payload + MCI_GPM_COEX_B_MAJOR_VERSION); 39162306a36Sopenharmony_ci minor = *(rx_payload + MCI_GPM_COEX_B_MINOR_VERSION); 39262306a36Sopenharmony_ci ar9003_mci_set_bt_version(ah, major, minor); 39362306a36Sopenharmony_ci break; 39462306a36Sopenharmony_ci case MCI_GPM_COEX_STATUS_QUERY: 39562306a36Sopenharmony_ci ar9003_mci_send_wlan_channels(ah); 39662306a36Sopenharmony_ci break; 39762306a36Sopenharmony_ci case MCI_GPM_COEX_BT_PROFILE_INFO: 39862306a36Sopenharmony_ci memcpy(&profile_info, 39962306a36Sopenharmony_ci (rx_payload + MCI_GPM_COEX_B_PROFILE_TYPE), 10); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if ((profile_info.type == MCI_GPM_COEX_PROFILE_UNKNOWN) || 40262306a36Sopenharmony_ci (profile_info.type >= MCI_GPM_COEX_PROFILE_MAX)) { 40362306a36Sopenharmony_ci ath_dbg(common, MCI, 40462306a36Sopenharmony_ci "Illegal profile type = %d, state = %d\n", 40562306a36Sopenharmony_ci profile_info.type, 40662306a36Sopenharmony_ci profile_info.start); 40762306a36Sopenharmony_ci break; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci update_scheme += ath_mci_process_profile(sc, &profile_info); 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci case MCI_GPM_COEX_BT_STATUS_UPDATE: 41362306a36Sopenharmony_ci profile_status.is_link = *(rx_payload + 41462306a36Sopenharmony_ci MCI_GPM_COEX_B_STATUS_TYPE); 41562306a36Sopenharmony_ci profile_status.conn_handle = *(rx_payload + 41662306a36Sopenharmony_ci MCI_GPM_COEX_B_STATUS_LINKID); 41762306a36Sopenharmony_ci profile_status.is_critical = *(rx_payload + 41862306a36Sopenharmony_ci MCI_GPM_COEX_B_STATUS_STATE); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci seq_num = *((u32 *)(rx_payload + 12)); 42162306a36Sopenharmony_ci ath_dbg(common, MCI, 42262306a36Sopenharmony_ci "BT_Status_Update: is_link=%d, linkId=%d, state=%d, SEQ=%u\n", 42362306a36Sopenharmony_ci profile_status.is_link, profile_status.conn_handle, 42462306a36Sopenharmony_ci profile_status.is_critical, seq_num); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci update_scheme += ath_mci_process_status(sc, &profile_status); 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci default: 42962306a36Sopenharmony_ci ath_dbg(common, MCI, "Unknown GPM COEX message = 0x%02x\n", opcode); 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci if (update_scheme) 43362306a36Sopenharmony_ci ieee80211_queue_work(sc->hw, &sc->mci_work); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ciint ath_mci_setup(struct ath_softc *sc) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 43962306a36Sopenharmony_ci struct ath_mci_coex *mci = &sc->mci_coex; 44062306a36Sopenharmony_ci struct ath_mci_buf *buf = &mci->sched_buf; 44162306a36Sopenharmony_ci int ret; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci buf->bf_addr = dmam_alloc_coherent(sc->dev, 44462306a36Sopenharmony_ci ATH_MCI_SCHED_BUF_SIZE + ATH_MCI_GPM_BUF_SIZE, 44562306a36Sopenharmony_ci &buf->bf_paddr, GFP_KERNEL); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (buf->bf_addr == NULL) { 44862306a36Sopenharmony_ci ath_dbg(common, FATAL, "MCI buffer alloc failed\n"); 44962306a36Sopenharmony_ci return -ENOMEM; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci memset(buf->bf_addr, MCI_GPM_RSVD_PATTERN, 45362306a36Sopenharmony_ci ATH_MCI_SCHED_BUF_SIZE + ATH_MCI_GPM_BUF_SIZE); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci mci->sched_buf.bf_len = ATH_MCI_SCHED_BUF_SIZE; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci mci->gpm_buf.bf_len = ATH_MCI_GPM_BUF_SIZE; 45862306a36Sopenharmony_ci mci->gpm_buf.bf_addr = mci->sched_buf.bf_addr + mci->sched_buf.bf_len; 45962306a36Sopenharmony_ci mci->gpm_buf.bf_paddr = mci->sched_buf.bf_paddr + mci->sched_buf.bf_len; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci ret = ar9003_mci_setup(sc->sc_ah, mci->gpm_buf.bf_paddr, 46262306a36Sopenharmony_ci mci->gpm_buf.bf_addr, (mci->gpm_buf.bf_len >> 4), 46362306a36Sopenharmony_ci mci->sched_buf.bf_paddr); 46462306a36Sopenharmony_ci if (ret) { 46562306a36Sopenharmony_ci ath_err(common, "Failed to initialize MCI\n"); 46662306a36Sopenharmony_ci return ret; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci INIT_WORK(&sc->mci_work, ath9k_mci_work); 47062306a36Sopenharmony_ci ath_dbg(common, MCI, "MCI Initialized\n"); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_civoid ath_mci_cleanup(struct ath_softc *sc) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 47862306a36Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci ar9003_mci_cleanup(ah); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci ath_dbg(common, MCI, "MCI De-Initialized\n"); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_civoid ath_mci_intr(struct ath_softc *sc) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct ath_mci_coex *mci = &sc->mci_coex; 48862306a36Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 48962306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 49062306a36Sopenharmony_ci struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; 49162306a36Sopenharmony_ci u32 mci_int, mci_int_rxmsg; 49262306a36Sopenharmony_ci u32 offset, subtype, opcode; 49362306a36Sopenharmony_ci u32 *pgpm; 49462306a36Sopenharmony_ci u32 more_data = MCI_GPM_MORE; 49562306a36Sopenharmony_ci bool skip_gpm = false; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci ar9003_mci_get_interrupt(sc->sc_ah, &mci_int, &mci_int_rxmsg); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (ar9003_mci_state(ah, MCI_STATE_ENABLE) == 0) { 50062306a36Sopenharmony_ci ar9003_mci_state(ah, MCI_STATE_INIT_GPM_OFFSET); 50162306a36Sopenharmony_ci return; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE) { 50562306a36Sopenharmony_ci u32 payload[4] = { 0xffffffff, 0xffffffff, 50662306a36Sopenharmony_ci 0xffffffff, 0xffffff00}; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* 50962306a36Sopenharmony_ci * The following REMOTE_RESET and SYS_WAKING used to sent 51062306a36Sopenharmony_ci * only when BT wake up. Now they are always sent, as a 51162306a36Sopenharmony_ci * recovery method to reset BT MCI's RX alignment. 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_ci ar9003_mci_send_message(ah, MCI_REMOTE_RESET, 0, 51462306a36Sopenharmony_ci payload, 16, true, false); 51562306a36Sopenharmony_ci ar9003_mci_send_message(ah, MCI_SYS_WAKING, 0, 51662306a36Sopenharmony_ci NULL, 0, true, false); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE; 51962306a36Sopenharmony_ci ar9003_mci_state(ah, MCI_STATE_RESET_REQ_WAKE); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* 52262306a36Sopenharmony_ci * always do this for recovery and 2G/5G toggling and LNA_TRANS 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_ci ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE); 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING) { 52862306a36Sopenharmony_ci mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if ((mci_hw->bt_state == MCI_BT_SLEEP) && 53162306a36Sopenharmony_ci (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP) != 53262306a36Sopenharmony_ci MCI_BT_SLEEP)) 53362306a36Sopenharmony_ci ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE); 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) { 53762306a36Sopenharmony_ci mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if ((mci_hw->bt_state == MCI_BT_AWAKE) && 54062306a36Sopenharmony_ci (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP) != 54162306a36Sopenharmony_ci MCI_BT_AWAKE)) 54262306a36Sopenharmony_ci mci_hw->bt_state = MCI_BT_SLEEP; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if ((mci_int & AR_MCI_INTERRUPT_RX_INVALID_HDR) || 54662306a36Sopenharmony_ci (mci_int & AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) { 54762306a36Sopenharmony_ci ar9003_mci_state(ah, MCI_STATE_RECOVER_RX); 54862306a36Sopenharmony_ci skip_gpm = true; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO) { 55262306a36Sopenharmony_ci mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO; 55362306a36Sopenharmony_ci ar9003_mci_state(ah, MCI_STATE_LAST_SCHD_MSG_OFFSET); 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_GPM) { 55762306a36Sopenharmony_ci mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_GPM; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci while (more_data == MCI_GPM_MORE) { 56062306a36Sopenharmony_ci if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) 56162306a36Sopenharmony_ci return; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci pgpm = mci->gpm_buf.bf_addr; 56462306a36Sopenharmony_ci offset = ar9003_mci_get_next_gpm_offset(ah, &more_data); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (offset == MCI_GPM_INVALID) 56762306a36Sopenharmony_ci break; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci pgpm += (offset >> 2); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* 57262306a36Sopenharmony_ci * The first dword is timer. 57362306a36Sopenharmony_ci * The real data starts from 2nd dword. 57462306a36Sopenharmony_ci */ 57562306a36Sopenharmony_ci subtype = MCI_GPM_TYPE(pgpm); 57662306a36Sopenharmony_ci opcode = MCI_GPM_OPCODE(pgpm); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (skip_gpm) 57962306a36Sopenharmony_ci goto recycle; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (MCI_GPM_IS_CAL_TYPE(subtype)) { 58262306a36Sopenharmony_ci ath_mci_cal_msg(sc, subtype, (u8 *)pgpm); 58362306a36Sopenharmony_ci } else { 58462306a36Sopenharmony_ci switch (subtype) { 58562306a36Sopenharmony_ci case MCI_GPM_COEX_AGENT: 58662306a36Sopenharmony_ci ath_mci_msg(sc, opcode, (u8 *)pgpm); 58762306a36Sopenharmony_ci break; 58862306a36Sopenharmony_ci default: 58962306a36Sopenharmony_ci break; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci recycle: 59362306a36Sopenharmony_ci MCI_GPM_RECYCLE(pgpm); 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_HW_MSG_MASK) { 59862306a36Sopenharmony_ci if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL) 59962306a36Sopenharmony_ci mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_LNA_INFO) 60262306a36Sopenharmony_ci mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_LNA_INFO; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_INFO) { 60562306a36Sopenharmony_ci int value_dbm = MS(mci_hw->cont_status, 60662306a36Sopenharmony_ci AR_MCI_CONT_RSSI_POWER); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_CONT_INFO; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci ath_dbg(common, MCI, 61162306a36Sopenharmony_ci "MCI CONT_INFO: (%s) pri = %d pwr = %d dBm\n", 61262306a36Sopenharmony_ci MS(mci_hw->cont_status, AR_MCI_CONT_TXRX) ? 61362306a36Sopenharmony_ci "tx" : "rx", 61462306a36Sopenharmony_ci MS(mci_hw->cont_status, AR_MCI_CONT_PRIORITY), 61562306a36Sopenharmony_ci value_dbm); 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_NACK) 61962306a36Sopenharmony_ci mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_CONT_NACK; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_RST) 62262306a36Sopenharmony_ci mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_CONT_RST; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if ((mci_int & AR_MCI_INTERRUPT_RX_INVALID_HDR) || 62662306a36Sopenharmony_ci (mci_int & AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) { 62762306a36Sopenharmony_ci mci_int &= ~(AR_MCI_INTERRUPT_RX_INVALID_HDR | 62862306a36Sopenharmony_ci AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT); 62962306a36Sopenharmony_ci ath_mci_msg(sc, MCI_GPM_COEX_NOOP, NULL); 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_civoid ath_mci_enable(struct ath_softc *sc) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (!common->btcoex_enabled) 63862306a36Sopenharmony_ci return; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) 64162306a36Sopenharmony_ci sc->sc_ah->imask |= ATH9K_INT_MCI; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_civoid ath9k_mci_update_wlan_channels(struct ath_softc *sc, bool allow_all) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 64762306a36Sopenharmony_ci struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; 64862306a36Sopenharmony_ci struct ath9k_channel *chan = ah->curchan; 64962306a36Sopenharmony_ci u32 channelmap[] = {0x00000000, 0xffff0000, 0xffffffff, 0x7fffffff}; 65062306a36Sopenharmony_ci int i; 65162306a36Sopenharmony_ci s16 chan_start, chan_end; 65262306a36Sopenharmony_ci u16 wlan_chan; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (!chan || !IS_CHAN_2GHZ(chan)) 65562306a36Sopenharmony_ci return; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (allow_all) 65862306a36Sopenharmony_ci goto send_wlan_chan; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci wlan_chan = chan->channel - 2402; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci chan_start = wlan_chan - 10; 66362306a36Sopenharmony_ci chan_end = wlan_chan + 10; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (IS_CHAN_HT40PLUS(chan)) 66662306a36Sopenharmony_ci chan_end += 20; 66762306a36Sopenharmony_ci else if (IS_CHAN_HT40MINUS(chan)) 66862306a36Sopenharmony_ci chan_start -= 20; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* adjust side band */ 67162306a36Sopenharmony_ci chan_start -= 7; 67262306a36Sopenharmony_ci chan_end += 7; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (chan_start <= 0) 67562306a36Sopenharmony_ci chan_start = 0; 67662306a36Sopenharmony_ci if (chan_end >= ATH_MCI_NUM_BT_CHANNELS) 67762306a36Sopenharmony_ci chan_end = ATH_MCI_NUM_BT_CHANNELS - 1; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci ath_dbg(ath9k_hw_common(ah), MCI, 68062306a36Sopenharmony_ci "WLAN current channel %d mask BT channel %d - %d\n", 68162306a36Sopenharmony_ci wlan_chan, chan_start, chan_end); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci for (i = chan_start; i < chan_end; i++) 68462306a36Sopenharmony_ci MCI_GPM_CLR_CHANNEL_BIT(&channelmap, i); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cisend_wlan_chan: 68762306a36Sopenharmony_ci /* update and send wlan channels info to BT */ 68862306a36Sopenharmony_ci for (i = 0; i < 4; i++) 68962306a36Sopenharmony_ci mci->wlan_channels[i] = channelmap[i]; 69062306a36Sopenharmony_ci ar9003_mci_send_wlan_channels(ah); 69162306a36Sopenharmony_ci ar9003_mci_state(ah, MCI_STATE_SEND_VERSION_QUERY); 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_civoid ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel, 69562306a36Sopenharmony_ci bool concur_tx) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 69862306a36Sopenharmony_ci struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci; 69962306a36Sopenharmony_ci bool old_concur_tx = mci_hw->concur_tx; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (!(mci_hw->config & ATH_MCI_CONFIG_CONCUR_TX)) { 70262306a36Sopenharmony_ci mci_hw->concur_tx = false; 70362306a36Sopenharmony_ci return; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (!IS_CHAN_2GHZ(ah->curchan)) 70762306a36Sopenharmony_ci return; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (setchannel) { 71062306a36Sopenharmony_ci struct ath9k_hw_cal_data *caldata = &sc->cur_chan->caldata; 71162306a36Sopenharmony_ci if (IS_CHAN_HT40PLUS(ah->curchan) && 71262306a36Sopenharmony_ci (ah->curchan->channel > caldata->channel) && 71362306a36Sopenharmony_ci (ah->curchan->channel <= caldata->channel + 20)) 71462306a36Sopenharmony_ci return; 71562306a36Sopenharmony_ci if (IS_CHAN_HT40MINUS(ah->curchan) && 71662306a36Sopenharmony_ci (ah->curchan->channel < caldata->channel) && 71762306a36Sopenharmony_ci (ah->curchan->channel >= caldata->channel - 20)) 71862306a36Sopenharmony_ci return; 71962306a36Sopenharmony_ci mci_hw->concur_tx = false; 72062306a36Sopenharmony_ci } else 72162306a36Sopenharmony_ci mci_hw->concur_tx = concur_tx; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (old_concur_tx != mci_hw->concur_tx) 72462306a36Sopenharmony_ci ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false); 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic void ath9k_mci_stomp_audio(struct ath_softc *sc) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 73062306a36Sopenharmony_ci struct ath_btcoex *btcoex = &sc->btcoex; 73162306a36Sopenharmony_ci struct ath_mci_profile *mci = &btcoex->mci; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (!mci->num_sco && !mci->num_a2dp) 73462306a36Sopenharmony_ci return; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if (ah->stats.avgbrssi > 25) { 73762306a36Sopenharmony_ci btcoex->stomp_audio = 0; 73862306a36Sopenharmony_ci return; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci btcoex->stomp_audio++; 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_civoid ath9k_mci_update_rssi(struct ath_softc *sc) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 74662306a36Sopenharmony_ci struct ath_btcoex *btcoex = &sc->btcoex; 74762306a36Sopenharmony_ci struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci ath9k_mci_stomp_audio(sc); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci if (!(mci_hw->config & ATH_MCI_CONFIG_CONCUR_TX)) 75262306a36Sopenharmony_ci return; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (ah->stats.avgbrssi >= 40) { 75562306a36Sopenharmony_ci if (btcoex->rssi_count < 0) 75662306a36Sopenharmony_ci btcoex->rssi_count = 0; 75762306a36Sopenharmony_ci if (++btcoex->rssi_count >= ATH_MCI_CONCUR_TX_SWITCH) { 75862306a36Sopenharmony_ci btcoex->rssi_count = 0; 75962306a36Sopenharmony_ci ath9k_mci_set_txpower(sc, false, true); 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci } else { 76262306a36Sopenharmony_ci if (btcoex->rssi_count > 0) 76362306a36Sopenharmony_ci btcoex->rssi_count = 0; 76462306a36Sopenharmony_ci if (--btcoex->rssi_count <= -ATH_MCI_CONCUR_TX_SWITCH) { 76562306a36Sopenharmony_ci btcoex->rssi_count = 0; 76662306a36Sopenharmony_ci ath9k_mci_set_txpower(sc, false, false); 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci} 770