xref: /linux/drivers/net/wireless/intel/iwlwifi/mld/power.c (revision 1a9239bb4253f9076b5b4b2a1a4e8d7defd77a95)
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 
iwl_mld_vif_ps_iterator(void * data,u8 * mac,struct ieee80211_vif * vif)14 static 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 
iwl_mld_update_device_power(struct iwl_mld * mld,bool d3)26 int 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 
iwl_mld_enable_beacon_filter(struct iwl_mld * mld,const struct ieee80211_bss_conf * link_conf,bool d3)58 int 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 
iwl_mld_disable_beacon_filter(struct iwl_mld * mld,struct ieee80211_vif * vif)91 int 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 
iwl_mld_power_is_radar(struct iwl_mld * mld,const struct ieee80211_bss_conf * link_conf)103 static 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 
iwl_mld_power_configure_uapsd(struct iwl_mld * mld,struct iwl_mld_link * link,struct iwl_mac_power_cmd * cmd,bool ps_poll)116 static 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 
178 static void
iwl_mld_power_config_skip_dtim(struct iwl_mld * mld,const struct ieee80211_bss_conf * link_conf,struct iwl_mac_power_cmd * cmd)179 iwl_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
iwl_mld_power_build_cmd(struct iwl_mld * mld,struct ieee80211_vif * vif,struct iwl_mac_power_cmd * cmd,bool d3)204 static 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 	/* firmware supports LPRX for beacons at rate 1 Mbps or 6 Mbps only */
257 	if (link_conf->beacon_rate &&
258 	    (link_conf->beacon_rate->bitrate == 10 ||
259 	     link_conf->beacon_rate->bitrate == 60)) {
260 		cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
261 		cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;
262 	}
263 
264 	if (d3) {
265 		iwl_mld_power_config_skip_dtim(mld, link_conf, cmd);
266 		cmd->rx_data_timeout =
267 			cpu_to_le32(IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT);
268 		cmd->tx_data_timeout =
269 			cpu_to_le32(IWL_MLD_WOWLAN_PS_TX_DATA_TIMEOUT);
270 	} else if (iwl_mld_vif_low_latency(mld_vif) && vif->p2p) {
271 		cmd->tx_data_timeout =
272 			cpu_to_le32(IWL_MLD_SHORT_PS_TX_DATA_TIMEOUT);
273 		cmd->rx_data_timeout =
274 			cpu_to_le32(IWL_MLD_SHORT_PS_RX_DATA_TIMEOUT);
275 	} else {
276 		cmd->rx_data_timeout =
277 			cpu_to_le32(IWL_MLD_DEFAULT_PS_RX_DATA_TIMEOUT);
278 		cmd->tx_data_timeout =
279 			cpu_to_le32(IWL_MLD_DEFAULT_PS_TX_DATA_TIMEOUT);
280 	}
281 
282 	/* uAPSD is only enabled for specific certifications. For those cases,
283 	 * mac80211 will allow uAPSD. Always call iwl_mld_power_configure_uapsd
284 	 * which will look at what mac80211 is saying.
285 	 */
286 #ifdef CONFIG_IWLWIFI_DEBUGFS
287 	ps_poll = mld_vif->use_ps_poll;
288 #endif
289 	iwl_mld_power_configure_uapsd(mld, link, cmd, ps_poll);
290 }
291 
iwl_mld_update_mac_power(struct iwl_mld * mld,struct ieee80211_vif * vif,bool d3)292 int iwl_mld_update_mac_power(struct iwl_mld *mld, struct ieee80211_vif *vif,
293 			     bool d3)
294 {
295 	struct iwl_mac_power_cmd cmd = {};
296 
297 	iwl_mld_power_build_cmd(mld, vif, &cmd, d3);
298 
299 	return iwl_mld_send_cmd_pdu(mld, MAC_PM_POWER_TABLE, &cmd);
300 }
301 
302 static void
iwl_mld_tpe_sta_cmd_data(struct iwl_txpower_constraints_cmd * cmd,const struct ieee80211_bss_conf * link)303 iwl_mld_tpe_sta_cmd_data(struct iwl_txpower_constraints_cmd *cmd,
304 			 const struct ieee80211_bss_conf *link)
305 {
306 	u8 i;
307 
308 	/* NOTE: the 0 here is IEEE80211_TPE_CAT_6GHZ_DEFAULT,
309 	 * we fully ignore IEEE80211_TPE_CAT_6GHZ_SUBORDINATE
310 	 */
311 
312 	BUILD_BUG_ON(ARRAY_SIZE(cmd->psd_pwr) !=
313 		     ARRAY_SIZE(link->tpe.psd_local[0].power));
314 
315 	/* if not valid, mac80211 puts default (max value) */
316 	for (i = 0; i < ARRAY_SIZE(cmd->psd_pwr); i++)
317 		cmd->psd_pwr[i] = min(link->tpe.psd_local[0].power[i],
318 				      link->tpe.psd_reg_client[0].power[i]);
319 
320 	BUILD_BUG_ON(ARRAY_SIZE(cmd->eirp_pwr) !=
321 		     ARRAY_SIZE(link->tpe.max_local[0].power));
322 
323 	for (i = 0; i < ARRAY_SIZE(cmd->eirp_pwr); i++)
324 		cmd->eirp_pwr[i] = min(link->tpe.max_local[0].power[i],
325 				       link->tpe.max_reg_client[0].power[i]);
326 }
327 
328 void
iwl_mld_send_ap_tx_power_constraint_cmd(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link)329 iwl_mld_send_ap_tx_power_constraint_cmd(struct iwl_mld *mld,
330 					struct ieee80211_vif *vif,
331 					struct ieee80211_bss_conf *link)
332 {
333 	struct iwl_txpower_constraints_cmd cmd = {};
334 	struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
335 	int ret;
336 
337 	lockdep_assert_wiphy(mld->wiphy);
338 
339 	if (!mld_link->active)
340 		return;
341 
342 	if (link->chanreq.oper.chan->band != NL80211_BAND_6GHZ)
343 		return;
344 
345 	cmd.link_id = cpu_to_le16(mld_link->fw_id);
346 	memset(cmd.psd_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.psd_pwr));
347 	memset(cmd.eirp_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.eirp_pwr));
348 
349 	if (vif->type == NL80211_IFTYPE_AP) {
350 		cmd.ap_type = cpu_to_le16(IWL_6GHZ_AP_TYPE_VLP);
351 	} else if (link->power_type == IEEE80211_REG_UNSET_AP) {
352 		return;
353 	} else {
354 		cmd.ap_type = cpu_to_le16(link->power_type - 1);
355 		iwl_mld_tpe_sta_cmd_data(&cmd, link);
356 	}
357 
358 	ret = iwl_mld_send_cmd_pdu(mld,
359 				   WIDE_ID(PHY_OPS_GROUP,
360 					   AP_TX_POWER_CONSTRAINTS_CMD),
361 				   &cmd);
362 	if (ret)
363 		IWL_ERR(mld,
364 			"failed to send AP_TX_POWER_CONSTRAINTS_CMD (%d)\n",
365 			ret);
366 }
367 
iwl_mld_set_tx_power(struct iwl_mld * mld,struct ieee80211_bss_conf * link_conf,s16 tx_power)368 int iwl_mld_set_tx_power(struct iwl_mld *mld,
369 			 struct ieee80211_bss_conf *link_conf,
370 			 s16 tx_power)
371 {
372 	u32 cmd_id = REDUCE_TX_POWER_CMD;
373 	struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
374 	u16 u_tx_power = tx_power == IWL_DEFAULT_MAX_TX_POWER ?
375 		IWL_DEV_MAX_TX_POWER : 8 * tx_power;
376 	struct iwl_dev_tx_power_cmd cmd = {
377 		/* Those fields sit on the same place for v9 and v10 */
378 		.common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_LINK),
379 		.common.pwr_restriction = cpu_to_le16(u_tx_power),
380 	};
381 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id,
382 					   IWL_FW_CMD_VER_UNKNOWN);
383 	int len = sizeof(cmd.common);
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 	if (cmd_ver == 10)
391 		len += sizeof(cmd.v10);
392 	else if (cmd_ver == 9)
393 		len += sizeof(cmd.v9);
394 
395 	return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len);
396 }
397