root/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. iwl_mvm_get_channel_width
  2. iwl_mvm_get_ctrl_pos
  3. iwl_mvm_phy_ctxt_cmd_hdr
  4. iwl_mvm_phy_ctxt_cmd_data
  5. iwl_mvm_phy_ctxt_apply
  6. iwl_mvm_phy_ctxt_add
  7. iwl_mvm_phy_ctxt_ref
  8. iwl_mvm_phy_ctxt_changed
  9. iwl_mvm_phy_ctxt_unref
  10. iwl_mvm_binding_iterator
  11. iwl_mvm_phy_ctx_count

   1 /******************************************************************************
   2  *
   3  * This file is provided under a dual BSD/GPLv2 license.  When using or
   4  * redistributing this file, you may do so under either license.
   5  *
   6  * GPL LICENSE SUMMARY
   7  *
   8  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
   9  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
  10  * Copyright(c) 2017           Intel Deutschland GmbH
  11  * Copyright(c) 2018           Intel Corporation
  12  *
  13  * This program is free software; you can redistribute it and/or modify
  14  * it under the terms of version 2 of the GNU General Public License as
  15  * published by the Free Software Foundation.
  16  *
  17  * This program is distributed in the hope that it will be useful, but
  18  * WITHOUT ANY WARRANTY; without even the implied warranty of
  19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  20  * General Public License for more details.
  21  *
  22  * The full GNU General Public License is included in this distribution
  23  * in the file called COPYING.
  24  *
  25  * Contact Information:
  26  *  Intel Linux Wireless <linuxwifi@intel.com>
  27  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  28  *
  29  * BSD LICENSE
  30  *
  31  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  32  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
  33  * Copyright(c) 2018           Intel Corporation
  34  * All rights reserved.
  35  *
  36  * Redistribution and use in source and binary forms, with or without
  37  * modification, are permitted provided that the following conditions
  38  * are met:
  39  *
  40  *  * Redistributions of source code must retain the above copyright
  41  *    notice, this list of conditions and the following disclaimer.
  42  *  * Redistributions in binary form must reproduce the above copyright
  43  *    notice, this list of conditions and the following disclaimer in
  44  *    the documentation and/or other materials provided with the
  45  *    distribution.
  46  *  * Neither the name Intel Corporation nor the names of its
  47  *    contributors may be used to endorse or promote products derived
  48  *    from this software without specific prior written permission.
  49  *
  50  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  51  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  52  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  53  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  54  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  55  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  56  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  57  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  58  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  59  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  60  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  61  *
  62  *****************************************************************************/
  63 
  64 #include <net/mac80211.h>
  65 #include "fw-api.h"
  66 #include "mvm.h"
  67 
  68 /* Maps the driver specific channel width definition to the fw values */
  69 u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
  70 {
  71         switch (chandef->width) {
  72         case NL80211_CHAN_WIDTH_20_NOHT:
  73         case NL80211_CHAN_WIDTH_20:
  74                 return PHY_VHT_CHANNEL_MODE20;
  75         case NL80211_CHAN_WIDTH_40:
  76                 return PHY_VHT_CHANNEL_MODE40;
  77         case NL80211_CHAN_WIDTH_80:
  78                 return PHY_VHT_CHANNEL_MODE80;
  79         case NL80211_CHAN_WIDTH_160:
  80                 return PHY_VHT_CHANNEL_MODE160;
  81         default:
  82                 WARN(1, "Invalid channel width=%u", chandef->width);
  83                 return PHY_VHT_CHANNEL_MODE20;
  84         }
  85 }
  86 
  87 /*
  88  * Maps the driver specific control channel position (relative to the center
  89  * freq) definitions to the the fw values
  90  */
  91 u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
  92 {
  93         switch (chandef->chan->center_freq - chandef->center_freq1) {
  94         case -70:
  95                 return PHY_VHT_CTRL_POS_4_BELOW;
  96         case -50:
  97                 return PHY_VHT_CTRL_POS_3_BELOW;
  98         case -30:
  99                 return PHY_VHT_CTRL_POS_2_BELOW;
 100         case -10:
 101                 return PHY_VHT_CTRL_POS_1_BELOW;
 102         case  10:
 103                 return PHY_VHT_CTRL_POS_1_ABOVE;
 104         case  30:
 105                 return PHY_VHT_CTRL_POS_2_ABOVE;
 106         case  50:
 107                 return PHY_VHT_CTRL_POS_3_ABOVE;
 108         case  70:
 109                 return PHY_VHT_CTRL_POS_4_ABOVE;
 110         default:
 111                 WARN(1, "Invalid channel definition");
 112                 /* fall through */
 113         case 0:
 114                 /*
 115                  * The FW is expected to check the control channel position only
 116                  * when in HT/VHT and the channel width is not 20MHz. Return
 117                  * this value as the default one.
 118                  */
 119                 return PHY_VHT_CTRL_POS_1_BELOW;
 120         }
 121 }
 122 
 123 /*
 124  * Construct the generic fields of the PHY context command
 125  */
 126 static void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt,
 127                                      struct iwl_phy_context_cmd *cmd,
 128                                      u32 action, u32 apply_time)
 129 {
 130         memset(cmd, 0, sizeof(struct iwl_phy_context_cmd));
 131 
 132         cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id,
 133                                                             ctxt->color));
 134         cmd->action = cpu_to_le32(action);
 135         cmd->apply_time = cpu_to_le32(apply_time);
 136 }
 137 
 138 /*
 139  * Add the phy configuration to the PHY context command
 140  */
 141 static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
 142                                       struct iwl_phy_context_cmd *cmd,
 143                                       struct cfg80211_chan_def *chandef,
 144                                       u8 chains_static, u8 chains_dynamic)
 145 {
 146         u8 active_cnt, idle_cnt;
 147         struct iwl_phy_context_cmd_tail *tail =
 148                 iwl_mvm_chan_info_cmd_tail(mvm, &cmd->ci);
 149 
 150         /* Set the channel info data */
 151         iwl_mvm_set_chan_info_chandef(mvm, &cmd->ci, chandef);
 152 
 153         /* Set rx the chains */
 154         idle_cnt = chains_static;
 155         active_cnt = chains_dynamic;
 156 
 157         /* In scenarios where we only ever use a single-stream rates,
 158          * i.e. legacy 11b/g/a associations, single-stream APs or even
 159          * static SMPS, enable both chains to get diversity, improving
 160          * the case where we're far enough from the AP that attenuation
 161          * between the two antennas is sufficiently different to impact
 162          * performance.
 163          */
 164         if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm)) {
 165                 idle_cnt = 2;
 166                 active_cnt = 2;
 167         }
 168 
 169         tail->rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) <<
 170                                         PHY_RX_CHAIN_VALID_POS);
 171         tail->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
 172         tail->rxchain_info |= cpu_to_le32(active_cnt <<
 173                                          PHY_RX_CHAIN_MIMO_CNT_POS);
 174 #ifdef CONFIG_IWLWIFI_DEBUGFS
 175         if (unlikely(mvm->dbgfs_rx_phyinfo))
 176                 tail->rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo);
 177 #endif
 178 
 179         tail->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
 180 }
 181 
 182 /*
 183  * Send a command to apply the current phy configuration. The command is send
 184  * only if something in the configuration changed: in case that this is the
 185  * first time that the phy configuration is applied or in case that the phy
 186  * configuration changed from the previous apply.
 187  */
 188 static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
 189                                   struct iwl_mvm_phy_ctxt *ctxt,
 190                                   struct cfg80211_chan_def *chandef,
 191                                   u8 chains_static, u8 chains_dynamic,
 192                                   u32 action, u32 apply_time)
 193 {
 194         struct iwl_phy_context_cmd cmd;
 195         int ret;
 196         u16 len = sizeof(cmd) - iwl_mvm_chan_info_padding(mvm);
 197 
 198         /* Set the command header fields */
 199         iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action, apply_time);
 200 
 201         /* Set the command data */
 202         iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef,
 203                                   chains_static, chains_dynamic);
 204 
 205         ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, 0, len, &cmd);
 206         if (ret)
 207                 IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret);
 208         return ret;
 209 }
 210 
 211 /*
 212  * Send a command to add a PHY context based on the current HW configuration.
 213  */
 214 int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
 215                          struct cfg80211_chan_def *chandef,
 216                          u8 chains_static, u8 chains_dynamic)
 217 {
 218         WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
 219                 ctxt->ref);
 220         lockdep_assert_held(&mvm->mutex);
 221 
 222         ctxt->channel = chandef->chan;
 223 
 224         return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
 225                                       chains_static, chains_dynamic,
 226                                       FW_CTXT_ACTION_ADD, 0);
 227 }
 228 
 229 /*
 230  * Update the number of references to the given PHY context. This is valid only
 231  * in case the PHY context was already created, i.e., its reference count > 0.
 232  */
 233 void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
 234 {
 235         lockdep_assert_held(&mvm->mutex);
 236         ctxt->ref++;
 237 }
 238 
 239 /*
 240  * Send a command to modify the PHY context based on the current HW
 241  * configuration. Note that the function does not check that the configuration
 242  * changed.
 243  */
 244 int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
 245                              struct cfg80211_chan_def *chandef,
 246                              u8 chains_static, u8 chains_dynamic)
 247 {
 248         enum iwl_ctxt_action action = FW_CTXT_ACTION_MODIFY;
 249 
 250         lockdep_assert_held(&mvm->mutex);
 251 
 252         if (fw_has_capa(&mvm->fw->ucode_capa,
 253                         IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT) &&
 254             ctxt->channel->band != chandef->chan->band) {
 255                 int ret;
 256 
 257                 /* ... remove it here ...*/
 258                 ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
 259                                              chains_static, chains_dynamic,
 260                                              FW_CTXT_ACTION_REMOVE, 0);
 261                 if (ret)
 262                         return ret;
 263 
 264                 /* ... and proceed to add it again */
 265                 action = FW_CTXT_ACTION_ADD;
 266         }
 267 
 268         ctxt->channel = chandef->chan;
 269         ctxt->width = chandef->width;
 270         return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
 271                                       chains_static, chains_dynamic,
 272                                       action, 0);
 273 }
 274 
 275 void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
 276 {
 277         lockdep_assert_held(&mvm->mutex);
 278 
 279         if (WARN_ON_ONCE(!ctxt))
 280                 return;
 281 
 282         ctxt->ref--;
 283 
 284         /*
 285          * Move unused phy's to a default channel. When the phy is moved the,
 286          * fw will cleanup immediate quiet bit if it was previously set,
 287          * otherwise we might not be able to reuse this phy.
 288          */
 289         if (ctxt->ref == 0) {
 290                 struct ieee80211_channel *chan;
 291                 struct cfg80211_chan_def chandef;
 292                 struct ieee80211_supported_band *sband = NULL;
 293                 enum nl80211_band band = NL80211_BAND_2GHZ;
 294 
 295                 while (!sband && band < NUM_NL80211_BANDS)
 296                         sband = mvm->hw->wiphy->bands[band++];
 297 
 298                 if (WARN_ON(!sband))
 299                         return;
 300 
 301                 chan = &sband->channels[0];
 302 
 303                 cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
 304                 iwl_mvm_phy_ctxt_changed(mvm, ctxt, &chandef, 1, 1);
 305         }
 306 }
 307 
 308 static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
 309                                      struct ieee80211_vif *vif)
 310 {
 311         unsigned long *data = _data;
 312         struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 313 
 314         if (!mvmvif->phy_ctxt)
 315                 return;
 316 
 317         if (vif->type == NL80211_IFTYPE_STATION ||
 318             vif->type == NL80211_IFTYPE_AP)
 319                 __set_bit(mvmvif->phy_ctxt->id, data);
 320 }
 321 
 322 int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm)
 323 {
 324         unsigned long phy_ctxt_counter = 0;
 325 
 326         ieee80211_iterate_active_interfaces_atomic(mvm->hw,
 327                                                    IEEE80211_IFACE_ITER_NORMAL,
 328                                                    iwl_mvm_binding_iterator,
 329                                                    &phy_ctxt_counter);
 330 
 331         return hweight8(phy_ctxt_counter);
 332 }

/* [<][>][^][v][top][bottom][index][help] */