xref: /freebsd/sys/contrib/dev/iwlwifi/mvm/link.c (revision a4128aad8503277614f2d214011ef60a19447b83)
19af1bba4SBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
29af1bba4SBjoern A. Zeeb /*
3*a4128aadSBjoern A. Zeeb  * Copyright (C) 2022 - 2024 Intel Corporation
49af1bba4SBjoern A. Zeeb  */
59af1bba4SBjoern A. Zeeb #include "mvm.h"
69af1bba4SBjoern A. Zeeb #include "time-event.h"
79af1bba4SBjoern A. Zeeb 
8*a4128aadSBjoern A. Zeeb #define HANDLE_ESR_REASONS(HOW)		\
9*a4128aadSBjoern A. Zeeb 	HOW(BLOCKED_PREVENTION)		\
10*a4128aadSBjoern A. Zeeb 	HOW(BLOCKED_WOWLAN)		\
11*a4128aadSBjoern A. Zeeb 	HOW(BLOCKED_TPT)		\
12*a4128aadSBjoern A. Zeeb 	HOW(BLOCKED_FW)			\
13*a4128aadSBjoern A. Zeeb 	HOW(BLOCKED_NON_BSS)		\
14*a4128aadSBjoern A. Zeeb 	HOW(BLOCKED_ROC)		\
15*a4128aadSBjoern A. Zeeb 	HOW(EXIT_MISSED_BEACON)		\
16*a4128aadSBjoern A. Zeeb 	HOW(EXIT_LOW_RSSI)		\
17*a4128aadSBjoern A. Zeeb 	HOW(EXIT_COEX)			\
18*a4128aadSBjoern A. Zeeb 	HOW(EXIT_BANDWIDTH)		\
19*a4128aadSBjoern A. Zeeb 	HOW(EXIT_CSA)			\
20*a4128aadSBjoern A. Zeeb 	HOW(EXIT_LINK_USAGE)
21*a4128aadSBjoern A. Zeeb 
22*a4128aadSBjoern A. Zeeb static const char *const iwl_mvm_esr_states_names[] = {
23*a4128aadSBjoern A. Zeeb #define NAME_ENTRY(x) [ilog2(IWL_MVM_ESR_##x)] = #x,
24*a4128aadSBjoern A. Zeeb 	HANDLE_ESR_REASONS(NAME_ENTRY)
25*a4128aadSBjoern A. Zeeb };
26*a4128aadSBjoern A. Zeeb 
27*a4128aadSBjoern A. Zeeb const char *iwl_get_esr_state_string(enum iwl_mvm_esr_state state)
28*a4128aadSBjoern A. Zeeb {
29*a4128aadSBjoern A. Zeeb 	int offs = ilog2(state);
30*a4128aadSBjoern A. Zeeb 
31*a4128aadSBjoern A. Zeeb 	if (offs >= ARRAY_SIZE(iwl_mvm_esr_states_names) ||
32*a4128aadSBjoern A. Zeeb 	    !iwl_mvm_esr_states_names[offs])
33*a4128aadSBjoern A. Zeeb 		return "UNKNOWN";
34*a4128aadSBjoern A. Zeeb 
35*a4128aadSBjoern A. Zeeb 	return iwl_mvm_esr_states_names[offs];
36*a4128aadSBjoern A. Zeeb }
37*a4128aadSBjoern A. Zeeb 
38*a4128aadSBjoern A. Zeeb static void iwl_mvm_print_esr_state(struct iwl_mvm *mvm, u32 mask)
39*a4128aadSBjoern A. Zeeb {
40*a4128aadSBjoern A. Zeeb #define NAME_FMT(x) "%s"
41*a4128aadSBjoern A. Zeeb #define NAME_PR(x) (mask & IWL_MVM_ESR_##x) ? "[" #x "]" : "",
42*a4128aadSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm,
43*a4128aadSBjoern A. Zeeb 		       "EMLSR state = " HANDLE_ESR_REASONS(NAME_FMT)
44*a4128aadSBjoern A. Zeeb 		       " (0x%x)\n",
45*a4128aadSBjoern A. Zeeb 		       HANDLE_ESR_REASONS(NAME_PR)
46*a4128aadSBjoern A. Zeeb 		       mask);
47*a4128aadSBjoern A. Zeeb #undef NAME_FMT
48*a4128aadSBjoern A. Zeeb #undef NAME_PR
49*a4128aadSBjoern A. Zeeb }
50*a4128aadSBjoern A. Zeeb 
519af1bba4SBjoern A. Zeeb static u32 iwl_mvm_get_free_fw_link_id(struct iwl_mvm *mvm,
529af1bba4SBjoern A. Zeeb 				       struct iwl_mvm_vif *mvm_vif)
539af1bba4SBjoern A. Zeeb {
54*a4128aadSBjoern A. Zeeb 	u32 i;
559af1bba4SBjoern A. Zeeb 
569af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
579af1bba4SBjoern A. Zeeb 
58*a4128aadSBjoern A. Zeeb 	for (i = 0; i < ARRAY_SIZE(mvm->link_id_to_link_conf); i++)
59*a4128aadSBjoern A. Zeeb 		if (!rcu_access_pointer(mvm->link_id_to_link_conf[i]))
60*a4128aadSBjoern A. Zeeb 			return i;
619af1bba4SBjoern A. Zeeb 
629af1bba4SBjoern A. Zeeb 	return IWL_MVM_FW_LINK_ID_INVALID;
639af1bba4SBjoern A. Zeeb }
649af1bba4SBjoern A. Zeeb 
659af1bba4SBjoern A. Zeeb static int iwl_mvm_link_cmd_send(struct iwl_mvm *mvm,
669af1bba4SBjoern A. Zeeb 				 struct iwl_link_config_cmd *cmd,
679af1bba4SBjoern A. Zeeb 				 enum iwl_ctxt_action action)
689af1bba4SBjoern A. Zeeb {
699af1bba4SBjoern A. Zeeb 	int ret;
709af1bba4SBjoern A. Zeeb 
719af1bba4SBjoern A. Zeeb 	cmd->action = cpu_to_le32(action);
729af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_send_cmd_pdu(mvm,
739af1bba4SBjoern A. Zeeb 				   WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD), 0,
749af1bba4SBjoern A. Zeeb 				   sizeof(*cmd), cmd);
759af1bba4SBjoern A. Zeeb 	if (ret)
769af1bba4SBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to send LINK_CONFIG_CMD (action:%d): %d\n",
779af1bba4SBjoern A. Zeeb 			action, ret);
789af1bba4SBjoern A. Zeeb 	return ret;
799af1bba4SBjoern A. Zeeb }
809af1bba4SBjoern A. Zeeb 
81*a4128aadSBjoern A. Zeeb int iwl_mvm_set_link_mapping(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
82*a4128aadSBjoern A. Zeeb 			     struct ieee80211_bss_conf *link_conf)
83*a4128aadSBjoern A. Zeeb {
84*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
85*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_vif_link_info *link_info =
86*a4128aadSBjoern A. Zeeb 		mvmvif->link[link_conf->link_id];
87*a4128aadSBjoern A. Zeeb 
88*a4128aadSBjoern A. Zeeb 	if (link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID) {
89*a4128aadSBjoern A. Zeeb 		link_info->fw_link_id = iwl_mvm_get_free_fw_link_id(mvm,
90*a4128aadSBjoern A. Zeeb 								    mvmvif);
91*a4128aadSBjoern A. Zeeb 		if (link_info->fw_link_id >=
92*a4128aadSBjoern A. Zeeb 		    ARRAY_SIZE(mvm->link_id_to_link_conf))
93*a4128aadSBjoern A. Zeeb 			return -EINVAL;
94*a4128aadSBjoern A. Zeeb 
95*a4128aadSBjoern A. Zeeb 		rcu_assign_pointer(mvm->link_id_to_link_conf[link_info->fw_link_id],
96*a4128aadSBjoern A. Zeeb 				   link_conf);
97*a4128aadSBjoern A. Zeeb 	}
98*a4128aadSBjoern A. Zeeb 
99*a4128aadSBjoern A. Zeeb 	return 0;
100*a4128aadSBjoern A. Zeeb }
101*a4128aadSBjoern A. Zeeb 
1029af1bba4SBjoern A. Zeeb int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
1039af1bba4SBjoern A. Zeeb 		     struct ieee80211_bss_conf *link_conf)
1049af1bba4SBjoern A. Zeeb {
1059af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1069af1bba4SBjoern A. Zeeb 	unsigned int link_id = link_conf->link_id;
1079af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
1089af1bba4SBjoern A. Zeeb 	struct iwl_link_config_cmd cmd = {};
109*a4128aadSBjoern A. Zeeb 	unsigned int cmd_id = WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD);
110*a4128aadSBjoern A. Zeeb 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1);
111*a4128aadSBjoern A. Zeeb 	int ret;
1129af1bba4SBjoern A. Zeeb 
1139af1bba4SBjoern A. Zeeb 	if (WARN_ON_ONCE(!link_info))
1149af1bba4SBjoern A. Zeeb 		return -EINVAL;
1159af1bba4SBjoern A. Zeeb 
116*a4128aadSBjoern A. Zeeb 	ret = iwl_mvm_set_link_mapping(mvm, vif, link_conf);
117*a4128aadSBjoern A. Zeeb 	if (ret)
118*a4128aadSBjoern A. Zeeb 		return ret;
1199af1bba4SBjoern A. Zeeb 
1209af1bba4SBjoern A. Zeeb 	/* Update SF - Disable if needed. if this fails, SF might still be on
1219af1bba4SBjoern A. Zeeb 	 * while many macs are bound, which is forbidden - so fail the binding.
1229af1bba4SBjoern A. Zeeb 	 */
1239af1bba4SBjoern A. Zeeb 	if (iwl_mvm_sf_update(mvm, vif, false))
1249af1bba4SBjoern A. Zeeb 		return -EINVAL;
1259af1bba4SBjoern A. Zeeb 
1269af1bba4SBjoern A. Zeeb 	cmd.link_id = cpu_to_le32(link_info->fw_link_id);
1279af1bba4SBjoern A. Zeeb 	cmd.mac_id = cpu_to_le32(mvmvif->id);
1289af1bba4SBjoern A. Zeeb 	cmd.spec_link_id = link_conf->link_id;
129*a4128aadSBjoern A. Zeeb 	WARN_ON_ONCE(link_info->phy_ctxt);
1309af1bba4SBjoern A. Zeeb 	cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
1319af1bba4SBjoern A. Zeeb 
1329af1bba4SBjoern A. Zeeb 	memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN);
1339af1bba4SBjoern A. Zeeb 
1349af1bba4SBjoern A. Zeeb 	if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
1359af1bba4SBjoern A. Zeeb 		memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN);
1369af1bba4SBjoern A. Zeeb 
137*a4128aadSBjoern A. Zeeb 	if (cmd_ver < 2)
1389af1bba4SBjoern A. Zeeb 		cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac);
1399af1bba4SBjoern A. Zeeb 
1409af1bba4SBjoern A. Zeeb 	return iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_ADD);
1419af1bba4SBjoern A. Zeeb }
1429af1bba4SBjoern A. Zeeb 
143*a4128aadSBjoern A. Zeeb struct iwl_mvm_esr_iter_data {
144*a4128aadSBjoern A. Zeeb 	struct ieee80211_vif *vif;
145*a4128aadSBjoern A. Zeeb 	unsigned int link_id;
146*a4128aadSBjoern A. Zeeb 	bool lift_block;
147*a4128aadSBjoern A. Zeeb };
148*a4128aadSBjoern A. Zeeb 
149*a4128aadSBjoern A. Zeeb static void iwl_mvm_esr_vif_iterator(void *_data, u8 *mac,
150*a4128aadSBjoern A. Zeeb 				     struct ieee80211_vif *vif)
151*a4128aadSBjoern A. Zeeb {
152*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_esr_iter_data *data = _data;
153*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
154*a4128aadSBjoern A. Zeeb 	int link_id;
155*a4128aadSBjoern A. Zeeb 
156*a4128aadSBjoern A. Zeeb 	if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION)
157*a4128aadSBjoern A. Zeeb 		return;
158*a4128aadSBjoern A. Zeeb 
159*a4128aadSBjoern A. Zeeb 	for_each_mvm_vif_valid_link(mvmvif, link_id) {
160*a4128aadSBjoern A. Zeeb 		struct iwl_mvm_vif_link_info *link_info =
161*a4128aadSBjoern A. Zeeb 			mvmvif->link[link_id];
162*a4128aadSBjoern A. Zeeb 		if (vif == data->vif && link_id == data->link_id)
163*a4128aadSBjoern A. Zeeb 			continue;
164*a4128aadSBjoern A. Zeeb 		if (link_info->active)
165*a4128aadSBjoern A. Zeeb 			data->lift_block = false;
166*a4128aadSBjoern A. Zeeb 	}
167*a4128aadSBjoern A. Zeeb }
168*a4128aadSBjoern A. Zeeb 
169*a4128aadSBjoern A. Zeeb int iwl_mvm_esr_non_bss_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
170*a4128aadSBjoern A. Zeeb 			     unsigned int link_id, bool active)
171*a4128aadSBjoern A. Zeeb {
172*a4128aadSBjoern A. Zeeb 	/* An active link of a non-station vif blocks EMLSR. Upon activation
173*a4128aadSBjoern A. Zeeb 	 * block EMLSR on the bss vif. Upon deactivation, check if this link
174*a4128aadSBjoern A. Zeeb 	 * was the last non-station link active, and if so unblock the bss vif
175*a4128aadSBjoern A. Zeeb 	 */
176*a4128aadSBjoern A. Zeeb 	struct ieee80211_vif *bss_vif = iwl_mvm_get_bss_vif(mvm);
177*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_esr_iter_data data = {
178*a4128aadSBjoern A. Zeeb 		.vif = vif,
179*a4128aadSBjoern A. Zeeb 		.link_id = link_id,
180*a4128aadSBjoern A. Zeeb 		.lift_block = true,
181*a4128aadSBjoern A. Zeeb 	};
182*a4128aadSBjoern A. Zeeb 
183*a4128aadSBjoern A. Zeeb 	if (IS_ERR_OR_NULL(bss_vif))
184*a4128aadSBjoern A. Zeeb 		return 0;
185*a4128aadSBjoern A. Zeeb 
186*a4128aadSBjoern A. Zeeb 	if (active)
187*a4128aadSBjoern A. Zeeb 		return iwl_mvm_block_esr_sync(mvm, bss_vif,
188*a4128aadSBjoern A. Zeeb 					      IWL_MVM_ESR_BLOCKED_NON_BSS);
189*a4128aadSBjoern A. Zeeb 
190*a4128aadSBjoern A. Zeeb 	ieee80211_iterate_active_interfaces(mvm->hw,
191*a4128aadSBjoern A. Zeeb 					    IEEE80211_IFACE_ITER_NORMAL,
192*a4128aadSBjoern A. Zeeb 					    iwl_mvm_esr_vif_iterator, &data);
193*a4128aadSBjoern A. Zeeb 	if (data.lift_block) {
194*a4128aadSBjoern A. Zeeb 		mutex_lock(&mvm->mutex);
195*a4128aadSBjoern A. Zeeb 		iwl_mvm_unblock_esr(mvm, bss_vif, IWL_MVM_ESR_BLOCKED_NON_BSS);
196*a4128aadSBjoern A. Zeeb 		mutex_unlock(&mvm->mutex);
197*a4128aadSBjoern A. Zeeb 	}
198*a4128aadSBjoern A. Zeeb 
199*a4128aadSBjoern A. Zeeb 	return 0;
200*a4128aadSBjoern A. Zeeb }
201*a4128aadSBjoern A. Zeeb 
2029af1bba4SBjoern A. Zeeb int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
2039af1bba4SBjoern A. Zeeb 			 struct ieee80211_bss_conf *link_conf,
2049af1bba4SBjoern A. Zeeb 			 u32 changes, bool active)
2059af1bba4SBjoern A. Zeeb {
2069af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
2079af1bba4SBjoern A. Zeeb 	unsigned int link_id = link_conf->link_id;
2089af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
2099af1bba4SBjoern A. Zeeb 	struct iwl_mvm_phy_ctxt *phyctxt;
2109af1bba4SBjoern A. Zeeb 	struct iwl_link_config_cmd cmd = {};
2119af1bba4SBjoern A. Zeeb 	u32 ht_flag, flags = 0, flags_mask = 0;
2129af1bba4SBjoern A. Zeeb 	int ret;
213*a4128aadSBjoern A. Zeeb 	unsigned int cmd_id = WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD);
214*a4128aadSBjoern A. Zeeb 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1);
2159af1bba4SBjoern A. Zeeb 
2169af1bba4SBjoern A. Zeeb 	if (WARN_ON_ONCE(!link_info ||
2179af1bba4SBjoern A. Zeeb 			 link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID))
2189af1bba4SBjoern A. Zeeb 		return -EINVAL;
2199af1bba4SBjoern A. Zeeb 
2209af1bba4SBjoern A. Zeeb 	if (changes & LINK_CONTEXT_MODIFY_ACTIVE) {
2219af1bba4SBjoern A. Zeeb 		/* When activating a link, phy context should be valid;
2229af1bba4SBjoern A. Zeeb 		 * when deactivating a link, it also should be valid since
2239af1bba4SBjoern A. Zeeb 		 * the link was active before. So, do nothing in this case.
2249af1bba4SBjoern A. Zeeb 		 * Since a link is added first with FW_CTXT_INVALID, then we
2259af1bba4SBjoern A. Zeeb 		 * can get here in case it's removed before it was activated.
2269af1bba4SBjoern A. Zeeb 		 */
2279af1bba4SBjoern A. Zeeb 		if (!link_info->phy_ctxt)
2289af1bba4SBjoern A. Zeeb 			return 0;
2299af1bba4SBjoern A. Zeeb 
2309af1bba4SBjoern A. Zeeb 		/* Catch early if driver tries to activate or deactivate a link
2319af1bba4SBjoern A. Zeeb 		 * twice.
2329af1bba4SBjoern A. Zeeb 		 */
2339af1bba4SBjoern A. Zeeb 		WARN_ON_ONCE(active == link_info->active);
2349af1bba4SBjoern A. Zeeb 
2359af1bba4SBjoern A. Zeeb 		/* When deactivating a link session protection should
2369af1bba4SBjoern A. Zeeb 		 * be stopped
2379af1bba4SBjoern A. Zeeb 		 */
2389af1bba4SBjoern A. Zeeb 		if (!active && vif->type == NL80211_IFTYPE_STATION)
2399af1bba4SBjoern A. Zeeb 			iwl_mvm_stop_session_protection(mvm, vif);
2409af1bba4SBjoern A. Zeeb 	}
2419af1bba4SBjoern A. Zeeb 
2429af1bba4SBjoern A. Zeeb 	cmd.link_id = cpu_to_le32(link_info->fw_link_id);
2439af1bba4SBjoern A. Zeeb 
2449af1bba4SBjoern A. Zeeb 	/* The phy_id, link address and listen_lmac can be modified only until
2459af1bba4SBjoern A. Zeeb 	 * the link becomes active, otherwise they will be ignored.
2469af1bba4SBjoern A. Zeeb 	 */
2479af1bba4SBjoern A. Zeeb 	phyctxt = link_info->phy_ctxt;
2489af1bba4SBjoern A. Zeeb 	if (phyctxt)
2499af1bba4SBjoern A. Zeeb 		cmd.phy_id = cpu_to_le32(phyctxt->id);
2509af1bba4SBjoern A. Zeeb 	else
2519af1bba4SBjoern A. Zeeb 		cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
2529af1bba4SBjoern A. Zeeb 	cmd.mac_id = cpu_to_le32(mvmvif->id);
2539af1bba4SBjoern A. Zeeb 
2549af1bba4SBjoern A. Zeeb 	memcpy(cmd.local_link_addr, link_conf->addr, ETH_ALEN);
2559af1bba4SBjoern A. Zeeb 
2569af1bba4SBjoern A. Zeeb 	cmd.active = cpu_to_le32(active);
2579af1bba4SBjoern A. Zeeb 
2589af1bba4SBjoern A. Zeeb 	if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
2599af1bba4SBjoern A. Zeeb 		memcpy(cmd.ibss_bssid_addr, link_conf->bssid, ETH_ALEN);
2609af1bba4SBjoern A. Zeeb 
2619af1bba4SBjoern A. Zeeb 	iwl_mvm_set_fw_basic_rates(mvm, vif, link_conf,
2629af1bba4SBjoern A. Zeeb 				   &cmd.cck_rates, &cmd.ofdm_rates);
2639af1bba4SBjoern A. Zeeb 
2649af1bba4SBjoern A. Zeeb 	cmd.cck_short_preamble = cpu_to_le32(link_conf->use_short_preamble);
2659af1bba4SBjoern A. Zeeb 	cmd.short_slot = cpu_to_le32(link_conf->use_short_slot);
2669af1bba4SBjoern A. Zeeb 
2679af1bba4SBjoern A. Zeeb 	/* The fw does not distinguish between ht and fat */
2689af1bba4SBjoern A. Zeeb 	ht_flag = LINK_PROT_FLG_HT_PROT | LINK_PROT_FLG_FAT_PROT;
2699af1bba4SBjoern A. Zeeb 	iwl_mvm_set_fw_protection_flags(mvm, vif, link_conf,
2709af1bba4SBjoern A. Zeeb 					&cmd.protection_flags,
2719af1bba4SBjoern A. Zeeb 					ht_flag, LINK_PROT_FLG_TGG_PROTECT);
2729af1bba4SBjoern A. Zeeb 
2739af1bba4SBjoern A. Zeeb 	iwl_mvm_set_fw_qos_params(mvm, vif, link_conf, cmd.ac,
2749af1bba4SBjoern A. Zeeb 				  &cmd.qos_flags);
2759af1bba4SBjoern A. Zeeb 
2769af1bba4SBjoern A. Zeeb 
2779af1bba4SBjoern A. Zeeb 	cmd.bi = cpu_to_le32(link_conf->beacon_int);
2789af1bba4SBjoern A. Zeeb 	cmd.dtim_interval = cpu_to_le32(link_conf->beacon_int *
2799af1bba4SBjoern A. Zeeb 					link_conf->dtim_period);
2809af1bba4SBjoern A. Zeeb 
2819af1bba4SBjoern A. Zeeb 	if (!link_conf->he_support || iwlwifi_mod_params.disable_11ax ||
2829af1bba4SBjoern A. Zeeb 	    (vif->type == NL80211_IFTYPE_STATION && !vif->cfg.assoc)) {
2839af1bba4SBjoern A. Zeeb 		changes &= ~LINK_CONTEXT_MODIFY_HE_PARAMS;
2849af1bba4SBjoern A. Zeeb 		goto send_cmd;
2859af1bba4SBjoern A. Zeeb 	}
2869af1bba4SBjoern A. Zeeb 
2879af1bba4SBjoern A. Zeeb 	cmd.htc_trig_based_pkt_ext = link_conf->htc_trig_based_pkt_ext;
2889af1bba4SBjoern A. Zeeb 
2899af1bba4SBjoern A. Zeeb 	if (link_conf->uora_exists) {
2909af1bba4SBjoern A. Zeeb 		cmd.rand_alloc_ecwmin =
2919af1bba4SBjoern A. Zeeb 			link_conf->uora_ocw_range & 0x7;
2929af1bba4SBjoern A. Zeeb 		cmd.rand_alloc_ecwmax =
2939af1bba4SBjoern A. Zeeb 			(link_conf->uora_ocw_range >> 3) & 0x7;
2949af1bba4SBjoern A. Zeeb 	}
2959af1bba4SBjoern A. Zeeb 
2969af1bba4SBjoern A. Zeeb 	/* TODO  how to set ndp_fdbk_buff_th_exp? */
2979af1bba4SBjoern A. Zeeb 
2989af1bba4SBjoern A. Zeeb 	if (iwl_mvm_set_fw_mu_edca_params(mvm, mvmvif->link[link_id],
2999af1bba4SBjoern A. Zeeb 					  &cmd.trig_based_txf[0])) {
3009af1bba4SBjoern A. Zeeb 		flags |= LINK_FLG_MU_EDCA_CW;
3019af1bba4SBjoern A. Zeeb 		flags_mask |= LINK_FLG_MU_EDCA_CW;
3029af1bba4SBjoern A. Zeeb 	}
3039af1bba4SBjoern A. Zeeb 
304*a4128aadSBjoern A. Zeeb 	if (changes & LINK_CONTEXT_MODIFY_EHT_PARAMS) {
305*a4128aadSBjoern A. Zeeb 		struct ieee80211_chanctx_conf *ctx;
306*a4128aadSBjoern A. Zeeb 		struct cfg80211_chan_def *def = NULL;
307*a4128aadSBjoern A. Zeeb 
308*a4128aadSBjoern A. Zeeb 		rcu_read_lock();
309*a4128aadSBjoern A. Zeeb 		ctx = rcu_dereference(link_conf->chanctx_conf);
310*a4128aadSBjoern A. Zeeb 		if (ctx)
311*a4128aadSBjoern A. Zeeb 			def = iwl_mvm_chanctx_def(mvm, ctx);
312*a4128aadSBjoern A. Zeeb 
313*a4128aadSBjoern A. Zeeb 		if (iwlwifi_mod_params.disable_11be ||
314*a4128aadSBjoern A. Zeeb 		    !link_conf->eht_support || !def ||
315*a4128aadSBjoern A. Zeeb 		    iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1) >= 6)
3169af1bba4SBjoern A. Zeeb 			changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS;
317*a4128aadSBjoern A. Zeeb 		else
318*a4128aadSBjoern A. Zeeb 			cmd.puncture_mask = cpu_to_le16(def->punctured);
319*a4128aadSBjoern A. Zeeb 		rcu_read_unlock();
320*a4128aadSBjoern A. Zeeb 	}
3219af1bba4SBjoern A. Zeeb 
3229af1bba4SBjoern A. Zeeb 	cmd.bss_color = link_conf->he_bss_color.color;
3239af1bba4SBjoern A. Zeeb 
3249af1bba4SBjoern A. Zeeb 	if (!link_conf->he_bss_color.enabled) {
3259af1bba4SBjoern A. Zeeb 		flags |= LINK_FLG_BSS_COLOR_DIS;
3269af1bba4SBjoern A. Zeeb 		flags_mask |= LINK_FLG_BSS_COLOR_DIS;
3279af1bba4SBjoern A. Zeeb 	}
3289af1bba4SBjoern A. Zeeb 
3299af1bba4SBjoern A. Zeeb 	cmd.frame_time_rts_th = cpu_to_le16(link_conf->frame_time_rts_th);
3309af1bba4SBjoern A. Zeeb 
3319af1bba4SBjoern A. Zeeb 	/* Block 26-tone RU OFDMA transmissions */
3329af1bba4SBjoern A. Zeeb 	if (link_info->he_ru_2mhz_block) {
3339af1bba4SBjoern A. Zeeb 		flags |= LINK_FLG_RU_2MHZ_BLOCK;
3349af1bba4SBjoern A. Zeeb 		flags_mask |= LINK_FLG_RU_2MHZ_BLOCK;
3359af1bba4SBjoern A. Zeeb 	}
3369af1bba4SBjoern A. Zeeb 
3379af1bba4SBjoern A. Zeeb 	if (link_conf->nontransmitted) {
3389af1bba4SBjoern A. Zeeb 		ether_addr_copy(cmd.ref_bssid_addr,
3399af1bba4SBjoern A. Zeeb 				link_conf->transmitter_bssid);
3409af1bba4SBjoern A. Zeeb 		cmd.bssid_index = link_conf->bssid_index;
3419af1bba4SBjoern A. Zeeb 	}
3429af1bba4SBjoern A. Zeeb 
3439af1bba4SBjoern A. Zeeb send_cmd:
3449af1bba4SBjoern A. Zeeb 	cmd.modify_mask = cpu_to_le32(changes);
3459af1bba4SBjoern A. Zeeb 	cmd.flags = cpu_to_le32(flags);
3469af1bba4SBjoern A. Zeeb 	cmd.flags_mask = cpu_to_le32(flags_mask);
3479af1bba4SBjoern A. Zeeb 	cmd.spec_link_id = link_conf->link_id;
348*a4128aadSBjoern A. Zeeb 	if (cmd_ver < 2)
3499af1bba4SBjoern A. Zeeb 		cmd.listen_lmac = cpu_to_le32(link_info->listen_lmac);
3509af1bba4SBjoern A. Zeeb 
3519af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_MODIFY);
3529af1bba4SBjoern A. Zeeb 	if (!ret && (changes & LINK_CONTEXT_MODIFY_ACTIVE))
3539af1bba4SBjoern A. Zeeb 		link_info->active = active;
3549af1bba4SBjoern A. Zeeb 
3559af1bba4SBjoern A. Zeeb 	return ret;
3569af1bba4SBjoern A. Zeeb }
3579af1bba4SBjoern A. Zeeb 
358*a4128aadSBjoern A. Zeeb int iwl_mvm_unset_link_mapping(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
359*a4128aadSBjoern A. Zeeb 			       struct ieee80211_bss_conf *link_conf)
360*a4128aadSBjoern A. Zeeb {
361*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
362*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_vif_link_info *link_info =
363*a4128aadSBjoern A. Zeeb 		mvmvif->link[link_conf->link_id];
364*a4128aadSBjoern A. Zeeb 
365*a4128aadSBjoern A. Zeeb 	/* mac80211 thought we have the link, but it was never configured */
366*a4128aadSBjoern A. Zeeb 	if (WARN_ON(!link_info ||
367*a4128aadSBjoern A. Zeeb 		    link_info->fw_link_id >=
368*a4128aadSBjoern A. Zeeb 		    ARRAY_SIZE(mvm->link_id_to_link_conf)))
369*a4128aadSBjoern A. Zeeb 		return -EINVAL;
370*a4128aadSBjoern A. Zeeb 
371*a4128aadSBjoern A. Zeeb 	RCU_INIT_POINTER(mvm->link_id_to_link_conf[link_info->fw_link_id],
372*a4128aadSBjoern A. Zeeb 			 NULL);
373*a4128aadSBjoern A. Zeeb 	return 0;
374*a4128aadSBjoern A. Zeeb }
375*a4128aadSBjoern A. Zeeb 
3769af1bba4SBjoern A. Zeeb int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
3779af1bba4SBjoern A. Zeeb 			struct ieee80211_bss_conf *link_conf)
3789af1bba4SBjoern A. Zeeb {
3799af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
3809af1bba4SBjoern A. Zeeb 	unsigned int link_id = link_conf->link_id;
3819af1bba4SBjoern A. Zeeb 	struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
3829af1bba4SBjoern A. Zeeb 	struct iwl_link_config_cmd cmd = {};
3839af1bba4SBjoern A. Zeeb 	int ret;
3849af1bba4SBjoern A. Zeeb 
385*a4128aadSBjoern A. Zeeb 	ret = iwl_mvm_unset_link_mapping(mvm, vif, link_conf);
386*a4128aadSBjoern A. Zeeb 	if (ret)
387*a4128aadSBjoern A. Zeeb 		return 0;
3889af1bba4SBjoern A. Zeeb 
3899af1bba4SBjoern A. Zeeb 	cmd.link_id = cpu_to_le32(link_info->fw_link_id);
3909af1bba4SBjoern A. Zeeb 	link_info->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID;
3919af1bba4SBjoern A. Zeeb 	cmd.spec_link_id = link_conf->link_id;
392*a4128aadSBjoern A. Zeeb 	cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
3939af1bba4SBjoern A. Zeeb 
3949af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_REMOVE);
3959af1bba4SBjoern A. Zeeb 
3969af1bba4SBjoern A. Zeeb 	if (!ret)
3979af1bba4SBjoern A. Zeeb 		if (iwl_mvm_sf_update(mvm, vif, true))
3989af1bba4SBjoern A. Zeeb 			IWL_ERR(mvm, "Failed to update SF state\n");
3999af1bba4SBjoern A. Zeeb 
4009af1bba4SBjoern A. Zeeb 	return ret;
4019af1bba4SBjoern A. Zeeb }
4029af1bba4SBjoern A. Zeeb 
4039af1bba4SBjoern A. Zeeb /* link should be deactivated before removal, so in most cases we need to
4049af1bba4SBjoern A. Zeeb  * perform these two operations together
4059af1bba4SBjoern A. Zeeb  */
4069af1bba4SBjoern A. Zeeb int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
4079af1bba4SBjoern A. Zeeb 			 struct ieee80211_bss_conf *link_conf)
4089af1bba4SBjoern A. Zeeb {
4099af1bba4SBjoern A. Zeeb 	int ret;
4109af1bba4SBjoern A. Zeeb 
4119af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_link_changed(mvm, vif, link_conf,
4129af1bba4SBjoern A. Zeeb 				   LINK_CONTEXT_MODIFY_ACTIVE, false);
4139af1bba4SBjoern A. Zeeb 	if (ret)
4149af1bba4SBjoern A. Zeeb 		return ret;
4159af1bba4SBjoern A. Zeeb 
4169af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_remove_link(mvm, vif, link_conf);
4179af1bba4SBjoern A. Zeeb 	if (ret)
4189af1bba4SBjoern A. Zeeb 		return ret;
4199af1bba4SBjoern A. Zeeb 
4209af1bba4SBjoern A. Zeeb 	return ret;
4219af1bba4SBjoern A. Zeeb }
422*a4128aadSBjoern A. Zeeb 
423*a4128aadSBjoern A. Zeeb struct iwl_mvm_rssi_to_grade {
424*a4128aadSBjoern A. Zeeb 	s8 rssi[2];
425*a4128aadSBjoern A. Zeeb 	u16 grade;
426*a4128aadSBjoern A. Zeeb };
427*a4128aadSBjoern A. Zeeb 
428*a4128aadSBjoern A. Zeeb #define RSSI_TO_GRADE_LINE(_lb, _hb_uhb, _grade) \
429*a4128aadSBjoern A. Zeeb 	{ \
430*a4128aadSBjoern A. Zeeb 		.rssi = {_lb, _hb_uhb}, \
431*a4128aadSBjoern A. Zeeb 		.grade = _grade \
432*a4128aadSBjoern A. Zeeb 	}
433*a4128aadSBjoern A. Zeeb 
434*a4128aadSBjoern A. Zeeb /*
435*a4128aadSBjoern A. Zeeb  * This array must be sorted by increasing RSSI for proper functionality.
436*a4128aadSBjoern A. Zeeb  * The grades are actually estimated throughput, represented as fixed-point
437*a4128aadSBjoern A. Zeeb  * with a scale factor of 1/10.
438*a4128aadSBjoern A. Zeeb  */
439*a4128aadSBjoern A. Zeeb static const struct iwl_mvm_rssi_to_grade rssi_to_grade_map[] = {
440*a4128aadSBjoern A. Zeeb 	RSSI_TO_GRADE_LINE(-85, -89, 177),
441*a4128aadSBjoern A. Zeeb 	RSSI_TO_GRADE_LINE(-83, -86, 344),
442*a4128aadSBjoern A. Zeeb 	RSSI_TO_GRADE_LINE(-82, -85, 516),
443*a4128aadSBjoern A. Zeeb 	RSSI_TO_GRADE_LINE(-80, -83, 688),
444*a4128aadSBjoern A. Zeeb 	RSSI_TO_GRADE_LINE(-77, -79, 1032),
445*a4128aadSBjoern A. Zeeb 	RSSI_TO_GRADE_LINE(-73, -76, 1376),
446*a4128aadSBjoern A. Zeeb 	RSSI_TO_GRADE_LINE(-70, -74, 1548),
447*a4128aadSBjoern A. Zeeb 	RSSI_TO_GRADE_LINE(-69, -72, 1750),
448*a4128aadSBjoern A. Zeeb 	RSSI_TO_GRADE_LINE(-65, -68, 2064),
449*a4128aadSBjoern A. Zeeb 	RSSI_TO_GRADE_LINE(-61, -66, 2294),
450*a4128aadSBjoern A. Zeeb 	RSSI_TO_GRADE_LINE(-58, -61, 2580),
451*a4128aadSBjoern A. Zeeb 	RSSI_TO_GRADE_LINE(-55, -58, 2868),
452*a4128aadSBjoern A. Zeeb 	RSSI_TO_GRADE_LINE(-46, -55, 3098),
453*a4128aadSBjoern A. Zeeb 	RSSI_TO_GRADE_LINE(-43, -54, 3442)
454*a4128aadSBjoern A. Zeeb };
455*a4128aadSBjoern A. Zeeb 
456*a4128aadSBjoern A. Zeeb #define MAX_GRADE (rssi_to_grade_map[ARRAY_SIZE(rssi_to_grade_map) - 1].grade)
457*a4128aadSBjoern A. Zeeb 
458*a4128aadSBjoern A. Zeeb #define DEFAULT_CHAN_LOAD_LB	30
459*a4128aadSBjoern A. Zeeb #define DEFAULT_CHAN_LOAD_HB	15
460*a4128aadSBjoern A. Zeeb #define DEFAULT_CHAN_LOAD_UHB	0
461*a4128aadSBjoern A. Zeeb 
462*a4128aadSBjoern A. Zeeb /* Factors calculation is done with fixed-point with a scaling factor of 1/256 */
463*a4128aadSBjoern A. Zeeb #define SCALE_FACTOR 256
464*a4128aadSBjoern A. Zeeb 
465*a4128aadSBjoern A. Zeeb /* Convert a percentage from [0,100] to [0,255] */
466*a4128aadSBjoern A. Zeeb #define NORMALIZE_PERCENT_TO_255(percentage) ((percentage) * SCALE_FACTOR / 100)
467*a4128aadSBjoern A. Zeeb 
468*a4128aadSBjoern A. Zeeb static unsigned int
469*a4128aadSBjoern A. Zeeb iwl_mvm_get_puncturing_factor(const struct ieee80211_bss_conf *link_conf)
470*a4128aadSBjoern A. Zeeb {
471*a4128aadSBjoern A. Zeeb 	enum nl80211_chan_width chan_width =
472*a4128aadSBjoern A. Zeeb 		link_conf->chanreq.oper.width;
473*a4128aadSBjoern A. Zeeb 	int mhz = nl80211_chan_width_to_mhz(chan_width);
474*a4128aadSBjoern A. Zeeb 	unsigned int n_subchannels, n_punctured, puncturing_penalty;
475*a4128aadSBjoern A. Zeeb 
476*a4128aadSBjoern A. Zeeb 	if (WARN_ONCE(mhz < 20 || mhz > 320,
477*a4128aadSBjoern A. Zeeb 		      "Invalid channel width : (%d)\n", mhz))
478*a4128aadSBjoern A. Zeeb 		return SCALE_FACTOR;
479*a4128aadSBjoern A. Zeeb 
480*a4128aadSBjoern A. Zeeb 	/* No puncturing, no penalty */
481*a4128aadSBjoern A. Zeeb 	if (mhz < 80)
482*a4128aadSBjoern A. Zeeb 		return SCALE_FACTOR;
483*a4128aadSBjoern A. Zeeb 
484*a4128aadSBjoern A. Zeeb 	/* total number of subchannels */
485*a4128aadSBjoern A. Zeeb 	n_subchannels = mhz / 20;
486*a4128aadSBjoern A. Zeeb 	/* how many of these are punctured */
487*a4128aadSBjoern A. Zeeb 	n_punctured = hweight16(link_conf->chanreq.oper.punctured);
488*a4128aadSBjoern A. Zeeb 
489*a4128aadSBjoern A. Zeeb 	puncturing_penalty = n_punctured * SCALE_FACTOR / n_subchannels;
490*a4128aadSBjoern A. Zeeb 	return SCALE_FACTOR - puncturing_penalty;
491*a4128aadSBjoern A. Zeeb }
492*a4128aadSBjoern A. Zeeb 
493*a4128aadSBjoern A. Zeeb static unsigned int
494*a4128aadSBjoern A. Zeeb iwl_mvm_get_chan_load(struct ieee80211_bss_conf *link_conf)
495*a4128aadSBjoern A. Zeeb {
496*a4128aadSBjoern A. Zeeb 	struct ieee80211_vif *vif = link_conf->vif;
497*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_vif_link_info *mvm_link =
498*a4128aadSBjoern A. Zeeb 		iwl_mvm_vif_from_mac80211(link_conf->vif)->link[link_conf->link_id];
499*a4128aadSBjoern A. Zeeb 	const struct element *bss_load_elem;
500*a4128aadSBjoern A. Zeeb 	const struct ieee80211_bss_load_elem *bss_load;
501*a4128aadSBjoern A. Zeeb 	enum nl80211_band band = link_conf->chanreq.oper.chan->band;
502*a4128aadSBjoern A. Zeeb 	const struct cfg80211_bss_ies *ies;
503*a4128aadSBjoern A. Zeeb 	unsigned int chan_load;
504*a4128aadSBjoern A. Zeeb 	u32 chan_load_by_us;
505*a4128aadSBjoern A. Zeeb 
506*a4128aadSBjoern A. Zeeb 	rcu_read_lock();
507*a4128aadSBjoern A. Zeeb 	if (ieee80211_vif_link_active(vif, link_conf->link_id))
508*a4128aadSBjoern A. Zeeb 		ies = rcu_dereference(link_conf->bss->beacon_ies);
509*a4128aadSBjoern A. Zeeb 	else
510*a4128aadSBjoern A. Zeeb 		ies = rcu_dereference(link_conf->bss->ies);
511*a4128aadSBjoern A. Zeeb 
512*a4128aadSBjoern A. Zeeb 	if (ies)
513*a4128aadSBjoern A. Zeeb 		bss_load_elem = cfg80211_find_elem(WLAN_EID_QBSS_LOAD,
514*a4128aadSBjoern A. Zeeb 						   ies->data, ies->len);
515*a4128aadSBjoern A. Zeeb 	else
516*a4128aadSBjoern A. Zeeb 		bss_load_elem = NULL;
517*a4128aadSBjoern A. Zeeb 
518*a4128aadSBjoern A. Zeeb 	/* If there isn't BSS Load element, take the defaults */
519*a4128aadSBjoern A. Zeeb 	if (!bss_load_elem ||
520*a4128aadSBjoern A. Zeeb 	    bss_load_elem->datalen != sizeof(*bss_load)) {
521*a4128aadSBjoern A. Zeeb 		rcu_read_unlock();
522*a4128aadSBjoern A. Zeeb 		switch (band) {
523*a4128aadSBjoern A. Zeeb 		case NL80211_BAND_2GHZ:
524*a4128aadSBjoern A. Zeeb 			chan_load = DEFAULT_CHAN_LOAD_LB;
525*a4128aadSBjoern A. Zeeb 			break;
526*a4128aadSBjoern A. Zeeb 		case NL80211_BAND_5GHZ:
527*a4128aadSBjoern A. Zeeb 			chan_load = DEFAULT_CHAN_LOAD_HB;
528*a4128aadSBjoern A. Zeeb 			break;
529*a4128aadSBjoern A. Zeeb 		case NL80211_BAND_6GHZ:
530*a4128aadSBjoern A. Zeeb 			chan_load = DEFAULT_CHAN_LOAD_UHB;
531*a4128aadSBjoern A. Zeeb 			break;
532*a4128aadSBjoern A. Zeeb 		default:
533*a4128aadSBjoern A. Zeeb 			chan_load = 0;
534*a4128aadSBjoern A. Zeeb 			break;
535*a4128aadSBjoern A. Zeeb 		}
536*a4128aadSBjoern A. Zeeb 		/* The defaults are given in percentage */
537*a4128aadSBjoern A. Zeeb 		return NORMALIZE_PERCENT_TO_255(chan_load);
538*a4128aadSBjoern A. Zeeb 	}
539*a4128aadSBjoern A. Zeeb 
540*a4128aadSBjoern A. Zeeb 	bss_load = (const void *)bss_load_elem->data;
541*a4128aadSBjoern A. Zeeb 	/* Channel util is in range 0-255 */
542*a4128aadSBjoern A. Zeeb 	chan_load = bss_load->channel_util;
543*a4128aadSBjoern A. Zeeb 	rcu_read_unlock();
544*a4128aadSBjoern A. Zeeb 
545*a4128aadSBjoern A. Zeeb 	if (!mvm_link || !mvm_link->active)
546*a4128aadSBjoern A. Zeeb 		return chan_load;
547*a4128aadSBjoern A. Zeeb 
548*a4128aadSBjoern A. Zeeb 	if (WARN_ONCE(!mvm_link->phy_ctxt,
549*a4128aadSBjoern A. Zeeb 		      "Active link (%u) without phy ctxt assigned!\n",
550*a4128aadSBjoern A. Zeeb 		      link_conf->link_id))
551*a4128aadSBjoern A. Zeeb 		return chan_load;
552*a4128aadSBjoern A. Zeeb 
553*a4128aadSBjoern A. Zeeb 	/* channel load by us is given in percentage */
554*a4128aadSBjoern A. Zeeb 	chan_load_by_us =
555*a4128aadSBjoern A. Zeeb 		NORMALIZE_PERCENT_TO_255(mvm_link->phy_ctxt->channel_load_by_us);
556*a4128aadSBjoern A. Zeeb 
557*a4128aadSBjoern A. Zeeb 	/* Use only values that firmware sends that can possibly be valid */
558*a4128aadSBjoern A. Zeeb 	if (chan_load_by_us <= chan_load)
559*a4128aadSBjoern A. Zeeb 		chan_load -= chan_load_by_us;
560*a4128aadSBjoern A. Zeeb 
561*a4128aadSBjoern A. Zeeb 	return chan_load;
562*a4128aadSBjoern A. Zeeb }
563*a4128aadSBjoern A. Zeeb 
564*a4128aadSBjoern A. Zeeb static unsigned int
565*a4128aadSBjoern A. Zeeb iwl_mvm_get_chan_load_factor(struct ieee80211_bss_conf *link_conf)
566*a4128aadSBjoern A. Zeeb {
567*a4128aadSBjoern A. Zeeb 	return SCALE_FACTOR - iwl_mvm_get_chan_load(link_conf);
568*a4128aadSBjoern A. Zeeb }
569*a4128aadSBjoern A. Zeeb 
570*a4128aadSBjoern A. Zeeb /* This function calculates the grade of a link. Returns 0 in error case */
571*a4128aadSBjoern A. Zeeb VISIBLE_IF_IWLWIFI_KUNIT
572*a4128aadSBjoern A. Zeeb unsigned int iwl_mvm_get_link_grade(struct ieee80211_bss_conf *link_conf)
573*a4128aadSBjoern A. Zeeb {
574*a4128aadSBjoern A. Zeeb 	enum nl80211_band band;
575*a4128aadSBjoern A. Zeeb 	int i, rssi_idx;
576*a4128aadSBjoern A. Zeeb 	s32 link_rssi;
577*a4128aadSBjoern A. Zeeb 	unsigned int grade = MAX_GRADE;
578*a4128aadSBjoern A. Zeeb 
579*a4128aadSBjoern A. Zeeb 	if (WARN_ON_ONCE(!link_conf))
580*a4128aadSBjoern A. Zeeb 		return 0;
581*a4128aadSBjoern A. Zeeb 
582*a4128aadSBjoern A. Zeeb 	band = link_conf->chanreq.oper.chan->band;
583*a4128aadSBjoern A. Zeeb 	if (WARN_ONCE(band != NL80211_BAND_2GHZ &&
584*a4128aadSBjoern A. Zeeb 		      band != NL80211_BAND_5GHZ &&
585*a4128aadSBjoern A. Zeeb 		      band != NL80211_BAND_6GHZ,
586*a4128aadSBjoern A. Zeeb 		      "Invalid band (%u)\n", band))
587*a4128aadSBjoern A. Zeeb 		return 0;
588*a4128aadSBjoern A. Zeeb 
589*a4128aadSBjoern A. Zeeb 	link_rssi = MBM_TO_DBM(link_conf->bss->signal);
590*a4128aadSBjoern A. Zeeb 	/*
591*a4128aadSBjoern A. Zeeb 	 * For 6 GHz the RSSI of the beacons is lower than
592*a4128aadSBjoern A. Zeeb 	 * the RSSI of the data.
593*a4128aadSBjoern A. Zeeb 	 */
594*a4128aadSBjoern A. Zeeb 	if (band == NL80211_BAND_6GHZ)
595*a4128aadSBjoern A. Zeeb 		link_rssi += 4;
596*a4128aadSBjoern A. Zeeb 
597*a4128aadSBjoern A. Zeeb 	rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1;
598*a4128aadSBjoern A. Zeeb 
599*a4128aadSBjoern A. Zeeb 	/* No valid RSSI - take the lowest grade */
600*a4128aadSBjoern A. Zeeb 	if (!link_rssi)
601*a4128aadSBjoern A. Zeeb 		link_rssi = rssi_to_grade_map[0].rssi[rssi_idx];
602*a4128aadSBjoern A. Zeeb 
603*a4128aadSBjoern A. Zeeb 	/* Get grade based on RSSI */
604*a4128aadSBjoern A. Zeeb 	for (i = 0; i < ARRAY_SIZE(rssi_to_grade_map); i++) {
605*a4128aadSBjoern A. Zeeb 		const struct iwl_mvm_rssi_to_grade *line =
606*a4128aadSBjoern A. Zeeb 			&rssi_to_grade_map[i];
607*a4128aadSBjoern A. Zeeb 
608*a4128aadSBjoern A. Zeeb 		if (link_rssi > line->rssi[rssi_idx])
609*a4128aadSBjoern A. Zeeb 			continue;
610*a4128aadSBjoern A. Zeeb 		grade = line->grade;
611*a4128aadSBjoern A. Zeeb 		break;
612*a4128aadSBjoern A. Zeeb 	}
613*a4128aadSBjoern A. Zeeb 
614*a4128aadSBjoern A. Zeeb 	/* apply the channel load and puncturing factors */
615*a4128aadSBjoern A. Zeeb 	grade = grade * iwl_mvm_get_chan_load_factor(link_conf) / SCALE_FACTOR;
616*a4128aadSBjoern A. Zeeb 	grade = grade * iwl_mvm_get_puncturing_factor(link_conf) / SCALE_FACTOR;
617*a4128aadSBjoern A. Zeeb 	return grade;
618*a4128aadSBjoern A. Zeeb }
619*a4128aadSBjoern A. Zeeb EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_get_link_grade);
620*a4128aadSBjoern A. Zeeb 
621*a4128aadSBjoern A. Zeeb static
622*a4128aadSBjoern A. Zeeb u8 iwl_mvm_set_link_selection_data(struct ieee80211_vif *vif,
623*a4128aadSBjoern A. Zeeb 				   struct iwl_mvm_link_sel_data *data,
624*a4128aadSBjoern A. Zeeb 				   unsigned long usable_links,
625*a4128aadSBjoern A. Zeeb 				   u8 *best_link_idx)
626*a4128aadSBjoern A. Zeeb {
627*a4128aadSBjoern A. Zeeb 	u8 n_data = 0;
628*a4128aadSBjoern A. Zeeb 	u16 max_grade = 0;
629*a4128aadSBjoern A. Zeeb 	unsigned long link_id;
630*a4128aadSBjoern A. Zeeb 
631*a4128aadSBjoern A. Zeeb 	/* TODO: don't select links that weren't discovered in the last scan */
632*a4128aadSBjoern A. Zeeb 	for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
633*a4128aadSBjoern A. Zeeb 		struct ieee80211_bss_conf *link_conf =
634*a4128aadSBjoern A. Zeeb 			link_conf_dereference_protected(vif, link_id);
635*a4128aadSBjoern A. Zeeb 
636*a4128aadSBjoern A. Zeeb 		if (WARN_ON_ONCE(!link_conf))
637*a4128aadSBjoern A. Zeeb 			continue;
638*a4128aadSBjoern A. Zeeb 
639*a4128aadSBjoern A. Zeeb 		data[n_data].link_id = link_id;
640*a4128aadSBjoern A. Zeeb 		data[n_data].chandef = &link_conf->chanreq.oper;
641*a4128aadSBjoern A. Zeeb 		data[n_data].signal = link_conf->bss->signal / 100;
642*a4128aadSBjoern A. Zeeb 		data[n_data].grade = iwl_mvm_get_link_grade(link_conf);
643*a4128aadSBjoern A. Zeeb 
644*a4128aadSBjoern A. Zeeb 		if (data[n_data].grade > max_grade) {
645*a4128aadSBjoern A. Zeeb 			max_grade = data[n_data].grade;
646*a4128aadSBjoern A. Zeeb 			*best_link_idx = n_data;
647*a4128aadSBjoern A. Zeeb 		}
648*a4128aadSBjoern A. Zeeb 		n_data++;
649*a4128aadSBjoern A. Zeeb 	}
650*a4128aadSBjoern A. Zeeb 
651*a4128aadSBjoern A. Zeeb 	return n_data;
652*a4128aadSBjoern A. Zeeb }
653*a4128aadSBjoern A. Zeeb 
654*a4128aadSBjoern A. Zeeb struct iwl_mvm_bw_to_rssi_threshs {
655*a4128aadSBjoern A. Zeeb 	s8 low;
656*a4128aadSBjoern A. Zeeb 	s8 high;
657*a4128aadSBjoern A. Zeeb };
658*a4128aadSBjoern A. Zeeb 
659*a4128aadSBjoern A. Zeeb #define BW_TO_RSSI_THRESHOLDS(_bw)				\
660*a4128aadSBjoern A. Zeeb 	[IWL_PHY_CHANNEL_MODE ## _bw] = {			\
661*a4128aadSBjoern A. Zeeb 		.low = IWL_MVM_LOW_RSSI_THRESH_##_bw##MHZ,	\
662*a4128aadSBjoern A. Zeeb 		.high = IWL_MVM_HIGH_RSSI_THRESH_##_bw##MHZ	\
663*a4128aadSBjoern A. Zeeb 	}
664*a4128aadSBjoern A. Zeeb 
665*a4128aadSBjoern A. Zeeb s8 iwl_mvm_get_esr_rssi_thresh(struct iwl_mvm *mvm,
666*a4128aadSBjoern A. Zeeb 			       const struct cfg80211_chan_def *chandef,
667*a4128aadSBjoern A. Zeeb 			       bool low)
668*a4128aadSBjoern A. Zeeb {
669*a4128aadSBjoern A. Zeeb 	const struct iwl_mvm_bw_to_rssi_threshs bw_to_rssi_threshs_map[] = {
670*a4128aadSBjoern A. Zeeb 		BW_TO_RSSI_THRESHOLDS(20),
671*a4128aadSBjoern A. Zeeb 		BW_TO_RSSI_THRESHOLDS(40),
672*a4128aadSBjoern A. Zeeb 		BW_TO_RSSI_THRESHOLDS(80),
673*a4128aadSBjoern A. Zeeb 		BW_TO_RSSI_THRESHOLDS(160)
674*a4128aadSBjoern A. Zeeb 		/* 320 MHz has the same thresholds as 20 MHz */
675*a4128aadSBjoern A. Zeeb 	};
676*a4128aadSBjoern A. Zeeb 	const struct iwl_mvm_bw_to_rssi_threshs *threshs;
677*a4128aadSBjoern A. Zeeb 	u8 chan_width = iwl_mvm_get_channel_width(chandef);
678*a4128aadSBjoern A. Zeeb 
679*a4128aadSBjoern A. Zeeb 	if (WARN_ON(chandef->chan->band != NL80211_BAND_2GHZ &&
680*a4128aadSBjoern A. Zeeb 		    chandef->chan->band != NL80211_BAND_5GHZ &&
681*a4128aadSBjoern A. Zeeb 		    chandef->chan->band != NL80211_BAND_6GHZ))
682*a4128aadSBjoern A. Zeeb 		return S8_MAX;
683*a4128aadSBjoern A. Zeeb 
684*a4128aadSBjoern A. Zeeb 	/* 6 GHz will always use 20 MHz thresholds, regardless of the BW */
685*a4128aadSBjoern A. Zeeb 	if (chan_width == IWL_PHY_CHANNEL_MODE320)
686*a4128aadSBjoern A. Zeeb 		chan_width = IWL_PHY_CHANNEL_MODE20;
687*a4128aadSBjoern A. Zeeb 
688*a4128aadSBjoern A. Zeeb 	threshs = &bw_to_rssi_threshs_map[chan_width];
689*a4128aadSBjoern A. Zeeb 
690*a4128aadSBjoern A. Zeeb 	return low ? threshs->low : threshs->high;
691*a4128aadSBjoern A. Zeeb }
692*a4128aadSBjoern A. Zeeb 
693*a4128aadSBjoern A. Zeeb static u32
694*a4128aadSBjoern A. Zeeb iwl_mvm_esr_disallowed_with_link(struct iwl_mvm *mvm,
695*a4128aadSBjoern A. Zeeb 				 struct ieee80211_vif *vif,
696*a4128aadSBjoern A. Zeeb 				 const struct iwl_mvm_link_sel_data *link,
697*a4128aadSBjoern A. Zeeb 				 bool primary)
698*a4128aadSBjoern A. Zeeb {
699*a4128aadSBjoern A. Zeeb 	struct wiphy *wiphy = mvm->hw->wiphy;
700*a4128aadSBjoern A. Zeeb 	struct ieee80211_bss_conf *conf;
701*a4128aadSBjoern A. Zeeb 	enum iwl_mvm_esr_state ret = 0;
702*a4128aadSBjoern A. Zeeb 	s8 thresh;
703*a4128aadSBjoern A. Zeeb 
704*a4128aadSBjoern A. Zeeb 	conf = wiphy_dereference(wiphy, vif->link_conf[link->link_id]);
705*a4128aadSBjoern A. Zeeb 	if (WARN_ON_ONCE(!conf))
706*a4128aadSBjoern A. Zeeb 		return false;
707*a4128aadSBjoern A. Zeeb 
708*a4128aadSBjoern A. Zeeb 	/* BT Coex effects eSR mode only if one of the links is on LB */
709*a4128aadSBjoern A. Zeeb 	if (link->chandef->chan->band == NL80211_BAND_2GHZ &&
710*a4128aadSBjoern A. Zeeb 	    (!iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, link->signal,
711*a4128aadSBjoern A. Zeeb 						 primary)))
712*a4128aadSBjoern A. Zeeb 		ret |= IWL_MVM_ESR_EXIT_COEX;
713*a4128aadSBjoern A. Zeeb 
714*a4128aadSBjoern A. Zeeb 	thresh = iwl_mvm_get_esr_rssi_thresh(mvm, link->chandef,
715*a4128aadSBjoern A. Zeeb 					     false);
716*a4128aadSBjoern A. Zeeb 
717*a4128aadSBjoern A. Zeeb 	if (link->signal < thresh)
718*a4128aadSBjoern A. Zeeb 		ret |= IWL_MVM_ESR_EXIT_LOW_RSSI;
719*a4128aadSBjoern A. Zeeb 
720*a4128aadSBjoern A. Zeeb 	if (conf->csa_active)
721*a4128aadSBjoern A. Zeeb 		ret |= IWL_MVM_ESR_EXIT_CSA;
722*a4128aadSBjoern A. Zeeb 
723*a4128aadSBjoern A. Zeeb 	if (ret) {
724*a4128aadSBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm,
725*a4128aadSBjoern A. Zeeb 			       "Link %d is not allowed for esr\n",
726*a4128aadSBjoern A. Zeeb 			       link->link_id);
727*a4128aadSBjoern A. Zeeb 		iwl_mvm_print_esr_state(mvm, ret);
728*a4128aadSBjoern A. Zeeb 	}
729*a4128aadSBjoern A. Zeeb 	return ret;
730*a4128aadSBjoern A. Zeeb }
731*a4128aadSBjoern A. Zeeb 
732*a4128aadSBjoern A. Zeeb VISIBLE_IF_IWLWIFI_KUNIT
733*a4128aadSBjoern A. Zeeb bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif,
734*a4128aadSBjoern A. Zeeb 				 const struct iwl_mvm_link_sel_data *a,
735*a4128aadSBjoern A. Zeeb 				 const struct iwl_mvm_link_sel_data *b)
736*a4128aadSBjoern A. Zeeb {
737*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
738*a4128aadSBjoern A. Zeeb 	struct iwl_mvm *mvm = mvmvif->mvm;
739*a4128aadSBjoern A. Zeeb 	enum iwl_mvm_esr_state ret = 0;
740*a4128aadSBjoern A. Zeeb 
741*a4128aadSBjoern A. Zeeb 	/* Per-link considerations */
742*a4128aadSBjoern A. Zeeb 	if (iwl_mvm_esr_disallowed_with_link(mvm, vif, a, true) ||
743*a4128aadSBjoern A. Zeeb 	    iwl_mvm_esr_disallowed_with_link(mvm, vif, b, false))
744*a4128aadSBjoern A. Zeeb 		return false;
745*a4128aadSBjoern A. Zeeb 
746*a4128aadSBjoern A. Zeeb 	if (a->chandef->width != b->chandef->width ||
747*a4128aadSBjoern A. Zeeb 	    !(a->chandef->chan->band == NL80211_BAND_6GHZ &&
748*a4128aadSBjoern A. Zeeb 	      b->chandef->chan->band == NL80211_BAND_5GHZ))
749*a4128aadSBjoern A. Zeeb 		ret |= IWL_MVM_ESR_EXIT_BANDWIDTH;
750*a4128aadSBjoern A. Zeeb 
751*a4128aadSBjoern A. Zeeb 	if (ret) {
752*a4128aadSBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm,
753*a4128aadSBjoern A. Zeeb 			       "Links %d and %d are not a valid pair for EMLSR\n",
754*a4128aadSBjoern A. Zeeb 			       a->link_id, b->link_id);
755*a4128aadSBjoern A. Zeeb 		iwl_mvm_print_esr_state(mvm, ret);
756*a4128aadSBjoern A. Zeeb 		return false;
757*a4128aadSBjoern A. Zeeb 	}
758*a4128aadSBjoern A. Zeeb 
759*a4128aadSBjoern A. Zeeb 	return true;
760*a4128aadSBjoern A. Zeeb 
761*a4128aadSBjoern A. Zeeb }
762*a4128aadSBjoern A. Zeeb EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_mld_valid_link_pair);
763*a4128aadSBjoern A. Zeeb 
764*a4128aadSBjoern A. Zeeb /*
765*a4128aadSBjoern A. Zeeb  * Returns the combined eSR grade of two given links.
766*a4128aadSBjoern A. Zeeb  * Returns 0 if eSR is not allowed with these 2 links.
767*a4128aadSBjoern A. Zeeb  */
768*a4128aadSBjoern A. Zeeb static
769*a4128aadSBjoern A. Zeeb unsigned int iwl_mvm_get_esr_grade(struct ieee80211_vif *vif,
770*a4128aadSBjoern A. Zeeb 				   const struct iwl_mvm_link_sel_data *a,
771*a4128aadSBjoern A. Zeeb 				   const struct iwl_mvm_link_sel_data *b,
772*a4128aadSBjoern A. Zeeb 				   u8 *primary_id)
773*a4128aadSBjoern A. Zeeb {
774*a4128aadSBjoern A. Zeeb 	struct ieee80211_bss_conf *primary_conf;
775*a4128aadSBjoern A. Zeeb 	struct wiphy *wiphy = ieee80211_vif_to_wdev(vif)->wiphy;
776*a4128aadSBjoern A. Zeeb 	unsigned int primary_load;
777*a4128aadSBjoern A. Zeeb 
778*a4128aadSBjoern A. Zeeb 	lockdep_assert_wiphy(wiphy);
779*a4128aadSBjoern A. Zeeb 
780*a4128aadSBjoern A. Zeeb 	/* a is always primary, b is always secondary */
781*a4128aadSBjoern A. Zeeb 	if (b->grade > a->grade)
782*a4128aadSBjoern A. Zeeb 		swap(a, b);
783*a4128aadSBjoern A. Zeeb 
784*a4128aadSBjoern A. Zeeb 	*primary_id = a->link_id;
785*a4128aadSBjoern A. Zeeb 
786*a4128aadSBjoern A. Zeeb 	if (!iwl_mvm_mld_valid_link_pair(vif, a, b))
787*a4128aadSBjoern A. Zeeb 		return 0;
788*a4128aadSBjoern A. Zeeb 
789*a4128aadSBjoern A. Zeeb 	primary_conf = wiphy_dereference(wiphy, vif->link_conf[*primary_id]);
790*a4128aadSBjoern A. Zeeb 
791*a4128aadSBjoern A. Zeeb 	if (WARN_ON_ONCE(!primary_conf))
792*a4128aadSBjoern A. Zeeb 		return 0;
793*a4128aadSBjoern A. Zeeb 
794*a4128aadSBjoern A. Zeeb 	primary_load = iwl_mvm_get_chan_load(primary_conf);
795*a4128aadSBjoern A. Zeeb 
796*a4128aadSBjoern A. Zeeb 	return a->grade +
797*a4128aadSBjoern A. Zeeb 		((b->grade * primary_load) / SCALE_FACTOR);
798*a4128aadSBjoern A. Zeeb }
799*a4128aadSBjoern A. Zeeb 
800*a4128aadSBjoern A. Zeeb void iwl_mvm_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
801*a4128aadSBjoern A. Zeeb {
802*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS];
803*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_link_sel_data *best_link;
804*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
805*a4128aadSBjoern A. Zeeb 	u32 max_active_links = iwl_mvm_max_active_links(mvm, vif);
806*a4128aadSBjoern A. Zeeb 	u16 usable_links = ieee80211_vif_usable_links(vif);
807*a4128aadSBjoern A. Zeeb 	u8 best, primary_link, best_in_pair, n_data;
808*a4128aadSBjoern A. Zeeb 	u16 max_esr_grade = 0, new_active_links;
809*a4128aadSBjoern A. Zeeb 
810*a4128aadSBjoern A. Zeeb 	lockdep_assert_wiphy(mvm->hw->wiphy);
811*a4128aadSBjoern A. Zeeb 
812*a4128aadSBjoern A. Zeeb 	if (!mvmvif->authorized || !ieee80211_vif_is_mld(vif))
813*a4128aadSBjoern A. Zeeb 		return;
814*a4128aadSBjoern A. Zeeb 
815*a4128aadSBjoern A. Zeeb 	if (!IWL_MVM_AUTO_EML_ENABLE)
816*a4128aadSBjoern A. Zeeb 		return;
817*a4128aadSBjoern A. Zeeb 
818*a4128aadSBjoern A. Zeeb 	/* The logic below is a simple version that doesn't suit more than 2
819*a4128aadSBjoern A. Zeeb 	 * links
820*a4128aadSBjoern A. Zeeb 	 */
821*a4128aadSBjoern A. Zeeb 	WARN_ON_ONCE(max_active_links > 2);
822*a4128aadSBjoern A. Zeeb 
823*a4128aadSBjoern A. Zeeb 	n_data = iwl_mvm_set_link_selection_data(vif, data, usable_links,
824*a4128aadSBjoern A. Zeeb 						 &best);
825*a4128aadSBjoern A. Zeeb 
826*a4128aadSBjoern A. Zeeb 	if (WARN(!n_data, "Couldn't find a valid grade for any link!\n"))
827*a4128aadSBjoern A. Zeeb 		return;
828*a4128aadSBjoern A. Zeeb 
829*a4128aadSBjoern A. Zeeb 	best_link = &data[best];
830*a4128aadSBjoern A. Zeeb 	primary_link = best_link->link_id;
831*a4128aadSBjoern A. Zeeb 	new_active_links = BIT(best_link->link_id);
832*a4128aadSBjoern A. Zeeb 
833*a4128aadSBjoern A. Zeeb 	/* eSR is not supported/blocked, or only one usable link */
834*a4128aadSBjoern A. Zeeb 	if (max_active_links == 1 || !iwl_mvm_vif_has_esr_cap(mvm, vif) ||
835*a4128aadSBjoern A. Zeeb 	    mvmvif->esr_disable_reason || n_data == 1)
836*a4128aadSBjoern A. Zeeb 		goto set_active;
837*a4128aadSBjoern A. Zeeb 
838*a4128aadSBjoern A. Zeeb 	for (u8 a = 0; a < n_data; a++)
839*a4128aadSBjoern A. Zeeb 		for (u8 b = a + 1; b < n_data; b++) {
840*a4128aadSBjoern A. Zeeb 			u16 esr_grade = iwl_mvm_get_esr_grade(vif, &data[a],
841*a4128aadSBjoern A. Zeeb 							      &data[b],
842*a4128aadSBjoern A. Zeeb 							      &best_in_pair);
843*a4128aadSBjoern A. Zeeb 
844*a4128aadSBjoern A. Zeeb 			if (esr_grade <= max_esr_grade)
845*a4128aadSBjoern A. Zeeb 				continue;
846*a4128aadSBjoern A. Zeeb 
847*a4128aadSBjoern A. Zeeb 			max_esr_grade = esr_grade;
848*a4128aadSBjoern A. Zeeb 			primary_link = best_in_pair;
849*a4128aadSBjoern A. Zeeb 			new_active_links = BIT(data[a].link_id) |
850*a4128aadSBjoern A. Zeeb 					   BIT(data[b].link_id);
851*a4128aadSBjoern A. Zeeb 		}
852*a4128aadSBjoern A. Zeeb 
853*a4128aadSBjoern A. Zeeb 	/* No valid pair was found, go with the best link */
854*a4128aadSBjoern A. Zeeb 	if (hweight16(new_active_links) <= 1)
855*a4128aadSBjoern A. Zeeb 		goto set_active;
856*a4128aadSBjoern A. Zeeb 
857*a4128aadSBjoern A. Zeeb 	/* For equal grade - prefer EMLSR */
858*a4128aadSBjoern A. Zeeb 	if (best_link->grade > max_esr_grade) {
859*a4128aadSBjoern A. Zeeb 		primary_link = best_link->link_id;
860*a4128aadSBjoern A. Zeeb 		new_active_links = BIT(best_link->link_id);
861*a4128aadSBjoern A. Zeeb 	}
862*a4128aadSBjoern A. Zeeb set_active:
863*a4128aadSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "Link selection result: 0x%x. Primary = %d\n",
864*a4128aadSBjoern A. Zeeb 		       new_active_links, primary_link);
865*a4128aadSBjoern A. Zeeb 	ieee80211_set_active_links_async(vif, new_active_links);
866*a4128aadSBjoern A. Zeeb 	mvmvif->link_selection_res = new_active_links;
867*a4128aadSBjoern A. Zeeb 	mvmvif->link_selection_primary = primary_link;
868*a4128aadSBjoern A. Zeeb }
869*a4128aadSBjoern A. Zeeb 
870*a4128aadSBjoern A. Zeeb u8 iwl_mvm_get_primary_link(struct ieee80211_vif *vif)
871*a4128aadSBjoern A. Zeeb {
872*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
873*a4128aadSBjoern A. Zeeb 
874*a4128aadSBjoern A. Zeeb 	/* relevant data is written with both locks held, so read with either */
875*a4128aadSBjoern A. Zeeb 	lockdep_assert(lockdep_is_held(&mvmvif->mvm->mutex) ||
876*a4128aadSBjoern A. Zeeb 		       lockdep_is_held(&mvmvif->mvm->hw->wiphy->mtx));
877*a4128aadSBjoern A. Zeeb 
878*a4128aadSBjoern A. Zeeb 	if (!ieee80211_vif_is_mld(vif))
879*a4128aadSBjoern A. Zeeb 		return 0;
880*a4128aadSBjoern A. Zeeb 
881*a4128aadSBjoern A. Zeeb 	/* In AP mode, there is no primary link */
882*a4128aadSBjoern A. Zeeb 	if (vif->type == NL80211_IFTYPE_AP)
883*a4128aadSBjoern A. Zeeb 		return __ffs(vif->active_links);
884*a4128aadSBjoern A. Zeeb 
885*a4128aadSBjoern A. Zeeb 	if (mvmvif->esr_active &&
886*a4128aadSBjoern A. Zeeb 	    !WARN_ON(!(BIT(mvmvif->primary_link) & vif->active_links)))
887*a4128aadSBjoern A. Zeeb 		return mvmvif->primary_link;
888*a4128aadSBjoern A. Zeeb 
889*a4128aadSBjoern A. Zeeb 	return __ffs(vif->active_links);
890*a4128aadSBjoern A. Zeeb }
891*a4128aadSBjoern A. Zeeb 
892*a4128aadSBjoern A. Zeeb /*
893*a4128aadSBjoern A. Zeeb  * For non-MLO/single link, this will return the deflink/single active link,
894*a4128aadSBjoern A. Zeeb  * respectively
895*a4128aadSBjoern A. Zeeb  */
896*a4128aadSBjoern A. Zeeb u8 iwl_mvm_get_other_link(struct ieee80211_vif *vif, u8 link_id)
897*a4128aadSBjoern A. Zeeb {
898*a4128aadSBjoern A. Zeeb 	switch (hweight16(vif->active_links)) {
899*a4128aadSBjoern A. Zeeb 	case 0:
900*a4128aadSBjoern A. Zeeb 		return 0;
901*a4128aadSBjoern A. Zeeb 	default:
902*a4128aadSBjoern A. Zeeb 		WARN_ON(1);
903*a4128aadSBjoern A. Zeeb 		fallthrough;
904*a4128aadSBjoern A. Zeeb 	case 1:
905*a4128aadSBjoern A. Zeeb 		return __ffs(vif->active_links);
906*a4128aadSBjoern A. Zeeb 	case 2:
907*a4128aadSBjoern A. Zeeb 		return __ffs(vif->active_links & ~BIT(link_id));
908*a4128aadSBjoern A. Zeeb 	}
909*a4128aadSBjoern A. Zeeb }
910*a4128aadSBjoern A. Zeeb 
911*a4128aadSBjoern A. Zeeb /* Reasons that can cause esr prevention */
912*a4128aadSBjoern A. Zeeb #define IWL_MVM_ESR_PREVENT_REASONS	IWL_MVM_ESR_EXIT_MISSED_BEACON
913*a4128aadSBjoern A. Zeeb #define IWL_MVM_PREVENT_ESR_TIMEOUT	(HZ * 400)
914*a4128aadSBjoern A. Zeeb #define IWL_MVM_ESR_PREVENT_SHORT	(HZ * 300)
915*a4128aadSBjoern A. Zeeb #define IWL_MVM_ESR_PREVENT_LONG	(HZ * 600)
916*a4128aadSBjoern A. Zeeb 
917*a4128aadSBjoern A. Zeeb static bool iwl_mvm_check_esr_prevention(struct iwl_mvm *mvm,
918*a4128aadSBjoern A. Zeeb 					 struct iwl_mvm_vif *mvmvif,
919*a4128aadSBjoern A. Zeeb 					 enum iwl_mvm_esr_state reason)
920*a4128aadSBjoern A. Zeeb {
921*a4128aadSBjoern A. Zeeb 	bool timeout_expired = time_after(jiffies,
922*a4128aadSBjoern A. Zeeb 					  mvmvif->last_esr_exit.ts +
923*a4128aadSBjoern A. Zeeb 					  IWL_MVM_PREVENT_ESR_TIMEOUT);
924*a4128aadSBjoern A. Zeeb 	unsigned long delay;
925*a4128aadSBjoern A. Zeeb 
926*a4128aadSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
927*a4128aadSBjoern A. Zeeb 
928*a4128aadSBjoern A. Zeeb 	/* Only handle reasons that can cause prevention */
929*a4128aadSBjoern A. Zeeb 	if (!(reason & IWL_MVM_ESR_PREVENT_REASONS))
930*a4128aadSBjoern A. Zeeb 		return false;
931*a4128aadSBjoern A. Zeeb 
932*a4128aadSBjoern A. Zeeb 	/*
933*a4128aadSBjoern A. Zeeb 	 * Reset the counter if more than 400 seconds have passed between one
934*a4128aadSBjoern A. Zeeb 	 * exit and the other, or if we exited due to a different reason.
935*a4128aadSBjoern A. Zeeb 	 * Will also reset the counter after the long prevention is done.
936*a4128aadSBjoern A. Zeeb 	 */
937*a4128aadSBjoern A. Zeeb 	if (timeout_expired || mvmvif->last_esr_exit.reason != reason) {
938*a4128aadSBjoern A. Zeeb 		mvmvif->exit_same_reason_count = 1;
939*a4128aadSBjoern A. Zeeb 		return false;
940*a4128aadSBjoern A. Zeeb 	}
941*a4128aadSBjoern A. Zeeb 
942*a4128aadSBjoern A. Zeeb 	mvmvif->exit_same_reason_count++;
943*a4128aadSBjoern A. Zeeb 	if (WARN_ON(mvmvif->exit_same_reason_count < 2 ||
944*a4128aadSBjoern A. Zeeb 		    mvmvif->exit_same_reason_count > 3))
945*a4128aadSBjoern A. Zeeb 		return false;
946*a4128aadSBjoern A. Zeeb 
947*a4128aadSBjoern A. Zeeb 	mvmvif->esr_disable_reason |= IWL_MVM_ESR_BLOCKED_PREVENTION;
948*a4128aadSBjoern A. Zeeb 
949*a4128aadSBjoern A. Zeeb 	/*
950*a4128aadSBjoern A. Zeeb 	 * For the second exit, use a short prevention, and for the third one,
951*a4128aadSBjoern A. Zeeb 	 * use a long prevention.
952*a4128aadSBjoern A. Zeeb 	 */
953*a4128aadSBjoern A. Zeeb 	delay = mvmvif->exit_same_reason_count == 2 ?
954*a4128aadSBjoern A. Zeeb 		IWL_MVM_ESR_PREVENT_SHORT :
955*a4128aadSBjoern A. Zeeb 		IWL_MVM_ESR_PREVENT_LONG;
956*a4128aadSBjoern A. Zeeb 
957*a4128aadSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm,
958*a4128aadSBjoern A. Zeeb 		       "Preventing EMLSR for %ld seconds due to %u exits with the reason = %s (0x%x)\n",
959*a4128aadSBjoern A. Zeeb 		       delay / HZ, mvmvif->exit_same_reason_count,
960*a4128aadSBjoern A. Zeeb 		       iwl_get_esr_state_string(reason), reason);
961*a4128aadSBjoern A. Zeeb 
962*a4128aadSBjoern A. Zeeb 	wiphy_delayed_work_queue(mvm->hw->wiphy,
963*a4128aadSBjoern A. Zeeb 				 &mvmvif->prevent_esr_done_wk, delay);
964*a4128aadSBjoern A. Zeeb 	return true;
965*a4128aadSBjoern A. Zeeb }
966*a4128aadSBjoern A. Zeeb 
967*a4128aadSBjoern A. Zeeb #define IWL_MVM_TRIGGER_LINK_SEL_TIME (IWL_MVM_TRIGGER_LINK_SEL_TIME_SEC * HZ)
968*a4128aadSBjoern A. Zeeb 
969*a4128aadSBjoern A. Zeeb /* API to exit eSR mode */
970*a4128aadSBjoern A. Zeeb void iwl_mvm_exit_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
971*a4128aadSBjoern A. Zeeb 		      enum iwl_mvm_esr_state reason,
972*a4128aadSBjoern A. Zeeb 		      u8 link_to_keep)
973*a4128aadSBjoern A. Zeeb {
974*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
975*a4128aadSBjoern A. Zeeb 	u16 new_active_links;
976*a4128aadSBjoern A. Zeeb 	bool prevented;
977*a4128aadSBjoern A. Zeeb 
978*a4128aadSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
979*a4128aadSBjoern A. Zeeb 
980*a4128aadSBjoern A. Zeeb 	if (!IWL_MVM_AUTO_EML_ENABLE)
981*a4128aadSBjoern A. Zeeb 		return;
982*a4128aadSBjoern A. Zeeb 
983*a4128aadSBjoern A. Zeeb 	/* Nothing to do */
984*a4128aadSBjoern A. Zeeb 	if (!mvmvif->esr_active)
985*a4128aadSBjoern A. Zeeb 		return;
986*a4128aadSBjoern A. Zeeb 
987*a4128aadSBjoern A. Zeeb 	if (WARN_ON(!ieee80211_vif_is_mld(vif) || !mvmvif->authorized))
988*a4128aadSBjoern A. Zeeb 		return;
989*a4128aadSBjoern A. Zeeb 
990*a4128aadSBjoern A. Zeeb 	if (WARN_ON(!(vif->active_links & BIT(link_to_keep))))
991*a4128aadSBjoern A. Zeeb 		link_to_keep = __ffs(vif->active_links);
992*a4128aadSBjoern A. Zeeb 
993*a4128aadSBjoern A. Zeeb 	new_active_links = BIT(link_to_keep);
994*a4128aadSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm,
995*a4128aadSBjoern A. Zeeb 		       "Exiting EMLSR. reason = %s (0x%x). Current active links=0x%x, new active links = 0x%x\n",
996*a4128aadSBjoern A. Zeeb 		       iwl_get_esr_state_string(reason), reason,
997*a4128aadSBjoern A. Zeeb 		       vif->active_links, new_active_links);
998*a4128aadSBjoern A. Zeeb 
999*a4128aadSBjoern A. Zeeb 	ieee80211_set_active_links_async(vif, new_active_links);
1000*a4128aadSBjoern A. Zeeb 
1001*a4128aadSBjoern A. Zeeb 	/* Prevent EMLSR if needed */
1002*a4128aadSBjoern A. Zeeb 	prevented = iwl_mvm_check_esr_prevention(mvm, mvmvif, reason);
1003*a4128aadSBjoern A. Zeeb 
1004*a4128aadSBjoern A. Zeeb 	/* Remember why and when we exited EMLSR */
1005*a4128aadSBjoern A. Zeeb 	mvmvif->last_esr_exit.ts = jiffies;
1006*a4128aadSBjoern A. Zeeb 	mvmvif->last_esr_exit.reason = reason;
1007*a4128aadSBjoern A. Zeeb 
1008*a4128aadSBjoern A. Zeeb 	/*
1009*a4128aadSBjoern A. Zeeb 	 * If EMLSR is prevented now - don't try to get back to EMLSR.
1010*a4128aadSBjoern A. Zeeb 	 * If we exited due to a blocking event, we will try to get back to
1011*a4128aadSBjoern A. Zeeb 	 * EMLSR when the corresponding unblocking event will happen.
1012*a4128aadSBjoern A. Zeeb 	 */
1013*a4128aadSBjoern A. Zeeb 	if (prevented || reason & IWL_MVM_BLOCK_ESR_REASONS)
1014*a4128aadSBjoern A. Zeeb 		return;
1015*a4128aadSBjoern A. Zeeb 
1016*a4128aadSBjoern A. Zeeb 	/* If EMLSR is not blocked - try enabling it again in 30 seconds */
1017*a4128aadSBjoern A. Zeeb 	wiphy_delayed_work_queue(mvm->hw->wiphy,
1018*a4128aadSBjoern A. Zeeb 				 &mvmvif->mlo_int_scan_wk,
1019*a4128aadSBjoern A. Zeeb 				 round_jiffies_relative(IWL_MVM_TRIGGER_LINK_SEL_TIME));
1020*a4128aadSBjoern A. Zeeb }
1021*a4128aadSBjoern A. Zeeb 
1022*a4128aadSBjoern A. Zeeb void iwl_mvm_block_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
1023*a4128aadSBjoern A. Zeeb 		       enum iwl_mvm_esr_state reason,
1024*a4128aadSBjoern A. Zeeb 		       u8 link_to_keep)
1025*a4128aadSBjoern A. Zeeb {
1026*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1027*a4128aadSBjoern A. Zeeb 
1028*a4128aadSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
1029*a4128aadSBjoern A. Zeeb 
1030*a4128aadSBjoern A. Zeeb 	if (!IWL_MVM_AUTO_EML_ENABLE)
1031*a4128aadSBjoern A. Zeeb 		return;
1032*a4128aadSBjoern A. Zeeb 
1033*a4128aadSBjoern A. Zeeb 	/* This should be called only with disable reasons */
1034*a4128aadSBjoern A. Zeeb 	if (WARN_ON(!(reason & IWL_MVM_BLOCK_ESR_REASONS)))
1035*a4128aadSBjoern A. Zeeb 		return;
1036*a4128aadSBjoern A. Zeeb 
1037*a4128aadSBjoern A. Zeeb 	if (mvmvif->esr_disable_reason & reason)
1038*a4128aadSBjoern A. Zeeb 		return;
1039*a4128aadSBjoern A. Zeeb 
1040*a4128aadSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm,
1041*a4128aadSBjoern A. Zeeb 		       "Blocking EMLSR mode. reason = %s (0x%x)\n",
1042*a4128aadSBjoern A. Zeeb 		       iwl_get_esr_state_string(reason), reason);
1043*a4128aadSBjoern A. Zeeb 
1044*a4128aadSBjoern A. Zeeb 	mvmvif->esr_disable_reason |= reason;
1045*a4128aadSBjoern A. Zeeb 
1046*a4128aadSBjoern A. Zeeb 	iwl_mvm_print_esr_state(mvm, mvmvif->esr_disable_reason);
1047*a4128aadSBjoern A. Zeeb 
1048*a4128aadSBjoern A. Zeeb 	iwl_mvm_exit_esr(mvm, vif, reason, link_to_keep);
1049*a4128aadSBjoern A. Zeeb }
1050*a4128aadSBjoern A. Zeeb 
1051*a4128aadSBjoern A. Zeeb int iwl_mvm_block_esr_sync(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
1052*a4128aadSBjoern A. Zeeb 			   enum iwl_mvm_esr_state reason)
1053*a4128aadSBjoern A. Zeeb {
1054*a4128aadSBjoern A. Zeeb 	int primary_link = iwl_mvm_get_primary_link(vif);
1055*a4128aadSBjoern A. Zeeb 	int ret;
1056*a4128aadSBjoern A. Zeeb 
1057*a4128aadSBjoern A. Zeeb 	if (!IWL_MVM_AUTO_EML_ENABLE || !ieee80211_vif_is_mld(vif))
1058*a4128aadSBjoern A. Zeeb 		return 0;
1059*a4128aadSBjoern A. Zeeb 
1060*a4128aadSBjoern A. Zeeb 	/* This should be called only with blocking reasons */
1061*a4128aadSBjoern A. Zeeb 	if (WARN_ON(!(reason & IWL_MVM_BLOCK_ESR_REASONS)))
1062*a4128aadSBjoern A. Zeeb 		return 0;
1063*a4128aadSBjoern A. Zeeb 
1064*a4128aadSBjoern A. Zeeb 	/* leave ESR immediately, not only async with iwl_mvm_block_esr() */
1065*a4128aadSBjoern A. Zeeb 	ret = ieee80211_set_active_links(vif, BIT(primary_link));
1066*a4128aadSBjoern A. Zeeb 	if (ret)
1067*a4128aadSBjoern A. Zeeb 		return ret;
1068*a4128aadSBjoern A. Zeeb 
1069*a4128aadSBjoern A. Zeeb 	mutex_lock(&mvm->mutex);
1070*a4128aadSBjoern A. Zeeb 	/* only additionally block for consistency and to avoid concurrency */
1071*a4128aadSBjoern A. Zeeb 	iwl_mvm_block_esr(mvm, vif, reason, primary_link);
1072*a4128aadSBjoern A. Zeeb 	mutex_unlock(&mvm->mutex);
1073*a4128aadSBjoern A. Zeeb 
1074*a4128aadSBjoern A. Zeeb 	return 0;
1075*a4128aadSBjoern A. Zeeb }
1076*a4128aadSBjoern A. Zeeb 
1077*a4128aadSBjoern A. Zeeb static void iwl_mvm_esr_unblocked(struct iwl_mvm *mvm,
1078*a4128aadSBjoern A. Zeeb 				  struct ieee80211_vif *vif)
1079*a4128aadSBjoern A. Zeeb {
1080*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1081*a4128aadSBjoern A. Zeeb 	bool need_new_sel = time_after(jiffies, mvmvif->last_esr_exit.ts +
1082*a4128aadSBjoern A. Zeeb 						IWL_MVM_TRIGGER_LINK_SEL_TIME);
1083*a4128aadSBjoern A. Zeeb 
1084*a4128aadSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
1085*a4128aadSBjoern A. Zeeb 
1086*a4128aadSBjoern A. Zeeb 	if (!ieee80211_vif_is_mld(vif) || !mvmvif->authorized ||
1087*a4128aadSBjoern A. Zeeb 	    mvmvif->esr_active)
1088*a4128aadSBjoern A. Zeeb 		return;
1089*a4128aadSBjoern A. Zeeb 
1090*a4128aadSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "EMLSR is unblocked\n");
1091*a4128aadSBjoern A. Zeeb 
1092*a4128aadSBjoern A. Zeeb 	/* If we exited due to an EXIT reason, and the exit was in less than
1093*a4128aadSBjoern A. Zeeb 	 * 30 seconds, then a MLO scan was scheduled already.
1094*a4128aadSBjoern A. Zeeb 	 */
1095*a4128aadSBjoern A. Zeeb 	if (!need_new_sel &&
1096*a4128aadSBjoern A. Zeeb 	    !(mvmvif->last_esr_exit.reason & IWL_MVM_BLOCK_ESR_REASONS)) {
1097*a4128aadSBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm, "Wait for MLO scan\n");
1098*a4128aadSBjoern A. Zeeb 		return;
1099*a4128aadSBjoern A. Zeeb 	}
1100*a4128aadSBjoern A. Zeeb 
1101*a4128aadSBjoern A. Zeeb 	/*
1102*a4128aadSBjoern A. Zeeb 	 * If EMLSR was blocked for more than 30 seconds, or the last link
1103*a4128aadSBjoern A. Zeeb 	 * selection decided to not enter EMLSR, trigger a new scan.
1104*a4128aadSBjoern A. Zeeb 	 */
1105*a4128aadSBjoern A. Zeeb 	if (need_new_sel || hweight16(mvmvif->link_selection_res) < 2) {
1106*a4128aadSBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm, "Trigger MLO scan\n");
1107*a4128aadSBjoern A. Zeeb 		wiphy_delayed_work_queue(mvm->hw->wiphy,
1108*a4128aadSBjoern A. Zeeb 					 &mvmvif->mlo_int_scan_wk, 0);
1109*a4128aadSBjoern A. Zeeb 	/*
1110*a4128aadSBjoern A. Zeeb 	 * If EMLSR was blocked for less than 30 seconds, and the last link
1111*a4128aadSBjoern A. Zeeb 	 * selection decided to use EMLSR, activate EMLSR using the previous
1112*a4128aadSBjoern A. Zeeb 	 * link selection result.
1113*a4128aadSBjoern A. Zeeb 	 */
1114*a4128aadSBjoern A. Zeeb 	} else {
1115*a4128aadSBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm,
1116*a4128aadSBjoern A. Zeeb 			       "Use the latest link selection result: 0x%x\n",
1117*a4128aadSBjoern A. Zeeb 			       mvmvif->link_selection_res);
1118*a4128aadSBjoern A. Zeeb 		ieee80211_set_active_links_async(vif,
1119*a4128aadSBjoern A. Zeeb 						 mvmvif->link_selection_res);
1120*a4128aadSBjoern A. Zeeb 	}
1121*a4128aadSBjoern A. Zeeb }
1122*a4128aadSBjoern A. Zeeb 
1123*a4128aadSBjoern A. Zeeb void iwl_mvm_unblock_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
1124*a4128aadSBjoern A. Zeeb 			 enum iwl_mvm_esr_state reason)
1125*a4128aadSBjoern A. Zeeb {
1126*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1127*a4128aadSBjoern A. Zeeb 
1128*a4128aadSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
1129*a4128aadSBjoern A. Zeeb 
1130*a4128aadSBjoern A. Zeeb 	if (!IWL_MVM_AUTO_EML_ENABLE)
1131*a4128aadSBjoern A. Zeeb 		return;
1132*a4128aadSBjoern A. Zeeb 
1133*a4128aadSBjoern A. Zeeb 	/* This should be called only with disable reasons */
1134*a4128aadSBjoern A. Zeeb 	if (WARN_ON(!(reason & IWL_MVM_BLOCK_ESR_REASONS)))
1135*a4128aadSBjoern A. Zeeb 		return;
1136*a4128aadSBjoern A. Zeeb 
1137*a4128aadSBjoern A. Zeeb 	/* No Change */
1138*a4128aadSBjoern A. Zeeb 	if (!(mvmvif->esr_disable_reason & reason))
1139*a4128aadSBjoern A. Zeeb 		return;
1140*a4128aadSBjoern A. Zeeb 
1141*a4128aadSBjoern A. Zeeb 	mvmvif->esr_disable_reason &= ~reason;
1142*a4128aadSBjoern A. Zeeb 
1143*a4128aadSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm,
1144*a4128aadSBjoern A. Zeeb 		       "Unblocking EMLSR mode. reason = %s (0x%x)\n",
1145*a4128aadSBjoern A. Zeeb 		       iwl_get_esr_state_string(reason), reason);
1146*a4128aadSBjoern A. Zeeb 	iwl_mvm_print_esr_state(mvm, mvmvif->esr_disable_reason);
1147*a4128aadSBjoern A. Zeeb 
1148*a4128aadSBjoern A. Zeeb 	if (!mvmvif->esr_disable_reason)
1149*a4128aadSBjoern A. Zeeb 		iwl_mvm_esr_unblocked(mvm, vif);
1150*a4128aadSBjoern A. Zeeb }
1151