18c2ecf20Sopenharmony_ci/****************************************************************************** 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 48c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright(c) 2017 Intel Deutschland GmbH 98c2ecf20Sopenharmony_ci * Copyright(c) 2018 - 2020 Intel Corporation 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 128c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 138c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 168c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 178c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 188c2ecf20Sopenharmony_ci * General Public License for more details. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * The full GNU General Public License is included in this distribution 218c2ecf20Sopenharmony_ci * in the file called COPYING. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Contact Information: 248c2ecf20Sopenharmony_ci * Intel Linux Wireless <linuxwifi@intel.com> 258c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * BSD LICENSE 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * Copyright(c) 2017 Intel Deutschland GmbH 308c2ecf20Sopenharmony_ci * Copyright(c) 2018 - 2020 Intel Corporation 318c2ecf20Sopenharmony_ci * All rights reserved. 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 348c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 358c2ecf20Sopenharmony_ci * are met: 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * * Redistributions of source code must retain the above copyright 388c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 398c2ecf20Sopenharmony_ci * * Redistributions in binary form must reproduce the above copyright 408c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 418c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 428c2ecf20Sopenharmony_ci * distribution. 438c2ecf20Sopenharmony_ci * * Neither the name Intel Corporation nor the names of its 448c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 458c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 488c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 498c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 508c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 518c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 528c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 538c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 548c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 558c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 568c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 578c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci *****************************************************************************/ 608c2ecf20Sopenharmony_ci#include "rs.h" 618c2ecf20Sopenharmony_ci#include "fw-api.h" 628c2ecf20Sopenharmony_ci#include "sta.h" 638c2ecf20Sopenharmony_ci#include "iwl-op-mode.h" 648c2ecf20Sopenharmony_ci#include "mvm.h" 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic u8 rs_fw_bw_from_sta_bw(struct ieee80211_sta *sta) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci switch (sta->bandwidth) { 698c2ecf20Sopenharmony_ci case IEEE80211_STA_RX_BW_160: 708c2ecf20Sopenharmony_ci return IWL_TLC_MNG_CH_WIDTH_160MHZ; 718c2ecf20Sopenharmony_ci case IEEE80211_STA_RX_BW_80: 728c2ecf20Sopenharmony_ci return IWL_TLC_MNG_CH_WIDTH_80MHZ; 738c2ecf20Sopenharmony_ci case IEEE80211_STA_RX_BW_40: 748c2ecf20Sopenharmony_ci return IWL_TLC_MNG_CH_WIDTH_40MHZ; 758c2ecf20Sopenharmony_ci case IEEE80211_STA_RX_BW_20: 768c2ecf20Sopenharmony_ci default: 778c2ecf20Sopenharmony_ci return IWL_TLC_MNG_CH_WIDTH_20MHZ; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic u8 rs_fw_set_active_chains(u8 chains) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci u8 fw_chains = 0; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (chains & ANT_A) 868c2ecf20Sopenharmony_ci fw_chains |= IWL_TLC_MNG_CHAIN_A_MSK; 878c2ecf20Sopenharmony_ci if (chains & ANT_B) 888c2ecf20Sopenharmony_ci fw_chains |= IWL_TLC_MNG_CHAIN_B_MSK; 898c2ecf20Sopenharmony_ci if (chains & ANT_C) 908c2ecf20Sopenharmony_ci WARN(false, 918c2ecf20Sopenharmony_ci "tlc offload doesn't support antenna C. chains: 0x%x\n", 928c2ecf20Sopenharmony_ci chains); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return fw_chains; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic u8 rs_fw_sgi_cw_support(struct ieee80211_sta *sta) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; 1008c2ecf20Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; 1018c2ecf20Sopenharmony_ci struct ieee80211_sta_he_cap *he_cap = &sta->he_cap; 1028c2ecf20Sopenharmony_ci u8 supp = 0; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (he_cap->has_he) 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20) 1088c2ecf20Sopenharmony_ci supp |= BIT(IWL_TLC_MNG_CH_WIDTH_20MHZ); 1098c2ecf20Sopenharmony_ci if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40) 1108c2ecf20Sopenharmony_ci supp |= BIT(IWL_TLC_MNG_CH_WIDTH_40MHZ); 1118c2ecf20Sopenharmony_ci if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80) 1128c2ecf20Sopenharmony_ci supp |= BIT(IWL_TLC_MNG_CH_WIDTH_80MHZ); 1138c2ecf20Sopenharmony_ci if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160) 1148c2ecf20Sopenharmony_ci supp |= BIT(IWL_TLC_MNG_CH_WIDTH_160MHZ); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return supp; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic u16 rs_fw_get_config_flags(struct iwl_mvm *mvm, 1208c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 1218c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; 1248c2ecf20Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; 1258c2ecf20Sopenharmony_ci struct ieee80211_sta_he_cap *he_cap = &sta->he_cap; 1268c2ecf20Sopenharmony_ci bool vht_ena = vht_cap->vht_supported; 1278c2ecf20Sopenharmony_ci u16 flags = 0; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (mvm->cfg->ht_params->stbc && 1308c2ecf20Sopenharmony_ci (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1)) { 1318c2ecf20Sopenharmony_ci if (he_cap->has_he) { 1328c2ecf20Sopenharmony_ci if (he_cap->he_cap_elem.phy_cap_info[2] & 1338c2ecf20Sopenharmony_ci IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ) 1348c2ecf20Sopenharmony_ci flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (he_cap->he_cap_elem.phy_cap_info[7] & 1378c2ecf20Sopenharmony_ci IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ) 1388c2ecf20Sopenharmony_ci flags |= IWL_TLC_MNG_CFG_FLAGS_HE_STBC_160MHZ_MSK; 1398c2ecf20Sopenharmony_ci } else if ((ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) || 1408c2ecf20Sopenharmony_ci (vht_ena && 1418c2ecf20Sopenharmony_ci (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK))) 1428c2ecf20Sopenharmony_ci flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (mvm->cfg->ht_params->ldpc && 1468c2ecf20Sopenharmony_ci ((ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) || 1478c2ecf20Sopenharmony_ci (vht_ena && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)))) 1488c2ecf20Sopenharmony_ci flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* consider LDPC support in case of HE */ 1518c2ecf20Sopenharmony_ci if (he_cap->has_he && (he_cap->he_cap_elem.phy_cap_info[1] & 1528c2ecf20Sopenharmony_ci IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) 1538c2ecf20Sopenharmony_ci flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (sband->iftype_data && sband->iftype_data->he_cap.has_he && 1568c2ecf20Sopenharmony_ci !(sband->iftype_data->he_cap.he_cap_elem.phy_cap_info[1] & 1578c2ecf20Sopenharmony_ci IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) 1588c2ecf20Sopenharmony_ci flags &= ~IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (he_cap->has_he && 1618c2ecf20Sopenharmony_ci (he_cap->he_cap_elem.phy_cap_info[3] & 1628c2ecf20Sopenharmony_ci IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK)) 1638c2ecf20Sopenharmony_ci flags |= IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return flags; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic 1698c2ecf20Sopenharmony_ciint rs_fw_vht_highest_rx_mcs_index(const struct ieee80211_sta_vht_cap *vht_cap, 1708c2ecf20Sopenharmony_ci int nss) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) & 1738c2ecf20Sopenharmony_ci (0x3 << (2 * (nss - 1))); 1748c2ecf20Sopenharmony_ci rx_mcs >>= (2 * (nss - 1)); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci switch (rx_mcs) { 1778c2ecf20Sopenharmony_ci case IEEE80211_VHT_MCS_SUPPORT_0_7: 1788c2ecf20Sopenharmony_ci return IWL_TLC_MNG_HT_RATE_MCS7; 1798c2ecf20Sopenharmony_ci case IEEE80211_VHT_MCS_SUPPORT_0_8: 1808c2ecf20Sopenharmony_ci return IWL_TLC_MNG_HT_RATE_MCS8; 1818c2ecf20Sopenharmony_ci case IEEE80211_VHT_MCS_SUPPORT_0_9: 1828c2ecf20Sopenharmony_ci return IWL_TLC_MNG_HT_RATE_MCS9; 1838c2ecf20Sopenharmony_ci default: 1848c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return 0; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void 1928c2ecf20Sopenharmony_cirs_fw_vht_set_enabled_rates(const struct ieee80211_sta *sta, 1938c2ecf20Sopenharmony_ci const struct ieee80211_sta_vht_cap *vht_cap, 1948c2ecf20Sopenharmony_ci struct iwl_tlc_config_cmd *cmd) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci u16 supp; 1978c2ecf20Sopenharmony_ci int i, highest_mcs; 1988c2ecf20Sopenharmony_ci u8 max_nss = sta->rx_nss; 1998c2ecf20Sopenharmony_ci struct ieee80211_vht_cap ieee_vht_cap = { 2008c2ecf20Sopenharmony_ci .vht_cap_info = cpu_to_le32(vht_cap->cap), 2018c2ecf20Sopenharmony_ci .supp_mcs = vht_cap->vht_mcs, 2028c2ecf20Sopenharmony_ci }; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* the station support only a single receive chain */ 2058c2ecf20Sopenharmony_ci if (sta->smps_mode == IEEE80211_SMPS_STATIC) 2068c2ecf20Sopenharmony_ci max_nss = 1; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci for (i = 0; i < max_nss && i < IWL_TLC_NSS_MAX; i++) { 2098c2ecf20Sopenharmony_ci int nss = i + 1; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci highest_mcs = rs_fw_vht_highest_rx_mcs_index(vht_cap, nss); 2128c2ecf20Sopenharmony_ci if (!highest_mcs) 2138c2ecf20Sopenharmony_ci continue; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci supp = BIT(highest_mcs + 1) - 1; 2168c2ecf20Sopenharmony_ci if (sta->bandwidth == IEEE80211_STA_RX_BW_20) 2178c2ecf20Sopenharmony_ci supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci cmd->ht_rates[i][IWL_TLC_HT_BW_NONE_160] = cpu_to_le16(supp); 2208c2ecf20Sopenharmony_ci /* 2218c2ecf20Sopenharmony_ci * Check if VHT extended NSS indicates that the bandwidth/NSS 2228c2ecf20Sopenharmony_ci * configuration is supported - only for MCS 0 since we already 2238c2ecf20Sopenharmony_ci * decoded the MCS bits anyway ourselves. 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ci if (sta->bandwidth == IEEE80211_STA_RX_BW_160 && 2268c2ecf20Sopenharmony_ci ieee80211_get_vht_max_nss(&ieee_vht_cap, 2278c2ecf20Sopenharmony_ci IEEE80211_VHT_CHANWIDTH_160MHZ, 2288c2ecf20Sopenharmony_ci 0, true, nss) >= nss) 2298c2ecf20Sopenharmony_ci cmd->ht_rates[i][IWL_TLC_HT_BW_160] = 2308c2ecf20Sopenharmony_ci cmd->ht_rates[i][IWL_TLC_HT_BW_NONE_160]; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic u16 rs_fw_he_ieee80211_mcs_to_rs_mcs(u16 mcs) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci switch (mcs) { 2378c2ecf20Sopenharmony_ci case IEEE80211_HE_MCS_SUPPORT_0_7: 2388c2ecf20Sopenharmony_ci return BIT(IWL_TLC_MNG_HT_RATE_MCS7 + 1) - 1; 2398c2ecf20Sopenharmony_ci case IEEE80211_HE_MCS_SUPPORT_0_9: 2408c2ecf20Sopenharmony_ci return BIT(IWL_TLC_MNG_HT_RATE_MCS9 + 1) - 1; 2418c2ecf20Sopenharmony_ci case IEEE80211_HE_MCS_SUPPORT_0_11: 2428c2ecf20Sopenharmony_ci return BIT(IWL_TLC_MNG_HT_RATE_MCS11 + 1) - 1; 2438c2ecf20Sopenharmony_ci case IEEE80211_HE_MCS_NOT_SUPPORTED: 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci WARN(1, "invalid HE MCS %d\n", mcs); 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic void 2528c2ecf20Sopenharmony_cirs_fw_he_set_enabled_rates(const struct ieee80211_sta *sta, 2538c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband, 2548c2ecf20Sopenharmony_ci struct iwl_tlc_config_cmd *cmd) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap = &sta->he_cap; 2578c2ecf20Sopenharmony_ci u16 mcs_160 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); 2588c2ecf20Sopenharmony_ci u16 mcs_80 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); 2598c2ecf20Sopenharmony_ci u16 tx_mcs_80 = 2608c2ecf20Sopenharmony_ci le16_to_cpu(sband->iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_80); 2618c2ecf20Sopenharmony_ci u16 tx_mcs_160 = 2628c2ecf20Sopenharmony_ci le16_to_cpu(sband->iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_160); 2638c2ecf20Sopenharmony_ci int i; 2648c2ecf20Sopenharmony_ci u8 nss = sta->rx_nss; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* the station support only a single receive chain */ 2678c2ecf20Sopenharmony_ci if (sta->smps_mode == IEEE80211_SMPS_STATIC) 2688c2ecf20Sopenharmony_ci nss = 1; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci for (i = 0; i < nss && i < IWL_TLC_NSS_MAX; i++) { 2718c2ecf20Sopenharmony_ci u16 _mcs_160 = (mcs_160 >> (2 * i)) & 0x3; 2728c2ecf20Sopenharmony_ci u16 _mcs_80 = (mcs_80 >> (2 * i)) & 0x3; 2738c2ecf20Sopenharmony_ci u16 _tx_mcs_160 = (tx_mcs_160 >> (2 * i)) & 0x3; 2748c2ecf20Sopenharmony_ci u16 _tx_mcs_80 = (tx_mcs_80 >> (2 * i)) & 0x3; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* If one side doesn't support - mark both as not supporting */ 2778c2ecf20Sopenharmony_ci if (_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED || 2788c2ecf20Sopenharmony_ci _tx_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED) { 2798c2ecf20Sopenharmony_ci _mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED; 2808c2ecf20Sopenharmony_ci _tx_mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci if (_mcs_80 > _tx_mcs_80) 2838c2ecf20Sopenharmony_ci _mcs_80 = _tx_mcs_80; 2848c2ecf20Sopenharmony_ci cmd->ht_rates[i][IWL_TLC_HT_BW_NONE_160] = 2858c2ecf20Sopenharmony_ci cpu_to_le16(rs_fw_he_ieee80211_mcs_to_rs_mcs(_mcs_80)); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* If one side doesn't support - mark both as not supporting */ 2888c2ecf20Sopenharmony_ci if (_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED || 2898c2ecf20Sopenharmony_ci _tx_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED) { 2908c2ecf20Sopenharmony_ci _mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED; 2918c2ecf20Sopenharmony_ci _tx_mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci if (_mcs_160 > _tx_mcs_160) 2948c2ecf20Sopenharmony_ci _mcs_160 = _tx_mcs_160; 2958c2ecf20Sopenharmony_ci cmd->ht_rates[i][IWL_TLC_HT_BW_160] = 2968c2ecf20Sopenharmony_ci cpu_to_le16(rs_fw_he_ieee80211_mcs_to_rs_mcs(_mcs_160)); 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic void rs_fw_set_supp_rates(struct ieee80211_sta *sta, 3018c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband, 3028c2ecf20Sopenharmony_ci struct iwl_tlc_config_cmd *cmd) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci int i; 3058c2ecf20Sopenharmony_ci unsigned long tmp; 3068c2ecf20Sopenharmony_ci unsigned long supp; /* must be unsigned long for for_each_set_bit */ 3078c2ecf20Sopenharmony_ci const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; 3088c2ecf20Sopenharmony_ci const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; 3098c2ecf20Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap = &sta->he_cap; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* non HT rates */ 3128c2ecf20Sopenharmony_ci supp = 0; 3138c2ecf20Sopenharmony_ci tmp = sta->supp_rates[sband->band]; 3148c2ecf20Sopenharmony_ci for_each_set_bit(i, &tmp, BITS_PER_LONG) 3158c2ecf20Sopenharmony_ci supp |= BIT(sband->bitrates[i].hw_value); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci cmd->non_ht_rates = cpu_to_le16(supp); 3188c2ecf20Sopenharmony_ci cmd->mode = IWL_TLC_MNG_MODE_NON_HT; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* HT/VHT rates */ 3218c2ecf20Sopenharmony_ci if (he_cap->has_he) { 3228c2ecf20Sopenharmony_ci cmd->mode = IWL_TLC_MNG_MODE_HE; 3238c2ecf20Sopenharmony_ci rs_fw_he_set_enabled_rates(sta, sband, cmd); 3248c2ecf20Sopenharmony_ci } else if (vht_cap->vht_supported) { 3258c2ecf20Sopenharmony_ci cmd->mode = IWL_TLC_MNG_MODE_VHT; 3268c2ecf20Sopenharmony_ci rs_fw_vht_set_enabled_rates(sta, vht_cap, cmd); 3278c2ecf20Sopenharmony_ci } else if (ht_cap->ht_supported) { 3288c2ecf20Sopenharmony_ci cmd->mode = IWL_TLC_MNG_MODE_HT; 3298c2ecf20Sopenharmony_ci cmd->ht_rates[IWL_TLC_NSS_1][IWL_TLC_HT_BW_NONE_160] = 3308c2ecf20Sopenharmony_ci cpu_to_le16(ht_cap->mcs.rx_mask[0]); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* the station support only a single receive chain */ 3338c2ecf20Sopenharmony_ci if (sta->smps_mode == IEEE80211_SMPS_STATIC) 3348c2ecf20Sopenharmony_ci cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_HT_BW_NONE_160] = 3358c2ecf20Sopenharmony_ci 0; 3368c2ecf20Sopenharmony_ci else 3378c2ecf20Sopenharmony_ci cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_HT_BW_NONE_160] = 3388c2ecf20Sopenharmony_ci cpu_to_le16(ht_cap->mcs.rx_mask[1]); 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_civoid iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, 3438c2ecf20Sopenharmony_ci struct iwl_rx_cmd_buffer *rxb) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 3468c2ecf20Sopenharmony_ci struct iwl_tlc_update_notif *notif; 3478c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 3488c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta; 3498c2ecf20Sopenharmony_ci struct iwl_lq_sta_rs_fw *lq_sta; 3508c2ecf20Sopenharmony_ci u32 flags; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci rcu_read_lock(); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci notif = (void *)pkt->data; 3558c2ecf20Sopenharmony_ci sta = rcu_dereference(mvm->fw_id_to_mac_id[notif->sta_id]); 3568c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(sta)) { 3578c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Invalid sta id (%d) in FW TLC notification\n", 3588c2ecf20Sopenharmony_ci notif->sta_id); 3598c2ecf20Sopenharmony_ci goto out; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci mvmsta = iwl_mvm_sta_from_mac80211(sta); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (!mvmsta) { 3658c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Invalid sta id (%d) in FW TLC notification\n", 3668c2ecf20Sopenharmony_ci notif->sta_id); 3678c2ecf20Sopenharmony_ci goto out; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci flags = le32_to_cpu(notif->flags); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci lq_sta = &mvmsta->lq_sta.rs_fw; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (flags & IWL_TLC_NOTIF_FLAG_RATE) { 3758c2ecf20Sopenharmony_ci char pretty_rate[100]; 3768c2ecf20Sopenharmony_ci lq_sta->last_rate_n_flags = le32_to_cpu(notif->rate); 3778c2ecf20Sopenharmony_ci rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate), 3788c2ecf20Sopenharmony_ci lq_sta->last_rate_n_flags); 3798c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "new rate: %s\n", pretty_rate); 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (flags & IWL_TLC_NOTIF_FLAG_AMSDU && !mvmsta->orig_amsdu_len) { 3838c2ecf20Sopenharmony_ci u16 size = le32_to_cpu(notif->amsdu_size); 3848c2ecf20Sopenharmony_ci int i; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (sta->max_amsdu_len < size) { 3878c2ecf20Sopenharmony_ci /* 3888c2ecf20Sopenharmony_ci * In debug sta->max_amsdu_len < size 3898c2ecf20Sopenharmony_ci * so also check with orig_amsdu_len which holds the 3908c2ecf20Sopenharmony_ci * original data before debugfs changed the value 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_ci WARN_ON(mvmsta->orig_amsdu_len < size); 3938c2ecf20Sopenharmony_ci goto out; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci mvmsta->amsdu_enabled = le32_to_cpu(notif->amsdu_enabled); 3978c2ecf20Sopenharmony_ci mvmsta->max_amsdu_len = size; 3988c2ecf20Sopenharmony_ci sta->max_rc_amsdu_len = mvmsta->max_amsdu_len; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci for (i = 0; i < IWL_MAX_TID_COUNT; i++) { 4018c2ecf20Sopenharmony_ci if (mvmsta->amsdu_enabled & BIT(i)) 4028c2ecf20Sopenharmony_ci sta->max_tid_amsdu_len[i] = 4038c2ecf20Sopenharmony_ci iwl_mvm_max_amsdu_size(mvm, sta, i); 4048c2ecf20Sopenharmony_ci else 4058c2ecf20Sopenharmony_ci /* 4068c2ecf20Sopenharmony_ci * Not so elegant, but this will effectively 4078c2ecf20Sopenharmony_ci * prevent AMSDU on this TID 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ci sta->max_tid_amsdu_len[i] = 1; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 4138c2ecf20Sopenharmony_ci "AMSDU update. AMSDU size: %d, AMSDU selected size: %d, AMSDU TID bitmap 0x%X\n", 4148c2ecf20Sopenharmony_ci le32_to_cpu(notif->amsdu_size), size, 4158c2ecf20Sopenharmony_ci mvmsta->amsdu_enabled); 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ciout: 4188c2ecf20Sopenharmony_ci rcu_read_unlock(); 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ciu16 rs_fw_get_max_amsdu_len(struct ieee80211_sta *sta) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; 4248c2ecf20Sopenharmony_ci const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (vht_cap->vht_supported) { 4278c2ecf20Sopenharmony_ci switch (vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK) { 4288c2ecf20Sopenharmony_ci case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454: 4298c2ecf20Sopenharmony_ci return IEEE80211_MAX_MPDU_LEN_VHT_11454; 4308c2ecf20Sopenharmony_ci case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991: 4318c2ecf20Sopenharmony_ci return IEEE80211_MAX_MPDU_LEN_VHT_7991; 4328c2ecf20Sopenharmony_ci default: 4338c2ecf20Sopenharmony_ci return IEEE80211_MAX_MPDU_LEN_VHT_3895; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci } else if (ht_cap->ht_supported) { 4368c2ecf20Sopenharmony_ci if (ht_cap->cap & IEEE80211_HT_CAP_MAX_AMSDU) 4378c2ecf20Sopenharmony_ci /* 4388c2ecf20Sopenharmony_ci * agg is offloaded so we need to assume that agg 4398c2ecf20Sopenharmony_ci * are enabled and max mpdu in ampdu is 4095 4408c2ecf20Sopenharmony_ci * (spec 802.11-2016 9.3.2.1) 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_ci return IEEE80211_MAX_MPDU_LEN_HT_BA; 4438c2ecf20Sopenharmony_ci else 4448c2ecf20Sopenharmony_ci return IEEE80211_MAX_MPDU_LEN_HT_3839; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* in legacy mode no amsdu is enabled so return zero */ 4488c2ecf20Sopenharmony_ci return 0; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_civoid rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, 4528c2ecf20Sopenharmony_ci enum nl80211_band band, bool update) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = mvm->hw; 4558c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 4568c2ecf20Sopenharmony_ci struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw; 4578c2ecf20Sopenharmony_ci u32 cmd_id = iwl_cmd_id(TLC_MNG_CONFIG_CMD, DATA_PATH_GROUP, 0); 4588c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband = hw->wiphy->bands[band]; 4598c2ecf20Sopenharmony_ci u16 max_amsdu_len = rs_fw_get_max_amsdu_len(sta); 4608c2ecf20Sopenharmony_ci struct iwl_tlc_config_cmd cfg_cmd = { 4618c2ecf20Sopenharmony_ci .sta_id = mvmsta->sta_id, 4628c2ecf20Sopenharmony_ci .max_ch_width = update ? 4638c2ecf20Sopenharmony_ci rs_fw_bw_from_sta_bw(sta) : RATE_MCS_CHAN_WIDTH_20, 4648c2ecf20Sopenharmony_ci .flags = cpu_to_le16(rs_fw_get_config_flags(mvm, sta, sband)), 4658c2ecf20Sopenharmony_ci .chains = rs_fw_set_active_chains(iwl_mvm_get_valid_tx_ant(mvm)), 4668c2ecf20Sopenharmony_ci .sgi_ch_width_supp = rs_fw_sgi_cw_support(sta), 4678c2ecf20Sopenharmony_ci .max_mpdu_len = cpu_to_le16(max_amsdu_len), 4688c2ecf20Sopenharmony_ci .amsdu = iwl_mvm_is_csum_supported(mvm), 4698c2ecf20Sopenharmony_ci }; 4708c2ecf20Sopenharmony_ci int ret; 4718c2ecf20Sopenharmony_ci u16 cmd_size = sizeof(cfg_cmd); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* In old versions of the API the struct is 4 bytes smaller */ 4748c2ecf20Sopenharmony_ci if (iwl_fw_lookup_cmd_ver(mvm->fw, DATA_PATH_GROUP, 4758c2ecf20Sopenharmony_ci TLC_MNG_CONFIG_CMD, 0) < 3) 4768c2ecf20Sopenharmony_ci cmd_size -= 4; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers)); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 4818c2ecf20Sopenharmony_ci iwl_mvm_reset_frame_stats(mvm); 4828c2ecf20Sopenharmony_ci#endif 4838c2ecf20Sopenharmony_ci rs_fw_set_supp_rates(sta, sband, &cfg_cmd); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* 4868c2ecf20Sopenharmony_ci * since TLC offload works with one mode we can assume 4878c2ecf20Sopenharmony_ci * that only vht/ht is used and also set it as station max amsdu 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_ci sta->max_amsdu_len = max_amsdu_len; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_ASYNC, cmd_size, 4928c2ecf20Sopenharmony_ci &cfg_cmd); 4938c2ecf20Sopenharmony_ci if (ret) 4948c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to send rate scale config (%d)\n", ret); 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ciint rs_fw_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, 4988c2ecf20Sopenharmony_ci bool enable) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci /* TODO: need to introduce a new FW cmd since LQ cmd is not relevant */ 5018c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "tx protection - not implemented yet.\n"); 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_civoid iwl_mvm_rs_add_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "create station rate scale window\n"); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci lq_sta->pers.drv = mvm; 5128c2ecf20Sopenharmony_ci lq_sta->pers.sta_id = mvmsta->sta_id; 5138c2ecf20Sopenharmony_ci lq_sta->pers.chains = 0; 5148c2ecf20Sopenharmony_ci memset(lq_sta->pers.chain_signal, 0, sizeof(lq_sta->pers.chain_signal)); 5158c2ecf20Sopenharmony_ci lq_sta->pers.last_rssi = S8_MIN; 5168c2ecf20Sopenharmony_ci lq_sta->last_rate_n_flags = 0; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_DEBUGFS 5198c2ecf20Sopenharmony_ci lq_sta->pers.dbg_fixed_rate = 0; 5208c2ecf20Sopenharmony_ci#endif 5218c2ecf20Sopenharmony_ci} 522