// SPDX-License-Identifier: GPL-2.0-only /****************************************************************************** * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. * Copyright (C) 2019, 2025 Intel Corporation * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files.
*****************************************************************************/
staticbool force_cam = true;
module_param(force_cam, bool, 0644);
MODULE_PARM_DESC(force_cam, "force continuously aware mode (no power saving at all)");
/* * Setting power level allows the card to go to sleep when not busy. * * We calculate a sleep command based on the required latency, which * we get from mac80211. In order to handle thermal throttling, we can * also use pre-defined power levels.
*/
/* * This defines the old power levels. They are still used by default * (level 1) and for thermal throttle (levels 3 through 5)
*/
struct iwl_power_vec_entry { struct iwl_powertable_cmd cmd;
u8 no_dtim; /* number of skip dtim */
};
if (period == 0) {
skip = 0;
period = 1; for (i = 0; i < IWL_POWER_VEC_SIZE; i++)
max_sleep[i] = 1;
} else {
skip = table[lvl].no_dtim; for (i = 0; i < IWL_POWER_VEC_SIZE; i++)
max_sleep[i] = le32_to_cpu(cmd->sleep_interval[i]);
max_sleep[IWL_POWER_VEC_SIZE - 1] = skip + 1;
}
slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); /* figure out the listen interval based on dtim period and skip */ if (slp_itrvl == 0xFF)
cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =
cpu_to_le32(period * (skip + 1));
/* enforce max sleep interval */ for (i = IWL_POWER_VEC_SIZE - 1; i >= 0 ; i--) { if (le32_to_cpu(cmd->sleep_interval[i]) >
(max_sleep[i] * period))
cmd->sleep_interval[i] =
cpu_to_le32(max_sleep[i] * period); if (i != (IWL_POWER_VEC_SIZE - 1)) { if (le32_to_cpu(cmd->sleep_interval[i]) >
le32_to_cpu(cmd->sleep_interval[i+1]))
cmd->sleep_interval[i] =
cmd->sleep_interval[i+1];
}
}
if (priv->power_data.bus_pm)
cmd->flags |= IWL_POWER_PCI_PM_MSK; else
cmd->flags &= ~IWL_POWER_PCI_PM_MSK;
IWL_DEBUG_POWER(priv, "numSkipDtim = %u, dtimPeriod = %d\n",
skip, period); /* The power level here is 0-4 (used as array index), but user expects
to see 1-5 (according to spec). */
IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1);
}
if (force_cam) {
iwl_power_sleep_cam_cmd(priv, cmd); return;
}
dtimper = priv->hw->conf.ps_dtim_period ?: 1;
if (priv->wowlan)
iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper); elseif (!priv->lib->no_idle_support &&
priv->hw->conf.flags & IEEE80211_CONF_IDLE)
iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20); elseif (iwl_tt_is_low_power_state(priv)) { /* in thermal throttling low power state */
iwl_static_sleep_cmd(priv, cmd,
iwl_tt_current_power_mode(priv), dtimper);
} elseif (!enabled)
iwl_power_sleep_cam_cmd(priv, cmd); elseif (priv->power_data.debug_sleep_level_override >= 0)
iwl_static_sleep_cmd(priv, cmd,
priv->power_data.debug_sleep_level_override,
dtimper); else { /* Note that the user parameter is 1-5 (according to spec),
but we pass 0-4 because it acts as an array index. */ if (iwlwifi_mod_params.power_level > IWL_POWER_INDEX_1 &&
iwlwifi_mod_params.power_level <= IWL_POWER_NUM)
iwl_static_sleep_cmd(priv, cmd,
iwlwifi_mod_params.power_level - 1, dtimper); else
iwl_static_sleep_cmd(priv, cmd,
IWL_POWER_INDEX_1, dtimper);
}
}
int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, bool force)
{ int ret; bool update_chains;
lockdep_assert_held(&priv->mutex);
/* Don't update the RX chain when chain noise calibration is running */
update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE ||
priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE;
if (!memcmp(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force) return 0;
if (!iwl_is_ready_rf(priv)) return -EIO;
/* scan complete use sleep_power_next, need to be updated */
memcpy(&priv->power_data.sleep_cmd_next, cmd, sizeof(*cmd)); if (test_bit(STATUS_SCANNING, &priv->status) && !force) {
IWL_DEBUG_INFO(priv, "Defer power set mode while scanning\n"); return 0;
}
if (cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)
iwl_dvm_set_pmi(priv, true);
ret = iwl_set_power(priv, cmd); if (!ret) { if (!(cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK))
iwl_dvm_set_pmi(priv, false);
if (update_chains)
iwl_update_chain_flags(priv); else
IWL_DEBUG_POWER(priv, "Cannot update the power, chain noise " "calibration running: %d\n",
priv->chain_noise_data.state);
memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd));
} else
IWL_ERR(priv, "set power fail, ret = %d\n", ret);
return ret;
}
int iwl_power_update_mode(struct iwl_priv *priv, bool force)
{ struct iwl_powertable_cmd cmd;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.