0001
0002
0003
0004
0005
0006
0007 #include <net/mac80211.h>
0008 #include "fw-api.h"
0009 #include "mvm.h"
0010
0011
0012 u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
0013 {
0014 switch (chandef->width) {
0015 case NL80211_CHAN_WIDTH_20_NOHT:
0016 case NL80211_CHAN_WIDTH_20:
0017 return PHY_VHT_CHANNEL_MODE20;
0018 case NL80211_CHAN_WIDTH_40:
0019 return PHY_VHT_CHANNEL_MODE40;
0020 case NL80211_CHAN_WIDTH_80:
0021 return PHY_VHT_CHANNEL_MODE80;
0022 case NL80211_CHAN_WIDTH_160:
0023 return PHY_VHT_CHANNEL_MODE160;
0024 default:
0025 WARN(1, "Invalid channel width=%u", chandef->width);
0026 return PHY_VHT_CHANNEL_MODE20;
0027 }
0028 }
0029
0030
0031
0032
0033
0034 u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
0035 {
0036 switch (chandef->chan->center_freq - chandef->center_freq1) {
0037 case -70:
0038 return PHY_VHT_CTRL_POS_4_BELOW;
0039 case -50:
0040 return PHY_VHT_CTRL_POS_3_BELOW;
0041 case -30:
0042 return PHY_VHT_CTRL_POS_2_BELOW;
0043 case -10:
0044 return PHY_VHT_CTRL_POS_1_BELOW;
0045 case 10:
0046 return PHY_VHT_CTRL_POS_1_ABOVE;
0047 case 30:
0048 return PHY_VHT_CTRL_POS_2_ABOVE;
0049 case 50:
0050 return PHY_VHT_CTRL_POS_3_ABOVE;
0051 case 70:
0052 return PHY_VHT_CTRL_POS_4_ABOVE;
0053 default:
0054 WARN(1, "Invalid channel definition");
0055 fallthrough;
0056 case 0:
0057
0058
0059
0060
0061
0062 return PHY_VHT_CTRL_POS_1_BELOW;
0063 }
0064 }
0065
0066
0067
0068
0069 static void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt,
0070 struct iwl_phy_context_cmd *cmd,
0071 u32 action)
0072 {
0073 cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id,
0074 ctxt->color));
0075 cmd->action = cpu_to_le32(action);
0076 }
0077
0078 static void iwl_mvm_phy_ctxt_set_rxchain(struct iwl_mvm *mvm,
0079 struct iwl_mvm_phy_ctxt *ctxt,
0080 __le32 *rxchain_info,
0081 u8 chains_static,
0082 u8 chains_dynamic)
0083 {
0084 u8 active_cnt, idle_cnt;
0085
0086
0087 idle_cnt = chains_static;
0088 active_cnt = chains_dynamic;
0089
0090
0091
0092
0093
0094
0095
0096
0097 if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm, ctxt)) {
0098 idle_cnt = 2;
0099 active_cnt = 2;
0100 }
0101
0102
0103
0104
0105
0106
0107
0108 if (mvm->fw_static_smps_request && active_cnt == 1 && idle_cnt == 1) {
0109 idle_cnt = 0;
0110 active_cnt = 0;
0111 }
0112
0113 *rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) <<
0114 PHY_RX_CHAIN_VALID_POS);
0115 *rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
0116 *rxchain_info |= cpu_to_le32(active_cnt <<
0117 PHY_RX_CHAIN_MIMO_CNT_POS);
0118 #ifdef CONFIG_IWLWIFI_DEBUGFS
0119 if (unlikely(mvm->dbgfs_rx_phyinfo))
0120 *rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo);
0121 #endif
0122 }
0123
0124
0125
0126
0127 static void iwl_mvm_phy_ctxt_cmd_data_v1(struct iwl_mvm *mvm,
0128 struct iwl_mvm_phy_ctxt *ctxt,
0129 struct iwl_phy_context_cmd_v1 *cmd,
0130 struct cfg80211_chan_def *chandef,
0131 u8 chains_static, u8 chains_dynamic)
0132 {
0133 struct iwl_phy_context_cmd_tail *tail =
0134 iwl_mvm_chan_info_cmd_tail(mvm, &cmd->ci);
0135
0136
0137 iwl_mvm_set_chan_info_chandef(mvm, &cmd->ci, chandef);
0138
0139 iwl_mvm_phy_ctxt_set_rxchain(mvm, ctxt, &tail->rxchain_info,
0140 chains_static, chains_dynamic);
0141
0142 tail->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
0143 }
0144
0145
0146
0147
0148 static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
0149 struct iwl_mvm_phy_ctxt *ctxt,
0150 struct iwl_phy_context_cmd *cmd,
0151 struct cfg80211_chan_def *chandef,
0152 u8 chains_static, u8 chains_dynamic)
0153 {
0154 cmd->lmac_id = cpu_to_le32(iwl_mvm_get_lmac_id(mvm->fw,
0155 chandef->chan->band));
0156
0157
0158 iwl_mvm_set_chan_info_chandef(mvm, &cmd->ci, chandef);
0159
0160
0161 if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, RLC_CONFIG_CMD), 0) < 2)
0162 iwl_mvm_phy_ctxt_set_rxchain(mvm, ctxt, &cmd->rxchain_info,
0163 chains_static, chains_dynamic);
0164 }
0165
0166 static int iwl_mvm_phy_send_rlc(struct iwl_mvm *mvm,
0167 struct iwl_mvm_phy_ctxt *ctxt,
0168 u8 chains_static, u8 chains_dynamic)
0169 {
0170 struct iwl_rlc_config_cmd cmd = {
0171 .phy_id = cpu_to_le32(ctxt->id),
0172 };
0173
0174 if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, RLC_CONFIG_CMD), 0) < 2)
0175 return 0;
0176
0177 BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_DRIVER_FORCE !=
0178 PHY_RX_CHAIN_DRIVER_FORCE_MSK);
0179 BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_VALID !=
0180 PHY_RX_CHAIN_VALID_MSK);
0181 BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_FORCE !=
0182 PHY_RX_CHAIN_FORCE_SEL_MSK);
0183 BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_FORCE_MIMO !=
0184 PHY_RX_CHAIN_FORCE_MIMO_SEL_MSK);
0185 BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_COUNT != PHY_RX_CHAIN_CNT_MSK);
0186 BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_MIMO_COUNT !=
0187 PHY_RX_CHAIN_MIMO_CNT_MSK);
0188
0189 iwl_mvm_phy_ctxt_set_rxchain(mvm, ctxt, &cmd.rlc.rx_chain_info,
0190 chains_static, chains_dynamic);
0191
0192 return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(RLC_CONFIG_CMD,
0193 DATA_PATH_GROUP, 2),
0194 0, sizeof(cmd), &cmd);
0195 }
0196
0197
0198
0199
0200
0201
0202
0203 static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
0204 struct iwl_mvm_phy_ctxt *ctxt,
0205 struct cfg80211_chan_def *chandef,
0206 u8 chains_static, u8 chains_dynamic,
0207 u32 action)
0208 {
0209 int ret;
0210 int ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1);
0211
0212 if (ver == 3 || ver == 4) {
0213 struct iwl_phy_context_cmd cmd = {};
0214
0215
0216 iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action);
0217
0218
0219 iwl_mvm_phy_ctxt_cmd_data(mvm, ctxt, &cmd, chandef,
0220 chains_static,
0221 chains_dynamic);
0222
0223 ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD,
0224 0, sizeof(cmd), &cmd);
0225 } else if (ver < 3) {
0226 struct iwl_phy_context_cmd_v1 cmd = {};
0227 u16 len = sizeof(cmd) - iwl_mvm_chan_info_padding(mvm);
0228
0229
0230 iwl_mvm_phy_ctxt_cmd_hdr(ctxt,
0231 (struct iwl_phy_context_cmd *)&cmd,
0232 action);
0233
0234
0235 iwl_mvm_phy_ctxt_cmd_data_v1(mvm, ctxt, &cmd, chandef,
0236 chains_static,
0237 chains_dynamic);
0238 ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD,
0239 0, len, &cmd);
0240 } else {
0241 IWL_ERR(mvm, "PHY ctxt cmd error ver %d not supported\n", ver);
0242 return -EOPNOTSUPP;
0243 }
0244
0245
0246 if (ret) {
0247 IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret);
0248 return ret;
0249 }
0250
0251 if (action != FW_CTXT_ACTION_REMOVE)
0252 return iwl_mvm_phy_send_rlc(mvm, ctxt, chains_static,
0253 chains_dynamic);
0254
0255 return 0;
0256 }
0257
0258
0259
0260
0261 int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
0262 struct cfg80211_chan_def *chandef,
0263 u8 chains_static, u8 chains_dynamic)
0264 {
0265 WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
0266 ctxt->ref);
0267 lockdep_assert_held(&mvm->mutex);
0268
0269 ctxt->channel = chandef->chan;
0270 ctxt->width = chandef->width;
0271 ctxt->center_freq1 = chandef->center_freq1;
0272
0273 return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
0274 chains_static, chains_dynamic,
0275 FW_CTXT_ACTION_ADD);
0276 }
0277
0278
0279
0280
0281
0282 void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
0283 {
0284 lockdep_assert_held(&mvm->mutex);
0285 ctxt->ref++;
0286 }
0287
0288
0289
0290
0291
0292
0293 int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
0294 struct cfg80211_chan_def *chandef,
0295 u8 chains_static, u8 chains_dynamic)
0296 {
0297 enum iwl_ctxt_action action = FW_CTXT_ACTION_MODIFY;
0298
0299 lockdep_assert_held(&mvm->mutex);
0300
0301 if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, RLC_CONFIG_CMD), 0) >= 2 &&
0302 ctxt->channel == chandef->chan &&
0303 ctxt->width == chandef->width &&
0304 ctxt->center_freq1 == chandef->center_freq1)
0305 return iwl_mvm_phy_send_rlc(mvm, ctxt, chains_static,
0306 chains_dynamic);
0307
0308 if (fw_has_capa(&mvm->fw->ucode_capa,
0309 IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT) &&
0310 ctxt->channel->band != chandef->chan->band) {
0311 int ret;
0312
0313
0314 ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
0315 chains_static, chains_dynamic,
0316 FW_CTXT_ACTION_REMOVE);
0317 if (ret)
0318 return ret;
0319
0320
0321 action = FW_CTXT_ACTION_ADD;
0322 }
0323
0324 ctxt->channel = chandef->chan;
0325 ctxt->width = chandef->width;
0326 ctxt->center_freq1 = chandef->center_freq1;
0327
0328 return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
0329 chains_static, chains_dynamic,
0330 action);
0331 }
0332
0333 void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
0334 {
0335 lockdep_assert_held(&mvm->mutex);
0336
0337 if (WARN_ON_ONCE(!ctxt))
0338 return;
0339
0340 ctxt->ref--;
0341
0342
0343
0344
0345
0346
0347 if (ctxt->ref == 0) {
0348 struct ieee80211_channel *chan = NULL;
0349 struct cfg80211_chan_def chandef;
0350 struct ieee80211_supported_band *sband;
0351 enum nl80211_band band;
0352 int channel;
0353
0354 for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) {
0355 sband = mvm->hw->wiphy->bands[band];
0356
0357 if (!sband)
0358 continue;
0359
0360 for (channel = 0; channel < sband->n_channels; channel++)
0361 if (!(sband->channels[channel].flags &
0362 IEEE80211_CHAN_DISABLED)) {
0363 chan = &sband->channels[channel];
0364 break;
0365 }
0366
0367 if (chan)
0368 break;
0369 }
0370
0371 if (WARN_ON(!chan))
0372 return;
0373
0374 cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
0375 iwl_mvm_phy_ctxt_changed(mvm, ctxt, &chandef, 1, 1);
0376 }
0377 }
0378
0379 static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
0380 struct ieee80211_vif *vif)
0381 {
0382 unsigned long *data = _data;
0383 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
0384
0385 if (!mvmvif->phy_ctxt)
0386 return;
0387
0388 if (vif->type == NL80211_IFTYPE_STATION ||
0389 vif->type == NL80211_IFTYPE_AP)
0390 __set_bit(mvmvif->phy_ctxt->id, data);
0391 }
0392
0393 int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm)
0394 {
0395 unsigned long phy_ctxt_counter = 0;
0396
0397 ieee80211_iterate_active_interfaces_atomic(mvm->hw,
0398 IEEE80211_IFACE_ITER_NORMAL,
0399 iwl_mvm_binding_iterator,
0400 &phy_ctxt_counter);
0401
0402 return hweight8(phy_ctxt_counter);
0403 }