xref: /freebsd/sys/contrib/dev/iwlwifi/mvm/rs-fw.c (revision 9af1bba44e1ce9b0296ae56760b564d67ab7a1cf)
1bfcc09ddSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2bfcc09ddSBjoern A. Zeeb /*
3bfcc09ddSBjoern A. Zeeb  * Copyright (C) 2017 Intel Deutschland GmbH
4*9af1bba4SBjoern A. Zeeb  * Copyright (C) 2018-2023 Intel Corporation
5bfcc09ddSBjoern A. Zeeb  */
6bfcc09ddSBjoern A. Zeeb #include "rs.h"
7bfcc09ddSBjoern A. Zeeb #include "fw-api.h"
8bfcc09ddSBjoern A. Zeeb #include "sta.h"
9bfcc09ddSBjoern A. Zeeb #include "iwl-op-mode.h"
10bfcc09ddSBjoern A. Zeeb #include "mvm.h"
11bfcc09ddSBjoern A. Zeeb 
12*9af1bba4SBjoern A. Zeeb static u8 rs_fw_bw_from_sta_bw(const struct ieee80211_link_sta *link_sta)
13bfcc09ddSBjoern A. Zeeb {
14*9af1bba4SBjoern A. Zeeb 	switch (link_sta->bandwidth) {
15*9af1bba4SBjoern A. Zeeb 	case IEEE80211_STA_RX_BW_320:
16*9af1bba4SBjoern A. Zeeb 		return IWL_TLC_MNG_CH_WIDTH_320MHZ;
17bfcc09ddSBjoern A. Zeeb 	case IEEE80211_STA_RX_BW_160:
18bfcc09ddSBjoern A. Zeeb 		return IWL_TLC_MNG_CH_WIDTH_160MHZ;
19bfcc09ddSBjoern A. Zeeb 	case IEEE80211_STA_RX_BW_80:
20bfcc09ddSBjoern A. Zeeb 		return IWL_TLC_MNG_CH_WIDTH_80MHZ;
21bfcc09ddSBjoern A. Zeeb 	case IEEE80211_STA_RX_BW_40:
22bfcc09ddSBjoern A. Zeeb 		return IWL_TLC_MNG_CH_WIDTH_40MHZ;
23bfcc09ddSBjoern A. Zeeb 	case IEEE80211_STA_RX_BW_20:
24bfcc09ddSBjoern A. Zeeb 	default:
25bfcc09ddSBjoern A. Zeeb 		return IWL_TLC_MNG_CH_WIDTH_20MHZ;
26bfcc09ddSBjoern A. Zeeb 	}
27bfcc09ddSBjoern A. Zeeb }
28bfcc09ddSBjoern A. Zeeb 
29bfcc09ddSBjoern A. Zeeb static u8 rs_fw_set_active_chains(u8 chains)
30bfcc09ddSBjoern A. Zeeb {
31bfcc09ddSBjoern A. Zeeb 	u8 fw_chains = 0;
32bfcc09ddSBjoern A. Zeeb 
33bfcc09ddSBjoern A. Zeeb 	if (chains & ANT_A)
34bfcc09ddSBjoern A. Zeeb 		fw_chains |= IWL_TLC_MNG_CHAIN_A_MSK;
35bfcc09ddSBjoern A. Zeeb 	if (chains & ANT_B)
36bfcc09ddSBjoern A. Zeeb 		fw_chains |= IWL_TLC_MNG_CHAIN_B_MSK;
37bfcc09ddSBjoern A. Zeeb 
38bfcc09ddSBjoern A. Zeeb 	return fw_chains;
39bfcc09ddSBjoern A. Zeeb }
40bfcc09ddSBjoern A. Zeeb 
41*9af1bba4SBjoern A. Zeeb static u8 rs_fw_sgi_cw_support(struct ieee80211_link_sta *link_sta)
42bfcc09ddSBjoern A. Zeeb {
43*9af1bba4SBjoern A. Zeeb 	struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
44*9af1bba4SBjoern A. Zeeb 	struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
45*9af1bba4SBjoern A. Zeeb 	struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
46bfcc09ddSBjoern A. Zeeb 	u8 supp = 0;
47bfcc09ddSBjoern A. Zeeb 
48bfcc09ddSBjoern A. Zeeb 	if (he_cap->has_he)
49bfcc09ddSBjoern A. Zeeb 		return 0;
50bfcc09ddSBjoern A. Zeeb 
51bfcc09ddSBjoern A. Zeeb 	if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
52bfcc09ddSBjoern A. Zeeb 		supp |= BIT(IWL_TLC_MNG_CH_WIDTH_20MHZ);
53bfcc09ddSBjoern A. Zeeb 	if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
54bfcc09ddSBjoern A. Zeeb 		supp |= BIT(IWL_TLC_MNG_CH_WIDTH_40MHZ);
55bfcc09ddSBjoern A. Zeeb 	if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80)
56bfcc09ddSBjoern A. Zeeb 		supp |= BIT(IWL_TLC_MNG_CH_WIDTH_80MHZ);
57bfcc09ddSBjoern A. Zeeb 	if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160)
58bfcc09ddSBjoern A. Zeeb 		supp |= BIT(IWL_TLC_MNG_CH_WIDTH_160MHZ);
59bfcc09ddSBjoern A. Zeeb 
60bfcc09ddSBjoern A. Zeeb 	return supp;
61bfcc09ddSBjoern A. Zeeb }
62bfcc09ddSBjoern A. Zeeb 
63bfcc09ddSBjoern A. Zeeb static u16 rs_fw_get_config_flags(struct iwl_mvm *mvm,
64*9af1bba4SBjoern A. Zeeb 				  struct ieee80211_vif *vif,
65*9af1bba4SBjoern A. Zeeb 				  struct ieee80211_link_sta *link_sta,
66*9af1bba4SBjoern A. Zeeb 				  const struct ieee80211_sta_he_cap *sband_he_cap)
67bfcc09ddSBjoern A. Zeeb {
68*9af1bba4SBjoern A. Zeeb 	struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
69*9af1bba4SBjoern A. Zeeb 	struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
70*9af1bba4SBjoern A. Zeeb 	struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
71bfcc09ddSBjoern A. Zeeb 	bool vht_ena = vht_cap->vht_supported;
72bfcc09ddSBjoern A. Zeeb 	u16 flags = 0;
73bfcc09ddSBjoern A. Zeeb 
74bfcc09ddSBjoern A. Zeeb 	/* get STBC flags */
75bfcc09ddSBjoern A. Zeeb 	if (mvm->cfg->ht_params->stbc &&
76bfcc09ddSBjoern A. Zeeb 	    (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1)) {
77bfcc09ddSBjoern A. Zeeb 		if (he_cap->has_he && he_cap->he_cap_elem.phy_cap_info[2] &
78bfcc09ddSBjoern A. Zeeb 				      IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ)
79bfcc09ddSBjoern A. Zeeb 			flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
80bfcc09ddSBjoern A. Zeeb 		else if (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)
81bfcc09ddSBjoern A. Zeeb 			flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
82bfcc09ddSBjoern A. Zeeb 		else if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)
83bfcc09ddSBjoern A. Zeeb 			flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
84bfcc09ddSBjoern A. Zeeb 	}
85bfcc09ddSBjoern A. Zeeb 
86bfcc09ddSBjoern A. Zeeb 	if (mvm->cfg->ht_params->ldpc &&
87bfcc09ddSBjoern A. Zeeb 	    ((ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) ||
88bfcc09ddSBjoern A. Zeeb 	     (vht_ena && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC))))
89bfcc09ddSBjoern A. Zeeb 		flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
90bfcc09ddSBjoern A. Zeeb 
91bfcc09ddSBjoern A. Zeeb 	/* consider LDPC support in case of HE */
92bfcc09ddSBjoern A. Zeeb 	if (he_cap->has_he && (he_cap->he_cap_elem.phy_cap_info[1] &
93bfcc09ddSBjoern A. Zeeb 	    IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
94bfcc09ddSBjoern A. Zeeb 		flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
95bfcc09ddSBjoern A. Zeeb 
96*9af1bba4SBjoern A. Zeeb 	if (sband_he_cap &&
97*9af1bba4SBjoern A. Zeeb 	    !(sband_he_cap->he_cap_elem.phy_cap_info[1] &
98bfcc09ddSBjoern A. Zeeb 			IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
99bfcc09ddSBjoern A. Zeeb 		flags &= ~IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
100bfcc09ddSBjoern A. Zeeb 
101bfcc09ddSBjoern A. Zeeb 	if (he_cap->has_he &&
102bfcc09ddSBjoern A. Zeeb 	    (he_cap->he_cap_elem.phy_cap_info[3] &
103d9836fb4SBjoern A. Zeeb 	     IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK &&
104*9af1bba4SBjoern A. Zeeb 	     sband_he_cap &&
105*9af1bba4SBjoern A. Zeeb 	     sband_he_cap->he_cap_elem.phy_cap_info[3] &
106d9836fb4SBjoern A. Zeeb 			IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK))
107bfcc09ddSBjoern A. Zeeb 		flags |= IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK;
108bfcc09ddSBjoern A. Zeeb 
109bfcc09ddSBjoern A. Zeeb 	return flags;
110bfcc09ddSBjoern A. Zeeb }
111bfcc09ddSBjoern A. Zeeb 
112bfcc09ddSBjoern A. Zeeb static
113bfcc09ddSBjoern A. Zeeb int rs_fw_vht_highest_rx_mcs_index(const struct ieee80211_sta_vht_cap *vht_cap,
114bfcc09ddSBjoern A. Zeeb 				   int nss)
115bfcc09ddSBjoern A. Zeeb {
116bfcc09ddSBjoern A. Zeeb 	u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) &
117bfcc09ddSBjoern A. Zeeb 		(0x3 << (2 * (nss - 1)));
118bfcc09ddSBjoern A. Zeeb 	rx_mcs >>= (2 * (nss - 1));
119bfcc09ddSBjoern A. Zeeb 
120bfcc09ddSBjoern A. Zeeb 	switch (rx_mcs) {
121bfcc09ddSBjoern A. Zeeb 	case IEEE80211_VHT_MCS_SUPPORT_0_7:
122bfcc09ddSBjoern A. Zeeb 		return IWL_TLC_MNG_HT_RATE_MCS7;
123bfcc09ddSBjoern A. Zeeb 	case IEEE80211_VHT_MCS_SUPPORT_0_8:
124bfcc09ddSBjoern A. Zeeb 		return IWL_TLC_MNG_HT_RATE_MCS8;
125bfcc09ddSBjoern A. Zeeb 	case IEEE80211_VHT_MCS_SUPPORT_0_9:
126bfcc09ddSBjoern A. Zeeb 		return IWL_TLC_MNG_HT_RATE_MCS9;
127bfcc09ddSBjoern A. Zeeb 	default:
128bfcc09ddSBjoern A. Zeeb 		WARN_ON_ONCE(1);
129bfcc09ddSBjoern A. Zeeb 		break;
130bfcc09ddSBjoern A. Zeeb 	}
131bfcc09ddSBjoern A. Zeeb 
132bfcc09ddSBjoern A. Zeeb 	return 0;
133bfcc09ddSBjoern A. Zeeb }
134bfcc09ddSBjoern A. Zeeb 
135bfcc09ddSBjoern A. Zeeb static void
136*9af1bba4SBjoern A. Zeeb rs_fw_vht_set_enabled_rates(const struct ieee80211_link_sta *link_sta,
137bfcc09ddSBjoern A. Zeeb 			    const struct ieee80211_sta_vht_cap *vht_cap,
138d9836fb4SBjoern A. Zeeb 			    struct iwl_tlc_config_cmd_v4 *cmd)
139bfcc09ddSBjoern A. Zeeb {
140bfcc09ddSBjoern A. Zeeb 	u16 supp;
141bfcc09ddSBjoern A. Zeeb 	int i, highest_mcs;
142*9af1bba4SBjoern A. Zeeb 	u8 max_nss = link_sta->rx_nss;
143bfcc09ddSBjoern A. Zeeb 	struct ieee80211_vht_cap ieee_vht_cap = {
144bfcc09ddSBjoern A. Zeeb 		.vht_cap_info = cpu_to_le32(vht_cap->cap),
145bfcc09ddSBjoern A. Zeeb 		.supp_mcs = vht_cap->vht_mcs,
146bfcc09ddSBjoern A. Zeeb 	};
147bfcc09ddSBjoern A. Zeeb 
148bfcc09ddSBjoern A. Zeeb 	/* the station support only a single receive chain */
149*9af1bba4SBjoern A. Zeeb 	if (link_sta->smps_mode == IEEE80211_SMPS_STATIC)
150bfcc09ddSBjoern A. Zeeb 		max_nss = 1;
151bfcc09ddSBjoern A. Zeeb 
152bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < max_nss && i < IWL_TLC_NSS_MAX; i++) {
153bfcc09ddSBjoern A. Zeeb 		int nss = i + 1;
154bfcc09ddSBjoern A. Zeeb 
155bfcc09ddSBjoern A. Zeeb 		highest_mcs = rs_fw_vht_highest_rx_mcs_index(vht_cap, nss);
156bfcc09ddSBjoern A. Zeeb 		if (!highest_mcs)
157bfcc09ddSBjoern A. Zeeb 			continue;
158bfcc09ddSBjoern A. Zeeb 
159bfcc09ddSBjoern A. Zeeb 		supp = BIT(highest_mcs + 1) - 1;
160*9af1bba4SBjoern A. Zeeb 		if (link_sta->bandwidth == IEEE80211_STA_RX_BW_20)
161bfcc09ddSBjoern A. Zeeb 			supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9);
162bfcc09ddSBjoern A. Zeeb 
163d9836fb4SBjoern A. Zeeb 		cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] = cpu_to_le16(supp);
164bfcc09ddSBjoern A. Zeeb 		/*
165bfcc09ddSBjoern A. Zeeb 		 * Check if VHT extended NSS indicates that the bandwidth/NSS
166bfcc09ddSBjoern A. Zeeb 		 * configuration is supported - only for MCS 0 since we already
167bfcc09ddSBjoern A. Zeeb 		 * decoded the MCS bits anyway ourselves.
168bfcc09ddSBjoern A. Zeeb 		 */
169*9af1bba4SBjoern A. Zeeb 		if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160 &&
170bfcc09ddSBjoern A. Zeeb 		    ieee80211_get_vht_max_nss(&ieee_vht_cap,
171bfcc09ddSBjoern A. Zeeb 					      IEEE80211_VHT_CHANWIDTH_160MHZ,
172bfcc09ddSBjoern A. Zeeb 					      0, true, nss) >= nss)
173d9836fb4SBjoern A. Zeeb 			cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] =
174d9836fb4SBjoern A. Zeeb 				cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80];
175bfcc09ddSBjoern A. Zeeb 	}
176bfcc09ddSBjoern A. Zeeb }
177bfcc09ddSBjoern A. Zeeb 
178bfcc09ddSBjoern A. Zeeb static u16 rs_fw_he_ieee80211_mcs_to_rs_mcs(u16 mcs)
179bfcc09ddSBjoern A. Zeeb {
180bfcc09ddSBjoern A. Zeeb 	switch (mcs) {
181bfcc09ddSBjoern A. Zeeb 	case IEEE80211_HE_MCS_SUPPORT_0_7:
182bfcc09ddSBjoern A. Zeeb 		return BIT(IWL_TLC_MNG_HT_RATE_MCS7 + 1) - 1;
183bfcc09ddSBjoern A. Zeeb 	case IEEE80211_HE_MCS_SUPPORT_0_9:
184bfcc09ddSBjoern A. Zeeb 		return BIT(IWL_TLC_MNG_HT_RATE_MCS9 + 1) - 1;
185bfcc09ddSBjoern A. Zeeb 	case IEEE80211_HE_MCS_SUPPORT_0_11:
186bfcc09ddSBjoern A. Zeeb 		return BIT(IWL_TLC_MNG_HT_RATE_MCS11 + 1) - 1;
187bfcc09ddSBjoern A. Zeeb 	case IEEE80211_HE_MCS_NOT_SUPPORTED:
188bfcc09ddSBjoern A. Zeeb 		return 0;
189bfcc09ddSBjoern A. Zeeb 	}
190bfcc09ddSBjoern A. Zeeb 
191bfcc09ddSBjoern A. Zeeb 	WARN(1, "invalid HE MCS %d\n", mcs);
192bfcc09ddSBjoern A. Zeeb 	return 0;
193bfcc09ddSBjoern A. Zeeb }
194bfcc09ddSBjoern A. Zeeb 
195bfcc09ddSBjoern A. Zeeb static void
196*9af1bba4SBjoern A. Zeeb rs_fw_he_set_enabled_rates(const struct ieee80211_link_sta *link_sta,
197*9af1bba4SBjoern A. Zeeb 			   const struct ieee80211_sta_he_cap *sband_he_cap,
198d9836fb4SBjoern A. Zeeb 			   struct iwl_tlc_config_cmd_v4 *cmd)
199bfcc09ddSBjoern A. Zeeb {
200*9af1bba4SBjoern A. Zeeb 	const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
201bfcc09ddSBjoern A. Zeeb 	u16 mcs_160 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
202bfcc09ddSBjoern A. Zeeb 	u16 mcs_80 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);
203*9af1bba4SBjoern A. Zeeb 	u16 tx_mcs_80 = le16_to_cpu(sband_he_cap->he_mcs_nss_supp.tx_mcs_80);
204*9af1bba4SBjoern A. Zeeb 	u16 tx_mcs_160 = le16_to_cpu(sband_he_cap->he_mcs_nss_supp.tx_mcs_160);
205bfcc09ddSBjoern A. Zeeb 	int i;
206*9af1bba4SBjoern A. Zeeb 	u8 nss = link_sta->rx_nss;
207bfcc09ddSBjoern A. Zeeb 
208bfcc09ddSBjoern A. Zeeb 	/* the station support only a single receive chain */
209*9af1bba4SBjoern A. Zeeb 	if (link_sta->smps_mode == IEEE80211_SMPS_STATIC)
210bfcc09ddSBjoern A. Zeeb 		nss = 1;
211bfcc09ddSBjoern A. Zeeb 
212bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < nss && i < IWL_TLC_NSS_MAX; i++) {
213bfcc09ddSBjoern A. Zeeb 		u16 _mcs_160 = (mcs_160 >> (2 * i)) & 0x3;
214bfcc09ddSBjoern A. Zeeb 		u16 _mcs_80 = (mcs_80 >> (2 * i)) & 0x3;
215bfcc09ddSBjoern A. Zeeb 		u16 _tx_mcs_160 = (tx_mcs_160 >> (2 * i)) & 0x3;
216bfcc09ddSBjoern A. Zeeb 		u16 _tx_mcs_80 = (tx_mcs_80 >> (2 * i)) & 0x3;
217bfcc09ddSBjoern A. Zeeb 
218bfcc09ddSBjoern A. Zeeb 		/* If one side doesn't support - mark both as not supporting */
219bfcc09ddSBjoern A. Zeeb 		if (_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED ||
220bfcc09ddSBjoern A. Zeeb 		    _tx_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED) {
221bfcc09ddSBjoern A. Zeeb 			_mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED;
222bfcc09ddSBjoern A. Zeeb 			_tx_mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED;
223bfcc09ddSBjoern A. Zeeb 		}
224bfcc09ddSBjoern A. Zeeb 		if (_mcs_80 > _tx_mcs_80)
225bfcc09ddSBjoern A. Zeeb 			_mcs_80 = _tx_mcs_80;
226d9836fb4SBjoern A. Zeeb 		cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] =
227bfcc09ddSBjoern A. Zeeb 			cpu_to_le16(rs_fw_he_ieee80211_mcs_to_rs_mcs(_mcs_80));
228bfcc09ddSBjoern A. Zeeb 
229bfcc09ddSBjoern A. Zeeb 		/* If one side doesn't support - mark both as not supporting */
230bfcc09ddSBjoern A. Zeeb 		if (_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED ||
231bfcc09ddSBjoern A. Zeeb 		    _tx_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED) {
232bfcc09ddSBjoern A. Zeeb 			_mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED;
233bfcc09ddSBjoern A. Zeeb 			_tx_mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED;
234bfcc09ddSBjoern A. Zeeb 		}
235bfcc09ddSBjoern A. Zeeb 		if (_mcs_160 > _tx_mcs_160)
236bfcc09ddSBjoern A. Zeeb 			_mcs_160 = _tx_mcs_160;
237d9836fb4SBjoern A. Zeeb 		cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] =
238bfcc09ddSBjoern A. Zeeb 			cpu_to_le16(rs_fw_he_ieee80211_mcs_to_rs_mcs(_mcs_160));
239bfcc09ddSBjoern A. Zeeb 	}
240bfcc09ddSBjoern A. Zeeb }
241bfcc09ddSBjoern A. Zeeb 
242*9af1bba4SBjoern A. Zeeb static u8 rs_fw_eht_max_nss(u8 rx_nss, u8 tx_nss)
243*9af1bba4SBjoern A. Zeeb {
244*9af1bba4SBjoern A. Zeeb 	u8 tx = u8_get_bits(tx_nss, IEEE80211_EHT_MCS_NSS_TX);
245*9af1bba4SBjoern A. Zeeb 	u8 rx = u8_get_bits(rx_nss, IEEE80211_EHT_MCS_NSS_RX);
246*9af1bba4SBjoern A. Zeeb 	/* the max nss that can be used,
247*9af1bba4SBjoern A. Zeeb 	 * is the min with our tx capa and the peer rx capa.
248*9af1bba4SBjoern A. Zeeb 	 */
249*9af1bba4SBjoern A. Zeeb 	return min(tx, rx);
250*9af1bba4SBjoern A. Zeeb }
251*9af1bba4SBjoern A. Zeeb 
252*9af1bba4SBjoern A. Zeeb #define MAX_NSS_MCS(mcs_num, rx, tx) \
253*9af1bba4SBjoern A. Zeeb 	rs_fw_eht_max_nss((rx)->rx_tx_mcs ##mcs_num## _max_nss, \
254*9af1bba4SBjoern A. Zeeb 			  (tx)->rx_tx_mcs ##mcs_num## _max_nss)
255*9af1bba4SBjoern A. Zeeb 
256*9af1bba4SBjoern A. Zeeb static void rs_fw_set_eht_mcs_nss(__le16 ht_rates[][3],
257*9af1bba4SBjoern A. Zeeb 				  enum IWL_TLC_MCS_PER_BW bw,
258*9af1bba4SBjoern A. Zeeb 				  u8 max_nss, u16 mcs_msk)
259*9af1bba4SBjoern A. Zeeb {
260*9af1bba4SBjoern A. Zeeb 	if (max_nss >= 2)
261*9af1bba4SBjoern A. Zeeb 		ht_rates[IWL_TLC_NSS_2][bw] |= cpu_to_le16(mcs_msk);
262*9af1bba4SBjoern A. Zeeb 
263*9af1bba4SBjoern A. Zeeb 	if (max_nss >= 1)
264*9af1bba4SBjoern A. Zeeb 		ht_rates[IWL_TLC_NSS_1][bw] |= cpu_to_le16(mcs_msk);
265*9af1bba4SBjoern A. Zeeb }
266*9af1bba4SBjoern A. Zeeb 
267*9af1bba4SBjoern A. Zeeb static const
268*9af1bba4SBjoern A. Zeeb struct ieee80211_eht_mcs_nss_supp_bw *
269*9af1bba4SBjoern A. Zeeb rs_fw_rs_mcs2eht_mcs(enum IWL_TLC_MCS_PER_BW bw,
270*9af1bba4SBjoern A. Zeeb 		     const struct ieee80211_eht_mcs_nss_supp *eht_mcs)
271*9af1bba4SBjoern A. Zeeb {
272*9af1bba4SBjoern A. Zeeb 	switch (bw) {
273*9af1bba4SBjoern A. Zeeb 	case IWL_TLC_MCS_PER_BW_80:
274*9af1bba4SBjoern A. Zeeb 		return &eht_mcs->bw._80;
275*9af1bba4SBjoern A. Zeeb 	case IWL_TLC_MCS_PER_BW_160:
276*9af1bba4SBjoern A. Zeeb 		return &eht_mcs->bw._160;
277*9af1bba4SBjoern A. Zeeb 	case IWL_TLC_MCS_PER_BW_320:
278*9af1bba4SBjoern A. Zeeb 		return &eht_mcs->bw._320;
279*9af1bba4SBjoern A. Zeeb 	default:
280*9af1bba4SBjoern A. Zeeb 		return NULL;
281*9af1bba4SBjoern A. Zeeb 	}
282*9af1bba4SBjoern A. Zeeb }
283*9af1bba4SBjoern A. Zeeb 
284*9af1bba4SBjoern A. Zeeb static void
285*9af1bba4SBjoern A. Zeeb rs_fw_eht_set_enabled_rates(struct ieee80211_vif *vif,
286*9af1bba4SBjoern A. Zeeb 			    const struct ieee80211_link_sta *link_sta,
287*9af1bba4SBjoern A. Zeeb 			    const struct ieee80211_sta_he_cap *sband_he_cap,
288*9af1bba4SBjoern A. Zeeb 			    const struct ieee80211_sta_eht_cap *sband_eht_cap,
289*9af1bba4SBjoern A. Zeeb 			    struct iwl_tlc_config_cmd_v4 *cmd)
290*9af1bba4SBjoern A. Zeeb {
291*9af1bba4SBjoern A. Zeeb 	/* peer RX mcs capa */
292*9af1bba4SBjoern A. Zeeb 	const struct ieee80211_eht_mcs_nss_supp *eht_rx_mcs =
293*9af1bba4SBjoern A. Zeeb 		&link_sta->eht_cap.eht_mcs_nss_supp;
294*9af1bba4SBjoern A. Zeeb 	/* our TX mcs capa */
295*9af1bba4SBjoern A. Zeeb 	const struct ieee80211_eht_mcs_nss_supp *eht_tx_mcs =
296*9af1bba4SBjoern A. Zeeb 		&sband_eht_cap->eht_mcs_nss_supp;
297*9af1bba4SBjoern A. Zeeb 
298*9af1bba4SBjoern A. Zeeb 	enum IWL_TLC_MCS_PER_BW bw;
299*9af1bba4SBjoern A. Zeeb 	struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_rx_20;
300*9af1bba4SBjoern A. Zeeb 	struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_tx_20;
301*9af1bba4SBjoern A. Zeeb 
302*9af1bba4SBjoern A. Zeeb 	/* peer is 20Mhz only */
303*9af1bba4SBjoern A. Zeeb 	if (vif->type == NL80211_IFTYPE_AP &&
304*9af1bba4SBjoern A. Zeeb 	    !(link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
305*9af1bba4SBjoern A. Zeeb 	      IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
306*9af1bba4SBjoern A. Zeeb 		mcs_rx_20 = eht_rx_mcs->only_20mhz;
307*9af1bba4SBjoern A. Zeeb 	} else {
308*9af1bba4SBjoern A. Zeeb 		mcs_rx_20.rx_tx_mcs7_max_nss = eht_rx_mcs->bw._80.rx_tx_mcs9_max_nss;
309*9af1bba4SBjoern A. Zeeb 		mcs_rx_20.rx_tx_mcs9_max_nss = eht_rx_mcs->bw._80.rx_tx_mcs9_max_nss;
310*9af1bba4SBjoern A. Zeeb 		mcs_rx_20.rx_tx_mcs11_max_nss = eht_rx_mcs->bw._80.rx_tx_mcs11_max_nss;
311*9af1bba4SBjoern A. Zeeb 		mcs_rx_20.rx_tx_mcs13_max_nss = eht_rx_mcs->bw._80.rx_tx_mcs13_max_nss;
312*9af1bba4SBjoern A. Zeeb 	}
313*9af1bba4SBjoern A. Zeeb 
314*9af1bba4SBjoern A. Zeeb 	/* nic is 20Mhz only */
315*9af1bba4SBjoern A. Zeeb 	if (!(sband_he_cap->he_cap_elem.phy_cap_info[0] &
316*9af1bba4SBjoern A. Zeeb 	      IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
317*9af1bba4SBjoern A. Zeeb 		mcs_tx_20 = eht_tx_mcs->only_20mhz;
318*9af1bba4SBjoern A. Zeeb 	} else {
319*9af1bba4SBjoern A. Zeeb 		mcs_tx_20.rx_tx_mcs7_max_nss = eht_tx_mcs->bw._80.rx_tx_mcs9_max_nss;
320*9af1bba4SBjoern A. Zeeb 		mcs_tx_20.rx_tx_mcs9_max_nss = eht_tx_mcs->bw._80.rx_tx_mcs9_max_nss;
321*9af1bba4SBjoern A. Zeeb 		mcs_tx_20.rx_tx_mcs11_max_nss = eht_tx_mcs->bw._80.rx_tx_mcs11_max_nss;
322*9af1bba4SBjoern A. Zeeb 		mcs_tx_20.rx_tx_mcs13_max_nss = eht_tx_mcs->bw._80.rx_tx_mcs13_max_nss;
323*9af1bba4SBjoern A. Zeeb 	}
324*9af1bba4SBjoern A. Zeeb 
325*9af1bba4SBjoern A. Zeeb 	/* rates for 20/40/80 bw */
326*9af1bba4SBjoern A. Zeeb 	bw = IWL_TLC_MCS_PER_BW_80;
327*9af1bba4SBjoern A. Zeeb 	rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw,
328*9af1bba4SBjoern A. Zeeb 			      MAX_NSS_MCS(7, &mcs_rx_20, &mcs_tx_20), GENMASK(7, 0));
329*9af1bba4SBjoern A. Zeeb 	rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw,
330*9af1bba4SBjoern A. Zeeb 			      MAX_NSS_MCS(9, &mcs_rx_20, &mcs_tx_20), GENMASK(9, 8));
331*9af1bba4SBjoern A. Zeeb 	rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw,
332*9af1bba4SBjoern A. Zeeb 			      MAX_NSS_MCS(11, &mcs_rx_20, &mcs_tx_20), GENMASK(11, 10));
333*9af1bba4SBjoern A. Zeeb 	rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw,
334*9af1bba4SBjoern A. Zeeb 			      MAX_NSS_MCS(13, &mcs_rx_20, &mcs_tx_20), GENMASK(13, 12));
335*9af1bba4SBjoern A. Zeeb 
336*9af1bba4SBjoern A. Zeeb 	/* rate for 160/320 bw */
337*9af1bba4SBjoern A. Zeeb 	for (bw = IWL_TLC_MCS_PER_BW_160; bw <= IWL_TLC_MCS_PER_BW_320; bw++) {
338*9af1bba4SBjoern A. Zeeb 		const struct ieee80211_eht_mcs_nss_supp_bw *mcs_rx =
339*9af1bba4SBjoern A. Zeeb 			rs_fw_rs_mcs2eht_mcs(bw, eht_rx_mcs);
340*9af1bba4SBjoern A. Zeeb 		const struct ieee80211_eht_mcs_nss_supp_bw *mcs_tx =
341*9af1bba4SBjoern A. Zeeb 			rs_fw_rs_mcs2eht_mcs(bw, eht_tx_mcs);
342*9af1bba4SBjoern A. Zeeb 
343*9af1bba4SBjoern A. Zeeb 		/* got unsupported index for bw */
344*9af1bba4SBjoern A. Zeeb 		if (!mcs_rx || !mcs_tx)
345*9af1bba4SBjoern A. Zeeb 			continue;
346*9af1bba4SBjoern A. Zeeb 
347*9af1bba4SBjoern A. Zeeb 		/* break out if we don't support the bandwidth */
348*9af1bba4SBjoern A. Zeeb 		if (cmd->max_ch_width < (bw + IWL_TLC_MNG_CH_WIDTH_80MHZ))
349*9af1bba4SBjoern A. Zeeb 			break;
350*9af1bba4SBjoern A. Zeeb 
351*9af1bba4SBjoern A. Zeeb 		rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw,
352*9af1bba4SBjoern A. Zeeb 				      MAX_NSS_MCS(9, mcs_rx, mcs_tx), GENMASK(9, 0));
353*9af1bba4SBjoern A. Zeeb 		rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw,
354*9af1bba4SBjoern A. Zeeb 				      MAX_NSS_MCS(11, mcs_rx, mcs_tx), GENMASK(11, 10));
355*9af1bba4SBjoern A. Zeeb 		rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw,
356*9af1bba4SBjoern A. Zeeb 				      MAX_NSS_MCS(13, mcs_rx, mcs_tx), GENMASK(13, 12));
357*9af1bba4SBjoern A. Zeeb 	}
358*9af1bba4SBjoern A. Zeeb 
359*9af1bba4SBjoern A. Zeeb 	/* the station support only a single receive chain */
360*9af1bba4SBjoern A. Zeeb 	if (link_sta->smps_mode == IEEE80211_SMPS_STATIC ||
361*9af1bba4SBjoern A. Zeeb 	    link_sta->rx_nss < 2)
362*9af1bba4SBjoern A. Zeeb 		memset(cmd->ht_rates[IWL_TLC_NSS_2], 0,
363*9af1bba4SBjoern A. Zeeb 		       sizeof(cmd->ht_rates[IWL_TLC_NSS_2]));
364*9af1bba4SBjoern A. Zeeb }
365*9af1bba4SBjoern A. Zeeb 
366*9af1bba4SBjoern A. Zeeb static void rs_fw_set_supp_rates(struct ieee80211_vif *vif,
367*9af1bba4SBjoern A. Zeeb 				 struct ieee80211_link_sta *link_sta,
368bfcc09ddSBjoern A. Zeeb 				 struct ieee80211_supported_band *sband,
369*9af1bba4SBjoern A. Zeeb 				 const struct ieee80211_sta_he_cap *sband_he_cap,
370*9af1bba4SBjoern A. Zeeb 				 const struct ieee80211_sta_eht_cap *sband_eht_cap,
371d9836fb4SBjoern A. Zeeb 				 struct iwl_tlc_config_cmd_v4 *cmd)
372bfcc09ddSBjoern A. Zeeb {
373bfcc09ddSBjoern A. Zeeb 	int i;
374bfcc09ddSBjoern A. Zeeb 	u16 supp = 0;
375bfcc09ddSBjoern A. Zeeb 	unsigned long tmp; /* must be unsigned long for for_each_set_bit */
376*9af1bba4SBjoern A. Zeeb 	const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
377*9af1bba4SBjoern A. Zeeb 	const struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
378*9af1bba4SBjoern A. Zeeb 	const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
379bfcc09ddSBjoern A. Zeeb 
380bfcc09ddSBjoern A. Zeeb 	/* non HT rates */
381*9af1bba4SBjoern A. Zeeb 	tmp = link_sta->supp_rates[sband->band];
382bfcc09ddSBjoern A. Zeeb 	for_each_set_bit(i, &tmp, BITS_PER_LONG)
383bfcc09ddSBjoern A. Zeeb 		supp |= BIT(sband->bitrates[i].hw_value);
384bfcc09ddSBjoern A. Zeeb 
385bfcc09ddSBjoern A. Zeeb 	cmd->non_ht_rates = cpu_to_le16(supp);
386bfcc09ddSBjoern A. Zeeb 	cmd->mode = IWL_TLC_MNG_MODE_NON_HT;
387bfcc09ddSBjoern A. Zeeb 
388bfcc09ddSBjoern A. Zeeb 	/* HT/VHT rates */
389*9af1bba4SBjoern A. Zeeb 	if (link_sta->eht_cap.has_eht && sband_he_cap && sband_eht_cap) {
390*9af1bba4SBjoern A. Zeeb 		cmd->mode = IWL_TLC_MNG_MODE_EHT;
391*9af1bba4SBjoern A. Zeeb 		rs_fw_eht_set_enabled_rates(vif, link_sta, sband_he_cap,
392*9af1bba4SBjoern A. Zeeb 					    sband_eht_cap, cmd);
393*9af1bba4SBjoern A. Zeeb 	} else if (he_cap->has_he && sband_he_cap) {
394bfcc09ddSBjoern A. Zeeb 		cmd->mode = IWL_TLC_MNG_MODE_HE;
395*9af1bba4SBjoern A. Zeeb 		rs_fw_he_set_enabled_rates(link_sta, sband_he_cap, cmd);
396bfcc09ddSBjoern A. Zeeb 	} else if (vht_cap->vht_supported) {
397bfcc09ddSBjoern A. Zeeb 		cmd->mode = IWL_TLC_MNG_MODE_VHT;
398*9af1bba4SBjoern A. Zeeb 		rs_fw_vht_set_enabled_rates(link_sta, vht_cap, cmd);
399bfcc09ddSBjoern A. Zeeb 	} else if (ht_cap->ht_supported) {
400bfcc09ddSBjoern A. Zeeb 		cmd->mode = IWL_TLC_MNG_MODE_HT;
401d9836fb4SBjoern A. Zeeb 		cmd->ht_rates[IWL_TLC_NSS_1][IWL_TLC_MCS_PER_BW_80] =
402bfcc09ddSBjoern A. Zeeb 			cpu_to_le16(ht_cap->mcs.rx_mask[0]);
403bfcc09ddSBjoern A. Zeeb 
404bfcc09ddSBjoern A. Zeeb 		/* the station support only a single receive chain */
405*9af1bba4SBjoern A. Zeeb 		if (link_sta->smps_mode == IEEE80211_SMPS_STATIC)
406d9836fb4SBjoern A. Zeeb 			cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] =
407bfcc09ddSBjoern A. Zeeb 				0;
408bfcc09ddSBjoern A. Zeeb 		else
409d9836fb4SBjoern A. Zeeb 			cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] =
410bfcc09ddSBjoern A. Zeeb 				cpu_to_le16(ht_cap->mcs.rx_mask[1]);
411bfcc09ddSBjoern A. Zeeb 	}
412bfcc09ddSBjoern A. Zeeb }
413bfcc09ddSBjoern A. Zeeb 
414bfcc09ddSBjoern A. Zeeb void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm,
415bfcc09ddSBjoern A. Zeeb 			      struct iwl_rx_cmd_buffer *rxb)
416bfcc09ddSBjoern A. Zeeb {
417bfcc09ddSBjoern A. Zeeb 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
418bfcc09ddSBjoern A. Zeeb 	struct iwl_tlc_update_notif *notif;
419bfcc09ddSBjoern A. Zeeb 	struct ieee80211_sta *sta;
420*9af1bba4SBjoern A. Zeeb 	struct ieee80211_link_sta *link_sta;
421bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_sta *mvmsta;
422*9af1bba4SBjoern A. Zeeb 	struct iwl_mvm_link_sta *mvm_link_sta;
423bfcc09ddSBjoern A. Zeeb 	struct iwl_lq_sta_rs_fw *lq_sta;
424bfcc09ddSBjoern A. Zeeb 	u32 flags;
425bfcc09ddSBjoern A. Zeeb 
426bfcc09ddSBjoern A. Zeeb 	rcu_read_lock();
427bfcc09ddSBjoern A. Zeeb 
428bfcc09ddSBjoern A. Zeeb 	notif = (void *)pkt->data;
429*9af1bba4SBjoern A. Zeeb 	link_sta = rcu_dereference(mvm->fw_id_to_link_sta[notif->sta_id]);
430bfcc09ddSBjoern A. Zeeb 	sta = rcu_dereference(mvm->fw_id_to_mac_id[notif->sta_id]);
431*9af1bba4SBjoern A. Zeeb 	if (IS_ERR_OR_NULL(sta) || !link_sta) {
432d9836fb4SBjoern A. Zeeb 		/* can happen in remove station flow where mvm removed internally
433d9836fb4SBjoern A. Zeeb 		 * the station before removing from FW
434d9836fb4SBjoern A. Zeeb 		 */
435d9836fb4SBjoern A. Zeeb 		IWL_DEBUG_RATE(mvm,
436d9836fb4SBjoern A. Zeeb 			       "Invalid mvm RCU pointer for sta id (%d) in TLC notification\n",
437bfcc09ddSBjoern A. Zeeb 			       notif->sta_id);
438bfcc09ddSBjoern A. Zeeb 		goto out;
439bfcc09ddSBjoern A. Zeeb 	}
440bfcc09ddSBjoern A. Zeeb 
441bfcc09ddSBjoern A. Zeeb 	mvmsta = iwl_mvm_sta_from_mac80211(sta);
442bfcc09ddSBjoern A. Zeeb 
443bfcc09ddSBjoern A. Zeeb 	if (!mvmsta) {
444bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Invalid sta id (%d) in FW TLC notification\n",
445bfcc09ddSBjoern A. Zeeb 			notif->sta_id);
446bfcc09ddSBjoern A. Zeeb 		goto out;
447bfcc09ddSBjoern A. Zeeb 	}
448bfcc09ddSBjoern A. Zeeb 
449bfcc09ddSBjoern A. Zeeb 	flags = le32_to_cpu(notif->flags);
450bfcc09ddSBjoern A. Zeeb 
451*9af1bba4SBjoern A. Zeeb 	mvm_link_sta = rcu_dereference(mvmsta->link[link_sta->link_id]);
452*9af1bba4SBjoern A. Zeeb 	if (!mvm_link_sta) {
453*9af1bba4SBjoern A. Zeeb 		IWL_DEBUG_RATE(mvm,
454*9af1bba4SBjoern A. Zeeb 			       "Invalid mvmsta RCU pointer for link (%d) of  sta id (%d) in TLC notification\n",
455*9af1bba4SBjoern A. Zeeb 			       link_sta->link_id, notif->sta_id);
456*9af1bba4SBjoern A. Zeeb 		goto out;
457*9af1bba4SBjoern A. Zeeb 	}
458*9af1bba4SBjoern A. Zeeb 	lq_sta = &mvm_link_sta->lq_sta.rs_fw;
459bfcc09ddSBjoern A. Zeeb 
460bfcc09ddSBjoern A. Zeeb 	if (flags & IWL_TLC_NOTIF_FLAG_RATE) {
461bfcc09ddSBjoern A. Zeeb 		char pretty_rate[100];
462bfcc09ddSBjoern A. Zeeb 
463bfcc09ddSBjoern A. Zeeb 		if (iwl_fw_lookup_notif_ver(mvm->fw, DATA_PATH_GROUP,
464bfcc09ddSBjoern A. Zeeb 					    TLC_MNG_UPDATE_NOTIF, 0) < 3) {
465d9836fb4SBjoern A. Zeeb 			rs_pretty_print_rate_v1(pretty_rate,
466d9836fb4SBjoern A. Zeeb 						sizeof(pretty_rate),
467bfcc09ddSBjoern A. Zeeb 						le32_to_cpu(notif->rate));
468bfcc09ddSBjoern A. Zeeb 			IWL_DEBUG_RATE(mvm,
469bfcc09ddSBjoern A. Zeeb 				       "Got rate in old format. Rate: %s. Converting.\n",
470bfcc09ddSBjoern A. Zeeb 				       pretty_rate);
471bfcc09ddSBjoern A. Zeeb 			lq_sta->last_rate_n_flags =
472bfcc09ddSBjoern A. Zeeb 				iwl_new_rate_from_v1(le32_to_cpu(notif->rate));
473bfcc09ddSBjoern A. Zeeb 		} else {
474bfcc09ddSBjoern A. Zeeb 			lq_sta->last_rate_n_flags = le32_to_cpu(notif->rate);
475bfcc09ddSBjoern A. Zeeb 		}
476bfcc09ddSBjoern A. Zeeb 		rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate),
477bfcc09ddSBjoern A. Zeeb 				     lq_sta->last_rate_n_flags);
478bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_RATE(mvm, "new rate: %s\n", pretty_rate);
479bfcc09ddSBjoern A. Zeeb 	}
480bfcc09ddSBjoern A. Zeeb 
481*9af1bba4SBjoern A. Zeeb 	if (flags & IWL_TLC_NOTIF_FLAG_AMSDU && !mvm_link_sta->orig_amsdu_len) {
482bfcc09ddSBjoern A. Zeeb 		u16 size = le32_to_cpu(notif->amsdu_size);
483bfcc09ddSBjoern A. Zeeb 		int i;
484bfcc09ddSBjoern A. Zeeb 
485*9af1bba4SBjoern A. Zeeb 		if (link_sta->agg.max_amsdu_len < size) {
486bfcc09ddSBjoern A. Zeeb 			/*
487*9af1bba4SBjoern A. Zeeb 			 * In debug link_sta->agg.max_amsdu_len < size
488bfcc09ddSBjoern A. Zeeb 			 * so also check with orig_amsdu_len which holds the
489bfcc09ddSBjoern A. Zeeb 			 * original data before debugfs changed the value
490bfcc09ddSBjoern A. Zeeb 			 */
491*9af1bba4SBjoern A. Zeeb 			WARN_ON(mvm_link_sta->orig_amsdu_len < size);
492bfcc09ddSBjoern A. Zeeb 			goto out;
493bfcc09ddSBjoern A. Zeeb 		}
494bfcc09ddSBjoern A. Zeeb 
495bfcc09ddSBjoern A. Zeeb 		mvmsta->amsdu_enabled = le32_to_cpu(notif->amsdu_enabled);
496bfcc09ddSBjoern A. Zeeb 		mvmsta->max_amsdu_len = size;
497*9af1bba4SBjoern A. Zeeb 		link_sta->agg.max_rc_amsdu_len = mvmsta->max_amsdu_len;
498bfcc09ddSBjoern A. Zeeb 
499bfcc09ddSBjoern A. Zeeb 		for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
500bfcc09ddSBjoern A. Zeeb 			if (mvmsta->amsdu_enabled & BIT(i))
501*9af1bba4SBjoern A. Zeeb 				link_sta->agg.max_tid_amsdu_len[i] =
502bfcc09ddSBjoern A. Zeeb 					iwl_mvm_max_amsdu_size(mvm, sta, i);
503bfcc09ddSBjoern A. Zeeb 			else
504bfcc09ddSBjoern A. Zeeb 				/*
505bfcc09ddSBjoern A. Zeeb 				 * Not so elegant, but this will effectively
506bfcc09ddSBjoern A. Zeeb 				 * prevent AMSDU on this TID
507bfcc09ddSBjoern A. Zeeb 				 */
508*9af1bba4SBjoern A. Zeeb 				link_sta->agg.max_tid_amsdu_len[i] = 1;
509bfcc09ddSBjoern A. Zeeb 		}
510bfcc09ddSBjoern A. Zeeb 
511bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_RATE(mvm,
512bfcc09ddSBjoern A. Zeeb 			       "AMSDU update. AMSDU size: %d, AMSDU selected size: %d, AMSDU TID bitmap 0x%X\n",
513bfcc09ddSBjoern A. Zeeb 			       le32_to_cpu(notif->amsdu_size), size,
514bfcc09ddSBjoern A. Zeeb 			       mvmsta->amsdu_enabled);
515bfcc09ddSBjoern A. Zeeb 	}
516bfcc09ddSBjoern A. Zeeb out:
517bfcc09ddSBjoern A. Zeeb 	rcu_read_unlock();
518bfcc09ddSBjoern A. Zeeb }
519bfcc09ddSBjoern A. Zeeb 
520*9af1bba4SBjoern A. Zeeb u16 rs_fw_get_max_amsdu_len(struct ieee80211_sta *sta,
521*9af1bba4SBjoern A. Zeeb 			    struct ieee80211_bss_conf *link_conf,
522*9af1bba4SBjoern A. Zeeb 			    struct ieee80211_link_sta *link_sta)
523bfcc09ddSBjoern A. Zeeb {
524*9af1bba4SBjoern A. Zeeb #if defined(__linux__)
525bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
526*9af1bba4SBjoern A. Zeeb #endif
527*9af1bba4SBjoern A. Zeeb 	const struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
528*9af1bba4SBjoern A. Zeeb 	const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
529*9af1bba4SBjoern A. Zeeb 	const struct ieee80211_sta_eht_cap *eht_cap = &link_sta->eht_cap;
530bfcc09ddSBjoern A. Zeeb 
531*9af1bba4SBjoern A. Zeeb 	if (WARN_ON_ONCE(!link_conf->chandef.chan))
532*9af1bba4SBjoern A. Zeeb 		return IEEE80211_MAX_MPDU_LEN_VHT_3895;
533*9af1bba4SBjoern A. Zeeb 
534*9af1bba4SBjoern A. Zeeb 	if (link_conf->chandef.chan->band == NL80211_BAND_6GHZ) {
535*9af1bba4SBjoern A. Zeeb 		switch (le16_get_bits(link_sta->he_6ghz_capa.capa,
536bfcc09ddSBjoern A. Zeeb 				      IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN)) {
537bfcc09ddSBjoern A. Zeeb 		case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454:
538bfcc09ddSBjoern A. Zeeb 			return IEEE80211_MAX_MPDU_LEN_VHT_11454;
539bfcc09ddSBjoern A. Zeeb 		case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991:
540bfcc09ddSBjoern A. Zeeb 			return IEEE80211_MAX_MPDU_LEN_VHT_7991;
541bfcc09ddSBjoern A. Zeeb 		default:
542bfcc09ddSBjoern A. Zeeb 			return IEEE80211_MAX_MPDU_LEN_VHT_3895;
543bfcc09ddSBjoern A. Zeeb 		}
544*9af1bba4SBjoern A. Zeeb 	} else if (link_conf->chandef.chan->band == NL80211_BAND_2GHZ &&
545*9af1bba4SBjoern A. Zeeb 		   eht_cap->has_eht) {
546*9af1bba4SBjoern A. Zeeb 		switch (u8_get_bits(eht_cap->eht_cap_elem.mac_cap_info[0],
547*9af1bba4SBjoern A. Zeeb 				    IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK)) {
548*9af1bba4SBjoern A. Zeeb 		case IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454:
549*9af1bba4SBjoern A. Zeeb 			return IEEE80211_MAX_MPDU_LEN_VHT_11454;
550*9af1bba4SBjoern A. Zeeb 		case IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_7991:
551*9af1bba4SBjoern A. Zeeb 			return IEEE80211_MAX_MPDU_LEN_VHT_7991;
552*9af1bba4SBjoern A. Zeeb 		default:
553*9af1bba4SBjoern A. Zeeb 			return IEEE80211_MAX_MPDU_LEN_VHT_3895;
554*9af1bba4SBjoern A. Zeeb 		}
555*9af1bba4SBjoern A. Zeeb 	} else if (vht_cap->vht_supported) {
556bfcc09ddSBjoern A. Zeeb 		switch (vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK) {
557bfcc09ddSBjoern A. Zeeb 		case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454:
558bfcc09ddSBjoern A. Zeeb 			return IEEE80211_MAX_MPDU_LEN_VHT_11454;
559bfcc09ddSBjoern A. Zeeb 		case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991:
560bfcc09ddSBjoern A. Zeeb 			return IEEE80211_MAX_MPDU_LEN_VHT_7991;
561bfcc09ddSBjoern A. Zeeb 		default:
562bfcc09ddSBjoern A. Zeeb 			return IEEE80211_MAX_MPDU_LEN_VHT_3895;
563bfcc09ddSBjoern A. Zeeb 		}
564bfcc09ddSBjoern A. Zeeb 	} else if (ht_cap->ht_supported) {
565bfcc09ddSBjoern A. Zeeb 		if (ht_cap->cap & IEEE80211_HT_CAP_MAX_AMSDU)
566bfcc09ddSBjoern A. Zeeb 			/*
567bfcc09ddSBjoern A. Zeeb 			 * agg is offloaded so we need to assume that agg
568bfcc09ddSBjoern A. Zeeb 			 * are enabled and max mpdu in ampdu is 4095
569bfcc09ddSBjoern A. Zeeb 			 * (spec 802.11-2016 9.3.2.1)
570bfcc09ddSBjoern A. Zeeb 			 */
571bfcc09ddSBjoern A. Zeeb 			return IEEE80211_MAX_MPDU_LEN_HT_BA;
572bfcc09ddSBjoern A. Zeeb 		else
573bfcc09ddSBjoern A. Zeeb 			return IEEE80211_MAX_MPDU_LEN_HT_3839;
574bfcc09ddSBjoern A. Zeeb 	}
575bfcc09ddSBjoern A. Zeeb 
576bfcc09ddSBjoern A. Zeeb 	/* in legacy mode no amsdu is enabled so return zero */
577bfcc09ddSBjoern A. Zeeb 	return 0;
578bfcc09ddSBjoern A. Zeeb }
579bfcc09ddSBjoern A. Zeeb 
580*9af1bba4SBjoern A. Zeeb void iwl_mvm_rs_fw_rate_init(struct iwl_mvm *mvm,
581*9af1bba4SBjoern A. Zeeb 			     struct ieee80211_vif *vif,
582*9af1bba4SBjoern A. Zeeb 			     struct ieee80211_sta *sta,
583*9af1bba4SBjoern A. Zeeb 			     struct ieee80211_bss_conf *link_conf,
584*9af1bba4SBjoern A. Zeeb 			     struct ieee80211_link_sta *link_sta,
585*9af1bba4SBjoern A. Zeeb 			     enum nl80211_band band)
586bfcc09ddSBjoern A. Zeeb {
587bfcc09ddSBjoern A. Zeeb 	struct ieee80211_hw *hw = mvm->hw;
588bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
589d9836fb4SBjoern A. Zeeb 	u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, TLC_MNG_CONFIG_CMD);
590bfcc09ddSBjoern A. Zeeb 	struct ieee80211_supported_band *sband = hw->wiphy->bands[band];
591*9af1bba4SBjoern A. Zeeb 	u16 max_amsdu_len = rs_fw_get_max_amsdu_len(sta, link_conf, link_sta);
592*9af1bba4SBjoern A. Zeeb 	const struct ieee80211_sta_he_cap *sband_he_cap =
593*9af1bba4SBjoern A. Zeeb 		ieee80211_get_he_iftype_cap_vif(sband, vif);
594*9af1bba4SBjoern A. Zeeb 	const struct ieee80211_sta_eht_cap *sband_eht_cap =
595*9af1bba4SBjoern A. Zeeb 		ieee80211_get_eht_iftype_cap_vif(sband, vif);
596*9af1bba4SBjoern A. Zeeb 	struct iwl_mvm_link_sta *mvm_link_sta;
597*9af1bba4SBjoern A. Zeeb 	struct iwl_lq_sta_rs_fw *lq_sta;
598d9836fb4SBjoern A. Zeeb 	struct iwl_tlc_config_cmd_v4 cfg_cmd = {
599*9af1bba4SBjoern A. Zeeb 		.max_ch_width = mvmsta->authorized ?
600*9af1bba4SBjoern A. Zeeb 			rs_fw_bw_from_sta_bw(link_sta) : IWL_TLC_MNG_CH_WIDTH_20MHZ,
601*9af1bba4SBjoern A. Zeeb 		.flags = cpu_to_le16(rs_fw_get_config_flags(mvm, vif, link_sta,
602*9af1bba4SBjoern A. Zeeb 							    sband_he_cap)),
603bfcc09ddSBjoern A. Zeeb 		.chains = rs_fw_set_active_chains(iwl_mvm_get_valid_tx_ant(mvm)),
604*9af1bba4SBjoern A. Zeeb 		.sgi_ch_width_supp = rs_fw_sgi_cw_support(link_sta),
605d9836fb4SBjoern A. Zeeb 		.max_mpdu_len = iwl_mvm_is_csum_supported(mvm) ?
606d9836fb4SBjoern A. Zeeb 				cpu_to_le16(max_amsdu_len) : 0,
607bfcc09ddSBjoern A. Zeeb 	};
608*9af1bba4SBjoern A. Zeeb 	unsigned int link_id = link_conf->link_id;
609d9836fb4SBjoern A. Zeeb 	int cmd_ver;
610*9af1bba4SBjoern A. Zeeb 	int ret;
611bfcc09ddSBjoern A. Zeeb 
612*9af1bba4SBjoern A. Zeeb 	/* Enable external EHT LTF only for GL device and if there's
613*9af1bba4SBjoern A. Zeeb 	 * mutual support by AP and client
614*9af1bba4SBjoern A. Zeeb 	 */
615*9af1bba4SBjoern A. Zeeb 	if (CSR_HW_REV_TYPE(mvm->trans->hw_rev) == IWL_CFG_MAC_TYPE_GL &&
616*9af1bba4SBjoern A. Zeeb 	    sband_eht_cap &&
617*9af1bba4SBjoern A. Zeeb 	    sband_eht_cap->eht_cap_elem.phy_cap_info[5] &
618*9af1bba4SBjoern A. Zeeb 		IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF &&
619*9af1bba4SBjoern A. Zeeb 	    link_sta->eht_cap.has_eht &&
620*9af1bba4SBjoern A. Zeeb 	    link_sta->eht_cap.eht_cap_elem.phy_cap_info[5] &
621*9af1bba4SBjoern A. Zeeb 	    IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF) {
622*9af1bba4SBjoern A. Zeeb 		IWL_DEBUG_RATE(mvm, "Set support for Extra EHT LTF\n");
623*9af1bba4SBjoern A. Zeeb 		cfg_cmd.flags |=
624*9af1bba4SBjoern A. Zeeb 			cpu_to_le16(IWL_TLC_MNG_CFG_FLAGS_EHT_EXTRA_LTF_MSK);
625*9af1bba4SBjoern A. Zeeb 	}
626*9af1bba4SBjoern A. Zeeb 
627*9af1bba4SBjoern A. Zeeb 	rcu_read_lock();
628*9af1bba4SBjoern A. Zeeb 	mvm_link_sta = rcu_dereference(mvmsta->link[link_id]);
629*9af1bba4SBjoern A. Zeeb 	if (WARN_ON_ONCE(!mvm_link_sta)) {
630*9af1bba4SBjoern A. Zeeb 		rcu_read_unlock();
631*9af1bba4SBjoern A. Zeeb 		return;
632*9af1bba4SBjoern A. Zeeb 	}
633*9af1bba4SBjoern A. Zeeb 
634*9af1bba4SBjoern A. Zeeb 	cfg_cmd.sta_id = mvm_link_sta->sta_id;
635*9af1bba4SBjoern A. Zeeb 
636*9af1bba4SBjoern A. Zeeb 	lq_sta = &mvm_link_sta->lq_sta.rs_fw;
637bfcc09ddSBjoern A. Zeeb 	memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers));
638bfcc09ddSBjoern A. Zeeb 
639*9af1bba4SBjoern A. Zeeb 	rcu_read_unlock();
640*9af1bba4SBjoern A. Zeeb 
641bfcc09ddSBjoern A. Zeeb #ifdef CONFIG_IWLWIFI_DEBUGFS
642bfcc09ddSBjoern A. Zeeb 	iwl_mvm_reset_frame_stats(mvm);
643bfcc09ddSBjoern A. Zeeb #endif
644*9af1bba4SBjoern A. Zeeb 	rs_fw_set_supp_rates(vif, link_sta, sband,
645*9af1bba4SBjoern A. Zeeb 			     sband_he_cap, sband_eht_cap,
646*9af1bba4SBjoern A. Zeeb 			     &cfg_cmd);
647bfcc09ddSBjoern A. Zeeb 
648bfcc09ddSBjoern A. Zeeb 	/*
649bfcc09ddSBjoern A. Zeeb 	 * since TLC offload works with one mode we can assume
650bfcc09ddSBjoern A. Zeeb 	 * that only vht/ht is used and also set it as station max amsdu
651bfcc09ddSBjoern A. Zeeb 	 */
652*9af1bba4SBjoern A. Zeeb 	sta->deflink.agg.max_amsdu_len = max_amsdu_len;
653bfcc09ddSBjoern A. Zeeb 
654d9836fb4SBjoern A. Zeeb 	cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
655d9836fb4SBjoern A. Zeeb 					WIDE_ID(DATA_PATH_GROUP,
656d9836fb4SBjoern A. Zeeb 						TLC_MNG_CONFIG_CMD),
657d9836fb4SBjoern A. Zeeb 					0);
658d9836fb4SBjoern A. Zeeb 	IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, sta_id=%d, max_ch_width=%d, mode=%d\n",
659d9836fb4SBjoern A. Zeeb 		       cfg_cmd.sta_id, cfg_cmd.max_ch_width, cfg_cmd.mode);
660d9836fb4SBjoern A. Zeeb 	IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, chains=0x%X, ch_wid_supp=%d, flags=0x%X\n",
661d9836fb4SBjoern A. Zeeb 		       cfg_cmd.chains, cfg_cmd.sgi_ch_width_supp, cfg_cmd.flags);
662d9836fb4SBjoern A. Zeeb 	IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, mpdu_len=%d, no_ht_rate=0x%X, tx_op=%d\n",
663d9836fb4SBjoern A. Zeeb 		       cfg_cmd.max_mpdu_len, cfg_cmd.non_ht_rates, cfg_cmd.max_tx_op);
664d9836fb4SBjoern A. Zeeb 	IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, ht_rate[0][0]=0x%X, ht_rate[1][0]=0x%X\n",
665d9836fb4SBjoern A. Zeeb 		       cfg_cmd.ht_rates[0][0], cfg_cmd.ht_rates[1][0]);
666d9836fb4SBjoern A. Zeeb 	IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, ht_rate[0][1]=0x%X, ht_rate[1][1]=0x%X\n",
667d9836fb4SBjoern A. Zeeb 		       cfg_cmd.ht_rates[0][1], cfg_cmd.ht_rates[1][1]);
668d9836fb4SBjoern A. Zeeb 	IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, ht_rate[0][2]=0x%X, ht_rate[1][2]=0x%X\n",
669d9836fb4SBjoern A. Zeeb 		       cfg_cmd.ht_rates[0][2], cfg_cmd.ht_rates[1][2]);
670d9836fb4SBjoern A. Zeeb 	if (cmd_ver == 4) {
671d9836fb4SBjoern A. Zeeb 		ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_ASYNC,
672d9836fb4SBjoern A. Zeeb 					   sizeof(cfg_cmd), &cfg_cmd);
673d9836fb4SBjoern A. Zeeb 	} else if (cmd_ver < 4) {
674d9836fb4SBjoern A. Zeeb 		struct iwl_tlc_config_cmd_v3 cfg_cmd_v3 = {
675d9836fb4SBjoern A. Zeeb 			.sta_id = cfg_cmd.sta_id,
676d9836fb4SBjoern A. Zeeb 			.max_ch_width = cfg_cmd.max_ch_width,
677d9836fb4SBjoern A. Zeeb 			.mode = cfg_cmd.mode,
678d9836fb4SBjoern A. Zeeb 			.chains = cfg_cmd.chains,
679d9836fb4SBjoern A. Zeeb 			.amsdu = !!cfg_cmd.max_mpdu_len,
680d9836fb4SBjoern A. Zeeb 			.flags = cfg_cmd.flags,
681d9836fb4SBjoern A. Zeeb 			.non_ht_rates = cfg_cmd.non_ht_rates,
682d9836fb4SBjoern A. Zeeb 			.ht_rates[0][0] = cfg_cmd.ht_rates[0][0],
683d9836fb4SBjoern A. Zeeb 			.ht_rates[0][1] = cfg_cmd.ht_rates[0][1],
684d9836fb4SBjoern A. Zeeb 			.ht_rates[1][0] = cfg_cmd.ht_rates[1][0],
685d9836fb4SBjoern A. Zeeb 			.ht_rates[1][1] = cfg_cmd.ht_rates[1][1],
686d9836fb4SBjoern A. Zeeb 			.sgi_ch_width_supp = cfg_cmd.sgi_ch_width_supp,
687d9836fb4SBjoern A. Zeeb 			.max_mpdu_len = cfg_cmd.max_mpdu_len,
688d9836fb4SBjoern A. Zeeb 		};
689d9836fb4SBjoern A. Zeeb 
690d9836fb4SBjoern A. Zeeb 		u16 cmd_size = sizeof(cfg_cmd_v3);
691d9836fb4SBjoern A. Zeeb 
692d9836fb4SBjoern A. Zeeb 		/* In old versions of the API the struct is 4 bytes smaller */
693d9836fb4SBjoern A. Zeeb 		if (iwl_fw_lookup_cmd_ver(mvm->fw,
694d9836fb4SBjoern A. Zeeb 					  WIDE_ID(DATA_PATH_GROUP,
695d9836fb4SBjoern A. Zeeb 						  TLC_MNG_CONFIG_CMD), 0) < 3)
696d9836fb4SBjoern A. Zeeb 			cmd_size -= 4;
697d9836fb4SBjoern A. Zeeb 
698bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_ASYNC, cmd_size,
699d9836fb4SBjoern A. Zeeb 					   &cfg_cmd_v3);
700d9836fb4SBjoern A. Zeeb 	} else {
701d9836fb4SBjoern A. Zeeb 		ret = -EINVAL;
702d9836fb4SBjoern A. Zeeb 	}
703d9836fb4SBjoern A. Zeeb 
704bfcc09ddSBjoern A. Zeeb 	if (ret)
705bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to send rate scale config (%d)\n", ret);
706bfcc09ddSBjoern A. Zeeb }
707bfcc09ddSBjoern A. Zeeb 
708bfcc09ddSBjoern A. Zeeb int rs_fw_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
709bfcc09ddSBjoern A. Zeeb 			bool enable)
710bfcc09ddSBjoern A. Zeeb {
711bfcc09ddSBjoern A. Zeeb 	/* TODO: need to introduce a new FW cmd since LQ cmd is not relevant */
712bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_RATE(mvm, "tx protection - not implemented yet.\n");
713bfcc09ddSBjoern A. Zeeb 	return 0;
714bfcc09ddSBjoern A. Zeeb }
715bfcc09ddSBjoern A. Zeeb 
716*9af1bba4SBjoern A. Zeeb void iwl_mvm_rs_add_sta_link(struct iwl_mvm *mvm,
717*9af1bba4SBjoern A. Zeeb 			     struct iwl_mvm_link_sta *link_sta)
718bfcc09ddSBjoern A. Zeeb {
719*9af1bba4SBjoern A. Zeeb 	struct iwl_lq_sta_rs_fw *lq_sta;
720bfcc09ddSBjoern A. Zeeb 
721*9af1bba4SBjoern A. Zeeb 	lq_sta = &link_sta->lq_sta.rs_fw;
722bfcc09ddSBjoern A. Zeeb 
723bfcc09ddSBjoern A. Zeeb 	lq_sta->pers.drv = mvm;
724*9af1bba4SBjoern A. Zeeb 	lq_sta->pers.sta_id = link_sta->sta_id;
725bfcc09ddSBjoern A. Zeeb 	lq_sta->pers.chains = 0;
726*9af1bba4SBjoern A. Zeeb 	memset(lq_sta->pers.chain_signal, 0,
727*9af1bba4SBjoern A. Zeeb 	       sizeof(lq_sta->pers.chain_signal));
728bfcc09ddSBjoern A. Zeeb 	lq_sta->pers.last_rssi = S8_MIN;
729bfcc09ddSBjoern A. Zeeb 	lq_sta->last_rate_n_flags = 0;
730bfcc09ddSBjoern A. Zeeb 
731bfcc09ddSBjoern A. Zeeb #ifdef CONFIG_MAC80211_DEBUGFS
732bfcc09ddSBjoern A. Zeeb 	lq_sta->pers.dbg_fixed_rate = 0;
733bfcc09ddSBjoern A. Zeeb #endif
734bfcc09ddSBjoern A. Zeeb }
735*9af1bba4SBjoern A. Zeeb 
736*9af1bba4SBjoern A. Zeeb void iwl_mvm_rs_add_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta)
737*9af1bba4SBjoern A. Zeeb {
738*9af1bba4SBjoern A. Zeeb 	unsigned int link_id;
739*9af1bba4SBjoern A. Zeeb 
740*9af1bba4SBjoern A. Zeeb 	IWL_DEBUG_RATE(mvm, "create station rate scale window\n");
741*9af1bba4SBjoern A. Zeeb 
742*9af1bba4SBjoern A. Zeeb 	for (link_id = 0; link_id < ARRAY_SIZE(mvmsta->link); link_id++) {
743*9af1bba4SBjoern A. Zeeb 		struct iwl_mvm_link_sta *link =
744*9af1bba4SBjoern A. Zeeb 			rcu_dereference_protected(mvmsta->link[link_id],
745*9af1bba4SBjoern A. Zeeb 						  lockdep_is_held(&mvm->mutex));
746*9af1bba4SBjoern A. Zeeb 		if (!link)
747*9af1bba4SBjoern A. Zeeb 			continue;
748*9af1bba4SBjoern A. Zeeb 
749*9af1bba4SBjoern A. Zeeb 		iwl_mvm_rs_add_sta_link(mvm, link);
750*9af1bba4SBjoern A. Zeeb 	}
751*9af1bba4SBjoern A. Zeeb }
752