linux/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/*
   3 * Copyright (C) 2012-2014, 2018-2021 Intel Corporation
   4 * Copyright (C) 2013-2014 Intel Mobile Communications GmbH
   5 * Copyright (C) 2017 Intel Deutschland GmbH
   6 */
   7#include <net/mac80211.h>
   8#include "fw-api.h"
   9#include "mvm.h"
  10
  11/* Maps the driver specific channel width definition to the fw values */
  12u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
  13{
  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;
  24        default:
  25                WARN(1, "Invalid channel width=%u", chandef->width);
  26                return PHY_VHT_CHANNEL_MODE20;
  27        }
  28}
  29
  30/*
  31 * Maps the driver specific control channel position (relative to the center
  32 * freq) definitions to the the fw values
  33 */
  34u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
  35{
  36        switch (chandef->chan->center_freq - chandef->center_freq1) {
  37        case -70:
  38                return PHY_VHT_CTRL_POS_4_BELOW;
  39        case -50:
  40                return PHY_VHT_CTRL_POS_3_BELOW;
  41        case -30:
  42                return PHY_VHT_CTRL_POS_2_BELOW;
  43        case -10:
  44                return PHY_VHT_CTRL_POS_1_BELOW;
  45        case  10:
  46                return PHY_VHT_CTRL_POS_1_ABOVE;
  47        case  30:
  48                return PHY_VHT_CTRL_POS_2_ABOVE;
  49        case  50:
  50                return PHY_VHT_CTRL_POS_3_ABOVE;
  51        case  70:
  52                return PHY_VHT_CTRL_POS_4_ABOVE;
  53        default:
  54                WARN(1, "Invalid channel definition");
  55                fallthrough;
  56        case 0:
  57                /*
  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.
  61                 */
  62                return PHY_VHT_CTRL_POS_1_BELOW;
  63        }
  64}
  65
  66/*
  67 * Construct the generic fields of the PHY context command
  68 */
  69static void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt,
  70                                     struct iwl_phy_context_cmd *cmd,
  71                                     u32 action)
  72{
  73        cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id,
  74                                                            ctxt->color));
  75        cmd->action = cpu_to_le32(action);
  76}
  77
  78static void iwl_mvm_phy_ctxt_set_rxchain(struct iwl_mvm *mvm,
  79                                         struct iwl_mvm_phy_ctxt *ctxt,
  80                                         __le32 *rxchain_info,
  81                                         u8 chains_static,
  82                                         u8 chains_dynamic)
  83{
  84        u8 active_cnt, idle_cnt;
  85
  86        /* Set rx the chains */
  87        idle_cnt = chains_static;
  88        active_cnt = chains_dynamic;
  89
  90        /* In scenarios where we only ever use a single-stream rates,
  91         * i.e. legacy 11b/g/a associations, single-stream APs or even
  92         * static SMPS, enable both chains to get diversity, improving
  93         * the case where we're far enough from the AP that attenuation
  94         * between the two antennas is sufficiently different to impact
  95         * performance.
  96         */
  97        if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm, ctxt)) {
  98                idle_cnt = 2;
  99                active_cnt = 2;
 100        }
 101
 102        /*
 103         * If the firmware requested it, then we know that it supports
 104         * getting zero for the values to indicate "use one, but pick
 105         * which one yourself", which means it can dynamically pick one
 106         * that e.g. has better RSSI.
 107         */
 108        if (mvm->fw_static_smps_request && active_cnt == 1 && idle_cnt == 1) {
 109                idle_cnt = 0;
 110                active_cnt = 0;
 111        }
 112
 113        *rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) <<
 114                                        PHY_RX_CHAIN_VALID_POS);
 115        *rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
 116        *rxchain_info |= cpu_to_le32(active_cnt <<
 117                                         PHY_RX_CHAIN_MIMO_CNT_POS);
 118#ifdef CONFIG_IWLWIFI_DEBUGFS
 119        if (unlikely(mvm->dbgfs_rx_phyinfo))
 120                *rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo);
 121#endif
 122}
 123
 124/*
 125 * Add the phy configuration to the PHY context command
 126 */
 127static void iwl_mvm_phy_ctxt_cmd_data_v1(struct iwl_mvm *mvm,
 128                                         struct iwl_mvm_phy_ctxt *ctxt,
 129                                         struct iwl_phy_context_cmd_v1 *cmd,
 130                                         struct cfg80211_chan_def *chandef,
 131                                         u8 chains_static, u8 chains_dynamic)
 132{
 133        struct iwl_phy_context_cmd_tail *tail =
 134                iwl_mvm_chan_info_cmd_tail(mvm, &cmd->ci);
 135
 136        /* Set the channel info data */
 137        iwl_mvm_set_chan_info_chandef(mvm, &cmd->ci, chandef);
 138
 139        iwl_mvm_phy_ctxt_set_rxchain(mvm, ctxt, &tail->rxchain_info,
 140                                     chains_static, chains_dynamic);
 141
 142        tail->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
 143}
 144
 145/*
 146 * Add the phy configuration to the PHY context command
 147 */
 148static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
 149                                      struct iwl_mvm_phy_ctxt *ctxt,
 150                                      struct iwl_phy_context_cmd *cmd,
 151                                      struct cfg80211_chan_def *chandef,
 152                                      u8 chains_static, u8 chains_dynamic)
 153{
 154        cmd->lmac_id = cpu_to_le32(iwl_mvm_get_lmac_id(mvm->fw,
 155                                                       chandef->chan->band));
 156
 157        /* Set the channel info data */
 158        iwl_mvm_set_chan_info_chandef(mvm, &cmd->ci, chandef);
 159
 160        iwl_mvm_phy_ctxt_set_rxchain(mvm, ctxt, &cmd->rxchain_info,
 161                                     chains_static, chains_dynamic);
 162}
 163
 164/*
 165 * Send a command to apply the current phy configuration. The command is send
 166 * only if something in the configuration changed: in case that this is the
 167 * first time that the phy configuration is applied or in case that the phy
 168 * configuration changed from the previous apply.
 169 */
 170static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
 171                                  struct iwl_mvm_phy_ctxt *ctxt,
 172                                  struct cfg80211_chan_def *chandef,
 173                                  u8 chains_static, u8 chains_dynamic,
 174                                  u32 action)
 175{
 176        int ret;
 177        int ver = iwl_fw_lookup_cmd_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP,
 178                                        PHY_CONTEXT_CMD, 1);
 179
 180        if (ver == 3) {
 181                struct iwl_phy_context_cmd cmd = {};
 182
 183                /* Set the command header fields */
 184                iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action);
 185
 186                /* Set the command data */
 187                iwl_mvm_phy_ctxt_cmd_data(mvm, ctxt, &cmd, chandef,
 188                                          chains_static,
 189                                          chains_dynamic);
 190
 191                ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD,
 192                                           0, sizeof(cmd), &cmd);
 193        } else if (ver < 3) {
 194                struct iwl_phy_context_cmd_v1 cmd = {};
 195                u16 len = sizeof(cmd) - iwl_mvm_chan_info_padding(mvm);
 196
 197                /* Set the command header fields */
 198                iwl_mvm_phy_ctxt_cmd_hdr(ctxt,
 199                                         (struct iwl_phy_context_cmd *)&cmd,
 200                                         action);
 201
 202                /* Set the command data */
 203                iwl_mvm_phy_ctxt_cmd_data_v1(mvm, ctxt, &cmd, chandef,
 204                                             chains_static,
 205                                             chains_dynamic);
 206                ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD,
 207                                           0, len, &cmd);
 208        } else {
 209                IWL_ERR(mvm, "PHY ctxt cmd error ver %d not supported\n", ver);
 210                return -EOPNOTSUPP;
 211        }
 212
 213
 214        if (ret)
 215                IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret);
 216        return ret;
 217}
 218
 219/*
 220 * Send a command to add a PHY context based on the current HW configuration.
 221 */
 222int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
 223                         struct cfg80211_chan_def *chandef,
 224                         u8 chains_static, u8 chains_dynamic)
 225{
 226        WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
 227                ctxt->ref);
 228        lockdep_assert_held(&mvm->mutex);
 229
 230        ctxt->channel = chandef->chan;
 231
 232        return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
 233                                      chains_static, chains_dynamic,
 234                                      FW_CTXT_ACTION_ADD);
 235}
 236
 237/*
 238 * Update the number of references to the given PHY context. This is valid only
 239 * in case the PHY context was already created, i.e., its reference count > 0.
 240 */
 241void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
 242{
 243        lockdep_assert_held(&mvm->mutex);
 244        ctxt->ref++;
 245}
 246
 247/*
 248 * Send a command to modify the PHY context based on the current HW
 249 * configuration. Note that the function does not check that the configuration
 250 * changed.
 251 */
 252int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
 253                             struct cfg80211_chan_def *chandef,
 254                             u8 chains_static, u8 chains_dynamic)
 255{
 256        enum iwl_ctxt_action action = FW_CTXT_ACTION_MODIFY;
 257
 258        lockdep_assert_held(&mvm->mutex);
 259
 260        if (fw_has_capa(&mvm->fw->ucode_capa,
 261                        IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT) &&
 262            ctxt->channel->band != chandef->chan->band) {
 263                int ret;
 264
 265                /* ... remove it here ...*/
 266                ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
 267                                             chains_static, chains_dynamic,
 268                                             FW_CTXT_ACTION_REMOVE);
 269                if (ret)
 270                        return ret;
 271
 272                /* ... and proceed to add it again */
 273                action = FW_CTXT_ACTION_ADD;
 274        }
 275
 276        ctxt->channel = chandef->chan;
 277        ctxt->width = chandef->width;
 278        return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
 279                                      chains_static, chains_dynamic,
 280                                      action);
 281}
 282
 283void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
 284{
 285        lockdep_assert_held(&mvm->mutex);
 286
 287        if (WARN_ON_ONCE(!ctxt))
 288                return;
 289
 290        ctxt->ref--;
 291
 292        /*
 293         * Move unused phy's to a default channel. When the phy is moved the,
 294         * fw will cleanup immediate quiet bit if it was previously set,
 295         * otherwise we might not be able to reuse this phy.
 296         */
 297        if (ctxt->ref == 0) {
 298                struct ieee80211_channel *chan;
 299                struct cfg80211_chan_def chandef;
 300                struct ieee80211_supported_band *sband = NULL;
 301                enum nl80211_band band = NL80211_BAND_2GHZ;
 302
 303                while (!sband && band < NUM_NL80211_BANDS)
 304                        sband = mvm->hw->wiphy->bands[band++];
 305
 306                if (WARN_ON(!sband))
 307                        return;
 308
 309                chan = &sband->channels[0];
 310
 311                cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
 312                iwl_mvm_phy_ctxt_changed(mvm, ctxt, &chandef, 1, 1);
 313        }
 314}
 315
 316static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
 317                                     struct ieee80211_vif *vif)
 318{
 319        unsigned long *data = _data;
 320        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 321
 322        if (!mvmvif->phy_ctxt)
 323                return;
 324
 325        if (vif->type == NL80211_IFTYPE_STATION ||
 326            vif->type == NL80211_IFTYPE_AP)
 327                __set_bit(mvmvif->phy_ctxt->id, data);
 328}
 329
 330int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm)
 331{
 332        unsigned long phy_ctxt_counter = 0;
 333
 334        ieee80211_iterate_active_interfaces_atomic(mvm->hw,
 335                                                   IEEE80211_IFACE_ITER_NORMAL,
 336                                                   iwl_mvm_binding_iterator,
 337                                                   &phy_ctxt_counter);
 338
 339        return hweight8(phy_ctxt_counter);
 340}
 341