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) 2012 - 2014 Intel Corporation. All rights reserved.
98c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
108c2ecf20Sopenharmony_ci * Copyright(c) 2017        Intel Deutschland GmbH
118c2ecf20Sopenharmony_ci * Copyright(c) 2018 - 2020 Intel Corporation
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
148c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as
158c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but
188c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
198c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
208c2ecf20Sopenharmony_ci * General Public License for more details.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * The full GNU General Public License is included in this distribution
238c2ecf20Sopenharmony_ci * in the file called COPYING.
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * Contact Information:
268c2ecf20Sopenharmony_ci *  Intel Linux Wireless <linuxwifi@intel.com>
278c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * BSD LICENSE
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
328c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
338c2ecf20Sopenharmony_ci * Copyright(c) 2018 - 2020 Intel Corporation
348c2ecf20Sopenharmony_ci * All rights reserved.
358c2ecf20Sopenharmony_ci *
368c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
378c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
388c2ecf20Sopenharmony_ci * are met:
398c2ecf20Sopenharmony_ci *
408c2ecf20Sopenharmony_ci *  * Redistributions of source code must retain the above copyright
418c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
428c2ecf20Sopenharmony_ci *  * Redistributions in binary form must reproduce the above copyright
438c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in
448c2ecf20Sopenharmony_ci *    the documentation and/or other materials provided with the
458c2ecf20Sopenharmony_ci *    distribution.
468c2ecf20Sopenharmony_ci *  * Neither the name Intel Corporation nor the names of its
478c2ecf20Sopenharmony_ci *    contributors may be used to endorse or promote products derived
488c2ecf20Sopenharmony_ci *    from this software without specific prior written permission.
498c2ecf20Sopenharmony_ci *
508c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
518c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
528c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
538c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
548c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
558c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
568c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
578c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
588c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
598c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
608c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
618c2ecf20Sopenharmony_ci *
628c2ecf20Sopenharmony_ci *****************************************************************************/
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#include <net/mac80211.h>
658c2ecf20Sopenharmony_ci#include "fw-api.h"
668c2ecf20Sopenharmony_ci#include "mvm.h"
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* Maps the driver specific channel width definition to the fw values */
698c2ecf20Sopenharmony_ciu8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	switch (chandef->width) {
728c2ecf20Sopenharmony_ci	case NL80211_CHAN_WIDTH_20_NOHT:
738c2ecf20Sopenharmony_ci	case NL80211_CHAN_WIDTH_20:
748c2ecf20Sopenharmony_ci		return PHY_VHT_CHANNEL_MODE20;
758c2ecf20Sopenharmony_ci	case NL80211_CHAN_WIDTH_40:
768c2ecf20Sopenharmony_ci		return PHY_VHT_CHANNEL_MODE40;
778c2ecf20Sopenharmony_ci	case NL80211_CHAN_WIDTH_80:
788c2ecf20Sopenharmony_ci		return PHY_VHT_CHANNEL_MODE80;
798c2ecf20Sopenharmony_ci	case NL80211_CHAN_WIDTH_160:
808c2ecf20Sopenharmony_ci		return PHY_VHT_CHANNEL_MODE160;
818c2ecf20Sopenharmony_ci	default:
828c2ecf20Sopenharmony_ci		WARN(1, "Invalid channel width=%u", chandef->width);
838c2ecf20Sopenharmony_ci		return PHY_VHT_CHANNEL_MODE20;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/*
888c2ecf20Sopenharmony_ci * Maps the driver specific control channel position (relative to the center
898c2ecf20Sopenharmony_ci * freq) definitions to the the fw values
908c2ecf20Sopenharmony_ci */
918c2ecf20Sopenharmony_ciu8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	switch (chandef->chan->center_freq - chandef->center_freq1) {
948c2ecf20Sopenharmony_ci	case -70:
958c2ecf20Sopenharmony_ci		return PHY_VHT_CTRL_POS_4_BELOW;
968c2ecf20Sopenharmony_ci	case -50:
978c2ecf20Sopenharmony_ci		return PHY_VHT_CTRL_POS_3_BELOW;
988c2ecf20Sopenharmony_ci	case -30:
998c2ecf20Sopenharmony_ci		return PHY_VHT_CTRL_POS_2_BELOW;
1008c2ecf20Sopenharmony_ci	case -10:
1018c2ecf20Sopenharmony_ci		return PHY_VHT_CTRL_POS_1_BELOW;
1028c2ecf20Sopenharmony_ci	case  10:
1038c2ecf20Sopenharmony_ci		return PHY_VHT_CTRL_POS_1_ABOVE;
1048c2ecf20Sopenharmony_ci	case  30:
1058c2ecf20Sopenharmony_ci		return PHY_VHT_CTRL_POS_2_ABOVE;
1068c2ecf20Sopenharmony_ci	case  50:
1078c2ecf20Sopenharmony_ci		return PHY_VHT_CTRL_POS_3_ABOVE;
1088c2ecf20Sopenharmony_ci	case  70:
1098c2ecf20Sopenharmony_ci		return PHY_VHT_CTRL_POS_4_ABOVE;
1108c2ecf20Sopenharmony_ci	default:
1118c2ecf20Sopenharmony_ci		WARN(1, "Invalid channel definition");
1128c2ecf20Sopenharmony_ci		/* fall through */
1138c2ecf20Sopenharmony_ci	case 0:
1148c2ecf20Sopenharmony_ci		/*
1158c2ecf20Sopenharmony_ci		 * The FW is expected to check the control channel position only
1168c2ecf20Sopenharmony_ci		 * when in HT/VHT and the channel width is not 20MHz. Return
1178c2ecf20Sopenharmony_ci		 * this value as the default one.
1188c2ecf20Sopenharmony_ci		 */
1198c2ecf20Sopenharmony_ci		return PHY_VHT_CTRL_POS_1_BELOW;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/*
1248c2ecf20Sopenharmony_ci * Construct the generic fields of the PHY context command
1258c2ecf20Sopenharmony_ci */
1268c2ecf20Sopenharmony_cistatic void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt,
1278c2ecf20Sopenharmony_ci				     struct iwl_phy_context_cmd *cmd,
1288c2ecf20Sopenharmony_ci				     u32 action)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id,
1318c2ecf20Sopenharmony_ci							    ctxt->color));
1328c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le32(action);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic void iwl_mvm_phy_ctxt_set_rxchain(struct iwl_mvm *mvm,
1368c2ecf20Sopenharmony_ci					 __le32 *rxchain_info,
1378c2ecf20Sopenharmony_ci					 u8 chains_static,
1388c2ecf20Sopenharmony_ci					 u8 chains_dynamic)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	u8 active_cnt, idle_cnt;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* Set rx the chains */
1438c2ecf20Sopenharmony_ci	idle_cnt = chains_static;
1448c2ecf20Sopenharmony_ci	active_cnt = chains_dynamic;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* In scenarios where we only ever use a single-stream rates,
1478c2ecf20Sopenharmony_ci	 * i.e. legacy 11b/g/a associations, single-stream APs or even
1488c2ecf20Sopenharmony_ci	 * static SMPS, enable both chains to get diversity, improving
1498c2ecf20Sopenharmony_ci	 * the case where we're far enough from the AP that attenuation
1508c2ecf20Sopenharmony_ci	 * between the two antennas is sufficiently different to impact
1518c2ecf20Sopenharmony_ci	 * performance.
1528c2ecf20Sopenharmony_ci	 */
1538c2ecf20Sopenharmony_ci	if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm)) {
1548c2ecf20Sopenharmony_ci		idle_cnt = 2;
1558c2ecf20Sopenharmony_ci		active_cnt = 2;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	*rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) <<
1598c2ecf20Sopenharmony_ci					PHY_RX_CHAIN_VALID_POS);
1608c2ecf20Sopenharmony_ci	*rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
1618c2ecf20Sopenharmony_ci	*rxchain_info |= cpu_to_le32(active_cnt <<
1628c2ecf20Sopenharmony_ci					 PHY_RX_CHAIN_MIMO_CNT_POS);
1638c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS
1648c2ecf20Sopenharmony_ci	if (unlikely(mvm->dbgfs_rx_phyinfo))
1658c2ecf20Sopenharmony_ci		*rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo);
1668c2ecf20Sopenharmony_ci#endif
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci/*
1708c2ecf20Sopenharmony_ci * Add the phy configuration to the PHY context command
1718c2ecf20Sopenharmony_ci */
1728c2ecf20Sopenharmony_cistatic void iwl_mvm_phy_ctxt_cmd_data_v1(struct iwl_mvm *mvm,
1738c2ecf20Sopenharmony_ci					 struct iwl_phy_context_cmd_v1 *cmd,
1748c2ecf20Sopenharmony_ci					 struct cfg80211_chan_def *chandef,
1758c2ecf20Sopenharmony_ci					 u8 chains_static, u8 chains_dynamic)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct iwl_phy_context_cmd_tail *tail =
1788c2ecf20Sopenharmony_ci		iwl_mvm_chan_info_cmd_tail(mvm, &cmd->ci);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* Set the channel info data */
1818c2ecf20Sopenharmony_ci	iwl_mvm_set_chan_info_chandef(mvm, &cmd->ci, chandef);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	iwl_mvm_phy_ctxt_set_rxchain(mvm, &tail->rxchain_info,
1848c2ecf20Sopenharmony_ci				     chains_static, chains_dynamic);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	tail->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci/*
1908c2ecf20Sopenharmony_ci * Add the phy configuration to the PHY context command
1918c2ecf20Sopenharmony_ci */
1928c2ecf20Sopenharmony_cistatic void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
1938c2ecf20Sopenharmony_ci				      struct iwl_phy_context_cmd *cmd,
1948c2ecf20Sopenharmony_ci				      struct cfg80211_chan_def *chandef,
1958c2ecf20Sopenharmony_ci				      u8 chains_static, u8 chains_dynamic)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	cmd->lmac_id = cpu_to_le32(iwl_mvm_get_lmac_id(mvm->fw,
1988c2ecf20Sopenharmony_ci						       chandef->chan->band));
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	/* Set the channel info data */
2018c2ecf20Sopenharmony_ci	iwl_mvm_set_chan_info_chandef(mvm, &cmd->ci, chandef);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	iwl_mvm_phy_ctxt_set_rxchain(mvm, &cmd->rxchain_info,
2048c2ecf20Sopenharmony_ci				     chains_static, chains_dynamic);
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/*
2088c2ecf20Sopenharmony_ci * Send a command to apply the current phy configuration. The command is send
2098c2ecf20Sopenharmony_ci * only if something in the configuration changed: in case that this is the
2108c2ecf20Sopenharmony_ci * first time that the phy configuration is applied or in case that the phy
2118c2ecf20Sopenharmony_ci * configuration changed from the previous apply.
2128c2ecf20Sopenharmony_ci */
2138c2ecf20Sopenharmony_cistatic int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
2148c2ecf20Sopenharmony_ci				  struct iwl_mvm_phy_ctxt *ctxt,
2158c2ecf20Sopenharmony_ci				  struct cfg80211_chan_def *chandef,
2168c2ecf20Sopenharmony_ci				  u8 chains_static, u8 chains_dynamic,
2178c2ecf20Sopenharmony_ci				  u32 action)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	int ret;
2208c2ecf20Sopenharmony_ci	int ver = iwl_fw_lookup_cmd_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP,
2218c2ecf20Sopenharmony_ci					PHY_CONTEXT_CMD, 1);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (ver == 3) {
2248c2ecf20Sopenharmony_ci		struct iwl_phy_context_cmd cmd = {};
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		/* Set the command header fields */
2278c2ecf20Sopenharmony_ci		iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		/* Set the command data */
2308c2ecf20Sopenharmony_ci		iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef,
2318c2ecf20Sopenharmony_ci					  chains_static,
2328c2ecf20Sopenharmony_ci					  chains_dynamic);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci		ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD,
2358c2ecf20Sopenharmony_ci					   0, sizeof(cmd), &cmd);
2368c2ecf20Sopenharmony_ci	} else if (ver < 3) {
2378c2ecf20Sopenharmony_ci		struct iwl_phy_context_cmd_v1 cmd = {};
2388c2ecf20Sopenharmony_ci		u16 len = sizeof(cmd) - iwl_mvm_chan_info_padding(mvm);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci		/* Set the command header fields */
2418c2ecf20Sopenharmony_ci		iwl_mvm_phy_ctxt_cmd_hdr(ctxt,
2428c2ecf20Sopenharmony_ci					 (struct iwl_phy_context_cmd *)&cmd,
2438c2ecf20Sopenharmony_ci					 action);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci		/* Set the command data */
2468c2ecf20Sopenharmony_ci		iwl_mvm_phy_ctxt_cmd_data_v1(mvm, &cmd, chandef,
2478c2ecf20Sopenharmony_ci					     chains_static,
2488c2ecf20Sopenharmony_ci					     chains_dynamic);
2498c2ecf20Sopenharmony_ci		ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD,
2508c2ecf20Sopenharmony_ci					   0, len, &cmd);
2518c2ecf20Sopenharmony_ci	} else {
2528c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "PHY ctxt cmd error ver %d not supported\n", ver);
2538c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (ret)
2588c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret);
2598c2ecf20Sopenharmony_ci	return ret;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci/*
2638c2ecf20Sopenharmony_ci * Send a command to add a PHY context based on the current HW configuration.
2648c2ecf20Sopenharmony_ci */
2658c2ecf20Sopenharmony_ciint iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
2668c2ecf20Sopenharmony_ci			 struct cfg80211_chan_def *chandef,
2678c2ecf20Sopenharmony_ci			 u8 chains_static, u8 chains_dynamic)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
2708c2ecf20Sopenharmony_ci		ctxt->ref);
2718c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	ctxt->channel = chandef->chan;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
2768c2ecf20Sopenharmony_ci				      chains_static, chains_dynamic,
2778c2ecf20Sopenharmony_ci				      FW_CTXT_ACTION_ADD);
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci/*
2818c2ecf20Sopenharmony_ci * Update the number of references to the given PHY context. This is valid only
2828c2ecf20Sopenharmony_ci * in case the PHY context was already created, i.e., its reference count > 0.
2838c2ecf20Sopenharmony_ci */
2848c2ecf20Sopenharmony_civoid iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
2878c2ecf20Sopenharmony_ci	ctxt->ref++;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci/*
2918c2ecf20Sopenharmony_ci * Send a command to modify the PHY context based on the current HW
2928c2ecf20Sopenharmony_ci * configuration. Note that the function does not check that the configuration
2938c2ecf20Sopenharmony_ci * changed.
2948c2ecf20Sopenharmony_ci */
2958c2ecf20Sopenharmony_ciint iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
2968c2ecf20Sopenharmony_ci			     struct cfg80211_chan_def *chandef,
2978c2ecf20Sopenharmony_ci			     u8 chains_static, u8 chains_dynamic)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	enum iwl_ctxt_action action = FW_CTXT_ACTION_MODIFY;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	if (fw_has_capa(&mvm->fw->ucode_capa,
3048c2ecf20Sopenharmony_ci			IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT) &&
3058c2ecf20Sopenharmony_ci	    ctxt->channel->band != chandef->chan->band) {
3068c2ecf20Sopenharmony_ci		int ret;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		/* ... remove it here ...*/
3098c2ecf20Sopenharmony_ci		ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
3108c2ecf20Sopenharmony_ci					     chains_static, chains_dynamic,
3118c2ecf20Sopenharmony_ci					     FW_CTXT_ACTION_REMOVE);
3128c2ecf20Sopenharmony_ci		if (ret)
3138c2ecf20Sopenharmony_ci			return ret;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci		/* ... and proceed to add it again */
3168c2ecf20Sopenharmony_ci		action = FW_CTXT_ACTION_ADD;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	ctxt->channel = chandef->chan;
3208c2ecf20Sopenharmony_ci	ctxt->width = chandef->width;
3218c2ecf20Sopenharmony_ci	return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
3228c2ecf20Sopenharmony_ci				      chains_static, chains_dynamic,
3238c2ecf20Sopenharmony_ci				      action);
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_civoid iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(!ctxt))
3318c2ecf20Sopenharmony_ci		return;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	ctxt->ref--;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/*
3368c2ecf20Sopenharmony_ci	 * Move unused phy's to a default channel. When the phy is moved the,
3378c2ecf20Sopenharmony_ci	 * fw will cleanup immediate quiet bit if it was previously set,
3388c2ecf20Sopenharmony_ci	 * otherwise we might not be able to reuse this phy.
3398c2ecf20Sopenharmony_ci	 */
3408c2ecf20Sopenharmony_ci	if (ctxt->ref == 0) {
3418c2ecf20Sopenharmony_ci		struct ieee80211_channel *chan;
3428c2ecf20Sopenharmony_ci		struct cfg80211_chan_def chandef;
3438c2ecf20Sopenharmony_ci		struct ieee80211_supported_band *sband = NULL;
3448c2ecf20Sopenharmony_ci		enum nl80211_band band = NL80211_BAND_2GHZ;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		while (!sband && band < NUM_NL80211_BANDS)
3478c2ecf20Sopenharmony_ci			sband = mvm->hw->wiphy->bands[band++];
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci		if (WARN_ON(!sband))
3508c2ecf20Sopenharmony_ci			return;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci		chan = &sband->channels[0];
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci		cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
3558c2ecf20Sopenharmony_ci		iwl_mvm_phy_ctxt_changed(mvm, ctxt, &chandef, 1, 1);
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic void iwl_mvm_binding_iterator(void *_data, u8 *mac,
3608c2ecf20Sopenharmony_ci				     struct ieee80211_vif *vif)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	unsigned long *data = _data;
3638c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (!mvmvif->phy_ctxt)
3668c2ecf20Sopenharmony_ci		return;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_STATION ||
3698c2ecf20Sopenharmony_ci	    vif->type == NL80211_IFTYPE_AP)
3708c2ecf20Sopenharmony_ci		__set_bit(mvmvif->phy_ctxt->id, data);
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ciint iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	unsigned long phy_ctxt_counter = 0;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(mvm->hw,
3788c2ecf20Sopenharmony_ci						   IEEE80211_IFACE_ITER_NORMAL,
3798c2ecf20Sopenharmony_ci						   iwl_mvm_binding_iterator,
3808c2ecf20Sopenharmony_ci						   &phy_ctxt_counter);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	return hweight8(phy_ctxt_counter);
3838c2ecf20Sopenharmony_ci}
384