1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67#include <net/mac80211.h>
68#include "fw-api.h"
69#include "mvm.h"
70
71
72u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
73{
74 switch (chandef->width) {
75 case NL80211_CHAN_WIDTH_20_NOHT:
76 case NL80211_CHAN_WIDTH_20:
77 return PHY_VHT_CHANNEL_MODE20;
78 case NL80211_CHAN_WIDTH_40:
79 return PHY_VHT_CHANNEL_MODE40;
80 case NL80211_CHAN_WIDTH_80:
81 return PHY_VHT_CHANNEL_MODE80;
82 case NL80211_CHAN_WIDTH_160:
83 return PHY_VHT_CHANNEL_MODE160;
84 default:
85 WARN(1, "Invalid channel width=%u", chandef->width);
86 return PHY_VHT_CHANNEL_MODE20;
87 }
88}
89
90
91
92
93
94u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
95{
96 switch (chandef->chan->center_freq - chandef->center_freq1) {
97 case -70:
98 return PHY_VHT_CTRL_POS_4_BELOW;
99 case -50:
100 return PHY_VHT_CTRL_POS_3_BELOW;
101 case -30:
102 return PHY_VHT_CTRL_POS_2_BELOW;
103 case -10:
104 return PHY_VHT_CTRL_POS_1_BELOW;
105 case 10:
106 return PHY_VHT_CTRL_POS_1_ABOVE;
107 case 30:
108 return PHY_VHT_CTRL_POS_2_ABOVE;
109 case 50:
110 return PHY_VHT_CTRL_POS_3_ABOVE;
111 case 70:
112 return PHY_VHT_CTRL_POS_4_ABOVE;
113 default:
114 WARN(1, "Invalid channel definition");
115 case 0:
116
117
118
119
120
121 return PHY_VHT_CTRL_POS_1_BELOW;
122 }
123}
124
125
126
127
128static void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt,
129 struct iwl_phy_context_cmd *cmd,
130 u32 action, u32 apply_time)
131{
132 memset(cmd, 0, sizeof(struct iwl_phy_context_cmd));
133
134 cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id,
135 ctxt->color));
136 cmd->action = cpu_to_le32(action);
137 cmd->apply_time = cpu_to_le32(apply_time);
138}
139
140
141
142
143static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
144 struct iwl_phy_context_cmd *cmd,
145 struct cfg80211_chan_def *chandef,
146 u8 chains_static, u8 chains_dynamic)
147{
148 u8 active_cnt, idle_cnt;
149
150
151 cmd->ci.band = (chandef->chan->band == NL80211_BAND_2GHZ ?
152 PHY_BAND_24 : PHY_BAND_5);
153
154 cmd->ci.channel = chandef->chan->hw_value;
155 cmd->ci.width = iwl_mvm_get_channel_width(chandef);
156 cmd->ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef);
157
158
159 idle_cnt = chains_static;
160 active_cnt = chains_dynamic;
161
162
163
164
165
166
167
168
169 if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm)) {
170 idle_cnt = 2;
171 active_cnt = 2;
172 }
173
174 cmd->rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) <<
175 PHY_RX_CHAIN_VALID_POS);
176 cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
177 cmd->rxchain_info |= cpu_to_le32(active_cnt <<
178 PHY_RX_CHAIN_MIMO_CNT_POS);
179#ifdef CONFIG_IWLWIFI_DEBUGFS
180 if (unlikely(mvm->dbgfs_rx_phyinfo))
181 cmd->rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo);
182#endif
183
184 cmd->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
185}
186
187
188
189
190
191
192
193static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
194 struct iwl_mvm_phy_ctxt *ctxt,
195 struct cfg80211_chan_def *chandef,
196 u8 chains_static, u8 chains_dynamic,
197 u32 action, u32 apply_time)
198{
199 struct iwl_phy_context_cmd cmd;
200 int ret;
201
202
203 iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action, apply_time);
204
205
206 iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef,
207 chains_static, chains_dynamic);
208
209 ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, 0,
210 sizeof(struct iwl_phy_context_cmd),
211 &cmd);
212 if (ret)
213 IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret);
214 return ret;
215}
216
217
218
219
220int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
221 struct cfg80211_chan_def *chandef,
222 u8 chains_static, u8 chains_dynamic)
223{
224 WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
225 ctxt->ref);
226 lockdep_assert_held(&mvm->mutex);
227
228 ctxt->channel = chandef->chan;
229
230 return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
231 chains_static, chains_dynamic,
232 FW_CTXT_ACTION_ADD, 0);
233}
234
235
236
237
238
239void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
240{
241 lockdep_assert_held(&mvm->mutex);
242 ctxt->ref++;
243}
244
245
246
247
248
249
250int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
251 struct cfg80211_chan_def *chandef,
252 u8 chains_static, u8 chains_dynamic)
253{
254 enum iwl_ctxt_action action = FW_CTXT_ACTION_MODIFY;
255
256 lockdep_assert_held(&mvm->mutex);
257
258 if (fw_has_capa(&mvm->fw->ucode_capa,
259 IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT) &&
260 ctxt->channel->band != chandef->chan->band) {
261 int ret;
262
263
264 ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
265 chains_static, chains_dynamic,
266 FW_CTXT_ACTION_REMOVE, 0);
267 if (ret)
268 return ret;
269
270
271 action = FW_CTXT_ACTION_ADD;
272 }
273
274 ctxt->channel = chandef->chan;
275 ctxt->width = chandef->width;
276 return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
277 chains_static, chains_dynamic,
278 action, 0);
279}
280
281void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
282{
283 lockdep_assert_held(&mvm->mutex);
284
285 if (WARN_ON_ONCE(!ctxt))
286 return;
287
288 ctxt->ref--;
289}
290
291static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
292 struct ieee80211_vif *vif)
293{
294 unsigned long *data = _data;
295 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
296
297 if (!mvmvif->phy_ctxt)
298 return;
299
300 if (vif->type == NL80211_IFTYPE_STATION ||
301 vif->type == NL80211_IFTYPE_AP)
302 __set_bit(mvmvif->phy_ctxt->id, data);
303}
304
305int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm)
306{
307 unsigned long phy_ctxt_counter = 0;
308
309 ieee80211_iterate_active_interfaces_atomic(mvm->hw,
310 IEEE80211_IFACE_ITER_NORMAL,
311 iwl_mvm_binding_iterator,
312 &phy_ctxt_counter);
313
314 return hweight8(phy_ctxt_counter);
315}
316