18c2ecf20Sopenharmony_ci/******************************************************************************
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license.  When using or
48c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2014, 2018 - 2020 Intel Corporation. All rights reserved.
98c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
128c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as
138c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but
168c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
178c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
188c2ecf20Sopenharmony_ci * General Public License for more details.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * The full GNU General Public License is included in this distribution
218c2ecf20Sopenharmony_ci * in the file called COPYING.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * Contact Information:
248c2ecf20Sopenharmony_ci *  Intel Linux Wireless <linuxwifi@intel.com>
258c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * BSD LICENSE
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2014, 2018 - 2020 Intel Corporation. All rights reserved.
308c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
318c2ecf20Sopenharmony_ci * All rights reserved.
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
348c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
358c2ecf20Sopenharmony_ci * are met:
368c2ecf20Sopenharmony_ci *
378c2ecf20Sopenharmony_ci *  * Redistributions of source code must retain the above copyright
388c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
398c2ecf20Sopenharmony_ci *  * Redistributions in binary form must reproduce the above copyright
408c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in
418c2ecf20Sopenharmony_ci *    the documentation and/or other materials provided with the
428c2ecf20Sopenharmony_ci *    distribution.
438c2ecf20Sopenharmony_ci *  * Neither the name Intel Corporation nor the names of its
448c2ecf20Sopenharmony_ci *    contributors may be used to endorse or promote products derived
458c2ecf20Sopenharmony_ci *    from this software without specific prior written permission.
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
488c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
498c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
508c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
518c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
528c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
538c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
548c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
558c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
568c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
578c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci *****************************************************************************/
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#include <linux/ieee80211.h>
628c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
638c2ecf20Sopenharmony_ci#include <net/mac80211.h>
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#include "fw/api/coex.h"
668c2ecf20Sopenharmony_ci#include "iwl-modparams.h"
678c2ecf20Sopenharmony_ci#include "mvm.h"
688c2ecf20Sopenharmony_ci#include "iwl-debug.h"
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* 20MHz / 40MHz below / 40Mhz above*/
718c2ecf20Sopenharmony_cistatic const __le64 iwl_ci_mask[][3] = {
728c2ecf20Sopenharmony_ci	/* dummy entry for channel 0 */
738c2ecf20Sopenharmony_ci	{cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)},
748c2ecf20Sopenharmony_ci	{
758c2ecf20Sopenharmony_ci		cpu_to_le64(0x0000001FFFULL),
768c2ecf20Sopenharmony_ci		cpu_to_le64(0x0ULL),
778c2ecf20Sopenharmony_ci		cpu_to_le64(0x00007FFFFFULL),
788c2ecf20Sopenharmony_ci	},
798c2ecf20Sopenharmony_ci	{
808c2ecf20Sopenharmony_ci		cpu_to_le64(0x000000FFFFULL),
818c2ecf20Sopenharmony_ci		cpu_to_le64(0x0ULL),
828c2ecf20Sopenharmony_ci		cpu_to_le64(0x0003FFFFFFULL),
838c2ecf20Sopenharmony_ci	},
848c2ecf20Sopenharmony_ci	{
858c2ecf20Sopenharmony_ci		cpu_to_le64(0x000003FFFCULL),
868c2ecf20Sopenharmony_ci		cpu_to_le64(0x0ULL),
878c2ecf20Sopenharmony_ci		cpu_to_le64(0x000FFFFFFCULL),
888c2ecf20Sopenharmony_ci	},
898c2ecf20Sopenharmony_ci	{
908c2ecf20Sopenharmony_ci		cpu_to_le64(0x00001FFFE0ULL),
918c2ecf20Sopenharmony_ci		cpu_to_le64(0x0ULL),
928c2ecf20Sopenharmony_ci		cpu_to_le64(0x007FFFFFE0ULL),
938c2ecf20Sopenharmony_ci	},
948c2ecf20Sopenharmony_ci	{
958c2ecf20Sopenharmony_ci		cpu_to_le64(0x00007FFF80ULL),
968c2ecf20Sopenharmony_ci		cpu_to_le64(0x00007FFFFFULL),
978c2ecf20Sopenharmony_ci		cpu_to_le64(0x01FFFFFF80ULL),
988c2ecf20Sopenharmony_ci	},
998c2ecf20Sopenharmony_ci	{
1008c2ecf20Sopenharmony_ci		cpu_to_le64(0x0003FFFC00ULL),
1018c2ecf20Sopenharmony_ci		cpu_to_le64(0x0003FFFFFFULL),
1028c2ecf20Sopenharmony_ci		cpu_to_le64(0x0FFFFFFC00ULL),
1038c2ecf20Sopenharmony_ci	},
1048c2ecf20Sopenharmony_ci	{
1058c2ecf20Sopenharmony_ci		cpu_to_le64(0x000FFFF000ULL),
1068c2ecf20Sopenharmony_ci		cpu_to_le64(0x000FFFFFFCULL),
1078c2ecf20Sopenharmony_ci		cpu_to_le64(0x3FFFFFF000ULL),
1088c2ecf20Sopenharmony_ci	},
1098c2ecf20Sopenharmony_ci	{
1108c2ecf20Sopenharmony_ci		cpu_to_le64(0x007FFF8000ULL),
1118c2ecf20Sopenharmony_ci		cpu_to_le64(0x007FFFFFE0ULL),
1128c2ecf20Sopenharmony_ci		cpu_to_le64(0xFFFFFF8000ULL),
1138c2ecf20Sopenharmony_ci	},
1148c2ecf20Sopenharmony_ci	{
1158c2ecf20Sopenharmony_ci		cpu_to_le64(0x01FFFE0000ULL),
1168c2ecf20Sopenharmony_ci		cpu_to_le64(0x01FFFFFF80ULL),
1178c2ecf20Sopenharmony_ci		cpu_to_le64(0xFFFFFE0000ULL),
1188c2ecf20Sopenharmony_ci	},
1198c2ecf20Sopenharmony_ci	{
1208c2ecf20Sopenharmony_ci		cpu_to_le64(0x0FFFF00000ULL),
1218c2ecf20Sopenharmony_ci		cpu_to_le64(0x0FFFFFFC00ULL),
1228c2ecf20Sopenharmony_ci		cpu_to_le64(0x0ULL),
1238c2ecf20Sopenharmony_ci	},
1248c2ecf20Sopenharmony_ci	{
1258c2ecf20Sopenharmony_ci		cpu_to_le64(0x3FFFC00000ULL),
1268c2ecf20Sopenharmony_ci		cpu_to_le64(0x3FFFFFF000ULL),
1278c2ecf20Sopenharmony_ci		cpu_to_le64(0x0)
1288c2ecf20Sopenharmony_ci	},
1298c2ecf20Sopenharmony_ci	{
1308c2ecf20Sopenharmony_ci		cpu_to_le64(0xFFFE000000ULL),
1318c2ecf20Sopenharmony_ci		cpu_to_le64(0xFFFFFF8000ULL),
1328c2ecf20Sopenharmony_ci		cpu_to_le64(0x0)
1338c2ecf20Sopenharmony_ci	},
1348c2ecf20Sopenharmony_ci	{
1358c2ecf20Sopenharmony_ci		cpu_to_le64(0xFFF8000000ULL),
1368c2ecf20Sopenharmony_ci		cpu_to_le64(0xFFFFFE0000ULL),
1378c2ecf20Sopenharmony_ci		cpu_to_le64(0x0)
1388c2ecf20Sopenharmony_ci	},
1398c2ecf20Sopenharmony_ci	{
1408c2ecf20Sopenharmony_ci		cpu_to_le64(0xFE00000000ULL),
1418c2ecf20Sopenharmony_ci		cpu_to_le64(0x0ULL),
1428c2ecf20Sopenharmony_ci		cpu_to_le64(0x0ULL)
1438c2ecf20Sopenharmony_ci	},
1448c2ecf20Sopenharmony_ci};
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic enum iwl_bt_coex_lut_type
1478c2ecf20Sopenharmony_ciiwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	struct ieee80211_chanctx_conf *chanctx_conf;
1508c2ecf20Sopenharmony_ci	enum iwl_bt_coex_lut_type ret;
1518c2ecf20Sopenharmony_ci	u16 phy_ctx_id;
1528c2ecf20Sopenharmony_ci	u32 primary_ch_phy_id, secondary_ch_phy_id;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/*
1558c2ecf20Sopenharmony_ci	 * Checking that we hold mvm->mutex is a good idea, but the rate
1568c2ecf20Sopenharmony_ci	 * control can't acquire the mutex since it runs in Tx path.
1578c2ecf20Sopenharmony_ci	 * So this is racy in that case, but in the worst case, the AMPDU
1588c2ecf20Sopenharmony_ci	 * size limit will be wrong for a short time which is not a big
1598c2ecf20Sopenharmony_ci	 * issue.
1608c2ecf20Sopenharmony_ci	 */
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	rcu_read_lock();
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	chanctx_conf = rcu_dereference(vif->chanctx_conf);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (!chanctx_conf ||
1678c2ecf20Sopenharmony_ci	     chanctx_conf->def.chan->band != NL80211_BAND_2GHZ) {
1688c2ecf20Sopenharmony_ci		rcu_read_unlock();
1698c2ecf20Sopenharmony_ci		return BT_COEX_INVALID_LUT;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	ret = BT_COEX_TX_DIS_LUT;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (mvm->cfg->bt_shared_single_ant) {
1758c2ecf20Sopenharmony_ci		rcu_read_unlock();
1768c2ecf20Sopenharmony_ci		return ret;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	phy_ctx_id = *((u16 *)chanctx_conf->drv_priv);
1808c2ecf20Sopenharmony_ci	primary_ch_phy_id = le32_to_cpu(mvm->last_bt_ci_cmd.primary_ch_phy_id);
1818c2ecf20Sopenharmony_ci	secondary_ch_phy_id =
1828c2ecf20Sopenharmony_ci		le32_to_cpu(mvm->last_bt_ci_cmd.secondary_ch_phy_id);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (primary_ch_phy_id == phy_ctx_id)
1858c2ecf20Sopenharmony_ci		ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut);
1868c2ecf20Sopenharmony_ci	else if (secondary_ch_phy_id == phy_ctx_id)
1878c2ecf20Sopenharmony_ci		ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut);
1888c2ecf20Sopenharmony_ci	/* else - default = TX TX disallowed */
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	rcu_read_unlock();
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return ret;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ciint iwl_mvm_send_bt_init_conf(struct iwl_mvm *mvm)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct iwl_bt_coex_cmd bt_cmd = {};
1988c2ecf20Sopenharmony_ci	u32 mode;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) {
2038c2ecf20Sopenharmony_ci		switch (mvm->bt_force_ant_mode) {
2048c2ecf20Sopenharmony_ci		case BT_FORCE_ANT_BT:
2058c2ecf20Sopenharmony_ci			mode = BT_COEX_BT;
2068c2ecf20Sopenharmony_ci			break;
2078c2ecf20Sopenharmony_ci		case BT_FORCE_ANT_WIFI:
2088c2ecf20Sopenharmony_ci			mode = BT_COEX_WIFI;
2098c2ecf20Sopenharmony_ci			break;
2108c2ecf20Sopenharmony_ci		default:
2118c2ecf20Sopenharmony_ci			WARN_ON(1);
2128c2ecf20Sopenharmony_ci			mode = 0;
2138c2ecf20Sopenharmony_ci		}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		bt_cmd.mode = cpu_to_le32(mode);
2168c2ecf20Sopenharmony_ci		goto send_cmd;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	bt_cmd.mode = cpu_to_le32(BT_COEX_NW);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	if (IWL_MVM_BT_COEX_SYNC2SCO)
2228c2ecf20Sopenharmony_ci		bt_cmd.enabled_modules |=
2238c2ecf20Sopenharmony_ci			cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (iwl_mvm_is_mplut_supported(mvm))
2268c2ecf20Sopenharmony_ci		bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cisend_cmd:
2318c2ecf20Sopenharmony_ci	memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
2328c2ecf20Sopenharmony_ci	memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, 0, sizeof(bt_cmd), &bt_cmd);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
2388c2ecf20Sopenharmony_ci				       bool enable)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct iwl_bt_coex_reduced_txp_update_cmd cmd = {};
2418c2ecf20Sopenharmony_ci	struct iwl_mvm_sta *mvmsta;
2428c2ecf20Sopenharmony_ci	u32 value;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
2458c2ecf20Sopenharmony_ci	if (!mvmsta)
2468c2ecf20Sopenharmony_ci		return 0;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/* nothing to do */
2498c2ecf20Sopenharmony_ci	if (mvmsta->bt_reduced_txpower == enable)
2508c2ecf20Sopenharmony_ci		return 0;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	value = mvmsta->sta_id;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (enable)
2558c2ecf20Sopenharmony_ci		value |= BT_REDUCED_TX_POWER_BIT;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
2588c2ecf20Sopenharmony_ci		       enable ? "en" : "dis", sta_id);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	cmd.reduced_txp = cpu_to_le32(value);
2618c2ecf20Sopenharmony_ci	mvmsta->bt_reduced_txpower = enable;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_REDUCED_TXP,
2648c2ecf20Sopenharmony_ci				    CMD_ASYNC, sizeof(cmd), &cmd);
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistruct iwl_bt_iterator_data {
2688c2ecf20Sopenharmony_ci	struct iwl_bt_coex_profile_notif *notif;
2698c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm;
2708c2ecf20Sopenharmony_ci	struct ieee80211_chanctx_conf *primary;
2718c2ecf20Sopenharmony_ci	struct ieee80211_chanctx_conf *secondary;
2728c2ecf20Sopenharmony_ci	bool primary_ll;
2738c2ecf20Sopenharmony_ci	u8 primary_load;
2748c2ecf20Sopenharmony_ci	u8 secondary_load;
2758c2ecf20Sopenharmony_ci};
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic inline
2788c2ecf20Sopenharmony_civoid iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm,
2798c2ecf20Sopenharmony_ci				       struct ieee80211_vif *vif,
2808c2ecf20Sopenharmony_ci				       bool enable, int rssi)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	mvmvif->bf_data.last_bt_coex_event = rssi;
2858c2ecf20Sopenharmony_ci	mvmvif->bf_data.bt_coex_max_thold =
2868c2ecf20Sopenharmony_ci		enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0;
2878c2ecf20Sopenharmony_ci	mvmvif->bf_data.bt_coex_min_thold =
2888c2ecf20Sopenharmony_ci		enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci#define MVM_COEX_TCM_PERIOD (HZ * 10)
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic void iwl_mvm_bt_coex_tcm_based_ci(struct iwl_mvm *mvm,
2948c2ecf20Sopenharmony_ci					 struct iwl_bt_iterator_data *data)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (!time_after(now, mvm->bt_coex_last_tcm_ts + MVM_COEX_TCM_PERIOD))
2998c2ecf20Sopenharmony_ci		return;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	mvm->bt_coex_last_tcm_ts = now;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	/* We assume here that we don't have more than 2 vifs on 2.4GHz */
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	/* if the primary is low latency, it will stay primary */
3068c2ecf20Sopenharmony_ci	if (data->primary_ll)
3078c2ecf20Sopenharmony_ci		return;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (data->primary_load >= data->secondary_load)
3108c2ecf20Sopenharmony_ci		return;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	swap(data->primary, data->secondary);
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci/* must be called under rcu_read_lock */
3168c2ecf20Sopenharmony_cistatic void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
3178c2ecf20Sopenharmony_ci				      struct ieee80211_vif *vif)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
3208c2ecf20Sopenharmony_ci	struct iwl_bt_iterator_data *data = _data;
3218c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm = data->mvm;
3228c2ecf20Sopenharmony_ci	struct ieee80211_chanctx_conf *chanctx_conf;
3238c2ecf20Sopenharmony_ci	/* default smps_mode is AUTOMATIC - only used for client modes */
3248c2ecf20Sopenharmony_ci	enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC;
3258c2ecf20Sopenharmony_ci	u32 bt_activity_grading, min_ag_for_static_smps;
3268c2ecf20Sopenharmony_ci	int ave_rssi;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	switch (vif->type) {
3318c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
3328c2ecf20Sopenharmony_ci		break;
3338c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_AP:
3348c2ecf20Sopenharmony_ci		if (!mvmvif->ap_ibss_active)
3358c2ecf20Sopenharmony_ci			return;
3368c2ecf20Sopenharmony_ci		break;
3378c2ecf20Sopenharmony_ci	default:
3388c2ecf20Sopenharmony_ci		return;
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	chanctx_conf = rcu_dereference(vif->chanctx_conf);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/* If channel context is invalid or not on 2.4GHz .. */
3448c2ecf20Sopenharmony_ci	if ((!chanctx_conf ||
3458c2ecf20Sopenharmony_ci	     chanctx_conf->def.chan->band != NL80211_BAND_2GHZ)) {
3468c2ecf20Sopenharmony_ci		if (vif->type == NL80211_IFTYPE_STATION) {
3478c2ecf20Sopenharmony_ci			/* ... relax constraints and disable rssi events */
3488c2ecf20Sopenharmony_ci			iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
3498c2ecf20Sopenharmony_ci					    smps_mode);
3508c2ecf20Sopenharmony_ci			iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
3518c2ecf20Sopenharmony_ci						    false);
3528c2ecf20Sopenharmony_ci			iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
3538c2ecf20Sopenharmony_ci		}
3548c2ecf20Sopenharmony_ci		return;
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2))
3588c2ecf20Sopenharmony_ci		min_ag_for_static_smps = BT_VERY_HIGH_TRAFFIC;
3598c2ecf20Sopenharmony_ci	else
3608c2ecf20Sopenharmony_ci		min_ag_for_static_smps = BT_HIGH_TRAFFIC;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading);
3638c2ecf20Sopenharmony_ci	if (bt_activity_grading >= min_ag_for_static_smps)
3648c2ecf20Sopenharmony_ci		smps_mode = IEEE80211_SMPS_STATIC;
3658c2ecf20Sopenharmony_ci	else if (bt_activity_grading >= BT_LOW_TRAFFIC)
3668c2ecf20Sopenharmony_ci		smps_mode = IEEE80211_SMPS_DYNAMIC;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* relax SMPS constraints for next association */
3698c2ecf20Sopenharmony_ci	if (!vif->bss_conf.assoc)
3708c2ecf20Sopenharmony_ci		smps_mode = IEEE80211_SMPS_AUTOMATIC;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (mvmvif->phy_ctxt &&
3738c2ecf20Sopenharmony_ci	    (mvm->last_bt_notif.rrc_status & BIT(mvmvif->phy_ctxt->id)))
3748c2ecf20Sopenharmony_ci		smps_mode = IEEE80211_SMPS_AUTOMATIC;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(data->mvm,
3778c2ecf20Sopenharmony_ci		       "mac %d: bt_activity_grading %d smps_req %d\n",
3788c2ecf20Sopenharmony_ci		       mvmvif->id, bt_activity_grading, smps_mode);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_STATION)
3818c2ecf20Sopenharmony_ci		iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
3828c2ecf20Sopenharmony_ci				    smps_mode);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	/* low latency is always primary */
3858c2ecf20Sopenharmony_ci	if (iwl_mvm_vif_low_latency(mvmvif)) {
3868c2ecf20Sopenharmony_ci		data->primary_ll = true;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		data->secondary = data->primary;
3898c2ecf20Sopenharmony_ci		data->primary = chanctx_conf;
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_AP) {
3938c2ecf20Sopenharmony_ci		if (!mvmvif->ap_ibss_active)
3948c2ecf20Sopenharmony_ci			return;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci		if (chanctx_conf == data->primary)
3978c2ecf20Sopenharmony_ci			return;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		if (!data->primary_ll) {
4008c2ecf20Sopenharmony_ci			/*
4018c2ecf20Sopenharmony_ci			 * downgrade the current primary no matter what its
4028c2ecf20Sopenharmony_ci			 * type is.
4038c2ecf20Sopenharmony_ci			 */
4048c2ecf20Sopenharmony_ci			data->secondary = data->primary;
4058c2ecf20Sopenharmony_ci			data->primary = chanctx_conf;
4068c2ecf20Sopenharmony_ci		} else {
4078c2ecf20Sopenharmony_ci			/* there is low latency vif - we will be secondary */
4088c2ecf20Sopenharmony_ci			data->secondary = chanctx_conf;
4098c2ecf20Sopenharmony_ci		}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci		if (data->primary == chanctx_conf)
4128c2ecf20Sopenharmony_ci			data->primary_load = mvm->tcm.result.load[mvmvif->id];
4138c2ecf20Sopenharmony_ci		else if (data->secondary == chanctx_conf)
4148c2ecf20Sopenharmony_ci			data->secondary_load = mvm->tcm.result.load[mvmvif->id];
4158c2ecf20Sopenharmony_ci		return;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	/*
4198c2ecf20Sopenharmony_ci	 * STA / P2P Client, try to be primary if first vif. If we are in low
4208c2ecf20Sopenharmony_ci	 * latency mode, we are already in primary and just don't do much
4218c2ecf20Sopenharmony_ci	 */
4228c2ecf20Sopenharmony_ci	if (!data->primary || data->primary == chanctx_conf)
4238c2ecf20Sopenharmony_ci		data->primary = chanctx_conf;
4248c2ecf20Sopenharmony_ci	else if (!data->secondary)
4258c2ecf20Sopenharmony_ci		/* if secondary is not NULL, it might be a GO */
4268c2ecf20Sopenharmony_ci		data->secondary = chanctx_conf;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (data->primary == chanctx_conf)
4298c2ecf20Sopenharmony_ci		data->primary_load = mvm->tcm.result.load[mvmvif->id];
4308c2ecf20Sopenharmony_ci	else if (data->secondary == chanctx_conf)
4318c2ecf20Sopenharmony_ci		data->secondary_load = mvm->tcm.result.load[mvmvif->id];
4328c2ecf20Sopenharmony_ci	/*
4338c2ecf20Sopenharmony_ci	 * don't reduce the Tx power if one of these is true:
4348c2ecf20Sopenharmony_ci	 *  we are in LOOSE
4358c2ecf20Sopenharmony_ci	 *  single share antenna product
4368c2ecf20Sopenharmony_ci	 *  BT is inactive
4378c2ecf20Sopenharmony_ci	 *  we are not associated
4388c2ecf20Sopenharmony_ci	 */
4398c2ecf20Sopenharmony_ci	if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
4408c2ecf20Sopenharmony_ci	    mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc ||
4418c2ecf20Sopenharmony_ci	    le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) {
4428c2ecf20Sopenharmony_ci		iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
4438c2ecf20Sopenharmony_ci		iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
4448c2ecf20Sopenharmony_ci		return;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	/* try to get the avg rssi from fw */
4488c2ecf20Sopenharmony_ci	ave_rssi = mvmvif->bf_data.ave_beacon_signal;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	/* if the RSSI isn't valid, fake it is very low */
4518c2ecf20Sopenharmony_ci	if (!ave_rssi)
4528c2ecf20Sopenharmony_ci		ave_rssi = -100;
4538c2ecf20Sopenharmony_ci	if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) {
4548c2ecf20Sopenharmony_ci		if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
4558c2ecf20Sopenharmony_ci			IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
4568c2ecf20Sopenharmony_ci	} else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) {
4578c2ecf20Sopenharmony_ci		if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
4588c2ecf20Sopenharmony_ci			IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	/* Begin to monitor the RSSI: it may influence the reduced Tx power */
4628c2ecf20Sopenharmony_ci	iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi);
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	struct iwl_bt_iterator_data data = {
4688c2ecf20Sopenharmony_ci		.mvm = mvm,
4698c2ecf20Sopenharmony_ci		.notif = &mvm->last_bt_notif,
4708c2ecf20Sopenharmony_ci	};
4718c2ecf20Sopenharmony_ci	struct iwl_bt_coex_ci_cmd cmd = {};
4728c2ecf20Sopenharmony_ci	u8 ci_bw_idx;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* Ignore updates if we are in force mode */
4758c2ecf20Sopenharmony_ci	if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
4768c2ecf20Sopenharmony_ci		return;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	rcu_read_lock();
4798c2ecf20Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(
4808c2ecf20Sopenharmony_ci					mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
4818c2ecf20Sopenharmony_ci					iwl_mvm_bt_notif_iterator, &data);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	iwl_mvm_bt_coex_tcm_based_ci(mvm, &data);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	if (data.primary) {
4868c2ecf20Sopenharmony_ci		struct ieee80211_chanctx_conf *chan = data.primary;
4878c2ecf20Sopenharmony_ci		if (WARN_ON(!chan->def.chan)) {
4888c2ecf20Sopenharmony_ci			rcu_read_unlock();
4898c2ecf20Sopenharmony_ci			return;
4908c2ecf20Sopenharmony_ci		}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci		if (chan->def.width < NL80211_CHAN_WIDTH_40) {
4938c2ecf20Sopenharmony_ci			ci_bw_idx = 0;
4948c2ecf20Sopenharmony_ci		} else {
4958c2ecf20Sopenharmony_ci			if (chan->def.center_freq1 >
4968c2ecf20Sopenharmony_ci			    chan->def.chan->center_freq)
4978c2ecf20Sopenharmony_ci				ci_bw_idx = 2;
4988c2ecf20Sopenharmony_ci			else
4998c2ecf20Sopenharmony_ci				ci_bw_idx = 1;
5008c2ecf20Sopenharmony_ci		}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci		cmd.bt_primary_ci =
5038c2ecf20Sopenharmony_ci			iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
5048c2ecf20Sopenharmony_ci		cmd.primary_ch_phy_id =
5058c2ecf20Sopenharmony_ci			cpu_to_le32(*((u16 *)data.primary->drv_priv));
5068c2ecf20Sopenharmony_ci	}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	if (data.secondary) {
5098c2ecf20Sopenharmony_ci		struct ieee80211_chanctx_conf *chan = data.secondary;
5108c2ecf20Sopenharmony_ci		if (WARN_ON(!data.secondary->def.chan)) {
5118c2ecf20Sopenharmony_ci			rcu_read_unlock();
5128c2ecf20Sopenharmony_ci			return;
5138c2ecf20Sopenharmony_ci		}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci		if (chan->def.width < NL80211_CHAN_WIDTH_40) {
5168c2ecf20Sopenharmony_ci			ci_bw_idx = 0;
5178c2ecf20Sopenharmony_ci		} else {
5188c2ecf20Sopenharmony_ci			if (chan->def.center_freq1 >
5198c2ecf20Sopenharmony_ci			    chan->def.chan->center_freq)
5208c2ecf20Sopenharmony_ci				ci_bw_idx = 2;
5218c2ecf20Sopenharmony_ci			else
5228c2ecf20Sopenharmony_ci				ci_bw_idx = 1;
5238c2ecf20Sopenharmony_ci		}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci		cmd.bt_secondary_ci =
5268c2ecf20Sopenharmony_ci			iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
5278c2ecf20Sopenharmony_ci		cmd.secondary_ch_phy_id =
5288c2ecf20Sopenharmony_ci			cpu_to_le32(*((u16 *)data.secondary->drv_priv));
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	rcu_read_unlock();
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	/* Don't spam the fw with the same command over and over */
5348c2ecf20Sopenharmony_ci	if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) {
5358c2ecf20Sopenharmony_ci		if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0,
5368c2ecf20Sopenharmony_ci					 sizeof(cmd), &cmd))
5378c2ecf20Sopenharmony_ci			IWL_ERR(mvm, "Failed to send BT_CI cmd\n");
5388c2ecf20Sopenharmony_ci		memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd));
5398c2ecf20Sopenharmony_ci	}
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_civoid iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
5438c2ecf20Sopenharmony_ci			      struct iwl_rx_cmd_buffer *rxb)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	struct iwl_rx_packet *pkt = rxb_addr(rxb);
5468c2ecf20Sopenharmony_ci	struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
5498c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
5508c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",
5518c2ecf20Sopenharmony_ci		       le32_to_cpu(notif->primary_ch_lut));
5528c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n",
5538c2ecf20Sopenharmony_ci		       le32_to_cpu(notif->secondary_ch_lut));
5548c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",
5558c2ecf20Sopenharmony_ci		       le32_to_cpu(notif->bt_activity_grading));
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	/* remember this notification for future use: rssi fluctuations */
5588c2ecf20Sopenharmony_ci	memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif));
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	iwl_mvm_bt_coex_notif_handle(mvm);
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_civoid iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
5648c2ecf20Sopenharmony_ci			   enum ieee80211_rssi_event_data rssi_event)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
5678c2ecf20Sopenharmony_ci	int ret;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	/* Ignore updates if we are in force mode */
5728c2ecf20Sopenharmony_ci	if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
5738c2ecf20Sopenharmony_ci		return;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	/*
5768c2ecf20Sopenharmony_ci	 * Rssi update while not associated - can happen since the statistics
5778c2ecf20Sopenharmony_ci	 * are handled asynchronously
5788c2ecf20Sopenharmony_ci	 */
5798c2ecf20Sopenharmony_ci	if (mvmvif->ap_sta_id == IWL_MVM_INVALID_STA)
5808c2ecf20Sopenharmony_ci		return;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	/* No BT - reports should be disabled */
5838c2ecf20Sopenharmony_ci	if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF)
5848c2ecf20Sopenharmony_ci		return;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
5878c2ecf20Sopenharmony_ci		       rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW");
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	/*
5908c2ecf20Sopenharmony_ci	 * Check if rssi is good enough for reduced Tx power, but not in loose
5918c2ecf20Sopenharmony_ci	 * scheme.
5928c2ecf20Sopenharmony_ci	 */
5938c2ecf20Sopenharmony_ci	if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant ||
5948c2ecf20Sopenharmony_ci	    iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT)
5958c2ecf20Sopenharmony_ci		ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
5968c2ecf20Sopenharmony_ci						  false);
5978c2ecf20Sopenharmony_ci	else
5988c2ecf20Sopenharmony_ci		ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	if (ret)
6018c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n");
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci#define LINK_QUAL_AGG_TIME_LIMIT_DEF	(4000)
6058c2ecf20Sopenharmony_ci#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT	(1200)
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ciu16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
6088c2ecf20Sopenharmony_ci				struct ieee80211_sta *sta)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
6118c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
6128c2ecf20Sopenharmony_ci	struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt;
6138c2ecf20Sopenharmony_ci	enum iwl_bt_coex_lut_type lut_type;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	if (mvm->last_bt_notif.ttc_status & BIT(phy_ctxt->id))
6168c2ecf20Sopenharmony_ci		return LINK_QUAL_AGG_TIME_LIMIT_DEF;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
6198c2ecf20Sopenharmony_ci	    BT_HIGH_TRAFFIC)
6208c2ecf20Sopenharmony_ci		return LINK_QUAL_AGG_TIME_LIMIT_DEF;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT)
6258c2ecf20Sopenharmony_ci		return LINK_QUAL_AGG_TIME_LIMIT_DEF;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	/* tight coex, high bt traffic, reduce AGG time limit */
6288c2ecf20Sopenharmony_ci	return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT;
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cibool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
6328c2ecf20Sopenharmony_ci				     struct ieee80211_sta *sta)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
6358c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
6368c2ecf20Sopenharmony_ci	struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt;
6378c2ecf20Sopenharmony_ci	enum iwl_bt_coex_lut_type lut_type;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (mvm->last_bt_notif.ttc_status & BIT(phy_ctxt->id))
6408c2ecf20Sopenharmony_ci		return true;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
6438c2ecf20Sopenharmony_ci	    BT_HIGH_TRAFFIC)
6448c2ecf20Sopenharmony_ci		return true;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	/*
6478c2ecf20Sopenharmony_ci	 * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas
6488c2ecf20Sopenharmony_ci	 * since BT is already killed.
6498c2ecf20Sopenharmony_ci	 * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while
6508c2ecf20Sopenharmony_ci	 * we Tx.
6518c2ecf20Sopenharmony_ci	 * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO.
6528c2ecf20Sopenharmony_ci	 */
6538c2ecf20Sopenharmony_ci	lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
6548c2ecf20Sopenharmony_ci	return lut_type != BT_COEX_LOOSE_LUT;
6558c2ecf20Sopenharmony_ci}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_cibool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant)
6588c2ecf20Sopenharmony_ci{
6598c2ecf20Sopenharmony_ci	/* there is no other antenna, shared antenna is always available */
6608c2ecf20Sopenharmony_ci	if (mvm->cfg->bt_shared_single_ant)
6618c2ecf20Sopenharmony_ci		return true;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	if (ant & mvm->cfg->non_shared_ant)
6648c2ecf20Sopenharmony_ci		return true;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
6678c2ecf20Sopenharmony_ci		BT_HIGH_TRAFFIC;
6688c2ecf20Sopenharmony_ci}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_cibool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	/* there is no other antenna, shared antenna is always available */
6738c2ecf20Sopenharmony_ci	if (mvm->cfg->bt_shared_single_ant)
6748c2ecf20Sopenharmony_ci		return true;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC;
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_cibool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
6808c2ecf20Sopenharmony_ci				    enum nl80211_band band)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	if (band != NL80211_BAND_2GHZ)
6858c2ecf20Sopenharmony_ci		return false;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	return bt_activity >= BT_LOW_TRAFFIC;
6888c2ecf20Sopenharmony_ci}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ciu8 iwl_mvm_bt_coex_get_single_ant_msk(struct iwl_mvm *mvm, u8 enabled_ants)
6918c2ecf20Sopenharmony_ci{
6928c2ecf20Sopenharmony_ci	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2) &&
6938c2ecf20Sopenharmony_ci	    (mvm->cfg->non_shared_ant & enabled_ants))
6948c2ecf20Sopenharmony_ci		return mvm->cfg->non_shared_ant;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	return first_antenna(enabled_ants);
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ciu8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
7008c2ecf20Sopenharmony_ci			   struct ieee80211_tx_info *info, u8 ac)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	__le16 fc = hdr->frame_control;
7038c2ecf20Sopenharmony_ci	bool mplut_enabled = iwl_mvm_is_mplut_supported(mvm);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	if (info->band != NL80211_BAND_2GHZ)
7068c2ecf20Sopenharmony_ci		return 0;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	if (unlikely(mvm->bt_tx_prio))
7098c2ecf20Sopenharmony_ci		return mvm->bt_tx_prio - 1;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	if (likely(ieee80211_is_data(fc))) {
7128c2ecf20Sopenharmony_ci		if (likely(ieee80211_is_data_qos(fc))) {
7138c2ecf20Sopenharmony_ci			switch (ac) {
7148c2ecf20Sopenharmony_ci			case IEEE80211_AC_BE:
7158c2ecf20Sopenharmony_ci				return mplut_enabled ? 1 : 0;
7168c2ecf20Sopenharmony_ci			case IEEE80211_AC_VI:
7178c2ecf20Sopenharmony_ci				return mplut_enabled ? 2 : 3;
7188c2ecf20Sopenharmony_ci			case IEEE80211_AC_VO:
7198c2ecf20Sopenharmony_ci				return 3;
7208c2ecf20Sopenharmony_ci			default:
7218c2ecf20Sopenharmony_ci				return 0;
7228c2ecf20Sopenharmony_ci			}
7238c2ecf20Sopenharmony_ci		} else if (is_multicast_ether_addr(hdr->addr1)) {
7248c2ecf20Sopenharmony_ci			return 3;
7258c2ecf20Sopenharmony_ci		} else
7268c2ecf20Sopenharmony_ci			return 0;
7278c2ecf20Sopenharmony_ci	} else if (ieee80211_is_mgmt(fc)) {
7288c2ecf20Sopenharmony_ci		return ieee80211_is_disassoc(fc) ? 0 : 3;
7298c2ecf20Sopenharmony_ci	} else if (ieee80211_is_ctl(fc)) {
7308c2ecf20Sopenharmony_ci		/* ignore cfend and cfendack frames as we never send those */
7318c2ecf20Sopenharmony_ci		return 3;
7328c2ecf20Sopenharmony_ci	}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	return 0;
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_civoid iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm)
7388c2ecf20Sopenharmony_ci{
7398c2ecf20Sopenharmony_ci	iwl_mvm_bt_coex_notif_handle(mvm);
7408c2ecf20Sopenharmony_ci}
741