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