File Index
Files and directories
Sign in
FILE

power.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/intel/iwlwifi/mld/power.c C
  1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
  2/*
  3 * Copyright (C) 2024-2025 Intel Corporation
  4 */
  5#include <net/mac80211.h>
  6
  7#include "mld.h"
  8#include "hcmd.h"
  9#include "power.h"
 10#include "iface.h"
 11#include "link.h"
 12#include "constants.h"
 13
 14static void iwl_mld_vif_ps_iterator(void *data, u8 *mac,
 15				    struct ieee80211_vif *vif)
 16{
 17	bool *ps_enable = (bool *)data;
 18	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
 19
 20	if (vif->type != NL80211_IFTYPE_STATION)
 21		return;
 22
 23	*ps_enable &= !mld_vif->ps_disabled;
 24}
 25
 26int iwl_mld_update_device_power(struct iwl_mld *mld, bool d3)
 27{
 28	struct iwl_device_power_cmd cmd = {};
 29	bool enable_ps = false;
 30
 31	if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) {
 32		enable_ps = true;
 33
 34		/* Disable power save if any STA interface has
 35		 * power save turned off
 36		 */
 37		ieee80211_iterate_active_interfaces_mtx(mld->hw,
 38							IEEE80211_IFACE_ITER_NORMAL,
 39							iwl_mld_vif_ps_iterator,
 40							&enable_ps);
 41	}
 42
 43	if (enable_ps)
 44		cmd.flags |=
 45			cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
 46
 47	if (d3)
 48		cmd.flags |=
 49			cpu_to_le16(DEVICE_POWER_FLAGS_NO_SLEEP_TILL_D3_MSK);
 50
 51	IWL_DEBUG_POWER(mld,
 52			"Sending device power command with flags = 0x%X\n",
 53			cmd.flags);
 54
 55	return iwl_mld_send_cmd_pdu(mld, POWER_TABLE_CMD, &cmd);
 56}
 57
 58int iwl_mld_enable_beacon_filter(struct iwl_mld *mld,
 59				 const struct ieee80211_bss_conf *link_conf,
 60				 bool d3)
 61{
 62	struct iwl_beacon_filter_cmd cmd = {
 63		IWL_BF_CMD_CONFIG_DEFAULTS,
 64		.bf_enable_beacon_filter = cpu_to_le32(1),
 65		.ba_enable_beacon_abort = cpu_to_le32(1),
 66	};
 67
 68	if (ieee80211_vif_type_p2p(link_conf->vif) != NL80211_IFTYPE_STATION)
 69		return 0;
 70
 71#ifdef CONFIG_IWLWIFI_DEBUGFS
 72	if (iwl_mld_vif_from_mac80211(link_conf->vif)->disable_bf)
 73		return 0;
 74#endif
 75
 76	if (link_conf->cqm_rssi_thold) {
 77		cmd.bf_energy_delta =
 78			cpu_to_le32(link_conf->cqm_rssi_hyst);
 79		/* fw uses an absolute value for this */
 80		cmd.bf_roaming_state =
 81			cpu_to_le32(-link_conf->cqm_rssi_thold);
 82	}
 83
 84	if (d3)
 85		cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3);
 86
 87	return iwl_mld_send_cmd_pdu(mld, REPLY_BEACON_FILTERING_CMD,
 88				    &cmd);
 89}
 90
 91int iwl_mld_disable_beacon_filter(struct iwl_mld *mld,
 92				  struct ieee80211_vif *vif)
 93{
 94	struct iwl_beacon_filter_cmd cmd = {};
 95
 96	if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION)
 97		return 0;
 98
 99	return iwl_mld_send_cmd_pdu(mld, REPLY_BEACON_FILTERING_CMD,
100				    &cmd);
101}
102
103static bool iwl_mld_power_is_radar(struct iwl_mld *mld,
104				   const struct ieee80211_bss_conf *link_conf)
105{
106	const struct ieee80211_chanctx_conf *chanctx_conf;
107
108	chanctx_conf = wiphy_dereference(mld->wiphy, link_conf->chanctx_conf);
109
110	if (WARN_ON(!chanctx_conf))
111		return false;
112
113	return chanctx_conf->def.chan->flags & IEEE80211_CHAN_RADAR;
114}
115
116static void iwl_mld_power_configure_uapsd(struct iwl_mld *mld,
117					  struct iwl_mld_link *link,
118					  struct iwl_mac_power_cmd *cmd,
119					  bool ps_poll)
120{
121	bool tid_found = false;
122
123	cmd->rx_data_timeout_uapsd =
124		cpu_to_le32(IWL_MLD_UAPSD_RX_DATA_TIMEOUT);
125	cmd->tx_data_timeout_uapsd =
126		cpu_to_le32(IWL_MLD_UAPSD_TX_DATA_TIMEOUT);
127
128	 /* set advanced pm flag with no uapsd ACs to enable ps-poll */
129	if (ps_poll) {
130		cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
131		return;
132	}
133
134	for (enum ieee80211_ac_numbers ac = IEEE80211_AC_VO;
135	     ac <= IEEE80211_AC_BK;
136	     ac++) {
137		if (!link->queue_params[ac].uapsd)
138			continue;
139
140		cmd->flags |=
141			cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK |
142				    POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK);
143
144		cmd->uapsd_ac_flags |= BIT(ac);
145
146		/* QNDP TID - the highest TID with no admission control */
147		if (!tid_found && !link->queue_params[ac].acm) {
148			tid_found = true;
149			switch (ac) {
150			case IEEE80211_AC_VO:
151				cmd->qndp_tid = 6;
152				break;
153			case IEEE80211_AC_VI:
154				cmd->qndp_tid = 5;
155				break;
156			case IEEE80211_AC_BE:
157				cmd->qndp_tid = 0;
158				break;
159			case IEEE80211_AC_BK:
160				cmd->qndp_tid = 1;
161				break;
162			}
163		}
164	}
165
166	if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |
167				    BIT(IEEE80211_AC_VI) |
168				    BIT(IEEE80211_AC_BE) |
169				    BIT(IEEE80211_AC_BK))) {
170		cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
171		cmd->snooze_interval = cpu_to_le16(IWL_MLD_PS_SNOOZE_INTERVAL);
172		cmd->snooze_window = cpu_to_le16(IWL_MLD_PS_SNOOZE_WINDOW);
173	}
174
175	cmd->uapsd_max_sp = mld->hw->uapsd_max_sp_len;
176}
177
178static void
179iwl_mld_power_config_skip_dtim(struct iwl_mld *mld,
180			       const struct ieee80211_bss_conf *link_conf,
181			       struct iwl_mac_power_cmd *cmd)
182{
183	unsigned int dtimper_tu;
184	unsigned int dtimper;
185	unsigned int skip;
186
187	dtimper = link_conf->dtim_period ?: 1;
188	dtimper_tu = dtimper * link_conf->beacon_int;
189
190	if (dtimper >= 10 || iwl_mld_power_is_radar(mld, link_conf))
191		return;
192
193	if (WARN_ON(!dtimper_tu))
194		return;
195
196	/* configure skip over dtim up to 900 TU DTIM interval */
197	skip = max_t(int, 1, 900 / dtimper_tu);
198
199	cmd->skip_dtim_periods = skip;
200	cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
201}
202
203#define POWER_KEEP_ALIVE_PERIOD_SEC    25
204static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
205				    struct ieee80211_vif *vif,
206				    struct iwl_mac_power_cmd *cmd,
207				    bool d3)
208{
209	int dtimper, bi;
210	int keep_alive;
211	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
212	struct ieee80211_bss_conf *link_conf = &vif->bss_conf;
213	struct iwl_mld_link *link = &mld_vif->deflink;
214	bool ps_poll = false;
215
216	cmd->id_and_color = cpu_to_le32(mld_vif->fw_id);
217
218	if (ieee80211_vif_is_mld(vif)) {
219		int link_id;
220
221		if (WARN_ON(!vif->active_links))
222			return;
223
224		/* The firmware consumes one single configuration for the vif
225		 * and can't differentiate between links, just pick the lowest
226		 * link_id's configuration and use that.
227		 */
228		link_id = __ffs(vif->active_links);
229		link_conf = link_conf_dereference_check(vif, link_id);
230		link = iwl_mld_link_dereference_check(mld_vif, link_id);
231
232		if (WARN_ON(!link_conf || !link))
233			return;
234	}
235	dtimper = link_conf->dtim_period;
236	bi = link_conf->beacon_int;
237
238	/* Regardless of power management state the driver must set
239	 * keep alive period. FW will use it for sending keep alive NDPs
240	 * immediately after association. Check that keep alive period
241	 * is at least 3 * DTIM
242	 */
243	keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi),
244				  USEC_PER_SEC);
245	keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC);
246	cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
247
248	if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
249		cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
250
251	if (!vif->cfg.ps || iwl_mld_tdls_sta_count(mld) > 0)
252		return;
253
254	cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
255
256	if (iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0) >= 2)
257		cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK);
258
259	/* firmware supports LPRX for beacons at rate 1 Mbps or 6 Mbps only */
260	if (link_conf->beacon_rate &&
261	    (link_conf->beacon_rate->bitrate == 10 ||
262	     link_conf->beacon_rate->bitrate == 60)) {
263		cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
264		cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;
265	}
266
267	if (d3) {
268		iwl_mld_power_config_skip_dtim(mld, link_conf, cmd);
269		cmd->rx_data_timeout =
270			cpu_to_le32(IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT);
271		cmd->tx_data_timeout =
272			cpu_to_le32(IWL_MLD_WOWLAN_PS_TX_DATA_TIMEOUT);
273	} else if (iwl_mld_vif_low_latency(mld_vif) && vif->p2p) {
274		cmd->tx_data_timeout =
275			cpu_to_le32(IWL_MLD_SHORT_PS_TX_DATA_TIMEOUT);
276		cmd->rx_data_timeout =
277			cpu_to_le32(IWL_MLD_SHORT_PS_RX_DATA_TIMEOUT);
278	} else {
279		cmd->rx_data_timeout =
280			cpu_to_le32(IWL_MLD_DEFAULT_PS_RX_DATA_TIMEOUT);
281		cmd->tx_data_timeout =
282			cpu_to_le32(IWL_MLD_DEFAULT_PS_TX_DATA_TIMEOUT);
283	}
284
285	/* uAPSD is only enabled for specific certifications. For those cases,
286	 * mac80211 will allow uAPSD. Always call iwl_mld_power_configure_uapsd
287	 * which will look at what mac80211 is saying.
288	 */
289#ifdef CONFIG_IWLWIFI_DEBUGFS
290	ps_poll = mld_vif->use_ps_poll;
291#endif
292	iwl_mld_power_configure_uapsd(mld, link, cmd, ps_poll);
293}
294
295int iwl_mld_update_mac_power(struct iwl_mld *mld, struct ieee80211_vif *vif,
296			     bool d3)
297{
298	struct iwl_mac_power_cmd cmd = {};
299
300	iwl_mld_power_build_cmd(mld, vif, &cmd, d3);
301
302	return iwl_mld_send_cmd_pdu(mld, MAC_PM_POWER_TABLE, &cmd);
303}
304
305static void
306iwl_mld_tpe_sta_cmd_data(struct iwl_txpower_constraints_cmd *cmd,
307			 const struct ieee80211_bss_conf *link)
308{
309	u8 i;
310
311	/* NOTE: the 0 here is IEEE80211_TPE_CAT_6GHZ_DEFAULT,
312	 * we fully ignore IEEE80211_TPE_CAT_6GHZ_SUBORDINATE
313	 */
314
315	BUILD_BUG_ON(ARRAY_SIZE(cmd->psd_pwr) !=
316		     ARRAY_SIZE(link->tpe.psd_local[0].power));
317
318	/* if not valid, mac80211 puts default (max value) */
319	for (i = 0; i < ARRAY_SIZE(cmd->psd_pwr); i++)
320		cmd->psd_pwr[i] = min(link->tpe.psd_local[0].power[i],
321				      link->tpe.psd_reg_client[0].power[i]);
322
323	BUILD_BUG_ON(ARRAY_SIZE(cmd->eirp_pwr) !=
324		     ARRAY_SIZE(link->tpe.max_local[0].power));
325
326	for (i = 0; i < ARRAY_SIZE(cmd->eirp_pwr); i++)
327		cmd->eirp_pwr[i] = min(link->tpe.max_local[0].power[i],
328				       link->tpe.max_reg_client[0].power[i]);
329}
330
331void
332iwl_mld_send_ap_tx_power_constraint_cmd(struct iwl_mld *mld,
333					struct ieee80211_vif *vif,
334					struct ieee80211_bss_conf *link)
335{
336	struct iwl_txpower_constraints_cmd cmd = {};
337	struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
338	int ret;
339
340	lockdep_assert_wiphy(mld->wiphy);
341
342	if (!mld_link->active)
343		return;
344
345	if (link->chanreq.oper.chan->band != NL80211_BAND_6GHZ)
346		return;
347
348	cmd.link_id = cpu_to_le16(mld_link->fw_id);
349	memset(cmd.psd_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.psd_pwr));
350	memset(cmd.eirp_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.eirp_pwr));
351
352	if (vif->type == NL80211_IFTYPE_AP) {
353		cmd.ap_type = cpu_to_le16(IWL_6GHZ_AP_TYPE_VLP);
354	} else if (link->power_type == IEEE80211_REG_UNSET_AP) {
355		return;
356	} else {
357		cmd.ap_type = cpu_to_le16(link->power_type - 1);
358		iwl_mld_tpe_sta_cmd_data(&cmd, link);
359	}
360
361	ret = iwl_mld_send_cmd_pdu(mld,
362				   WIDE_ID(PHY_OPS_GROUP,
363					   AP_TX_POWER_CONSTRAINTS_CMD),
364				   &cmd);
365	if (ret)
366		IWL_ERR(mld,
367			"failed to send AP_TX_POWER_CONSTRAINTS_CMD (%d)\n",
368			ret);
369}
370
371int iwl_mld_set_tx_power(struct iwl_mld *mld,
372			 struct ieee80211_bss_conf *link_conf,
373			 s16 tx_power)
374{
375	u32 cmd_id = REDUCE_TX_POWER_CMD;
376	struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
377	u16 u_tx_power = tx_power == IWL_DEFAULT_MAX_TX_POWER ?
378		IWL_DEV_MAX_TX_POWER : 8 * tx_power;
379	struct iwl_dev_tx_power_cmd cmd = {
380		.common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_LINK),
381		.common.pwr_restriction = cpu_to_le16(u_tx_power),
382	};
383	int len = sizeof(cmd.common) + sizeof(cmd.v10);
384
385	if (WARN_ON(!mld_link))
386		return -ENODEV;
387
388	cmd.common.link_id = cpu_to_le32(mld_link->fw_id);
389
390	return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len);
391}
~/ Read-only file preview.