1*bfcc09ddSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2*bfcc09ddSBjoern A. Zeeb /* 3*bfcc09ddSBjoern A. Zeeb * Copyright (C) 2013-2014, 2018-2020 Intel Corporation 4*bfcc09ddSBjoern A. Zeeb * Copyright (C) 2013-2015 Intel Mobile Communications GmbH 5*bfcc09ddSBjoern A. Zeeb */ 6*bfcc09ddSBjoern A. Zeeb #include <linux/ieee80211.h> 7*bfcc09ddSBjoern A. Zeeb #include <linux/etherdevice.h> 8*bfcc09ddSBjoern A. Zeeb #include <net/mac80211.h> 9*bfcc09ddSBjoern A. Zeeb 10*bfcc09ddSBjoern A. Zeeb #include "fw/api/coex.h" 11*bfcc09ddSBjoern A. Zeeb #include "iwl-modparams.h" 12*bfcc09ddSBjoern A. Zeeb #include "mvm.h" 13*bfcc09ddSBjoern A. Zeeb #include "iwl-debug.h" 14*bfcc09ddSBjoern A. Zeeb 15*bfcc09ddSBjoern A. Zeeb /* 20MHz / 40MHz below / 40Mhz above*/ 16*bfcc09ddSBjoern A. Zeeb static const __le64 iwl_ci_mask[][3] = { 17*bfcc09ddSBjoern A. Zeeb /* dummy entry for channel 0 */ 18*bfcc09ddSBjoern A. Zeeb {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)}, 19*bfcc09ddSBjoern A. Zeeb { 20*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0000001FFFULL), 21*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0ULL), 22*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x00007FFFFFULL), 23*bfcc09ddSBjoern A. Zeeb }, 24*bfcc09ddSBjoern A. Zeeb { 25*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x000000FFFFULL), 26*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0ULL), 27*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0003FFFFFFULL), 28*bfcc09ddSBjoern A. Zeeb }, 29*bfcc09ddSBjoern A. Zeeb { 30*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x000003FFFCULL), 31*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0ULL), 32*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x000FFFFFFCULL), 33*bfcc09ddSBjoern A. Zeeb }, 34*bfcc09ddSBjoern A. Zeeb { 35*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x00001FFFE0ULL), 36*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0ULL), 37*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x007FFFFFE0ULL), 38*bfcc09ddSBjoern A. Zeeb }, 39*bfcc09ddSBjoern A. Zeeb { 40*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x00007FFF80ULL), 41*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x00007FFFFFULL), 42*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x01FFFFFF80ULL), 43*bfcc09ddSBjoern A. Zeeb }, 44*bfcc09ddSBjoern A. Zeeb { 45*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0003FFFC00ULL), 46*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0003FFFFFFULL), 47*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0FFFFFFC00ULL), 48*bfcc09ddSBjoern A. Zeeb }, 49*bfcc09ddSBjoern A. Zeeb { 50*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x000FFFF000ULL), 51*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x000FFFFFFCULL), 52*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x3FFFFFF000ULL), 53*bfcc09ddSBjoern A. Zeeb }, 54*bfcc09ddSBjoern A. Zeeb { 55*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x007FFF8000ULL), 56*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x007FFFFFE0ULL), 57*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0xFFFFFF8000ULL), 58*bfcc09ddSBjoern A. Zeeb }, 59*bfcc09ddSBjoern A. Zeeb { 60*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x01FFFE0000ULL), 61*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x01FFFFFF80ULL), 62*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0xFFFFFE0000ULL), 63*bfcc09ddSBjoern A. Zeeb }, 64*bfcc09ddSBjoern A. Zeeb { 65*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0FFFF00000ULL), 66*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0FFFFFFC00ULL), 67*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0ULL), 68*bfcc09ddSBjoern A. Zeeb }, 69*bfcc09ddSBjoern A. Zeeb { 70*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x3FFFC00000ULL), 71*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x3FFFFFF000ULL), 72*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0) 73*bfcc09ddSBjoern A. Zeeb }, 74*bfcc09ddSBjoern A. Zeeb { 75*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0xFFFE000000ULL), 76*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0xFFFFFF8000ULL), 77*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0) 78*bfcc09ddSBjoern A. Zeeb }, 79*bfcc09ddSBjoern A. Zeeb { 80*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0xFFF8000000ULL), 81*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0xFFFFFE0000ULL), 82*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0) 83*bfcc09ddSBjoern A. Zeeb }, 84*bfcc09ddSBjoern A. Zeeb { 85*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0xFE00000000ULL), 86*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0ULL), 87*bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0ULL) 88*bfcc09ddSBjoern A. Zeeb }, 89*bfcc09ddSBjoern A. Zeeb }; 90*bfcc09ddSBjoern A. Zeeb 91*bfcc09ddSBjoern A. Zeeb static enum iwl_bt_coex_lut_type 92*bfcc09ddSBjoern A. Zeeb iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) 93*bfcc09ddSBjoern A. Zeeb { 94*bfcc09ddSBjoern A. Zeeb struct ieee80211_chanctx_conf *chanctx_conf; 95*bfcc09ddSBjoern A. Zeeb enum iwl_bt_coex_lut_type ret; 96*bfcc09ddSBjoern A. Zeeb u16 phy_ctx_id; 97*bfcc09ddSBjoern A. Zeeb u32 primary_ch_phy_id, secondary_ch_phy_id; 98*bfcc09ddSBjoern A. Zeeb 99*bfcc09ddSBjoern A. Zeeb /* 100*bfcc09ddSBjoern A. Zeeb * Checking that we hold mvm->mutex is a good idea, but the rate 101*bfcc09ddSBjoern A. Zeeb * control can't acquire the mutex since it runs in Tx path. 102*bfcc09ddSBjoern A. Zeeb * So this is racy in that case, but in the worst case, the AMPDU 103*bfcc09ddSBjoern A. Zeeb * size limit will be wrong for a short time which is not a big 104*bfcc09ddSBjoern A. Zeeb * issue. 105*bfcc09ddSBjoern A. Zeeb */ 106*bfcc09ddSBjoern A. Zeeb 107*bfcc09ddSBjoern A. Zeeb rcu_read_lock(); 108*bfcc09ddSBjoern A. Zeeb 109*bfcc09ddSBjoern A. Zeeb chanctx_conf = rcu_dereference(vif->chanctx_conf); 110*bfcc09ddSBjoern A. Zeeb 111*bfcc09ddSBjoern A. Zeeb if (!chanctx_conf || 112*bfcc09ddSBjoern A. Zeeb chanctx_conf->def.chan->band != NL80211_BAND_2GHZ) { 113*bfcc09ddSBjoern A. Zeeb rcu_read_unlock(); 114*bfcc09ddSBjoern A. Zeeb return BT_COEX_INVALID_LUT; 115*bfcc09ddSBjoern A. Zeeb } 116*bfcc09ddSBjoern A. Zeeb 117*bfcc09ddSBjoern A. Zeeb ret = BT_COEX_TX_DIS_LUT; 118*bfcc09ddSBjoern A. Zeeb 119*bfcc09ddSBjoern A. Zeeb if (mvm->cfg->bt_shared_single_ant) { 120*bfcc09ddSBjoern A. Zeeb rcu_read_unlock(); 121*bfcc09ddSBjoern A. Zeeb return ret; 122*bfcc09ddSBjoern A. Zeeb } 123*bfcc09ddSBjoern A. Zeeb 124*bfcc09ddSBjoern A. Zeeb phy_ctx_id = *((u16 *)chanctx_conf->drv_priv); 125*bfcc09ddSBjoern A. Zeeb primary_ch_phy_id = le32_to_cpu(mvm->last_bt_ci_cmd.primary_ch_phy_id); 126*bfcc09ddSBjoern A. Zeeb secondary_ch_phy_id = 127*bfcc09ddSBjoern A. Zeeb le32_to_cpu(mvm->last_bt_ci_cmd.secondary_ch_phy_id); 128*bfcc09ddSBjoern A. Zeeb 129*bfcc09ddSBjoern A. Zeeb if (primary_ch_phy_id == phy_ctx_id) 130*bfcc09ddSBjoern A. Zeeb ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut); 131*bfcc09ddSBjoern A. Zeeb else if (secondary_ch_phy_id == phy_ctx_id) 132*bfcc09ddSBjoern A. Zeeb ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut); 133*bfcc09ddSBjoern A. Zeeb /* else - default = TX TX disallowed */ 134*bfcc09ddSBjoern A. Zeeb 135*bfcc09ddSBjoern A. Zeeb rcu_read_unlock(); 136*bfcc09ddSBjoern A. Zeeb 137*bfcc09ddSBjoern A. Zeeb return ret; 138*bfcc09ddSBjoern A. Zeeb } 139*bfcc09ddSBjoern A. Zeeb 140*bfcc09ddSBjoern A. Zeeb int iwl_mvm_send_bt_init_conf(struct iwl_mvm *mvm) 141*bfcc09ddSBjoern A. Zeeb { 142*bfcc09ddSBjoern A. Zeeb struct iwl_bt_coex_cmd bt_cmd = {}; 143*bfcc09ddSBjoern A. Zeeb u32 mode; 144*bfcc09ddSBjoern A. Zeeb 145*bfcc09ddSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 146*bfcc09ddSBjoern A. Zeeb 147*bfcc09ddSBjoern A. Zeeb if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) { 148*bfcc09ddSBjoern A. Zeeb switch (mvm->bt_force_ant_mode) { 149*bfcc09ddSBjoern A. Zeeb case BT_FORCE_ANT_BT: 150*bfcc09ddSBjoern A. Zeeb mode = BT_COEX_BT; 151*bfcc09ddSBjoern A. Zeeb break; 152*bfcc09ddSBjoern A. Zeeb case BT_FORCE_ANT_WIFI: 153*bfcc09ddSBjoern A. Zeeb mode = BT_COEX_WIFI; 154*bfcc09ddSBjoern A. Zeeb break; 155*bfcc09ddSBjoern A. Zeeb default: 156*bfcc09ddSBjoern A. Zeeb WARN_ON(1); 157*bfcc09ddSBjoern A. Zeeb mode = 0; 158*bfcc09ddSBjoern A. Zeeb } 159*bfcc09ddSBjoern A. Zeeb 160*bfcc09ddSBjoern A. Zeeb bt_cmd.mode = cpu_to_le32(mode); 161*bfcc09ddSBjoern A. Zeeb goto send_cmd; 162*bfcc09ddSBjoern A. Zeeb } 163*bfcc09ddSBjoern A. Zeeb 164*bfcc09ddSBjoern A. Zeeb bt_cmd.mode = cpu_to_le32(BT_COEX_NW); 165*bfcc09ddSBjoern A. Zeeb 166*bfcc09ddSBjoern A. Zeeb if (IWL_MVM_BT_COEX_SYNC2SCO) 167*bfcc09ddSBjoern A. Zeeb bt_cmd.enabled_modules |= 168*bfcc09ddSBjoern A. Zeeb cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED); 169*bfcc09ddSBjoern A. Zeeb 170*bfcc09ddSBjoern A. Zeeb if (iwl_mvm_is_mplut_supported(mvm)) 171*bfcc09ddSBjoern A. Zeeb bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED); 172*bfcc09ddSBjoern A. Zeeb 173*bfcc09ddSBjoern A. Zeeb bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET); 174*bfcc09ddSBjoern A. Zeeb 175*bfcc09ddSBjoern A. Zeeb send_cmd: 176*bfcc09ddSBjoern A. Zeeb memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); 177*bfcc09ddSBjoern A. Zeeb memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd)); 178*bfcc09ddSBjoern A. Zeeb 179*bfcc09ddSBjoern A. Zeeb return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, 0, sizeof(bt_cmd), &bt_cmd); 180*bfcc09ddSBjoern A. Zeeb } 181*bfcc09ddSBjoern A. Zeeb 182*bfcc09ddSBjoern A. Zeeb static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, 183*bfcc09ddSBjoern A. Zeeb bool enable) 184*bfcc09ddSBjoern A. Zeeb { 185*bfcc09ddSBjoern A. Zeeb struct iwl_bt_coex_reduced_txp_update_cmd cmd = {}; 186*bfcc09ddSBjoern A. Zeeb struct iwl_mvm_sta *mvmsta; 187*bfcc09ddSBjoern A. Zeeb u32 value; 188*bfcc09ddSBjoern A. Zeeb 189*bfcc09ddSBjoern A. Zeeb mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); 190*bfcc09ddSBjoern A. Zeeb if (!mvmsta) 191*bfcc09ddSBjoern A. Zeeb return 0; 192*bfcc09ddSBjoern A. Zeeb 193*bfcc09ddSBjoern A. Zeeb /* nothing to do */ 194*bfcc09ddSBjoern A. Zeeb if (mvmsta->bt_reduced_txpower == enable) 195*bfcc09ddSBjoern A. Zeeb return 0; 196*bfcc09ddSBjoern A. Zeeb 197*bfcc09ddSBjoern A. Zeeb value = mvmsta->sta_id; 198*bfcc09ddSBjoern A. Zeeb 199*bfcc09ddSBjoern A. Zeeb if (enable) 200*bfcc09ddSBjoern A. Zeeb value |= BT_REDUCED_TX_POWER_BIT; 201*bfcc09ddSBjoern A. Zeeb 202*bfcc09ddSBjoern A. Zeeb IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n", 203*bfcc09ddSBjoern A. Zeeb enable ? "en" : "dis", sta_id); 204*bfcc09ddSBjoern A. Zeeb 205*bfcc09ddSBjoern A. Zeeb cmd.reduced_txp = cpu_to_le32(value); 206*bfcc09ddSBjoern A. Zeeb mvmsta->bt_reduced_txpower = enable; 207*bfcc09ddSBjoern A. Zeeb 208*bfcc09ddSBjoern A. Zeeb return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_REDUCED_TXP, 209*bfcc09ddSBjoern A. Zeeb CMD_ASYNC, sizeof(cmd), &cmd); 210*bfcc09ddSBjoern A. Zeeb } 211*bfcc09ddSBjoern A. Zeeb 212*bfcc09ddSBjoern A. Zeeb struct iwl_bt_iterator_data { 213*bfcc09ddSBjoern A. Zeeb struct iwl_bt_coex_profile_notif *notif; 214*bfcc09ddSBjoern A. Zeeb struct iwl_mvm *mvm; 215*bfcc09ddSBjoern A. Zeeb struct ieee80211_chanctx_conf *primary; 216*bfcc09ddSBjoern A. Zeeb struct ieee80211_chanctx_conf *secondary; 217*bfcc09ddSBjoern A. Zeeb bool primary_ll; 218*bfcc09ddSBjoern A. Zeeb u8 primary_load; 219*bfcc09ddSBjoern A. Zeeb u8 secondary_load; 220*bfcc09ddSBjoern A. Zeeb }; 221*bfcc09ddSBjoern A. Zeeb 222*bfcc09ddSBjoern A. Zeeb static inline 223*bfcc09ddSBjoern A. Zeeb void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm, 224*bfcc09ddSBjoern A. Zeeb struct ieee80211_vif *vif, 225*bfcc09ddSBjoern A. Zeeb bool enable, int rssi) 226*bfcc09ddSBjoern A. Zeeb { 227*bfcc09ddSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 228*bfcc09ddSBjoern A. Zeeb 229*bfcc09ddSBjoern A. Zeeb mvmvif->bf_data.last_bt_coex_event = rssi; 230*bfcc09ddSBjoern A. Zeeb mvmvif->bf_data.bt_coex_max_thold = 231*bfcc09ddSBjoern A. Zeeb enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0; 232*bfcc09ddSBjoern A. Zeeb mvmvif->bf_data.bt_coex_min_thold = 233*bfcc09ddSBjoern A. Zeeb enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0; 234*bfcc09ddSBjoern A. Zeeb } 235*bfcc09ddSBjoern A. Zeeb 236*bfcc09ddSBjoern A. Zeeb #define MVM_COEX_TCM_PERIOD (HZ * 10) 237*bfcc09ddSBjoern A. Zeeb 238*bfcc09ddSBjoern A. Zeeb static void iwl_mvm_bt_coex_tcm_based_ci(struct iwl_mvm *mvm, 239*bfcc09ddSBjoern A. Zeeb struct iwl_bt_iterator_data *data) 240*bfcc09ddSBjoern A. Zeeb { 241*bfcc09ddSBjoern A. Zeeb unsigned long now = jiffies; 242*bfcc09ddSBjoern A. Zeeb 243*bfcc09ddSBjoern A. Zeeb if (!time_after(now, mvm->bt_coex_last_tcm_ts + MVM_COEX_TCM_PERIOD)) 244*bfcc09ddSBjoern A. Zeeb return; 245*bfcc09ddSBjoern A. Zeeb 246*bfcc09ddSBjoern A. Zeeb mvm->bt_coex_last_tcm_ts = now; 247*bfcc09ddSBjoern A. Zeeb 248*bfcc09ddSBjoern A. Zeeb /* We assume here that we don't have more than 2 vifs on 2.4GHz */ 249*bfcc09ddSBjoern A. Zeeb 250*bfcc09ddSBjoern A. Zeeb /* if the primary is low latency, it will stay primary */ 251*bfcc09ddSBjoern A. Zeeb if (data->primary_ll) 252*bfcc09ddSBjoern A. Zeeb return; 253*bfcc09ddSBjoern A. Zeeb 254*bfcc09ddSBjoern A. Zeeb if (data->primary_load >= data->secondary_load) 255*bfcc09ddSBjoern A. Zeeb return; 256*bfcc09ddSBjoern A. Zeeb 257*bfcc09ddSBjoern A. Zeeb swap(data->primary, data->secondary); 258*bfcc09ddSBjoern A. Zeeb } 259*bfcc09ddSBjoern A. Zeeb 260*bfcc09ddSBjoern A. Zeeb /* must be called under rcu_read_lock */ 261*bfcc09ddSBjoern A. Zeeb static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, 262*bfcc09ddSBjoern A. Zeeb struct ieee80211_vif *vif) 263*bfcc09ddSBjoern A. Zeeb { 264*bfcc09ddSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 265*bfcc09ddSBjoern A. Zeeb struct iwl_bt_iterator_data *data = _data; 266*bfcc09ddSBjoern A. Zeeb struct iwl_mvm *mvm = data->mvm; 267*bfcc09ddSBjoern A. Zeeb struct ieee80211_chanctx_conf *chanctx_conf; 268*bfcc09ddSBjoern A. Zeeb /* default smps_mode is AUTOMATIC - only used for client modes */ 269*bfcc09ddSBjoern A. Zeeb enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC; 270*bfcc09ddSBjoern A. Zeeb u32 bt_activity_grading, min_ag_for_static_smps; 271*bfcc09ddSBjoern A. Zeeb int ave_rssi; 272*bfcc09ddSBjoern A. Zeeb 273*bfcc09ddSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 274*bfcc09ddSBjoern A. Zeeb 275*bfcc09ddSBjoern A. Zeeb switch (vif->type) { 276*bfcc09ddSBjoern A. Zeeb case NL80211_IFTYPE_STATION: 277*bfcc09ddSBjoern A. Zeeb break; 278*bfcc09ddSBjoern A. Zeeb case NL80211_IFTYPE_AP: 279*bfcc09ddSBjoern A. Zeeb if (!mvmvif->ap_ibss_active) 280*bfcc09ddSBjoern A. Zeeb return; 281*bfcc09ddSBjoern A. Zeeb break; 282*bfcc09ddSBjoern A. Zeeb default: 283*bfcc09ddSBjoern A. Zeeb return; 284*bfcc09ddSBjoern A. Zeeb } 285*bfcc09ddSBjoern A. Zeeb 286*bfcc09ddSBjoern A. Zeeb chanctx_conf = rcu_dereference(vif->chanctx_conf); 287*bfcc09ddSBjoern A. Zeeb 288*bfcc09ddSBjoern A. Zeeb /* If channel context is invalid or not on 2.4GHz .. */ 289*bfcc09ddSBjoern A. Zeeb if ((!chanctx_conf || 290*bfcc09ddSBjoern A. Zeeb chanctx_conf->def.chan->band != NL80211_BAND_2GHZ)) { 291*bfcc09ddSBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_STATION) { 292*bfcc09ddSBjoern A. Zeeb /* ... relax constraints and disable rssi events */ 293*bfcc09ddSBjoern A. Zeeb iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, 294*bfcc09ddSBjoern A. Zeeb smps_mode); 295*bfcc09ddSBjoern A. Zeeb iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, 296*bfcc09ddSBjoern A. Zeeb false); 297*bfcc09ddSBjoern A. Zeeb iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); 298*bfcc09ddSBjoern A. Zeeb } 299*bfcc09ddSBjoern A. Zeeb return; 300*bfcc09ddSBjoern A. Zeeb } 301*bfcc09ddSBjoern A. Zeeb 302*bfcc09ddSBjoern A. Zeeb if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2)) 303*bfcc09ddSBjoern A. Zeeb min_ag_for_static_smps = BT_VERY_HIGH_TRAFFIC; 304*bfcc09ddSBjoern A. Zeeb else 305*bfcc09ddSBjoern A. Zeeb min_ag_for_static_smps = BT_HIGH_TRAFFIC; 306*bfcc09ddSBjoern A. Zeeb 307*bfcc09ddSBjoern A. Zeeb bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading); 308*bfcc09ddSBjoern A. Zeeb if (bt_activity_grading >= min_ag_for_static_smps) 309*bfcc09ddSBjoern A. Zeeb smps_mode = IEEE80211_SMPS_STATIC; 310*bfcc09ddSBjoern A. Zeeb else if (bt_activity_grading >= BT_LOW_TRAFFIC) 311*bfcc09ddSBjoern A. Zeeb smps_mode = IEEE80211_SMPS_DYNAMIC; 312*bfcc09ddSBjoern A. Zeeb 313*bfcc09ddSBjoern A. Zeeb /* relax SMPS constraints for next association */ 314*bfcc09ddSBjoern A. Zeeb if (!vif->bss_conf.assoc) 315*bfcc09ddSBjoern A. Zeeb smps_mode = IEEE80211_SMPS_AUTOMATIC; 316*bfcc09ddSBjoern A. Zeeb 317*bfcc09ddSBjoern A. Zeeb if (mvmvif->phy_ctxt && 318*bfcc09ddSBjoern A. Zeeb (mvm->last_bt_notif.rrc_status & BIT(mvmvif->phy_ctxt->id))) 319*bfcc09ddSBjoern A. Zeeb smps_mode = IEEE80211_SMPS_AUTOMATIC; 320*bfcc09ddSBjoern A. Zeeb 321*bfcc09ddSBjoern A. Zeeb IWL_DEBUG_COEX(data->mvm, 322*bfcc09ddSBjoern A. Zeeb "mac %d: bt_activity_grading %d smps_req %d\n", 323*bfcc09ddSBjoern A. Zeeb mvmvif->id, bt_activity_grading, smps_mode); 324*bfcc09ddSBjoern A. Zeeb 325*bfcc09ddSBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_STATION) 326*bfcc09ddSBjoern A. Zeeb iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, 327*bfcc09ddSBjoern A. Zeeb smps_mode); 328*bfcc09ddSBjoern A. Zeeb 329*bfcc09ddSBjoern A. Zeeb /* low latency is always primary */ 330*bfcc09ddSBjoern A. Zeeb if (iwl_mvm_vif_low_latency(mvmvif)) { 331*bfcc09ddSBjoern A. Zeeb data->primary_ll = true; 332*bfcc09ddSBjoern A. Zeeb 333*bfcc09ddSBjoern A. Zeeb data->secondary = data->primary; 334*bfcc09ddSBjoern A. Zeeb data->primary = chanctx_conf; 335*bfcc09ddSBjoern A. Zeeb } 336*bfcc09ddSBjoern A. Zeeb 337*bfcc09ddSBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_AP) { 338*bfcc09ddSBjoern A. Zeeb if (!mvmvif->ap_ibss_active) 339*bfcc09ddSBjoern A. Zeeb return; 340*bfcc09ddSBjoern A. Zeeb 341*bfcc09ddSBjoern A. Zeeb if (chanctx_conf == data->primary) 342*bfcc09ddSBjoern A. Zeeb return; 343*bfcc09ddSBjoern A. Zeeb 344*bfcc09ddSBjoern A. Zeeb if (!data->primary_ll) { 345*bfcc09ddSBjoern A. Zeeb /* 346*bfcc09ddSBjoern A. Zeeb * downgrade the current primary no matter what its 347*bfcc09ddSBjoern A. Zeeb * type is. 348*bfcc09ddSBjoern A. Zeeb */ 349*bfcc09ddSBjoern A. Zeeb data->secondary = data->primary; 350*bfcc09ddSBjoern A. Zeeb data->primary = chanctx_conf; 351*bfcc09ddSBjoern A. Zeeb } else { 352*bfcc09ddSBjoern A. Zeeb /* there is low latency vif - we will be secondary */ 353*bfcc09ddSBjoern A. Zeeb data->secondary = chanctx_conf; 354*bfcc09ddSBjoern A. Zeeb } 355*bfcc09ddSBjoern A. Zeeb 356*bfcc09ddSBjoern A. Zeeb if (data->primary == chanctx_conf) 357*bfcc09ddSBjoern A. Zeeb data->primary_load = mvm->tcm.result.load[mvmvif->id]; 358*bfcc09ddSBjoern A. Zeeb else if (data->secondary == chanctx_conf) 359*bfcc09ddSBjoern A. Zeeb data->secondary_load = mvm->tcm.result.load[mvmvif->id]; 360*bfcc09ddSBjoern A. Zeeb return; 361*bfcc09ddSBjoern A. Zeeb } 362*bfcc09ddSBjoern A. Zeeb 363*bfcc09ddSBjoern A. Zeeb /* 364*bfcc09ddSBjoern A. Zeeb * STA / P2P Client, try to be primary if first vif. If we are in low 365*bfcc09ddSBjoern A. Zeeb * latency mode, we are already in primary and just don't do much 366*bfcc09ddSBjoern A. Zeeb */ 367*bfcc09ddSBjoern A. Zeeb if (!data->primary || data->primary == chanctx_conf) 368*bfcc09ddSBjoern A. Zeeb data->primary = chanctx_conf; 369*bfcc09ddSBjoern A. Zeeb else if (!data->secondary) 370*bfcc09ddSBjoern A. Zeeb /* if secondary is not NULL, it might be a GO */ 371*bfcc09ddSBjoern A. Zeeb data->secondary = chanctx_conf; 372*bfcc09ddSBjoern A. Zeeb 373*bfcc09ddSBjoern A. Zeeb if (data->primary == chanctx_conf) 374*bfcc09ddSBjoern A. Zeeb data->primary_load = mvm->tcm.result.load[mvmvif->id]; 375*bfcc09ddSBjoern A. Zeeb else if (data->secondary == chanctx_conf) 376*bfcc09ddSBjoern A. Zeeb data->secondary_load = mvm->tcm.result.load[mvmvif->id]; 377*bfcc09ddSBjoern A. Zeeb /* 378*bfcc09ddSBjoern A. Zeeb * don't reduce the Tx power if one of these is true: 379*bfcc09ddSBjoern A. Zeeb * we are in LOOSE 380*bfcc09ddSBjoern A. Zeeb * single share antenna product 381*bfcc09ddSBjoern A. Zeeb * BT is inactive 382*bfcc09ddSBjoern A. Zeeb * we are not associated 383*bfcc09ddSBjoern A. Zeeb */ 384*bfcc09ddSBjoern A. Zeeb if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT || 385*bfcc09ddSBjoern A. Zeeb mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc || 386*bfcc09ddSBjoern A. Zeeb le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) { 387*bfcc09ddSBjoern A. Zeeb iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false); 388*bfcc09ddSBjoern A. Zeeb iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); 389*bfcc09ddSBjoern A. Zeeb return; 390*bfcc09ddSBjoern A. Zeeb } 391*bfcc09ddSBjoern A. Zeeb 392*bfcc09ddSBjoern A. Zeeb /* try to get the avg rssi from fw */ 393*bfcc09ddSBjoern A. Zeeb ave_rssi = mvmvif->bf_data.ave_beacon_signal; 394*bfcc09ddSBjoern A. Zeeb 395*bfcc09ddSBjoern A. Zeeb /* if the RSSI isn't valid, fake it is very low */ 396*bfcc09ddSBjoern A. Zeeb if (!ave_rssi) 397*bfcc09ddSBjoern A. Zeeb ave_rssi = -100; 398*bfcc09ddSBjoern A. Zeeb if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) { 399*bfcc09ddSBjoern A. Zeeb if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true)) 400*bfcc09ddSBjoern A. Zeeb IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); 401*bfcc09ddSBjoern A. Zeeb } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) { 402*bfcc09ddSBjoern A. Zeeb if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false)) 403*bfcc09ddSBjoern A. Zeeb IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); 404*bfcc09ddSBjoern A. Zeeb } 405*bfcc09ddSBjoern A. Zeeb 406*bfcc09ddSBjoern A. Zeeb /* Begin to monitor the RSSI: it may influence the reduced Tx power */ 407*bfcc09ddSBjoern A. Zeeb iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi); 408*bfcc09ddSBjoern A. Zeeb } 409*bfcc09ddSBjoern A. Zeeb 410*bfcc09ddSBjoern A. Zeeb static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) 411*bfcc09ddSBjoern A. Zeeb { 412*bfcc09ddSBjoern A. Zeeb struct iwl_bt_iterator_data data = { 413*bfcc09ddSBjoern A. Zeeb .mvm = mvm, 414*bfcc09ddSBjoern A. Zeeb .notif = &mvm->last_bt_notif, 415*bfcc09ddSBjoern A. Zeeb }; 416*bfcc09ddSBjoern A. Zeeb struct iwl_bt_coex_ci_cmd cmd = {}; 417*bfcc09ddSBjoern A. Zeeb u8 ci_bw_idx; 418*bfcc09ddSBjoern A. Zeeb 419*bfcc09ddSBjoern A. Zeeb /* Ignore updates if we are in force mode */ 420*bfcc09ddSBjoern A. Zeeb if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) 421*bfcc09ddSBjoern A. Zeeb return; 422*bfcc09ddSBjoern A. Zeeb 423*bfcc09ddSBjoern A. Zeeb rcu_read_lock(); 424*bfcc09ddSBjoern A. Zeeb ieee80211_iterate_active_interfaces_atomic( 425*bfcc09ddSBjoern A. Zeeb mvm->hw, IEEE80211_IFACE_ITER_NORMAL, 426*bfcc09ddSBjoern A. Zeeb iwl_mvm_bt_notif_iterator, &data); 427*bfcc09ddSBjoern A. Zeeb 428*bfcc09ddSBjoern A. Zeeb iwl_mvm_bt_coex_tcm_based_ci(mvm, &data); 429*bfcc09ddSBjoern A. Zeeb 430*bfcc09ddSBjoern A. Zeeb if (data.primary) { 431*bfcc09ddSBjoern A. Zeeb struct ieee80211_chanctx_conf *chan = data.primary; 432*bfcc09ddSBjoern A. Zeeb if (WARN_ON(!chan->def.chan)) { 433*bfcc09ddSBjoern A. Zeeb rcu_read_unlock(); 434*bfcc09ddSBjoern A. Zeeb return; 435*bfcc09ddSBjoern A. Zeeb } 436*bfcc09ddSBjoern A. Zeeb 437*bfcc09ddSBjoern A. Zeeb if (chan->def.width < NL80211_CHAN_WIDTH_40) { 438*bfcc09ddSBjoern A. Zeeb ci_bw_idx = 0; 439*bfcc09ddSBjoern A. Zeeb } else { 440*bfcc09ddSBjoern A. Zeeb if (chan->def.center_freq1 > 441*bfcc09ddSBjoern A. Zeeb chan->def.chan->center_freq) 442*bfcc09ddSBjoern A. Zeeb ci_bw_idx = 2; 443*bfcc09ddSBjoern A. Zeeb else 444*bfcc09ddSBjoern A. Zeeb ci_bw_idx = 1; 445*bfcc09ddSBjoern A. Zeeb } 446*bfcc09ddSBjoern A. Zeeb 447*bfcc09ddSBjoern A. Zeeb cmd.bt_primary_ci = 448*bfcc09ddSBjoern A. Zeeb iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; 449*bfcc09ddSBjoern A. Zeeb cmd.primary_ch_phy_id = 450*bfcc09ddSBjoern A. Zeeb cpu_to_le32(*((u16 *)data.primary->drv_priv)); 451*bfcc09ddSBjoern A. Zeeb } 452*bfcc09ddSBjoern A. Zeeb 453*bfcc09ddSBjoern A. Zeeb if (data.secondary) { 454*bfcc09ddSBjoern A. Zeeb struct ieee80211_chanctx_conf *chan = data.secondary; 455*bfcc09ddSBjoern A. Zeeb if (WARN_ON(!data.secondary->def.chan)) { 456*bfcc09ddSBjoern A. Zeeb rcu_read_unlock(); 457*bfcc09ddSBjoern A. Zeeb return; 458*bfcc09ddSBjoern A. Zeeb } 459*bfcc09ddSBjoern A. Zeeb 460*bfcc09ddSBjoern A. Zeeb if (chan->def.width < NL80211_CHAN_WIDTH_40) { 461*bfcc09ddSBjoern A. Zeeb ci_bw_idx = 0; 462*bfcc09ddSBjoern A. Zeeb } else { 463*bfcc09ddSBjoern A. Zeeb if (chan->def.center_freq1 > 464*bfcc09ddSBjoern A. Zeeb chan->def.chan->center_freq) 465*bfcc09ddSBjoern A. Zeeb ci_bw_idx = 2; 466*bfcc09ddSBjoern A. Zeeb else 467*bfcc09ddSBjoern A. Zeeb ci_bw_idx = 1; 468*bfcc09ddSBjoern A. Zeeb } 469*bfcc09ddSBjoern A. Zeeb 470*bfcc09ddSBjoern A. Zeeb cmd.bt_secondary_ci = 471*bfcc09ddSBjoern A. Zeeb iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; 472*bfcc09ddSBjoern A. Zeeb cmd.secondary_ch_phy_id = 473*bfcc09ddSBjoern A. Zeeb cpu_to_le32(*((u16 *)data.secondary->drv_priv)); 474*bfcc09ddSBjoern A. Zeeb } 475*bfcc09ddSBjoern A. Zeeb 476*bfcc09ddSBjoern A. Zeeb rcu_read_unlock(); 477*bfcc09ddSBjoern A. Zeeb 478*bfcc09ddSBjoern A. Zeeb /* Don't spam the fw with the same command over and over */ 479*bfcc09ddSBjoern A. Zeeb if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) { 480*bfcc09ddSBjoern A. Zeeb if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0, 481*bfcc09ddSBjoern A. Zeeb sizeof(cmd), &cmd)) 482*bfcc09ddSBjoern A. Zeeb IWL_ERR(mvm, "Failed to send BT_CI cmd\n"); 483*bfcc09ddSBjoern A. Zeeb memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd)); 484*bfcc09ddSBjoern A. Zeeb } 485*bfcc09ddSBjoern A. Zeeb } 486*bfcc09ddSBjoern A. Zeeb 487*bfcc09ddSBjoern A. Zeeb void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, 488*bfcc09ddSBjoern A. Zeeb struct iwl_rx_cmd_buffer *rxb) 489*bfcc09ddSBjoern A. Zeeb { 490*bfcc09ddSBjoern A. Zeeb struct iwl_rx_packet *pkt = rxb_addr(rxb); 491*bfcc09ddSBjoern A. Zeeb struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; 492*bfcc09ddSBjoern A. Zeeb 493*bfcc09ddSBjoern A. Zeeb IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); 494*bfcc09ddSBjoern A. Zeeb IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); 495*bfcc09ddSBjoern A. Zeeb IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n", 496*bfcc09ddSBjoern A. Zeeb le32_to_cpu(notif->primary_ch_lut)); 497*bfcc09ddSBjoern A. Zeeb IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n", 498*bfcc09ddSBjoern A. Zeeb le32_to_cpu(notif->secondary_ch_lut)); 499*bfcc09ddSBjoern A. Zeeb IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n", 500*bfcc09ddSBjoern A. Zeeb le32_to_cpu(notif->bt_activity_grading)); 501*bfcc09ddSBjoern A. Zeeb 502*bfcc09ddSBjoern A. Zeeb /* remember this notification for future use: rssi fluctuations */ 503*bfcc09ddSBjoern A. Zeeb memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif)); 504*bfcc09ddSBjoern A. Zeeb 505*bfcc09ddSBjoern A. Zeeb iwl_mvm_bt_coex_notif_handle(mvm); 506*bfcc09ddSBjoern A. Zeeb } 507*bfcc09ddSBjoern A. Zeeb 508*bfcc09ddSBjoern A. Zeeb void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 509*bfcc09ddSBjoern A. Zeeb enum ieee80211_rssi_event_data rssi_event) 510*bfcc09ddSBjoern A. Zeeb { 511*bfcc09ddSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 512*bfcc09ddSBjoern A. Zeeb int ret; 513*bfcc09ddSBjoern A. Zeeb 514*bfcc09ddSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 515*bfcc09ddSBjoern A. Zeeb 516*bfcc09ddSBjoern A. Zeeb /* Ignore updates if we are in force mode */ 517*bfcc09ddSBjoern A. Zeeb if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) 518*bfcc09ddSBjoern A. Zeeb return; 519*bfcc09ddSBjoern A. Zeeb 520*bfcc09ddSBjoern A. Zeeb /* 521*bfcc09ddSBjoern A. Zeeb * Rssi update while not associated - can happen since the statistics 522*bfcc09ddSBjoern A. Zeeb * are handled asynchronously 523*bfcc09ddSBjoern A. Zeeb */ 524*bfcc09ddSBjoern A. Zeeb if (mvmvif->ap_sta_id == IWL_MVM_INVALID_STA) 525*bfcc09ddSBjoern A. Zeeb return; 526*bfcc09ddSBjoern A. Zeeb 527*bfcc09ddSBjoern A. Zeeb /* No BT - reports should be disabled */ 528*bfcc09ddSBjoern A. Zeeb if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) 529*bfcc09ddSBjoern A. Zeeb return; 530*bfcc09ddSBjoern A. Zeeb 531*bfcc09ddSBjoern A. Zeeb IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid, 532*bfcc09ddSBjoern A. Zeeb rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW"); 533*bfcc09ddSBjoern A. Zeeb 534*bfcc09ddSBjoern A. Zeeb /* 535*bfcc09ddSBjoern A. Zeeb * Check if rssi is good enough for reduced Tx power, but not in loose 536*bfcc09ddSBjoern A. Zeeb * scheme. 537*bfcc09ddSBjoern A. Zeeb */ 538*bfcc09ddSBjoern A. Zeeb if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant || 539*bfcc09ddSBjoern A. Zeeb iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT) 540*bfcc09ddSBjoern A. Zeeb ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, 541*bfcc09ddSBjoern A. Zeeb false); 542*bfcc09ddSBjoern A. Zeeb else 543*bfcc09ddSBjoern A. Zeeb ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true); 544*bfcc09ddSBjoern A. Zeeb 545*bfcc09ddSBjoern A. Zeeb if (ret) 546*bfcc09ddSBjoern A. Zeeb IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n"); 547*bfcc09ddSBjoern A. Zeeb } 548*bfcc09ddSBjoern A. Zeeb 549*bfcc09ddSBjoern A. Zeeb #define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) 550*bfcc09ddSBjoern A. Zeeb #define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200) 551*bfcc09ddSBjoern A. Zeeb 552*bfcc09ddSBjoern A. Zeeb u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, 553*bfcc09ddSBjoern A. Zeeb struct ieee80211_sta *sta) 554*bfcc09ddSBjoern A. Zeeb { 555*bfcc09ddSBjoern A. Zeeb struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 556*bfcc09ddSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); 557*bfcc09ddSBjoern A. Zeeb struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; 558*bfcc09ddSBjoern A. Zeeb enum iwl_bt_coex_lut_type lut_type; 559*bfcc09ddSBjoern A. Zeeb 560*bfcc09ddSBjoern A. Zeeb if (mvm->last_bt_notif.ttc_status & BIT(phy_ctxt->id)) 561*bfcc09ddSBjoern A. Zeeb return LINK_QUAL_AGG_TIME_LIMIT_DEF; 562*bfcc09ddSBjoern A. Zeeb 563*bfcc09ddSBjoern A. Zeeb if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < 564*bfcc09ddSBjoern A. Zeeb BT_HIGH_TRAFFIC) 565*bfcc09ddSBjoern A. Zeeb return LINK_QUAL_AGG_TIME_LIMIT_DEF; 566*bfcc09ddSBjoern A. Zeeb 567*bfcc09ddSBjoern A. Zeeb lut_type = iwl_get_coex_type(mvm, mvmsta->vif); 568*bfcc09ddSBjoern A. Zeeb 569*bfcc09ddSBjoern A. Zeeb if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT) 570*bfcc09ddSBjoern A. Zeeb return LINK_QUAL_AGG_TIME_LIMIT_DEF; 571*bfcc09ddSBjoern A. Zeeb 572*bfcc09ddSBjoern A. Zeeb /* tight coex, high bt traffic, reduce AGG time limit */ 573*bfcc09ddSBjoern A. Zeeb return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT; 574*bfcc09ddSBjoern A. Zeeb } 575*bfcc09ddSBjoern A. Zeeb 576*bfcc09ddSBjoern A. Zeeb bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, 577*bfcc09ddSBjoern A. Zeeb struct ieee80211_sta *sta) 578*bfcc09ddSBjoern A. Zeeb { 579*bfcc09ddSBjoern A. Zeeb struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 580*bfcc09ddSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); 581*bfcc09ddSBjoern A. Zeeb struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; 582*bfcc09ddSBjoern A. Zeeb enum iwl_bt_coex_lut_type lut_type; 583*bfcc09ddSBjoern A. Zeeb 584*bfcc09ddSBjoern A. Zeeb if (mvm->last_bt_notif.ttc_status & BIT(phy_ctxt->id)) 585*bfcc09ddSBjoern A. Zeeb return true; 586*bfcc09ddSBjoern A. Zeeb 587*bfcc09ddSBjoern A. Zeeb if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < 588*bfcc09ddSBjoern A. Zeeb BT_HIGH_TRAFFIC) 589*bfcc09ddSBjoern A. Zeeb return true; 590*bfcc09ddSBjoern A. Zeeb 591*bfcc09ddSBjoern A. Zeeb /* 592*bfcc09ddSBjoern A. Zeeb * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas 593*bfcc09ddSBjoern A. Zeeb * since BT is already killed. 594*bfcc09ddSBjoern A. Zeeb * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while 595*bfcc09ddSBjoern A. Zeeb * we Tx. 596*bfcc09ddSBjoern A. Zeeb * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO. 597*bfcc09ddSBjoern A. Zeeb */ 598*bfcc09ddSBjoern A. Zeeb lut_type = iwl_get_coex_type(mvm, mvmsta->vif); 599*bfcc09ddSBjoern A. Zeeb return lut_type != BT_COEX_LOOSE_LUT; 600*bfcc09ddSBjoern A. Zeeb } 601*bfcc09ddSBjoern A. Zeeb 602*bfcc09ddSBjoern A. Zeeb bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant) 603*bfcc09ddSBjoern A. Zeeb { 604*bfcc09ddSBjoern A. Zeeb /* there is no other antenna, shared antenna is always available */ 605*bfcc09ddSBjoern A. Zeeb if (mvm->cfg->bt_shared_single_ant) 606*bfcc09ddSBjoern A. Zeeb return true; 607*bfcc09ddSBjoern A. Zeeb 608*bfcc09ddSBjoern A. Zeeb if (ant & mvm->cfg->non_shared_ant) 609*bfcc09ddSBjoern A. Zeeb return true; 610*bfcc09ddSBjoern A. Zeeb 611*bfcc09ddSBjoern A. Zeeb return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < 612*bfcc09ddSBjoern A. Zeeb BT_HIGH_TRAFFIC; 613*bfcc09ddSBjoern A. Zeeb } 614*bfcc09ddSBjoern A. Zeeb 615*bfcc09ddSBjoern A. Zeeb bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm) 616*bfcc09ddSBjoern A. Zeeb { 617*bfcc09ddSBjoern A. Zeeb /* there is no other antenna, shared antenna is always available */ 618*bfcc09ddSBjoern A. Zeeb if (mvm->cfg->bt_shared_single_ant) 619*bfcc09ddSBjoern A. Zeeb return true; 620*bfcc09ddSBjoern A. Zeeb 621*bfcc09ddSBjoern A. Zeeb return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC; 622*bfcc09ddSBjoern A. Zeeb } 623*bfcc09ddSBjoern A. Zeeb 624*bfcc09ddSBjoern A. Zeeb bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, 625*bfcc09ddSBjoern A. Zeeb enum nl80211_band band) 626*bfcc09ddSBjoern A. Zeeb { 627*bfcc09ddSBjoern A. Zeeb u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading); 628*bfcc09ddSBjoern A. Zeeb 629*bfcc09ddSBjoern A. Zeeb if (band != NL80211_BAND_2GHZ) 630*bfcc09ddSBjoern A. Zeeb return false; 631*bfcc09ddSBjoern A. Zeeb 632*bfcc09ddSBjoern A. Zeeb return bt_activity >= BT_LOW_TRAFFIC; 633*bfcc09ddSBjoern A. Zeeb } 634*bfcc09ddSBjoern A. Zeeb 635*bfcc09ddSBjoern A. Zeeb u8 iwl_mvm_bt_coex_get_single_ant_msk(struct iwl_mvm *mvm, u8 enabled_ants) 636*bfcc09ddSBjoern A. Zeeb { 637*bfcc09ddSBjoern A. Zeeb if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2) && 638*bfcc09ddSBjoern A. Zeeb (mvm->cfg->non_shared_ant & enabled_ants)) 639*bfcc09ddSBjoern A. Zeeb return mvm->cfg->non_shared_ant; 640*bfcc09ddSBjoern A. Zeeb 641*bfcc09ddSBjoern A. Zeeb return first_antenna(enabled_ants); 642*bfcc09ddSBjoern A. Zeeb } 643*bfcc09ddSBjoern A. Zeeb 644*bfcc09ddSBjoern A. Zeeb u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, 645*bfcc09ddSBjoern A. Zeeb struct ieee80211_tx_info *info, u8 ac) 646*bfcc09ddSBjoern A. Zeeb { 647*bfcc09ddSBjoern A. Zeeb __le16 fc = hdr->frame_control; 648*bfcc09ddSBjoern A. Zeeb bool mplut_enabled = iwl_mvm_is_mplut_supported(mvm); 649*bfcc09ddSBjoern A. Zeeb 650*bfcc09ddSBjoern A. Zeeb if (info->band != NL80211_BAND_2GHZ) 651*bfcc09ddSBjoern A. Zeeb return 0; 652*bfcc09ddSBjoern A. Zeeb 653*bfcc09ddSBjoern A. Zeeb if (unlikely(mvm->bt_tx_prio)) 654*bfcc09ddSBjoern A. Zeeb return mvm->bt_tx_prio - 1; 655*bfcc09ddSBjoern A. Zeeb 656*bfcc09ddSBjoern A. Zeeb if (likely(ieee80211_is_data(fc))) { 657*bfcc09ddSBjoern A. Zeeb if (likely(ieee80211_is_data_qos(fc))) { 658*bfcc09ddSBjoern A. Zeeb switch (ac) { 659*bfcc09ddSBjoern A. Zeeb case IEEE80211_AC_BE: 660*bfcc09ddSBjoern A. Zeeb return mplut_enabled ? 1 : 0; 661*bfcc09ddSBjoern A. Zeeb case IEEE80211_AC_VI: 662*bfcc09ddSBjoern A. Zeeb return mplut_enabled ? 2 : 3; 663*bfcc09ddSBjoern A. Zeeb case IEEE80211_AC_VO: 664*bfcc09ddSBjoern A. Zeeb return 3; 665*bfcc09ddSBjoern A. Zeeb default: 666*bfcc09ddSBjoern A. Zeeb return 0; 667*bfcc09ddSBjoern A. Zeeb } 668*bfcc09ddSBjoern A. Zeeb } else if (is_multicast_ether_addr(hdr->addr1)) { 669*bfcc09ddSBjoern A. Zeeb return 3; 670*bfcc09ddSBjoern A. Zeeb } else 671*bfcc09ddSBjoern A. Zeeb return 0; 672*bfcc09ddSBjoern A. Zeeb } else if (ieee80211_is_mgmt(fc)) { 673*bfcc09ddSBjoern A. Zeeb return ieee80211_is_disassoc(fc) ? 0 : 3; 674*bfcc09ddSBjoern A. Zeeb } else if (ieee80211_is_ctl(fc)) { 675*bfcc09ddSBjoern A. Zeeb /* ignore cfend and cfendack frames as we never send those */ 676*bfcc09ddSBjoern A. Zeeb return 3; 677*bfcc09ddSBjoern A. Zeeb } 678*bfcc09ddSBjoern A. Zeeb 679*bfcc09ddSBjoern A. Zeeb return 0; 680*bfcc09ddSBjoern A. Zeeb } 681*bfcc09ddSBjoern A. Zeeb 682*bfcc09ddSBjoern A. Zeeb void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm) 683*bfcc09ddSBjoern A. Zeeb { 684*bfcc09ddSBjoern A. Zeeb iwl_mvm_bt_coex_notif_handle(mvm); 685*bfcc09ddSBjoern A. Zeeb } 686