wifi: ath12k: enable MLO setup and teardown from core
authorKarthikeyan Periyasamy <quic_periyasa@quicinc.com>
Wed, 11 Dec 2024 15:34:31 +0000 (17:34 +0200)
committerJeff Johnson <jeff.johnson@oss.qualcomm.com>
Mon, 16 Dec 2024 20:46:57 +0000 (12:46 -0800)
In case of multi device group abstraction, host has to exchange the multi-link
operation commands such as setup and ready to firmware before registering the
device group to mac80211.

The multi-link operation commands - setup, ready and teardown are necessary for
many commands such as WMI_PEER_ASSOC_CMD, WMI_BCN_TMPL_CMD in case of
multi-link interfaces.

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3

Signed-off-by: Karthikeyan Periyasamy <quic_periyasa@quicinc.com>
Signed-off-by: Harshitha Prem <quic_hprem@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://patch.msgid.link/20241211153432.775335-8-kvalo@kernel.org
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
drivers/net/wireless/ath/ath12k/core.c
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 1a43e00cffb2666190e684877774777793b3b98b..af642b466ea0d557f1599fae8b0d4eb5c3e02a67 100644 (file)
@@ -887,6 +887,70 @@ static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag)
        ath12k_mac_destroy(ag);
 }
 
+static int __ath12k_mac_mlo_ready(struct ath12k *ar)
+{
+       int ret;
+
+       ret = ath12k_wmi_mlo_ready(ar);
+       if (ret) {
+               ath12k_err(ar->ab, "MLO ready failed for pdev %d: %d\n",
+                          ar->pdev_idx, ret);
+               return ret;
+       }
+
+       ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mlo ready done for pdev %d\n",
+                  ar->pdev_idx);
+
+       return 0;
+}
+
+int ath12k_mac_mlo_ready(struct ath12k_hw_group *ag)
+{
+       struct ath12k_hw *ah;
+       struct ath12k *ar;
+       int ret;
+       int i, j;
+
+       for (i = 0; i < ag->num_hw; i++) {
+               ah = ag->ah[i];
+               if (!ah)
+                       continue;
+
+               for_each_ar(ah, ar, j) {
+                       ar = &ah->radio[j];
+                       ret = __ath12k_mac_mlo_ready(ar);
+                       if (ret)
+                               goto out;
+               }
+       }
+
+out:
+       return ret;
+}
+
+static int ath12k_core_mlo_setup(struct ath12k_hw_group *ag)
+{
+       int ret;
+
+       if (!ag->mlo_capable || ag->num_devices == 1)
+               return 0;
+
+       ret = ath12k_mac_mlo_setup(ag);
+       if (ret)
+               return ret;
+
+       ret = ath12k_mac_mlo_ready(ag);
+       if (ret)
+               goto err_mlo_teardown;
+
+       return 0;
+
+err_mlo_teardown:
+       ath12k_mac_mlo_teardown(ag);
+
+       return ret;
+}
+
 static int ath12k_core_hw_group_start(struct ath12k_hw_group *ag)
 {
        struct ath12k_base *ab;
@@ -901,10 +965,14 @@ static int ath12k_core_hw_group_start(struct ath12k_hw_group *ag)
        if (WARN_ON(ret))
                return ret;
 
-       ret = ath12k_mac_register(ag);
+       ret = ath12k_core_mlo_setup(ag);
        if (WARN_ON(ret))
                goto err_mac_destroy;
 
+       ret = ath12k_mac_register(ag);
+       if (WARN_ON(ret))
+               goto err_mlo_teardown;
+
        set_bit(ATH12K_GROUP_FLAG_REGISTERED, &ag->flags);
 
 core_pdev_create:
@@ -939,6 +1007,9 @@ err:
        ath12k_core_hw_group_stop(ag);
        return ret;
 
+err_mlo_teardown:
+       ath12k_mac_mlo_teardown(ag);
+
 err_mac_destroy:
        ath12k_mac_destroy(ag);
 
index bf310df3d8f71c9f66dce25b106420669651911a..dc01f7b3fd73f9384f53baa1a65308475d3c9b0a 100644 (file)
@@ -715,6 +715,9 @@ struct ath12k {
        u32 freq_high;
 
        bool nlo_enabled;
+
+       struct completion mlo_setup_done;
+       u32 mlo_setup_status;
 };
 
 struct ath12k_hw {
index 47a80d28d1d7f83ae7f61ecb89c6cd7d1d779494..161cc018230f555f151c7f1399d08657123cecc6 100644 (file)
@@ -10810,6 +10810,7 @@ static void ath12k_mac_setup(struct ath12k *ar)
        init_completion(&ar->scan.started);
        init_completion(&ar->scan.completed);
        init_completion(&ar->scan.on_channel);
+       init_completion(&ar->mlo_setup_done);
 
        INIT_DELAYED_WORK(&ar->scan.timeout, ath12k_scan_timeout_work);
        INIT_WORK(&ar->regd_update_work, ath12k_regd_update_work);
@@ -10818,6 +10819,147 @@ static void ath12k_mac_setup(struct ath12k *ar)
        skb_queue_head_init(&ar->wmi_mgmt_tx_queue);
 }
 
+static int __ath12k_mac_mlo_setup(struct ath12k *ar)
+{
+       u8 num_link = 0, partner_link_id[ATH12K_GROUP_MAX_RADIO] = {};
+       struct ath12k_base *partner_ab, *ab = ar->ab;
+       struct ath12k_hw_group *ag = ab->ag;
+       struct wmi_mlo_setup_arg mlo = {};
+       struct ath12k_pdev *pdev;
+       unsigned long time_left;
+       int i, j, ret;
+
+       lockdep_assert_held(&ag->mutex);
+
+       reinit_completion(&ar->mlo_setup_done);
+
+       for (i = 0; i < ag->num_devices; i++) {
+               partner_ab = ag->ab[i];
+
+               for (j = 0; j < partner_ab->num_radios; j++) {
+                       pdev = &partner_ab->pdevs[j];
+
+                       /* Avoid the self link */
+                       if (ar == pdev->ar)
+                               continue;
+
+                       partner_link_id[num_link] = pdev->hw_link_id;
+                       num_link++;
+
+                       ath12k_dbg(ab, ATH12K_DBG_MAC, "device %d pdev %d hw_link_id %d num_link %d\n",
+                                  i, j, pdev->hw_link_id, num_link);
+               }
+       }
+
+       mlo.group_id = cpu_to_le32(ag->id);
+       mlo.partner_link_id = partner_link_id;
+       mlo.num_partner_links = num_link;
+       ar->mlo_setup_status = 0;
+
+       ath12k_dbg(ab, ATH12K_DBG_MAC, "group id %d num_link %d\n", ag->id, num_link);
+
+       ret = ath12k_wmi_mlo_setup(ar, &mlo);
+       if (ret) {
+               ath12k_err(ab, "failed to send  setup MLO WMI command for pdev %d: %d\n",
+                          ar->pdev_idx, ret);
+               return ret;
+       }
+
+       time_left = wait_for_completion_timeout(&ar->mlo_setup_done,
+                                               WMI_MLO_CMD_TIMEOUT_HZ);
+
+       if (!time_left || ar->mlo_setup_status)
+               return ar->mlo_setup_status ? : -ETIMEDOUT;
+
+       ath12k_dbg(ab, ATH12K_DBG_MAC, "mlo setup done for pdev %d\n", ar->pdev_idx);
+
+       return 0;
+}
+
+static int __ath12k_mac_mlo_teardown(struct ath12k *ar)
+{
+       struct ath12k_base *ab = ar->ab;
+       int ret;
+
+       if (test_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags))
+               return 0;
+
+       ret = ath12k_wmi_mlo_teardown(ar);
+       if (ret) {
+               ath12k_warn(ab, "failed to send MLO teardown WMI command for pdev %d: %d\n",
+                           ar->pdev_idx, ret);
+               return ret;
+       }
+
+       ath12k_dbg(ab, ATH12K_DBG_MAC, "mlo teardown for pdev %d\n", ar->pdev_idx);
+
+       return 0;
+}
+
+int ath12k_mac_mlo_setup(struct ath12k_hw_group *ag)
+{
+       struct ath12k_hw *ah;
+       struct ath12k *ar;
+       int ret;
+       int i, j;
+
+       for (i = 0; i < ag->num_hw; i++) {
+               ah = ag->ah[i];
+               if (!ah)
+                       continue;
+
+               for_each_ar(ah, ar, j) {
+                       ar = &ah->radio[j];
+                       ret = __ath12k_mac_mlo_setup(ar);
+                       if (ret) {
+                               ath12k_err(ar->ab, "failed to setup MLO: %d\n", ret);
+                               goto err_setup;
+                       }
+               }
+       }
+
+       return 0;
+
+err_setup:
+       for (i = i - 1; i >= 0; i--) {
+               ah = ag->ah[i];
+               if (!ah)
+                       continue;
+
+               for (j = j - 1; j >= 0; j--) {
+                       ar = &ah->radio[j];
+                       if (!ar)
+                               continue;
+
+                       __ath12k_mac_mlo_teardown(ar);
+               }
+       }
+
+       return ret;
+}
+
+void ath12k_mac_mlo_teardown(struct ath12k_hw_group *ag)
+{
+       struct ath12k_hw *ah;
+       struct ath12k *ar;
+       int ret, i, j;
+
+       for (i = 0; i < ag->num_hw; i++) {
+               ah = ag->ah[i];
+               if (!ah)
+                       continue;
+
+               for_each_ar(ah, ar, j) {
+                       ar = &ah->radio[j];
+                       ret = __ath12k_mac_mlo_teardown(ar);
+                       if (ret) {
+                               ath12k_err(ar->ab, "failed to teardown MLO: %d\n", ret);
+                               break;
+                       }
+               }
+       }
+}
+
 int ath12k_mac_register(struct ath12k_hw_group *ag)
 {
        struct ath12k_base *ab = ag->ab[0];
index ccfc215d83ffe9adb4ff6015a4e45a77d118d21d..81cfb950e6cddfcf31747e46c7a9a805b94f55dd 100644 (file)
@@ -96,6 +96,9 @@ int ath12k_mac_vif_set_keepalive(struct ath12k_link_vif *arvif,
                                 enum wmi_sta_keepalive_method method,
                                 u32 interval);
 u8 ath12k_mac_get_target_pdev_id(struct ath12k *ar);
+int ath12k_mac_mlo_setup(struct ath12k_hw_group *ag);
+int ath12k_mac_mlo_ready(struct ath12k_hw_group *ag);
+void ath12k_mac_mlo_teardown(struct ath12k_hw_group *ag);
 int ath12k_mac_vdev_stop(struct ath12k_link_vif *arvif);
 
 #endif
index 705e0973ebb0df01d82f148a55f2599bcfe6ca9b..892cc4846e4fa7f85172c8b0d703561c03f7e8d7 100644 (file)
@@ -7369,6 +7369,9 @@ skip_lookup:
                goto out;
        }
 
+       ar->mlo_setup_status = le32_to_cpu(ev->status);
+       complete(&ar->mlo_setup_done);
+
 out:
        kfree(tb);
 }
index 640720b687821d662462a2765eb84cf6afb9a24f..270ed458302e535b9957e53cbeeca5cb64e41a0b 100644 (file)
@@ -4938,6 +4938,7 @@ struct wmi_probe_tmpl_cmd {
 
 #define MAX_RADIOS 2
 
+#define WMI_MLO_CMD_TIMEOUT_HZ (5 * HZ)
 #define WMI_SERVICE_READY_TIMEOUT_HZ (5 * HZ)
 #define WMI_SEND_TIMEOUT_HZ (3 * HZ)