linux/drivers/net/wireless/iwlwifi/mvm/power.c
<<
>>
Prefs
   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 - 2013 Intel Corporation. All rights reserved.
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of version 2 of the GNU General Public License as
  12 * published by the Free Software Foundation.
  13 *
  14 * This program is distributed in the hope that it will be useful, but
  15 * WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this program; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
  22 * USA
  23 *
  24 * The full GNU General Public License is included in this distribution
  25 * in the file called COPYING.
  26 *
  27 * Contact Information:
  28 *  Intel Linux Wireless <ilw@linux.intel.com>
  29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  30 *
  31 * BSD LICENSE
  32 *
  33 * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
  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 <linux/kernel.h>
  65#include <linux/module.h>
  66#include <linux/slab.h>
  67#include <linux/init.h>
  68
  69#include <net/mac80211.h>
  70
  71#include "iwl-debug.h"
  72#include "mvm.h"
  73#include "iwl-modparams.h"
  74#include "fw-api-power.h"
  75
  76#define POWER_KEEP_ALIVE_PERIOD_SEC    25
  77
  78static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
  79                                          struct iwl_beacon_filter_cmd *cmd)
  80{
  81        int ret;
  82
  83        ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, CMD_SYNC,
  84                                   sizeof(struct iwl_beacon_filter_cmd), cmd);
  85
  86        if (!ret) {
  87                IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
  88                                cmd->ba_enable_beacon_abort);
  89                IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
  90                                cmd->ba_escape_timer);
  91                IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
  92                                cmd->bf_debug_flag);
  93                IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
  94                                cmd->bf_enable_beacon_filter);
  95                IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
  96                                cmd->bf_energy_delta);
  97                IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
  98                                cmd->bf_escape_timer);
  99                IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
 100                                cmd->bf_roaming_energy_delta);
 101                IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
 102                                cmd->bf_roaming_state);
 103                IWL_DEBUG_POWER(mvm, "bf_temperature_delta is: %d\n",
 104                                cmd->bf_temperature_delta);
 105        }
 106        return ret;
 107}
 108
 109static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
 110                                       struct ieee80211_vif *vif, bool enable)
 111{
 112        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 113        struct iwl_beacon_filter_cmd cmd = {
 114                IWL_BF_CMD_CONFIG_DEFAULTS,
 115                .bf_enable_beacon_filter = 1,
 116                .ba_enable_beacon_abort = enable,
 117        };
 118
 119        if (!mvmvif->bf_enabled)
 120                return 0;
 121
 122        iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
 123        return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
 124}
 125
 126static void iwl_mvm_power_log(struct iwl_mvm *mvm,
 127                              struct iwl_powertable_cmd *cmd)
 128{
 129        IWL_DEBUG_POWER(mvm,
 130                        "Sending power table command for power level %d, flags = 0x%X\n",
 131                        iwlmvm_mod_params.power_scheme,
 132                        le16_to_cpu(cmd->flags));
 133        IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", cmd->keep_alive_seconds);
 134
 135        if (cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
 136                IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
 137                                le32_to_cpu(cmd->rx_data_timeout));
 138                IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
 139                                le32_to_cpu(cmd->tx_data_timeout));
 140                if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))
 141                        IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",
 142                                        le32_to_cpu(cmd->skip_dtim_periods));
 143                if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
 144                        IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
 145                                        le32_to_cpu(cmd->lprx_rssi_threshold));
 146        }
 147}
 148
 149void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 150                             struct iwl_powertable_cmd *cmd)
 151{
 152        struct ieee80211_hw *hw = mvm->hw;
 153        struct ieee80211_chanctx_conf *chanctx_conf;
 154        struct ieee80211_channel *chan;
 155        int dtimper, dtimper_msec;
 156        int keep_alive;
 157        bool radar_detect = false;
 158        struct iwl_mvm_vif *mvmvif __maybe_unused =
 159                iwl_mvm_vif_from_mac80211(vif);
 160
 161        /*
 162         * Regardless of power management state the driver must set
 163         * keep alive period. FW will use it for sending keep alive NDPs
 164         * immediately after association.
 165         */
 166        cmd->keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC;
 167
 168        if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
 169                return;
 170
 171        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
 172        if (!vif->bss_conf.assoc)
 173                cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
 174
 175#ifdef CONFIG_IWLWIFI_DEBUGFS
 176        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
 177            mvmvif->dbgfs_pm.disable_power_off)
 178                cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
 179#endif
 180        if (!vif->bss_conf.ps)
 181                return;
 182
 183        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
 184
 185        if (vif->bss_conf.beacon_rate &&
 186            (vif->bss_conf.beacon_rate->bitrate == 10 ||
 187             vif->bss_conf.beacon_rate->bitrate == 60)) {
 188                cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
 189                cmd->lprx_rssi_threshold =
 190                        cpu_to_le32(POWER_LPRX_RSSI_THRESHOLD);
 191        }
 192
 193        dtimper = hw->conf.ps_dtim_period ?: 1;
 194
 195        /* Check if radar detection is required on current channel */
 196        rcu_read_lock();
 197        chanctx_conf = rcu_dereference(vif->chanctx_conf);
 198        WARN_ON(!chanctx_conf);
 199        if (chanctx_conf) {
 200                chan = chanctx_conf->def.chan;
 201                radar_detect = chan->flags & IEEE80211_CHAN_RADAR;
 202        }
 203        rcu_read_unlock();
 204
 205        /* Check skip over DTIM conditions */
 206        if (!radar_detect && (dtimper <= 10) &&
 207            (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
 208             mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
 209                cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
 210                cmd->skip_dtim_periods = cpu_to_le32(3);
 211        }
 212
 213        /* Check that keep alive period is at least 3 * DTIM */
 214        dtimper_msec = dtimper * vif->bss_conf.beacon_int;
 215        keep_alive = max_t(int, 3 * dtimper_msec,
 216                           MSEC_PER_SEC * cmd->keep_alive_seconds);
 217        keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
 218        cmd->keep_alive_seconds = keep_alive;
 219
 220        if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
 221                cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
 222                cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
 223        } else {
 224                cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
 225                cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
 226        }
 227
 228#ifdef CONFIG_IWLWIFI_DEBUGFS
 229        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
 230                cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds;
 231        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
 232                if (mvmvif->dbgfs_pm.skip_over_dtim)
 233                        cmd->flags |=
 234                                cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
 235                else
 236                        cmd->flags &=
 237                                cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK);
 238        }
 239        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT)
 240                cmd->rx_data_timeout =
 241                        cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout);
 242        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT)
 243                cmd->tx_data_timeout =
 244                        cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
 245        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
 246                cmd->skip_dtim_periods =
 247                        cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods);
 248        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {
 249                if (mvmvif->dbgfs_pm.lprx_ena)
 250                        cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
 251                else
 252                        cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);
 253        }
 254        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)
 255                cmd->lprx_rssi_threshold =
 256                        cpu_to_le32(mvmvif->dbgfs_pm.lprx_rssi_threshold);
 257#endif /* CONFIG_IWLWIFI_DEBUGFS */
 258}
 259
 260int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 261{
 262        int ret;
 263        bool ba_enable;
 264        struct iwl_powertable_cmd cmd = {};
 265
 266        if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
 267                return 0;
 268
 269        /*
 270         * TODO: The following vif_count verification is temporary condition.
 271         * Avoid power mode update if more than one interface is currently
 272         * active. Remove this condition when FW will support power management
 273         * on multiple MACs.
 274         */
 275        IWL_DEBUG_POWER(mvm, "Currently %d interfaces active\n",
 276                        mvm->vif_count);
 277        if (mvm->vif_count > 1)
 278                return 0;
 279
 280        iwl_mvm_power_build_cmd(mvm, vif, &cmd);
 281        iwl_mvm_power_log(mvm, &cmd);
 282
 283        ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
 284                                   sizeof(cmd), &cmd);
 285        if (ret)
 286                return ret;
 287
 288        ba_enable = !!(cmd.flags &
 289                       cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK));
 290
 291        return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
 292}
 293
 294int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 295{
 296        struct iwl_powertable_cmd cmd = {};
 297        struct iwl_mvm_vif *mvmvif __maybe_unused =
 298                iwl_mvm_vif_from_mac80211(vif);
 299
 300        if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
 301                return 0;
 302
 303        if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
 304                cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
 305
 306#ifdef CONFIG_IWLWIFI_DEBUGFS
 307        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
 308            mvmvif->dbgfs_pm.disable_power_off)
 309                cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
 310#endif
 311        iwl_mvm_power_log(mvm, &cmd);
 312
 313        return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC,
 314                                    sizeof(cmd), &cmd);
 315}
 316
 317#ifdef CONFIG_IWLWIFI_DEBUGFS
 318void
 319iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
 320                                         struct iwl_beacon_filter_cmd *cmd)
 321{
 322        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 323        struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
 324
 325        if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA)
 326                cmd->bf_energy_delta = dbgfs_bf->bf_energy_delta;
 327        if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA)
 328                cmd->bf_roaming_energy_delta =
 329                                 dbgfs_bf->bf_roaming_energy_delta;
 330        if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE)
 331                cmd->bf_roaming_state = dbgfs_bf->bf_roaming_state;
 332        if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMPERATURE_DELTA)
 333                cmd->bf_temperature_delta = dbgfs_bf->bf_temperature_delta;
 334        if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG)
 335                cmd->bf_debug_flag = dbgfs_bf->bf_debug_flag;
 336        if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER)
 337                cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer);
 338        if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER)
 339                cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer);
 340        if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT)
 341                cmd->ba_enable_beacon_abort = dbgfs_bf->ba_enable_beacon_abort;
 342}
 343#endif
 344
 345int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
 346                                 struct ieee80211_vif *vif)
 347{
 348        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 349        struct iwl_beacon_filter_cmd cmd = {
 350                IWL_BF_CMD_CONFIG_DEFAULTS,
 351                .bf_enable_beacon_filter = 1,
 352        };
 353        int ret;
 354
 355        if (mvmvif != mvm->bf_allowed_vif ||
 356            vif->type != NL80211_IFTYPE_STATION || vif->p2p)
 357                return 0;
 358
 359        iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
 360        ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
 361
 362        if (!ret)
 363                mvmvif->bf_enabled = true;
 364
 365        return ret;
 366}
 367
 368int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
 369                                  struct ieee80211_vif *vif)
 370{
 371        struct iwl_beacon_filter_cmd cmd = {};
 372        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 373        int ret;
 374
 375        if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
 376                return 0;
 377
 378        ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
 379
 380        if (!ret)
 381                mvmvif->bf_enabled = false;
 382
 383        return ret;
 384}
 385