xref: /freebsd/sys/contrib/dev/iwlwifi/mvm/phy-ctxt.c (revision a4128aad8503277614f2d214011ef60a19447b83)
1bfcc09ddSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2bfcc09ddSBjoern A. Zeeb /*
3*a4128aadSBjoern A. Zeeb  * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
4bfcc09ddSBjoern A. Zeeb  * Copyright (C) 2013-2014 Intel Mobile Communications GmbH
5bfcc09ddSBjoern A. Zeeb  * Copyright (C) 2017 Intel Deutschland GmbH
6bfcc09ddSBjoern A. Zeeb  */
7bfcc09ddSBjoern A. Zeeb #include <net/mac80211.h>
8bfcc09ddSBjoern A. Zeeb #include "fw-api.h"
9bfcc09ddSBjoern A. Zeeb #include "mvm.h"
10bfcc09ddSBjoern A. Zeeb 
11bfcc09ddSBjoern A. Zeeb /* Maps the driver specific channel width definition to the fw values */
12*a4128aadSBjoern A. Zeeb u8 iwl_mvm_get_channel_width(const struct cfg80211_chan_def *chandef)
13bfcc09ddSBjoern A. Zeeb {
14bfcc09ddSBjoern A. Zeeb 	switch (chandef->width) {
15bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_20_NOHT:
16bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_20:
179af1bba4SBjoern A. Zeeb 		return IWL_PHY_CHANNEL_MODE20;
18bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_40:
199af1bba4SBjoern A. Zeeb 		return IWL_PHY_CHANNEL_MODE40;
20bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_80:
219af1bba4SBjoern A. Zeeb 		return IWL_PHY_CHANNEL_MODE80;
22bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_160:
239af1bba4SBjoern A. Zeeb 		return IWL_PHY_CHANNEL_MODE160;
249af1bba4SBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_320:
259af1bba4SBjoern A. Zeeb 		return IWL_PHY_CHANNEL_MODE320;
26bfcc09ddSBjoern A. Zeeb 	default:
27bfcc09ddSBjoern A. Zeeb 		WARN(1, "Invalid channel width=%u", chandef->width);
289af1bba4SBjoern A. Zeeb 		return IWL_PHY_CHANNEL_MODE20;
29bfcc09ddSBjoern A. Zeeb 	}
30bfcc09ddSBjoern A. Zeeb }
31bfcc09ddSBjoern A. Zeeb 
32bfcc09ddSBjoern A. Zeeb /*
33bfcc09ddSBjoern A. Zeeb  * Maps the driver specific control channel position (relative to the center
34bfcc09ddSBjoern A. Zeeb  * freq) definitions to the the fw values
35bfcc09ddSBjoern A. Zeeb  */
36*a4128aadSBjoern A. Zeeb u8 iwl_mvm_get_ctrl_pos(const struct cfg80211_chan_def *chandef)
37bfcc09ddSBjoern A. Zeeb {
389af1bba4SBjoern A. Zeeb 	int offs = chandef->chan->center_freq - chandef->center_freq1;
399af1bba4SBjoern A. Zeeb 	int abs_offs = abs(offs);
409af1bba4SBjoern A. Zeeb 	u8 ret;
419af1bba4SBjoern A. Zeeb 
429af1bba4SBjoern A. Zeeb 	if (offs == 0) {
43bfcc09ddSBjoern A. Zeeb 		/*
44bfcc09ddSBjoern A. Zeeb 		 * The FW is expected to check the control channel position only
45bfcc09ddSBjoern A. Zeeb 		 * when in HT/VHT and the channel width is not 20MHz. Return
46bfcc09ddSBjoern A. Zeeb 		 * this value as the default one.
47bfcc09ddSBjoern A. Zeeb 		 */
489af1bba4SBjoern A. Zeeb 		return 0;
49bfcc09ddSBjoern A. Zeeb 	}
509af1bba4SBjoern A. Zeeb 
519af1bba4SBjoern A. Zeeb 	/* this results in a value 0-7, i.e. fitting into 0b0111 */
529af1bba4SBjoern A. Zeeb 	ret = (abs_offs - 10) / 20;
539af1bba4SBjoern A. Zeeb 	/*
549af1bba4SBjoern A. Zeeb 	 * But we need the value to be in 0b1011 because 0b0100 is
559af1bba4SBjoern A. Zeeb 	 * IWL_PHY_CTRL_POS_ABOVE, so shift bit 2 up to land in
569af1bba4SBjoern A. Zeeb 	 * IWL_PHY_CTRL_POS_OFFS_EXT (0b1000)
579af1bba4SBjoern A. Zeeb 	 */
589af1bba4SBjoern A. Zeeb 	ret = (ret & IWL_PHY_CTRL_POS_OFFS_MSK) |
599af1bba4SBjoern A. Zeeb 	      ((ret & BIT(2)) << 1);
609af1bba4SBjoern A. Zeeb 	/* and add the above bit */
619af1bba4SBjoern A. Zeeb 	ret |= (offs > 0) * IWL_PHY_CTRL_POS_ABOVE;
629af1bba4SBjoern A. Zeeb 
639af1bba4SBjoern A. Zeeb 	return ret;
64bfcc09ddSBjoern A. Zeeb }
65bfcc09ddSBjoern A. Zeeb 
66bfcc09ddSBjoern A. Zeeb /*
67bfcc09ddSBjoern A. Zeeb  * Construct the generic fields of the PHY context command
68bfcc09ddSBjoern A. Zeeb  */
69bfcc09ddSBjoern A. Zeeb static void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt,
70bfcc09ddSBjoern A. Zeeb 				     struct iwl_phy_context_cmd *cmd,
71bfcc09ddSBjoern A. Zeeb 				     u32 action)
72bfcc09ddSBjoern A. Zeeb {
73bfcc09ddSBjoern A. Zeeb 	cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id,
74bfcc09ddSBjoern A. Zeeb 							    ctxt->color));
75bfcc09ddSBjoern A. Zeeb 	cmd->action = cpu_to_le32(action);
76bfcc09ddSBjoern A. Zeeb }
77bfcc09ddSBjoern A. Zeeb 
78bfcc09ddSBjoern A. Zeeb static void iwl_mvm_phy_ctxt_set_rxchain(struct iwl_mvm *mvm,
79bfcc09ddSBjoern A. Zeeb 					 struct iwl_mvm_phy_ctxt *ctxt,
80bfcc09ddSBjoern A. Zeeb 					 __le32 *rxchain_info,
81bfcc09ddSBjoern A. Zeeb 					 u8 chains_static,
82bfcc09ddSBjoern A. Zeeb 					 u8 chains_dynamic)
83bfcc09ddSBjoern A. Zeeb {
84bfcc09ddSBjoern A. Zeeb 	u8 active_cnt, idle_cnt;
85bfcc09ddSBjoern A. Zeeb 
86bfcc09ddSBjoern A. Zeeb 	/* Set rx the chains */
87bfcc09ddSBjoern A. Zeeb 	idle_cnt = chains_static;
88bfcc09ddSBjoern A. Zeeb 	active_cnt = chains_dynamic;
89bfcc09ddSBjoern A. Zeeb 
90bfcc09ddSBjoern A. Zeeb 	/* In scenarios where we only ever use a single-stream rates,
91bfcc09ddSBjoern A. Zeeb 	 * i.e. legacy 11b/g/a associations, single-stream APs or even
92bfcc09ddSBjoern A. Zeeb 	 * static SMPS, enable both chains to get diversity, improving
93bfcc09ddSBjoern A. Zeeb 	 * the case where we're far enough from the AP that attenuation
94bfcc09ddSBjoern A. Zeeb 	 * between the two antennas is sufficiently different to impact
95bfcc09ddSBjoern A. Zeeb 	 * performance.
96bfcc09ddSBjoern A. Zeeb 	 */
97bfcc09ddSBjoern A. Zeeb 	if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm, ctxt)) {
98bfcc09ddSBjoern A. Zeeb 		idle_cnt = 2;
99bfcc09ddSBjoern A. Zeeb 		active_cnt = 2;
100bfcc09ddSBjoern A. Zeeb 	}
101bfcc09ddSBjoern A. Zeeb 
102bfcc09ddSBjoern A. Zeeb 	*rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) <<
103bfcc09ddSBjoern A. Zeeb 					PHY_RX_CHAIN_VALID_POS);
104bfcc09ddSBjoern A. Zeeb 	*rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
105bfcc09ddSBjoern A. Zeeb 	*rxchain_info |= cpu_to_le32(active_cnt <<
106bfcc09ddSBjoern A. Zeeb 					 PHY_RX_CHAIN_MIMO_CNT_POS);
107bfcc09ddSBjoern A. Zeeb #ifdef CONFIG_IWLWIFI_DEBUGFS
108bfcc09ddSBjoern A. Zeeb 	if (unlikely(mvm->dbgfs_rx_phyinfo))
109bfcc09ddSBjoern A. Zeeb 		*rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo);
110bfcc09ddSBjoern A. Zeeb #endif
111bfcc09ddSBjoern A. Zeeb }
112bfcc09ddSBjoern A. Zeeb 
113bfcc09ddSBjoern A. Zeeb /*
114bfcc09ddSBjoern A. Zeeb  * Add the phy configuration to the PHY context command
115bfcc09ddSBjoern A. Zeeb  */
116bfcc09ddSBjoern A. Zeeb static void iwl_mvm_phy_ctxt_cmd_data_v1(struct iwl_mvm *mvm,
117bfcc09ddSBjoern A. Zeeb 					 struct iwl_mvm_phy_ctxt *ctxt,
118bfcc09ddSBjoern A. Zeeb 					 struct iwl_phy_context_cmd_v1 *cmd,
119*a4128aadSBjoern A. Zeeb 					 const struct cfg80211_chan_def *chandef,
120bfcc09ddSBjoern A. Zeeb 					 u8 chains_static, u8 chains_dynamic)
121bfcc09ddSBjoern A. Zeeb {
122bfcc09ddSBjoern A. Zeeb 	struct iwl_phy_context_cmd_tail *tail =
123bfcc09ddSBjoern A. Zeeb 		iwl_mvm_chan_info_cmd_tail(mvm, &cmd->ci);
124bfcc09ddSBjoern A. Zeeb 
125bfcc09ddSBjoern A. Zeeb 	/* Set the channel info data */
126bfcc09ddSBjoern A. Zeeb 	iwl_mvm_set_chan_info_chandef(mvm, &cmd->ci, chandef);
127bfcc09ddSBjoern A. Zeeb 
128bfcc09ddSBjoern A. Zeeb 	iwl_mvm_phy_ctxt_set_rxchain(mvm, ctxt, &tail->rxchain_info,
129bfcc09ddSBjoern A. Zeeb 				     chains_static, chains_dynamic);
130bfcc09ddSBjoern A. Zeeb 
131bfcc09ddSBjoern A. Zeeb 	tail->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
132bfcc09ddSBjoern A. Zeeb }
133bfcc09ddSBjoern A. Zeeb 
134bfcc09ddSBjoern A. Zeeb /*
135bfcc09ddSBjoern A. Zeeb  * Add the phy configuration to the PHY context command
136bfcc09ddSBjoern A. Zeeb  */
137bfcc09ddSBjoern A. Zeeb static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
138bfcc09ddSBjoern A. Zeeb 				      struct iwl_mvm_phy_ctxt *ctxt,
139bfcc09ddSBjoern A. Zeeb 				      struct iwl_phy_context_cmd *cmd,
140*a4128aadSBjoern A. Zeeb 				      const struct cfg80211_chan_def *chandef,
141bfcc09ddSBjoern A. Zeeb 				      u8 chains_static, u8 chains_dynamic)
142bfcc09ddSBjoern A. Zeeb {
1439af1bba4SBjoern A. Zeeb 	cmd->lmac_id = cpu_to_le32(iwl_mvm_get_lmac_id(mvm,
144bfcc09ddSBjoern A. Zeeb 						       chandef->chan->band));
145bfcc09ddSBjoern A. Zeeb 
146bfcc09ddSBjoern A. Zeeb 	/* Set the channel info data */
147bfcc09ddSBjoern A. Zeeb 	iwl_mvm_set_chan_info_chandef(mvm, &cmd->ci, chandef);
148bfcc09ddSBjoern A. Zeeb 
149d9836fb4SBjoern A. Zeeb 	/* we only support RLC command version 2 */
150d9836fb4SBjoern A. Zeeb 	if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, RLC_CONFIG_CMD), 0) < 2)
151bfcc09ddSBjoern A. Zeeb 		iwl_mvm_phy_ctxt_set_rxchain(mvm, ctxt, &cmd->rxchain_info,
152bfcc09ddSBjoern A. Zeeb 					     chains_static, chains_dynamic);
153bfcc09ddSBjoern A. Zeeb }
154bfcc09ddSBjoern A. Zeeb 
1559af1bba4SBjoern A. Zeeb int iwl_mvm_phy_send_rlc(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
156d9836fb4SBjoern A. Zeeb 			 u8 chains_static, u8 chains_dynamic)
157d9836fb4SBjoern A. Zeeb {
158d9836fb4SBjoern A. Zeeb 	struct iwl_rlc_config_cmd cmd = {
159d9836fb4SBjoern A. Zeeb 		.phy_id = cpu_to_le32(ctxt->id),
160d9836fb4SBjoern A. Zeeb 	};
161d9836fb4SBjoern A. Zeeb 
1629af1bba4SBjoern A. Zeeb 	if (ctxt->rlc_disabled)
1639af1bba4SBjoern A. Zeeb 		return 0;
1649af1bba4SBjoern A. Zeeb 
1659af1bba4SBjoern A. Zeeb 	if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP,
1669af1bba4SBjoern A. Zeeb 						   RLC_CONFIG_CMD), 0) < 2)
167d9836fb4SBjoern A. Zeeb 		return 0;
168d9836fb4SBjoern A. Zeeb 
169d9836fb4SBjoern A. Zeeb 	BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_DRIVER_FORCE !=
170d9836fb4SBjoern A. Zeeb 		     PHY_RX_CHAIN_DRIVER_FORCE_MSK);
171d9836fb4SBjoern A. Zeeb 	BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_VALID !=
172d9836fb4SBjoern A. Zeeb 		     PHY_RX_CHAIN_VALID_MSK);
173d9836fb4SBjoern A. Zeeb 	BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_FORCE !=
174d9836fb4SBjoern A. Zeeb 		     PHY_RX_CHAIN_FORCE_SEL_MSK);
175d9836fb4SBjoern A. Zeeb 	BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_FORCE_MIMO !=
176d9836fb4SBjoern A. Zeeb 		     PHY_RX_CHAIN_FORCE_MIMO_SEL_MSK);
177d9836fb4SBjoern A. Zeeb 	BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_COUNT != PHY_RX_CHAIN_CNT_MSK);
178d9836fb4SBjoern A. Zeeb 	BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_MIMO_COUNT !=
179d9836fb4SBjoern A. Zeeb 		     PHY_RX_CHAIN_MIMO_CNT_MSK);
180d9836fb4SBjoern A. Zeeb 
181d9836fb4SBjoern A. Zeeb 	iwl_mvm_phy_ctxt_set_rxchain(mvm, ctxt, &cmd.rlc.rx_chain_info,
182d9836fb4SBjoern A. Zeeb 				     chains_static, chains_dynamic);
183d9836fb4SBjoern A. Zeeb 
184*a4128aadSBjoern A. Zeeb 	IWL_DEBUG_FW(mvm, "Send RLC command: phy=%d, rx_chain_info=0x%x\n",
185*a4128aadSBjoern A. Zeeb 		     ctxt->id, cmd.rlc.rx_chain_info);
186*a4128aadSBjoern A. Zeeb 
187d9836fb4SBjoern A. Zeeb 	return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(RLC_CONFIG_CMD,
188d9836fb4SBjoern A. Zeeb 						    DATA_PATH_GROUP, 2),
189d9836fb4SBjoern A. Zeeb 				    0, sizeof(cmd), &cmd);
190d9836fb4SBjoern A. Zeeb }
191d9836fb4SBjoern A. Zeeb 
192bfcc09ddSBjoern A. Zeeb /*
193bfcc09ddSBjoern A. Zeeb  * Send a command to apply the current phy configuration. The command is send
194bfcc09ddSBjoern A. Zeeb  * only if something in the configuration changed: in case that this is the
195bfcc09ddSBjoern A. Zeeb  * first time that the phy configuration is applied or in case that the phy
196bfcc09ddSBjoern A. Zeeb  * configuration changed from the previous apply.
197bfcc09ddSBjoern A. Zeeb  */
198bfcc09ddSBjoern A. Zeeb static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
199bfcc09ddSBjoern A. Zeeb 				  struct iwl_mvm_phy_ctxt *ctxt,
200*a4128aadSBjoern A. Zeeb 				  const struct cfg80211_chan_def *chandef,
201*a4128aadSBjoern A. Zeeb 				  const struct cfg80211_chan_def *ap,
202bfcc09ddSBjoern A. Zeeb 				  u8 chains_static, u8 chains_dynamic,
203bfcc09ddSBjoern A. Zeeb 				  u32 action)
204bfcc09ddSBjoern A. Zeeb {
205bfcc09ddSBjoern A. Zeeb 	int ret;
206d9836fb4SBjoern A. Zeeb 	int ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1);
207bfcc09ddSBjoern A. Zeeb 
208*a4128aadSBjoern A. Zeeb 	if (ver < 5 || !ap || !ap->chan)
209*a4128aadSBjoern A. Zeeb 		ap = NULL;
210*a4128aadSBjoern A. Zeeb 
211*a4128aadSBjoern A. Zeeb 	if (ver >= 3 && ver <= 6) {
212bfcc09ddSBjoern A. Zeeb 		struct iwl_phy_context_cmd cmd = {};
213bfcc09ddSBjoern A. Zeeb 
214bfcc09ddSBjoern A. Zeeb 		/* Set the command header fields */
215bfcc09ddSBjoern A. Zeeb 		iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action);
216bfcc09ddSBjoern A. Zeeb 
217bfcc09ddSBjoern A. Zeeb 		/* Set the command data */
218bfcc09ddSBjoern A. Zeeb 		iwl_mvm_phy_ctxt_cmd_data(mvm, ctxt, &cmd, chandef,
219bfcc09ddSBjoern A. Zeeb 					  chains_static,
220bfcc09ddSBjoern A. Zeeb 					  chains_dynamic);
221bfcc09ddSBjoern A. Zeeb 
222*a4128aadSBjoern A. Zeeb 		if (ap) {
223*a4128aadSBjoern A. Zeeb 			cmd.sbb_bandwidth = iwl_mvm_get_channel_width(ap);
224*a4128aadSBjoern A. Zeeb 			cmd.sbb_ctrl_channel_loc = iwl_mvm_get_ctrl_pos(ap);
225*a4128aadSBjoern A. Zeeb 		}
226*a4128aadSBjoern A. Zeeb 
227*a4128aadSBjoern A. Zeeb 		if (ver == 6)
228*a4128aadSBjoern A. Zeeb 			cmd.puncture_mask = cpu_to_le16(chandef->punctured);
229*a4128aadSBjoern A. Zeeb 
230bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD,
231bfcc09ddSBjoern A. Zeeb 					   0, sizeof(cmd), &cmd);
232bfcc09ddSBjoern A. Zeeb 	} else if (ver < 3) {
233bfcc09ddSBjoern A. Zeeb 		struct iwl_phy_context_cmd_v1 cmd = {};
234bfcc09ddSBjoern A. Zeeb 		u16 len = sizeof(cmd) - iwl_mvm_chan_info_padding(mvm);
235bfcc09ddSBjoern A. Zeeb 
236bfcc09ddSBjoern A. Zeeb 		/* Set the command header fields */
237bfcc09ddSBjoern A. Zeeb 		iwl_mvm_phy_ctxt_cmd_hdr(ctxt,
238bfcc09ddSBjoern A. Zeeb 					 (struct iwl_phy_context_cmd *)&cmd,
239bfcc09ddSBjoern A. Zeeb 					 action);
240bfcc09ddSBjoern A. Zeeb 
241bfcc09ddSBjoern A. Zeeb 		/* Set the command data */
242bfcc09ddSBjoern A. Zeeb 		iwl_mvm_phy_ctxt_cmd_data_v1(mvm, ctxt, &cmd, chandef,
243bfcc09ddSBjoern A. Zeeb 					     chains_static,
244bfcc09ddSBjoern A. Zeeb 					     chains_dynamic);
245bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD,
246bfcc09ddSBjoern A. Zeeb 					   0, len, &cmd);
247bfcc09ddSBjoern A. Zeeb 	} else {
248bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "PHY ctxt cmd error ver %d not supported\n", ver);
249bfcc09ddSBjoern A. Zeeb 		return -EOPNOTSUPP;
250bfcc09ddSBjoern A. Zeeb 	}
251bfcc09ddSBjoern A. Zeeb 
252bfcc09ddSBjoern A. Zeeb 
253d9836fb4SBjoern A. Zeeb 	if (ret) {
254bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret);
255bfcc09ddSBjoern A. Zeeb 		return ret;
256bfcc09ddSBjoern A. Zeeb 	}
257bfcc09ddSBjoern A. Zeeb 
258d9836fb4SBjoern A. Zeeb 	if (action != FW_CTXT_ACTION_REMOVE)
259d9836fb4SBjoern A. Zeeb 		return iwl_mvm_phy_send_rlc(mvm, ctxt, chains_static,
260d9836fb4SBjoern A. Zeeb 					    chains_dynamic);
261d9836fb4SBjoern A. Zeeb 
262d9836fb4SBjoern A. Zeeb 	return 0;
263d9836fb4SBjoern A. Zeeb }
264d9836fb4SBjoern A. Zeeb 
265bfcc09ddSBjoern A. Zeeb /*
266bfcc09ddSBjoern A. Zeeb  * Send a command to add a PHY context based on the current HW configuration.
267bfcc09ddSBjoern A. Zeeb  */
268bfcc09ddSBjoern A. Zeeb int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
269*a4128aadSBjoern A. Zeeb 			 const struct cfg80211_chan_def *chandef,
270*a4128aadSBjoern A. Zeeb 			 const struct cfg80211_chan_def *ap,
271bfcc09ddSBjoern A. Zeeb 			 u8 chains_static, u8 chains_dynamic)
272bfcc09ddSBjoern A. Zeeb {
273*a4128aadSBjoern A. Zeeb 	int ret;
274*a4128aadSBjoern A. Zeeb 
275bfcc09ddSBjoern A. Zeeb 	WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
276bfcc09ddSBjoern A. Zeeb 		ctxt->ref);
277bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
278bfcc09ddSBjoern A. Zeeb 
279bfcc09ddSBjoern A. Zeeb 	ctxt->channel = chandef->chan;
280d9836fb4SBjoern A. Zeeb 	ctxt->width = chandef->width;
281d9836fb4SBjoern A. Zeeb 	ctxt->center_freq1 = chandef->center_freq1;
282bfcc09ddSBjoern A. Zeeb 
283*a4128aadSBjoern A. Zeeb 	ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, ap,
284bfcc09ddSBjoern A. Zeeb 				     chains_static, chains_dynamic,
285bfcc09ddSBjoern A. Zeeb 				     FW_CTXT_ACTION_ADD);
286*a4128aadSBjoern A. Zeeb 
287*a4128aadSBjoern A. Zeeb 	if (ret)
288*a4128aadSBjoern A. Zeeb 		return ret;
289*a4128aadSBjoern A. Zeeb 
290*a4128aadSBjoern A. Zeeb 	ctxt->ref++;
291*a4128aadSBjoern A. Zeeb 
292*a4128aadSBjoern A. Zeeb 	return 0;
293bfcc09ddSBjoern A. Zeeb }
294bfcc09ddSBjoern A. Zeeb 
295bfcc09ddSBjoern A. Zeeb /*
296bfcc09ddSBjoern A. Zeeb  * Update the number of references to the given PHY context. This is valid only
297bfcc09ddSBjoern A. Zeeb  * in case the PHY context was already created, i.e., its reference count > 0.
298bfcc09ddSBjoern A. Zeeb  */
299bfcc09ddSBjoern A. Zeeb void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
300bfcc09ddSBjoern A. Zeeb {
301bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
302*a4128aadSBjoern A. Zeeb 
303*a4128aadSBjoern A. Zeeb 	/* If we were taking the first ref, we should have
304*a4128aadSBjoern A. Zeeb 	 * called iwl_mvm_phy_ctxt_add.
305*a4128aadSBjoern A. Zeeb 	 */
306*a4128aadSBjoern A. Zeeb 	WARN_ON(!ctxt->ref);
307bfcc09ddSBjoern A. Zeeb 	ctxt->ref++;
308bfcc09ddSBjoern A. Zeeb }
309bfcc09ddSBjoern A. Zeeb 
310bfcc09ddSBjoern A. Zeeb /*
311bfcc09ddSBjoern A. Zeeb  * Send a command to modify the PHY context based on the current HW
312bfcc09ddSBjoern A. Zeeb  * configuration. Note that the function does not check that the configuration
313bfcc09ddSBjoern A. Zeeb  * changed.
314bfcc09ddSBjoern A. Zeeb  */
315bfcc09ddSBjoern A. Zeeb int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
316*a4128aadSBjoern A. Zeeb 			     const struct cfg80211_chan_def *chandef,
317*a4128aadSBjoern A. Zeeb 			     const struct cfg80211_chan_def *ap,
318bfcc09ddSBjoern A. Zeeb 			     u8 chains_static, u8 chains_dynamic)
319bfcc09ddSBjoern A. Zeeb {
320bfcc09ddSBjoern A. Zeeb 	enum iwl_ctxt_action action = FW_CTXT_ACTION_MODIFY;
321bfcc09ddSBjoern A. Zeeb 
322bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
323bfcc09ddSBjoern A. Zeeb 
324*a4128aadSBjoern A. Zeeb 	if (WARN_ON_ONCE(!ctxt->ref))
325*a4128aadSBjoern A. Zeeb 		return -EINVAL;
326*a4128aadSBjoern A. Zeeb 
327*a4128aadSBjoern A. Zeeb 	if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP,
328*a4128aadSBjoern A. Zeeb 						   RLC_CONFIG_CMD), 0) >= 2 &&
329d9836fb4SBjoern A. Zeeb 	    ctxt->channel == chandef->chan &&
330d9836fb4SBjoern A. Zeeb 	    ctxt->width == chandef->width &&
331d9836fb4SBjoern A. Zeeb 	    ctxt->center_freq1 == chandef->center_freq1)
332d9836fb4SBjoern A. Zeeb 		return iwl_mvm_phy_send_rlc(mvm, ctxt, chains_static,
333d9836fb4SBjoern A. Zeeb 					    chains_dynamic);
334d9836fb4SBjoern A. Zeeb 
335bfcc09ddSBjoern A. Zeeb 	if (fw_has_capa(&mvm->fw->ucode_capa,
336bfcc09ddSBjoern A. Zeeb 			IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT) &&
337bfcc09ddSBjoern A. Zeeb 	    ctxt->channel->band != chandef->chan->band) {
338bfcc09ddSBjoern A. Zeeb 		int ret;
339bfcc09ddSBjoern A. Zeeb 
340bfcc09ddSBjoern A. Zeeb 		/* ... remove it here ...*/
341*a4128aadSBjoern A. Zeeb 		ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, NULL,
342bfcc09ddSBjoern A. Zeeb 					     chains_static, chains_dynamic,
343bfcc09ddSBjoern A. Zeeb 					     FW_CTXT_ACTION_REMOVE);
344bfcc09ddSBjoern A. Zeeb 		if (ret)
345bfcc09ddSBjoern A. Zeeb 			return ret;
346bfcc09ddSBjoern A. Zeeb 
347bfcc09ddSBjoern A. Zeeb 		/* ... and proceed to add it again */
348bfcc09ddSBjoern A. Zeeb 		action = FW_CTXT_ACTION_ADD;
349bfcc09ddSBjoern A. Zeeb 	}
350bfcc09ddSBjoern A. Zeeb 
351bfcc09ddSBjoern A. Zeeb 	ctxt->channel = chandef->chan;
352bfcc09ddSBjoern A. Zeeb 	ctxt->width = chandef->width;
353d9836fb4SBjoern A. Zeeb 	ctxt->center_freq1 = chandef->center_freq1;
354d9836fb4SBjoern A. Zeeb 
355*a4128aadSBjoern A. Zeeb 	return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, ap,
356bfcc09ddSBjoern A. Zeeb 				      chains_static, chains_dynamic,
357bfcc09ddSBjoern A. Zeeb 				      action);
358bfcc09ddSBjoern A. Zeeb }
359bfcc09ddSBjoern A. Zeeb 
360bfcc09ddSBjoern A. Zeeb void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
361bfcc09ddSBjoern A. Zeeb {
362*a4128aadSBjoern A. Zeeb 	struct cfg80211_chan_def chandef;
363bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
364bfcc09ddSBjoern A. Zeeb 
365bfcc09ddSBjoern A. Zeeb 	if (WARN_ON_ONCE(!ctxt))
366bfcc09ddSBjoern A. Zeeb 		return;
367bfcc09ddSBjoern A. Zeeb 
368bfcc09ddSBjoern A. Zeeb 	ctxt->ref--;
369bfcc09ddSBjoern A. Zeeb 
370*a4128aadSBjoern A. Zeeb 	if (ctxt->ref)
371bfcc09ddSBjoern A. Zeeb 		return;
372bfcc09ddSBjoern A. Zeeb 
373*a4128aadSBjoern A. Zeeb 	cfg80211_chandef_create(&chandef, ctxt->channel, NL80211_CHAN_NO_HT);
374*a4128aadSBjoern A. Zeeb 
375*a4128aadSBjoern A. Zeeb 	iwl_mvm_phy_ctxt_apply(mvm, ctxt, &chandef, NULL, 1, 1,
376*a4128aadSBjoern A. Zeeb 			       FW_CTXT_ACTION_REMOVE);
377bfcc09ddSBjoern A. Zeeb }
378bfcc09ddSBjoern A. Zeeb 
379bfcc09ddSBjoern A. Zeeb static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
380bfcc09ddSBjoern A. Zeeb 				     struct ieee80211_vif *vif)
381bfcc09ddSBjoern A. Zeeb {
382bfcc09ddSBjoern A. Zeeb 	unsigned long *data = _data;
383bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
384bfcc09ddSBjoern A. Zeeb 
3859af1bba4SBjoern A. Zeeb 	if (!mvmvif->deflink.phy_ctxt)
386bfcc09ddSBjoern A. Zeeb 		return;
387bfcc09ddSBjoern A. Zeeb 
388bfcc09ddSBjoern A. Zeeb 	if (vif->type == NL80211_IFTYPE_STATION ||
389bfcc09ddSBjoern A. Zeeb 	    vif->type == NL80211_IFTYPE_AP)
3909af1bba4SBjoern A. Zeeb 		__set_bit(mvmvif->deflink.phy_ctxt->id, data);
391bfcc09ddSBjoern A. Zeeb }
392bfcc09ddSBjoern A. Zeeb 
393bfcc09ddSBjoern A. Zeeb int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm)
394bfcc09ddSBjoern A. Zeeb {
395bfcc09ddSBjoern A. Zeeb 	unsigned long phy_ctxt_counter = 0;
396bfcc09ddSBjoern A. Zeeb 
397bfcc09ddSBjoern A. Zeeb 	ieee80211_iterate_active_interfaces_atomic(mvm->hw,
398bfcc09ddSBjoern A. Zeeb 						   IEEE80211_IFACE_ITER_NORMAL,
399bfcc09ddSBjoern A. Zeeb 						   iwl_mvm_binding_iterator,
400bfcc09ddSBjoern A. Zeeb 						   &phy_ctxt_counter);
401bfcc09ddSBjoern A. Zeeb 
402bfcc09ddSBjoern A. Zeeb 	return hweight8(phy_ctxt_counter);
403bfcc09ddSBjoern A. Zeeb }
404