1bfcc09ddSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2bfcc09ddSBjoern A. Zeeb /* 3*a4128aadSBjoern A. Zeeb * Copyright (C) 2013-2014, 2018-2020, 2022-2024 Intel Corporation 4bfcc09ddSBjoern A. Zeeb * Copyright (C) 2013-2015 Intel Mobile Communications GmbH 5bfcc09ddSBjoern A. Zeeb */ 6bfcc09ddSBjoern A. Zeeb #include <linux/ieee80211.h> 7bfcc09ddSBjoern A. Zeeb #include <linux/etherdevice.h> 8bfcc09ddSBjoern A. Zeeb #include <net/mac80211.h> 9bfcc09ddSBjoern A. Zeeb 10bfcc09ddSBjoern A. Zeeb #include "fw/api/coex.h" 11bfcc09ddSBjoern A. Zeeb #include "iwl-modparams.h" 12bfcc09ddSBjoern A. Zeeb #include "mvm.h" 13bfcc09ddSBjoern A. Zeeb #include "iwl-debug.h" 14bfcc09ddSBjoern A. Zeeb 15bfcc09ddSBjoern A. Zeeb /* 20MHz / 40MHz below / 40Mhz above*/ 16bfcc09ddSBjoern A. Zeeb static const __le64 iwl_ci_mask[][3] = { 17bfcc09ddSBjoern A. Zeeb /* dummy entry for channel 0 */ 18bfcc09ddSBjoern A. Zeeb {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)}, 19bfcc09ddSBjoern A. Zeeb { 20bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0000001FFFULL), 21bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0ULL), 22bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x00007FFFFFULL), 23bfcc09ddSBjoern A. Zeeb }, 24bfcc09ddSBjoern A. Zeeb { 25bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x000000FFFFULL), 26bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0ULL), 27bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0003FFFFFFULL), 28bfcc09ddSBjoern A. Zeeb }, 29bfcc09ddSBjoern A. Zeeb { 30bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x000003FFFCULL), 31bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0ULL), 32bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x000FFFFFFCULL), 33bfcc09ddSBjoern A. Zeeb }, 34bfcc09ddSBjoern A. Zeeb { 35bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x00001FFFE0ULL), 36bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0ULL), 37bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x007FFFFFE0ULL), 38bfcc09ddSBjoern A. Zeeb }, 39bfcc09ddSBjoern A. Zeeb { 40bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x00007FFF80ULL), 41bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x00007FFFFFULL), 42bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x01FFFFFF80ULL), 43bfcc09ddSBjoern A. Zeeb }, 44bfcc09ddSBjoern A. Zeeb { 45bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0003FFFC00ULL), 46bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0003FFFFFFULL), 47bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0FFFFFFC00ULL), 48bfcc09ddSBjoern A. Zeeb }, 49bfcc09ddSBjoern A. Zeeb { 50bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x000FFFF000ULL), 51bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x000FFFFFFCULL), 52bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x3FFFFFF000ULL), 53bfcc09ddSBjoern A. Zeeb }, 54bfcc09ddSBjoern A. Zeeb { 55bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x007FFF8000ULL), 56bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x007FFFFFE0ULL), 57bfcc09ddSBjoern A. Zeeb cpu_to_le64(0xFFFFFF8000ULL), 58bfcc09ddSBjoern A. Zeeb }, 59bfcc09ddSBjoern A. Zeeb { 60bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x01FFFE0000ULL), 61bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x01FFFFFF80ULL), 62bfcc09ddSBjoern A. Zeeb cpu_to_le64(0xFFFFFE0000ULL), 63bfcc09ddSBjoern A. Zeeb }, 64bfcc09ddSBjoern A. Zeeb { 65bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0FFFF00000ULL), 66bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0FFFFFFC00ULL), 67bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0ULL), 68bfcc09ddSBjoern A. Zeeb }, 69bfcc09ddSBjoern A. Zeeb { 70bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x3FFFC00000ULL), 71bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x3FFFFFF000ULL), 72bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0) 73bfcc09ddSBjoern A. Zeeb }, 74bfcc09ddSBjoern A. Zeeb { 75bfcc09ddSBjoern A. Zeeb cpu_to_le64(0xFFFE000000ULL), 76bfcc09ddSBjoern A. Zeeb cpu_to_le64(0xFFFFFF8000ULL), 77bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0) 78bfcc09ddSBjoern A. Zeeb }, 79bfcc09ddSBjoern A. Zeeb { 80bfcc09ddSBjoern A. Zeeb cpu_to_le64(0xFFF8000000ULL), 81bfcc09ddSBjoern A. Zeeb cpu_to_le64(0xFFFFFE0000ULL), 82bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0) 83bfcc09ddSBjoern A. Zeeb }, 84bfcc09ddSBjoern A. Zeeb { 85bfcc09ddSBjoern A. Zeeb cpu_to_le64(0xFE00000000ULL), 86bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0ULL), 87bfcc09ddSBjoern A. Zeeb cpu_to_le64(0x0ULL) 88bfcc09ddSBjoern A. Zeeb }, 89bfcc09ddSBjoern A. Zeeb }; 90bfcc09ddSBjoern A. Zeeb 91bfcc09ddSBjoern A. Zeeb static enum iwl_bt_coex_lut_type 92bfcc09ddSBjoern A. Zeeb iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) 93bfcc09ddSBjoern A. Zeeb { 94bfcc09ddSBjoern A. Zeeb struct ieee80211_chanctx_conf *chanctx_conf; 95bfcc09ddSBjoern A. Zeeb enum iwl_bt_coex_lut_type ret; 96bfcc09ddSBjoern A. Zeeb u16 phy_ctx_id; 97bfcc09ddSBjoern A. Zeeb u32 primary_ch_phy_id, secondary_ch_phy_id; 98bfcc09ddSBjoern A. Zeeb 99bfcc09ddSBjoern A. Zeeb /* 100bfcc09ddSBjoern A. Zeeb * Checking that we hold mvm->mutex is a good idea, but the rate 101bfcc09ddSBjoern A. Zeeb * control can't acquire the mutex since it runs in Tx path. 102bfcc09ddSBjoern A. Zeeb * So this is racy in that case, but in the worst case, the AMPDU 103bfcc09ddSBjoern A. Zeeb * size limit will be wrong for a short time which is not a big 104bfcc09ddSBjoern A. Zeeb * issue. 105bfcc09ddSBjoern A. Zeeb */ 106bfcc09ddSBjoern A. Zeeb 107bfcc09ddSBjoern A. Zeeb rcu_read_lock(); 108bfcc09ddSBjoern A. Zeeb 1099af1bba4SBjoern A. Zeeb chanctx_conf = rcu_dereference(vif->bss_conf.chanctx_conf); 110bfcc09ddSBjoern A. Zeeb 111bfcc09ddSBjoern A. Zeeb if (!chanctx_conf || 112bfcc09ddSBjoern A. Zeeb chanctx_conf->def.chan->band != NL80211_BAND_2GHZ) { 113bfcc09ddSBjoern A. Zeeb rcu_read_unlock(); 114bfcc09ddSBjoern A. Zeeb return BT_COEX_INVALID_LUT; 115bfcc09ddSBjoern A. Zeeb } 116bfcc09ddSBjoern A. Zeeb 117bfcc09ddSBjoern A. Zeeb ret = BT_COEX_TX_DIS_LUT; 118bfcc09ddSBjoern A. Zeeb 119bfcc09ddSBjoern A. Zeeb phy_ctx_id = *((u16 *)chanctx_conf->drv_priv); 120bfcc09ddSBjoern A. Zeeb primary_ch_phy_id = le32_to_cpu(mvm->last_bt_ci_cmd.primary_ch_phy_id); 121bfcc09ddSBjoern A. Zeeb secondary_ch_phy_id = 122bfcc09ddSBjoern A. Zeeb le32_to_cpu(mvm->last_bt_ci_cmd.secondary_ch_phy_id); 123bfcc09ddSBjoern A. Zeeb 124bfcc09ddSBjoern A. Zeeb if (primary_ch_phy_id == phy_ctx_id) 125bfcc09ddSBjoern A. Zeeb ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut); 126bfcc09ddSBjoern A. Zeeb else if (secondary_ch_phy_id == phy_ctx_id) 127bfcc09ddSBjoern A. Zeeb ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut); 128bfcc09ddSBjoern A. Zeeb /* else - default = TX TX disallowed */ 129bfcc09ddSBjoern A. Zeeb 130bfcc09ddSBjoern A. Zeeb rcu_read_unlock(); 131bfcc09ddSBjoern A. Zeeb 132bfcc09ddSBjoern A. Zeeb return ret; 133bfcc09ddSBjoern A. Zeeb } 134bfcc09ddSBjoern A. Zeeb 135bfcc09ddSBjoern A. Zeeb int iwl_mvm_send_bt_init_conf(struct iwl_mvm *mvm) 136bfcc09ddSBjoern A. Zeeb { 137bfcc09ddSBjoern A. Zeeb struct iwl_bt_coex_cmd bt_cmd = {}; 138bfcc09ddSBjoern A. Zeeb u32 mode; 139bfcc09ddSBjoern A. Zeeb 140bfcc09ddSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 141bfcc09ddSBjoern A. Zeeb 142bfcc09ddSBjoern A. Zeeb if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) { 143bfcc09ddSBjoern A. Zeeb switch (mvm->bt_force_ant_mode) { 144bfcc09ddSBjoern A. Zeeb case BT_FORCE_ANT_BT: 145bfcc09ddSBjoern A. Zeeb mode = BT_COEX_BT; 146bfcc09ddSBjoern A. Zeeb break; 147bfcc09ddSBjoern A. Zeeb case BT_FORCE_ANT_WIFI: 148bfcc09ddSBjoern A. Zeeb mode = BT_COEX_WIFI; 149bfcc09ddSBjoern A. Zeeb break; 150bfcc09ddSBjoern A. Zeeb default: 151bfcc09ddSBjoern A. Zeeb WARN_ON(1); 152bfcc09ddSBjoern A. Zeeb mode = 0; 153bfcc09ddSBjoern A. Zeeb } 154bfcc09ddSBjoern A. Zeeb 155bfcc09ddSBjoern A. Zeeb bt_cmd.mode = cpu_to_le32(mode); 156bfcc09ddSBjoern A. Zeeb goto send_cmd; 157bfcc09ddSBjoern A. Zeeb } 158bfcc09ddSBjoern A. Zeeb 159bfcc09ddSBjoern A. Zeeb bt_cmd.mode = cpu_to_le32(BT_COEX_NW); 160bfcc09ddSBjoern A. Zeeb 161bfcc09ddSBjoern A. Zeeb if (IWL_MVM_BT_COEX_SYNC2SCO) 162bfcc09ddSBjoern A. Zeeb bt_cmd.enabled_modules |= 163bfcc09ddSBjoern A. Zeeb cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED); 164bfcc09ddSBjoern A. Zeeb 165bfcc09ddSBjoern A. Zeeb if (iwl_mvm_is_mplut_supported(mvm)) 166bfcc09ddSBjoern A. Zeeb bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED); 167bfcc09ddSBjoern A. Zeeb 168bfcc09ddSBjoern A. Zeeb bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET); 169bfcc09ddSBjoern A. Zeeb 170bfcc09ddSBjoern A. Zeeb send_cmd: 171bfcc09ddSBjoern A. Zeeb memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); 172bfcc09ddSBjoern A. Zeeb memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd)); 173bfcc09ddSBjoern A. Zeeb 174bfcc09ddSBjoern A. Zeeb return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, 0, sizeof(bt_cmd), &bt_cmd); 175bfcc09ddSBjoern A. Zeeb } 176bfcc09ddSBjoern A. Zeeb 177bfcc09ddSBjoern A. Zeeb static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, 178bfcc09ddSBjoern A. Zeeb bool enable) 179bfcc09ddSBjoern A. Zeeb { 180bfcc09ddSBjoern A. Zeeb struct iwl_bt_coex_reduced_txp_update_cmd cmd = {}; 181bfcc09ddSBjoern A. Zeeb struct iwl_mvm_sta *mvmsta; 182bfcc09ddSBjoern A. Zeeb u32 value; 183bfcc09ddSBjoern A. Zeeb 184*a4128aadSBjoern A. Zeeb if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) 185*a4128aadSBjoern A. Zeeb return 0; 186*a4128aadSBjoern A. Zeeb 187bfcc09ddSBjoern A. Zeeb mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); 188bfcc09ddSBjoern A. Zeeb if (!mvmsta) 189bfcc09ddSBjoern A. Zeeb return 0; 190bfcc09ddSBjoern A. Zeeb 191bfcc09ddSBjoern A. Zeeb /* nothing to do */ 192bfcc09ddSBjoern A. Zeeb if (mvmsta->bt_reduced_txpower == enable) 193bfcc09ddSBjoern A. Zeeb return 0; 194bfcc09ddSBjoern A. Zeeb 1959af1bba4SBjoern A. Zeeb value = mvmsta->deflink.sta_id; 196bfcc09ddSBjoern A. Zeeb 197bfcc09ddSBjoern A. Zeeb if (enable) 198bfcc09ddSBjoern A. Zeeb value |= BT_REDUCED_TX_POWER_BIT; 199bfcc09ddSBjoern A. Zeeb 200bfcc09ddSBjoern A. Zeeb IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n", 201bfcc09ddSBjoern A. Zeeb enable ? "en" : "dis", sta_id); 202bfcc09ddSBjoern A. Zeeb 203bfcc09ddSBjoern A. Zeeb cmd.reduced_txp = cpu_to_le32(value); 204bfcc09ddSBjoern A. Zeeb mvmsta->bt_reduced_txpower = enable; 205bfcc09ddSBjoern A. Zeeb 206bfcc09ddSBjoern A. Zeeb return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_REDUCED_TXP, 207bfcc09ddSBjoern A. Zeeb CMD_ASYNC, sizeof(cmd), &cmd); 208bfcc09ddSBjoern A. Zeeb } 209bfcc09ddSBjoern A. Zeeb 210bfcc09ddSBjoern A. Zeeb struct iwl_bt_iterator_data { 211bfcc09ddSBjoern A. Zeeb struct iwl_bt_coex_profile_notif *notif; 212bfcc09ddSBjoern A. Zeeb struct iwl_mvm *mvm; 213bfcc09ddSBjoern A. Zeeb struct ieee80211_chanctx_conf *primary; 214bfcc09ddSBjoern A. Zeeb struct ieee80211_chanctx_conf *secondary; 215bfcc09ddSBjoern A. Zeeb bool primary_ll; 216bfcc09ddSBjoern A. Zeeb u8 primary_load; 217bfcc09ddSBjoern A. Zeeb u8 secondary_load; 218bfcc09ddSBjoern A. Zeeb }; 219bfcc09ddSBjoern A. Zeeb 220bfcc09ddSBjoern A. Zeeb static inline 221bfcc09ddSBjoern A. Zeeb void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm, 222*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif_link_info *link_info, 223bfcc09ddSBjoern A. Zeeb bool enable, int rssi) 224bfcc09ddSBjoern A. Zeeb { 225*a4128aadSBjoern A. Zeeb link_info->bf_data.last_bt_coex_event = rssi; 226*a4128aadSBjoern A. Zeeb link_info->bf_data.bt_coex_max_thold = 227bfcc09ddSBjoern A. Zeeb enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0; 228*a4128aadSBjoern A. Zeeb link_info->bf_data.bt_coex_min_thold = 229bfcc09ddSBjoern A. Zeeb enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0; 230bfcc09ddSBjoern A. Zeeb } 231bfcc09ddSBjoern A. Zeeb 232bfcc09ddSBjoern A. Zeeb #define MVM_COEX_TCM_PERIOD (HZ * 10) 233bfcc09ddSBjoern A. Zeeb 234bfcc09ddSBjoern A. Zeeb static void iwl_mvm_bt_coex_tcm_based_ci(struct iwl_mvm *mvm, 235bfcc09ddSBjoern A. Zeeb struct iwl_bt_iterator_data *data) 236bfcc09ddSBjoern A. Zeeb { 237bfcc09ddSBjoern A. Zeeb unsigned long now = jiffies; 238bfcc09ddSBjoern A. Zeeb 239bfcc09ddSBjoern A. Zeeb if (!time_after(now, mvm->bt_coex_last_tcm_ts + MVM_COEX_TCM_PERIOD)) 240bfcc09ddSBjoern A. Zeeb return; 241bfcc09ddSBjoern A. Zeeb 242bfcc09ddSBjoern A. Zeeb mvm->bt_coex_last_tcm_ts = now; 243bfcc09ddSBjoern A. Zeeb 244bfcc09ddSBjoern A. Zeeb /* We assume here that we don't have more than 2 vifs on 2.4GHz */ 245bfcc09ddSBjoern A. Zeeb 246bfcc09ddSBjoern A. Zeeb /* if the primary is low latency, it will stay primary */ 247bfcc09ddSBjoern A. Zeeb if (data->primary_ll) 248bfcc09ddSBjoern A. Zeeb return; 249bfcc09ddSBjoern A. Zeeb 250bfcc09ddSBjoern A. Zeeb if (data->primary_load >= data->secondary_load) 251bfcc09ddSBjoern A. Zeeb return; 252bfcc09ddSBjoern A. Zeeb 253bfcc09ddSBjoern A. Zeeb swap(data->primary, data->secondary); 254bfcc09ddSBjoern A. Zeeb } 255bfcc09ddSBjoern A. Zeeb 256*a4128aadSBjoern A. Zeeb /* 257*a4128aadSBjoern A. Zeeb * This function receives the LB link id and checks if eSR should be 258*a4128aadSBjoern A. Zeeb * enabled or disabled (due to BT coex) 259*a4128aadSBjoern A. Zeeb */ 260*a4128aadSBjoern A. Zeeb bool 261*a4128aadSBjoern A. Zeeb iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm, 262*a4128aadSBjoern A. Zeeb struct ieee80211_vif *vif, 263*a4128aadSBjoern A. Zeeb s32 link_rssi, 264*a4128aadSBjoern A. Zeeb bool primary) 265*a4128aadSBjoern A. Zeeb { 266*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 267*a4128aadSBjoern A. Zeeb bool have_wifi_loss_rate = 268*a4128aadSBjoern A. Zeeb iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, 269*a4128aadSBjoern A. Zeeb BT_PROFILE_NOTIFICATION, 0) > 4; 270*a4128aadSBjoern A. Zeeb u8 wifi_loss_rate; 271*a4128aadSBjoern A. Zeeb 272*a4128aadSBjoern A. Zeeb if (mvm->last_bt_notif.wifi_loss_low_rssi == BT_OFF) 273*a4128aadSBjoern A. Zeeb return true; 274*a4128aadSBjoern A. Zeeb 275*a4128aadSBjoern A. Zeeb if (primary) 276*a4128aadSBjoern A. Zeeb return false; 277*a4128aadSBjoern A. Zeeb 278*a4128aadSBjoern A. Zeeb /* The feature is not supported */ 279*a4128aadSBjoern A. Zeeb if (!have_wifi_loss_rate) 280*a4128aadSBjoern A. Zeeb return true; 281*a4128aadSBjoern A. Zeeb 282*a4128aadSBjoern A. Zeeb 283*a4128aadSBjoern A. Zeeb /* 284*a4128aadSBjoern A. Zeeb * In case we don't know the RSSI - take the lower wifi loss, 285*a4128aadSBjoern A. Zeeb * so we will more likely enter eSR, and if RSSI is low - 286*a4128aadSBjoern A. Zeeb * we will get an update on this and exit eSR. 287*a4128aadSBjoern A. Zeeb */ 288*a4128aadSBjoern A. Zeeb if (!link_rssi) 289*a4128aadSBjoern A. Zeeb wifi_loss_rate = mvm->last_bt_notif.wifi_loss_mid_high_rssi; 290*a4128aadSBjoern A. Zeeb 291*a4128aadSBjoern A. Zeeb else if (mvmvif->esr_active) 292*a4128aadSBjoern A. Zeeb /* RSSI needs to get really low to disable eSR... */ 293*a4128aadSBjoern A. Zeeb wifi_loss_rate = 294*a4128aadSBjoern A. Zeeb link_rssi <= -IWL_MVM_BT_COEX_DISABLE_ESR_THRESH ? 295*a4128aadSBjoern A. Zeeb mvm->last_bt_notif.wifi_loss_low_rssi : 296*a4128aadSBjoern A. Zeeb mvm->last_bt_notif.wifi_loss_mid_high_rssi; 297*a4128aadSBjoern A. Zeeb else 298*a4128aadSBjoern A. Zeeb /* ...And really high before we enable it back */ 299*a4128aadSBjoern A. Zeeb wifi_loss_rate = 300*a4128aadSBjoern A. Zeeb link_rssi <= -IWL_MVM_BT_COEX_ENABLE_ESR_THRESH ? 301*a4128aadSBjoern A. Zeeb mvm->last_bt_notif.wifi_loss_low_rssi : 302*a4128aadSBjoern A. Zeeb mvm->last_bt_notif.wifi_loss_mid_high_rssi; 303*a4128aadSBjoern A. Zeeb 304*a4128aadSBjoern A. Zeeb return wifi_loss_rate <= IWL_MVM_BT_COEX_WIFI_LOSS_THRESH; 305*a4128aadSBjoern A. Zeeb } 306*a4128aadSBjoern A. Zeeb 307*a4128aadSBjoern A. Zeeb void iwl_mvm_bt_coex_update_link_esr(struct iwl_mvm *mvm, 308*a4128aadSBjoern A. Zeeb struct ieee80211_vif *vif, 309*a4128aadSBjoern A. Zeeb int link_id) 310*a4128aadSBjoern A. Zeeb { 311*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 312*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif_link_info *link = mvmvif->link[link_id]; 313*a4128aadSBjoern A. Zeeb 314*a4128aadSBjoern A. Zeeb if (!ieee80211_vif_is_mld(vif) || 315*a4128aadSBjoern A. Zeeb !iwl_mvm_vif_from_mac80211(vif)->authorized || 316*a4128aadSBjoern A. Zeeb WARN_ON(!link)) 317*a4128aadSBjoern A. Zeeb return; 318*a4128aadSBjoern A. Zeeb 319*a4128aadSBjoern A. Zeeb if (!iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, 320*a4128aadSBjoern A. Zeeb (s8)link->beacon_stats.avg_signal, 321*a4128aadSBjoern A. Zeeb link_id == iwl_mvm_get_primary_link(vif))) 322*a4128aadSBjoern A. Zeeb /* In case we decided to exit eSR - stay with the primary */ 323*a4128aadSBjoern A. Zeeb iwl_mvm_exit_esr(mvm, vif, IWL_MVM_ESR_EXIT_COEX, 324*a4128aadSBjoern A. Zeeb iwl_mvm_get_primary_link(vif)); 325*a4128aadSBjoern A. Zeeb } 326*a4128aadSBjoern A. Zeeb 3279af1bba4SBjoern A. Zeeb static void iwl_mvm_bt_notif_per_link(struct iwl_mvm *mvm, 3289af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif, 3299af1bba4SBjoern A. Zeeb struct iwl_bt_iterator_data *data, 3309af1bba4SBjoern A. Zeeb unsigned int link_id) 331bfcc09ddSBjoern A. Zeeb { 332bfcc09ddSBjoern A. Zeeb /* default smps_mode is AUTOMATIC - only used for client modes */ 333bfcc09ddSBjoern A. Zeeb enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC; 3349af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 335bfcc09ddSBjoern A. Zeeb u32 bt_activity_grading, min_ag_for_static_smps; 3369af1bba4SBjoern A. Zeeb struct ieee80211_chanctx_conf *chanctx_conf; 3379af1bba4SBjoern A. Zeeb struct iwl_mvm_vif_link_info *link_info; 3389af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf; 339bfcc09ddSBjoern A. Zeeb int ave_rssi; 340bfcc09ddSBjoern A. Zeeb 341bfcc09ddSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 342bfcc09ddSBjoern A. Zeeb 3439af1bba4SBjoern A. Zeeb link_info = mvmvif->link[link_id]; 3449af1bba4SBjoern A. Zeeb if (!link_info) 345bfcc09ddSBjoern A. Zeeb return; 346bfcc09ddSBjoern A. Zeeb 3479af1bba4SBjoern A. Zeeb link_conf = rcu_dereference(vif->link_conf[link_id]); 3489af1bba4SBjoern A. Zeeb /* This can happen due to races: if we receive the notification 3499af1bba4SBjoern A. Zeeb * and have the mutex held, while mac80211 is stuck on our mutex 3509af1bba4SBjoern A. Zeeb * in the middle of removing the link. 3519af1bba4SBjoern A. Zeeb */ 3529af1bba4SBjoern A. Zeeb if (!link_conf) 3539af1bba4SBjoern A. Zeeb return; 3549af1bba4SBjoern A. Zeeb 3559af1bba4SBjoern A. Zeeb chanctx_conf = rcu_dereference(link_conf->chanctx_conf); 356bfcc09ddSBjoern A. Zeeb 357bfcc09ddSBjoern A. Zeeb /* If channel context is invalid or not on 2.4GHz .. */ 358bfcc09ddSBjoern A. Zeeb if ((!chanctx_conf || 359bfcc09ddSBjoern A. Zeeb chanctx_conf->def.chan->band != NL80211_BAND_2GHZ)) { 360bfcc09ddSBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_STATION) { 361bfcc09ddSBjoern A. Zeeb /* ... relax constraints and disable rssi events */ 362bfcc09ddSBjoern A. Zeeb iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, 3639af1bba4SBjoern A. Zeeb smps_mode, link_id); 3649af1bba4SBjoern A. Zeeb iwl_mvm_bt_coex_reduced_txp(mvm, link_info->ap_sta_id, 365bfcc09ddSBjoern A. Zeeb false); 366*a4128aadSBjoern A. Zeeb iwl_mvm_bt_coex_enable_rssi_event(mvm, link_info, false, 367*a4128aadSBjoern A. Zeeb 0); 368bfcc09ddSBjoern A. Zeeb } 369bfcc09ddSBjoern A. Zeeb return; 370bfcc09ddSBjoern A. Zeeb } 371bfcc09ddSBjoern A. Zeeb 372*a4128aadSBjoern A. Zeeb iwl_mvm_bt_coex_update_link_esr(mvm, vif, link_id); 373*a4128aadSBjoern A. Zeeb 374bfcc09ddSBjoern A. Zeeb if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2)) 375bfcc09ddSBjoern A. Zeeb min_ag_for_static_smps = BT_VERY_HIGH_TRAFFIC; 376bfcc09ddSBjoern A. Zeeb else 377bfcc09ddSBjoern A. Zeeb min_ag_for_static_smps = BT_HIGH_TRAFFIC; 378bfcc09ddSBjoern A. Zeeb 379bfcc09ddSBjoern A. Zeeb bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading); 380bfcc09ddSBjoern A. Zeeb if (bt_activity_grading >= min_ag_for_static_smps) 381bfcc09ddSBjoern A. Zeeb smps_mode = IEEE80211_SMPS_STATIC; 382bfcc09ddSBjoern A. Zeeb else if (bt_activity_grading >= BT_LOW_TRAFFIC) 383bfcc09ddSBjoern A. Zeeb smps_mode = IEEE80211_SMPS_DYNAMIC; 384bfcc09ddSBjoern A. Zeeb 385bfcc09ddSBjoern A. Zeeb /* relax SMPS constraints for next association */ 3869af1bba4SBjoern A. Zeeb if (!vif->cfg.assoc) 387bfcc09ddSBjoern A. Zeeb smps_mode = IEEE80211_SMPS_AUTOMATIC; 388bfcc09ddSBjoern A. Zeeb 3899af1bba4SBjoern A. Zeeb if (link_info->phy_ctxt && 3909af1bba4SBjoern A. Zeeb (mvm->last_bt_notif.rrc_status & BIT(link_info->phy_ctxt->id))) 391bfcc09ddSBjoern A. Zeeb smps_mode = IEEE80211_SMPS_AUTOMATIC; 392bfcc09ddSBjoern A. Zeeb 393bfcc09ddSBjoern A. Zeeb IWL_DEBUG_COEX(data->mvm, 3949af1bba4SBjoern A. Zeeb "mac %d link %d: bt_activity_grading %d smps_req %d\n", 3959af1bba4SBjoern A. Zeeb mvmvif->id, link_info->fw_link_id, 3969af1bba4SBjoern A. Zeeb bt_activity_grading, smps_mode); 397bfcc09ddSBjoern A. Zeeb 398bfcc09ddSBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_STATION) 399bfcc09ddSBjoern A. Zeeb iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, 4009af1bba4SBjoern A. Zeeb smps_mode, link_id); 401bfcc09ddSBjoern A. Zeeb 402bfcc09ddSBjoern A. Zeeb /* low latency is always primary */ 403bfcc09ddSBjoern A. Zeeb if (iwl_mvm_vif_low_latency(mvmvif)) { 404bfcc09ddSBjoern A. Zeeb data->primary_ll = true; 405bfcc09ddSBjoern A. Zeeb 406bfcc09ddSBjoern A. Zeeb data->secondary = data->primary; 407bfcc09ddSBjoern A. Zeeb data->primary = chanctx_conf; 408bfcc09ddSBjoern A. Zeeb } 409bfcc09ddSBjoern A. Zeeb 410bfcc09ddSBjoern A. Zeeb if (vif->type == NL80211_IFTYPE_AP) { 411bfcc09ddSBjoern A. Zeeb if (!mvmvif->ap_ibss_active) 412bfcc09ddSBjoern A. Zeeb return; 413bfcc09ddSBjoern A. Zeeb 414bfcc09ddSBjoern A. Zeeb if (chanctx_conf == data->primary) 415bfcc09ddSBjoern A. Zeeb return; 416bfcc09ddSBjoern A. Zeeb 417bfcc09ddSBjoern A. Zeeb if (!data->primary_ll) { 418bfcc09ddSBjoern A. Zeeb /* 419bfcc09ddSBjoern A. Zeeb * downgrade the current primary no matter what its 420bfcc09ddSBjoern A. Zeeb * type is. 421bfcc09ddSBjoern A. Zeeb */ 422bfcc09ddSBjoern A. Zeeb data->secondary = data->primary; 423bfcc09ddSBjoern A. Zeeb data->primary = chanctx_conf; 424bfcc09ddSBjoern A. Zeeb } else { 425bfcc09ddSBjoern A. Zeeb /* there is low latency vif - we will be secondary */ 426bfcc09ddSBjoern A. Zeeb data->secondary = chanctx_conf; 427bfcc09ddSBjoern A. Zeeb } 428bfcc09ddSBjoern A. Zeeb 4299af1bba4SBjoern A. Zeeb /* FIXME: TCM load per interface? or need something per link? */ 430bfcc09ddSBjoern A. Zeeb if (data->primary == chanctx_conf) 431bfcc09ddSBjoern A. Zeeb data->primary_load = mvm->tcm.result.load[mvmvif->id]; 432bfcc09ddSBjoern A. Zeeb else if (data->secondary == chanctx_conf) 433bfcc09ddSBjoern A. Zeeb data->secondary_load = mvm->tcm.result.load[mvmvif->id]; 434bfcc09ddSBjoern A. Zeeb return; 435bfcc09ddSBjoern A. Zeeb } 436bfcc09ddSBjoern A. Zeeb 437bfcc09ddSBjoern A. Zeeb /* 438bfcc09ddSBjoern A. Zeeb * STA / P2P Client, try to be primary if first vif. If we are in low 439bfcc09ddSBjoern A. Zeeb * latency mode, we are already in primary and just don't do much 440bfcc09ddSBjoern A. Zeeb */ 441bfcc09ddSBjoern A. Zeeb if (!data->primary || data->primary == chanctx_conf) 442bfcc09ddSBjoern A. Zeeb data->primary = chanctx_conf; 443bfcc09ddSBjoern A. Zeeb else if (!data->secondary) 444bfcc09ddSBjoern A. Zeeb /* if secondary is not NULL, it might be a GO */ 445bfcc09ddSBjoern A. Zeeb data->secondary = chanctx_conf; 446bfcc09ddSBjoern A. Zeeb 4479af1bba4SBjoern A. Zeeb /* FIXME: TCM load per interface? or need something per link? */ 448bfcc09ddSBjoern A. Zeeb if (data->primary == chanctx_conf) 449bfcc09ddSBjoern A. Zeeb data->primary_load = mvm->tcm.result.load[mvmvif->id]; 450bfcc09ddSBjoern A. Zeeb else if (data->secondary == chanctx_conf) 451bfcc09ddSBjoern A. Zeeb data->secondary_load = mvm->tcm.result.load[mvmvif->id]; 452bfcc09ddSBjoern A. Zeeb /* 453bfcc09ddSBjoern A. Zeeb * don't reduce the Tx power if one of these is true: 454bfcc09ddSBjoern A. Zeeb * we are in LOOSE 455bfcc09ddSBjoern A. Zeeb * BT is inactive 456bfcc09ddSBjoern A. Zeeb * we are not associated 457bfcc09ddSBjoern A. Zeeb */ 458bfcc09ddSBjoern A. Zeeb if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT || 459*a4128aadSBjoern A. Zeeb le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF || 460*a4128aadSBjoern A. Zeeb !vif->cfg.assoc) { 4619af1bba4SBjoern A. Zeeb iwl_mvm_bt_coex_reduced_txp(mvm, link_info->ap_sta_id, false); 462*a4128aadSBjoern A. Zeeb iwl_mvm_bt_coex_enable_rssi_event(mvm, link_info, false, 0); 463bfcc09ddSBjoern A. Zeeb return; 464bfcc09ddSBjoern A. Zeeb } 465bfcc09ddSBjoern A. Zeeb 466bfcc09ddSBjoern A. Zeeb /* try to get the avg rssi from fw */ 467*a4128aadSBjoern A. Zeeb ave_rssi = link_info->bf_data.ave_beacon_signal; 468bfcc09ddSBjoern A. Zeeb 469bfcc09ddSBjoern A. Zeeb /* if the RSSI isn't valid, fake it is very low */ 470bfcc09ddSBjoern A. Zeeb if (!ave_rssi) 471bfcc09ddSBjoern A. Zeeb ave_rssi = -100; 472bfcc09ddSBjoern A. Zeeb if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) { 4739af1bba4SBjoern A. Zeeb if (iwl_mvm_bt_coex_reduced_txp(mvm, link_info->ap_sta_id, 4749af1bba4SBjoern A. Zeeb true)) 475bfcc09ddSBjoern A. Zeeb IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); 476bfcc09ddSBjoern A. Zeeb } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) { 4779af1bba4SBjoern A. Zeeb if (iwl_mvm_bt_coex_reduced_txp(mvm, link_info->ap_sta_id, 4789af1bba4SBjoern A. Zeeb false)) 479bfcc09ddSBjoern A. Zeeb IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); 480bfcc09ddSBjoern A. Zeeb } 481bfcc09ddSBjoern A. Zeeb 482bfcc09ddSBjoern A. Zeeb /* Begin to monitor the RSSI: it may influence the reduced Tx power */ 483*a4128aadSBjoern A. Zeeb iwl_mvm_bt_coex_enable_rssi_event(mvm, link_info, true, ave_rssi); 484bfcc09ddSBjoern A. Zeeb } 485bfcc09ddSBjoern A. Zeeb 4869af1bba4SBjoern A. Zeeb /* must be called under rcu_read_lock */ 4879af1bba4SBjoern A. Zeeb static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, 4889af1bba4SBjoern A. Zeeb struct ieee80211_vif *vif) 4899af1bba4SBjoern A. Zeeb { 4909af1bba4SBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 4919af1bba4SBjoern A. Zeeb struct iwl_bt_iterator_data *data = _data; 4929af1bba4SBjoern A. Zeeb struct iwl_mvm *mvm = data->mvm; 4939af1bba4SBjoern A. Zeeb unsigned int link_id; 4949af1bba4SBjoern A. Zeeb 4959af1bba4SBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 4969af1bba4SBjoern A. Zeeb 4979af1bba4SBjoern A. Zeeb switch (vif->type) { 4989af1bba4SBjoern A. Zeeb case NL80211_IFTYPE_STATION: 4999af1bba4SBjoern A. Zeeb break; 5009af1bba4SBjoern A. Zeeb case NL80211_IFTYPE_AP: 5019af1bba4SBjoern A. Zeeb if (!mvmvif->ap_ibss_active) 5029af1bba4SBjoern A. Zeeb return; 5039af1bba4SBjoern A. Zeeb break; 5049af1bba4SBjoern A. Zeeb default: 5059af1bba4SBjoern A. Zeeb return; 5069af1bba4SBjoern A. Zeeb } 5079af1bba4SBjoern A. Zeeb 5089af1bba4SBjoern A. Zeeb for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) 5099af1bba4SBjoern A. Zeeb iwl_mvm_bt_notif_per_link(mvm, vif, data, link_id); 5109af1bba4SBjoern A. Zeeb } 5119af1bba4SBjoern A. Zeeb 512bfcc09ddSBjoern A. Zeeb static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) 513bfcc09ddSBjoern A. Zeeb { 514bfcc09ddSBjoern A. Zeeb struct iwl_bt_iterator_data data = { 515bfcc09ddSBjoern A. Zeeb .mvm = mvm, 516bfcc09ddSBjoern A. Zeeb .notif = &mvm->last_bt_notif, 517bfcc09ddSBjoern A. Zeeb }; 518bfcc09ddSBjoern A. Zeeb struct iwl_bt_coex_ci_cmd cmd = {}; 519bfcc09ddSBjoern A. Zeeb u8 ci_bw_idx; 520bfcc09ddSBjoern A. Zeeb 521bfcc09ddSBjoern A. Zeeb /* Ignore updates if we are in force mode */ 522bfcc09ddSBjoern A. Zeeb if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) 523bfcc09ddSBjoern A. Zeeb return; 524bfcc09ddSBjoern A. Zeeb 525bfcc09ddSBjoern A. Zeeb rcu_read_lock(); 526bfcc09ddSBjoern A. Zeeb ieee80211_iterate_active_interfaces_atomic( 527bfcc09ddSBjoern A. Zeeb mvm->hw, IEEE80211_IFACE_ITER_NORMAL, 528bfcc09ddSBjoern A. Zeeb iwl_mvm_bt_notif_iterator, &data); 529bfcc09ddSBjoern A. Zeeb 530*a4128aadSBjoern A. Zeeb if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { 531*a4128aadSBjoern A. Zeeb rcu_read_unlock(); 532*a4128aadSBjoern A. Zeeb return; 533*a4128aadSBjoern A. Zeeb } 534*a4128aadSBjoern A. Zeeb 535bfcc09ddSBjoern A. Zeeb iwl_mvm_bt_coex_tcm_based_ci(mvm, &data); 536bfcc09ddSBjoern A. Zeeb 537bfcc09ddSBjoern A. Zeeb if (data.primary) { 538bfcc09ddSBjoern A. Zeeb struct ieee80211_chanctx_conf *chan = data.primary; 539bfcc09ddSBjoern A. Zeeb if (WARN_ON(!chan->def.chan)) { 540bfcc09ddSBjoern A. Zeeb rcu_read_unlock(); 541bfcc09ddSBjoern A. Zeeb return; 542bfcc09ddSBjoern A. Zeeb } 543bfcc09ddSBjoern A. Zeeb 544bfcc09ddSBjoern A. Zeeb if (chan->def.width < NL80211_CHAN_WIDTH_40) { 545bfcc09ddSBjoern A. Zeeb ci_bw_idx = 0; 546bfcc09ddSBjoern A. Zeeb } else { 547bfcc09ddSBjoern A. Zeeb if (chan->def.center_freq1 > 548bfcc09ddSBjoern A. Zeeb chan->def.chan->center_freq) 549bfcc09ddSBjoern A. Zeeb ci_bw_idx = 2; 550bfcc09ddSBjoern A. Zeeb else 551bfcc09ddSBjoern A. Zeeb ci_bw_idx = 1; 552bfcc09ddSBjoern A. Zeeb } 553bfcc09ddSBjoern A. Zeeb 554bfcc09ddSBjoern A. Zeeb cmd.bt_primary_ci = 555bfcc09ddSBjoern A. Zeeb iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; 556bfcc09ddSBjoern A. Zeeb cmd.primary_ch_phy_id = 557bfcc09ddSBjoern A. Zeeb cpu_to_le32(*((u16 *)data.primary->drv_priv)); 558bfcc09ddSBjoern A. Zeeb } 559bfcc09ddSBjoern A. Zeeb 560bfcc09ddSBjoern A. Zeeb if (data.secondary) { 561bfcc09ddSBjoern A. Zeeb struct ieee80211_chanctx_conf *chan = data.secondary; 562bfcc09ddSBjoern A. Zeeb if (WARN_ON(!data.secondary->def.chan)) { 563bfcc09ddSBjoern A. Zeeb rcu_read_unlock(); 564bfcc09ddSBjoern A. Zeeb return; 565bfcc09ddSBjoern A. Zeeb } 566bfcc09ddSBjoern A. Zeeb 567bfcc09ddSBjoern A. Zeeb if (chan->def.width < NL80211_CHAN_WIDTH_40) { 568bfcc09ddSBjoern A. Zeeb ci_bw_idx = 0; 569bfcc09ddSBjoern A. Zeeb } else { 570bfcc09ddSBjoern A. Zeeb if (chan->def.center_freq1 > 571bfcc09ddSBjoern A. Zeeb chan->def.chan->center_freq) 572bfcc09ddSBjoern A. Zeeb ci_bw_idx = 2; 573bfcc09ddSBjoern A. Zeeb else 574bfcc09ddSBjoern A. Zeeb ci_bw_idx = 1; 575bfcc09ddSBjoern A. Zeeb } 576bfcc09ddSBjoern A. Zeeb 577bfcc09ddSBjoern A. Zeeb cmd.bt_secondary_ci = 578bfcc09ddSBjoern A. Zeeb iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; 579bfcc09ddSBjoern A. Zeeb cmd.secondary_ch_phy_id = 580bfcc09ddSBjoern A. Zeeb cpu_to_le32(*((u16 *)data.secondary->drv_priv)); 581bfcc09ddSBjoern A. Zeeb } 582bfcc09ddSBjoern A. Zeeb 583bfcc09ddSBjoern A. Zeeb rcu_read_unlock(); 584bfcc09ddSBjoern A. Zeeb 585bfcc09ddSBjoern A. Zeeb /* Don't spam the fw with the same command over and over */ 586bfcc09ddSBjoern A. Zeeb if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) { 587bfcc09ddSBjoern A. Zeeb if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0, 588bfcc09ddSBjoern A. Zeeb sizeof(cmd), &cmd)) 589bfcc09ddSBjoern A. Zeeb IWL_ERR(mvm, "Failed to send BT_CI cmd\n"); 590bfcc09ddSBjoern A. Zeeb memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd)); 591bfcc09ddSBjoern A. Zeeb } 592bfcc09ddSBjoern A. Zeeb } 593bfcc09ddSBjoern A. Zeeb 594bfcc09ddSBjoern A. Zeeb void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, 595bfcc09ddSBjoern A. Zeeb struct iwl_rx_cmd_buffer *rxb) 596bfcc09ddSBjoern A. Zeeb { 597bfcc09ddSBjoern A. Zeeb struct iwl_rx_packet *pkt = rxb_addr(rxb); 598bfcc09ddSBjoern A. Zeeb struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; 599bfcc09ddSBjoern A. Zeeb 600bfcc09ddSBjoern A. Zeeb IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); 601bfcc09ddSBjoern A. Zeeb IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); 602bfcc09ddSBjoern A. Zeeb IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n", 603bfcc09ddSBjoern A. Zeeb le32_to_cpu(notif->primary_ch_lut)); 604bfcc09ddSBjoern A. Zeeb IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n", 605bfcc09ddSBjoern A. Zeeb le32_to_cpu(notif->secondary_ch_lut)); 606bfcc09ddSBjoern A. Zeeb IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n", 607bfcc09ddSBjoern A. Zeeb le32_to_cpu(notif->bt_activity_grading)); 608bfcc09ddSBjoern A. Zeeb 609bfcc09ddSBjoern A. Zeeb /* remember this notification for future use: rssi fluctuations */ 610bfcc09ddSBjoern A. Zeeb memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif)); 611bfcc09ddSBjoern A. Zeeb 612bfcc09ddSBjoern A. Zeeb iwl_mvm_bt_coex_notif_handle(mvm); 613bfcc09ddSBjoern A. Zeeb } 614bfcc09ddSBjoern A. Zeeb 615bfcc09ddSBjoern A. Zeeb void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 616bfcc09ddSBjoern A. Zeeb enum ieee80211_rssi_event_data rssi_event) 617bfcc09ddSBjoern A. Zeeb { 618bfcc09ddSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 619bfcc09ddSBjoern A. Zeeb int ret; 620bfcc09ddSBjoern A. Zeeb 621bfcc09ddSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 622bfcc09ddSBjoern A. Zeeb 623bfcc09ddSBjoern A. Zeeb /* Ignore updates if we are in force mode */ 624bfcc09ddSBjoern A. Zeeb if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) 625bfcc09ddSBjoern A. Zeeb return; 626bfcc09ddSBjoern A. Zeeb 627bfcc09ddSBjoern A. Zeeb /* 628bfcc09ddSBjoern A. Zeeb * Rssi update while not associated - can happen since the statistics 629bfcc09ddSBjoern A. Zeeb * are handled asynchronously 630bfcc09ddSBjoern A. Zeeb */ 6319af1bba4SBjoern A. Zeeb if (mvmvif->deflink.ap_sta_id == IWL_MVM_INVALID_STA) 632bfcc09ddSBjoern A. Zeeb return; 633bfcc09ddSBjoern A. Zeeb 634bfcc09ddSBjoern A. Zeeb /* No BT - reports should be disabled */ 635bfcc09ddSBjoern A. Zeeb if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) 636bfcc09ddSBjoern A. Zeeb return; 637bfcc09ddSBjoern A. Zeeb 638bfcc09ddSBjoern A. Zeeb IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid, 639bfcc09ddSBjoern A. Zeeb rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW"); 640bfcc09ddSBjoern A. Zeeb 641bfcc09ddSBjoern A. Zeeb /* 642bfcc09ddSBjoern A. Zeeb * Check if rssi is good enough for reduced Tx power, but not in loose 643bfcc09ddSBjoern A. Zeeb * scheme. 644bfcc09ddSBjoern A. Zeeb */ 645*a4128aadSBjoern A. Zeeb if (rssi_event == RSSI_EVENT_LOW || 646bfcc09ddSBjoern A. Zeeb iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT) 6479af1bba4SBjoern A. Zeeb ret = iwl_mvm_bt_coex_reduced_txp(mvm, 6489af1bba4SBjoern A. Zeeb mvmvif->deflink.ap_sta_id, 649bfcc09ddSBjoern A. Zeeb false); 650bfcc09ddSBjoern A. Zeeb else 6519af1bba4SBjoern A. Zeeb ret = iwl_mvm_bt_coex_reduced_txp(mvm, 6529af1bba4SBjoern A. Zeeb mvmvif->deflink.ap_sta_id, 6539af1bba4SBjoern A. Zeeb true); 654bfcc09ddSBjoern A. Zeeb 655bfcc09ddSBjoern A. Zeeb if (ret) 656bfcc09ddSBjoern A. Zeeb IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n"); 657bfcc09ddSBjoern A. Zeeb } 658bfcc09ddSBjoern A. Zeeb 659bfcc09ddSBjoern A. Zeeb #define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) 660bfcc09ddSBjoern A. Zeeb #define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200) 661bfcc09ddSBjoern A. Zeeb 662bfcc09ddSBjoern A. Zeeb u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, 663bfcc09ddSBjoern A. Zeeb struct ieee80211_sta *sta) 664bfcc09ddSBjoern A. Zeeb { 665bfcc09ddSBjoern A. Zeeb struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 666bfcc09ddSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); 6679af1bba4SBjoern A. Zeeb struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->deflink.phy_ctxt; 668bfcc09ddSBjoern A. Zeeb enum iwl_bt_coex_lut_type lut_type; 669bfcc09ddSBjoern A. Zeeb 670bfcc09ddSBjoern A. Zeeb if (mvm->last_bt_notif.ttc_status & BIT(phy_ctxt->id)) 671bfcc09ddSBjoern A. Zeeb return LINK_QUAL_AGG_TIME_LIMIT_DEF; 672bfcc09ddSBjoern A. Zeeb 673bfcc09ddSBjoern A. Zeeb if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < 674bfcc09ddSBjoern A. Zeeb BT_HIGH_TRAFFIC) 675bfcc09ddSBjoern A. Zeeb return LINK_QUAL_AGG_TIME_LIMIT_DEF; 676bfcc09ddSBjoern A. Zeeb 677bfcc09ddSBjoern A. Zeeb lut_type = iwl_get_coex_type(mvm, mvmsta->vif); 678bfcc09ddSBjoern A. Zeeb 679bfcc09ddSBjoern A. Zeeb if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT) 680bfcc09ddSBjoern A. Zeeb return LINK_QUAL_AGG_TIME_LIMIT_DEF; 681bfcc09ddSBjoern A. Zeeb 682bfcc09ddSBjoern A. Zeeb /* tight coex, high bt traffic, reduce AGG time limit */ 683bfcc09ddSBjoern A. Zeeb return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT; 684bfcc09ddSBjoern A. Zeeb } 685bfcc09ddSBjoern A. Zeeb 686bfcc09ddSBjoern A. Zeeb bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, 687bfcc09ddSBjoern A. Zeeb struct ieee80211_sta *sta) 688bfcc09ddSBjoern A. Zeeb { 689bfcc09ddSBjoern A. Zeeb struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 690bfcc09ddSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); 6919af1bba4SBjoern A. Zeeb struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->deflink.phy_ctxt; 692bfcc09ddSBjoern A. Zeeb enum iwl_bt_coex_lut_type lut_type; 693bfcc09ddSBjoern A. Zeeb 694bfcc09ddSBjoern A. Zeeb if (mvm->last_bt_notif.ttc_status & BIT(phy_ctxt->id)) 695bfcc09ddSBjoern A. Zeeb return true; 696bfcc09ddSBjoern A. Zeeb 697bfcc09ddSBjoern A. Zeeb if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < 698bfcc09ddSBjoern A. Zeeb BT_HIGH_TRAFFIC) 699bfcc09ddSBjoern A. Zeeb return true; 700bfcc09ddSBjoern A. Zeeb 701bfcc09ddSBjoern A. Zeeb /* 702bfcc09ddSBjoern A. Zeeb * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas 703bfcc09ddSBjoern A. Zeeb * since BT is already killed. 704bfcc09ddSBjoern A. Zeeb * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while 705bfcc09ddSBjoern A. Zeeb * we Tx. 706bfcc09ddSBjoern A. Zeeb * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO. 707bfcc09ddSBjoern A. Zeeb */ 708bfcc09ddSBjoern A. Zeeb lut_type = iwl_get_coex_type(mvm, mvmsta->vif); 709bfcc09ddSBjoern A. Zeeb return lut_type != BT_COEX_LOOSE_LUT; 710bfcc09ddSBjoern A. Zeeb } 711bfcc09ddSBjoern A. Zeeb 712bfcc09ddSBjoern A. Zeeb bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant) 713bfcc09ddSBjoern A. Zeeb { 714bfcc09ddSBjoern A. Zeeb if (ant & mvm->cfg->non_shared_ant) 715bfcc09ddSBjoern A. Zeeb return true; 716bfcc09ddSBjoern A. Zeeb 717bfcc09ddSBjoern A. Zeeb return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < 718bfcc09ddSBjoern A. Zeeb BT_HIGH_TRAFFIC; 719bfcc09ddSBjoern A. Zeeb } 720bfcc09ddSBjoern A. Zeeb 721bfcc09ddSBjoern A. Zeeb bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm) 722bfcc09ddSBjoern A. Zeeb { 723bfcc09ddSBjoern A. Zeeb return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC; 724bfcc09ddSBjoern A. Zeeb } 725bfcc09ddSBjoern A. Zeeb 726bfcc09ddSBjoern A. Zeeb bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, 727bfcc09ddSBjoern A. Zeeb enum nl80211_band band) 728bfcc09ddSBjoern A. Zeeb { 729bfcc09ddSBjoern A. Zeeb u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading); 730bfcc09ddSBjoern A. Zeeb 731bfcc09ddSBjoern A. Zeeb if (band != NL80211_BAND_2GHZ) 732bfcc09ddSBjoern A. Zeeb return false; 733bfcc09ddSBjoern A. Zeeb 734bfcc09ddSBjoern A. Zeeb return bt_activity >= BT_LOW_TRAFFIC; 735bfcc09ddSBjoern A. Zeeb } 736bfcc09ddSBjoern A. Zeeb 737bfcc09ddSBjoern A. Zeeb u8 iwl_mvm_bt_coex_get_single_ant_msk(struct iwl_mvm *mvm, u8 enabled_ants) 738bfcc09ddSBjoern A. Zeeb { 739bfcc09ddSBjoern A. Zeeb if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2) && 740bfcc09ddSBjoern A. Zeeb (mvm->cfg->non_shared_ant & enabled_ants)) 741bfcc09ddSBjoern A. Zeeb return mvm->cfg->non_shared_ant; 742bfcc09ddSBjoern A. Zeeb 743bfcc09ddSBjoern A. Zeeb return first_antenna(enabled_ants); 744bfcc09ddSBjoern A. Zeeb } 745bfcc09ddSBjoern A. Zeeb 746bfcc09ddSBjoern A. Zeeb u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, 747bfcc09ddSBjoern A. Zeeb struct ieee80211_tx_info *info, u8 ac) 748bfcc09ddSBjoern A. Zeeb { 749bfcc09ddSBjoern A. Zeeb __le16 fc = hdr->frame_control; 750bfcc09ddSBjoern A. Zeeb bool mplut_enabled = iwl_mvm_is_mplut_supported(mvm); 751bfcc09ddSBjoern A. Zeeb 752bfcc09ddSBjoern A. Zeeb if (info->band != NL80211_BAND_2GHZ) 753bfcc09ddSBjoern A. Zeeb return 0; 754bfcc09ddSBjoern A. Zeeb 755bfcc09ddSBjoern A. Zeeb if (unlikely(mvm->bt_tx_prio)) 756bfcc09ddSBjoern A. Zeeb return mvm->bt_tx_prio - 1; 757bfcc09ddSBjoern A. Zeeb 758bfcc09ddSBjoern A. Zeeb if (likely(ieee80211_is_data(fc))) { 759bfcc09ddSBjoern A. Zeeb if (likely(ieee80211_is_data_qos(fc))) { 760bfcc09ddSBjoern A. Zeeb switch (ac) { 761bfcc09ddSBjoern A. Zeeb case IEEE80211_AC_BE: 762bfcc09ddSBjoern A. Zeeb return mplut_enabled ? 1 : 0; 763bfcc09ddSBjoern A. Zeeb case IEEE80211_AC_VI: 764bfcc09ddSBjoern A. Zeeb return mplut_enabled ? 2 : 3; 765bfcc09ddSBjoern A. Zeeb case IEEE80211_AC_VO: 766bfcc09ddSBjoern A. Zeeb return 3; 767bfcc09ddSBjoern A. Zeeb default: 768bfcc09ddSBjoern A. Zeeb return 0; 769bfcc09ddSBjoern A. Zeeb } 770bfcc09ddSBjoern A. Zeeb } else if (is_multicast_ether_addr(hdr->addr1)) { 771bfcc09ddSBjoern A. Zeeb return 3; 772bfcc09ddSBjoern A. Zeeb } else 773bfcc09ddSBjoern A. Zeeb return 0; 774bfcc09ddSBjoern A. Zeeb } else if (ieee80211_is_mgmt(fc)) { 775bfcc09ddSBjoern A. Zeeb return ieee80211_is_disassoc(fc) ? 0 : 3; 776bfcc09ddSBjoern A. Zeeb } else if (ieee80211_is_ctl(fc)) { 777bfcc09ddSBjoern A. Zeeb /* ignore cfend and cfendack frames as we never send those */ 778bfcc09ddSBjoern A. Zeeb return 3; 779bfcc09ddSBjoern A. Zeeb } 780bfcc09ddSBjoern A. Zeeb 781bfcc09ddSBjoern A. Zeeb return 0; 782bfcc09ddSBjoern A. Zeeb } 783bfcc09ddSBjoern A. Zeeb 784bfcc09ddSBjoern A. Zeeb void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm) 785bfcc09ddSBjoern A. Zeeb { 786bfcc09ddSBjoern A. Zeeb iwl_mvm_bt_coex_notif_handle(mvm); 787bfcc09ddSBjoern A. Zeeb } 788