Path: blob/main/sys/contrib/dev/iwlwifi/mld/power.c
48285 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2024-2025 Intel Corporation3*/4#include <net/mac80211.h>56#include "mld.h"7#include "hcmd.h"8#include "power.h"9#include "iface.h"10#include "link.h"11#include "constants.h"1213static void iwl_mld_vif_ps_iterator(void *data, u8 *mac,14struct ieee80211_vif *vif)15{16bool *ps_enable = (bool *)data;17struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);1819if (vif->type != NL80211_IFTYPE_STATION)20return;2122*ps_enable &= !mld_vif->ps_disabled;23}2425int iwl_mld_update_device_power(struct iwl_mld *mld, bool d3)26{27struct iwl_device_power_cmd cmd = {};28bool enable_ps = false;2930if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) {31enable_ps = true;3233/* Disable power save if any STA interface has34* power save turned off35*/36ieee80211_iterate_active_interfaces_mtx(mld->hw,37IEEE80211_IFACE_ITER_NORMAL,38iwl_mld_vif_ps_iterator,39&enable_ps);40}4142if (enable_ps)43cmd.flags |=44cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);4546if (d3)47cmd.flags |=48cpu_to_le16(DEVICE_POWER_FLAGS_NO_SLEEP_TILL_D3_MSK);4950IWL_DEBUG_POWER(mld,51"Sending device power command with flags = 0x%X\n",52cmd.flags);5354return iwl_mld_send_cmd_pdu(mld, POWER_TABLE_CMD, &cmd);55}5657int iwl_mld_enable_beacon_filter(struct iwl_mld *mld,58const struct ieee80211_bss_conf *link_conf,59bool d3)60{61struct iwl_beacon_filter_cmd cmd = {62IWL_BF_CMD_CONFIG_DEFAULTS,63.bf_enable_beacon_filter = cpu_to_le32(1),64.ba_enable_beacon_abort = cpu_to_le32(1),65};6667if (ieee80211_vif_type_p2p(link_conf->vif) != NL80211_IFTYPE_STATION)68return 0;6970#ifdef CONFIG_IWLWIFI_DEBUGFS71if (iwl_mld_vif_from_mac80211(link_conf->vif)->disable_bf)72return 0;73#endif7475if (link_conf->cqm_rssi_thold) {76cmd.bf_energy_delta =77cpu_to_le32(link_conf->cqm_rssi_hyst);78/* fw uses an absolute value for this */79cmd.bf_roaming_state =80cpu_to_le32(-link_conf->cqm_rssi_thold);81}8283if (d3)84cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3);8586return iwl_mld_send_cmd_pdu(mld, REPLY_BEACON_FILTERING_CMD,87&cmd);88}8990int iwl_mld_disable_beacon_filter(struct iwl_mld *mld,91struct ieee80211_vif *vif)92{93struct iwl_beacon_filter_cmd cmd = {};9495if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION)96return 0;9798return iwl_mld_send_cmd_pdu(mld, REPLY_BEACON_FILTERING_CMD,99&cmd);100}101102static bool iwl_mld_power_is_radar(struct iwl_mld *mld,103const struct ieee80211_bss_conf *link_conf)104{105const struct ieee80211_chanctx_conf *chanctx_conf;106107chanctx_conf = wiphy_dereference(mld->wiphy, link_conf->chanctx_conf);108109if (WARN_ON(!chanctx_conf))110return false;111112return chanctx_conf->def.chan->flags & IEEE80211_CHAN_RADAR;113}114115static void iwl_mld_power_configure_uapsd(struct iwl_mld *mld,116struct iwl_mld_link *link,117struct iwl_mac_power_cmd *cmd,118bool ps_poll)119{120bool tid_found = false;121122cmd->rx_data_timeout_uapsd =123cpu_to_le32(IWL_MLD_UAPSD_RX_DATA_TIMEOUT);124cmd->tx_data_timeout_uapsd =125cpu_to_le32(IWL_MLD_UAPSD_TX_DATA_TIMEOUT);126127/* set advanced pm flag with no uapsd ACs to enable ps-poll */128if (ps_poll) {129cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);130return;131}132133for (enum ieee80211_ac_numbers ac = IEEE80211_AC_VO;134ac <= IEEE80211_AC_BK;135ac++) {136if (!link->queue_params[ac].uapsd)137continue;138139cmd->flags |=140cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK |141POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK);142143cmd->uapsd_ac_flags |= BIT(ac);144145/* QNDP TID - the highest TID with no admission control */146if (!tid_found && !link->queue_params[ac].acm) {147tid_found = true;148switch (ac) {149case IEEE80211_AC_VO:150cmd->qndp_tid = 6;151break;152case IEEE80211_AC_VI:153cmd->qndp_tid = 5;154break;155case IEEE80211_AC_BE:156cmd->qndp_tid = 0;157break;158case IEEE80211_AC_BK:159cmd->qndp_tid = 1;160break;161}162}163}164165if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |166BIT(IEEE80211_AC_VI) |167BIT(IEEE80211_AC_BE) |168BIT(IEEE80211_AC_BK))) {169cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);170cmd->snooze_interval = cpu_to_le16(IWL_MLD_PS_SNOOZE_INTERVAL);171cmd->snooze_window = cpu_to_le16(IWL_MLD_PS_SNOOZE_WINDOW);172}173174cmd->uapsd_max_sp = mld->hw->uapsd_max_sp_len;175}176177static void178iwl_mld_power_config_skip_dtim(struct iwl_mld *mld,179const struct ieee80211_bss_conf *link_conf,180struct iwl_mac_power_cmd *cmd)181{182unsigned int dtimper_tu;183unsigned int dtimper;184unsigned int skip;185186dtimper = link_conf->dtim_period ?: 1;187dtimper_tu = dtimper * link_conf->beacon_int;188189if (dtimper >= 10 || iwl_mld_power_is_radar(mld, link_conf))190return;191192if (WARN_ON(!dtimper_tu))193return;194195/* configure skip over dtim up to 900 TU DTIM interval */196skip = max_t(int, 1, 900 / dtimper_tu);197198cmd->skip_dtim_periods = skip;199cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);200}201202#define POWER_KEEP_ALIVE_PERIOD_SEC 25203static void iwl_mld_power_build_cmd(struct iwl_mld *mld,204struct ieee80211_vif *vif,205struct iwl_mac_power_cmd *cmd,206bool d3)207{208int dtimper, bi;209int keep_alive;210struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);211struct ieee80211_bss_conf *link_conf = &vif->bss_conf;212struct iwl_mld_link *link = &mld_vif->deflink;213bool ps_poll = false;214215cmd->id_and_color = cpu_to_le32(mld_vif->fw_id);216217if (ieee80211_vif_is_mld(vif)) {218int link_id;219220if (WARN_ON(!vif->active_links))221return;222223/* The firmware consumes one single configuration for the vif224* and can't differentiate between links, just pick the lowest225* link_id's configuration and use that.226*/227link_id = __ffs(vif->active_links);228link_conf = link_conf_dereference_check(vif, link_id);229link = iwl_mld_link_dereference_check(mld_vif, link_id);230231if (WARN_ON(!link_conf || !link))232return;233}234dtimper = link_conf->dtim_period;235bi = link_conf->beacon_int;236237/* Regardless of power management state the driver must set238* keep alive period. FW will use it for sending keep alive NDPs239* immediately after association. Check that keep alive period240* is at least 3 * DTIM241*/242keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi),243USEC_PER_SEC);244keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC);245cmd->keep_alive_seconds = cpu_to_le16(keep_alive);246247if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)248cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);249250if (!vif->cfg.ps || iwl_mld_tdls_sta_count(mld) > 0)251return;252253cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);254255if (iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0) >= 2)256cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK);257258/* firmware supports LPRX for beacons at rate 1 Mbps or 6 Mbps only */259if (link_conf->beacon_rate &&260(link_conf->beacon_rate->bitrate == 10 ||261link_conf->beacon_rate->bitrate == 60)) {262cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);263cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;264}265266if (d3) {267iwl_mld_power_config_skip_dtim(mld, link_conf, cmd);268cmd->rx_data_timeout =269cpu_to_le32(IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT);270cmd->tx_data_timeout =271cpu_to_le32(IWL_MLD_WOWLAN_PS_TX_DATA_TIMEOUT);272} else if (iwl_mld_vif_low_latency(mld_vif) && vif->p2p) {273cmd->tx_data_timeout =274cpu_to_le32(IWL_MLD_SHORT_PS_TX_DATA_TIMEOUT);275cmd->rx_data_timeout =276cpu_to_le32(IWL_MLD_SHORT_PS_RX_DATA_TIMEOUT);277} else {278cmd->rx_data_timeout =279cpu_to_le32(IWL_MLD_DEFAULT_PS_RX_DATA_TIMEOUT);280cmd->tx_data_timeout =281cpu_to_le32(IWL_MLD_DEFAULT_PS_TX_DATA_TIMEOUT);282}283284/* uAPSD is only enabled for specific certifications. For those cases,285* mac80211 will allow uAPSD. Always call iwl_mld_power_configure_uapsd286* which will look at what mac80211 is saying.287*/288#ifdef CONFIG_IWLWIFI_DEBUGFS289ps_poll = mld_vif->use_ps_poll;290#endif291iwl_mld_power_configure_uapsd(mld, link, cmd, ps_poll);292}293294int iwl_mld_update_mac_power(struct iwl_mld *mld, struct ieee80211_vif *vif,295bool d3)296{297struct iwl_mac_power_cmd cmd = {};298299iwl_mld_power_build_cmd(mld, vif, &cmd, d3);300301return iwl_mld_send_cmd_pdu(mld, MAC_PM_POWER_TABLE, &cmd);302}303304static void305iwl_mld_tpe_sta_cmd_data(struct iwl_txpower_constraints_cmd *cmd,306const struct ieee80211_bss_conf *link)307{308u8 i;309310/* NOTE: the 0 here is IEEE80211_TPE_CAT_6GHZ_DEFAULT,311* we fully ignore IEEE80211_TPE_CAT_6GHZ_SUBORDINATE312*/313314BUILD_BUG_ON(ARRAY_SIZE(cmd->psd_pwr) !=315ARRAY_SIZE(link->tpe.psd_local[0].power));316317/* if not valid, mac80211 puts default (max value) */318for (i = 0; i < ARRAY_SIZE(cmd->psd_pwr); i++)319cmd->psd_pwr[i] = min(link->tpe.psd_local[0].power[i],320link->tpe.psd_reg_client[0].power[i]);321322BUILD_BUG_ON(ARRAY_SIZE(cmd->eirp_pwr) !=323ARRAY_SIZE(link->tpe.max_local[0].power));324325for (i = 0; i < ARRAY_SIZE(cmd->eirp_pwr); i++)326cmd->eirp_pwr[i] = min(link->tpe.max_local[0].power[i],327link->tpe.max_reg_client[0].power[i]);328}329330void331iwl_mld_send_ap_tx_power_constraint_cmd(struct iwl_mld *mld,332struct ieee80211_vif *vif,333struct ieee80211_bss_conf *link)334{335struct iwl_txpower_constraints_cmd cmd = {};336struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);337int ret;338339lockdep_assert_wiphy(mld->wiphy);340341if (!mld_link->active)342return;343344if (link->chanreq.oper.chan->band != NL80211_BAND_6GHZ)345return;346347cmd.link_id = cpu_to_le16(mld_link->fw_id);348memset(cmd.psd_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.psd_pwr));349memset(cmd.eirp_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.eirp_pwr));350351if (vif->type == NL80211_IFTYPE_AP) {352cmd.ap_type = cpu_to_le16(IWL_6GHZ_AP_TYPE_VLP);353} else if (link->power_type == IEEE80211_REG_UNSET_AP) {354return;355} else {356cmd.ap_type = cpu_to_le16(link->power_type - 1);357iwl_mld_tpe_sta_cmd_data(&cmd, link);358}359360ret = iwl_mld_send_cmd_pdu(mld,361WIDE_ID(PHY_OPS_GROUP,362AP_TX_POWER_CONSTRAINTS_CMD),363&cmd);364if (ret)365IWL_ERR(mld,366"failed to send AP_TX_POWER_CONSTRAINTS_CMD (%d)\n",367ret);368}369370int iwl_mld_set_tx_power(struct iwl_mld *mld,371struct ieee80211_bss_conf *link_conf,372s16 tx_power)373{374u32 cmd_id = REDUCE_TX_POWER_CMD;375struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);376u16 u_tx_power = tx_power == IWL_DEFAULT_MAX_TX_POWER ?377IWL_DEV_MAX_TX_POWER : 8 * tx_power;378struct iwl_dev_tx_power_cmd cmd = {379.common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_LINK),380.common.pwr_restriction = cpu_to_le16(u_tx_power),381};382int len = sizeof(cmd.common) + sizeof(cmd.v10);383384if (WARN_ON(!mld_link))385return -ENODEV;386387cmd.common.link_id = cpu_to_le32(mld_link->fw_id);388389return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len);390}391392393