]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: ath12k: add support for 160 MHz bandwidth
authorPradeep Kumar Chitrapu <quic_pradeepc@quicinc.com>
Tue, 1 Jul 2025 01:04:07 +0000 (18:04 -0700)
committerJeff Johnson <jeff.johnson@oss.qualcomm.com>
Wed, 2 Jul 2025 14:02:26 +0000 (07:02 -0700)
Add support to configure maximum NSS in 160 MHz bandwidth.
Firmware advertises support for handling NSS ratio information
as a part of service ready ext event using nss_ratio_enabled
flag. Save this information in ath12k_pdev_cap to calculate
NSS ratio.

Additionally, reorder the code by moving
ath12k_peer_assoc_h_phymode() before ath12k_peer_assoc_h_vht()
to ensure that arg->peer_phymode correctly reflects the bandwidth
in the max NSS calculation.

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1

Co-developed-by: P Praneesh <quic_ppranees@quicinc.com>
Signed-off-by: P Praneesh <quic_ppranees@quicinc.com>
Signed-off-by: Pradeep Kumar Chitrapu <quic_pradeepc@quicinc.com>
Acked-by: Jeff Johnson <quic_jjohnson@quicinc.com>
Link: https://patch.msgid.link/20250701010408.1257201-9-quic_pradeepc@quicinc.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
drivers/net/wireless/ath/ath12k/core.h
drivers/net/wireless/ath/ath12k/mac.c
drivers/net/wireless/ath/ath12k/mac.h
drivers/net/wireless/ath/ath12k/wmi.c
drivers/net/wireless/ath/ath12k/wmi.h

index 3c10d7eb9669da603c2c4a42fd39bfc508515641..96ff9b08a8482062ddce03f918b8e4bdb8801bd8 100644 (file)
@@ -894,6 +894,8 @@ struct ath12k_pdev_cap {
        struct ath12k_band_cap band[NUM_NL80211_BANDS];
        u32 eml_cap;
        u32 mld_cap;
+       bool nss_ratio_enabled;
+       u8 nss_ratio_info;
 };
 
 struct mlo_timestamp {
index 8fba1bd2e372695f66468938b7b1e87df9bd49e2..c0d58a587d6a756fa68a6850f190e5c4ac7b3fac 100644 (file)
@@ -2203,6 +2203,34 @@ ath12k_peer_assoc_h_vht_limit(u16 tx_mcs_set,
        return tx_mcs_set;
 }
 
+static u8 ath12k_get_nss_160mhz(struct ath12k *ar,
+                               u8 max_nss)
+{
+       u8 nss_ratio_info = ar->pdev->cap.nss_ratio_info;
+       u8 max_sup_nss = 0;
+
+       switch (nss_ratio_info) {
+       case WMI_NSS_RATIO_1BY2_NSS:
+               max_sup_nss = max_nss >> 1;
+               break;
+       case WMI_NSS_RATIO_3BY4_NSS:
+               ath12k_warn(ar->ab, "WMI_NSS_RATIO_3BY4_NSS not supported\n");
+               break;
+       case WMI_NSS_RATIO_1_NSS:
+               max_sup_nss = max_nss;
+               break;
+       case WMI_NSS_RATIO_2_NSS:
+               ath12k_warn(ar->ab, "WMI_NSS_RATIO_2_NSS not supported\n");
+               break;
+       default:
+               ath12k_warn(ar->ab, "invalid nss ratio received from fw: %d\n",
+                           nss_ratio_info);
+               break;
+       }
+
+       return max_sup_nss;
+}
+
 static void ath12k_peer_assoc_h_vht(struct ath12k *ar,
                                    struct ath12k_link_vif *arvif,
                                    struct ath12k_link_sta *arsta,
@@ -2220,6 +2248,7 @@ static void ath12k_peer_assoc_h_vht(struct ath12k *ar,
        u8 max_nss, vht_mcs;
        int i, vht_nss, nss_idx;
        bool user_rate_valid = true;
+       u32 rx_nss, tx_nss, nss_160;
 
        lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
 
@@ -2324,10 +2353,24 @@ static void ath12k_peer_assoc_h_vht(struct ath12k *ar,
        /* TODO:  Check */
        arg->tx_max_mcs_nss = 0xFF;
 
-       ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac vht peer %pM max_mpdu %d flags 0x%x\n",
-                  arsta->addr, arg->peer_max_mpdu, arg->peer_flags);
+       if (arg->peer_phymode == MODE_11AC_VHT160) {
+               tx_nss = ath12k_get_nss_160mhz(ar, max_nss);
+               rx_nss = min(arg->peer_nss, tx_nss);
+               arg->peer_bw_rxnss_override = ATH12K_BW_NSS_MAP_ENABLE;
+
+               if (!rx_nss) {
+                       ath12k_warn(ar->ab, "invalid max_nss\n");
+                       return;
+               }
+
+               nss_160 = u32_encode_bits(rx_nss - 1, ATH12K_PEER_RX_NSS_160MHZ);
+               arg->peer_bw_rxnss_override |= nss_160;
+       }
 
-       /* TODO: rxnss_override */
+       ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
+                  "mac vht peer %pM max_mpdu %d flags 0x%x nss_override 0x%x\n",
+                  arsta->addr, arg->peer_max_mpdu, arg->peer_flags,
+                  arg->peer_bw_rxnss_override);
 }
 
 static int ath12k_mac_get_max_he_mcs_map(u16 mcs_map, int nss)
@@ -2420,6 +2463,7 @@ static void ath12k_peer_assoc_h_he(struct ath12k *ar,
        u16 he_tx_mcs = 0, v = 0;
        int he_nss, nss_idx;
        bool user_rate_valid = true;
+       u32 rx_nss, tx_nss, nss_160;
 
        if (WARN_ON(ath12k_mac_vif_link_chan(vif, link_id, &def)))
                return;
@@ -2617,9 +2661,25 @@ static void ath12k_peer_assoc_h_he(struct ath12k *ar,
        max_nss = min(max_nss, ar->num_tx_chains);
        arg->peer_nss = min(link_sta->rx_nss, max_nss);
 
+       if (arg->peer_phymode == MODE_11AX_HE160) {
+               tx_nss = ath12k_get_nss_160mhz(ar, max_nss);
+               rx_nss = min(arg->peer_nss, tx_nss);
+               arg->peer_bw_rxnss_override = ATH12K_BW_NSS_MAP_ENABLE;
+
+               if (!rx_nss) {
+                       ath12k_warn(ar->ab, "invalid max_nss\n");
+                       return;
+               }
+
+               nss_160 = u32_encode_bits(rx_nss - 1, ATH12K_PEER_RX_NSS_160MHZ);
+               arg->peer_bw_rxnss_override |= nss_160;
+       }
+
        ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
-                  "mac he peer %pM nss %d mcs cnt %d\n",
-                  arsta->addr, arg->peer_nss, arg->peer_he_mcs_count);
+                  "mac he peer %pM nss %d mcs cnt %d nss_override 0x%x\n",
+                  arsta->addr, arg->peer_nss,
+                  arg->peer_he_mcs_count,
+                  arg->peer_bw_rxnss_override);
 }
 
 static void ath12k_peer_assoc_h_he_6ghz(struct ath12k *ar,
@@ -2860,10 +2920,13 @@ static enum wmi_phy_mode ath12k_mac_get_phymode_vht(struct ath12k *ar,
                                                    struct ieee80211_link_sta *link_sta)
 {
        if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160) {
-               if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)
+               if (link_sta->vht_cap.cap & (IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
+                   IEEE80211_VHT_CAP_EXT_NSS_BW_MASK))
                        return MODE_11AC_VHT160;
 
-               /* not sure if this is a valid case? */
+               /* Allow STA to connect even if it does not explicitly advertise 160 MHz
+                * support
+                */
                return MODE_11AC_VHT160;
        }
 
@@ -7575,10 +7638,8 @@ ath12k_create_vht_cap(struct ath12k *ar, u32 rate_cap_tx_chainmask,
 
        ath12k_set_vht_txbf_cap(ar, &vht_cap.cap);
 
-       /* TODO: Enable back VHT160 mode once association issues are fixed */
-       /* Disabling VHT160 and VHT80+80 modes */
-       vht_cap.cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
-       vht_cap.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160;
+       /* 80P80 is not supported */
+       vht_cap.cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
 
        rxmcs_map = 0;
        txmcs_map = 0;
@@ -12974,7 +13035,8 @@ ath12k_mac_setup_radio_iface_comb(struct ath12k *ar,
                comb[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
                                                BIT(NL80211_CHAN_WIDTH_20) |
                                                BIT(NL80211_CHAN_WIDTH_40) |
-                                               BIT(NL80211_CHAN_WIDTH_80);
+                                               BIT(NL80211_CHAN_WIDTH_80) |
+                                               BIT(NL80211_CHAN_WIDTH_160);
        }
 
        return 0;
@@ -13356,6 +13418,9 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah)
        ieee80211_hw_set(hw, REPORTS_LOW_ACK);
        ieee80211_hw_set(hw, NO_VIRTUAL_MONITOR);
 
+       if (cap->nss_ratio_enabled)
+               ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
+
        if ((ht_cap & WMI_HT_CAP_ENABLED) || is_6ghz) {
                ieee80211_hw_set(hw, AMPDU_AGGREGATION);
                ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
index 9241afe7dc020e7ea9536068839fba1cb762d6b4..18c79d4002cb5a5e4bffc354086f909a2becf930 100644 (file)
@@ -41,6 +41,8 @@ struct ath12k_generic_iter {
 #define IEEE80211_DISABLE_VHT_MCS_SUPPORT_0_11 BIT(24)
 
 #define ATH12K_CHAN_WIDTH_NUM                  14
+#define ATH12K_BW_NSS_MAP_ENABLE               BIT(31)
+#define ATH12K_PEER_RX_NSS_160MHZ              GENMASK(2, 0)
 
 #define ATH12K_TX_POWER_MAX_VAL        70
 #define ATH12K_TX_POWER_MIN_VAL        0
index 05e406273ebfeb90a6cda477e462fae6e33f3aa2..b34f2c1833126cc37d4125eed793aa9efc3e4460 100644 (file)
@@ -537,6 +537,10 @@ ath12k_pull_mac_phy_cap_svc_ready_ext(struct ath12k_wmi_pdev *wmi_handle,
                pdev_cap->he_mcs = le32_to_cpu(mac_caps->he_supp_mcs_5g);
                pdev_cap->tx_chain_mask = le32_to_cpu(mac_caps->tx_chain_mask_5g);
                pdev_cap->rx_chain_mask = le32_to_cpu(mac_caps->rx_chain_mask_5g);
+               pdev_cap->nss_ratio_enabled =
+                       WMI_NSS_RATIO_EN_DIS_GET(mac_caps->nss_ratio);
+               pdev_cap->nss_ratio_info =
+                       WMI_NSS_RATIO_INFO_GET(mac_caps->nss_ratio);
        } else {
                return -EINVAL;
        }
@@ -1059,7 +1063,8 @@ static void ath12k_wmi_put_wmi_channel(struct ath12k_wmi_channel_params *chan,
 
                chan->band_center_freq2 = cpu_to_le32(center_freq1);
 
-       } else if (arg->mode == MODE_11BE_EHT160) {
+       } else if (arg->mode == MODE_11BE_EHT160 ||
+                  arg->mode == MODE_11AX_HE160) {
                if (arg->freq > center_freq1)
                        chan->band_center_freq1 = cpu_to_le32(center_freq1 + 40);
                else
index 79cc3db3f27c3b134753cb333d3363ffac4f6a00..ed9b4324a7b87ad31a79b2decebbe7c164496692 100644 (file)
@@ -2333,6 +2333,21 @@ enum wmi_direct_buffer_module {
        WMI_DIRECT_BUF_MAX
 };
 
+/**
+ * enum wmi_nss_ratio - NSS ratio received from FW during service ready ext event
+ * @WMI_NSS_RATIO_1BY2_NSS: Max nss of 160MHz is equals to half of the max nss of 80MHz
+ * @WMI_NSS_RATIO_3BY4_NSS: Max nss of 160MHz is equals to 3/4 of the max nss of 80MHz
+ * @WMI_NSS_RATIO_1_NSS: Max nss of 160MHz is equals to the max nss of 80MHz
+ * @WMI_NSS_RATIO_2_NSS: Max nss of 160MHz is equals to two times the max nss of 80MHz
+ */
+
+enum wmi_nss_ratio {
+       WMI_NSS_RATIO_1BY2_NSS,
+       WMI_NSS_RATIO_3BY4_NSS,
+       WMI_NSS_RATIO_1_NSS,
+       WMI_NSS_RATIO_2_NSS
+};
+
 struct ath12k_wmi_pdev_band_arg {
        u32 pdev_id;
        u32 start_freq;
@@ -2652,6 +2667,12 @@ struct ath12k_wmi_hw_mode_cap_params {
 } __packed;
 
 #define WMI_MAX_HECAP_PHY_SIZE                 (3)
+#define WMI_NSS_RATIO_EN_DIS_BITPOS    BIT(0)
+#define WMI_NSS_RATIO_EN_DIS_GET(_val) \
+       le32_get_bits(_val, WMI_NSS_RATIO_EN_DIS_BITPOS)
+#define WMI_NSS_RATIO_INFO_BITPOS              GENMASK(4, 1)
+#define WMI_NSS_RATIO_INFO_GET(_val) \
+       le32_get_bits(_val, WMI_NSS_RATIO_INFO_BITPOS)
 
 /* pdev_id is present in lower 16 bits of pdev_and_hw_link_ids in
  * ath12k_wmi_mac_phy_caps_params & ath12k_wmi_caps_ext_params.