1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
3 * Copyright (C) 2012-2014, 2018-2020 Intel Corporation
4 * Copyright (C) 2013-2014 Intel Mobile Communications GmbH
5 * Copyright (C) 2017 Intel Deutschland GmbH
7 #include <net/mac80211.h>
11 /* Maps the driver specific channel width definition to the fw values */
12 u8
iwl_mvm_get_channel_width(struct cfg80211_chan_def
*chandef
)
14 switch (chandef
->width
) {
15 case NL80211_CHAN_WIDTH_20_NOHT
:
16 case NL80211_CHAN_WIDTH_20
:
17 return PHY_VHT_CHANNEL_MODE20
;
18 case NL80211_CHAN_WIDTH_40
:
19 return PHY_VHT_CHANNEL_MODE40
;
20 case NL80211_CHAN_WIDTH_80
:
21 return PHY_VHT_CHANNEL_MODE80
;
22 case NL80211_CHAN_WIDTH_160
:
23 return PHY_VHT_CHANNEL_MODE160
;
25 WARN(1, "Invalid channel width=%u", chandef
->width
);
26 return PHY_VHT_CHANNEL_MODE20
;
31 * Maps the driver specific control channel position (relative to the center
32 * freq) definitions to the the fw values
34 u8
iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def
*chandef
)
36 switch (chandef
->chan
->center_freq
- chandef
->center_freq1
) {
38 return PHY_VHT_CTRL_POS_4_BELOW
;
40 return PHY_VHT_CTRL_POS_3_BELOW
;
42 return PHY_VHT_CTRL_POS_2_BELOW
;
44 return PHY_VHT_CTRL_POS_1_BELOW
;
46 return PHY_VHT_CTRL_POS_1_ABOVE
;
48 return PHY_VHT_CTRL_POS_2_ABOVE
;
50 return PHY_VHT_CTRL_POS_3_ABOVE
;
52 return PHY_VHT_CTRL_POS_4_ABOVE
;
54 WARN(1, "Invalid channel definition");
58 * The FW is expected to check the control channel position only
59 * when in HT/VHT and the channel width is not 20MHz. Return
60 * this value as the default one.
62 return PHY_VHT_CTRL_POS_1_BELOW
;
67 * Construct the generic fields of the PHY context command
69 static void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt
*ctxt
,
70 struct iwl_phy_context_cmd
*cmd
,
73 cmd
->id_and_color
= cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt
->id
,
75 cmd
->action
= cpu_to_le32(action
);
78 static void iwl_mvm_phy_ctxt_set_rxchain(struct iwl_mvm
*mvm
,
83 u8 active_cnt
, idle_cnt
;
85 /* Set rx the chains */
86 idle_cnt
= chains_static
;
87 active_cnt
= chains_dynamic
;
89 /* In scenarios where we only ever use a single-stream rates,
90 * i.e. legacy 11b/g/a associations, single-stream APs or even
91 * static SMPS, enable both chains to get diversity, improving
92 * the case where we're far enough from the AP that attenuation
93 * between the two antennas is sufficiently different to impact
96 if (active_cnt
== 1 && iwl_mvm_rx_diversity_allowed(mvm
)) {
101 *rxchain_info
= cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm
) <<
102 PHY_RX_CHAIN_VALID_POS
);
103 *rxchain_info
|= cpu_to_le32(idle_cnt
<< PHY_RX_CHAIN_CNT_POS
);
104 *rxchain_info
|= cpu_to_le32(active_cnt
<<
105 PHY_RX_CHAIN_MIMO_CNT_POS
);
106 #ifdef CONFIG_IWLWIFI_DEBUGFS
107 if (unlikely(mvm
->dbgfs_rx_phyinfo
))
108 *rxchain_info
= cpu_to_le32(mvm
->dbgfs_rx_phyinfo
);
113 * Add the phy configuration to the PHY context command
115 static void iwl_mvm_phy_ctxt_cmd_data_v1(struct iwl_mvm
*mvm
,
116 struct iwl_phy_context_cmd_v1
*cmd
,
117 struct cfg80211_chan_def
*chandef
,
118 u8 chains_static
, u8 chains_dynamic
)
120 struct iwl_phy_context_cmd_tail
*tail
=
121 iwl_mvm_chan_info_cmd_tail(mvm
, &cmd
->ci
);
123 /* Set the channel info data */
124 iwl_mvm_set_chan_info_chandef(mvm
, &cmd
->ci
, chandef
);
126 iwl_mvm_phy_ctxt_set_rxchain(mvm
, &tail
->rxchain_info
,
127 chains_static
, chains_dynamic
);
129 tail
->txchain_info
= cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm
));
133 * Add the phy configuration to the PHY context command
135 static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm
*mvm
,
136 struct iwl_phy_context_cmd
*cmd
,
137 struct cfg80211_chan_def
*chandef
,
138 u8 chains_static
, u8 chains_dynamic
)
140 cmd
->lmac_id
= cpu_to_le32(iwl_mvm_get_lmac_id(mvm
->fw
,
141 chandef
->chan
->band
));
143 /* Set the channel info data */
144 iwl_mvm_set_chan_info_chandef(mvm
, &cmd
->ci
, chandef
);
146 iwl_mvm_phy_ctxt_set_rxchain(mvm
, &cmd
->rxchain_info
,
147 chains_static
, chains_dynamic
);
151 * Send a command to apply the current phy configuration. The command is send
152 * only if something in the configuration changed: in case that this is the
153 * first time that the phy configuration is applied or in case that the phy
154 * configuration changed from the previous apply.
156 static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm
*mvm
,
157 struct iwl_mvm_phy_ctxt
*ctxt
,
158 struct cfg80211_chan_def
*chandef
,
159 u8 chains_static
, u8 chains_dynamic
,
163 int ver
= iwl_fw_lookup_cmd_ver(mvm
->fw
, IWL_ALWAYS_LONG_GROUP
,
167 struct iwl_phy_context_cmd cmd
= {};
169 /* Set the command header fields */
170 iwl_mvm_phy_ctxt_cmd_hdr(ctxt
, &cmd
, action
);
172 /* Set the command data */
173 iwl_mvm_phy_ctxt_cmd_data(mvm
, &cmd
, chandef
,
177 ret
= iwl_mvm_send_cmd_pdu(mvm
, PHY_CONTEXT_CMD
,
178 0, sizeof(cmd
), &cmd
);
179 } else if (ver
< 3) {
180 struct iwl_phy_context_cmd_v1 cmd
= {};
181 u16 len
= sizeof(cmd
) - iwl_mvm_chan_info_padding(mvm
);
183 /* Set the command header fields */
184 iwl_mvm_phy_ctxt_cmd_hdr(ctxt
,
185 (struct iwl_phy_context_cmd
*)&cmd
,
188 /* Set the command data */
189 iwl_mvm_phy_ctxt_cmd_data_v1(mvm
, &cmd
, chandef
,
192 ret
= iwl_mvm_send_cmd_pdu(mvm
, PHY_CONTEXT_CMD
,
195 IWL_ERR(mvm
, "PHY ctxt cmd error ver %d not supported\n", ver
);
201 IWL_ERR(mvm
, "PHY ctxt cmd error. ret=%d\n", ret
);
206 * Send a command to add a PHY context based on the current HW configuration.
208 int iwl_mvm_phy_ctxt_add(struct iwl_mvm
*mvm
, struct iwl_mvm_phy_ctxt
*ctxt
,
209 struct cfg80211_chan_def
*chandef
,
210 u8 chains_static
, u8 chains_dynamic
)
212 WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART
, &mvm
->status
) &&
214 lockdep_assert_held(&mvm
->mutex
);
216 ctxt
->channel
= chandef
->chan
;
218 return iwl_mvm_phy_ctxt_apply(mvm
, ctxt
, chandef
,
219 chains_static
, chains_dynamic
,
224 * Update the number of references to the given PHY context. This is valid only
225 * in case the PHY context was already created, i.e., its reference count > 0.
227 void iwl_mvm_phy_ctxt_ref(struct iwl_mvm
*mvm
, struct iwl_mvm_phy_ctxt
*ctxt
)
229 lockdep_assert_held(&mvm
->mutex
);
234 * Send a command to modify the PHY context based on the current HW
235 * configuration. Note that the function does not check that the configuration
238 int iwl_mvm_phy_ctxt_changed(struct iwl_mvm
*mvm
, struct iwl_mvm_phy_ctxt
*ctxt
,
239 struct cfg80211_chan_def
*chandef
,
240 u8 chains_static
, u8 chains_dynamic
)
242 enum iwl_ctxt_action action
= FW_CTXT_ACTION_MODIFY
;
244 lockdep_assert_held(&mvm
->mutex
);
246 if (fw_has_capa(&mvm
->fw
->ucode_capa
,
247 IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT
) &&
248 ctxt
->channel
->band
!= chandef
->chan
->band
) {
251 /* ... remove it here ...*/
252 ret
= iwl_mvm_phy_ctxt_apply(mvm
, ctxt
, chandef
,
253 chains_static
, chains_dynamic
,
254 FW_CTXT_ACTION_REMOVE
);
258 /* ... and proceed to add it again */
259 action
= FW_CTXT_ACTION_ADD
;
262 ctxt
->channel
= chandef
->chan
;
263 ctxt
->width
= chandef
->width
;
264 return iwl_mvm_phy_ctxt_apply(mvm
, ctxt
, chandef
,
265 chains_static
, chains_dynamic
,
269 void iwl_mvm_phy_ctxt_unref(struct iwl_mvm
*mvm
, struct iwl_mvm_phy_ctxt
*ctxt
)
271 lockdep_assert_held(&mvm
->mutex
);
273 if (WARN_ON_ONCE(!ctxt
))
279 * Move unused phy's to a default channel. When the phy is moved the,
280 * fw will cleanup immediate quiet bit if it was previously set,
281 * otherwise we might not be able to reuse this phy.
283 if (ctxt
->ref
== 0) {
284 struct ieee80211_channel
*chan
;
285 struct cfg80211_chan_def chandef
;
286 struct ieee80211_supported_band
*sband
= NULL
;
287 enum nl80211_band band
= NL80211_BAND_2GHZ
;
289 while (!sband
&& band
< NUM_NL80211_BANDS
)
290 sband
= mvm
->hw
->wiphy
->bands
[band
++];
295 chan
= &sband
->channels
[0];
297 cfg80211_chandef_create(&chandef
, chan
, NL80211_CHAN_NO_HT
);
298 iwl_mvm_phy_ctxt_changed(mvm
, ctxt
, &chandef
, 1, 1);
302 static void iwl_mvm_binding_iterator(void *_data
, u8
*mac
,
303 struct ieee80211_vif
*vif
)
305 unsigned long *data
= _data
;
306 struct iwl_mvm_vif
*mvmvif
= iwl_mvm_vif_from_mac80211(vif
);
308 if (!mvmvif
->phy_ctxt
)
311 if (vif
->type
== NL80211_IFTYPE_STATION
||
312 vif
->type
== NL80211_IFTYPE_AP
)
313 __set_bit(mvmvif
->phy_ctxt
->id
, data
);
316 int iwl_mvm_phy_ctx_count(struct iwl_mvm
*mvm
)
318 unsigned long phy_ctxt_counter
= 0;
320 ieee80211_iterate_active_interfaces_atomic(mvm
->hw
,
321 IEEE80211_IFACE_ITER_NORMAL
,
322 iwl_mvm_binding_iterator
,
325 return hweight8(phy_ctxt_counter
);