From 4938ba733ee2147724c4b10025fe0d349d3b605f Mon Sep 17 00:00:00 2001 From: Rameshkumar Sundaram Date: Tue, 11 Jun 2024 09:42:31 +0300 Subject: [PATCH 01/38] wifi: ath12k: modify remain on channel for single wiphy When multiple radios are advertised as a single wiphy which supports various bands, vdev creation for the vif is deferred until channel is assigned to it. If a remain on channel (RoC) request is received from mac80211, select the corresponding radio (ar) based on channel and create a vdev on that radio to initiate an RoC scan. Note that on RoC completion this vdev is not deleted. If a new RoC/hw scan request is seen on that same vif for a different band the vdev will be deleted and created on the new radio supporting the request. Also if the RoC scan is requested when the vdev is in started state, no switching to new radio is allowed and RoC request can be accepted only on channels within same radio. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-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: Rameshkumar Sundaram Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240528082739.1226758-1-quic_ramess@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 68 ++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 509c02bffdae..d1529612fddd 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -3372,7 +3372,7 @@ static void ath12k_mac_op_bss_info_changed(struct ieee80211_hw *hw, static struct ath12k* ath12k_mac_select_scan_device(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_scan_request *req) + u32 center_freq) { struct ath12k_hw *ah = hw->priv; enum nl80211_band band; @@ -3389,9 +3389,9 @@ ath12k_mac_select_scan_device(struct ieee80211_hw *hw, * split the hw request and perform multiple scans */ - if (req->req.channels[0]->center_freq < ATH12K_MIN_5G_FREQ) + if (center_freq < ATH12K_MIN_5G_FREQ) band = NL80211_BAND_2GHZ; - else if (req->req.channels[0]->center_freq < ATH12K_MIN_6G_FREQ) + else if (center_freq < ATH12K_MIN_6G_FREQ) band = NL80211_BAND_5GHZ; else band = NL80211_BAND_6GHZ; @@ -3591,7 +3591,7 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw, /* Since the targeted scan device could depend on the frequency * requested in the hw_req, select the corresponding radio */ - ar = ath12k_mac_select_scan_device(hw, vif, hw_req); + ar = ath12k_mac_select_scan_device(hw, vif, hw_req->req.channels[0]->center_freq); if (!ar) return -EINVAL; @@ -8416,12 +8416,68 @@ static int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw, struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k_wmi_scan_req_arg arg; - struct ath12k *ar; + struct ath12k *ar, *prev_ar; u32 scan_time_msec; + bool create = true; int ret; - ar = ath12k_ah_to_ar(ah, 0); + if (ah->num_radio == 1) { + WARN_ON(!arvif->is_created); + ar = ath12k_ah_to_ar(ah, 0); + goto scan; + } + ar = ath12k_mac_select_scan_device(hw, vif, chan->center_freq); + if (!ar) + return -EINVAL; + + /* If the vif is already assigned to a specific vdev of an ar, + * check whether its already started, vdev which is started + * are not allowed to switch to a new radio. + * If the vdev is not started, but was earlier created on a + * different ar, delete that vdev and create a new one. We don't + * delete at the scan stop as an optimization to avoid redundant + * delete-create vdev's for the same ar, in case the request is + * always on the same band for the vif + */ + if (arvif->is_created) { + if (WARN_ON(!arvif->ar)) + return -EINVAL; + + if (ar != arvif->ar && arvif->is_started) + return -EBUSY; + + if (ar != arvif->ar) { + /* backup the previously used ar ptr, since the vdev delete + * would assign the arvif->ar to NULL after the call + */ + prev_ar = arvif->ar; + mutex_lock(&prev_ar->conf_mutex); + ret = ath12k_mac_vdev_delete(prev_ar, vif); + mutex_unlock(&prev_ar->conf_mutex); + if (ret) { + ath12k_warn(prev_ar->ab, + "unable to delete scan vdev for roc: %d\n", + ret); + return ret; + } + } else { + create = false; + } + } + + if (create) { + mutex_lock(&ar->conf_mutex); + ret = ath12k_mac_vdev_create(ar, vif); + mutex_unlock(&ar->conf_mutex); + if (ret) { + ath12k_warn(ar->ab, "unable to create scan vdev for roc: %d\n", + ret); + return -EINVAL; + } + } + +scan: mutex_lock(&ar->conf_mutex); spin_lock_bh(&ar->data_lock); From 8b2a12749b08726f006303814ecc1c37024d3617 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Tue, 11 Jun 2024 09:42:33 +0300 Subject: [PATCH 02/38] wifi: ath12k: fix ACPI warning when resume Currently ACPI notification handler is installed when driver loads and only gets removed when driver unloads. During resume after firmware is reloaded, ath12k tries to install it by default. Since it is installed already, ACPI subsystem rejects it and returns an error: [ 83.094206] ath12k_pci 0000:03:00.0: failed to install DSM notify callback: 7 Fix it by removing that handler when going to suspend. This also avoid any possible ACPI call to firmware before firmware is reloaded/reinitialized. Note ab->acpi also needs to be cleared in ath12k_acpi_stop() such that we are in a clean state when ACPI structures are reinitialized in ath12k_acpi_start(). Tested-on: WCN7850 HW2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Fixes: 576771c9fa21 ("wifi: ath12k: ACPI TAS support") Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240531024000.9291-1-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/acpi.c | 2 ++ drivers/net/wireless/ath/ath12k/core.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/acpi.c b/drivers/net/wireless/ath/ath12k/acpi.c index 443ba12e01f3..0555d35aab47 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.c +++ b/drivers/net/wireless/ath/ath12k/acpi.c @@ -391,4 +391,6 @@ void ath12k_acpi_stop(struct ath12k_base *ab) acpi_remove_notify_handler(ACPI_HANDLE(ab->dev), ACPI_DEVICE_NOTIFY, ath12k_acpi_dsm_notify); + + memset(&ab->acpi, 0, sizeof(ab->acpi)); } diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 4c3eab4686c2..da02d3528737 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -83,6 +83,8 @@ int ath12k_core_suspend_late(struct ath12k_base *ab) if (!ab->hw_params->supports_suspend) return -EOPNOTSUPP; + ath12k_acpi_stop(ab); + ath12k_hif_irq_disable(ab); ath12k_hif_ce_irq_disable(ab); From 53d7c99719e29819eaeb9f373d262a6b56ea7cce Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Tue, 11 Jun 2024 09:42:33 +0300 Subject: [PATCH 03/38] wifi: ath11k: fix RCU documentation in ath11k_mac_op_ipv6_changed() Current documentation on RCU in ath11k_mac_op_ipv6_changed() says: /* Note: read_lock_bh() calls rcu_read_lock() */ read_lock_bh(&idev->lock); This is wrong because without enabling CONFIG_PREEMPT_RT rcu_read_lock() is not called by read_lock_bh(). The reason why current code works even in a CONFIG_PREEMPT_RT=n kernel is because atomic_notifier_call_chain() already does that for us, see: int atomic_notifier_call_chain() { ... rcu_read_lock(); ret = notifier_call_chain(&nh->head, val, v, -1, NULL); rcu_read_unlock(); ... } and backtrace: ath11k_mac_op_ipv6_changed ieee80211_ifa6_changed notifier_call_chain atomic_notifier_call_chain So update the comment to make it correct. This is found during code review, compile tested only. Fixes: feafe59c8975 ("wifi: ath11k: use RCU when accessing struct inet6_dev::ac_list") Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240531022411.6543-1-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index a1800c75d32b..29af015a16f9 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -9020,7 +9020,12 @@ static void ath11k_mac_op_ipv6_changed(struct ieee80211_hw *hw, offload = &arvif->arp_ns_offload; count = 0; - /* Note: read_lock_bh() calls rcu_read_lock() */ + /* The _ipv6_changed() is called with RCU lock already held in + * atomic_notifier_call_chain(), so we don't need to call + * rcu_read_lock() again here. But note that with CONFIG_PREEMPT_RT + * enabled, read_lock_bh() also calls rcu_read_lock(). This is OK + * because RCU read critical section is allowed to get nested. + */ read_lock_bh(&idev->lock); memset(offload->ipv6_addr, 0, sizeof(offload->ipv6_addr)); From 65a8368bf34e51b4499205658d7015d40e5a3f80 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 11 Jun 2024 09:42:33 +0300 Subject: [PATCH 04/38] wifi: ath11k: use 'time_left' variable with wait_event_timeout() There is a confusing pattern in the kernel to use a variable named 'timeout' to store the result of wait_event_timeout() causing patterns like: timeout = wait_event_timeout(...) if (!timeout) return -ETIMEDOUT; with all kinds of permutations. Use 'time_left' as a variable to make the code self explaining. Fix to the proper variable type 'long' while here. Signed-off-by: Wolfram Sang Signed-off-by: Kalle Valo Link: https://msgid.link/20240603091541.8367-2-wsa+renesas@sang-engineering.com --- drivers/net/wireless/ath/ath11k/qmi.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index aa160e6fe24f..1bc648920ab6 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -2859,7 +2859,7 @@ int ath11k_qmi_firmware_start(struct ath11k_base *ab, int ath11k_qmi_fwreset_from_cold_boot(struct ath11k_base *ab) { - int timeout; + long time_left; if (!ath11k_core_coldboot_cal_support(ab) || ab->hw_params.cbcal_restart_fw == 0) @@ -2867,11 +2867,11 @@ int ath11k_qmi_fwreset_from_cold_boot(struct ath11k_base *ab) ath11k_dbg(ab, ATH11K_DBG_QMI, "wait for cold boot done\n"); - timeout = wait_event_timeout(ab->qmi.cold_boot_waitq, - (ab->qmi.cal_done == 1), - ATH11K_COLD_BOOT_FW_RESET_DELAY); + time_left = wait_event_timeout(ab->qmi.cold_boot_waitq, + (ab->qmi.cal_done == 1), + ATH11K_COLD_BOOT_FW_RESET_DELAY); - if (timeout <= 0) { + if (time_left <= 0) { ath11k_warn(ab, "Coldboot Calibration timed out\n"); return -ETIMEDOUT; } @@ -2886,7 +2886,7 @@ EXPORT_SYMBOL(ath11k_qmi_fwreset_from_cold_boot); static int ath11k_qmi_process_coldboot_calibration(struct ath11k_base *ab) { - int timeout; + long time_left; int ret; ret = ath11k_qmi_wlanfw_mode_send(ab, ATH11K_FIRMWARE_MODE_COLD_BOOT); @@ -2897,10 +2897,10 @@ static int ath11k_qmi_process_coldboot_calibration(struct ath11k_base *ab) ath11k_dbg(ab, ATH11K_DBG_QMI, "Coldboot calibration wait started\n"); - timeout = wait_event_timeout(ab->qmi.cold_boot_waitq, - (ab->qmi.cal_done == 1), - ATH11K_COLD_BOOT_FW_RESET_DELAY); - if (timeout <= 0) { + time_left = wait_event_timeout(ab->qmi.cold_boot_waitq, + (ab->qmi.cal_done == 1), + ATH11K_COLD_BOOT_FW_RESET_DELAY); + if (time_left <= 0) { ath11k_warn(ab, "coldboot calibration timed out\n"); return 0; } From 9e32058ae62f1d8fdf1ee7b177a1307f90335fbd Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Tue, 11 Jun 2024 09:42:33 +0300 Subject: [PATCH 05/38] wifi: ath12k: avoid unnecessary MSDU drop in the Rx error process Currently, in the Rx error processing handler, once an MSDU drop is detected, the subsequent MSDUs get unintentionally dropped due to the previous drop flag being retained across all MSDU processing, leading to the discarding of valid MSDUs. To resolve this issue, the drop flag should be reset to false before processing each descriptor. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240604062641.2956288-1-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/dp_rx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index cb1f308f096b..54aea3c22311 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -3402,7 +3402,7 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, struct ath12k *ar; dma_addr_t paddr; bool is_frag; - bool drop = false; + bool drop; int pdev_id; tot_n_bufs_reaped = 0; @@ -3420,7 +3420,9 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, while (budget && (reo_desc = ath12k_hal_srng_dst_get_next_entry(ab, srng))) { + drop = false; ab->soc_stats.err_ring_pkts++; + ret = ath12k_hal_desc_reo_parse_err(ab, reo_desc, &paddr, &desc_bank); if (ret) { From d2b0ca38d362ebf16ca79cd7f309d5bb8b581deb Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Tue, 11 Jun 2024 09:42:34 +0300 Subject: [PATCH 06/38] wifi: ath11k: fix wrong handling of CCMP256 and GCMP ciphers Currently for CCMP256, GCMP128 and GCMP256 ciphers, in ath11k_install_key() IEEE80211_KEY_FLAG_GENERATE_IV_MGMT is not set. And in ath11k_mac_mgmt_tx_wmi() a length of IEEE80211_CCMP_MIC_LEN is reserved for all ciphers. This results in unexpected management frame drop in case either of above 3 ciphers is used. The reason is, without IEEE80211_KEY_FLAG_GENERATE_IV_MGMT set, mac80211 will not generate CCMP/GCMP headers in frame for ath11k. Also MIC length reserved is wrong. Such frame is dropped later by hardware: ath11k_pci 0000:5a:00.0: mac tx mgmt frame, buf id 0 ath11k_pci 0000:5a:00.0: mgmt tx compl ev pdev_id 1, desc_id 0, status 1 From user point of view, we have observed very low throughput due to this issue: action frames are all dropped so ADDBA response from DUT never reaches AP. AP can not use aggregation thus throughput is low. Fix this by setting IEEE80211_KEY_FLAG_GENERATE_IV_MGMT flag and by reserving proper MIC length for those ciphers. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30 Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Reported-by: Yaroslav Isakov Tested-by: Yaroslav Isakov Closes: https://lore.kernel.org/all/CADS+iDX5=JtJr0apAtAQ02WWBxgOFEv8G063vuGYwDTC8AVZaw@mail.gmail.com Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240605014826.22498-1-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/dp_rx.c | 3 +-- drivers/net/wireless/ath/ath11k/dp_rx.h | 3 +++ drivers/net/wireless/ath/ath11k/mac.c | 15 +++++++++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 198fb359a688..86485580dd89 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -1877,8 +1877,7 @@ static void ath11k_dp_rx_h_csum_offload(struct ath11k *ar, struct sk_buff *msdu) CHECKSUM_NONE : CHECKSUM_UNNECESSARY; } -static int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar, - enum hal_encrypt_type enctype) +int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar, enum hal_encrypt_type enctype) { switch (enctype) { case HAL_ENCRYPT_TYPE_OPEN: diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.h b/drivers/net/wireless/ath/ath11k/dp_rx.h index 623da3bf9dc8..c322e30caa96 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.h +++ b/drivers/net/wireless/ath/ath11k/dp_rx.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_DP_RX_H #define ATH11K_DP_RX_H @@ -95,4 +96,6 @@ int ath11k_peer_rx_frag_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id int ath11k_dp_rx_pktlog_start(struct ath11k_base *ab); int ath11k_dp_rx_pktlog_stop(struct ath11k_base *ab, bool stop_timer); +int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar, enum hal_encrypt_type enctype); + #endif /* ATH11K_DP_RX_H */ diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 29af015a16f9..bb56160048ba 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -4229,6 +4229,7 @@ static int ath11k_install_key(struct ath11k_vif *arvif, switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_CCMP_256: arg.key_cipher = WMI_CIPHER_AES_CCM; /* TODO: Re-check if flag is valid */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; @@ -4238,12 +4239,10 @@ static int ath11k_install_key(struct ath11k_vif *arvif, arg.key_txmic_len = 8; arg.key_rxmic_len = 8; break; - case WLAN_CIPHER_SUITE_CCMP_256: - arg.key_cipher = WMI_CIPHER_AES_CCM; - break; case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256: arg.key_cipher = WMI_CIPHER_AES_GCM; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; break; default: ath11k_warn(ar->ab, "cipher %d is not supported\n", key->cipher); @@ -5903,7 +5902,10 @@ static int ath11k_mac_mgmt_tx_wmi(struct ath11k *ar, struct ath11k_vif *arvif, { struct ath11k_base *ab = ar->ab; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB(skb); struct ieee80211_tx_info *info; + enum hal_encrypt_type enctype; + unsigned int mic_len; dma_addr_t paddr; int buf_id; int ret; @@ -5927,7 +5929,12 @@ static int ath11k_mac_mgmt_tx_wmi(struct ath11k *ar, struct ath11k_vif *arvif, ieee80211_is_deauth(hdr->frame_control) || ieee80211_is_disassoc(hdr->frame_control)) && ieee80211_has_protected(hdr->frame_control)) { - skb_put(skb, IEEE80211_CCMP_MIC_LEN); + if (!(skb_cb->flags & ATH11K_SKB_CIPHER_SET)) + ath11k_warn(ab, "WMI management tx frame without ATH11K_SKB_CIPHER_SET"); + + enctype = ath11k_dp_tx_get_encrypt_type(skb_cb->cipher); + mic_len = ath11k_dp_rx_crypto_mic_len(ar, enctype); + skb_put(skb, mic_len); } } From 8dd65ccbdf91e2fe3ab6e4da158b38f81746c3b4 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Tue, 11 Jun 2024 09:42:32 +0300 Subject: [PATCH 07/38] wifi: ath12k: fix per pdev debugfs registration Function ath12k_debugfs_register() is called once inside the function ath12k_mac_hw_register(). However, with single wiphy model, there could be multiple pdevs registered under single hardware. Hence, need to register debugfs for each one of them. Move the caller inside the loop which iterates over all underlying pdevs. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Fixes: f8bde02a26b9 ("wifi: ath12k: initial debugfs support") Signed-off-by: Aditya Kumar Singh Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240529043043.2488031-2-quic_adisi@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index d1529612fddd..4b71dfbfe728 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -9133,9 +9133,9 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) ath12k_err(ar->ab, "ath12k regd update failed: %d\n", ret); goto err_unregister_hw; } - } - ath12k_debugfs_register(ar); + ath12k_debugfs_register(ar); + } return 0; From 4e1eff38d37a27328251ea47602703b811a028e1 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Tue, 11 Jun 2024 09:42:32 +0300 Subject: [PATCH 08/38] wifi: ath12k: unregister per pdev debugfs During normal de-initialization path or if any error happens while registering the hardware, there is no support to unregister the per pdev debugfs. Add support for the same. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aditya Kumar Singh Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240529043043.2488031-3-quic_adisi@quicinc.com --- drivers/net/wireless/ath/ath12k/debugfs.c | 10 ++++++++++ drivers/net/wireless/ath/ath12k/debugfs.h | 6 +++++- drivers/net/wireless/ath/ath12k/mac.c | 7 ++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index 8d8ba951093b..3b464cd40da6 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -88,3 +88,13 @@ void ath12k_debugfs_register(struct ath12k *ar) &fops_simulate_radar); } } + +void ath12k_debugfs_unregister(struct ath12k *ar) +{ + if (!ar->debug.debugfs_pdev) + return; + + /* TODO: Remove symlink under ieee80211/phy* */ + debugfs_remove_recursive(ar->debug.debugfs_pdev); + ar->debug.debugfs_pdev = NULL; +} diff --git a/drivers/net/wireless/ath/ath12k/debugfs.h b/drivers/net/wireless/ath/ath12k/debugfs.h index a62f2a550b23..8d64ba03aa9a 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.h +++ b/drivers/net/wireless/ath/ath12k/debugfs.h @@ -11,7 +11,7 @@ void ath12k_debugfs_soc_create(struct ath12k_base *ab); void ath12k_debugfs_soc_destroy(struct ath12k_base *ab); void ath12k_debugfs_register(struct ath12k *ar); - +void ath12k_debugfs_unregister(struct ath12k *ar); #else static inline void ath12k_debugfs_soc_create(struct ath12k_base *ab) { @@ -25,6 +25,10 @@ static inline void ath12k_debugfs_register(struct ath12k *ar) { } +static inline void ath12k_debugfs_unregister(struct ath12k *ar) +{ +} + #endif /* CONFIG_ATH12K_DEBUGFS */ #endif /* _ATH12K_DEBUGFS_H_ */ diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 4b71dfbfe728..2259ce22cc8b 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -8892,8 +8892,10 @@ static void ath12k_mac_hw_unregister(struct ath12k_hw *ah) struct ath12k *ar; int i; - for_each_ar(ah, ar, i) + for_each_ar(ah, ar, i) { cancel_work_sync(&ar->regd_update_work); + ath12k_debugfs_unregister(ar); + } ieee80211_unregister_hw(hw); @@ -9140,6 +9142,9 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) return 0; err_unregister_hw: + for_each_ar(ah, ar, i) + ath12k_debugfs_unregister(ar); + ieee80211_unregister_hw(hw); err_free_if_combs: From 5a16da9cfb40d45e797df3ae1523ac64fe0b98ae Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Tue, 11 Jun 2024 09:42:33 +0300 Subject: [PATCH 09/38] wifi: ath12k: handle symlink cleanup for per pdev debugfs dentry Whenever per pdev debugfs directory is created, a symlink to it is also placed in ieee80211/phy* directory. During clean up of per pdev debugfs, this symlink also needs to be cleaned up. Add changes to clean up the symlink whenever the per pdev debugfs is removed. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aditya Kumar Singh Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240529043043.2488031-4-quic_adisi@quicinc.com --- drivers/net/wireless/ath/ath12k/core.h | 1 + drivers/net/wireless/ath/ath12k/debugfs.c | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index d03326a68a9f..b483899ca136 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -474,6 +474,7 @@ struct ath12k_fw_stats { struct ath12k_debug { struct dentry *debugfs_pdev; + struct dentry *debugfs_pdev_symlink; }; struct ath12k_per_peer_tx_stats { diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index 3b464cd40da6..751a9b04386a 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -80,7 +80,9 @@ void ath12k_debugfs_register(struct ath12k *ar) /* Create a symlink under ieee80211/phy* */ scnprintf(buf, sizeof(buf), "../../ath12k/%pd2", ar->debug.debugfs_pdev); - debugfs_create_symlink("ath12k", hw->wiphy->debugfsdir, buf); + ar->debug.debugfs_pdev_symlink = debugfs_create_symlink("ath12k", + hw->wiphy->debugfsdir, + buf); if (ar->mac.sbands[NL80211_BAND_5GHZ].channels) { debugfs_create_file("dfs_simulate_radar", 0200, @@ -94,7 +96,9 @@ void ath12k_debugfs_unregister(struct ath12k *ar) if (!ar->debug.debugfs_pdev) return; - /* TODO: Remove symlink under ieee80211/phy* */ + /* Remove symlink under ieee80211/phy* */ + debugfs_remove(ar->debug.debugfs_pdev_symlink); debugfs_remove_recursive(ar->debug.debugfs_pdev); + ar->debug.debugfs_pdev_symlink = NULL; ar->debug.debugfs_pdev = NULL; } From 71839a929d9ee97daab319d370fc0f6720daef43 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 5 Jun 2024 14:21:04 +0200 Subject: [PATCH 10/38] dt-bindings: net: wireless: qcom,ath11k: describe the ath11k on QCA6390 Add a PCI compatible for the ATH11K module on QCA6390 and describe the power inputs from the PMU that it consumes. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski Signed-off-by: Kalle Valo Link: https://msgid.link/20240605122106.23818-2-brgl@bgdev.pl --- .../net/wireless/qcom,ath11k-pci.yaml | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml index 41d023797d7d..8675d7d0215c 100644 --- a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml @@ -17,6 +17,7 @@ description: | properties: compatible: enum: + - pci17cb,1101 # QCA6390 - pci17cb,1103 # WCN6855 reg: @@ -28,10 +29,55 @@ properties: string to uniquely identify variant of the calibration data for designs with colliding bus and device ids + vddrfacmn-supply: + description: VDD_RFA_CMN supply regulator handle + + vddaon-supply: + description: VDD_AON supply regulator handle + + vddwlcx-supply: + description: VDD_WL_CX supply regulator handle + + vddwlmx-supply: + description: VDD_WL_MX supply regulator handle + + vddrfa0p8-supply: + description: VDD_RFA_0P8 supply regulator handle + + vddrfa1p2-supply: + description: VDD_RFA_1P2 supply regulator handle + + vddrfa1p7-supply: + description: VDD_RFA_1P7 supply regulator handle + + vddpcie0p9-supply: + description: VDD_PCIE_0P9 supply regulator handle + + vddpcie1p8-supply: + description: VDD_PCIE_1P8 supply regulator handle + required: - compatible - reg +allOf: + - if: + properties: + compatible: + contains: + const: pci17cb,1101 + then: + required: + - vddrfacmn-supply + - vddaon-supply + - vddwlcx-supply + - vddwlmx-supply + - vddrfa0p8-supply + - vddrfa1p2-supply + - vddrfa1p7-supply + - vddpcie0p9-supply + - vddpcie1p8-supply + additionalProperties: false examples: From aa17d384971b62a8a7dc901c96f615efc7328a62 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 5 Jun 2024 14:21:05 +0200 Subject: [PATCH 11/38] dt-bindings: net: wireless: describe the ath12k PCI module Add device-tree bindings for the ATH12K module found in the WCN7850 package. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski Signed-off-by: Kalle Valo Link: https://msgid.link/20240605122106.23818-3-brgl@bgdev.pl --- .../bindings/net/wireless/qcom,ath12k.yaml | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/wireless/qcom,ath12k.yaml diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath12k.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath12k.yaml new file mode 100644 index 000000000000..1b5884015b15 --- /dev/null +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath12k.yaml @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (c) 2024 Linaro Limited +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/wireless/qcom,ath12k.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies ath12k wireless devices (PCIe) + +maintainers: + - Jeff Johnson + - Kalle Valo + +description: + Qualcomm Technologies IEEE 802.11be PCIe devices. + +properties: + compatible: + enum: + - pci17cb,1107 # WCN7850 + + reg: + maxItems: 1 + + vddaon-supply: + description: VDD_AON supply regulator handle + + vddwlcx-supply: + description: VDD_WLCX supply regulator handle + + vddwlmx-supply: + description: VDD_WLMX supply regulator handle + + vddrfacmn-supply: + description: VDD_RFA_CMN supply regulator handle + + vddrfa0p8-supply: + description: VDD_RFA_0P8 supply regulator handle + + vddrfa1p2-supply: + description: VDD_RFA_1P2 supply regulator handle + + vddrfa1p8-supply: + description: VDD_RFA_1P8 supply regulator handle + + vddpcie0p9-supply: + description: VDD_PCIE_0P9 supply regulator handle + + vddpcie1p8-supply: + description: VDD_PCIE_1P8 supply regulator handle + +required: + - compatible + - reg + - vddaon-supply + - vddwlcx-supply + - vddwlmx-supply + - vddrfacmn-supply + - vddrfa0p8-supply + - vddrfa1p2-supply + - vddrfa1p8-supply + - vddpcie0p9-supply + - vddpcie1p8-supply + +additionalProperties: false + +examples: + - | + #include + #include + pcie { + #address-cells = <3>; + #size-cells = <2>; + + pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + + bus-range = <0x01 0xff>; + + wifi@0 { + compatible = "pci17cb,1107"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; + vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; + }; + }; + }; From 670d4949bc8e7178420761130664f563453085bd Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Mon, 17 Jun 2024 14:12:47 +0300 Subject: [PATCH 12/38] wifi: ath12k: Fix WARN_ON during firmware crash in split-phy Whenever firmware is crashed in split-phy below WARN_ON() triggered: WARNING: CPU: 3 PID: 82 at net/mac80211/driver-ops.c:41 drv_stop+0xac/0xbc Modules linked in: ath12k qmi_helpers CPU: 3 PID: 82 Comm: kworker/3:2 Tainted: G D W 6.9.0-next-20240520-00113-gd981a3784e15 #39 Hardware name: Qualcomm Technologies, Inc. IPQ9574/AP-AL02-C9 (DT) Workqueue: events_freezable ieee80211_restart_work pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : drv_stop+0xac/0xbc lr : ieee80211_stop_device+0x54/0x64 sp : ffff8000848dbb20 x29: ffff8000848dbb20 x28: 0000000000000790 x27: ffff000014d78900 x26: ffff000014d791f8 x25: ffff000007f0d9b0 x24: 0000000000000018 x23: 0000000000000001 x22: 0000000000000000 x21: ffff000014d78e10 x20: ffff800081dc0000 x19: ffff000014d78900 x18: ffffffffffffffff x17: ffff7fffbca84000 x16: ffff800083fe0000 x15: ffff800081dc0b48 x14: 0000000000000076 x13: 0000000000000076 x12: 0000000000000001 x11: 0000000000000000 x10: 0000000000000a60 x9 : ffff8000848db980 x8 : ffff000000dddfc0 x7 : 0000000000000400 x6 : ffff800083b012d8 x5 : ffff800083b012d8 x4 : 0000000000000000 x3 : ffff000014d78398 x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff000014d78900 Call trace: drv_stop+0xac/0xbc ieee80211_stop_device+0x54/0x64 ieee80211_do_stop+0x5a0/0x790 ieee80211_stop+0x4c/0x178 __dev_close_many+0xb0/0x150 dev_close_many+0x88/0x130 dev_close.part.171+0x44/0x74 dev_close+0x1c/0x28 cfg80211_shutdown_all_interfaces+0x44/0xfc ieee80211_restart_work+0xfc/0x14c process_scheduled_works+0x18c/0x2dc worker_thread+0x13c/0x314 kthread+0x118/0x124 ret_from_fork+0x10/0x20 ---[ end trace 0000000000000000 ]--- The warning in question is from drv_stop(): if (WARN_ON(!local->started)) return; The sequence of WARN_ON() is: Thread 1: -Firmware crash calls ath12k_core_reset(). -Call ieee80211_restart_hw() inside ath12k_core_post_reconfigure_recovery() which schedules worker for both hardware. -Wait for completion of ab->recovery_start. Thread 2 (worker thread): -One hardware acquires rtnl_lock() inside ieee80211_restart_hw() and calls ath12k_mac_wait_reconfigure() into ath12k_mac_op_start(). -Hardware is waiting for ab->reconfigure_complete but at this time recovery_start_count value is 1 because another worker thread (local->restart_work) is still waiting for rtnl_lock(). recovery_start_count is not equal to number of radios (2 in split-phy). So ab->recovery_start complete does not set due to this, thread 1 is still waiting and not able to perform hif power down up and firmware reload. -Wait timeout happens for ab->reconfigure_complete and comeback to caller (ath12k_mac_op_start()) and sends WMI command to crashed firmware and gets error. -This returns error to drv_start() and local->started is set to false. -Hardware calls cfg80211_shutdown_all_interfaces() after receiving error inside ieee80211_restart_work() and goes to drv_stop(), here we trigger WARN_ON as local->started is false. To fix this issue call ieee80211_restart_hw() after firmware has been reloaded. Now, each hardware can send WMI command to firmware successfully. With this fix we don't need to wait for ab->recovery_start completion so remove ath12k_mac_wait_reconfigure(). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.1.1-00209-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: Aaradhana Sahu Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240529034405.2863150-1-quic_aarasahu@quicinc.com --- drivers/net/wireless/ath/ath12k/core.c | 21 ++++++++------------- drivers/net/wireless/ath/ath12k/core.h | 3 --- drivers/net/wireless/ath/ath12k/mac.c | 23 ----------------------- 3 files changed, 8 insertions(+), 39 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index da02d3528737..197099f15e3f 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -1061,8 +1061,6 @@ static void ath12k_core_post_reconfigure_recovery(struct ath12k_base *ab) mutex_unlock(&ar->conf_mutex); } - /* Restart after all the link/radio halt */ - ieee80211_restart_hw(ah->hw); break; case ATH12K_HW_STATE_OFF: ath12k_warn(ab, @@ -1089,7 +1087,8 @@ static void ath12k_core_post_reconfigure_recovery(struct ath12k_base *ab) static void ath12k_core_restart(struct work_struct *work) { struct ath12k_base *ab = container_of(work, struct ath12k_base, restart_work); - int ret; + struct ath12k_hw *ah; + int ret, i; ret = ath12k_core_reconfigure_on_crash(ab); if (ret) { @@ -1097,8 +1096,12 @@ static void ath12k_core_restart(struct work_struct *work) return; } - if (ab->is_reset) - complete_all(&ab->reconfigure_complete); + if (ab->is_reset) { + for (i = 0; i < ab->num_hw; i++) { + ah = ab->ah[i]; + ieee80211_restart_hw(ah->hw); + } + } complete(&ab->restart_completed); } @@ -1152,20 +1155,14 @@ static void ath12k_core_reset(struct work_struct *work) ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset starting\n"); ab->is_reset = true; - atomic_set(&ab->recovery_start_count, 0); - reinit_completion(&ab->recovery_start); atomic_set(&ab->recovery_count, 0); ath12k_core_pre_reconfigure_recovery(ab); - reinit_completion(&ab->reconfigure_complete); ath12k_core_post_reconfigure_recovery(ab); ath12k_dbg(ab, ATH12K_DBG_BOOT, "waiting recovery start...\n"); - time_left = wait_for_completion_timeout(&ab->recovery_start, - ATH12K_RECOVER_START_TIMEOUT_HZ); - ath12k_hif_irq_disable(ab); ath12k_hif_ce_irq_disable(ab); @@ -1277,8 +1274,6 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, mutex_init(&ab->core_lock); spin_lock_init(&ab->base_lock); init_completion(&ab->reset_complete); - init_completion(&ab->reconfigure_complete); - init_completion(&ab->recovery_start); INIT_LIST_HEAD(&ab->peers); init_waitqueue_head(&ab->peer_mapping_wq); diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index b483899ca136..82ec23a7329d 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -849,11 +849,8 @@ struct ath12k_base { struct work_struct reset_work; atomic_t reset_count; atomic_t recovery_count; - atomic_t recovery_start_count; bool is_reset; struct completion reset_complete; - struct completion reconfigure_complete; - struct completion recovery_start; /* continuous recovery fail count */ atomic_t fail_cont_count; unsigned long reset_fail_timeout; diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 2259ce22cc8b..1f00a2362ae6 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -5834,28 +5834,6 @@ static int ath12k_mac_config_mon_status_default(struct ath12k *ar, bool enable) /* TODO: Need to support new monitor mode */ } -static void ath12k_mac_wait_reconfigure(struct ath12k_base *ab) -{ - int recovery_start_count; - - if (!ab->is_reset) - return; - - recovery_start_count = atomic_inc_return(&ab->recovery_start_count); - - ath12k_dbg(ab, ATH12K_DBG_MAC, "recovery start count %d\n", recovery_start_count); - - if (recovery_start_count == ab->num_radios) { - complete(&ab->recovery_start); - ath12k_dbg(ab, ATH12K_DBG_MAC, "recovery started success\n"); - } - - ath12k_dbg(ab, ATH12K_DBG_MAC, "waiting reconfigure...\n"); - - wait_for_completion_timeout(&ab->reconfigure_complete, - ATH12K_RECONFIGURE_TIMEOUT_HZ); -} - static int ath12k_mac_start(struct ath12k *ar) { struct ath12k_hw *ah = ar->ah; @@ -5987,7 +5965,6 @@ static int ath12k_mac_op_start(struct ieee80211_hw *hw) break; case ATH12K_HW_STATE_RESTARTING: ah->state = ATH12K_HW_STATE_RESTARTED; - ath12k_mac_wait_reconfigure(ah->ab); break; case ATH12K_HW_STATE_RESTARTED: case ATH12K_HW_STATE_WEDGED: From 4f15b06e5782e9a44acc652034090b5fc40b49e5 Mon Sep 17 00:00:00 2001 From: Harshitha Prem Date: Mon, 17 Jun 2024 14:12:47 +0300 Subject: [PATCH 13/38] wifi: ath12k: Remove unused ath12k_base from ath12k_hw Currently, device (ab) reference in hardware abstraction (ah) is not used anywhere. Also, with multiple device group abstraction, hardware abstraction would be coupled with device group abstraction rather than single device. Hence, remove the ab reference from hardware abstraction. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Signed-off-by: Harshitha Prem Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://msgid.link/20240529060939.4156281-1-quic_hprem@quicinc.com --- drivers/net/wireless/ath/ath12k/core.h | 2 -- drivers/net/wireless/ath/ath12k/mac.c | 1 - 2 files changed, 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 82ec23a7329d..d7a3e8fddb20 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -630,8 +630,6 @@ struct ath12k { struct ath12k_hw { struct ieee80211_hw *hw; - struct ath12k_base *ab; - /* Protect the write operation of the hardware state ath12k_hw::state * between hardware start<=>reconfigure<=>stop transitions. */ diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 1f00a2362ae6..bc2da8530651 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -9254,7 +9254,6 @@ static struct ath12k_hw *ath12k_mac_hw_allocate(struct ath12k_base *ab, ah = ath12k_hw_to_ah(hw); ah->hw = hw; - ah->ab = ab; ah->num_radio = num_pdev_map; mutex_init(&ah->hw_mutex); From 94f228ac4f5b8c22f83865e51d3346c2d44d370d Mon Sep 17 00:00:00 2001 From: Lingbo Kong Date: Tue, 11 Jun 2024 10:25:50 +0800 Subject: [PATCH 14/38] wifi: ath11k: fix ack signal strength calculation Currently, the calculation of ack signal strength is incorrect. This is because before calculating the ack signal strength, ath11k need to determine whether the hardware and firmware support db2dbm. If the hardware and firmware support db2dbm, do not need to add noise floor, otherwise, need to add noise floor. Besides, the value of ack_rssi passed by firmware to ath11k should be a signed number, so change its type to s8. After that, "iw wlan0 station dump" show the correct ack signal strength. Such as: root@CDCCSTEX0799733-LIN:~# iw wlp88s0 station dump Station 00:03:7f:12:df:df (on wlp88s0) inactive time: 75 ms rx bytes: 11599 rx packets: 99 tx bytes: 9029 tx packets: 81 tx retries: 4 tx failed: 0 rx drop misc: 2 signal: -16 dBm signal avg: -24 dBm tx bitrate: 1560.0 MBit/s VHT-MCS 9 80MHz VHT-NSS 4 tx duration: 9230 us rx bitrate: 1560.0 MBit/s VHT-MCS 9 80MHz VHT-NSS 4 rx duration: 7201 us last ack signal:-23 dBm avg ack signal: -22 dBm Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30 Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Signed-off-by: Lingbo Kong Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240611022550.59078-1-quic_lingbok@quicinc.com --- drivers/net/wireless/ath/ath11k/dp_tx.c | 16 ++++++++++++---- drivers/net/wireless/ath/ath11k/dp_tx.h | 4 ++-- drivers/net/wireless/ath/ath11k/hal_tx.h | 4 ++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c index 21819b741701..8522c67baabf 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.c +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -353,8 +353,12 @@ ath11k_dp_tx_htt_tx_complete_buf(struct ath11k_base *ab, if (ts->acked) { if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { info->flags |= IEEE80211_TX_STAT_ACK; - info->status.ack_signal = ATH11K_DEFAULT_NOISE_FLOOR + - ts->ack_rssi; + info->status.ack_signal = ts->ack_rssi; + + if (!test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT, + ab->wmi_ab.svc_map)) + info->status.ack_signal += ATH11K_DEFAULT_NOISE_FLOOR; + info->status.flags |= IEEE80211_TX_STATUS_ACK_SIGNAL_VALID; } else { @@ -584,8 +588,12 @@ static void ath11k_dp_tx_complete_msdu(struct ath11k *ar, if (ts->status == HAL_WBM_TQM_REL_REASON_FRAME_ACKED && !(info->flags & IEEE80211_TX_CTL_NO_ACK)) { info->flags |= IEEE80211_TX_STAT_ACK; - info->status.ack_signal = ATH11K_DEFAULT_NOISE_FLOOR + - ts->ack_rssi; + info->status.ack_signal = ts->ack_rssi; + + if (!test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT, + ab->wmi_ab.svc_map)) + info->status.ack_signal += ATH11K_DEFAULT_NOISE_FLOOR; + info->status.flags |= IEEE80211_TX_STATUS_ACK_SIGNAL_VALID; } diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.h b/drivers/net/wireless/ath/ath11k/dp_tx.h index 61be2265e09f..795fe3b8fa0d 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.h +++ b/drivers/net/wireless/ath/ath11k/dp_tx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021, 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021, 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_DP_TX_H @@ -13,7 +13,7 @@ struct ath11k_dp_htt_wbm_tx_status { u32 msdu_id; bool acked; - int ack_rssi; + s8 ack_rssi; u16 peer_id; }; diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.h b/drivers/net/wireless/ath/ath11k/hal_tx.h index c5e88364afe5..46d17abd808b 100644 --- a/drivers/net/wireless/ath/ath11k/hal_tx.h +++ b/drivers/net/wireless/ath/ath11k/hal_tx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_HAL_TX_H @@ -54,7 +54,7 @@ struct hal_tx_info { struct hal_tx_status { enum hal_wbm_rel_src_module buf_rel_source; enum hal_wbm_tqm_rel_reason status; - u8 ack_rssi; + s8 ack_rssi; u32 flags; /* %HAL_TX_STATUS_FLAGS_ */ u32 ppdu_id; u8 try_cnt; From aadeee47e9d7ae98f7116f63fa63fcceffffc65c Mon Sep 17 00:00:00 2001 From: Lingbo Kong Date: Sat, 9 Mar 2024 20:11:29 +0800 Subject: [PATCH 15/38] wifi: ath11k: modify the calculation of the average signal strength in station mode Currently, the calculation of the average signal strength in station mode is incorrect. This is because before calculating the average signal strength, ath11k need to determine whether the hardware and firmware support db2dbm, if the hardware and firmware support db2dbm, do not need to add noise floor, otherwise, need to add noise floor. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 Signed-off-by: Lingbo Kong Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240309121129.5379-1-quic_lingbok@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index bb56160048ba..779009774567 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -8984,8 +8984,11 @@ static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw, sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); } - sinfo->signal_avg = ewma_avg_rssi_read(&arsta->avg_rssi) + - ATH11K_DEFAULT_NOISE_FLOOR; + sinfo->signal_avg = ewma_avg_rssi_read(&arsta->avg_rssi); + + if (!db2dbm) + sinfo->signal_avg += ATH11K_DEFAULT_NOISE_FLOOR; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); } From 59ca26c9a6e844204d25299774caa5441c0e68eb Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Tue, 11 Jun 2024 08:40:16 +0530 Subject: [PATCH 16/38] wifi: ath12k: fix NULL pointer access in ath12k_mac_op_get_survey() Smatch throws below error drivers/net/wireless/ath/ath12k/mac.c:8318 ath12k_mac_op_get_survey() error: we previously assumed 'sband' could be null Currently, we access sband inside the null check of the sband in ath12k_mac_op_get_survey(). Fix this issue by removing the entire if block, because decrement idx is unnecessary since there are no more band to test. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Fixes: 70e3be54bbdd ("wifi: ath12k: fix survey dump collection in 6 GHz") Signed-off-by: Aaradhana Sahu Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240611031017.297927-2-quic_aarasahu@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index bc2da8530651..00a158c22971 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -8291,10 +8291,6 @@ static int ath12k_mac_op_get_survey(struct ieee80211_hw *hw, int idx, if (!sband) sband = hw->wiphy->bands[NL80211_BAND_6GHZ]; - if (!sband || idx >= sband->n_channels) { - idx -= sband->n_channels; - sband = NULL; - } if (!sband || idx >= sband->n_channels) return -ENOENT; From 19b77e7c656a3e125319cc3ef347b397cf042bf6 Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Tue, 11 Jun 2024 08:40:17 +0530 Subject: [PATCH 17/38] wifi: ath12k: fix uninitialize symbol error on ath12k_peer_assoc_h_he() Smatch throws following errors drivers/net/wireless/ath/ath12k/mac.c:1922 ath12k_peer_assoc_h_he() error: uninitialized symbol 'rx_mcs_80'. drivers/net/wireless/ath/ath12k/mac.c:1922 ath12k_peer_assoc_h_he() error: uninitialized symbol 'rx_mcs_160'. drivers/net/wireless/ath/ath12k/mac.c:1924 ath12k_peer_assoc_h_he() error: uninitialized symbol 'rx_mcs_80'. In ath12k_peer_assoc_h_he() rx_mcs_80 and rx_mcs_160 variables remain uninitialized in the following conditions: 1. Whenever the value of mcs_80 become equal to IEEE80211_HE_MCS_NOT_SUPPORTED then rx_mcs_80 remains uninitialized. 2. Whenever phy capability is not supported 160 channel width and value of mcs_160 become equal to IEEE80211_HE_MCS_NOT_SUPPORTED then rx_mcs_160 remains uninitialized. Initialize these variables during declaration. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.1.1-00188-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240611031017.297927-3-quic_aarasahu@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 00a158c22971..2a4ca73dfc22 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -2050,7 +2050,9 @@ static void ath12k_peer_assoc_h_he(struct ath12k *ar, { const struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap; int i; - u8 ampdu_factor, rx_mcs_80, rx_mcs_160, max_nss; + u8 ampdu_factor, max_nss; + u8 rx_mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED; + u8 rx_mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED; u16 mcs_160_map, mcs_80_map; bool support_160; u16 v; From 3005c63a3673eaf3202b2e0c951e5092539bf758 Mon Sep 17 00:00:00 2001 From: Pradeep Kumar Chitrapu Date: Wed, 12 Jun 2024 15:53:36 -0700 Subject: [PATCH 18/38] wifi: ath12k: fix legacy peer association due to missing HT or 6 GHz capabilities Currently SMPS configuration failed when the Information Elements (IEs) did not contain HT or 6 GHz capabilities. This caused legacy peer association to fail as legacy peers do not have HT or 6 GHz capabilities. Fix this by not returning an error when SMPS configuration fails due to the absence of HT or 6 GHz capabilities. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Fixes: f0e61dc7ecf9 ("wifi: ath12k: refactor SMPS configuration") Reported-by: Aditya Kumar Singh Reported-by: Zachary Smith Closes: https://lore.kernel.org/all/CAM=znoFPcXrn5GhDmDmo50Syic3-hXpWvD+vkv8KX5o_ZTo8kQ@mail.gmail.com/ Signed-off-by: Pradeep Kumar Chitrapu Reported-by: Aditya Kumar Singh Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240612225336.2303119-1-quic_pradeepc@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 2a4ca73dfc22..af19870eb8c0 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -2257,9 +2257,6 @@ static int ath12k_get_smps_from_capa(const struct ieee80211_sta_ht_cap *ht_cap, const struct ieee80211_he_6ghz_capa *he_6ghz_capa, int *smps) { - if (!ht_cap->ht_supported && !he_6ghz_capa->capa) - return -EOPNOTSUPP; - if (ht_cap->ht_supported) *smps = u16_get_bits(ht_cap->cap, IEEE80211_HT_CAP_SM_PS); else @@ -2279,6 +2276,9 @@ static void ath12k_peer_assoc_h_smps(struct ieee80211_sta *sta, const struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap; int smps; + if (!ht_cap->ht_supported && !he_6ghz_capa->capa) + return; + if (ath12k_get_smps_from_capa(ht_cap, he_6ghz_capa, &smps)) return; @@ -2758,6 +2758,9 @@ static int ath12k_setup_peer_smps(struct ath12k *ar, struct ath12k_vif *arvif, { int smps, ret = 0; + if (!ht_cap->ht_supported && !he_6ghz_capa) + return 0; + ret = ath12k_get_smps_from_capa(ht_cap, he_6ghz_capa, &smps); if (ret < 0) return ret; From db163a463bb93cd3e37e1e7b10b9726fb6f95857 Mon Sep 17 00:00:00 2001 From: Ajith C Date: Thu, 13 Jun 2024 11:05:28 +0530 Subject: [PATCH 19/38] wifi: ath12k: fix firmware crash due to invalid peer nss Currently, if the access point receives an association request containing an Extended HE Capabilities Information Element with an invalid MCS-NSS, it triggers a firmware crash. This issue arises when EHT-PHY capabilities shows support for a bandwidth and MCS-NSS set for that particular bandwidth is filled by zeros and due to this, driver obtains peer_nss as 0 and sending this value to firmware causes crash. Address this issue by implementing a validation step for the peer_nss value before passing it to the firmware. If the value is greater than zero, proceed with forwarding it to the firmware. However, if the value is invalid, reject the association request to prevent potential firmware crashes. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Signed-off-by: Ajith C Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240613053528.2541645-1-quic_ajithc@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index af19870eb8c0..f9caed8ffd63 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -4092,6 +4092,11 @@ static int ath12k_station_assoc(struct ath12k *ar, ath12k_peer_assoc_prepare(ar, vif, sta, &peer_arg, reassoc); + if (peer_arg.peer_nss < 1) { + ath12k_warn(ar->ab, + "invalid peer NSS %d\n", peer_arg.peer_nss); + return -EINVAL; + } ret = ath12k_wmi_send_peer_assoc_cmd(ar, &peer_arg); if (ret) { ath12k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n", From 253ec89c9013b0e61f5c54344df7c065d54934d1 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Thu, 13 Jun 2024 21:08:13 +0530 Subject: [PATCH 20/38] wifi: ath12k: fix mbssid max interface advertisement The Current method for advertising the maximum MBSSID interface count assumes single radio per wiphy (multi wiphy model). However, this assumption is incorrect for multi radio per wiphy (single wiphy model). Therefore, populate the parameter for each radio present in the MAC abstraction layer (ah). This approach ensure scalability for both single wiphy and multi wiphy models. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Fixes: 519a545cfee7 ("wifi: ath12k: advertise driver capabilities for MBSSID and EMA") Signed-off-by: Karthikeyan Periyasamy Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240613153813.3509837-1-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index f9caed8ffd63..17b7fab03d50 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -8942,6 +8942,7 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) u32 ht_cap = U32_MAX, antennas_rx = 0, antennas_tx = 0; bool is_6ghz = false, is_raw_mode = false, is_monitor_disable = false; u8 *mac_addr = NULL; + u8 mbssid_max_interfaces = 0; wiphy->max_ap_assoc_sta = 0; @@ -8985,6 +8986,8 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) mac_addr = ar->mac_addr; else mac_addr = ab->mac_addr; + + mbssid_max_interfaces += TARGET_NUM_VDEVS; } wiphy->available_antennas_rx = antennas_rx; @@ -9077,7 +9080,7 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) wiphy->iftype_ext_capab = ath12k_iftypes_ext_capa; wiphy->num_iftype_ext_capab = ARRAY_SIZE(ath12k_iftypes_ext_capa); - wiphy->mbssid_max_interfaces = TARGET_NUM_VDEVS; + wiphy->mbssid_max_interfaces = mbssid_max_interfaces; wiphy->ema_max_profile_periodicity = TARGET_EMA_MAX_PROFILE_PERIOD; if (is_6ghz) { From 3216b7bcd7197ca5da204d3eea81d308cae2a849 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 19 Jun 2024 17:21:11 +0300 Subject: [PATCH 21/38] wifi: ath12k: add ATH12K_DBG_WOW log level Currently there is no dedicated log level for WoW, so create it for future use. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240604055407.12506-2-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/debug.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/debug.h b/drivers/net/wireless/ath/ath12k/debug.h index aa685295f8a4..f7005917362c 100644 --- a/drivers/net/wireless/ath/ath12k/debug.h +++ b/drivers/net/wireless/ath/ath12k/debug.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _ATH12K_DEBUG_H_ @@ -25,6 +25,7 @@ enum ath12k_debug_mask { ATH12K_DBG_PCI = 0x00001000, ATH12K_DBG_DP_TX = 0x00002000, ATH12K_DBG_DP_RX = 0x00004000, + ATH12K_DBG_WOW = 0x00008000, ATH12K_DBG_ANY = 0xffffffff, }; From 5931741709195da9abb0fb0b8c0fed5d0898b275 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 19 Jun 2024 17:21:11 +0300 Subject: [PATCH 22/38] wifi: ath12k: implement WoW enable and wakeup commands Implement WoW enable and WoW wakeup commands which are needed for suspend/resume. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240604055407.12506-3-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/Makefile | 1 + drivers/net/wireless/ath/ath12k/core.c | 2 + drivers/net/wireless/ath/ath12k/core.h | 4 + drivers/net/wireless/ath/ath12k/wmi.c | 105 ++++++++++++++ drivers/net/wireless/ath/ath12k/wmi.h | 174 +++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/wow.c | 79 ++++++++++ drivers/net/wireless/ath/ath12k/wow.h | 30 ++++ 7 files changed, 395 insertions(+) create mode 100644 drivers/net/wireless/ath/ath12k/wow.c create mode 100644 drivers/net/wireless/ath/ath12k/wow.h diff --git a/drivers/net/wireless/ath/ath12k/Makefile b/drivers/net/wireless/ath/ath12k/Makefile index d42480db7463..f0dcc933aa95 100644 --- a/drivers/net/wireless/ath/ath12k/Makefile +++ b/drivers/net/wireless/ath/ath12k/Makefile @@ -26,6 +26,7 @@ ath12k-y += core.o \ ath12k-$(CONFIG_ATH12K_DEBUGFS) += debugfs.o ath12k-$(CONFIG_ACPI) += acpi.o ath12k-$(CONFIG_ATH12K_TRACING) += trace.o +ath12k-$(CONFIG_PM) += wow.o # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 197099f15e3f..7603516500ba 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -16,6 +16,7 @@ #include "hif.h" #include "fw.h" #include "debugfs.h" +#include "wow.h" unsigned int ath12k_debug_mask; module_param_named(debug_mask, ath12k_debug_mask, uint, 0644); @@ -1285,6 +1286,7 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, timer_setup(&ab->rx_replenish_retry, ath12k_ce_rx_replenish_retry, 0); init_completion(&ab->htc_suspend); init_completion(&ab->restart_completed); + init_completion(&ab->wow.wakeup_completed); ab->dev = dev; ab->hif.bus = bus; diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index d7a3e8fddb20..10aa3aca7250 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -765,6 +765,10 @@ struct ath12k_base { const struct ath12k_hif_ops *ops; } hif; + struct { + struct completion wakeup_completed; + } wow; + struct ath12k_ce ce; struct timer_list rx_replenish_retry; struct ath12k_hal hal; diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index d6e1d1398cdb..3eafa46d737b 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -7030,6 +7030,66 @@ static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab, kfree(tb); } +static int ath12k_wmi_wow_wakeup_host_parse(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + const struct wmi_wow_ev_pg_fault_param *pf_param; + const struct wmi_wow_ev_param *param; + struct wmi_wow_ev_arg *arg = data; + int pf_len; + + switch (tag) { + case WMI_TAG_WOW_EVENT_INFO: + param = ptr; + arg->wake_reason = le32_to_cpu(param->wake_reason); + ath12k_dbg(ab, ATH12K_DBG_WMI, "wow wakeup host reason %d %s\n", + arg->wake_reason, wow_reason(arg->wake_reason)); + break; + + case WMI_TAG_ARRAY_BYTE: + if (arg && arg->wake_reason == WOW_REASON_PAGE_FAULT) { + pf_param = ptr; + pf_len = le32_to_cpu(pf_param->len); + if (pf_len > len - sizeof(pf_len) || + pf_len < 0) { + ath12k_warn(ab, "invalid wo reason page fault buffer len %d\n", + pf_len); + return -EINVAL; + } + ath12k_dbg(ab, ATH12K_DBG_WMI, "wow_reason_page_fault len %d\n", + pf_len); + ath12k_dbg_dump(ab, ATH12K_DBG_WMI, + "wow_reason_page_fault packet present", + "wow_pg_fault ", + pf_param->data, + pf_len); + } + break; + default: + break; + } + + return 0; +} + +static void ath12k_wmi_event_wow_wakeup_host(struct ath12k_base *ab, struct sk_buff *skb) +{ + struct wmi_wow_ev_arg arg = { }; + int ret; + + ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len, + ath12k_wmi_wow_wakeup_host_parse, + &arg); + if (ret) { + ath12k_warn(ab, "failed to parse wmi wow wakeup host event tlv: %d\n", + ret); + return; + } + + complete(&ab->wow.wakeup_completed); +} + static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; @@ -7150,6 +7210,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) case WMI_DIAG_EVENTID: ath12k_wmi_diag_event(ab, skb); break; + case WMI_WOW_WAKEUP_HOST_EVENTID: + ath12k_wmi_event_wow_wakeup_host(ab, skb); + break; /* TODO: Add remaining events */ default: ath12k_dbg(ab, ATH12K_DBG_WMI, "Unknown eventid: 0x%x\n", id); @@ -7359,3 +7422,45 @@ void ath12k_wmi_detach(struct ath12k_base *ab) ath12k_wmi_free_dbring_caps(ab); } + +int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar) +{ + struct wmi_wow_host_wakeup_cmd *cmd; + struct sk_buff *skb; + size_t len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_wow_host_wakeup_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_WOW_HOSTWAKEUP_FROM_SLEEP_CMD, + sizeof(*cmd)); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow host wakeup ind\n"); + + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); +} + +int ath12k_wmi_wow_enable(struct ath12k *ar) +{ + struct wmi_wow_enable_cmd *cmd; + struct sk_buff *skb; + int len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_wow_enable_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_WOW_ENABLE_CMD, + sizeof(*cmd)); + + cmd->enable = cpu_to_le32(1); + cmd->pause_iface_config = cpu_to_le32(WOW_IFACE_PAUSE_ENABLED); + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow enable\n"); + + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); +} diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index c2b86e187a03..9cee3a114645 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -4902,6 +4902,178 @@ struct wmi_twt_disable_event { __le32 status; } __packed; +/* WOW structures */ +enum wmi_wow_wakeup_event { + WOW_BMISS_EVENT = 0, + WOW_BETTER_AP_EVENT, + WOW_DEAUTH_RECVD_EVENT, + WOW_MAGIC_PKT_RECVD_EVENT, + WOW_GTK_ERR_EVENT, + WOW_FOURWAY_HSHAKE_EVENT, + WOW_EAPOL_RECVD_EVENT, + WOW_NLO_DETECTED_EVENT, + WOW_DISASSOC_RECVD_EVENT, + WOW_PATTERN_MATCH_EVENT, + WOW_CSA_IE_EVENT, + WOW_PROBE_REQ_WPS_IE_EVENT, + WOW_AUTH_REQ_EVENT, + WOW_ASSOC_REQ_EVENT, + WOW_HTT_EVENT, + WOW_RA_MATCH_EVENT, + WOW_HOST_AUTO_SHUTDOWN_EVENT, + WOW_IOAC_MAGIC_EVENT, + WOW_IOAC_SHORT_EVENT, + WOW_IOAC_EXTEND_EVENT, + WOW_IOAC_TIMER_EVENT, + WOW_DFS_PHYERR_RADAR_EVENT, + WOW_BEACON_EVENT, + WOW_CLIENT_KICKOUT_EVENT, + WOW_EVENT_MAX, +}; + +enum wmi_wow_interface_cfg { + WOW_IFACE_PAUSE_ENABLED, + WOW_IFACE_PAUSE_DISABLED +}; + +#define C2S(x) case x: return #x + +static inline const char *wow_wakeup_event(enum wmi_wow_wakeup_event ev) +{ + switch (ev) { + C2S(WOW_BMISS_EVENT); + C2S(WOW_BETTER_AP_EVENT); + C2S(WOW_DEAUTH_RECVD_EVENT); + C2S(WOW_MAGIC_PKT_RECVD_EVENT); + C2S(WOW_GTK_ERR_EVENT); + C2S(WOW_FOURWAY_HSHAKE_EVENT); + C2S(WOW_EAPOL_RECVD_EVENT); + C2S(WOW_NLO_DETECTED_EVENT); + C2S(WOW_DISASSOC_RECVD_EVENT); + C2S(WOW_PATTERN_MATCH_EVENT); + C2S(WOW_CSA_IE_EVENT); + C2S(WOW_PROBE_REQ_WPS_IE_EVENT); + C2S(WOW_AUTH_REQ_EVENT); + C2S(WOW_ASSOC_REQ_EVENT); + C2S(WOW_HTT_EVENT); + C2S(WOW_RA_MATCH_EVENT); + C2S(WOW_HOST_AUTO_SHUTDOWN_EVENT); + C2S(WOW_IOAC_MAGIC_EVENT); + C2S(WOW_IOAC_SHORT_EVENT); + C2S(WOW_IOAC_EXTEND_EVENT); + C2S(WOW_IOAC_TIMER_EVENT); + C2S(WOW_DFS_PHYERR_RADAR_EVENT); + C2S(WOW_BEACON_EVENT); + C2S(WOW_CLIENT_KICKOUT_EVENT); + C2S(WOW_EVENT_MAX); + default: + return NULL; + } +} + +enum wmi_wow_wake_reason { + WOW_REASON_UNSPECIFIED = -1, + WOW_REASON_NLOD = 0, + WOW_REASON_AP_ASSOC_LOST, + WOW_REASON_LOW_RSSI, + WOW_REASON_DEAUTH_RECVD, + WOW_REASON_DISASSOC_RECVD, + WOW_REASON_GTK_HS_ERR, + WOW_REASON_EAP_REQ, + WOW_REASON_FOURWAY_HS_RECV, + WOW_REASON_TIMER_INTR_RECV, + WOW_REASON_PATTERN_MATCH_FOUND, + WOW_REASON_RECV_MAGIC_PATTERN, + WOW_REASON_P2P_DISC, + WOW_REASON_WLAN_HB, + WOW_REASON_CSA_EVENT, + WOW_REASON_PROBE_REQ_WPS_IE_RECV, + WOW_REASON_AUTH_REQ_RECV, + WOW_REASON_ASSOC_REQ_RECV, + WOW_REASON_HTT_EVENT, + WOW_REASON_RA_MATCH, + WOW_REASON_HOST_AUTO_SHUTDOWN, + WOW_REASON_IOAC_MAGIC_EVENT, + WOW_REASON_IOAC_SHORT_EVENT, + WOW_REASON_IOAC_EXTEND_EVENT, + WOW_REASON_IOAC_TIMER_EVENT, + WOW_REASON_ROAM_HO, + WOW_REASON_DFS_PHYERR_RADADR_EVENT, + WOW_REASON_BEACON_RECV, + WOW_REASON_CLIENT_KICKOUT_EVENT, + WOW_REASON_PAGE_FAULT = 0x3a, + WOW_REASON_DEBUG_TEST = 0xFF, +}; + +static inline const char *wow_reason(enum wmi_wow_wake_reason reason) +{ + switch (reason) { + C2S(WOW_REASON_UNSPECIFIED); + C2S(WOW_REASON_NLOD); + C2S(WOW_REASON_AP_ASSOC_LOST); + C2S(WOW_REASON_LOW_RSSI); + C2S(WOW_REASON_DEAUTH_RECVD); + C2S(WOW_REASON_DISASSOC_RECVD); + C2S(WOW_REASON_GTK_HS_ERR); + C2S(WOW_REASON_EAP_REQ); + C2S(WOW_REASON_FOURWAY_HS_RECV); + C2S(WOW_REASON_TIMER_INTR_RECV); + C2S(WOW_REASON_PATTERN_MATCH_FOUND); + C2S(WOW_REASON_RECV_MAGIC_PATTERN); + C2S(WOW_REASON_P2P_DISC); + C2S(WOW_REASON_WLAN_HB); + C2S(WOW_REASON_CSA_EVENT); + C2S(WOW_REASON_PROBE_REQ_WPS_IE_RECV); + C2S(WOW_REASON_AUTH_REQ_RECV); + C2S(WOW_REASON_ASSOC_REQ_RECV); + C2S(WOW_REASON_HTT_EVENT); + C2S(WOW_REASON_RA_MATCH); + C2S(WOW_REASON_HOST_AUTO_SHUTDOWN); + C2S(WOW_REASON_IOAC_MAGIC_EVENT); + C2S(WOW_REASON_IOAC_SHORT_EVENT); + C2S(WOW_REASON_IOAC_EXTEND_EVENT); + C2S(WOW_REASON_IOAC_TIMER_EVENT); + C2S(WOW_REASON_ROAM_HO); + C2S(WOW_REASON_DFS_PHYERR_RADADR_EVENT); + C2S(WOW_REASON_BEACON_RECV); + C2S(WOW_REASON_CLIENT_KICKOUT_EVENT); + C2S(WOW_REASON_PAGE_FAULT); + C2S(WOW_REASON_DEBUG_TEST); + default: + return NULL; + } +} + +#undef C2S + +struct wmi_wow_enable_cmd { + __le32 tlv_header; + __le32 enable; + __le32 pause_iface_config; + __le32 flags; +} __packed; + +struct wmi_wow_host_wakeup_cmd { + __le32 tlv_header; + __le32 reserved; +} __packed; + +struct wmi_wow_ev_param { + __le32 vdev_id; + __le32 flag; + __le32 wake_reason; + __le32 data_len; +} __packed; + +struct wmi_wow_ev_pg_fault_param { + __le32 len; + u8 data[]; +} __packed; + +struct wmi_wow_ev_arg { + enum wmi_wow_wake_reason wake_reason; +}; + void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, struct ath12k_wmi_resource_config_arg *config); void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, @@ -5054,4 +5226,6 @@ ath12k_wmi_mac_phy_get_hw_link_id(const struct ath12k_wmi_mac_phy_caps_params *p WMI_CAPS_PARAMS_HW_LINK_ID); } +int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar); +int ath12k_wmi_wow_enable(struct ath12k *ar); #endif diff --git a/drivers/net/wireless/ath/ath12k/wow.c b/drivers/net/wireless/ath/ath12k/wow.c new file mode 100644 index 000000000000..d6d3ed7332ab --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/wow.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include + +#include "mac.h" +#include "core.h" +#include "hif.h" +#include "debug.h" +#include "wmi.h" +#include "wow.h" + +int ath12k_wow_enable(struct ath12k *ar) +{ + struct ath12k_base *ab = ar->ab; + int i, ret; + + clear_bit(ATH12K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags); + + /* The firmware might be busy and it can not enter WoW immediately. + * In that case firmware notifies host with + * ATH12K_HTC_MSG_NACK_SUSPEND message, asking host to try again + * later. Per the firmware team there could be up to 10 loops. + */ + for (i = 0; i < ATH12K_WOW_RETRY_NUM; i++) { + reinit_completion(&ab->htc_suspend); + + ret = ath12k_wmi_wow_enable(ar); + if (ret) { + ath12k_warn(ab, "failed to issue wow enable: %d\n", ret); + return ret; + } + + ret = wait_for_completion_timeout(&ab->htc_suspend, 3 * HZ); + if (ret == 0) { + ath12k_warn(ab, + "timed out while waiting for htc suspend completion\n"); + return -ETIMEDOUT; + } + + if (test_bit(ATH12K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags)) + /* success, suspend complete received */ + return 0; + + ath12k_warn(ab, "htc suspend not complete, retrying (try %d)\n", + i); + msleep(ATH12K_WOW_RETRY_WAIT_MS); + } + + ath12k_warn(ab, "htc suspend not complete, failing after %d tries\n", i); + + return -ETIMEDOUT; +} + +int ath12k_wow_wakeup(struct ath12k *ar) +{ + struct ath12k_base *ab = ar->ab; + int ret; + + reinit_completion(&ab->wow.wakeup_completed); + + ret = ath12k_wmi_wow_host_wakeup_ind(ar); + if (ret) { + ath12k_warn(ab, "failed to send wow wakeup indication: %d\n", + ret); + return ret; + } + + ret = wait_for_completion_timeout(&ab->wow.wakeup_completed, 3 * HZ); + if (ret == 0) { + ath12k_warn(ab, "timed out while waiting for wow wakeup completion\n"); + return -ETIMEDOUT; + } + + return 0; +} diff --git a/drivers/net/wireless/ath/ath12k/wow.h b/drivers/net/wireless/ath/ath12k/wow.h new file mode 100644 index 000000000000..6817ceaa4cfe --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/wow.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef ATH12K_WOW_H +#define ATH12K_WOW_H + +#define ATH12K_WOW_RETRY_NUM 10 +#define ATH12K_WOW_RETRY_WAIT_MS 200 + +#ifdef CONFIG_PM + +int ath12k_wow_enable(struct ath12k *ar); +int ath12k_wow_wakeup(struct ath12k *ar); + +#else + +static inline int ath12k_wow_enable(struct ath12k *ar) +{ + return 0; +} + +static inline int ath12k_wow_wakeup(struct ath12k *ar) +{ + return 0; +} +#endif /* CONFIG_PM */ +#endif /* ATH12K_WOW_H */ From 4a3c212eee0e36c1cd55bfbfb94041a0d3ad265b Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 19 Jun 2024 17:21:11 +0300 Subject: [PATCH 23/38] wifi: ath12k: add basic WoW functionalities Implement basic WoW functionalities such as magic-packet, disconnect and pattern. The logic is very similar to ath11k. When WoW is configured, ath12k_core_suspend and ath12k_core_resume are skipped (by checking ar->state) as we are not allowed to power cycle firmware. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240604055407.12506-4-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/core.c | 46 ++- drivers/net/wireless/ath/ath12k/core.h | 5 + drivers/net/wireless/ath/ath12k/htc.c | 6 + drivers/net/wireless/ath/ath12k/mac.c | 14 + drivers/net/wireless/ath/ath12k/wmi.c | 143 +++++++++ drivers/net/wireless/ath/ath12k/wmi.h | 63 ++++ drivers/net/wireless/ath/ath12k/wow.c | 416 +++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/wow.h | 32 ++ 8 files changed, 717 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 7603516500ba..51252e8bc1ae 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -43,13 +43,37 @@ static int ath12k_core_rfkill_config(struct ath12k_base *ab) return ret; } +/* Check if we need to continue with suspend/resume operation. + * Return: + * a negative value: error happens and don't continue. + * 0: no error but don't continue. + * positive value: no error and do continue. + */ +static int ath12k_core_continue_suspend_resume(struct ath12k_base *ab) +{ + struct ath12k *ar; + + if (!ab->hw_params->supports_suspend) + return -EOPNOTSUPP; + + /* so far single_pdev_only chips have supports_suspend as true + * so pass 0 as a dummy pdev_id here. + */ + ar = ab->pdevs[0].ar; + if (!ar || !ar->ah || ar->ah->state != ATH12K_HW_STATE_OFF) + return 0; + + return 1; +} + int ath12k_core_suspend(struct ath12k_base *ab) { struct ath12k *ar; int ret, i; - if (!ab->hw_params->supports_suspend) - return -EOPNOTSUPP; + ret = ath12k_core_continue_suspend_resume(ab); + if (ret <= 0) + return ret; for (i = 0; i < ab->num_radios; i++) { ar = ab->pdevs[i].ar; @@ -81,8 +105,11 @@ EXPORT_SYMBOL(ath12k_core_suspend); int ath12k_core_suspend_late(struct ath12k_base *ab) { - if (!ab->hw_params->supports_suspend) - return -EOPNOTSUPP; + int ret; + + ret = ath12k_core_continue_suspend_resume(ab); + if (ret <= 0) + return ret; ath12k_acpi_stop(ab); @@ -99,8 +126,9 @@ int ath12k_core_resume_early(struct ath12k_base *ab) { int ret; - if (!ab->hw_params->supports_suspend) - return -EOPNOTSUPP; + ret = ath12k_core_continue_suspend_resume(ab); + if (ret <= 0) + return ret; reinit_completion(&ab->restart_completed); ret = ath12k_hif_power_up(ab); @@ -114,9 +142,11 @@ EXPORT_SYMBOL(ath12k_core_resume_early); int ath12k_core_resume(struct ath12k_base *ab) { long time_left; + int ret; - if (!ab->hw_params->supports_suspend) - return -EOPNOTSUPP; + ret = ath12k_core_continue_suspend_resume(ab); + if (ret <= 0) + return ret; time_left = wait_for_completion_timeout(&ab->restart_completed, ATH12K_RESET_TIMEOUT_HZ); diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 10aa3aca7250..2fd08648cef3 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -28,6 +28,7 @@ #include "dbring.h" #include "fw.h" #include "acpi.h" +#include "wow.h" #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) @@ -607,6 +608,9 @@ struct ath12k { struct work_struct wmi_mgmt_tx_work; struct sk_buff_head wmi_mgmt_tx_queue; + struct ath12k_wow wow; + struct completion target_suspend; + bool target_suspend_ack; struct ath12k_per_peer_tx_stats peer_tx_stats; struct list_head ppdu_stats_info; u32 ppdu_stat_list_depth; @@ -767,6 +771,7 @@ struct ath12k_base { struct { struct completion wakeup_completed; + u32 wmi_conf_rx_decap_mode; } wow; struct ath12k_ce ce; diff --git a/drivers/net/wireless/ath/ath12k/htc.c b/drivers/net/wireless/ath/ath12k/htc.c index 2f2230f565bb..d13616bf07f4 100644 --- a/drivers/net/wireless/ath/ath12k/htc.c +++ b/drivers/net/wireless/ath/ath12k/htc.c @@ -244,6 +244,11 @@ static void ath12k_htc_suspend_complete(struct ath12k_base *ab, bool ack) complete(&ab->htc_suspend); } +static void ath12k_htc_wakeup_from_suspend(struct ath12k_base *ab) +{ + ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot wakeup from suspend is received\n"); +} + void ath12k_htc_rx_completion_handler(struct ath12k_base *ab, struct sk_buff *skb) { @@ -349,6 +354,7 @@ void ath12k_htc_rx_completion_handler(struct ath12k_base *ab, ath12k_htc_suspend_complete(ab, false); break; case ATH12K_HTC_MSG_WAKEUP_FROM_SUSPEND_ID: + ath12k_htc_wakeup_from_suspend(ab); break; default: ath12k_warn(ab, "ignoring unsolicited htc ep0 event %u\n", diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 17b7fab03d50..59662ca241ca 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -15,6 +15,8 @@ #include "dp_rx.h" #include "peer.h" #include "debugfs.h" +#include "hif.h" +#include "wow.h" #define CHAN2G(_channel, _freq, _flags) { \ .band = NL80211_BAND_2GHZ, \ @@ -8578,6 +8580,12 @@ static const struct ieee80211_ops ath12k_ops = { .sta_statistics = ath12k_mac_op_sta_statistics, .remain_on_channel = ath12k_mac_op_remain_on_channel, .cancel_remain_on_channel = ath12k_mac_op_cancel_remain_on_channel, + +#ifdef CONFIG_PM + .suspend = ath12k_wow_op_suspend, + .resume = ath12k_wow_op_resume, + .set_wakeup = ath12k_wow_op_set_wakeup, +#endif }; static void ath12k_mac_update_ch_list(struct ath12k *ar, @@ -9100,6 +9108,12 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); } + ret = ath12k_wow_init(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to init wow: %d\n", ret); + goto err_free_if_combs; + } + ret = ieee80211_register_hw(hw); if (ret) { ath12k_err(ab, "ieee80211 registration failed: %d\n", ret); diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 3eafa46d737b..f72ec153723c 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -3717,6 +3717,7 @@ int ath12k_wmi_cmd_init(struct ath12k_base *ab) arg.res_cfg.is_reg_cc_ext_event_supported = true; ab->hw_params->wmi_init(ab, &arg.res_cfg); + ab->wow.wmi_conf_rx_decap_mode = arg.res_cfg.rx_decap_mode; arg.num_mem_chunks = wmi_ab->num_mem_chunks; arg.hw_mode_id = wmi_ab->preferred_hw_mode; @@ -7464,3 +7465,145 @@ int ath12k_wmi_wow_enable(struct ath12k *ar) return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); } + +int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id, + enum wmi_wow_wakeup_event event, + u32 enable) +{ + struct wmi_wow_add_del_event_cmd *cmd; + struct sk_buff *skb; + size_t len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_wow_add_del_event_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_WOW_ADD_DEL_EVT_CMD, + sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(vdev_id); + cmd->is_add = cpu_to_le32(enable); + cmd->event_bitmap = cpu_to_le32((1 << event)); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow add wakeup event %s enable %d vdev_id %d\n", + wow_wakeup_event(event), enable, vdev_id); + + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID); +} + +int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id, + const u8 *pattern, const u8 *mask, + int pattern_len, int pattern_offset) +{ + struct wmi_wow_add_pattern_cmd *cmd; + struct wmi_wow_bitmap_pattern_params *bitmap; + struct wmi_tlv *tlv; + struct sk_buff *skb; + void *ptr; + size_t len; + + len = sizeof(*cmd) + + sizeof(*tlv) + /* array struct */ + sizeof(*bitmap) + /* bitmap */ + sizeof(*tlv) + /* empty ipv4 sync */ + sizeof(*tlv) + /* empty ipv6 sync */ + sizeof(*tlv) + /* empty magic */ + sizeof(*tlv) + /* empty info timeout */ + sizeof(*tlv) + sizeof(u32); /* ratelimit interval */ + + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + /* cmd */ + ptr = skb->data; + cmd = ptr; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_WOW_ADD_PATTERN_CMD, + sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(vdev_id); + cmd->pattern_id = cpu_to_le32(pattern_id); + cmd->pattern_type = cpu_to_le32(WOW_BITMAP_PATTERN); + + ptr += sizeof(*cmd); + + /* bitmap */ + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, sizeof(*bitmap)); + + ptr += sizeof(*tlv); + + bitmap = ptr; + bitmap->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_WOW_BITMAP_PATTERN_T, + sizeof(*bitmap)); + memcpy(bitmap->patternbuf, pattern, pattern_len); + memcpy(bitmap->bitmaskbuf, mask, pattern_len); + bitmap->pattern_offset = cpu_to_le32(pattern_offset); + bitmap->pattern_len = cpu_to_le32(pattern_len); + bitmap->bitmask_len = cpu_to_le32(pattern_len); + bitmap->pattern_id = cpu_to_le32(pattern_id); + + ptr += sizeof(*bitmap); + + /* ipv4 sync */ + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, 0); + + ptr += sizeof(*tlv); + + /* ipv6 sync */ + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, 0); + + ptr += sizeof(*tlv); + + /* magic */ + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, 0); + + ptr += sizeof(*tlv); + + /* pattern info timeout */ + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, 0); + + ptr += sizeof(*tlv); + + /* ratelimit interval */ + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, sizeof(u32)); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow add pattern vdev_id %d pattern_id %d pattern_offset %d pattern_len %d\n", + vdev_id, pattern_id, pattern_offset, pattern_len); + + ath12k_dbg_dump(ar->ab, ATH12K_DBG_WMI, NULL, "wow pattern: ", + bitmap->patternbuf, pattern_len); + ath12k_dbg_dump(ar->ab, ATH12K_DBG_WMI, NULL, "wow bitmask: ", + bitmap->bitmaskbuf, pattern_len); + + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID); +} + +int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id) +{ + struct wmi_wow_del_pattern_cmd *cmd; + struct sk_buff *skb; + size_t len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_wow_del_pattern_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_WOW_DEL_PATTERN_CMD, + sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(vdev_id); + cmd->pattern_id = cpu_to_le32(pattern_id); + cmd->pattern_type = cpu_to_le32(WOW_BITMAP_PATTERN); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow del pattern vdev_id %d pattern_id %d\n", + vdev_id, pattern_id); + + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID); +} diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 9cee3a114645..6cb06f6f7df4 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -5046,6 +5046,62 @@ static inline const char *wow_reason(enum wmi_wow_wake_reason reason) #undef C2S +#define WOW_DEFAULT_BITMAP_PATTERN_SIZE 148 +#define WOW_DEFAULT_BITMASK_SIZE 148 + +#define WOW_MIN_PATTERN_SIZE 1 +#define WOW_MAX_PATTERN_SIZE 148 +#define WOW_MAX_PKT_OFFSET 128 +#define WOW_HDR_LEN (sizeof(struct ieee80211_hdr_3addr) + \ + sizeof(struct rfc1042_hdr)) +#define WOW_MAX_REDUCE (WOW_HDR_LEN - sizeof(struct ethhdr) - \ + offsetof(struct ieee80211_hdr_3addr, addr1)) + +struct wmi_wow_bitmap_pattern_params { + __le32 tlv_header; + u8 patternbuf[WOW_DEFAULT_BITMAP_PATTERN_SIZE]; + u8 bitmaskbuf[WOW_DEFAULT_BITMASK_SIZE]; + __le32 pattern_offset; + __le32 pattern_len; + __le32 bitmask_len; + __le32 pattern_id; +} __packed; + +struct wmi_wow_add_pattern_cmd { + __le32 tlv_header; + __le32 vdev_id; + __le32 pattern_id; + __le32 pattern_type; +} __packed; + +struct wmi_wow_del_pattern_cmd { + __le32 tlv_header; + __le32 vdev_id; + __le32 pattern_id; + __le32 pattern_type; +} __packed; + +enum wmi_tlv_pattern_type { + WOW_PATTERN_MIN = 0, + WOW_BITMAP_PATTERN = WOW_PATTERN_MIN, + WOW_IPV4_SYNC_PATTERN, + WOW_IPV6_SYNC_PATTERN, + WOW_WILD_CARD_PATTERN, + WOW_TIMER_PATTERN, + WOW_MAGIC_PATTERN, + WOW_IPV6_RA_PATTERN, + WOW_IOAC_PKT_PATTERN, + WOW_IOAC_TMR_PATTERN, + WOW_PATTERN_MAX +}; + +struct wmi_wow_add_del_event_cmd { + __le32 tlv_header; + __le32 vdev_id; + __le32 is_add; + __le32 event_bitmap; +} __packed; + struct wmi_wow_enable_cmd { __le32 tlv_header; __le32 enable; @@ -5228,4 +5284,11 @@ ath12k_wmi_mac_phy_get_hw_link_id(const struct ath12k_wmi_mac_phy_caps_params *p int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar); int ath12k_wmi_wow_enable(struct ath12k *ar); +int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id); +int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id, + const u8 *pattern, const u8 *mask, + int pattern_len, int pattern_offset); +int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id, + enum wmi_wow_wakeup_event event, + u32 enable); #endif diff --git a/drivers/net/wireless/ath/ath12k/wow.c b/drivers/net/wireless/ath/ath12k/wow.c index d6d3ed7332ab..648e7cb970ea 100644 --- a/drivers/net/wireless/ath/ath12k/wow.c +++ b/drivers/net/wireless/ath/ath12k/wow.c @@ -7,12 +7,22 @@ #include #include "mac.h" + +#include #include "core.h" #include "hif.h" #include "debug.h" #include "wmi.h" #include "wow.h" +static const struct wiphy_wowlan_support ath12k_wowlan_support = { + .flags = WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_MAGIC_PKT, + .pattern_min_len = WOW_MIN_PATTERN_SIZE, + .pattern_max_len = WOW_MAX_PATTERN_SIZE, + .max_pkt_offset = WOW_MAX_PKT_OFFSET, +}; + int ath12k_wow_enable(struct ath12k *ar) { struct ath12k_base *ab = ar->ab; @@ -77,3 +87,409 @@ int ath12k_wow_wakeup(struct ath12k *ar) return 0; } + +static int ath12k_wow_vif_cleanup(struct ath12k_vif *arvif) +{ + struct ath12k *ar = arvif->ar; + int i, ret; + + for (i = 0; i < WOW_EVENT_MAX; i++) { + ret = ath12k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0); + if (ret) { + ath12k_warn(ar->ab, "failed to issue wow wakeup for event %s on vdev %i: %d\n", + wow_wakeup_event(i), arvif->vdev_id, ret); + return ret; + } + } + + for (i = 0; i < ar->wow.max_num_patterns; i++) { + ret = ath12k_wmi_wow_del_pattern(ar, arvif->vdev_id, i); + if (ret) { + ath12k_warn(ar->ab, "failed to delete wow pattern %d for vdev %i: %d\n", + i, arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + +static int ath12k_wow_cleanup(struct ath12k *ar) +{ + struct ath12k_vif *arvif; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + list_for_each_entry(arvif, &ar->arvifs, list) { + ret = ath12k_wow_vif_cleanup(arvif); + if (ret) { + ath12k_warn(ar->ab, "failed to clean wow wakeups on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + +/* Convert a 802.3 format to a 802.11 format. + * +------------+-----------+--------+----------------+ + * 802.3: |dest mac(6B)|src mac(6B)|type(2B)| body... | + * +------------+-----------+--------+----------------+ + * |__ |_______ |____________ |________ + * | | | | + * +--+------------+----+-----------+---------------+-----------+ + * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)| 8B |type(2B)| body... | + * +--+------------+----+-----------+---------------+-----------+ + */ +static void +ath12k_wow_convert_8023_to_80211(struct ath12k *ar, + const struct cfg80211_pkt_pattern *eth_pattern, + struct ath12k_pkt_pattern *i80211_pattern) +{ + size_t r1042_eth_ofs = offsetof(struct rfc1042_hdr, eth_type); + size_t a1_ofs = offsetof(struct ieee80211_hdr_3addr, addr1); + size_t a3_ofs = offsetof(struct ieee80211_hdr_3addr, addr3); + size_t i80211_hdr_len = sizeof(struct ieee80211_hdr_3addr); + size_t prot_ofs = offsetof(struct ethhdr, h_proto); + size_t src_ofs = offsetof(struct ethhdr, h_source); + u8 eth_bytemask[WOW_MAX_PATTERN_SIZE] = {}; + const u8 *eth_pat = eth_pattern->pattern; + size_t eth_pat_len = eth_pattern->pattern_len; + size_t eth_pkt_ofs = eth_pattern->pkt_offset; + u8 *bytemask = i80211_pattern->bytemask; + u8 *pat = i80211_pattern->pattern; + size_t pat_len = 0; + size_t pkt_ofs = 0; + size_t delta; + int i; + + /* convert bitmask to bytemask */ + for (i = 0; i < eth_pat_len; i++) + if (eth_pattern->mask[i / 8] & BIT(i % 8)) + eth_bytemask[i] = 0xff; + + if (eth_pkt_ofs < ETH_ALEN) { + pkt_ofs = eth_pkt_ofs + a1_ofs; + + if (eth_pkt_ofs + eth_pat_len < ETH_ALEN) { + memcpy(pat, eth_pat, eth_pat_len); + memcpy(bytemask, eth_bytemask, eth_pat_len); + + pat_len = eth_pat_len; + } else if (eth_pkt_ofs + eth_pat_len < prot_ofs) { + memcpy(pat, eth_pat, ETH_ALEN - eth_pkt_ofs); + memcpy(bytemask, eth_bytemask, ETH_ALEN - eth_pkt_ofs); + + delta = eth_pkt_ofs + eth_pat_len - src_ofs; + memcpy(pat + a3_ofs - pkt_ofs, + eth_pat + ETH_ALEN - eth_pkt_ofs, + delta); + memcpy(bytemask + a3_ofs - pkt_ofs, + eth_bytemask + ETH_ALEN - eth_pkt_ofs, + delta); + + pat_len = a3_ofs - pkt_ofs + delta; + } else { + memcpy(pat, eth_pat, ETH_ALEN - eth_pkt_ofs); + memcpy(bytemask, eth_bytemask, ETH_ALEN - eth_pkt_ofs); + + memcpy(pat + a3_ofs - pkt_ofs, + eth_pat + ETH_ALEN - eth_pkt_ofs, + ETH_ALEN); + memcpy(bytemask + a3_ofs - pkt_ofs, + eth_bytemask + ETH_ALEN - eth_pkt_ofs, + ETH_ALEN); + + delta = eth_pkt_ofs + eth_pat_len - prot_ofs; + memcpy(pat + i80211_hdr_len + r1042_eth_ofs - pkt_ofs, + eth_pat + prot_ofs - eth_pkt_ofs, + delta); + memcpy(bytemask + i80211_hdr_len + r1042_eth_ofs - pkt_ofs, + eth_bytemask + prot_ofs - eth_pkt_ofs, + delta); + + pat_len = i80211_hdr_len + r1042_eth_ofs - pkt_ofs + delta; + } + } else if (eth_pkt_ofs < prot_ofs) { + pkt_ofs = eth_pkt_ofs - ETH_ALEN + a3_ofs; + + if (eth_pkt_ofs + eth_pat_len < prot_ofs) { + memcpy(pat, eth_pat, eth_pat_len); + memcpy(bytemask, eth_bytemask, eth_pat_len); + + pat_len = eth_pat_len; + } else { + memcpy(pat, eth_pat, prot_ofs - eth_pkt_ofs); + memcpy(bytemask, eth_bytemask, prot_ofs - eth_pkt_ofs); + + delta = eth_pkt_ofs + eth_pat_len - prot_ofs; + memcpy(pat + i80211_hdr_len + r1042_eth_ofs - pkt_ofs, + eth_pat + prot_ofs - eth_pkt_ofs, + delta); + memcpy(bytemask + i80211_hdr_len + r1042_eth_ofs - pkt_ofs, + eth_bytemask + prot_ofs - eth_pkt_ofs, + delta); + + pat_len = i80211_hdr_len + r1042_eth_ofs - pkt_ofs + delta; + } + } else { + pkt_ofs = eth_pkt_ofs - prot_ofs + i80211_hdr_len + r1042_eth_ofs; + + memcpy(pat, eth_pat, eth_pat_len); + memcpy(bytemask, eth_bytemask, eth_pat_len); + + pat_len = eth_pat_len; + } + + i80211_pattern->pattern_len = pat_len; + i80211_pattern->pkt_offset = pkt_ofs; +} + +static int ath12k_vif_wow_set_wakeups(struct ath12k_vif *arvif, + struct cfg80211_wowlan *wowlan) +{ + const struct cfg80211_pkt_pattern *patterns = wowlan->patterns; + struct ath12k *ar = arvif->ar; + unsigned long wow_mask = 0; + int pattern_id = 0; + int ret, i; + + /* Setup requested WOW features */ + switch (arvif->vdev_type) { + case WMI_VDEV_TYPE_IBSS: + __set_bit(WOW_BEACON_EVENT, &wow_mask); + fallthrough; + case WMI_VDEV_TYPE_AP: + __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); + __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); + __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask); + __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask); + __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask); + __set_bit(WOW_HTT_EVENT, &wow_mask); + __set_bit(WOW_RA_MATCH_EVENT, &wow_mask); + break; + case WMI_VDEV_TYPE_STA: + if (wowlan->disconnect) { + __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); + __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); + __set_bit(WOW_BMISS_EVENT, &wow_mask); + __set_bit(WOW_CSA_IE_EVENT, &wow_mask); + } + + if (wowlan->magic_pkt) + __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask); + break; + default: + break; + } + + for (i = 0; i < wowlan->n_patterns; i++) { + const struct cfg80211_pkt_pattern *eth_pattern = &patterns[i]; + struct ath12k_pkt_pattern new_pattern = {}; + + if (WARN_ON(eth_pattern->pattern_len > WOW_MAX_PATTERN_SIZE)) + return -EINVAL; + + if (ar->ab->wow.wmi_conf_rx_decap_mode == + ATH12K_HW_TXRX_NATIVE_WIFI) { + ath12k_wow_convert_8023_to_80211(ar, eth_pattern, + &new_pattern); + + if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE)) + return -EINVAL; + } else { + memcpy(new_pattern.pattern, eth_pattern->pattern, + eth_pattern->pattern_len); + + /* convert bitmask to bytemask */ + for (i = 0; i < eth_pattern->pattern_len; i++) + if (eth_pattern->mask[i / 8] & BIT(i % 8)) + new_pattern.bytemask[i] = 0xff; + + new_pattern.pattern_len = eth_pattern->pattern_len; + new_pattern.pkt_offset = eth_pattern->pkt_offset; + } + + ret = ath12k_wmi_wow_add_pattern(ar, arvif->vdev_id, + pattern_id, + new_pattern.pattern, + new_pattern.bytemask, + new_pattern.pattern_len, + new_pattern.pkt_offset); + if (ret) { + ath12k_warn(ar->ab, "failed to add pattern %i to vdev %i: %d\n", + pattern_id, + arvif->vdev_id, ret); + return ret; + } + + pattern_id++; + __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask); + } + + for (i = 0; i < WOW_EVENT_MAX; i++) { + if (!test_bit(i, &wow_mask)) + continue; + ret = ath12k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1); + if (ret) { + ath12k_warn(ar->ab, "failed to enable wakeup event %s on vdev %i: %d\n", + wow_wakeup_event(i), arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + +static int ath12k_wow_set_wakeups(struct ath12k *ar, + struct cfg80211_wowlan *wowlan) +{ + struct ath12k_vif *arvif; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + list_for_each_entry(arvif, &ar->arvifs, list) { + ret = ath12k_vif_wow_set_wakeups(arvif, wowlan); + if (ret) { + ath12k_warn(ar->ab, "failed to set wow wakeups on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + +int ath12k_wow_op_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) +{ + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar = ath12k_ah_to_ar(ah, 0); + int ret; + + mutex_lock(&ar->conf_mutex); + + ret = ath12k_wow_cleanup(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to clear wow wakeup events: %d\n", + ret); + goto exit; + } + + ret = ath12k_wow_set_wakeups(ar, wowlan); + if (ret) { + ath12k_warn(ar->ab, "failed to set wow wakeup events: %d\n", + ret); + goto cleanup; + } + + ret = ath12k_mac_wait_tx_complete(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to wait tx complete: %d\n", ret); + goto cleanup; + } + + ret = ath12k_wow_enable(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to start wow: %d\n", ret); + goto cleanup; + } + + ath12k_hif_irq_disable(ar->ab); + ath12k_hif_ce_irq_disable(ar->ab); + + ret = ath12k_hif_suspend(ar->ab); + if (ret) { + ath12k_warn(ar->ab, "failed to suspend hif: %d\n", ret); + goto wakeup; + } + + goto exit; + +wakeup: + ath12k_wow_wakeup(ar); + +cleanup: + ath12k_wow_cleanup(ar); + +exit: + mutex_unlock(&ar->conf_mutex); + return ret ? 1 : 0; +} + +void ath12k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled) +{ + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar = ath12k_ah_to_ar(ah, 0); + + mutex_lock(&ar->conf_mutex); + device_set_wakeup_enable(ar->ab->dev, enabled); + mutex_unlock(&ar->conf_mutex); +} + +int ath12k_wow_op_resume(struct ieee80211_hw *hw) +{ + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar = ath12k_ah_to_ar(ah, 0); + int ret; + + mutex_lock(&ar->conf_mutex); + + ret = ath12k_hif_resume(ar->ab); + if (ret) { + ath12k_warn(ar->ab, "failed to resume hif: %d\n", ret); + goto exit; + } + + ath12k_hif_ce_irq_enable(ar->ab); + ath12k_hif_irq_enable(ar->ab); + + ret = ath12k_wow_wakeup(ar); + if (ret) + ath12k_warn(ar->ab, "failed to wakeup from wow: %d\n", ret); + +exit: + if (ret) { + switch (ah->state) { + case ATH12K_HW_STATE_ON: + ah->state = ATH12K_HW_STATE_RESTARTING; + ret = 1; + break; + case ATH12K_HW_STATE_OFF: + case ATH12K_HW_STATE_RESTARTING: + case ATH12K_HW_STATE_RESTARTED: + case ATH12K_HW_STATE_WEDGED: + ath12k_warn(ar->ab, "encountered unexpected device state %d on resume, cannot recover\n", + ah->state); + ret = -EIO; + break; + } + } + + mutex_unlock(&ar->conf_mutex); + return ret; +} + +int ath12k_wow_init(struct ath12k *ar) +{ + if (WARN_ON(!test_bit(WMI_TLV_SERVICE_WOW, ar->wmi->wmi_ab->svc_map))) + return -EINVAL; + + ar->wow.wowlan_support = ath12k_wowlan_support; + + if (ar->ab->wow.wmi_conf_rx_decap_mode == ATH12K_HW_TXRX_NATIVE_WIFI) { + ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE; + ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE; + } + + ar->wow.max_num_patterns = ATH12K_WOW_PATTERNS; + ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns; + ar->ah->hw->wiphy->wowlan = &ar->wow.wowlan_support; + + device_set_wakeup_capable(ar->ab->dev, true); + + return 0; +} diff --git a/drivers/net/wireless/ath/ath12k/wow.h b/drivers/net/wireless/ath/ath12k/wow.h index 6817ceaa4cfe..af9be5fadcc3 100644 --- a/drivers/net/wireless/ath/ath12k/wow.h +++ b/drivers/net/wireless/ath/ath12k/wow.h @@ -9,14 +9,46 @@ #define ATH12K_WOW_RETRY_NUM 10 #define ATH12K_WOW_RETRY_WAIT_MS 200 +#define ATH12K_WOW_PATTERNS 22 + +struct ath12k_wow { + u32 max_num_patterns; + struct completion wakeup_completed; + struct wiphy_wowlan_support wowlan_support; +}; + +struct ath12k_pkt_pattern { + u8 pattern[WOW_MAX_PATTERN_SIZE]; + u8 bytemask[WOW_MAX_PATTERN_SIZE]; + int pattern_len; + int pkt_offset; +}; + +struct rfc1042_hdr { + u8 llc_dsap; + u8 llc_ssap; + u8 llc_ctrl; + u8 snap_oui[3]; + __be16 eth_type; +} __packed; #ifdef CONFIG_PM +int ath12k_wow_init(struct ath12k *ar); +int ath12k_wow_op_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan); +int ath12k_wow_op_resume(struct ieee80211_hw *hw); +void ath12k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled); int ath12k_wow_enable(struct ath12k *ar); int ath12k_wow_wakeup(struct ath12k *ar); #else +static inline int ath12k_wow_init(struct ath12k *ar) +{ + return 0; +} + static inline int ath12k_wow_enable(struct ath12k *ar) { return 0; From 16f474d6d49db5d93e79c2d4bdcc3a730cf1d1f1 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Tue, 4 Jun 2024 13:54:03 +0800 Subject: [PATCH 24/38] wifi: ath12k: add WoW net-detect functionality Implement net-detect feature by setting flag WIPHY_WOWLAN_NET_DETECT if firmware supports this feature. Driver sets the related PNO configuration to firmware before entering WoW and firmware then scans periodically and wakes up host if a specific SSID is found. Note that firmware crashes if we enable it for both P2P vdev and station vdev simultaneously because firmware can only support one vdev at a time. Since there is rare scenario for a P2P vdev to do net-detect, skip it for P2P vdevs. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240604055407.12506-5-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/core.h | 2 + drivers/net/wireless/ath/ath12k/mac.c | 12 ++ drivers/net/wireless/ath/ath12k/wmi.c | 145 ++++++++++++++++++ drivers/net/wireless/ath/ath12k/wmi.h | 166 ++++++++++++++++++++ drivers/net/wireless/ath/ath12k/wow.c | 202 ++++++++++++++++++++++++- 5 files changed, 524 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 2fd08648cef3..81c4bfe7792f 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -630,6 +630,8 @@ struct ath12k { u32 freq_low; u32 freq_high; + + bool nlo_enabled; }; struct ath12k_hw { diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 59662ca241ca..cd8fb3797eea 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -9108,6 +9108,18 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); } + if (test_bit(WMI_TLV_SERVICE_NLO, ar->wmi->wmi_ab->svc_map)) { + wiphy->max_sched_scan_ssids = WMI_PNO_MAX_SUPP_NETWORKS; + wiphy->max_match_sets = WMI_PNO_MAX_SUPP_NETWORKS; + wiphy->max_sched_scan_ie_len = WMI_PNO_MAX_IE_LENGTH; + wiphy->max_sched_scan_plans = WMI_PNO_MAX_SCHED_SCAN_PLANS; + wiphy->max_sched_scan_plan_interval = + WMI_PNO_MAX_SCHED_SCAN_PLAN_INT; + wiphy->max_sched_scan_plan_iterations = + WMI_PNO_MAX_SCHED_SCAN_PLAN_ITRNS; + wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR; + } + ret = ath12k_wow_init(ar); if (ret) { ath12k_warn(ar->ab, "failed to init wow: %d\n", ret); diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index f72ec153723c..b69f20495ab4 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -7607,3 +7607,148 @@ int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id) return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID); } + +static struct sk_buff * +ath12k_wmi_op_gen_config_pno_start(struct ath12k *ar, u32 vdev_id, + struct wmi_pno_scan_req_arg *pno) +{ + struct nlo_configured_params *nlo_list; + size_t len, nlo_list_len, channel_list_len; + struct wmi_wow_nlo_config_cmd *cmd; + __le32 *channel_list; + struct wmi_tlv *tlv; + struct sk_buff *skb; + void *ptr; + u32 i; + + len = sizeof(*cmd) + + sizeof(*tlv) + + /* TLV place holder for array of structures + * nlo_configured_params(nlo_list) + */ + sizeof(*tlv); + /* TLV place holder for array of uint32 channel_list */ + + channel_list_len = sizeof(u32) * pno->a_networks[0].channel_count; + len += channel_list_len; + + nlo_list_len = sizeof(*nlo_list) * pno->uc_networks_count; + len += nlo_list_len; + + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = skb->data; + cmd = ptr; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_NLO_CONFIG_CMD, sizeof(*cmd)); + + cmd->vdev_id = cpu_to_le32(pno->vdev_id); + cmd->flags = cpu_to_le32(WMI_NLO_CONFIG_START | WMI_NLO_CONFIG_SSID_HIDE_EN); + + /* current FW does not support min-max range for dwell time */ + cmd->active_dwell_time = cpu_to_le32(pno->active_max_time); + cmd->passive_dwell_time = cpu_to_le32(pno->passive_max_time); + + if (pno->do_passive_scan) + cmd->flags |= cpu_to_le32(WMI_NLO_CONFIG_SCAN_PASSIVE); + + cmd->fast_scan_period = cpu_to_le32(pno->fast_scan_period); + cmd->slow_scan_period = cpu_to_le32(pno->slow_scan_period); + cmd->fast_scan_max_cycles = cpu_to_le32(pno->fast_scan_max_cycles); + cmd->delay_start_time = cpu_to_le32(pno->delay_start_time); + + if (pno->enable_pno_scan_randomization) { + cmd->flags |= cpu_to_le32(WMI_NLO_CONFIG_SPOOFED_MAC_IN_PROBE_REQ | + WMI_NLO_CONFIG_RANDOM_SEQ_NO_IN_PROBE_REQ); + ether_addr_copy(cmd->mac_addr.addr, pno->mac_addr); + ether_addr_copy(cmd->mac_mask.addr, pno->mac_addr_mask); + } + + ptr += sizeof(*cmd); + + /* nlo_configured_params(nlo_list) */ + cmd->no_of_ssids = cpu_to_le32(pno->uc_networks_count); + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, nlo_list_len); + + ptr += sizeof(*tlv); + nlo_list = ptr; + for (i = 0; i < pno->uc_networks_count; i++) { + tlv = (struct wmi_tlv *)(&nlo_list[i].tlv_header); + tlv->header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_ARRAY_BYTE, + sizeof(*nlo_list)); + + nlo_list[i].ssid.valid = cpu_to_le32(1); + nlo_list[i].ssid.ssid.ssid_len = + cpu_to_le32(pno->a_networks[i].ssid.ssid_len); + memcpy(nlo_list[i].ssid.ssid.ssid, + pno->a_networks[i].ssid.ssid, + le32_to_cpu(nlo_list[i].ssid.ssid.ssid_len)); + + if (pno->a_networks[i].rssi_threshold && + pno->a_networks[i].rssi_threshold > -300) { + nlo_list[i].rssi_cond.valid = cpu_to_le32(1); + nlo_list[i].rssi_cond.rssi = + cpu_to_le32(pno->a_networks[i].rssi_threshold); + } + + nlo_list[i].bcast_nw_type.valid = cpu_to_le32(1); + nlo_list[i].bcast_nw_type.bcast_nw_type = + cpu_to_le32(pno->a_networks[i].bcast_nw_type); + } + + ptr += nlo_list_len; + cmd->num_of_channels = cpu_to_le32(pno->a_networks[0].channel_count); + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, channel_list_len); + ptr += sizeof(*tlv); + channel_list = ptr; + + for (i = 0; i < pno->a_networks[0].channel_count; i++) + channel_list[i] = cpu_to_le32(pno->a_networks[0].channels[i]); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv start pno config vdev_id %d\n", + vdev_id); + + return skb; +} + +static struct sk_buff *ath12k_wmi_op_gen_config_pno_stop(struct ath12k *ar, + u32 vdev_id) +{ + struct wmi_wow_nlo_config_cmd *cmd; + struct sk_buff *skb; + size_t len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + cmd = (struct wmi_wow_nlo_config_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_NLO_CONFIG_CMD, len); + + cmd->vdev_id = cpu_to_le32(vdev_id); + cmd->flags = cpu_to_le32(WMI_NLO_CONFIG_STOP); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "wmi tlv stop pno config vdev_id %d\n", vdev_id); + return skb; +} + +int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id, + struct wmi_pno_scan_req_arg *pno_scan) +{ + struct sk_buff *skb; + + if (pno_scan->enable) + skb = ath12k_wmi_op_gen_config_pno_start(ar, vdev_id, pno_scan); + else + skb = ath12k_wmi_op_gen_config_pno_stop(ar, vdev_id); + + if (IS_ERR_OR_NULL(skb)) + return -ENOMEM; + + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID); +} diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 6cb06f6f7df4..0842ab760f2b 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -5130,6 +5130,170 @@ struct wmi_wow_ev_arg { enum wmi_wow_wake_reason wake_reason; }; +#define WMI_PNO_MAX_SCHED_SCAN_PLANS 2 +#define WMI_PNO_MAX_SCHED_SCAN_PLAN_INT 7200 +#define WMI_PNO_MAX_SCHED_SCAN_PLAN_ITRNS 100 +#define WMI_PNO_MAX_NETW_CHANNELS 26 +#define WMI_PNO_MAX_NETW_CHANNELS_EX 60 +#define WMI_PNO_MAX_SUPP_NETWORKS WLAN_SCAN_PARAMS_MAX_SSID +#define WMI_PNO_MAX_IE_LENGTH WLAN_SCAN_PARAMS_MAX_IE_LEN + +/* size based of dot11 declaration without extra IEs as we will not carry those for PNO */ +#define WMI_PNO_MAX_PB_REQ_SIZE 450 + +#define WMI_PNO_24GHZ_DEFAULT_CH 1 +#define WMI_PNO_5GHZ_DEFAULT_CH 36 + +#define WMI_ACTIVE_MAX_CHANNEL_TIME 40 +#define WMI_PASSIVE_MAX_CHANNEL_TIME 110 + +/* SSID broadcast type */ +enum wmi_ssid_bcast_type { + BCAST_UNKNOWN = 0, + BCAST_NORMAL = 1, + BCAST_HIDDEN = 2, +}; + +#define WMI_NLO_MAX_SSIDS 16 +#define WMI_NLO_MAX_CHAN 48 + +#define WMI_NLO_CONFIG_STOP BIT(0) +#define WMI_NLO_CONFIG_START BIT(1) +#define WMI_NLO_CONFIG_RESET BIT(2) +#define WMI_NLO_CONFIG_SLOW_SCAN BIT(4) +#define WMI_NLO_CONFIG_FAST_SCAN BIT(5) +#define WMI_NLO_CONFIG_SSID_HIDE_EN BIT(6) + +/* This bit is used to indicate if EPNO or supplicant PNO is enabled. + * Only one of them can be enabled at a given time + */ +#define WMI_NLO_CONFIG_ENLO BIT(7) +#define WMI_NLO_CONFIG_SCAN_PASSIVE BIT(8) +#define WMI_NLO_CONFIG_ENLO_RESET BIT(9) +#define WMI_NLO_CONFIG_SPOOFED_MAC_IN_PROBE_REQ BIT(10) +#define WMI_NLO_CONFIG_RANDOM_SEQ_NO_IN_PROBE_REQ BIT(11) +#define WMI_NLO_CONFIG_ENABLE_IE_WHITELIST_IN_PROBE_REQ BIT(12) +#define WMI_NLO_CONFIG_ENABLE_CNLO_RSSI_CONFIG BIT(13) + +struct wmi_nlo_ssid_params { + __le32 valid; + struct ath12k_wmi_ssid_params ssid; +} __packed; + +struct wmi_nlo_enc_params { + __le32 valid; + __le32 enc_type; +} __packed; + +struct wmi_nlo_auth_params { + __le32 valid; + __le32 auth_type; +} __packed; + +struct wmi_nlo_bcast_nw_params { + __le32 valid; + __le32 bcast_nw_type; +} __packed; + +struct wmi_nlo_rssi_params { + __le32 valid; + __le32 rssi; +} __packed; + +struct nlo_configured_params { + /* TLV tag and len;*/ + __le32 tlv_header; + struct wmi_nlo_ssid_params ssid; + struct wmi_nlo_enc_params enc_type; + struct wmi_nlo_auth_params auth_type; + struct wmi_nlo_rssi_params rssi_cond; + + /* indicates if the SSID is hidden or not */ + struct wmi_nlo_bcast_nw_params bcast_nw_type; +} __packed; + +struct wmi_network_type_arg { + struct cfg80211_ssid ssid; + u32 authentication; + u32 encryption; + u32 bcast_nw_type; + u8 channel_count; + u16 channels[WMI_PNO_MAX_NETW_CHANNELS_EX]; + s32 rssi_threshold; +}; + +struct wmi_pno_scan_req_arg { + u8 enable; + u8 vdev_id; + u8 uc_networks_count; + struct wmi_network_type_arg a_networks[WMI_PNO_MAX_SUPP_NETWORKS]; + u32 fast_scan_period; + u32 slow_scan_period; + u8 fast_scan_max_cycles; + + bool do_passive_scan; + + u32 delay_start_time; + u32 active_min_time; + u32 active_max_time; + u32 passive_min_time; + u32 passive_max_time; + + /* mac address randomization attributes */ + u32 enable_pno_scan_randomization; + u8 mac_addr[ETH_ALEN]; + u8 mac_addr_mask[ETH_ALEN]; +}; + +struct wmi_wow_nlo_config_cmd { + __le32 tlv_header; + __le32 flags; + __le32 vdev_id; + __le32 fast_scan_max_cycles; + __le32 active_dwell_time; + __le32 passive_dwell_time; + __le32 probe_bundle_size; + + /* ART = IRT */ + __le32 rest_time; + + /* max value that can be reached after scan_backoff_multiplier */ + __le32 max_rest_time; + + __le32 scan_backoff_multiplier; + __le32 fast_scan_period; + + /* specific to windows */ + __le32 slow_scan_period; + + __le32 no_of_ssids; + + __le32 num_of_channels; + + /* NLO scan start delay time in milliseconds */ + __le32 delay_start_time; + + /* MAC Address to use in Probe Req as SA */ + struct ath12k_wmi_mac_addr_params mac_addr; + + /* Mask on which MAC has to be randomized */ + struct ath12k_wmi_mac_addr_params mac_mask; + + /* IE bitmap to use in Probe Req */ + __le32 ie_bitmap[8]; + + /* Number of vendor OUIs. In the TLV vendor_oui[] */ + __le32 num_vendor_oui; + + /* Number of connected NLO band preferences */ + __le32 num_cnlo_band_pref; + + /* The TLVs will follow. + * nlo_configured_params nlo_list[]; + * u32 channel_list[num_of_channels]; + */ +} __packed; + void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, struct ath12k_wmi_resource_config_arg *config); void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, @@ -5291,4 +5455,6 @@ int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id, int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id, enum wmi_wow_wakeup_event event, u32 enable); +int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id, + struct wmi_pno_scan_req_arg *pno_scan); #endif diff --git a/drivers/net/wireless/ath/ath12k/wow.c b/drivers/net/wireless/ath/ath12k/wow.c index 648e7cb970ea..5d4f11028403 100644 --- a/drivers/net/wireless/ath/ath12k/wow.c +++ b/drivers/net/wireless/ath/ath12k/wow.c @@ -23,6 +23,13 @@ static const struct wiphy_wowlan_support ath12k_wowlan_support = { .max_pkt_offset = WOW_MAX_PKT_OFFSET, }; +static inline bool ath12k_wow_is_p2p_vdev(struct ath12k_vif *arvif) +{ + return (arvif->vdev_subtype == WMI_VDEV_SUBTYPE_P2P_DEVICE || + arvif->vdev_subtype == WMI_VDEV_SUBTYPE_P2P_CLIENT || + arvif->vdev_subtype == WMI_VDEV_SUBTYPE_P2P_GO); +} + int ath12k_wow_enable(struct ath12k *ar) { struct ath12k_base *ab = ar->ab; @@ -247,7 +254,101 @@ ath12k_wow_convert_8023_to_80211(struct ath12k *ar, i80211_pattern->pkt_offset = pkt_ofs; } -static int ath12k_vif_wow_set_wakeups(struct ath12k_vif *arvif, +static int +ath12k_wow_pno_check_and_convert(struct ath12k *ar, u32 vdev_id, + const struct cfg80211_sched_scan_request *nd_config, + struct wmi_pno_scan_req_arg *pno) +{ + int i, j; + u8 ssid_len; + + pno->enable = 1; + pno->vdev_id = vdev_id; + pno->uc_networks_count = nd_config->n_match_sets; + + if (!pno->uc_networks_count || + pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS) + return -EINVAL; + + if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX) + return -EINVAL; + + /* Filling per profile params */ + for (i = 0; i < pno->uc_networks_count; i++) { + ssid_len = nd_config->match_sets[i].ssid.ssid_len; + + if (ssid_len == 0 || ssid_len > 32) + return -EINVAL; + + pno->a_networks[i].ssid.ssid_len = ssid_len; + + memcpy(pno->a_networks[i].ssid.ssid, + nd_config->match_sets[i].ssid.ssid, + ssid_len); + pno->a_networks[i].authentication = 0; + pno->a_networks[i].encryption = 0; + pno->a_networks[i].bcast_nw_type = 0; + + /* Copying list of valid channel into request */ + pno->a_networks[i].channel_count = nd_config->n_channels; + pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold; + + for (j = 0; j < nd_config->n_channels; j++) { + pno->a_networks[i].channels[j] = + nd_config->channels[j]->center_freq; + } + } + + /* set scan to passive if no SSIDs are specified in the request */ + if (nd_config->n_ssids == 0) + pno->do_passive_scan = true; + else + pno->do_passive_scan = false; + + for (i = 0; i < nd_config->n_ssids; i++) { + for (j = 0; j < pno->uc_networks_count; j++) { + if (pno->a_networks[j].ssid.ssid_len == + nd_config->ssids[i].ssid_len && + !memcmp(pno->a_networks[j].ssid.ssid, + nd_config->ssids[i].ssid, + pno->a_networks[j].ssid.ssid_len)) { + pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN; + break; + } + } + } + + if (nd_config->n_scan_plans == 2) { + pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; + pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations; + pno->slow_scan_period = + nd_config->scan_plans[1].interval * MSEC_PER_SEC; + } else if (nd_config->n_scan_plans == 1) { + pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; + pno->fast_scan_max_cycles = 1; + pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; + } else { + ath12k_warn(ar->ab, "Invalid number of PNO scan plans: %d", + nd_config->n_scan_plans); + } + + if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + /* enable mac randomization */ + pno->enable_pno_scan_randomization = 1; + memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN); + memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN); + } + + pno->delay_start_time = nd_config->delay; + + /* Current FW does not support min-max range for dwell time */ + pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME; + pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME; + + return 0; +} + +static int ath12k_wow_vif_set_wakeups(struct ath12k_vif *arvif, struct cfg80211_wowlan *wowlan) { const struct cfg80211_pkt_pattern *patterns = wowlan->patterns; @@ -280,6 +381,26 @@ static int ath12k_vif_wow_set_wakeups(struct ath12k_vif *arvif, if (wowlan->magic_pkt) __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask); + + if (wowlan->nd_config) { + struct wmi_pno_scan_req_arg *pno; + int ret; + + pno = kzalloc(sizeof(*pno), GFP_KERNEL); + if (!pno) + return -ENOMEM; + + ar->nlo_enabled = true; + + ret = ath12k_wow_pno_check_and_convert(ar, arvif->vdev_id, + wowlan->nd_config, pno); + if (!ret) { + ath12k_wmi_wow_config_pno(ar, arvif->vdev_id, pno); + __set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask); + } + + kfree(pno); + } break; default: break; @@ -352,7 +473,9 @@ static int ath12k_wow_set_wakeups(struct ath12k *ar, lockdep_assert_held(&ar->conf_mutex); list_for_each_entry(arvif, &ar->arvifs, list) { - ret = ath12k_vif_wow_set_wakeups(arvif, wowlan); + if (ath12k_wow_is_p2p_vdev(arvif)) + continue; + ret = ath12k_wow_vif_set_wakeups(arvif, wowlan); if (ret) { ath12k_warn(ar->ab, "failed to set wow wakeups on vdev %i: %d\n", arvif->vdev_id, ret); @@ -363,6 +486,66 @@ static int ath12k_wow_set_wakeups(struct ath12k *ar, return 0; } +static int ath12k_wow_vdev_clean_nlo(struct ath12k *ar, u32 vdev_id) +{ + struct wmi_pno_scan_req_arg *pno; + int ret; + + if (!ar->nlo_enabled) + return 0; + + pno = kzalloc(sizeof(*pno), GFP_KERNEL); + if (!pno) + return -ENOMEM; + + pno->enable = 0; + ret = ath12k_wmi_wow_config_pno(ar, vdev_id, pno); + if (ret) { + ath12k_warn(ar->ab, "failed to disable PNO: %d", ret); + goto out; + } + + ar->nlo_enabled = false; + +out: + kfree(pno); + return ret; +} + +static int ath12k_wow_vif_clean_nlo(struct ath12k_vif *arvif) +{ + struct ath12k *ar = arvif->ar; + + switch (arvif->vdev_type) { + case WMI_VDEV_TYPE_STA: + return ath12k_wow_vdev_clean_nlo(ar, arvif->vdev_id); + default: + return 0; + } +} + +static int ath12k_wow_nlo_cleanup(struct ath12k *ar) +{ + struct ath12k_vif *arvif; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (ath12k_wow_is_p2p_vdev(arvif)) + continue; + + ret = ath12k_wow_vif_clean_nlo(arvif); + if (ret) { + ath12k_warn(ar->ab, "failed to clean nlo settings on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + int ath12k_wow_op_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { @@ -448,8 +631,16 @@ int ath12k_wow_op_resume(struct ieee80211_hw *hw) ath12k_hif_irq_enable(ar->ab); ret = ath12k_wow_wakeup(ar); - if (ret) + if (ret) { ath12k_warn(ar->ab, "failed to wakeup from wow: %d\n", ret); + goto exit; + } + + ret = ath12k_wow_nlo_cleanup(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to cleanup nlo: %d\n", ret); + goto exit; + } exit: if (ret) { @@ -485,6 +676,11 @@ int ath12k_wow_init(struct ath12k *ar) ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE; } + if (test_bit(WMI_TLV_SERVICE_NLO, ar->wmi->wmi_ab->svc_map)) { + ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT; + ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS; + } + ar->wow.max_num_patterns = ATH12K_WOW_PATTERNS; ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns; ar->ah->hw->wiphy->wowlan = &ar->wow.wowlan_support; From 66a9448b1b897793a3237f832b5ce3bb255e28e7 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 19 Jun 2024 17:21:12 +0300 Subject: [PATCH 25/38] wifi: ath12k: implement hardware data filter Host needs to set hardware data filter before entering WoW to let firmware drop needless broadcast/multicast frames to avoid frequent wakeup. Host clears hardware data filter when leaving WoW. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240604055407.12506-6-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/wmi.c | 31 +++++++++++++ drivers/net/wireless/ath/ath12k/wmi.h | 21 +++++++++ drivers/net/wireless/ath/ath12k/wow.c | 66 +++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index b69f20495ab4..b1f343efa4b0 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -7424,6 +7424,37 @@ void ath12k_wmi_detach(struct ath12k_base *ab) ath12k_wmi_free_dbring_caps(ab); } +int ath12k_wmi_hw_data_filter_cmd(struct ath12k *ar, struct wmi_hw_data_filter_arg *arg) +{ + struct wmi_hw_data_filter_cmd *cmd; + struct sk_buff *skb; + int len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_hw_data_filter_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_HW_DATA_FILTER_CMD, + sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(arg->vdev_id); + cmd->enable = cpu_to_le32(arg->enable ? 1 : 0); + + /* Set all modes in case of disable */ + if (arg->enable) + cmd->hw_filter_bitmap = cpu_to_le32(arg->hw_filter_bitmap); + else + cmd->hw_filter_bitmap = cpu_to_le32((u32)~0U); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "wmi hw data filter enable %d filter_bitmap 0x%x\n", + arg->enable, arg->hw_filter_bitmap); + + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID); +} + int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar) { struct wmi_wow_host_wakeup_cmd *cmd; diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 0842ab760f2b..62d7bb9e791d 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -5294,6 +5294,25 @@ struct wmi_wow_nlo_config_cmd { */ } __packed; +/* Definition of HW data filtering */ +enum hw_data_filter_type { + WMI_HW_DATA_FILTER_DROP_NON_ARP_BC = BIT(0), + WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC = BIT(1), +}; + +struct wmi_hw_data_filter_cmd { + __le32 tlv_header; + __le32 vdev_id; + __le32 enable; + __le32 hw_filter_bitmap; +} __packed; + +struct wmi_hw_data_filter_arg { + u32 vdev_id; + bool enable; + u32 hw_filter_bitmap; +}; + void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, struct ath12k_wmi_resource_config_arg *config); void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, @@ -5457,4 +5476,6 @@ int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id, u32 enable); int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id, struct wmi_pno_scan_req_arg *pno_scan); +int ath12k_wmi_hw_data_filter_cmd(struct ath12k *ar, + struct wmi_hw_data_filter_arg *arg); #endif diff --git a/drivers/net/wireless/ath/ath12k/wow.c b/drivers/net/wireless/ath/ath12k/wow.c index 5d4f11028403..146281244f70 100644 --- a/drivers/net/wireless/ath/ath12k/wow.c +++ b/drivers/net/wireless/ath/ath12k/wow.c @@ -546,6 +546,59 @@ static int ath12k_wow_nlo_cleanup(struct ath12k *ar) return 0; } +static int ath12k_wow_set_hw_filter(struct ath12k *ar) +{ + struct wmi_hw_data_filter_arg arg; + struct ath12k_vif *arvif; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->vdev_type != WMI_VDEV_TYPE_STA) + continue; + + arg.vdev_id = arvif->vdev_id; + arg.enable = true; + arg.hw_filter_bitmap = WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC; + ret = ath12k_wmi_hw_data_filter_cmd(ar, &arg); + if (ret) { + ath12k_warn(ar->ab, "failed to set hw data filter on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + +static int ath12k_wow_clear_hw_filter(struct ath12k *ar) +{ + struct wmi_hw_data_filter_arg arg; + struct ath12k_vif *arvif; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->vdev_type != WMI_VDEV_TYPE_STA) + continue; + + arg.vdev_id = arvif->vdev_id; + arg.enable = false; + arg.hw_filter_bitmap = 0; + ret = ath12k_wmi_hw_data_filter_cmd(ar, &arg); + + if (ret) { + ath12k_warn(ar->ab, "failed to clear hw data filter on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + int ath12k_wow_op_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { @@ -575,6 +628,13 @@ int ath12k_wow_op_suspend(struct ieee80211_hw *hw, goto cleanup; } + ret = ath12k_wow_set_hw_filter(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to set hw filter: %d\n", + ret); + goto cleanup; + } + ret = ath12k_wow_enable(ar); if (ret) { ath12k_warn(ar->ab, "failed to start wow: %d\n", ret); @@ -642,6 +702,12 @@ int ath12k_wow_op_resume(struct ieee80211_hw *hw) goto exit; } + ret = ath12k_wow_clear_hw_filter(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to clear hw filter: %d\n", ret); + goto exit; + } + exit: if (ret) { switch (ah->state) { From 1666108c74c438180dd69973e57909420836fd49 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 19 Jun 2024 17:21:12 +0300 Subject: [PATCH 26/38] wifi: ath12k: support ARP and NS offload Support ARP and NS offload in WoW state. Tested this way: put machine A with QCA6390 to WoW state, ping/ping6 machine A from another machine B, check sniffer to see any ARP response and Neighbor Advertisement from machine A. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240604055407.12506-7-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 1 + drivers/net/wireless/ath/ath12k/wmi.c | 148 ++++++++++++++++++++ drivers/net/wireless/ath/ath12k/wmi.h | 66 +++++++++ drivers/net/wireless/ath/ath12k/wow.c | 191 ++++++++++++++++++++++++++ 4 files changed, 406 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index cd8fb3797eea..13186496728b 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -6,6 +6,7 @@ #include #include + #include "mac.h" #include "core.h" #include "debug.h" diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index b1f343efa4b0..8b80d5b6bc01 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -7783,3 +7783,151 @@ int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id, return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID); } + +static void ath12k_wmi_fill_ns_offload(struct ath12k *ar, + struct wmi_arp_ns_offload_arg *offload, + void **ptr, + bool enable, + bool ext) +{ + struct wmi_ns_offload_params *ns; + struct wmi_tlv *tlv; + void *buf_ptr = *ptr; + u32 ns_cnt, ns_ext_tuples; + int i, max_offloads; + + ns_cnt = offload->ipv6_count; + + tlv = buf_ptr; + + if (ext) { + ns_ext_tuples = offload->ipv6_count - WMI_MAX_NS_OFFLOADS; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, + ns_ext_tuples * sizeof(*ns)); + i = WMI_MAX_NS_OFFLOADS; + max_offloads = offload->ipv6_count; + } else { + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, + WMI_MAX_NS_OFFLOADS * sizeof(*ns)); + i = 0; + max_offloads = WMI_MAX_NS_OFFLOADS; + } + + buf_ptr += sizeof(*tlv); + + for (; i < max_offloads; i++) { + ns = buf_ptr; + ns->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_NS_OFFLOAD_TUPLE, + sizeof(*ns)); + + if (enable) { + if (i < ns_cnt) + ns->flags |= cpu_to_le32(WMI_NSOL_FLAGS_VALID); + + memcpy(ns->target_ipaddr[0], offload->ipv6_addr[i], 16); + memcpy(ns->solicitation_ipaddr, offload->self_ipv6_addr[i], 16); + + if (offload->ipv6_type[i]) + ns->flags |= cpu_to_le32(WMI_NSOL_FLAGS_IS_IPV6_ANYCAST); + + memcpy(ns->target_mac.addr, offload->mac_addr, ETH_ALEN); + + if (!is_zero_ether_addr(ns->target_mac.addr)) + ns->flags |= cpu_to_le32(WMI_NSOL_FLAGS_MAC_VALID); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "wmi index %d ns_solicited %pI6 target %pI6", + i, ns->solicitation_ipaddr, + ns->target_ipaddr[0]); + } + + buf_ptr += sizeof(*ns); + } + + *ptr = buf_ptr; +} + +static void ath12k_wmi_fill_arp_offload(struct ath12k *ar, + struct wmi_arp_ns_offload_arg *offload, + void **ptr, + bool enable) +{ + struct wmi_arp_offload_params *arp; + struct wmi_tlv *tlv; + void *buf_ptr = *ptr; + int i; + + /* fill arp tuple */ + tlv = buf_ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, + WMI_MAX_ARP_OFFLOADS * sizeof(*arp)); + buf_ptr += sizeof(*tlv); + + for (i = 0; i < WMI_MAX_ARP_OFFLOADS; i++) { + arp = buf_ptr; + arp->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_ARP_OFFLOAD_TUPLE, + sizeof(*arp)); + + if (enable && i < offload->ipv4_count) { + /* Copy the target ip addr and flags */ + arp->flags = cpu_to_le32(WMI_ARPOL_FLAGS_VALID); + memcpy(arp->target_ipaddr, offload->ipv4_addr[i], 4); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi arp offload address %pI4", + arp->target_ipaddr); + } + + buf_ptr += sizeof(*arp); + } + + *ptr = buf_ptr; +} + +int ath12k_wmi_arp_ns_offload(struct ath12k *ar, + struct ath12k_vif *arvif, + struct wmi_arp_ns_offload_arg *offload, + bool enable) +{ + struct wmi_set_arp_ns_offload_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + void *buf_ptr; + size_t len; + u8 ns_cnt, ns_ext_tuples = 0; + + ns_cnt = offload->ipv6_count; + + len = sizeof(*cmd) + + sizeof(*tlv) + + WMI_MAX_NS_OFFLOADS * sizeof(struct wmi_ns_offload_params) + + sizeof(*tlv) + + WMI_MAX_ARP_OFFLOADS * sizeof(struct wmi_arp_offload_params); + + if (ns_cnt > WMI_MAX_NS_OFFLOADS) { + ns_ext_tuples = ns_cnt - WMI_MAX_NS_OFFLOADS; + len += sizeof(*tlv) + + ns_ext_tuples * sizeof(struct wmi_ns_offload_params); + } + + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + buf_ptr = skb->data; + cmd = buf_ptr; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_SET_ARP_NS_OFFLOAD_CMD, + sizeof(*cmd)); + cmd->flags = cpu_to_le32(0); + cmd->vdev_id = cpu_to_le32(arvif->vdev_id); + cmd->num_ns_ext_tuples = cpu_to_le32(ns_ext_tuples); + + buf_ptr += sizeof(*cmd); + + ath12k_wmi_fill_ns_offload(ar, offload, &buf_ptr, enable, 0); + ath12k_wmi_fill_arp_offload(ar, offload, &buf_ptr, enable); + + if (ns_ext_tuples) + ath12k_wmi_fill_ns_offload(ar, offload, &buf_ptr, enable, 1); + + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID); +} diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 62d7bb9e791d..ac0517f46b72 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -24,6 +24,7 @@ struct ath12k_base; struct ath12k; +struct ath12k_vif; /* There is no signed version of __le32, so for a temporary solution come * up with our own version. The idea is from fs/ntfs/endian.h. @@ -5313,6 +5314,66 @@ struct wmi_hw_data_filter_arg { u32 hw_filter_bitmap; }; +#define WMI_IPV6_UC_TYPE 0 +#define WMI_IPV6_AC_TYPE 1 + +#define WMI_IPV6_MAX_COUNT 16 +#define WMI_IPV4_MAX_COUNT 2 + +struct wmi_arp_ns_offload_arg { + u8 ipv4_addr[WMI_IPV4_MAX_COUNT][4]; + u32 ipv4_count; + u32 ipv6_count; + u8 ipv6_addr[WMI_IPV6_MAX_COUNT][16]; + u8 self_ipv6_addr[WMI_IPV6_MAX_COUNT][16]; + u8 ipv6_type[WMI_IPV6_MAX_COUNT]; + bool ipv6_valid[WMI_IPV6_MAX_COUNT]; + u8 mac_addr[ETH_ALEN]; +}; + +#define WMI_MAX_NS_OFFLOADS 2 +#define WMI_MAX_ARP_OFFLOADS 2 + +#define WMI_ARPOL_FLAGS_VALID BIT(0) +#define WMI_ARPOL_FLAGS_MAC_VALID BIT(1) +#define WMI_ARPOL_FLAGS_REMOTE_IP_VALID BIT(2) + +struct wmi_arp_offload_params { + __le32 tlv_header; + __le32 flags; + u8 target_ipaddr[4]; + u8 remote_ipaddr[4]; + struct ath12k_wmi_mac_addr_params target_mac; +} __packed; + +#define WMI_NSOL_FLAGS_VALID BIT(0) +#define WMI_NSOL_FLAGS_MAC_VALID BIT(1) +#define WMI_NSOL_FLAGS_REMOTE_IP_VALID BIT(2) +#define WMI_NSOL_FLAGS_IS_IPV6_ANYCAST BIT(3) + +#define WMI_NSOL_MAX_TARGET_IPS 2 + +struct wmi_ns_offload_params { + __le32 tlv_header; + __le32 flags; + u8 target_ipaddr[WMI_NSOL_MAX_TARGET_IPS][16]; + u8 solicitation_ipaddr[16]; + u8 remote_ipaddr[16]; + struct ath12k_wmi_mac_addr_params target_mac; +} __packed; + +struct wmi_set_arp_ns_offload_cmd { + __le32 tlv_header; + __le32 flags; + __le32 vdev_id; + __le32 num_ns_ext_tuples; + /* The TLVs follow: + * wmi_ns_offload_params ns[WMI_MAX_NS_OFFLOADS]; + * wmi_arp_offload_params arp[WMI_MAX_ARP_OFFLOADS]; + * wmi_ns_offload_params ns_ext[num_ns_ext_tuples]; + */ +} __packed; + void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, struct ath12k_wmi_resource_config_arg *config); void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, @@ -5478,4 +5539,9 @@ int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id, struct wmi_pno_scan_req_arg *pno_scan); int ath12k_wmi_hw_data_filter_cmd(struct ath12k *ar, struct wmi_hw_data_filter_arg *arg); +int ath12k_wmi_arp_ns_offload(struct ath12k *ar, + struct ath12k_vif *arvif, + struct wmi_arp_ns_offload_arg *offload, + bool enable); + #endif diff --git a/drivers/net/wireless/ath/ath12k/wow.c b/drivers/net/wireless/ath/ath12k/wow.c index 146281244f70..8dfa7853e7a4 100644 --- a/drivers/net/wireless/ath/ath12k/wow.c +++ b/drivers/net/wireless/ath/ath12k/wow.c @@ -5,6 +5,10 @@ */ #include +#include +#include +#include +#include #include "mac.h" @@ -599,6 +603,179 @@ static int ath12k_wow_clear_hw_filter(struct ath12k *ar) return 0; } +static void ath12k_wow_generate_ns_mc_addr(struct ath12k_base *ab, + struct wmi_arp_ns_offload_arg *offload) +{ + int i; + + for (i = 0; i < offload->ipv6_count; i++) { + offload->self_ipv6_addr[i][0] = 0xff; + offload->self_ipv6_addr[i][1] = 0x02; + offload->self_ipv6_addr[i][11] = 0x01; + offload->self_ipv6_addr[i][12] = 0xff; + offload->self_ipv6_addr[i][13] = + offload->ipv6_addr[i][13]; + offload->self_ipv6_addr[i][14] = + offload->ipv6_addr[i][14]; + offload->self_ipv6_addr[i][15] = + offload->ipv6_addr[i][15]; + ath12k_dbg(ab, ATH12K_DBG_WOW, "NS solicited addr %pI6\n", + offload->self_ipv6_addr[i]); + } +} + +static void ath12k_wow_prepare_ns_offload(struct ath12k_vif *arvif, + struct wmi_arp_ns_offload_arg *offload) +{ + struct net_device *ndev = ieee80211_vif_to_wdev(arvif->vif)->netdev; + struct ath12k_base *ab = arvif->ar->ab; + struct inet6_ifaddr *ifa6; + struct ifacaddr6 *ifaca6; + struct inet6_dev *idev; + u32 count = 0, scope; + + if (!ndev) + return; + + idev = in6_dev_get(ndev); + if (!idev) + return; + + ath12k_dbg(ab, ATH12K_DBG_WOW, "wow prepare ns offload\n"); + + read_lock_bh(&idev->lock); + + /* get unicast address */ + list_for_each_entry(ifa6, &idev->addr_list, if_list) { + if (count >= WMI_IPV6_MAX_COUNT) + goto unlock; + + if (ifa6->flags & IFA_F_DADFAILED) + continue; + + scope = ipv6_addr_src_scope(&ifa6->addr); + if (scope != IPV6_ADDR_SCOPE_LINKLOCAL && + scope != IPV6_ADDR_SCOPE_GLOBAL) { + ath12k_dbg(ab, ATH12K_DBG_WOW, + "Unsupported ipv6 scope: %d\n", scope); + continue; + } + + memcpy(offload->ipv6_addr[count], &ifa6->addr.s6_addr, + sizeof(ifa6->addr.s6_addr)); + offload->ipv6_type[count] = WMI_IPV6_UC_TYPE; + ath12k_dbg(ab, ATH12K_DBG_WOW, "mac count %d ipv6 uc %pI6 scope %d\n", + count, offload->ipv6_addr[count], + scope); + count++; + } + + /* get anycast address */ + rcu_read_lock(); + + for (ifaca6 = rcu_dereference(idev->ac_list); ifaca6; + ifaca6 = rcu_dereference(ifaca6->aca_next)) { + if (count >= WMI_IPV6_MAX_COUNT) { + rcu_read_unlock(); + goto unlock; + } + + scope = ipv6_addr_src_scope(&ifaca6->aca_addr); + if (scope != IPV6_ADDR_SCOPE_LINKLOCAL && + scope != IPV6_ADDR_SCOPE_GLOBAL) { + ath12k_dbg(ab, ATH12K_DBG_WOW, + "Unsupported ipv scope: %d\n", scope); + continue; + } + + memcpy(offload->ipv6_addr[count], &ifaca6->aca_addr, + sizeof(ifaca6->aca_addr)); + offload->ipv6_type[count] = WMI_IPV6_AC_TYPE; + ath12k_dbg(ab, ATH12K_DBG_WOW, "mac count %d ipv6 ac %pI6 scope %d\n", + count, offload->ipv6_addr[count], + scope); + count++; + } + + rcu_read_unlock(); + +unlock: + read_unlock_bh(&idev->lock); + + in6_dev_put(idev); + + offload->ipv6_count = count; + ath12k_wow_generate_ns_mc_addr(ab, offload); +} + +static void ath12k_wow_prepare_arp_offload(struct ath12k_vif *arvif, + struct wmi_arp_ns_offload_arg *offload) +{ + struct ieee80211_vif *vif = arvif->vif; + struct ieee80211_vif_cfg vif_cfg = vif->cfg; + struct ath12k_base *ab = arvif->ar->ab; + u32 ipv4_cnt; + + ath12k_dbg(ab, ATH12K_DBG_WOW, "wow prepare arp offload\n"); + + ipv4_cnt = min(vif_cfg.arp_addr_cnt, WMI_IPV4_MAX_COUNT); + memcpy(offload->ipv4_addr, vif_cfg.arp_addr_list, ipv4_cnt * sizeof(u32)); + offload->ipv4_count = ipv4_cnt; + + ath12k_dbg(ab, ATH12K_DBG_WOW, + "wow arp_addr_cnt %d vif->addr %pM, offload_addr %pI4\n", + vif_cfg.arp_addr_cnt, vif->addr, offload->ipv4_addr); +} + +static int ath12k_wow_arp_ns_offload(struct ath12k *ar, bool enable) +{ + struct wmi_arp_ns_offload_arg *offload; + struct ath12k_vif *arvif; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + offload = kmalloc(sizeof(*offload), GFP_KERNEL); + if (!offload) + return -ENOMEM; + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->vdev_type != WMI_VDEV_TYPE_STA) + continue; + + memset(offload, 0, sizeof(*offload)); + + memcpy(offload->mac_addr, arvif->vif->addr, ETH_ALEN); + ath12k_wow_prepare_ns_offload(arvif, offload); + ath12k_wow_prepare_arp_offload(arvif, offload); + + ret = ath12k_wmi_arp_ns_offload(ar, arvif, offload, enable); + if (ret) { + ath12k_warn(ar->ab, "failed to set arp ns offload vdev %i: enable %d, ret %d\n", + arvif->vdev_id, enable, ret); + return ret; + } + } + + kfree(offload); + + return 0; +} + +static int ath12k_wow_protocol_offload(struct ath12k *ar, bool enable) +{ + int ret; + + ret = ath12k_wow_arp_ns_offload(ar, enable); + if (ret) { + ath12k_warn(ar->ab, "failed to offload ARP and NS %d %d\n", + enable, ret); + return ret; + } + + return 0; +} + int ath12k_wow_op_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { @@ -622,6 +799,13 @@ int ath12k_wow_op_suspend(struct ieee80211_hw *hw, goto cleanup; } + ret = ath12k_wow_protocol_offload(ar, true); + if (ret) { + ath12k_warn(ar->ab, "failed to set wow protocol offload events: %d\n", + ret); + goto cleanup; + } + ret = ath12k_mac_wait_tx_complete(ar); if (ret) { ath12k_warn(ar->ab, "failed to wait tx complete: %d\n", ret); @@ -708,6 +892,13 @@ int ath12k_wow_op_resume(struct ieee80211_hw *hw) goto exit; } + ret = ath12k_wow_protocol_offload(ar, false); + if (ret) { + ath12k_warn(ar->ab, "failed to clear wow protocol offload events: %d\n", + ret); + goto exit; + } + exit: if (ret) { switch (ah->state) { From aab4ae566fa15c19efadad16a8b54306b9b37e10 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 19 Jun 2024 17:21:12 +0300 Subject: [PATCH 27/38] wifi: ath12k: support GTK rekey offload Host sets GTK related info to firmware before WoW is enabled, and gets rekey replay_count and then disables GTK rekey when WoW quits. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240604055407.12506-8-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/core.h | 8 ++ drivers/net/wireless/ath/ath12k/mac.c | 38 +++++++++ drivers/net/wireless/ath/ath12k/wmi.c | 112 +++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/wmi.h | 39 +++++++++ drivers/net/wireless/ath/ath12k/wow.c | 46 +++++++++- 5 files changed, 242 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 81c4bfe7792f..9de3df924a1c 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -230,6 +230,13 @@ struct ath12k_vif_cache { u32 bss_conf_changed; }; +struct ath12k_rekey_data { + u8 kck[NL80211_KCK_LEN]; + u8 kek[NL80211_KCK_LEN]; + u64 replay_ctr; + bool enable_offload; +}; + struct ath12k_vif { u32 vdev_id; enum wmi_vdev_type vdev_type; @@ -286,6 +293,7 @@ struct ath12k_vif { u32 punct_bitmap; bool ps; struct ath12k_vif_cache *cache; + struct ath12k_rekey_data rekey_data; }; struct ath12k_vif_iter { diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 13186496728b..261859ad967c 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -2842,6 +2842,7 @@ static void ath12k_bss_assoc(struct ath12k *ar, } arvif->is_up = true; + arvif->rekey_data.enable_offload = false; ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac vdev %d up (associated) bssid %pM aid %d\n", @@ -2889,6 +2890,8 @@ static void ath12k_bss_disassoc(struct ath12k *ar, arvif->is_up = false; + memset(&arvif->rekey_data, 0, sizeof(arvif->rekey_data)); + cancel_delayed_work(&arvif->connection_loss_work); } @@ -8545,6 +8548,40 @@ static int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw, return ret; } +static void ath12k_mac_op_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data) +{ + struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif); + struct ath12k_rekey_data *rekey_data = &arvif->rekey_data; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k *ar = ath12k_ah_to_ar(ah, 0); + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac set rekey data vdev %d\n", + arvif->vdev_id); + + mutex_lock(&ar->conf_mutex); + + memcpy(rekey_data->kck, data->kck, NL80211_KCK_LEN); + memcpy(rekey_data->kek, data->kek, NL80211_KEK_LEN); + + /* The supplicant works on big-endian, the firmware expects it on + * little endian. + */ + rekey_data->replay_ctr = get_unaligned_be64(data->replay_ctr); + + arvif->rekey_data.enable_offload = true; + + ath12k_dbg_dump(ar->ab, ATH12K_DBG_MAC, "kck", NULL, + rekey_data->kck, NL80211_KCK_LEN); + ath12k_dbg_dump(ar->ab, ATH12K_DBG_MAC, "kek", NULL, + rekey_data->kck, NL80211_KEK_LEN); + ath12k_dbg_dump(ar->ab, ATH12K_DBG_MAC, "replay ctr", NULL, + &rekey_data->replay_ctr, sizeof(rekey_data->replay_ctr)); + + mutex_unlock(&ar->conf_mutex); +} + static const struct ieee80211_ops ath12k_ops = { .tx = ath12k_mac_op_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, @@ -8560,6 +8597,7 @@ static const struct ieee80211_ops ath12k_ops = { .hw_scan = ath12k_mac_op_hw_scan, .cancel_hw_scan = ath12k_mac_op_cancel_hw_scan, .set_key = ath12k_mac_op_set_key, + .set_rekey_data = ath12k_mac_op_set_rekey_data, .sta_state = ath12k_mac_op_sta_state, .sta_set_txpwr = ath12k_mac_op_sta_set_txpwr, .sta_rc_update = ath12k_mac_op_sta_rc_update, diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 8b80d5b6bc01..0e41386bca45 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -7091,6 +7091,56 @@ static void ath12k_wmi_event_wow_wakeup_host(struct ath12k_base *ab, struct sk_b complete(&ab->wow.wakeup_completed); } +static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab, + struct sk_buff *skb) +{ + const struct wmi_gtk_offload_status_event *ev; + struct ath12k_vif *arvif; + __be64 replay_ctr_be; + u64 replay_ctr; + const void **tb; + int ret; + + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath12k_warn(ab, "failed to parse tlv: %d\n", ret); + return; + } + + ev = tb[WMI_TAG_GTK_OFFLOAD_STATUS_EVENT]; + if (!ev) { + ath12k_warn(ab, "failed to fetch gtk offload status ev"); + kfree(tb); + return; + } + + rcu_read_lock(); + arvif = ath12k_mac_get_arvif_by_vdev_id(ab, le32_to_cpu(ev->vdev_id)); + if (!arvif) { + rcu_read_unlock(); + ath12k_warn(ab, "failed to get arvif for vdev_id:%d\n", + le32_to_cpu(ev->vdev_id)); + kfree(tb); + return; + } + + replay_ctr = le64_to_cpu(ev->replay_ctr); + arvif->rekey_data.replay_ctr = replay_ctr; + ath12k_dbg(ab, ATH12K_DBG_WMI, "wmi gtk offload event refresh_cnt %d replay_ctr %llu\n", + le32_to_cpu(ev->refresh_cnt), replay_ctr); + + /* supplicant expects big-endian replay counter */ + replay_ctr_be = cpu_to_be64(replay_ctr); + + ieee80211_gtk_rekey_notify(arvif->vif, arvif->bssid, + (void *)&replay_ctr_be, GFP_ATOMIC); + + rcu_read_unlock(); + + kfree(tb); +} + static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; @@ -7214,6 +7264,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) case WMI_WOW_WAKEUP_HOST_EVENTID: ath12k_wmi_event_wow_wakeup_host(ab, skb); break; + case WMI_GTK_OFFLOAD_STATUS_EVENTID: + ath12k_wmi_gtk_offload_status_event(ab, skb); + break; /* TODO: Add remaining events */ default: ath12k_dbg(ab, ATH12K_DBG_WMI, "Unknown eventid: 0x%x\n", id); @@ -7931,3 +7984,62 @@ int ath12k_wmi_arp_ns_offload(struct ath12k *ar, return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID); } + +int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar, + struct ath12k_vif *arvif, bool enable) +{ + struct ath12k_rekey_data *rekey_data = &arvif->rekey_data; + struct wmi_gtk_rekey_offload_cmd *cmd; + struct sk_buff *skb; + __le64 replay_ctr; + int len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_gtk_rekey_offload_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_GTK_OFFLOAD_CMD, sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(arvif->vdev_id); + + if (enable) { + cmd->flags = cpu_to_le32(GTK_OFFLOAD_ENABLE_OPCODE); + + /* the length in rekey_data and cmd is equal */ + memcpy(cmd->kck, rekey_data->kck, sizeof(cmd->kck)); + memcpy(cmd->kek, rekey_data->kek, sizeof(cmd->kek)); + + replay_ctr = cpu_to_le64(rekey_data->replay_ctr); + memcpy(cmd->replay_ctr, &replay_ctr, + sizeof(replay_ctr)); + } else { + cmd->flags = cpu_to_le32(GTK_OFFLOAD_DISABLE_OPCODE); + } + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "offload gtk rekey vdev: %d %d\n", + arvif->vdev_id, enable); + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); +} + +int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar, + struct ath12k_vif *arvif) +{ + struct wmi_gtk_rekey_offload_cmd *cmd; + struct sk_buff *skb; + int len; + + len = sizeof(*cmd); + skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_gtk_rekey_offload_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_GTK_OFFLOAD_CMD, sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(arvif->vdev_id); + cmd->flags = cpu_to_le32(GTK_OFFLOAD_REQUEST_STATUS_OPCODE); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "get gtk rekey vdev_id: %d\n", + arvif->vdev_id); + return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); +} diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index ac0517f46b72..32f74993b129 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -5374,6 +5374,41 @@ struct wmi_set_arp_ns_offload_cmd { */ } __packed; +#define GTK_OFFLOAD_OPCODE_MASK 0xFF000000 +#define GTK_OFFLOAD_ENABLE_OPCODE 0x01000000 +#define GTK_OFFLOAD_DISABLE_OPCODE 0x02000000 +#define GTK_OFFLOAD_REQUEST_STATUS_OPCODE 0x04000000 + +#define GTK_OFFLOAD_KEK_BYTES 16 +#define GTK_OFFLOAD_KCK_BYTES 16 +#define GTK_REPLAY_COUNTER_BYTES 8 +#define WMI_MAX_KEY_LEN 32 +#define IGTK_PN_SIZE 6 + +struct wmi_gtk_offload_status_event { + __le32 vdev_id; + __le32 flags; + __le32 refresh_cnt; + __le64 replay_ctr; + u8 igtk_key_index; + u8 igtk_key_length; + u8 igtk_key_rsc[IGTK_PN_SIZE]; + u8 igtk_key[WMI_MAX_KEY_LEN]; + u8 gtk_key_index; + u8 gtk_key_length; + u8 gtk_key_rsc[GTK_REPLAY_COUNTER_BYTES]; + u8 gtk_key[WMI_MAX_KEY_LEN]; +} __packed; + +struct wmi_gtk_rekey_offload_cmd { + __le32 tlv_header; + __le32 vdev_id; + __le32 flags; + u8 kek[GTK_OFFLOAD_KEK_BYTES]; + u8 kck[GTK_OFFLOAD_KCK_BYTES]; + u8 replay_ctr[GTK_REPLAY_COUNTER_BYTES]; +} __packed; + void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, struct ath12k_wmi_resource_config_arg *config); void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, @@ -5543,5 +5578,9 @@ int ath12k_wmi_arp_ns_offload(struct ath12k *ar, struct ath12k_vif *arvif, struct wmi_arp_ns_offload_arg *offload, bool enable); +int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar, + struct ath12k_vif *arvif, bool enable); +int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar, + struct ath12k_vif *arvif); #endif diff --git a/drivers/net/wireless/ath/ath12k/wow.c b/drivers/net/wireless/ath/ath12k/wow.c index 8dfa7853e7a4..5a7001edafe0 100644 --- a/drivers/net/wireless/ath/ath12k/wow.c +++ b/drivers/net/wireless/ath/ath12k/wow.c @@ -21,7 +21,9 @@ static const struct wiphy_wowlan_support ath12k_wowlan_support = { .flags = WIPHY_WOWLAN_DISCONNECT | - WIPHY_WOWLAN_MAGIC_PKT, + WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE, .pattern_min_len = WOW_MIN_PATTERN_SIZE, .pattern_max_len = WOW_MAX_PATTERN_SIZE, .max_pkt_offset = WOW_MAX_PKT_OFFSET, @@ -762,6 +764,41 @@ static int ath12k_wow_arp_ns_offload(struct ath12k *ar, bool enable) return 0; } +static int ath12k_gtk_rekey_offload(struct ath12k *ar, bool enable) +{ + struct ath12k_vif *arvif; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->vdev_type != WMI_VDEV_TYPE_STA || + !arvif->is_up || + !arvif->rekey_data.enable_offload) + continue; + + /* get rekey info before disable rekey offload */ + if (!enable) { + ret = ath12k_wmi_gtk_rekey_getinfo(ar, arvif); + if (ret) { + ath12k_warn(ar->ab, "failed to request rekey info vdev %i, ret %d\n", + arvif->vdev_id, ret); + return ret; + } + } + + ret = ath12k_wmi_gtk_rekey_offload(ar, arvif, enable); + + if (ret) { + ath12k_warn(ar->ab, "failed to offload gtk reky vdev %i: enable %d, ret %d\n", + arvif->vdev_id, enable, ret); + return ret; + } + } + + return 0; +} + static int ath12k_wow_protocol_offload(struct ath12k *ar, bool enable) { int ret; @@ -773,6 +810,13 @@ static int ath12k_wow_protocol_offload(struct ath12k *ar, bool enable) return ret; } + ret = ath12k_gtk_rekey_offload(ar, enable); + if (ret) { + ath12k_warn(ar->ab, "failed to offload gtk rekey %d %d\n", + enable, ret); + return ret; + } + return 0; } From 7af01e5695295444d47f4ead3e27e91d8c9e60f4 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 19 Jun 2024 17:21:12 +0300 Subject: [PATCH 28/38] wifi: ath12k: handle keepalive during WoWLAN suspend and resume With WoWLAN enabled and after sleeping for a rather long time, we are seeing that with some APs, it is not able to wake up the STA though the correct wake up pattern has been configured. This is because the host doesn't send keepalive command to firmware, thus firmware will not send any packet to the AP and after a specific time the AP kicks out the STA. So enable keepalive before going to suspend and disable it after resume back. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Baochen Qiang Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240604055407.12506-9-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 31 ++++++++++++++++++ drivers/net/wireless/ath/ath12k/mac.h | 4 +++ drivers/net/wireless/ath/ath12k/wmi.c | 38 ++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/wmi.h | 46 +++++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/wow.c | 34 ++++++++++++++++++++ 5 files changed, 153 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 261859ad967c..43801ff5bfd8 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -9418,3 +9418,34 @@ int ath12k_mac_allocate(struct ath12k_base *ab) return ret; } + +int ath12k_mac_vif_set_keepalive(struct ath12k_vif *arvif, + enum wmi_sta_keepalive_method method, + u32 interval) +{ + struct wmi_sta_keepalive_arg arg = {}; + struct ath12k *ar = arvif->ar; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + if (arvif->vdev_type != WMI_VDEV_TYPE_STA) + return 0; + + if (!test_bit(WMI_TLV_SERVICE_STA_KEEP_ALIVE, ar->ab->wmi_ab.svc_map)) + return 0; + + arg.vdev_id = arvif->vdev_id; + arg.enabled = 1; + arg.method = method; + arg.interval = interval; + + ret = ath12k_wmi_sta_keepalive(ar, &arg); + if (ret) { + ath12k_warn(ar->ab, "failed to set keepalive on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + return 0; +} diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h index 69fd282b9dd3..2c6cb144827a 100644 --- a/drivers/net/wireless/ath/ath12k/mac.h +++ b/drivers/net/wireless/ath/ath12k/mac.h @@ -9,6 +9,7 @@ #include #include +#include "wmi.h" struct ath12k; struct ath12k_base; @@ -81,5 +82,8 @@ int ath12k_mac_rfkill_config(struct ath12k *ar); int ath12k_mac_wait_tx_complete(struct ath12k *ar); void ath12k_mac_handle_beacon(struct ath12k *ar, struct sk_buff *skb); void ath12k_mac_handle_beacon_miss(struct ath12k *ar, u32 vdev_id); +int ath12k_mac_vif_set_keepalive(struct ath12k_vif *arvif, + enum wmi_sta_keepalive_method method, + u32 interval); #endif diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 0e41386bca45..7c5976366615 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -8043,3 +8043,41 @@ int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar, arvif->vdev_id); return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); } + +int ath12k_wmi_sta_keepalive(struct ath12k *ar, + const struct wmi_sta_keepalive_arg *arg) +{ + struct wmi_sta_keepalive_arp_resp_params *arp; + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct wmi_sta_keepalive_cmd *cmd; + struct sk_buff *skb; + size_t len; + + len = sizeof(*cmd) + sizeof(*arp); + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_sta_keepalive_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_STA_KEEPALIVE_CMD, sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(arg->vdev_id); + cmd->enabled = cpu_to_le32(arg->enabled); + cmd->interval = cpu_to_le32(arg->interval); + cmd->method = cpu_to_le32(arg->method); + + arp = (struct wmi_sta_keepalive_arp_resp_params *)(cmd + 1); + arp->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_STA_KEEPALVE_ARP_RESPONSE, + sizeof(*arp)); + if (arg->method == WMI_STA_KEEPALIVE_METHOD_UNSOLICITED_ARP_RESPONSE || + arg->method == WMI_STA_KEEPALIVE_METHOD_GRATUITOUS_ARP_REQUEST) { + arp->src_ip4_addr = cpu_to_le32(arg->src_ip4_addr); + arp->dest_ip4_addr = cpu_to_le32(arg->dest_ip4_addr); + ether_addr_copy(arp->dest_mac_addr.addr, arg->dest_mac_addr); + } + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "wmi sta keepalive vdev %d enabled %d method %d interval %d\n", + arg->vdev_id, arg->enabled, arg->method, arg->interval); + + return ath12k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID); +} diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 32f74993b129..0ac7accc45dc 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -5409,6 +5409,50 @@ struct wmi_gtk_rekey_offload_cmd { u8 replay_ctr[GTK_REPLAY_COUNTER_BYTES]; } __packed; +struct wmi_sta_keepalive_cmd { + __le32 tlv_header; + __le32 vdev_id; + __le32 enabled; + + /* WMI_STA_KEEPALIVE_METHOD_ */ + __le32 method; + + /* in seconds */ + __le32 interval; + + /* following this structure is the TLV for struct + * wmi_sta_keepalive_arp_resp_params + */ +} __packed; + +struct wmi_sta_keepalive_arp_resp_params { + __le32 tlv_header; + __le32 src_ip4_addr; + __le32 dest_ip4_addr; + struct ath12k_wmi_mac_addr_params dest_mac_addr; +} __packed; + +struct wmi_sta_keepalive_arg { + u32 vdev_id; + u32 enabled; + u32 method; + u32 interval; + u32 src_ip4_addr; + u32 dest_ip4_addr; + const u8 dest_mac_addr[ETH_ALEN]; +}; + +enum wmi_sta_keepalive_method { + WMI_STA_KEEPALIVE_METHOD_NULL_FRAME = 1, + WMI_STA_KEEPALIVE_METHOD_UNSOLICITED_ARP_RESPONSE = 2, + WMI_STA_KEEPALIVE_METHOD_ETHERNET_LOOPBACK = 3, + WMI_STA_KEEPALIVE_METHOD_GRATUITOUS_ARP_REQUEST = 4, + WMI_STA_KEEPALIVE_METHOD_MGMT_VENDOR_ACTION = 5, +}; + +#define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30 +#define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0 + void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, struct ath12k_wmi_resource_config_arg *config); void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, @@ -5582,5 +5626,7 @@ int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar, struct ath12k_vif *arvif, bool enable); int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar, struct ath12k_vif *arvif); +int ath12k_wmi_sta_keepalive(struct ath12k *ar, + const struct wmi_sta_keepalive_arg *arg); #endif diff --git a/drivers/net/wireless/ath/ath12k/wow.c b/drivers/net/wireless/ath/ath12k/wow.c index 5a7001edafe0..685e8e98d845 100644 --- a/drivers/net/wireless/ath/ath12k/wow.c +++ b/drivers/net/wireless/ath/ath12k/wow.c @@ -820,6 +820,24 @@ static int ath12k_wow_protocol_offload(struct ath12k *ar, bool enable) return 0; } +static int ath12k_wow_set_keepalive(struct ath12k *ar, + enum wmi_sta_keepalive_method method, + u32 interval) +{ + struct ath12k_vif *arvif; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + list_for_each_entry(arvif, &ar->arvifs, list) { + ret = ath12k_mac_vif_set_keepalive(arvif, method, interval); + if (ret) + return ret; + } + + return 0; +} + int ath12k_wow_op_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { @@ -863,6 +881,14 @@ int ath12k_wow_op_suspend(struct ieee80211_hw *hw, goto cleanup; } + ret = ath12k_wow_set_keepalive(ar, + WMI_STA_KEEPALIVE_METHOD_NULL_FRAME, + WMI_STA_KEEPALIVE_INTERVAL_DEFAULT); + if (ret) { + ath12k_warn(ar->ab, "failed to enable wow keepalive: %d\n", ret); + goto cleanup; + } + ret = ath12k_wow_enable(ar); if (ret) { ath12k_warn(ar->ab, "failed to start wow: %d\n", ret); @@ -943,6 +969,14 @@ int ath12k_wow_op_resume(struct ieee80211_hw *hw) goto exit; } + ret = ath12k_wow_set_keepalive(ar, + WMI_STA_KEEPALIVE_METHOD_NULL_FRAME, + WMI_STA_KEEPALIVE_INTERVAL_DISABLE); + if (ret) { + ath12k_warn(ar->ab, "failed to disable wow keepalive: %d\n", ret); + goto exit; + } + exit: if (ret) { switch (ah->state) { From 2c7857677d695166e913123b23ed7e55d80f72be Mon Sep 17 00:00:00 2001 From: Lingbo Kong Date: Fri, 21 Jun 2024 15:58:09 +0530 Subject: [PATCH 29/38] wifi: ath12k: Fix pdev id sent to firmware for single phy devices Pdev id from mac phy capabilities will be sent as a part of HTT/WMI command to firmware. This causes issue with single pdev devices where firmware does not respond to the WMI/HTT request sent from host. For single pdev devices firmware expects pdev id as 1 for 5 GHz/6 GHz phy and 2 for 2 GHz band. Add wrapper ath12k_mac_get_target_pdev_id() to help fetch right pdev for single pdev devices. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-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: Lingbo Kong Signed-off-by: Ramya Gnanasekar Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240621102809.3984004-1-quic_rgnanase@quicinc.com --- drivers/net/wireless/ath/ath12k/mac.c | 76 +++++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/mac.h | 1 + 2 files changed, 77 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 43801ff5bfd8..7475dddca8c5 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -673,6 +673,82 @@ static struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw, return NULL; } +static struct ath12k_vif *ath12k_mac_get_vif_up(struct ath12k *ar) +{ + struct ath12k_vif *arvif; + + lockdep_assert_held(&ar->conf_mutex); + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->is_up) + return arvif; + } + + return NULL; +} + +static bool ath12k_mac_band_match(enum nl80211_band band1, enum WMI_HOST_WLAN_BAND band2) +{ + switch (band1) { + case NL80211_BAND_2GHZ: + if (band2 & WMI_HOST_WLAN_2G_CAP) + return true; + break; + case NL80211_BAND_5GHZ: + case NL80211_BAND_6GHZ: + if (band2 & WMI_HOST_WLAN_5G_CAP) + return true; + break; + default: + return false; + } + + return false; +} + +static u8 ath12k_mac_get_target_pdev_id_from_vif(struct ath12k_vif *arvif) +{ + struct ath12k *ar = arvif->ar; + struct ath12k_base *ab = ar->ab; + struct ieee80211_vif *vif = arvif->vif; + struct cfg80211_chan_def def; + enum nl80211_band band; + u8 pdev_id = ab->fw_pdev[0].pdev_id; + int i; + + if (WARN_ON(ath12k_mac_vif_chan(vif, &def))) + return pdev_id; + + band = def.chan->band; + + for (i = 0; i < ab->fw_pdev_count; i++) { + if (ath12k_mac_band_match(band, ab->fw_pdev[i].supported_bands)) + return ab->fw_pdev[i].pdev_id; + } + + return pdev_id; +} + +u8 ath12k_mac_get_target_pdev_id(struct ath12k *ar) +{ + struct ath12k_vif *arvif; + struct ath12k_base *ab = ar->ab; + + if (!ab->hw_params->single_pdev_only) + return ar->pdev->pdev_id; + + arvif = ath12k_mac_get_vif_up(ar); + + /* fw_pdev array has pdev ids derived from phy capability + * service ready event (pdev_and_hw_link_ids). + * If no vif is active, return default first index. + */ + if (!arvif) + return ar->ab->fw_pdev[0].pdev_id; + + /* If active vif is found, return the pdev id matching chandef band */ + return ath12k_mac_get_target_pdev_id_from_vif(arvif); +} + static void ath12k_pdev_caps_update(struct ath12k *ar) { struct ath12k_base *ab = ar->ab; diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h index 2c6cb144827a..5efbb6822628 100644 --- a/drivers/net/wireless/ath/ath12k/mac.h +++ b/drivers/net/wireless/ath/ath12k/mac.h @@ -85,5 +85,6 @@ void ath12k_mac_handle_beacon_miss(struct ath12k *ar, u32 vdev_id); int ath12k_mac_vif_set_keepalive(struct ath12k_vif *arvif, enum wmi_sta_keepalive_method method, u32 interval); +u8 ath12k_mac_get_target_pdev_id(struct ath12k *ar); #endif From 1eeafd64c7b455381b77c546e41bc267e13e2809 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Mon, 24 Jun 2024 20:24:18 +0530 Subject: [PATCH 30/38] wifi: ath12k: fix peer metadata parsing Currently, the Rx data path only supports parsing peer metadata of version zero. However, the QCN9274 platform configures the peer metadata version as V1B. When V1B peer metadata is parsed using the version zero logic, invalid data is populated, causing valid packets to be dropped. To address this issue, refactor the peer metadata version and add the version based parsing to populate the data from peer metadata correctly. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Fixes: 287033810990 ("wifi: ath12k: add support for peer meta data version") Signed-off-by: Karthikeyan Periyasamy Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240624145418.2043461-1-quic_periyasa@quicinc.com --- drivers/net/wireless/ath/ath12k/dp.h | 1 + drivers/net/wireless/ath/ath12k/dp_rx.c | 40 ++++++++++++++++++---- drivers/net/wireless/ath/ath12k/hal_desc.h | 26 ++++++++++++-- drivers/net/wireless/ath/ath12k/hw.h | 2 -- drivers/net/wireless/ath/ath12k/wmi.c | 6 ++-- drivers/net/wireless/ath/ath12k/wmi.h | 11 ++++-- 6 files changed, 72 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 742094545089..b77497c14ac4 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -333,6 +333,7 @@ struct ath12k_dp { struct dp_srng reo_except_ring; struct dp_srng reo_cmd_ring; struct dp_srng reo_status_ring; + enum ath12k_peer_metadata_version peer_metadata_ver; struct dp_srng reo_dst_ring[DP_REO_DST_RING_MAX]; struct dp_tx_ring tx_ring[DP_TCL_NUM_RING_MAX]; struct hal_wbm_idle_scatter_list scatter_list[DP_IDLE_SCATTER_BUFS_MAX]; diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 54aea3c22311..d7bb412ad391 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -2583,6 +2583,29 @@ static void ath12k_dp_rx_process_received_packets(struct ath12k_base *ab, rcu_read_unlock(); } +static u16 ath12k_dp_rx_get_peer_id(struct ath12k_base *ab, + enum ath12k_peer_metadata_version ver, + __le32 peer_metadata) +{ + switch (ver) { + default: + ath12k_warn(ab, "Unknown peer metadata version: %d", ver); + fallthrough; + case ATH12K_PEER_METADATA_V0: + return le32_get_bits(peer_metadata, + RX_MPDU_DESC_META_DATA_V0_PEER_ID); + case ATH12K_PEER_METADATA_V1: + return le32_get_bits(peer_metadata, + RX_MPDU_DESC_META_DATA_V1_PEER_ID); + case ATH12K_PEER_METADATA_V1A: + return le32_get_bits(peer_metadata, + RX_MPDU_DESC_META_DATA_V1A_PEER_ID); + case ATH12K_PEER_METADATA_V1B: + return le32_get_bits(peer_metadata, + RX_MPDU_DESC_META_DATA_V1B_PEER_ID); + } +} + int ath12k_dp_rx_process(struct ath12k_base *ab, int ring_id, struct napi_struct *napi, int budget) { @@ -2611,6 +2634,8 @@ int ath12k_dp_rx_process(struct ath12k_base *ab, int ring_id, ath12k_hal_srng_access_begin(ab, srng); while ((desc = ath12k_hal_srng_dst_get_next_entry(ab, srng))) { + struct rx_mpdu_desc *mpdu_info; + struct rx_msdu_desc *msdu_info; enum hal_reo_dest_ring_push_reason push_reason; u32 cookie; @@ -2658,16 +2683,19 @@ int ath12k_dp_rx_process(struct ath12k_base *ab, int ring_id, continue; } - rxcb->is_first_msdu = !!(le32_to_cpu(desc->rx_msdu_info.info0) & + msdu_info = &desc->rx_msdu_info; + mpdu_info = &desc->rx_mpdu_info; + + rxcb->is_first_msdu = !!(le32_to_cpu(msdu_info->info0) & RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU); - rxcb->is_last_msdu = !!(le32_to_cpu(desc->rx_msdu_info.info0) & + rxcb->is_last_msdu = !!(le32_to_cpu(msdu_info->info0) & RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); - rxcb->is_continuation = !!(le32_to_cpu(desc->rx_msdu_info.info0) & + rxcb->is_continuation = !!(le32_to_cpu(msdu_info->info0) & RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); rxcb->mac_id = mac_id; - rxcb->peer_id = le32_get_bits(desc->rx_mpdu_info.peer_meta_data, - RX_MPDU_DESC_META_DATA_PEER_ID); - rxcb->tid = le32_get_bits(desc->rx_mpdu_info.info0, + rxcb->peer_id = ath12k_dp_rx_get_peer_id(ab, dp->peer_metadata_ver, + mpdu_info->peer_meta_data); + rxcb->tid = le32_get_bits(mpdu_info->info0, RX_MPDU_DESC_INFO0_TID); __skb_queue_tail(&msdu_list, msdu); diff --git a/drivers/net/wireless/ath/ath12k/hal_desc.h b/drivers/net/wireless/ath/ath12k/hal_desc.h index 02b7db06b24e..739f73370015 100644 --- a/drivers/net/wireless/ath/ath12k/hal_desc.h +++ b/drivers/net/wireless/ath/ath12k/hal_desc.h @@ -597,8 +597,30 @@ struct hal_tlv_64_hdr { #define RX_MPDU_DESC_INFO0_MPDU_QOS_CTRL_VALID BIT(27) #define RX_MPDU_DESC_INFO0_TID GENMASK(31, 28) -/* TODO revisit after meta data is concluded */ -#define RX_MPDU_DESC_META_DATA_PEER_ID GENMASK(15, 0) +/* Peer Metadata classification */ + +/* Version 0 */ +#define RX_MPDU_DESC_META_DATA_V0_PEER_ID GENMASK(15, 0) +#define RX_MPDU_DESC_META_DATA_V0_VDEV_ID GENMASK(23, 16) + +/* Version 1 */ +#define RX_MPDU_DESC_META_DATA_V1_PEER_ID GENMASK(13, 0) +#define RX_MPDU_DESC_META_DATA_V1_LOGICAL_LINK_ID GENMASK(15, 14) +#define RX_MPDU_DESC_META_DATA_V1_VDEV_ID GENMASK(23, 16) +#define RX_MPDU_DESC_META_DATA_V1_LMAC_ID GENMASK(25, 24) +#define RX_MPDU_DESC_META_DATA_V1_DEVICE_ID GENMASK(28, 26) + +/* Version 1A */ +#define RX_MPDU_DESC_META_DATA_V1A_PEER_ID GENMASK(13, 0) +#define RX_MPDU_DESC_META_DATA_V1A_VDEV_ID GENMASK(21, 14) +#define RX_MPDU_DESC_META_DATA_V1A_LOGICAL_LINK_ID GENMASK(25, 22) +#define RX_MPDU_DESC_META_DATA_V1A_DEVICE_ID GENMASK(28, 26) + +/* Version 1B */ +#define RX_MPDU_DESC_META_DATA_V1B_PEER_ID GENMASK(13, 0) +#define RX_MPDU_DESC_META_DATA_V1B_VDEV_ID GENMASK(21, 14) +#define RX_MPDU_DESC_META_DATA_V1B_HW_LINK_ID GENMASK(25, 22) +#define RX_MPDU_DESC_META_DATA_V1B_DEVICE_ID GENMASK(28, 26) struct rx_mpdu_desc { __le32 info0; /* %RX_MPDU_DESC_INFO */ diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index af33bf11416b..e792eb6b249b 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -78,8 +78,6 @@ #define TARGET_NUM_WDS_ENTRIES 32 #define TARGET_DMA_BURST_SIZE 1 #define TARGET_RX_BATCHMODE 1 -#define TARGET_RX_PEER_METADATA_VER_V1A 2 -#define TARGET_RX_PEER_METADATA_VER_V1B 3 #define TARGET_EMA_MAX_PROFILE_PERIOD 8 #define ATH12K_HW_DEFAULT_QUEUE 0 diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 7c5976366615..9f6be557365e 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -233,7 +233,7 @@ void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, config->beacon_tx_offload_max_vdev += config->ema_max_vap_cnt; if (test_bit(WMI_TLV_SERVICE_PEER_METADATA_V1A_V1B_SUPPORT, ab->wmi_ab.svc_map)) - config->dp_peer_meta_data_ver = TARGET_RX_PEER_METADATA_VER_V1B; + config->peer_metadata_ver = ATH12K_PEER_METADATA_V1B; } void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, @@ -3498,7 +3498,7 @@ ath12k_wmi_copy_resource_config(struct ath12k_wmi_resource_config_params *wmi_cf wmi_cfg->sched_params = cpu_to_le32(tg_cfg->sched_params); wmi_cfg->twt_ap_pdev_count = cpu_to_le32(tg_cfg->twt_ap_pdev_count); wmi_cfg->twt_ap_sta_count = cpu_to_le32(tg_cfg->twt_ap_sta_count); - wmi_cfg->flags2 = le32_encode_bits(tg_cfg->dp_peer_meta_data_ver, + wmi_cfg->flags2 = le32_encode_bits(tg_cfg->peer_metadata_ver, WMI_RSRC_CFG_FLAGS2_RX_PEER_METADATA_VERSION); wmi_cfg->host_service_flags = cpu_to_le32(tg_cfg->is_reg_cc_ext_event_supported << WMI_RSRC_CFG_HOST_SVC_FLAG_REG_CC_EXT_SUPPORT_BIT); @@ -3729,6 +3729,8 @@ int ath12k_wmi_cmd_init(struct ath12k_base *ab) arg.num_band_to_mac = ab->num_radios; ath12k_fill_band_to_mac_param(ab, arg.band_to_mac); + ab->dp.peer_metadata_ver = arg.res_cfg.peer_metadata_ver; + return ath12k_init_cmd_send(&wmi_ab->wmi[0], &arg); } diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 0ac7accc45dc..f1f52175a52b 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -2294,6 +2294,13 @@ struct ath12k_wmi_host_mem_chunk_arg { u32 req_id; }; +enum ath12k_peer_metadata_version { + ATH12K_PEER_METADATA_V0, + ATH12K_PEER_METADATA_V1, + ATH12K_PEER_METADATA_V1A, + ATH12K_PEER_METADATA_V1B +}; + struct ath12k_wmi_resource_config_arg { u32 num_vdevs; u32 num_peers; @@ -2356,10 +2363,10 @@ struct ath12k_wmi_resource_config_arg { u32 sched_params; u32 twt_ap_pdev_count; u32 twt_ap_sta_count; - bool is_reg_cc_ext_event_supported; - u8 dp_peer_meta_data_ver; + enum ath12k_peer_metadata_version peer_metadata_ver; u32 ema_max_vap_cnt; u32 ema_max_profile_period; + bool is_reg_cc_ext_event_supported; }; struct ath12k_wmi_init_cmd_arg { From 9783e0407421a4dfc25e3a3001d0e0cff4236806 Mon Sep 17 00:00:00 2001 From: Rameshkumar Sundaram Date: Wed, 26 Jun 2024 08:12:57 +0530 Subject: [PATCH 31/38] wifi: ath12k: fix driver initialization for WoW unsupported devices Commit 4a3c212eee0e ("wifi: ath12k: add basic WoW functionalities") broke driver initialization, now mac registration is allowed only for devices that advertise WMI_TLV_SERVICE_WOW, but QCN9274 doesn't support WoW and hence mac registration is aborted and driver is de-initialized. Allow mac registration to proceed without WoW Support for devices that don't advertise WMI_TLV_SERVICE_WOW. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Fixes: 4a3c212eee0e ("wifi: ath12k: add basic WoW functionalities") Signed-off-by: Rameshkumar Sundaram Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240626024257.709460-1-quic_ramess@quicinc.com --- drivers/net/wireless/ath/ath12k/wow.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/wow.c b/drivers/net/wireless/ath/ath12k/wow.c index 685e8e98d845..c5cba825a84a 100644 --- a/drivers/net/wireless/ath/ath12k/wow.c +++ b/drivers/net/wireless/ath/ath12k/wow.c @@ -1001,8 +1001,8 @@ int ath12k_wow_op_resume(struct ieee80211_hw *hw) int ath12k_wow_init(struct ath12k *ar) { - if (WARN_ON(!test_bit(WMI_TLV_SERVICE_WOW, ar->wmi->wmi_ab->svc_map))) - return -EINVAL; + if (!test_bit(WMI_TLV_SERVICE_WOW, ar->wmi->wmi_ab->svc_map)) + return 0; ar->wow.wowlan_support = ath12k_wowlan_support; From 3f73c24f28b317f22df7870c25ff82f1d625c6c2 Mon Sep 17 00:00:00 2001 From: Dinesh Karthikeyan Date: Wed, 26 Jun 2024 14:28:51 +0530 Subject: [PATCH 32/38] wifi: ath12k: Add support to enable debugfs_htt_stats Create debugfs_htt_stats file when ath12k debugfs support is enabled. Add basic ath12k_debugfs_htt_stats_register and handle htt_stats_type file operations. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-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: Dinesh Karthikeyan Co-developed-by: Ramya Gnanasekar Signed-off-by: Ramya Gnanasekar Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240626085854.2500681-2-quic_rgnanase@quicinc.com --- drivers/net/wireless/ath/ath12k/Makefile | 2 +- drivers/net/wireless/ath/ath12k/core.h | 7 ++ drivers/net/wireless/ath/ath12k/debugfs.c | 3 + .../wireless/ath/ath12k/debugfs_htt_stats.c | 81 +++++++++++++++++++ .../wireless/ath/ath12k/debugfs_htt_stats.h | 20 +++++ 5 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c create mode 100644 drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h diff --git a/drivers/net/wireless/ath/ath12k/Makefile b/drivers/net/wireless/ath/ath12k/Makefile index f0dcc933aa95..5a1ed20d730e 100644 --- a/drivers/net/wireless/ath/ath12k/Makefile +++ b/drivers/net/wireless/ath/ath12k/Makefile @@ -23,7 +23,7 @@ ath12k-y += core.o \ fw.o \ p2p.o -ath12k-$(CONFIG_ATH12K_DEBUGFS) += debugfs.o +ath12k-$(CONFIG_ATH12K_DEBUGFS) += debugfs.o debugfs_htt_stats.o ath12k-$(CONFIG_ACPI) += acpi.o ath12k-$(CONFIG_ATH12K_TRACING) += trace.o ath12k-$(CONFIG_PM) += wow.o diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 9de3df924a1c..7173c7e174a7 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -29,6 +29,7 @@ #include "fw.h" #include "acpi.h" #include "wow.h" +#include "debugfs_htt_stats.h" #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) @@ -481,9 +482,15 @@ struct ath12k_fw_stats { struct list_head bcn; }; +struct ath12k_dbg_htt_stats { + enum ath12k_dbg_htt_ext_stats_type type; + u32 cfg_param[4]; +}; + struct ath12k_debug { struct dentry *debugfs_pdev; struct dentry *debugfs_pdev_symlink; + struct ath12k_dbg_htt_stats htt_stats; }; struct ath12k_per_peer_tx_stats { diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index 751a9b04386a..2a977c36af00 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -6,6 +6,7 @@ #include "core.h" #include "debugfs.h" +#include "debugfs_htt_stats.h" static ssize_t ath12k_write_simulate_radar(struct file *file, const char __user *user_buf, @@ -89,6 +90,8 @@ void ath12k_debugfs_register(struct ath12k *ar) ar->debug.debugfs_pdev, ar, &fops_simulate_radar); } + + ath12k_debugfs_htt_stats_register(ar); } void ath12k_debugfs_unregister(struct ath12k *ar) diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c new file mode 100644 index 000000000000..d1d469ae6d2b --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include "core.h" +#include "debug.h" +#include "debugfs_htt_stats.h" + +static ssize_t ath12k_read_htt_stats_type(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + enum ath12k_dbg_htt_ext_stats_type type; + char buf[32]; + size_t len; + + mutex_lock(&ar->conf_mutex); + type = ar->debug.htt_stats.type; + mutex_unlock(&ar->conf_mutex); + + len = scnprintf(buf, sizeof(buf), "%u\n", type); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath12k_write_htt_stats_type(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + enum ath12k_dbg_htt_ext_stats_type type; + unsigned int cfg_param[4] = {0}; + const int size = 32; + int num_args; + + char *buf __free(kfree) = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + num_args = sscanf(buf, "%u %u %u %u %u\n", &type, &cfg_param[0], + &cfg_param[1], &cfg_param[2], &cfg_param[3]); + if (!num_args || num_args > 5) + return -EINVAL; + + if (type == ATH12K_DBG_HTT_EXT_STATS_RESET || + type >= ATH12K_DBG_HTT_NUM_EXT_STATS) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + ar->debug.htt_stats.type = type; + ar->debug.htt_stats.cfg_param[0] = cfg_param[0]; + ar->debug.htt_stats.cfg_param[1] = cfg_param[1]; + ar->debug.htt_stats.cfg_param[2] = cfg_param[2]; + ar->debug.htt_stats.cfg_param[3] = cfg_param[3]; + + mutex_unlock(&ar->conf_mutex); + + return count; +} + +static const struct file_operations fops_htt_stats_type = { + .read = ath12k_read_htt_stats_type, + .write = ath12k_write_htt_stats_type, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath12k_debugfs_htt_stats_register(struct ath12k *ar) +{ + debugfs_create_file("htt_stats_type", 0600, ar->debug.debugfs_pdev, + ar, &fops_htt_stats_type); +} diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h new file mode 100644 index 000000000000..8610db89289a --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef DEBUG_HTT_STATS_H +#define DEBUG_HTT_STATS_H + +void ath12k_debugfs_htt_stats_register(struct ath12k *ar); + +/* htt_dbg_ext_stats_type */ +enum ath12k_dbg_htt_ext_stats_type { + ATH12K_DBG_HTT_EXT_STATS_RESET = 0, + + /* keep this last */ + ATH12K_DBG_HTT_NUM_EXT_STATS, +}; + +#endif From ad201b35d3ec48d81da6c873b9a50586afd8defa Mon Sep 17 00:00:00 2001 From: Dinesh Karthikeyan Date: Thu, 27 Jun 2024 19:36:50 +0300 Subject: [PATCH 33/38] wifi: ath12k: Add htt_stats_dump file ops support Add dump_htt_stats file operation to dump the stats value requested for the requested stats_type. Stats sent from firmware will be cumulative. Hence add debugfs to reset the requested stats type. Example with one ath12k device: ath12k `-- pci-0000:06:00.0 |-- mac0 `-- htt_stats |-- htt_stats_type |-- htt_stats_reset Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-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: Dinesh Karthikeyan Co-developed-by: Ramya Gnanasekar Signed-off-by: Ramya Gnanasekar Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240626085854.2500681-3-quic_rgnanase@quicinc.com --- drivers/net/wireless/ath/ath12k/core.h | 2 + .../wireless/ath/ath12k/debugfs_htt_stats.c | 205 ++++++++++++++++++ .../wireless/ath/ath12k/debugfs_htt_stats.h | 30 +++ drivers/net/wireless/ath/ath12k/dp_tx.c | 4 +- 4 files changed, 240 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 7173c7e174a7..cdfd43a7321a 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -485,6 +485,8 @@ struct ath12k_fw_stats { struct ath12k_dbg_htt_stats { enum ath12k_dbg_htt_ext_stats_type type; u32 cfg_param[4]; + u8 reset; + struct debug_htt_stats_req *stats_req; }; struct ath12k_debug { diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index d1d469ae6d2b..9f44285caa58 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -8,6 +8,7 @@ #include "core.h" #include "debug.h" #include "debugfs_htt_stats.h" +#include "dp_tx.h" static ssize_t ath12k_read_htt_stats_type(struct file *file, char __user *user_buf, @@ -74,8 +75,212 @@ static const struct file_operations fops_htt_stats_type = { .llseek = default_llseek, }; +static int ath12k_debugfs_htt_stats_req(struct ath12k *ar) +{ + struct debug_htt_stats_req *stats_req = ar->debug.htt_stats.stats_req; + enum ath12k_dbg_htt_ext_stats_type type = stats_req->type; + u64 cookie; + int ret, pdev_id; + struct htt_ext_stats_cfg_params cfg_params = { 0 }; + + lockdep_assert_held(&ar->conf_mutex); + + init_completion(&stats_req->htt_stats_rcvd); + + pdev_id = ath12k_mac_get_target_pdev_id(ar); + stats_req->done = false; + stats_req->pdev_id = pdev_id; + + cookie = u64_encode_bits(ATH12K_HTT_STATS_MAGIC_VALUE, + ATH12K_HTT_STATS_COOKIE_MSB); + cookie |= u64_encode_bits(pdev_id, ATH12K_HTT_STATS_COOKIE_LSB); + + if (stats_req->override_cfg_param) { + cfg_params.cfg0 = stats_req->cfg_param[0]; + cfg_params.cfg1 = stats_req->cfg_param[1]; + cfg_params.cfg2 = stats_req->cfg_param[2]; + cfg_params.cfg3 = stats_req->cfg_param[3]; + } + + ret = ath12k_dp_tx_htt_h2t_ext_stats_req(ar, type, &cfg_params, cookie); + if (ret) { + ath12k_warn(ar->ab, "failed to send htt stats request: %d\n", ret); + return ret; + } + if (!wait_for_completion_timeout(&stats_req->htt_stats_rcvd, 3 * HZ)) { + spin_lock_bh(&ar->data_lock); + if (!stats_req->done) { + stats_req->done = true; + spin_unlock_bh(&ar->data_lock); + ath12k_warn(ar->ab, "stats request timed out\n"); + return -ETIMEDOUT; + } + spin_unlock_bh(&ar->data_lock); + } + + return 0; +} + +static int ath12k_open_htt_stats(struct inode *inode, + struct file *file) +{ + struct ath12k *ar = inode->i_private; + struct debug_htt_stats_req *stats_req; + enum ath12k_dbg_htt_ext_stats_type type = ar->debug.htt_stats.type; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + int ret; + + if (type == ATH12K_DBG_HTT_EXT_STATS_RESET) + return -EPERM; + + mutex_lock(&ar->conf_mutex); + + if (ah->state != ATH12K_HW_STATE_ON) { + ret = -ENETDOWN; + goto err_unlock; + } + + if (ar->debug.htt_stats.stats_req) { + ret = -EAGAIN; + goto err_unlock; + } + + stats_req = kzalloc(sizeof(*stats_req) + ATH12K_HTT_STATS_BUF_SIZE, GFP_KERNEL); + if (!stats_req) { + ret = -ENOMEM; + goto err_unlock; + } + + ar->debug.htt_stats.stats_req = stats_req; + stats_req->type = type; + stats_req->cfg_param[0] = ar->debug.htt_stats.cfg_param[0]; + stats_req->cfg_param[1] = ar->debug.htt_stats.cfg_param[1]; + stats_req->cfg_param[2] = ar->debug.htt_stats.cfg_param[2]; + stats_req->cfg_param[3] = ar->debug.htt_stats.cfg_param[3]; + stats_req->override_cfg_param = !!stats_req->cfg_param[0] || + !!stats_req->cfg_param[1] || + !!stats_req->cfg_param[2] || + !!stats_req->cfg_param[3]; + + ret = ath12k_debugfs_htt_stats_req(ar); + if (ret < 0) + goto out; + + file->private_data = stats_req; + + mutex_unlock(&ar->conf_mutex); + + return 0; +out: + kfree(stats_req); + ar->debug.htt_stats.stats_req = NULL; +err_unlock: + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static int ath12k_release_htt_stats(struct inode *inode, + struct file *file) +{ + struct ath12k *ar = inode->i_private; + + mutex_lock(&ar->conf_mutex); + kfree(file->private_data); + ar->debug.htt_stats.stats_req = NULL; + mutex_unlock(&ar->conf_mutex); + + return 0; +} + +static ssize_t ath12k_read_htt_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct debug_htt_stats_req *stats_req = file->private_data; + char *buf; + u32 length; + + buf = stats_req->buf; + length = min_t(u32, stats_req->buf_len, ATH12K_HTT_STATS_BUF_SIZE); + return simple_read_from_buffer(user_buf, count, ppos, buf, length); +} + +static const struct file_operations fops_dump_htt_stats = { + .open = ath12k_open_htt_stats, + .release = ath12k_release_htt_stats, + .read = ath12k_read_htt_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath12k_write_htt_stats_reset(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + enum ath12k_dbg_htt_ext_stats_type type; + struct htt_ext_stats_cfg_params cfg_params = { 0 }; + u8 param_pos; + int ret; + + ret = kstrtou32_from_user(user_buf, count, 0, &type); + if (ret) + return ret; + + if (type >= ATH12K_DBG_HTT_NUM_EXT_STATS || + type == ATH12K_DBG_HTT_EXT_STATS_RESET) + return -E2BIG; + + mutex_lock(&ar->conf_mutex); + cfg_params.cfg0 = HTT_STAT_DEFAULT_RESET_START_OFFSET; + param_pos = (type >> 5) + 1; + + switch (param_pos) { + case ATH12K_HTT_STATS_RESET_PARAM_CFG_32_BYTES: + cfg_params.cfg1 = 1 << (cfg_params.cfg0 + type); + break; + case ATH12K_HTT_STATS_RESET_PARAM_CFG_64_BYTES: + cfg_params.cfg2 = ATH12K_HTT_STATS_RESET_BITMAP32_BIT(cfg_params.cfg0 + + type); + break; + case ATH12K_HTT_STATS_RESET_PARAM_CFG_128_BYTES: + cfg_params.cfg3 = ATH12K_HTT_STATS_RESET_BITMAP64_BIT(cfg_params.cfg0 + + type); + break; + default: + break; + } + + ret = ath12k_dp_tx_htt_h2t_ext_stats_req(ar, + ATH12K_DBG_HTT_EXT_STATS_RESET, + &cfg_params, + 0ULL); + if (ret) { + ath12k_warn(ar->ab, "failed to send htt stats request: %d\n", ret); + mutex_unlock(&ar->conf_mutex); + return ret; + } + + ar->debug.htt_stats.reset = type; + mutex_unlock(&ar->conf_mutex); + + return count; +} + +static const struct file_operations fops_htt_stats_reset = { + .write = ath12k_write_htt_stats_reset, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + void ath12k_debugfs_htt_stats_register(struct ath12k *ar) { debugfs_create_file("htt_stats_type", 0600, ar->debug.debugfs_pdev, ar, &fops_htt_stats_type); + debugfs_create_file("htt_stats", 0400, ar->debug.debugfs_pdev, + ar, &fops_dump_htt_stats); + debugfs_create_file("htt_stats_reset", 0200, ar->debug.debugfs_pdev, + ar, &fops_htt_stats_reset); } diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index 8610db89289a..252c59f5eac8 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -7,6 +7,18 @@ #ifndef DEBUG_HTT_STATS_H #define DEBUG_HTT_STATS_H +#define ATH12K_HTT_STATS_BUF_SIZE (1024 * 512) +#define ATH12K_HTT_STATS_COOKIE_LSB GENMASK_ULL(31, 0) +#define ATH12K_HTT_STATS_COOKIE_MSB GENMASK_ULL(63, 32) +#define ATH12K_HTT_STATS_MAGIC_VALUE 0xF0F0F0F0 + +#define ATH12K_HTT_STATS_RESET_BITMAP32_OFFSET(_idx) ((_idx) & 0x1f) +#define ATH12K_HTT_STATS_RESET_BITMAP64_OFFSET(_idx) ((_idx) & 0x3f) +#define ATH12K_HTT_STATS_RESET_BITMAP32_BIT(_idx) (1 << \ + ATH12K_HTT_STATS_RESET_BITMAP32_OFFSET(_idx)) +#define ATH12K_HTT_STATS_RESET_BITMAP64_BIT(_idx) (1 << \ + ATH12K_HTT_STATS_RESET_BITMAP64_OFFSET(_idx)) + void ath12k_debugfs_htt_stats_register(struct ath12k *ar); /* htt_dbg_ext_stats_type */ @@ -17,4 +29,22 @@ enum ath12k_dbg_htt_ext_stats_type { ATH12K_DBG_HTT_NUM_EXT_STATS, }; +enum ath12k_htt_stats_reset_cfg_param_alloc_pos { + ATH12K_HTT_STATS_RESET_PARAM_CFG_32_BYTES = 1, + ATH12K_HTT_STATS_RESET_PARAM_CFG_64_BYTES, + ATH12K_HTT_STATS_RESET_PARAM_CFG_128_BYTES, +}; + +struct debug_htt_stats_req { + bool done; + bool override_cfg_param; + u8 pdev_id; + enum ath12k_dbg_htt_ext_stats_type type; + u32 cfg_param[4]; + u8 peer_addr[ETH_ALEN]; + struct completion htt_stats_rcvd; + u32 buf_len; + u8 buf[]; +}; + #endif diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index c4cfa7cf7cb9..d08c04343e90 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -1086,6 +1086,7 @@ ath12k_dp_tx_htt_h2t_ext_stats_req(struct ath12k *ar, u8 type, struct htt_ext_stats_cfg_cmd *cmd; int len = sizeof(*cmd); int ret; + u32 pdev_id; skb = ath12k_htc_alloc_skb(ab, len); if (!skb) @@ -1097,7 +1098,8 @@ ath12k_dp_tx_htt_h2t_ext_stats_req(struct ath12k *ar, u8 type, memset(cmd, 0, sizeof(*cmd)); cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_EXT_STATS_CFG; - cmd->hdr.pdev_mask = 1 << ar->pdev->pdev_id; + pdev_id = ath12k_mac_get_target_pdev_id(ar); + cmd->hdr.pdev_mask = 1 << pdev_id; cmd->hdr.stats_type = type; cmd->cfg_param0 = cpu_to_le32(cfg_params->cfg0); From adf6df963c0325c6f3848ade1b73d2afc4948351 Mon Sep 17 00:00:00 2001 From: Dinesh Karthikeyan Date: Thu, 27 Jun 2024 19:36:56 +0300 Subject: [PATCH 34/38] wifi: ath12k: Add support to parse requested stats_type Add extended htt stats parser and print the corresponding TLVs associated with the requested htt_stats_type. Add support for TX PDEV related htt stats. Sample output: -------------- echo 1 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats_type cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats HTT_TX_PDEV_STATS_CMN_TLV: mac_id = 0 comp_delivered = 0 self_triggers = 256 hw_queued = 275 hw_reaped = 275 underrun = 241 hw_paused = 0 hw_flush = 0 hw_filt = 1 tx_abort = 0 ppdu_ok = 246 mpdu_requeued = 0 tx_xretry = 0 data_rc = 3 mpdu_dropped_xretry = 0 illegal_rate_phy_err = 0 cont_xretry = 0 tx_timeout = 0 tx_time_dur_data = 0 pdev_resets = 0 phy_underrun = 0 txop_ovf = 0 seq_posted = 247 seq_failed_queueing = 0 seq_completed = 247 seq_restarted = 0 seq_txop_repost_stop = 0 next_seq_cancel = 0 dl_mu_mimo_seq_posted = 0 dl_mu_ofdma_seq_posted = 0 ul_mu_mimo_seq_posted = 0 ul_mu_ofdma_seq_posted = 0 mu_mimo_peer_blacklisted = 0 seq_qdepth_repost_stop = 0 seq_min_msdu_repost_stop = 0 mu_seq_min_msdu_repost_stop = 0 seq_switch_hw_paused = 0 next_seq_posted_dsr = 0 seq_posted_isr = 0 seq_ctrl_cached = 0 mpdu_count_tqm = 0 msdu_count_tqm = 0 mpdu_removed_tqm = 0 msdu_removed_tqm = 0 remove_mpdus_max_retries = 0 mpdus_sw_flush = 0 mpdus_hw_filter = 0 mpdus_truncated = 0 mpdus_ack_failed = 0 mpdus_expired = 0 mpdus_seq_hw_retry = 0 ack_tlv_proc = 0 coex_abort_mpdu_cnt_valid = 0 coex_abort_mpdu_cnt = 5 num_total_ppdus_tried_ota = 5 num_data_ppdus_tried_ota = 0 local_ctrl_mgmt_enqued = 247 local_ctrl_mgmt_freed = 247 local_data_enqued = 0 local_data_freed = 0 mpdu_tried = 0 isr_wait_seq_posted = 0 tx_active_dur_us_low = 0 tx_active_dur_us_high = 0 fes_offsets_err_cnt = 0 HTT_TX_PDEV_STATS_URRN_TLV: urrn_stats = 0:0, 1:241, 2:0 HTT_TX_PDEV_STATS_SIFS_TLV: sifs_status = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0 HTT_TX_PDEV_STATS_FLUSH_TLV: flush_errs = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0, 16:0, 17:0, 18:0, 19:0, 20:0, 21:0, 22:0, 23:0, 24:0, 25:0, 26:0, 27:0, 28:0, 29:0, 30:0, 31:0, 32:0, 33:0, 34:0, 35:0, 36:0, 37:0, 38:0, 39:0, 40:0, 41:0, 42:0, 43:0, 44:0, 45:0, 46:0, 47:0, 48:0, 49:0, 50:0, 51:0, 52:0, 53:0, 54:0, 55:0, 56:0, 57:0, 58:0, 59:0, 60:0, 61:0, 62:0, 63:0, 64:0, 65:0, 66:0, 67:0, 68:0, 69:0, 70:0, 71:0, 72:0, 73:0, 74:0, 75:0, 76:0, 77:0, 78:0, 79:0, 80:0, 81:0, 82:0, 83:0, 84:0, 85:0, 86:0, 87:0, 88:0, 89:0, 90:0, 91:0, 92:0, 93:0, 94:0, 95:0, 96:0, 97:0, 98:0, 99:0, 100:0, 101:0, 102:0, 103:0, 104:0, 105:0, 106:0, 107:0, 108:0, 109:0, 110:0, 111:0, 112:0, 113:0, 114:0, 115:0, 116:0, 117:0, 118:0, 119:0, 120:0, 121:0, 122:0, 123:0, 124:0, 125:0, 126:0, 127:0 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-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: Dinesh Karthikeyan Co-developed-by: Ramya Gnanasekar Signed-off-by: Ramya Gnanasekar Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240626085854.2500681-4-quic_rgnanase@quicinc.com --- .../wireless/ath/ath12k/debugfs_htt_stats.c | 328 ++++++++++++++++++ .../wireless/ath/ath12k/debugfs_htt_stats.h | 211 +++++++++++ drivers/net/wireless/ath/ath12k/dp_rx.c | 10 +- drivers/net/wireless/ath/ath12k/dp_rx.h | 4 + 4 files changed, 549 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index 9f44285caa58..0c1d8644c1f6 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -9,6 +9,334 @@ #include "debug.h" #include "debugfs_htt_stats.h" #include "dp_tx.h" +#include "dp_rx.h" + +static u32 +print_array_to_buf(u8 *buf, u32 offset, const char *header, + const __le32 *array, u32 array_len, const char *footer) +{ + int index = 0; + u8 i; + + if (header) { + index += scnprintf(buf + offset, + ATH12K_HTT_STATS_BUF_SIZE - offset, + "%s = ", header); + } + for (i = 0; i < array_len; i++) { + index += scnprintf(buf + offset + index, + (ATH12K_HTT_STATS_BUF_SIZE - offset) - index, + " %u:%u,", i, le32_to_cpu(array[i])); + } + /* To overwrite the last trailing comma */ + index--; + *(buf + offset + index) = '\0'; + + if (footer) { + index += scnprintf(buf + offset + index, + (ATH12K_HTT_STATS_BUF_SIZE - offset) - index, + "%s", footer); + } + return index; +} + +static void +htt_print_tx_pdev_stats_cmn_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_stats_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_CMN_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "comp_delivered = %u\n", + le32_to_cpu(htt_stats_buf->comp_delivered)); + len += scnprintf(buf + len, buf_len - len, "self_triggers = %u\n", + le32_to_cpu(htt_stats_buf->self_triggers)); + len += scnprintf(buf + len, buf_len - len, "hw_queued = %u\n", + le32_to_cpu(htt_stats_buf->hw_queued)); + len += scnprintf(buf + len, buf_len - len, "hw_reaped = %u\n", + le32_to_cpu(htt_stats_buf->hw_reaped)); + len += scnprintf(buf + len, buf_len - len, "underrun = %u\n", + le32_to_cpu(htt_stats_buf->underrun)); + len += scnprintf(buf + len, buf_len - len, "hw_paused = %u\n", + le32_to_cpu(htt_stats_buf->hw_paused)); + len += scnprintf(buf + len, buf_len - len, "hw_flush = %u\n", + le32_to_cpu(htt_stats_buf->hw_flush)); + len += scnprintf(buf + len, buf_len - len, "hw_filt = %u\n", + le32_to_cpu(htt_stats_buf->hw_filt)); + len += scnprintf(buf + len, buf_len - len, "tx_abort = %u\n", + le32_to_cpu(htt_stats_buf->tx_abort)); + len += scnprintf(buf + len, buf_len - len, "ppdu_ok = %u\n", + le32_to_cpu(htt_stats_buf->ppdu_ok)); + len += scnprintf(buf + len, buf_len - len, "mpdu_requeued = %u\n", + le32_to_cpu(htt_stats_buf->mpdu_requed)); + len += scnprintf(buf + len, buf_len - len, "tx_xretry = %u\n", + le32_to_cpu(htt_stats_buf->tx_xretry)); + len += scnprintf(buf + len, buf_len - len, "data_rc = %u\n", + le32_to_cpu(htt_stats_buf->data_rc)); + len += scnprintf(buf + len, buf_len - len, "mpdu_dropped_xretry = %u\n", + le32_to_cpu(htt_stats_buf->mpdu_dropped_xretry)); + len += scnprintf(buf + len, buf_len - len, "illegal_rate_phy_err = %u\n", + le32_to_cpu(htt_stats_buf->illgl_rate_phy_err)); + len += scnprintf(buf + len, buf_len - len, "cont_xretry = %u\n", + le32_to_cpu(htt_stats_buf->cont_xretry)); + len += scnprintf(buf + len, buf_len - len, "tx_timeout = %u\n", + le32_to_cpu(htt_stats_buf->tx_timeout)); + len += scnprintf(buf + len, buf_len - len, "tx_time_dur_data = %u\n", + le32_to_cpu(htt_stats_buf->tx_time_dur_data)); + len += scnprintf(buf + len, buf_len - len, "pdev_resets = %u\n", + le32_to_cpu(htt_stats_buf->pdev_resets)); + len += scnprintf(buf + len, buf_len - len, "phy_underrun = %u\n", + le32_to_cpu(htt_stats_buf->phy_underrun)); + len += scnprintf(buf + len, buf_len - len, "txop_ovf = %u\n", + le32_to_cpu(htt_stats_buf->txop_ovf)); + len += scnprintf(buf + len, buf_len - len, "seq_posted = %u\n", + le32_to_cpu(htt_stats_buf->seq_posted)); + len += scnprintf(buf + len, buf_len - len, "seq_failed_queueing = %u\n", + le32_to_cpu(htt_stats_buf->seq_failed_queueing)); + len += scnprintf(buf + len, buf_len - len, "seq_completed = %u\n", + le32_to_cpu(htt_stats_buf->seq_completed)); + len += scnprintf(buf + len, buf_len - len, "seq_restarted = %u\n", + le32_to_cpu(htt_stats_buf->seq_restarted)); + len += scnprintf(buf + len, buf_len - len, "seq_txop_repost_stop = %u\n", + le32_to_cpu(htt_stats_buf->seq_txop_repost_stop)); + len += scnprintf(buf + len, buf_len - len, "next_seq_cancel = %u\n", + le32_to_cpu(htt_stats_buf->next_seq_cancel)); + len += scnprintf(buf + len, buf_len - len, "dl_mu_mimo_seq_posted = %u\n", + le32_to_cpu(htt_stats_buf->mu_seq_posted)); + len += scnprintf(buf + len, buf_len - len, "dl_mu_ofdma_seq_posted = %u\n", + le32_to_cpu(htt_stats_buf->mu_ofdma_seq_posted)); + len += scnprintf(buf + len, buf_len - len, "ul_mu_mimo_seq_posted = %u\n", + le32_to_cpu(htt_stats_buf->ul_mumimo_seq_posted)); + len += scnprintf(buf + len, buf_len - len, "ul_mu_ofdma_seq_posted = %u\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_seq_posted)); + len += scnprintf(buf + len, buf_len - len, "mu_mimo_peer_blacklisted = %u\n", + le32_to_cpu(htt_stats_buf->num_mu_peer_blacklisted)); + len += scnprintf(buf + len, buf_len - len, "seq_qdepth_repost_stop = %u\n", + le32_to_cpu(htt_stats_buf->seq_qdepth_repost_stop)); + len += scnprintf(buf + len, buf_len - len, "seq_min_msdu_repost_stop = %u\n", + le32_to_cpu(htt_stats_buf->seq_min_msdu_repost_stop)); + len += scnprintf(buf + len, buf_len - len, "mu_seq_min_msdu_repost_stop = %u\n", + le32_to_cpu(htt_stats_buf->mu_seq_min_msdu_repost_stop)); + len += scnprintf(buf + len, buf_len - len, "seq_switch_hw_paused = %u\n", + le32_to_cpu(htt_stats_buf->seq_switch_hw_paused)); + len += scnprintf(buf + len, buf_len - len, "next_seq_posted_dsr = %u\n", + le32_to_cpu(htt_stats_buf->next_seq_posted_dsr)); + len += scnprintf(buf + len, buf_len - len, "seq_posted_isr = %u\n", + le32_to_cpu(htt_stats_buf->seq_posted_isr)); + len += scnprintf(buf + len, buf_len - len, "seq_ctrl_cached = %u\n", + le32_to_cpu(htt_stats_buf->seq_ctrl_cached)); + len += scnprintf(buf + len, buf_len - len, "mpdu_count_tqm = %u\n", + le32_to_cpu(htt_stats_buf->mpdu_count_tqm)); + len += scnprintf(buf + len, buf_len - len, "msdu_count_tqm = %u\n", + le32_to_cpu(htt_stats_buf->msdu_count_tqm)); + len += scnprintf(buf + len, buf_len - len, "mpdu_removed_tqm = %u\n", + le32_to_cpu(htt_stats_buf->mpdu_removed_tqm)); + len += scnprintf(buf + len, buf_len - len, "msdu_removed_tqm = %u\n", + le32_to_cpu(htt_stats_buf->msdu_removed_tqm)); + len += scnprintf(buf + len, buf_len - len, "remove_mpdus_max_retries = %u\n", + le32_to_cpu(htt_stats_buf->remove_mpdus_max_retries)); + len += scnprintf(buf + len, buf_len - len, "mpdus_sw_flush = %u\n", + le32_to_cpu(htt_stats_buf->mpdus_sw_flush)); + len += scnprintf(buf + len, buf_len - len, "mpdus_hw_filter = %u\n", + le32_to_cpu(htt_stats_buf->mpdus_hw_filter)); + len += scnprintf(buf + len, buf_len - len, "mpdus_truncated = %u\n", + le32_to_cpu(htt_stats_buf->mpdus_truncated)); + len += scnprintf(buf + len, buf_len - len, "mpdus_ack_failed = %u\n", + le32_to_cpu(htt_stats_buf->mpdus_ack_failed)); + len += scnprintf(buf + len, buf_len - len, "mpdus_expired = %u\n", + le32_to_cpu(htt_stats_buf->mpdus_expired)); + len += scnprintf(buf + len, buf_len - len, "mpdus_seq_hw_retry = %u\n", + le32_to_cpu(htt_stats_buf->mpdus_seq_hw_retry)); + len += scnprintf(buf + len, buf_len - len, "ack_tlv_proc = %u\n", + le32_to_cpu(htt_stats_buf->ack_tlv_proc)); + len += scnprintf(buf + len, buf_len - len, "coex_abort_mpdu_cnt_valid = %u\n", + le32_to_cpu(htt_stats_buf->coex_abort_mpdu_cnt_valid)); + len += scnprintf(buf + len, buf_len - len, "coex_abort_mpdu_cnt = %u\n", + le32_to_cpu(htt_stats_buf->coex_abort_mpdu_cnt)); + len += scnprintf(buf + len, buf_len - len, "num_total_ppdus_tried_ota = %u\n", + le32_to_cpu(htt_stats_buf->num_total_ppdus_tried_ota)); + len += scnprintf(buf + len, buf_len - len, "num_data_ppdus_tried_ota = %u\n", + le32_to_cpu(htt_stats_buf->num_data_ppdus_tried_ota)); + len += scnprintf(buf + len, buf_len - len, "local_ctrl_mgmt_enqued = %u\n", + le32_to_cpu(htt_stats_buf->local_ctrl_mgmt_enqued)); + len += scnprintf(buf + len, buf_len - len, "local_ctrl_mgmt_freed = %u\n", + le32_to_cpu(htt_stats_buf->local_ctrl_mgmt_freed)); + len += scnprintf(buf + len, buf_len - len, "local_data_enqued = %u\n", + le32_to_cpu(htt_stats_buf->local_data_enqued)); + len += scnprintf(buf + len, buf_len - len, "local_data_freed = %u\n", + le32_to_cpu(htt_stats_buf->local_data_freed)); + len += scnprintf(buf + len, buf_len - len, "mpdu_tried = %u\n", + le32_to_cpu(htt_stats_buf->mpdu_tried)); + len += scnprintf(buf + len, buf_len - len, "isr_wait_seq_posted = %u\n", + le32_to_cpu(htt_stats_buf->isr_wait_seq_posted)); + len += scnprintf(buf + len, buf_len - len, "tx_active_dur_us_low = %u\n", + le32_to_cpu(htt_stats_buf->tx_active_dur_us_low)); + len += scnprintf(buf + len, buf_len - len, "tx_active_dur_us_high = %u\n", + le32_to_cpu(htt_stats_buf->tx_active_dur_us_high)); + len += scnprintf(buf + len, buf_len - len, "fes_offsets_err_cnt = %u\n\n", + le32_to_cpu(htt_stats_buf->fes_offsets_err_cnt)); + + stats_req->buf_len = len; +} + +static void +htt_print_tx_pdev_stats_urrn_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_stats_urrn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elems = min_t(u16, (tag_len >> 2), + HTT_TX_PDEV_MAX_URRN_STATS); + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_URRN_TLV:\n"); + + len += print_array_to_buf(buf, len, "urrn_stats", htt_stats_buf->urrn_stats, + num_elems, "\n\n"); + + stats_req->buf_len = len; +} + +static void +htt_print_tx_pdev_stats_flush_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_stats_flush_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elems = min_t(u16, (tag_len >> 2), + ATH12K_HTT_TX_PDEV_MAX_FLUSH_REASON_STATS); + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_FLUSH_TLV:\n"); + + len += print_array_to_buf(buf, len, "flush_errs", htt_stats_buf->flush_errs, + num_elems, "\n\n"); + + stats_req->buf_len = len; +} + +static void +htt_print_tx_pdev_stats_sifs_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_stats_sifs_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elems = min_t(u16, (tag_len >> 2), + ATH12K_HTT_TX_PDEV_MAX_SIFS_BURST_STATS); + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_SIFS_TLV:\n"); + + len += print_array_to_buf(buf, len, "sifs_status", htt_stats_buf->sifs_status, + num_elems, "\n\n"); + + stats_req->buf_len = len; +} + +static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, + u16 tag, u16 len, const void *tag_buf, + void *user_data) +{ + struct debug_htt_stats_req *stats_req = user_data; + + switch (tag) { + case HTT_STATS_TX_PDEV_CMN_TAG: + htt_print_tx_pdev_stats_cmn_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_UNDERRUN_TAG: + htt_print_tx_pdev_stats_urrn_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_SIFS_TAG: + htt_print_tx_pdev_stats_sifs_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_FLUSH_TAG: + htt_print_tx_pdev_stats_flush_tlv(tag_buf, len, stats_req); + break; + default: + break; + } + + return 0; +} + +void ath12k_debugfs_htt_ext_stats_handler(struct ath12k_base *ab, + struct sk_buff *skb) +{ + struct ath12k_htt_extd_stats_msg *msg; + struct debug_htt_stats_req *stats_req; + struct ath12k *ar; + u32 len, pdev_id, stats_info; + u64 cookie; + int ret; + bool send_completion = false; + + msg = (struct ath12k_htt_extd_stats_msg *)skb->data; + cookie = le64_to_cpu(msg->cookie); + + if (u64_get_bits(cookie, ATH12K_HTT_STATS_COOKIE_MSB) != + ATH12K_HTT_STATS_MAGIC_VALUE) { + ath12k_warn(ab, "received invalid htt ext stats event\n"); + return; + } + + pdev_id = u64_get_bits(cookie, ATH12K_HTT_STATS_COOKIE_LSB); + rcu_read_lock(); + ar = ath12k_mac_get_ar_by_pdev_id(ab, pdev_id); + if (!ar) { + ath12k_warn(ab, "failed to get ar for pdev_id %d\n", pdev_id); + goto exit; + } + + stats_req = ar->debug.htt_stats.stats_req; + if (!stats_req) + goto exit; + + spin_lock_bh(&ar->data_lock); + + stats_info = le32_to_cpu(msg->info1); + stats_req->done = u32_get_bits(stats_info, ATH12K_HTT_T2H_EXT_STATS_INFO1_DONE); + if (stats_req->done) + send_completion = true; + + spin_unlock_bh(&ar->data_lock); + + len = u32_get_bits(stats_info, ATH12K_HTT_T2H_EXT_STATS_INFO1_LENGTH); + if (len > skb->len) { + ath12k_warn(ab, "invalid length %d for HTT stats", len); + goto exit; + } + + ret = ath12k_dp_htt_tlv_iter(ab, msg->data, len, + ath12k_dbg_htt_ext_stats_parse, + stats_req); + if (ret) + ath12k_warn(ab, "Failed to parse tlv %d\n", ret); + + if (send_completion) + complete(&stats_req->htt_stats_rcvd); +exit: + rcu_read_unlock(); +} static ssize_t ath12k_read_htt_stats_type(struct file *file, char __user *user_buf, diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index 252c59f5eac8..83060c54d00b 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -21,14 +21,134 @@ void ath12k_debugfs_htt_stats_register(struct ath12k *ar); +#ifdef CONFIG_ATH12K_DEBUGFS +void ath12k_debugfs_htt_ext_stats_handler(struct ath12k_base *ab, + struct sk_buff *skb); +#else /* CONFIG_ATH12K_DEBUGFS */ +static inline void ath12k_debugfs_htt_ext_stats_handler(struct ath12k_base *ab, + struct sk_buff *skb) +{ +} +#endif + +/** + * DOC: target -> host extended statistics upload + * + * The following field definitions describe the format of the HTT + * target to host stats upload confirmation message. + * The message contains a cookie echoed from the HTT host->target stats + * upload request, which identifies which request the confirmation is + * for, and a single stats can span over multiple HTT stats indication + * due to the HTT message size limitation so every HTT ext stats + * indication will have tag-length-value stats information elements. + * The tag-length header for each HTT stats IND message also includes a + * status field, to indicate whether the request for the stat type in + * question was fully met, partially met, unable to be met, or invalid + * (if the stat type in question is disabled in the target). + * A Done bit 1's indicate the end of the of stats info elements. + * + * + * |31 16|15 12|11|10 8|7 5|4 0| + * |--------------------------------------------------------------| + * | reserved | msg type | + * |--------------------------------------------------------------| + * | cookie LSBs | + * |--------------------------------------------------------------| + * | cookie MSBs | + * |--------------------------------------------------------------| + * | stats entry length | rsvd | D| S | stat type | + * |--------------------------------------------------------------| + * | type-specific stats info | + * | (see debugfs_htt_stats.h) | + * |--------------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: Identifies this is a extended statistics upload confirmation + * message. + * Value: 0x1c + * - COOKIE_LSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: MSBs of the opaque cookie specified by the host-side requestor + * - COOKIE_MSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: MSBs of the opaque cookie specified by the host-side requestor + * + * Stats Information Element tag-length header fields: + * - STAT_TYPE + * Bits 7:0 + * Purpose: identifies the type of statistics info held in the + * following information element + * Value: ath12k_dbg_htt_ext_stats_type + * - STATUS + * Bits 10:8 + * Purpose: indicate whether the requested stats are present + * Value: + * 0 -> The requested stats have been delivered in full + * 1 -> The requested stats have been delivered in part + * 2 -> The requested stats could not be delivered (error case) + * 3 -> The requested stat type is either not recognized (invalid) + * - DONE + * Bits 11 + * Purpose: + * Indicates the completion of the stats entry, this will be the last + * stats conf HTT segment for the requested stats type. + * Value: + * 0 -> the stats retrieval is ongoing + * 1 -> the stats retrieval is complete + * - LENGTH + * Bits 31:16 + * Purpose: indicate the stats information size + * Value: This field specifies the number of bytes of stats information + * that follows the element tag-length header. + * It is expected but not required that this length is a multiple of + * 4 bytes. + */ + +#define ATH12K_HTT_T2H_EXT_STATS_INFO1_DONE BIT(11) +#define ATH12K_HTT_T2H_EXT_STATS_INFO1_LENGTH GENMASK(31, 16) + +struct ath12k_htt_extd_stats_msg { + __le32 info0; + __le64 cookie; + __le32 info1; + u8 data[]; +} __packed; + /* htt_dbg_ext_stats_type */ enum ath12k_dbg_htt_ext_stats_type { ATH12K_DBG_HTT_EXT_STATS_RESET = 0, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX = 1, /* keep this last */ ATH12K_DBG_HTT_NUM_EXT_STATS, }; +enum ath12k_dbg_htt_tlv_tag { + HTT_STATS_TX_PDEV_CMN_TAG = 0, + HTT_STATS_TX_PDEV_UNDERRUN_TAG = 1, + HTT_STATS_TX_PDEV_SIFS_TAG = 2, + HTT_STATS_TX_PDEV_FLUSH_TAG = 3, + + HTT_STATS_MAX_TAG, +}; + +#define ATH12K_HTT_STATS_MAC_ID GENMASK(7, 0) + +#define ATH12K_HTT_TX_PDEV_MAX_SIFS_BURST_STATS 9 +#define ATH12K_HTT_TX_PDEV_MAX_FLUSH_REASON_STATS 150 + +enum ath12k_htt_tx_pdev_underrun_enum { + HTT_STATS_TX_PDEV_NO_DATA_UNDERRUN = 0, + HTT_STATS_TX_PDEV_DATA_UNDERRUN_BETWEEN_MPDU = 1, + HTT_STATS_TX_PDEV_DATA_UNDERRUN_WITHIN_MPDU = 2, + HTT_TX_PDEV_MAX_URRN_STATS = 3, +}; + enum ath12k_htt_stats_reset_cfg_param_alloc_pos { ATH12K_HTT_STATS_RESET_PARAM_CFG_32_BYTES = 1, ATH12K_HTT_STATS_RESET_PARAM_CFG_64_BYTES, @@ -47,4 +167,95 @@ struct debug_htt_stats_req { u8 buf[]; }; +struct ath12k_htt_tx_pdev_stats_cmn_tlv { + __le32 mac_id__word; + __le32 hw_queued; + __le32 hw_reaped; + __le32 underrun; + __le32 hw_paused; + __le32 hw_flush; + __le32 hw_filt; + __le32 tx_abort; + __le32 mpdu_requed; + __le32 tx_xretry; + __le32 data_rc; + __le32 mpdu_dropped_xretry; + __le32 illgl_rate_phy_err; + __le32 cont_xretry; + __le32 tx_timeout; + __le32 pdev_resets; + __le32 phy_underrun; + __le32 txop_ovf; + __le32 seq_posted; + __le32 seq_failed_queueing; + __le32 seq_completed; + __le32 seq_restarted; + __le32 mu_seq_posted; + __le32 seq_switch_hw_paused; + __le32 next_seq_posted_dsr; + __le32 seq_posted_isr; + __le32 seq_ctrl_cached; + __le32 mpdu_count_tqm; + __le32 msdu_count_tqm; + __le32 mpdu_removed_tqm; + __le32 msdu_removed_tqm; + __le32 mpdus_sw_flush; + __le32 mpdus_hw_filter; + __le32 mpdus_truncated; + __le32 mpdus_ack_failed; + __le32 mpdus_expired; + __le32 mpdus_seq_hw_retry; + __le32 ack_tlv_proc; + __le32 coex_abort_mpdu_cnt_valid; + __le32 coex_abort_mpdu_cnt; + __le32 num_total_ppdus_tried_ota; + __le32 num_data_ppdus_tried_ota; + __le32 local_ctrl_mgmt_enqued; + __le32 local_ctrl_mgmt_freed; + __le32 local_data_enqued; + __le32 local_data_freed; + __le32 mpdu_tried; + __le32 isr_wait_seq_posted; + + __le32 tx_active_dur_us_low; + __le32 tx_active_dur_us_high; + __le32 remove_mpdus_max_retries; + __le32 comp_delivered; + __le32 ppdu_ok; + __le32 self_triggers; + __le32 tx_time_dur_data; + __le32 seq_qdepth_repost_stop; + __le32 mu_seq_min_msdu_repost_stop; + __le32 seq_min_msdu_repost_stop; + __le32 seq_txop_repost_stop; + __le32 next_seq_cancel; + __le32 fes_offsets_err_cnt; + __le32 num_mu_peer_blacklisted; + __le32 mu_ofdma_seq_posted; + __le32 ul_mumimo_seq_posted; + __le32 ul_ofdma_seq_posted; + + __le32 thermal_suspend_cnt; + __le32 dfs_suspend_cnt; + __le32 tx_abort_suspend_cnt; + __le32 tgt_specific_opaque_txq_suspend_info; + __le32 last_suspend_reason; +} __packed; + +struct ath12k_htt_tx_pdev_stats_urrn_tlv { + DECLARE_FLEX_ARRAY(__le32, urrn_stats); +} __packed; + +struct ath12k_htt_tx_pdev_stats_flush_tlv { + DECLARE_FLEX_ARRAY(__le32, flush_errs); +} __packed; + +struct ath12k_htt_tx_pdev_stats_phy_err_tlv { + DECLARE_FLEX_ARRAY(__le32, phy_errs); +} __packed; + +struct ath12k_htt_tx_pdev_stats_sifs_tlv { + DECLARE_FLEX_ARRAY(__le32, sifs_status); +} __packed; + #endif diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index d7bb412ad391..14236d0a0c89 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -17,6 +17,7 @@ #include "dp_tx.h" #include "peer.h" #include "dp_mon.h" +#include "debugfs_htt_stats.h" #define ATH12K_DP_RX_FRAGMENT_TIMEOUT_MS (2 * HZ) @@ -1268,10 +1269,10 @@ static int ath12k_htt_tlv_ppdu_stats_parse(struct ath12k_base *ab, return 0; } -static int ath12k_dp_htt_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len, - int (*iter)(struct ath12k_base *ar, u16 tag, u16 len, - const void *ptr, void *data), - void *data) +int ath12k_dp_htt_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len, + int (*iter)(struct ath12k_base *ar, u16 tag, u16 len, + const void *ptr, void *data), + void *data) { const struct htt_tlv *tlv; const void *begin = ptr; @@ -1741,6 +1742,7 @@ void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab, ath12k_htt_pull_ppdu_stats(ab, skb); break; case HTT_T2H_MSG_TYPE_EXT_STATS_CONF: + ath12k_debugfs_htt_ext_stats_handler(ab, skb); break; case HTT_T2H_MSG_TYPE_MLO_TIMESTAMP_OFFSET_IND: ath12k_htt_mlo_offset_event_handler(ab, skb); diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h index 2ff421160181..eb1f92559179 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/dp_rx.h @@ -139,4 +139,8 @@ ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu); int ath12k_dp_rxdma_ring_sel_config_qcn9274(struct ath12k_base *ab); int ath12k_dp_rxdma_ring_sel_config_wcn7850(struct ath12k_base *ab); +int ath12k_dp_htt_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len, + int (*iter)(struct ath12k_base *ar, u16 tag, u16 len, + const void *ptr, void *data), + void *data); #endif /* ATH12K_DP_RX_H */ From a9f2976f0e9ebc4a579cdd507b153a191692a72c Mon Sep 17 00:00:00 2001 From: Ramya Gnanasekar Date: Thu, 27 Jun 2024 19:37:02 +0300 Subject: [PATCH 35/38] wifi: ath12k: Dump additional Tx PDEV HTT stats Support to dump additional Tx PDEV stats through HTT stats debugfs. Following stats dump are supported: 1. PDEV control path stat to dump Tx management frame count 2. Tx PDEV SIFS histogram stats 3. Tx MU MIMO PPDU stats for 802.11ac, 802.11ax and 802.11be Sample Output: --------------- echo 1 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats_type cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats HTT_TX_PDEV_STATS_CMN_TLV: mac_id = 0 comp_delivered = 0 self_triggers = 13 ...... ...... HTT_TX_PDEV_STATS_CTRL_PATH_TX_STATS: fw_tx_mgmt_subtype = 0:1, 1:0, 2:0, 3:0, 4:38, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:1, 12:0, 13:7, 14:0, 15:0 HTT_TX_PDEV_STATS_SIFS_HIST_TLV: sifs_hist_status = 0:237, 1:185, 2:1, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0 HTT_TX_PDEV_AC_MU_PPDU_DISTRIBUTION_STATS: ac_mu_mimo_num_seq_posted_nr4 = 0 ac_mu_mimo_num_ppdu_posted_per_burst_nr4 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0 ac_mu_mimo_num_ppdu_completed_per_burst_nr4 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0 ac_mu_mimo_num_seq_term_status_nr4 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0 ac_mu_mimo_num_seq_posted_nr8 = 0 ac_mu_mimo_num_ppdu_posted_per_burst_nr8 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0 ac_mu_mimo_num_ppdu_completed_per_burst_nr8 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0 ac_mu_mimo_num_seq_term_status_nr8 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0 HTT_TX_PDEV_AX_MU_PPDU_DISTRIBUTION_STATS: ax_mu_mimo_num_seq_posted_nr4 = 0 ax_mu_mimo_num_ppdu_posted_per_burst_nr4 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0 ax_mu_mimo_num_ppdu_completed_per_burst_nr4 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0 ax_mu_mimo_num_seq_term_status_nr4 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0 ax_mu_mimo_num_seq_posted_nr8 = 0 ax_mu_mimo_num_ppdu_posted_per_burst_nr8 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0 ax_mu_mimo_num_ppdu_completed_per_burst_nr8 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0 ax_mu_mimo_num_seq_term_status_nr8 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0 HTT_TX_PDEV_BE_MU_PPDU_DISTRIBUTION_STATS: be_mu_mimo_num_seq_posted_nr4 = 0 be_mu_mimo_num_ppdu_posted_per_burst_nr4 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0 be_mu_mimo_num_ppdu_completed_per_burst_nr4 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0 be_mu_mimo_num_seq_term_status_nr4 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0 be_mu_mimo_num_seq_posted_nr8 = 0 be_mu_mimo_num_ppdu_posted_per_burst_nr8 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0 be_mu_mimo_num_ppdu_completed_per_burst_nr8 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0 be_mu_mimo_num_seq_term_status_nr8 = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-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: Ramya Gnanasekar Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240626085854.2500681-5-quic_rgnanase@quicinc.com --- .../wireless/ath/ath12k/debugfs_htt_stats.c | 142 ++++++++++++++++++ .../wireless/ath/ath12k/debugfs_htt_stats.h | 39 +++++ 2 files changed, 181 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index 0c1d8644c1f6..fd04f057598d 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -253,6 +253,139 @@ htt_print_tx_pdev_stats_sifs_tlv(const void *tag_buf, stats_req->buf_len = len; } +static void +htt_print_tx_pdev_mu_ppdu_dist_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_mu_ppdu_dist_stats_tlv *htt_stats_buf = tag_buf; + char *mode; + u8 j, hw_mode, i, str_buf_len; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 stats_value; + u8 max_ppdu = ATH12K_HTT_STATS_MAX_NUM_MU_PPDU_PER_BURST; + u8 max_sched = ATH12K_HTT_STATS_MAX_NUM_SCHED_STATUS; + char str_buf[ATH12K_HTT_MAX_STRING_LEN]; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + hw_mode = le32_to_cpu(htt_stats_buf->hw_mode); + + switch (hw_mode) { + case ATH12K_HTT_STATS_HWMODE_AC: + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_AC_MU_PPDU_DISTRIBUTION_STATS:\n"); + mode = "ac"; + break; + case ATH12K_HTT_STATS_HWMODE_AX: + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_AX_MU_PPDU_DISTRIBUTION_STATS:\n"); + mode = "ax"; + break; + case ATH12K_HTT_STATS_HWMODE_BE: + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_BE_MU_PPDU_DISTRIBUTION_STATS:\n"); + mode = "be"; + break; + default: + return; + } + + for (i = 0; i < ATH12K_HTT_STATS_NUM_NR_BINS ; i++) { + len += scnprintf(buf + len, buf_len - len, + "%s_mu_mimo_num_seq_posted_nr%u = %u\n", mode, + ((i + 1) * 4), htt_stats_buf->num_seq_posted[i]); + str_buf_len = 0; + memset(str_buf, 0x0, sizeof(str_buf)); + for (j = 0; j < ATH12K_HTT_STATS_MAX_NUM_MU_PPDU_PER_BURST ; j++) { + stats_value = le32_to_cpu(htt_stats_buf->num_ppdu_posted_per_burst + [i * max_ppdu + j]); + str_buf_len += scnprintf(&str_buf[str_buf_len], + ATH12K_HTT_MAX_STRING_LEN - str_buf_len, + " %u:%u,", j, stats_value); + } + /* To overwrite the last trailing comma */ + str_buf[str_buf_len - 1] = '\0'; + len += scnprintf(buf + len, buf_len - len, + "%s_mu_mimo_num_ppdu_posted_per_burst_nr%u = %s\n", + mode, ((i + 1) * 4), str_buf); + str_buf_len = 0; + memset(str_buf, 0x0, sizeof(str_buf)); + for (j = 0; j < ATH12K_HTT_STATS_MAX_NUM_MU_PPDU_PER_BURST ; j++) { + stats_value = le32_to_cpu(htt_stats_buf->num_ppdu_cmpl_per_burst + [i * max_ppdu + j]); + str_buf_len += scnprintf(&str_buf[str_buf_len], + ATH12K_HTT_MAX_STRING_LEN - str_buf_len, + " %u:%u,", j, stats_value); + } + /* To overwrite the last trailing comma */ + str_buf[str_buf_len - 1] = '\0'; + len += scnprintf(buf + len, buf_len - len, + "%s_mu_mimo_num_ppdu_completed_per_burst_nr%u = %s\n", + mode, ((i + 1) * 4), str_buf); + str_buf_len = 0; + memset(str_buf, 0x0, sizeof(str_buf)); + for (j = 0; j < ATH12K_HTT_STATS_MAX_NUM_SCHED_STATUS ; j++) { + stats_value = le32_to_cpu(htt_stats_buf->num_seq_term_status + [i * max_sched + j]); + str_buf_len += scnprintf(&str_buf[str_buf_len], + ATH12K_HTT_MAX_STRING_LEN - str_buf_len, + " %u:%u,", j, stats_value); + } + /* To overwrite the last trailing comma */ + str_buf[str_buf_len - 1] = '\0'; + len += scnprintf(buf + len, buf_len - len, + "%s_mu_mimo_num_seq_term_status_nr%u = %s\n\n", + mode, ((i + 1) * 4), str_buf); + } + + stats_req->buf_len = len; +} + +static void +htt_print_tx_pdev_stats_sifs_hist_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_stats_sifs_hist_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elems = min_t(u16, (tag_len >> 2), + ATH12K_HTT_TX_PDEV_MAX_SIFS_BURST_HIST_STATS); + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_SIFS_HIST_TLV:\n"); + + len += print_array_to_buf(buf, len, "sifs_hist_status", + htt_stats_buf->sifs_hist_status, num_elems, "\n\n"); + + stats_req->buf_len = len; +} + +static void +htt_print_pdev_ctrl_path_tx_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_pdev_ctrl_path_tx_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_CTRL_PATH_TX_STATS:\n"); + len += print_array_to_buf(buf, len, "fw_tx_mgmt_subtype", + htt_stats_buf->fw_tx_mgmt_subtype, + ATH12K_HTT_STATS_SUBTYPE_MAX, "\n\n"); + + stats_req->buf_len = len; +} + static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, u16 tag, u16 len, const void *tag_buf, void *user_data) @@ -272,6 +405,15 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, case HTT_STATS_TX_PDEV_FLUSH_TAG: htt_print_tx_pdev_stats_flush_tlv(tag_buf, len, stats_req); break; + case HTT_STATS_TX_PDEV_SIFS_HIST_TAG: + htt_print_tx_pdev_stats_sifs_hist_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_PDEV_CTRL_PATH_TX_STATS_TAG: + htt_print_pdev_ctrl_path_tx_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_MU_PPDU_DIST_TAG: + htt_print_tx_pdev_mu_ppdu_dist_stats_tlv(tag_buf, len, stats_req); + break; default: break; } diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index 83060c54d00b..878d9cf6e7cb 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -11,6 +11,8 @@ #define ATH12K_HTT_STATS_COOKIE_LSB GENMASK_ULL(31, 0) #define ATH12K_HTT_STATS_COOKIE_MSB GENMASK_ULL(63, 32) #define ATH12K_HTT_STATS_MAGIC_VALUE 0xF0F0F0F0 +#define ATH12K_HTT_STATS_SUBTYPE_MAX 16 +#define ATH12K_HTT_MAX_STRING_LEN 256 #define ATH12K_HTT_STATS_RESET_BITMAP32_OFFSET(_idx) ((_idx) & 0x1f) #define ATH12K_HTT_STATS_RESET_BITMAP64_OFFSET(_idx) ((_idx) & 0x3f) @@ -133,6 +135,9 @@ enum ath12k_dbg_htt_tlv_tag { HTT_STATS_TX_PDEV_UNDERRUN_TAG = 1, HTT_STATS_TX_PDEV_SIFS_TAG = 2, HTT_STATS_TX_PDEV_FLUSH_TAG = 3, + HTT_STATS_TX_PDEV_SIFS_HIST_TAG = 67, + HTT_STATS_PDEV_CTRL_PATH_TX_STATS_TAG = 102, + HTT_STATS_MU_PPDU_DIST_TAG = 129, HTT_STATS_MAX_TAG, }; @@ -142,6 +147,18 @@ enum ath12k_dbg_htt_tlv_tag { #define ATH12K_HTT_TX_PDEV_MAX_SIFS_BURST_STATS 9 #define ATH12K_HTT_TX_PDEV_MAX_FLUSH_REASON_STATS 150 +/* MU MIMO distribution stats is a 2-dimensional array + * with dimension one denoting stats for nr4[0] or nr8[1] + */ +#define ATH12K_HTT_STATS_NUM_NR_BINS 2 +#define ATH12K_HTT_STATS_MAX_NUM_MU_PPDU_PER_BURST 10 +#define ATH12K_HTT_TX_PDEV_MAX_SIFS_BURST_HIST_STATS 10 +#define ATH12K_HTT_STATS_MAX_NUM_SCHED_STATUS 9 +#define ATH12K_HTT_STATS_NUM_SCHED_STATUS_WORDS \ + (ATH12K_HTT_STATS_NUM_NR_BINS * ATH12K_HTT_STATS_MAX_NUM_SCHED_STATUS) +#define ATH12K_HTT_STATS_MU_PPDU_PER_BURST_WORDS \ + (ATH12K_HTT_STATS_NUM_NR_BINS * ATH12K_HTT_STATS_MAX_NUM_MU_PPDU_PER_BURST) + enum ath12k_htt_tx_pdev_underrun_enum { HTT_STATS_TX_PDEV_NO_DATA_UNDERRUN = 0, HTT_STATS_TX_PDEV_DATA_UNDERRUN_BETWEEN_MPDU = 1, @@ -258,4 +275,26 @@ struct ath12k_htt_tx_pdev_stats_sifs_tlv { DECLARE_FLEX_ARRAY(__le32, sifs_status); } __packed; +struct ath12k_htt_pdev_ctrl_path_tx_stats_tlv { + __le32 fw_tx_mgmt_subtype[ATH12K_HTT_STATS_SUBTYPE_MAX]; +} __packed; + +struct ath12k_htt_tx_pdev_stats_sifs_hist_tlv { + DECLARE_FLEX_ARRAY(__le32, sifs_hist_status); +} __packed; + +enum ath12k_htt_stats_hw_mode { + ATH12K_HTT_STATS_HWMODE_AC = 0, + ATH12K_HTT_STATS_HWMODE_AX = 1, + ATH12K_HTT_STATS_HWMODE_BE = 2, +}; + +struct ath12k_htt_tx_pdev_mu_ppdu_dist_stats_tlv { + __le32 hw_mode; + __le32 num_seq_term_status[ATH12K_HTT_STATS_NUM_SCHED_STATUS_WORDS]; + __le32 num_ppdu_cmpl_per_burst[ATH12K_HTT_STATS_MU_PPDU_PER_BURST_WORDS]; + __le32 num_seq_posted[ATH12K_HTT_STATS_NUM_NR_BINS]; + __le32 num_ppdu_posted_per_burst[ATH12K_HTT_STATS_MU_PPDU_PER_BURST_WORDS]; +} __packed; + #endif From 4b6dfc758c2f6ce61336637b22bc0db57fa6d649 Mon Sep 17 00:00:00 2001 From: Dinesh Karthikeyan Date: Tue, 25 Jun 2024 09:52:15 +0530 Subject: [PATCH 36/38] wifi: ath12k: Support Transmit Scheduler stats Add support to request transmission scheduler stats from firmware through HTT stats type 4. This stat gives information such as pdev stats scheduled per transmission queue, transmission queue command posted and reaped, scheduler order, scheduler ineligibility and supercycle triggers. Sample output: ------------- echo 4 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats_type cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats HTT_STATS_TX_SCHED_CMN_TLV: mac_id = 0 current_timestamp = 952546828 HTT_TX_PDEV_STATS_SCHED_PER_TXQ_TLV: mac_id = 0 txq_id = 14 sched_policy = 2 ..... HTT_SCHED_TXQ_CMD_POSTED_TLV: sched_cmd_posted = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0 HTT_SCHED_TXQ_CMD_REAPED_TLV: sched_cmd_reaped = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0 HTT_SCHED_TXQ_SCHED_ORDER_SU_TLV: sched_order_su = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0, 16:0, 17:0, 18:0, 19:0 HTT_SCHED_TXQ_SCHED_INELIGIBILITY: sched_ineligibility = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0, 16:0, 17:0, 18:0, 19:0, 20:0, 21:0, 22:0, 23:0, 24:0, 25:0, 26:0, 27:0, 28:0, 29:0, 30:0, 31:0, 32:0, 33:0, 34:0, 35:0 HTT_SCHED_TXQ_SUPERCYCLE_TRIGGER: supercycle_triggers = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Dinesh Karthikeyan Signed-off-by: Roopni Devanathan Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240625042217.1303332-2-quic_rdevanat@quicinc.com --- .../wireless/ath/ath12k/debugfs_htt_stats.c | 233 ++++++++++++++++++ .../wireless/ath/ath12k/debugfs_htt_stats.h | 81 ++++++ 2 files changed, 314 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index fd04f057598d..586f002df15e 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -386,6 +386,216 @@ htt_print_pdev_ctrl_path_tx_stats_tlv(const void *tag_buf, u16 tag_len, stats_req->buf_len = len; } +static void +ath12k_htt_print_stats_tx_sched_cmn_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_stats_tx_sched_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, "HTT_STATS_TX_SCHED_CMN_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "current_timestamp = %u\n\n", + le32_to_cpu(htt_stats_buf->current_timestamp)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_pdev_stats_sched_per_txq_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_stats_sched_per_txq_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_SCHED_PER_TXQ_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, + ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "txq_id = %u\n", + u32_get_bits(mac_id_word, + ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_ID)); + len += scnprintf(buf + len, buf_len - len, "sched_policy = %u\n", + le32_to_cpu(htt_stats_buf->sched_policy)); + len += scnprintf(buf + len, buf_len - len, + "last_sched_cmd_posted_timestamp = %u\n", + le32_to_cpu(htt_stats_buf->last_sched_cmd_posted_timestamp)); + len += scnprintf(buf + len, buf_len - len, + "last_sched_cmd_compl_timestamp = %u\n", + le32_to_cpu(htt_stats_buf->last_sched_cmd_compl_timestamp)); + len += scnprintf(buf + len, buf_len - len, "sched_2_tac_lwm_count = %u\n", + le32_to_cpu(htt_stats_buf->sched_2_tac_lwm_count)); + len += scnprintf(buf + len, buf_len - len, "sched_2_tac_ring_full = %u\n", + le32_to_cpu(htt_stats_buf->sched_2_tac_ring_full)); + len += scnprintf(buf + len, buf_len - len, "sched_cmd_post_failure = %u\n", + le32_to_cpu(htt_stats_buf->sched_cmd_post_failure)); + len += scnprintf(buf + len, buf_len - len, "num_active_tids = %u\n", + le32_to_cpu(htt_stats_buf->num_active_tids)); + len += scnprintf(buf + len, buf_len - len, "num_ps_schedules = %u\n", + le32_to_cpu(htt_stats_buf->num_ps_schedules)); + len += scnprintf(buf + len, buf_len - len, "sched_cmds_pending = %u\n", + le32_to_cpu(htt_stats_buf->sched_cmds_pending)); + len += scnprintf(buf + len, buf_len - len, "num_tid_register = %u\n", + le32_to_cpu(htt_stats_buf->num_tid_register)); + len += scnprintf(buf + len, buf_len - len, "num_tid_unregister = %u\n", + le32_to_cpu(htt_stats_buf->num_tid_unregister)); + len += scnprintf(buf + len, buf_len - len, "num_qstats_queried = %u\n", + le32_to_cpu(htt_stats_buf->num_qstats_queried)); + len += scnprintf(buf + len, buf_len - len, "qstats_update_pending = %u\n", + le32_to_cpu(htt_stats_buf->qstats_update_pending)); + len += scnprintf(buf + len, buf_len - len, "last_qstats_query_timestamp = %u\n", + le32_to_cpu(htt_stats_buf->last_qstats_query_timestamp)); + len += scnprintf(buf + len, buf_len - len, "num_tqm_cmdq_full = %u\n", + le32_to_cpu(htt_stats_buf->num_tqm_cmdq_full)); + len += scnprintf(buf + len, buf_len - len, "num_de_sched_algo_trigger = %u\n", + le32_to_cpu(htt_stats_buf->num_de_sched_algo_trigger)); + len += scnprintf(buf + len, buf_len - len, "num_rt_sched_algo_trigger = %u\n", + le32_to_cpu(htt_stats_buf->num_rt_sched_algo_trigger)); + len += scnprintf(buf + len, buf_len - len, "num_tqm_sched_algo_trigger = %u\n", + le32_to_cpu(htt_stats_buf->num_tqm_sched_algo_trigger)); + len += scnprintf(buf + len, buf_len - len, "notify_sched = %u\n", + le32_to_cpu(htt_stats_buf->notify_sched)); + len += scnprintf(buf + len, buf_len - len, "dur_based_sendn_term = %u\n", + le32_to_cpu(htt_stats_buf->dur_based_sendn_term)); + len += scnprintf(buf + len, buf_len - len, "su_notify2_sched = %u\n", + le32_to_cpu(htt_stats_buf->su_notify2_sched)); + len += scnprintf(buf + len, buf_len - len, "su_optimal_queued_msdus_sched = %u\n", + le32_to_cpu(htt_stats_buf->su_optimal_queued_msdus_sched)); + len += scnprintf(buf + len, buf_len - len, "su_delay_timeout_sched = %u\n", + le32_to_cpu(htt_stats_buf->su_delay_timeout_sched)); + len += scnprintf(buf + len, buf_len - len, "su_min_txtime_sched_delay = %u\n", + le32_to_cpu(htt_stats_buf->su_min_txtime_sched_delay)); + len += scnprintf(buf + len, buf_len - len, "su_no_delay = %u\n", + le32_to_cpu(htt_stats_buf->su_no_delay)); + len += scnprintf(buf + len, buf_len - len, "num_supercycles = %u\n", + le32_to_cpu(htt_stats_buf->num_supercycles)); + len += scnprintf(buf + len, buf_len - len, "num_subcycles_with_sort = %u\n", + le32_to_cpu(htt_stats_buf->num_subcycles_with_sort)); + len += scnprintf(buf + len, buf_len - len, "num_subcycles_no_sort = %u\n\n", + le32_to_cpu(htt_stats_buf->num_subcycles_no_sort)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_sched_txq_cmd_posted_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_sched_txq_cmd_posted_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elements = tag_len >> 2; + + len += scnprintf(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_POSTED_TLV:\n"); + len += print_array_to_buf(buf, len, "sched_cmd_posted", + htt_stats_buf->sched_cmd_posted, num_elements, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_sched_txq_cmd_reaped_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_sched_txq_cmd_reaped_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elements = tag_len >> 2; + + len += scnprintf(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_REAPED_TLV:\n"); + len += print_array_to_buf(buf, len, "sched_cmd_reaped", + htt_stats_buf->sched_cmd_reaped, num_elements, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_sched_txq_sched_order_su_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_sched_txq_sched_order_su_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 sched_order_su_num_entries = min_t(u32, (tag_len >> 2), + ATH12K_HTT_TX_PDEV_NUM_SCHED_ORDER_LOG); + + len += scnprintf(buf + len, buf_len - len, + "HTT_SCHED_TXQ_SCHED_ORDER_SU_TLV:\n"); + len += print_array_to_buf(buf, len, "sched_order_su", + htt_stats_buf->sched_order_su, + sched_order_su_num_entries, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_sched_txq_sched_ineligibility_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_sched_txq_sched_ineligibility_tlv *htt_stats_buf = + tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 sched_ineligibility_num_entries = tag_len >> 2; + + len += scnprintf(buf + len, buf_len - len, + "HTT_SCHED_TXQ_SCHED_INELIGIBILITY:\n"); + len += print_array_to_buf(buf, len, "sched_ineligibility", + htt_stats_buf->sched_ineligibility, + sched_ineligibility_num_entries, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_sched_txq_supercycle_trigger_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_sched_txq_supercycle_triggers_tlv *htt_stats_buf = + tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elems = min_t(u16, (tag_len >> 2), + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_MAX); + + len += scnprintf(buf + len, buf_len - len, + "HTT_SCHED_TXQ_SUPERCYCLE_TRIGGER:\n"); + len += print_array_to_buf(buf, len, "supercycle_triggers", + htt_stats_buf->supercycle_triggers, num_elems, "\n\n"); + + stats_req->buf_len = len; +} + static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, u16 tag, u16 len, const void *tag_buf, void *user_data) @@ -414,6 +624,29 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, case HTT_STATS_MU_PPDU_DIST_TAG: htt_print_tx_pdev_mu_ppdu_dist_stats_tlv(tag_buf, len, stats_req); break; + case HTT_STATS_TX_SCHED_CMN_TAG: + ath12k_htt_print_stats_tx_sched_cmn_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_SCHEDULER_TXQ_STATS_TAG: + ath12k_htt_print_tx_pdev_stats_sched_per_txq_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_SCHED_TXQ_CMD_POSTED_TAG: + ath12k_htt_print_sched_txq_cmd_posted_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_SCHED_TXQ_CMD_REAPED_TAG: + ath12k_htt_print_sched_txq_cmd_reaped_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_SCHED_TXQ_SCHED_ORDER_SU_TAG: + ath12k_htt_print_sched_txq_sched_order_su_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_SCHED_TXQ_SCHED_INELIGIBILITY_TAG: + ath12k_htt_print_sched_txq_sched_ineligibility_tlv(tag_buf, len, + stats_req); + break; + case HTT_STATS_SCHED_TXQ_SUPERCYCLE_TRIGGER_TAG: + ath12k_htt_print_sched_txq_supercycle_trigger_tlv(tag_buf, len, + stats_req); + break; default: break; } diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index 878d9cf6e7cb..ee90d620fc1d 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -125,6 +125,7 @@ struct ath12k_htt_extd_stats_msg { enum ath12k_dbg_htt_ext_stats_type { ATH12K_DBG_HTT_EXT_STATS_RESET = 0, ATH12K_DBG_HTT_EXT_STATS_PDEV_TX = 1, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED = 4, /* keep this last */ ATH12K_DBG_HTT_NUM_EXT_STATS, @@ -135,7 +136,14 @@ enum ath12k_dbg_htt_tlv_tag { HTT_STATS_TX_PDEV_UNDERRUN_TAG = 1, HTT_STATS_TX_PDEV_SIFS_TAG = 2, HTT_STATS_TX_PDEV_FLUSH_TAG = 3, + HTT_STATS_TX_PDEV_SCHEDULER_TXQ_STATS_TAG = 36, + HTT_STATS_TX_SCHED_CMN_TAG = 37, + HTT_STATS_SCHED_TXQ_CMD_POSTED_TAG = 39, + HTT_STATS_SCHED_TXQ_CMD_REAPED_TAG = 44, HTT_STATS_TX_PDEV_SIFS_HIST_TAG = 67, + HTT_STATS_SCHED_TXQ_SCHED_ORDER_SU_TAG = 86, + HTT_STATS_SCHED_TXQ_SCHED_INELIGIBILITY_TAG = 87, + HTT_STATS_SCHED_TXQ_SUPERCYCLE_TRIGGER_TAG = 100, HTT_STATS_PDEV_CTRL_PATH_TX_STATS_TAG = 102, HTT_STATS_MU_PPDU_DIST_TAG = 129, @@ -297,4 +305,77 @@ struct ath12k_htt_tx_pdev_mu_ppdu_dist_stats_tlv { __le32 num_ppdu_posted_per_burst[ATH12K_HTT_STATS_MU_PPDU_PER_BURST_WORDS]; } __packed; +#define ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_MAC_ID GENMASK(7, 0) +#define ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_ID GENMASK(15, 8) + +#define ATH12K_HTT_TX_PDEV_NUM_SCHED_ORDER_LOG 20 + +struct ath12k_htt_stats_tx_sched_cmn_tlv { + __le32 mac_id__word; + __le32 current_timestamp; +} __packed; + +struct ath12k_htt_tx_pdev_stats_sched_per_txq_tlv { + __le32 mac_id__word; + __le32 sched_policy; + __le32 last_sched_cmd_posted_timestamp; + __le32 last_sched_cmd_compl_timestamp; + __le32 sched_2_tac_lwm_count; + __le32 sched_2_tac_ring_full; + __le32 sched_cmd_post_failure; + __le32 num_active_tids; + __le32 num_ps_schedules; + __le32 sched_cmds_pending; + __le32 num_tid_register; + __le32 num_tid_unregister; + __le32 num_qstats_queried; + __le32 qstats_update_pending; + __le32 last_qstats_query_timestamp; + __le32 num_tqm_cmdq_full; + __le32 num_de_sched_algo_trigger; + __le32 num_rt_sched_algo_trigger; + __le32 num_tqm_sched_algo_trigger; + __le32 notify_sched; + __le32 dur_based_sendn_term; + __le32 su_notify2_sched; + __le32 su_optimal_queued_msdus_sched; + __le32 su_delay_timeout_sched; + __le32 su_min_txtime_sched_delay; + __le32 su_no_delay; + __le32 num_supercycles; + __le32 num_subcycles_with_sort; + __le32 num_subcycles_no_sort; +} __packed; + +struct ath12k_htt_sched_txq_cmd_posted_tlv { + DECLARE_FLEX_ARRAY(__le32, sched_cmd_posted); +} __packed; + +struct ath12k_htt_sched_txq_cmd_reaped_tlv { + DECLARE_FLEX_ARRAY(__le32, sched_cmd_reaped); +} __packed; + +struct ath12k_htt_sched_txq_sched_order_su_tlv { + DECLARE_FLEX_ARRAY(__le32, sched_order_su); +} __packed; + +struct ath12k_htt_sched_txq_sched_ineligibility_tlv { + DECLARE_FLEX_ARRAY(__le32, sched_ineligibility); +} __packed; + +enum ath12k_htt_sched_txq_supercycle_triggers_tlv_enum { + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_NONE = 0, + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_FORCED, + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_LESS_NUM_TIDQ_ENTRIES, + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_LESS_NUM_ACTIVE_TIDS, + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_MAX_ITR_REACHED, + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_DUR_THRESHOLD_REACHED, + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_TWT_TRIGGER, + ATH12K_HTT_SCHED_SUPERCYCLE_TRIGGER_MAX, +}; + +struct ath12k_htt_sched_txq_supercycle_triggers_tlv { + DECLARE_FLEX_ARRAY(__le32, supercycle_triggers); +} __packed; + #endif From cf1187b3a683feb718a50ee19178dc6e90a73a64 Mon Sep 17 00:00:00 2001 From: Dinesh Karthikeyan Date: Tue, 25 Jun 2024 09:52:16 +0530 Subject: [PATCH 37/38] wifi: ath12k: Support pdev error stats Add support to request pdev error stats from firmware through HTT stats type 5. This stats type gives information such as list of pdev errors, reasons for reset, logging information about recovery, drain and war stats. etc. Sample output: ------------- echo 5 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats_type cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats HTT_HW_STATS_PDEV_ERRS_TLV: mac_id = 0 tx_abort = 0 tx_abort_fail_count = 0 rx_abort = 53 ..... PDEV_PHY_WARM_RESET_REASONS: phy_warm_reset_reason_phy_m3 = 0 phy_warm_reset_reason_tx_hw_stuck = 0 phy_warm_reset_reason_num_cca_rx_frame_stuck = 0 ..... WAL_RX_RECOVERY_STATS: wal_rx_recovery_rst_mac_hang_count = 0 wal_rx_recovery_rst_known_sig_count = 0 wal_rx_recovery_rst_no_rx_count = 0 ..... HTT_RX_DEST_DRAIN_STATS: rx_dest_drain_rx_descs_leak_prevention_done = 0 rx_dest_drain_rx_descs_saved_cnt = 0 rx_dest_drain_rxdma2reo_leak_detected = 0 ..... HTT_HW_STATS_INTR_MISC_TLV: hw_intr_name = AMPI mask = 0 count = 2 HTT_HW_STATS_WHAL_TX_TLV: mac_id = 0 last_unpause_ppdu_id = 0 hwsch_unpause_wait_tqm_write = 0 ..... HTT_HW_WAR_STATS_TLV: mac_id = 0 hw_war 0 = 0 hw_war 1 = 0 ..... Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Dinesh Karthikeyan Signed-off-by: Roopni Devanathan Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240625042217.1303332-3-quic_rdevanat@quicinc.com --- .../wireless/ath/ath12k/debugfs_htt_stats.c | 262 ++++++++++++++++++ .../wireless/ath/ath12k/debugfs_htt_stats.h | 84 ++++++ 2 files changed, 346 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index 586f002df15e..05bb0ea68e15 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -596,6 +596,256 @@ ath12k_htt_print_sched_txq_supercycle_trigger_tlv(const void *tag_buf, stats_req->buf_len = len; } +static void +ath12k_htt_print_hw_stats_pdev_errs_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_hw_stats_pdev_errs_tlv *htt_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_buf)) + return; + + mac_id_word = le32_to_cpu(htt_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, "HTT_HW_STATS_PDEV_ERRS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "tx_abort = %u\n", + le32_to_cpu(htt_buf->tx_abort)); + len += scnprintf(buf + len, buf_len - len, "tx_abort_fail_count = %u\n", + le32_to_cpu(htt_buf->tx_abort_fail_count)); + len += scnprintf(buf + len, buf_len - len, "rx_abort = %u\n", + le32_to_cpu(htt_buf->rx_abort)); + len += scnprintf(buf + len, buf_len - len, "rx_abort_fail_count = %u\n", + le32_to_cpu(htt_buf->rx_abort_fail_count)); + len += scnprintf(buf + len, buf_len - len, "rx_flush_cnt = %u\n", + le32_to_cpu(htt_buf->rx_flush_cnt)); + len += scnprintf(buf + len, buf_len - len, "warm_reset = %u\n", + le32_to_cpu(htt_buf->warm_reset)); + len += scnprintf(buf + len, buf_len - len, "cold_reset = %u\n", + le32_to_cpu(htt_buf->cold_reset)); + len += scnprintf(buf + len, buf_len - len, "mac_cold_reset_restore_cal = %u\n", + le32_to_cpu(htt_buf->mac_cold_reset_restore_cal)); + len += scnprintf(buf + len, buf_len - len, "mac_cold_reset = %u\n", + le32_to_cpu(htt_buf->mac_cold_reset)); + len += scnprintf(buf + len, buf_len - len, "mac_warm_reset = %u\n", + le32_to_cpu(htt_buf->mac_warm_reset)); + len += scnprintf(buf + len, buf_len - len, "mac_only_reset = %u\n", + le32_to_cpu(htt_buf->mac_only_reset)); + len += scnprintf(buf + len, buf_len - len, "phy_warm_reset = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset)); + len += scnprintf(buf + len, buf_len - len, "phy_warm_reset_ucode_trig = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_ucode_trig)); + len += scnprintf(buf + len, buf_len - len, "mac_warm_reset_restore_cal = %u\n", + le32_to_cpu(htt_buf->mac_warm_reset_restore_cal)); + len += scnprintf(buf + len, buf_len - len, "mac_sfm_reset = %u\n", + le32_to_cpu(htt_buf->mac_sfm_reset)); + len += scnprintf(buf + len, buf_len - len, "phy_warm_reset_m3_ssr = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_m3_ssr)); + len += scnprintf(buf + len, buf_len - len, "fw_rx_rings_reset = %u\n", + le32_to_cpu(htt_buf->fw_rx_rings_reset)); + len += scnprintf(buf + len, buf_len - len, "tx_flush = %u\n", + le32_to_cpu(htt_buf->tx_flush)); + len += scnprintf(buf + len, buf_len - len, "tx_glb_reset = %u\n", + le32_to_cpu(htt_buf->tx_glb_reset)); + len += scnprintf(buf + len, buf_len - len, "tx_txq_reset = %u\n", + le32_to_cpu(htt_buf->tx_txq_reset)); + len += scnprintf(buf + len, buf_len - len, "rx_timeout_reset = %u\n\n", + le32_to_cpu(htt_buf->rx_timeout_reset)); + + len += scnprintf(buf + len, buf_len - len, "PDEV_PHY_WARM_RESET_REASONS:\n"); + len += scnprintf(buf + len, buf_len - len, "phy_warm_reset_reason_phy_m3 = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_phy_m3)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_tx_hw_stuck = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_tx_hw_stuck)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_num_cca_rx_frame_stuck = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_num_rx_frame_stuck)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_wal_rx_recovery_rst_rx_busy = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_wal_rx_rec_rx_busy)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_wal_rx_recovery_rst_mac_hang = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_wal_rx_rec_mac_hng)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_mac_reset_converted_phy_reset = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_mac_conv_phy_reset)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_tx_lifetime_expiry_cca_stuck = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_tx_exp_cca_stuck)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_tx_consecutive_flush9_war = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_tx_consec_flsh_war)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_tx_hwsch_reset_war = %u\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_tx_hwsch_reset_war)); + len += scnprintf(buf + len, buf_len - len, + "phy_warm_reset_reason_hwsch_wdog_or_cca_wdog_war = %u\n\n", + le32_to_cpu(htt_buf->phy_warm_reset_reason_hwsch_cca_wdog_war)); + + len += scnprintf(buf + len, buf_len - len, "WAL_RX_RECOVERY_STATS:\n"); + len += scnprintf(buf + len, buf_len - len, + "wal_rx_recovery_rst_mac_hang_count = %u\n", + le32_to_cpu(htt_buf->wal_rx_recovery_rst_mac_hang_cnt)); + len += scnprintf(buf + len, buf_len - len, + "wal_rx_recovery_rst_known_sig_count = %u\n", + le32_to_cpu(htt_buf->wal_rx_recovery_rst_known_sig_cnt)); + len += scnprintf(buf + len, buf_len - len, + "wal_rx_recovery_rst_no_rx_count = %u\n", + le32_to_cpu(htt_buf->wal_rx_recovery_rst_no_rx_cnt)); + len += scnprintf(buf + len, buf_len - len, + "wal_rx_recovery_rst_no_rx_consecutive_count = %u\n", + le32_to_cpu(htt_buf->wal_rx_recovery_rst_no_rx_consec_cnt)); + len += scnprintf(buf + len, buf_len - len, + "wal_rx_recovery_rst_rx_busy_count = %u\n", + le32_to_cpu(htt_buf->wal_rx_recovery_rst_rx_busy_cnt)); + len += scnprintf(buf + len, buf_len - len, + "wal_rx_recovery_rst_phy_mac_hang_count = %u\n\n", + le32_to_cpu(htt_buf->wal_rx_recovery_rst_phy_mac_hang_cnt)); + + len += scnprintf(buf + len, buf_len - len, "HTT_RX_DEST_DRAIN_STATS:\n"); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_rx_descs_leak_prevention_done = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_rx_descs_leak_prevented)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_rx_descs_saved_cnt = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_rx_descs_saved_cnt)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_rxdma2reo_leak_detected = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_rxdma2reo_leak_detected)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_rxdma2fw_leak_detected = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_rxdma2fw_leak_detected)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_rxdma2wbm_leak_detected = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_rxdma2wbm_leak_detected)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_rxdma1_2sw_leak_detected = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_rxdma1_2sw_leak_detected)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_rx_drain_ok_mac_idle = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_rx_drain_ok_mac_idle)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_ok_mac_not_idle = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_ok_mac_not_idle)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_prerequisite_invld = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_prerequisite_invld)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_skip_for_non_lmac_reset = %u\n", + le32_to_cpu(htt_buf->rx_dest_drain_skip_non_lmac_reset)); + len += scnprintf(buf + len, buf_len - len, + "rx_dest_drain_hw_fifo_not_empty_post_drain_wait = %u\n\n", + le32_to_cpu(htt_buf->rx_dest_drain_hw_fifo_notempty_post_wait)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_hw_stats_intr_misc_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_hw_stats_intr_misc_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_HW_STATS_INTR_MISC_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "hw_intr_name = %s\n", + htt_stats_buf->hw_intr_name); + len += scnprintf(buf + len, buf_len - len, "mask = %u\n", + le32_to_cpu(htt_stats_buf->mask)); + len += scnprintf(buf + len, buf_len - len, "count = %u\n\n", + le32_to_cpu(htt_stats_buf->count)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_hw_stats_whal_tx_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_hw_stats_whal_tx_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, "HTT_HW_STATS_WHAL_TX_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "last_unpause_ppdu_id = %u\n", + le32_to_cpu(htt_stats_buf->last_unpause_ppdu_id)); + len += scnprintf(buf + len, buf_len - len, "hwsch_unpause_wait_tqm_write = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_unpause_wait_tqm_write)); + len += scnprintf(buf + len, buf_len - len, "hwsch_dummy_tlv_skipped = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_dummy_tlv_skipped)); + len += scnprintf(buf + len, buf_len - len, + "hwsch_misaligned_offset_received = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_misaligned_offset_received)); + len += scnprintf(buf + len, buf_len - len, "hwsch_reset_count = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_reset_count)); + len += scnprintf(buf + len, buf_len - len, "hwsch_dev_reset_war = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_dev_reset_war)); + len += scnprintf(buf + len, buf_len - len, "hwsch_delayed_pause = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_delayed_pause)); + len += scnprintf(buf + len, buf_len - len, "hwsch_long_delayed_pause = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_long_delayed_pause)); + len += scnprintf(buf + len, buf_len - len, "sch_rx_ppdu_no_response = %u\n", + le32_to_cpu(htt_stats_buf->sch_rx_ppdu_no_response)); + len += scnprintf(buf + len, buf_len - len, "sch_selfgen_response = %u\n", + le32_to_cpu(htt_stats_buf->sch_selfgen_response)); + len += scnprintf(buf + len, buf_len - len, "sch_rx_sifs_resp_trigger= %u\n\n", + le32_to_cpu(htt_stats_buf->sch_rx_sifs_resp_trigger)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_hw_war_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_hw_war_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 fixed_len, array_len; + u8 i, array_words; + u32 mac_id; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id = __le32_to_cpu(htt_stats_buf->mac_id__word); + fixed_len = sizeof(*htt_stats_buf); + array_len = tag_len - fixed_len; + array_words = array_len >> 2; + + len += scnprintf(buf + len, buf_len - len, "HTT_HW_WAR_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id, ATH12K_HTT_STATS_MAC_ID)); + + for (i = 0; i < array_words; i++) { + len += scnprintf(buf + len, buf_len - len, "hw_war %u = %u\n\n", + i, le32_to_cpu(htt_stats_buf->hw_wars[i])); + } + + stats_req->buf_len = len; +} + static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, u16 tag, u16 len, const void *tag_buf, void *user_data) @@ -647,6 +897,18 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, ath12k_htt_print_sched_txq_supercycle_trigger_tlv(tag_buf, len, stats_req); break; + case HTT_STATS_HW_PDEV_ERRS_TAG: + ath12k_htt_print_hw_stats_pdev_errs_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_HW_INTR_MISC_TAG: + ath12k_htt_print_hw_stats_intr_misc_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_WHAL_TX_TAG: + ath12k_htt_print_hw_stats_whal_tx_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_HW_WAR_TAG: + ath12k_htt_print_hw_war_tlv(tag_buf, len, stats_req); + break; default: break; } diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index ee90d620fc1d..777a2e067a04 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -126,6 +126,7 @@ enum ath12k_dbg_htt_ext_stats_type { ATH12K_DBG_HTT_EXT_STATS_RESET = 0, ATH12K_DBG_HTT_EXT_STATS_PDEV_TX = 1, ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED = 4, + ATH12K_DBG_HTT_EXT_STATS_PDEV_ERROR = 5, /* keep this last */ ATH12K_DBG_HTT_NUM_EXT_STATS, @@ -140,9 +141,13 @@ enum ath12k_dbg_htt_tlv_tag { HTT_STATS_TX_SCHED_CMN_TAG = 37, HTT_STATS_SCHED_TXQ_CMD_POSTED_TAG = 39, HTT_STATS_SCHED_TXQ_CMD_REAPED_TAG = 44, + HTT_STATS_HW_INTR_MISC_TAG = 54, + HTT_STATS_HW_PDEV_ERRS_TAG = 56, + HTT_STATS_WHAL_TX_TAG = 66, HTT_STATS_TX_PDEV_SIFS_HIST_TAG = 67, HTT_STATS_SCHED_TXQ_SCHED_ORDER_SU_TAG = 86, HTT_STATS_SCHED_TXQ_SCHED_INELIGIBILITY_TAG = 87, + HTT_STATS_HW_WAR_TAG = 89, HTT_STATS_SCHED_TXQ_SUPERCYCLE_TRIGGER_TAG = 100, HTT_STATS_PDEV_CTRL_PATH_TX_STATS_TAG = 102, HTT_STATS_MU_PPDU_DIST_TAG = 129, @@ -378,4 +383,83 @@ struct ath12k_htt_sched_txq_supercycle_triggers_tlv { DECLARE_FLEX_ARRAY(__le32, supercycle_triggers); } __packed; +struct ath12k_htt_hw_stats_pdev_errs_tlv { + __le32 mac_id__word; + __le32 tx_abort; + __le32 tx_abort_fail_count; + __le32 rx_abort; + __le32 rx_abort_fail_count; + __le32 warm_reset; + __le32 cold_reset; + __le32 tx_flush; + __le32 tx_glb_reset; + __le32 tx_txq_reset; + __le32 rx_timeout_reset; + __le32 mac_cold_reset_restore_cal; + __le32 mac_cold_reset; + __le32 mac_warm_reset; + __le32 mac_only_reset; + __le32 phy_warm_reset; + __le32 phy_warm_reset_ucode_trig; + __le32 mac_warm_reset_restore_cal; + __le32 mac_sfm_reset; + __le32 phy_warm_reset_m3_ssr; + __le32 phy_warm_reset_reason_phy_m3; + __le32 phy_warm_reset_reason_tx_hw_stuck; + __le32 phy_warm_reset_reason_num_rx_frame_stuck; + __le32 phy_warm_reset_reason_wal_rx_rec_rx_busy; + __le32 phy_warm_reset_reason_wal_rx_rec_mac_hng; + __le32 phy_warm_reset_reason_mac_conv_phy_reset; + __le32 wal_rx_recovery_rst_mac_hang_cnt; + __le32 wal_rx_recovery_rst_known_sig_cnt; + __le32 wal_rx_recovery_rst_no_rx_cnt; + __le32 wal_rx_recovery_rst_no_rx_consec_cnt; + __le32 wal_rx_recovery_rst_rx_busy_cnt; + __le32 wal_rx_recovery_rst_phy_mac_hang_cnt; + __le32 rx_flush_cnt; + __le32 phy_warm_reset_reason_tx_exp_cca_stuck; + __le32 phy_warm_reset_reason_tx_consec_flsh_war; + __le32 phy_warm_reset_reason_tx_hwsch_reset_war; + __le32 phy_warm_reset_reason_hwsch_cca_wdog_war; + __le32 fw_rx_rings_reset; + __le32 rx_dest_drain_rx_descs_leak_prevented; + __le32 rx_dest_drain_rx_descs_saved_cnt; + __le32 rx_dest_drain_rxdma2reo_leak_detected; + __le32 rx_dest_drain_rxdma2fw_leak_detected; + __le32 rx_dest_drain_rxdma2wbm_leak_detected; + __le32 rx_dest_drain_rxdma1_2sw_leak_detected; + __le32 rx_dest_drain_rx_drain_ok_mac_idle; + __le32 rx_dest_drain_ok_mac_not_idle; + __le32 rx_dest_drain_prerequisite_invld; + __le32 rx_dest_drain_skip_non_lmac_reset; + __le32 rx_dest_drain_hw_fifo_notempty_post_wait; +} __packed; + +#define ATH12K_HTT_STATS_MAX_HW_INTR_NAME_LEN 8 +struct ath12k_htt_hw_stats_intr_misc_tlv { + u8 hw_intr_name[ATH12K_HTT_STATS_MAX_HW_INTR_NAME_LEN]; + __le32 mask; + __le32 count; +} __packed; + +struct ath12k_htt_hw_stats_whal_tx_tlv { + __le32 mac_id__word; + __le32 last_unpause_ppdu_id; + __le32 hwsch_unpause_wait_tqm_write; + __le32 hwsch_dummy_tlv_skipped; + __le32 hwsch_misaligned_offset_received; + __le32 hwsch_reset_count; + __le32 hwsch_dev_reset_war; + __le32 hwsch_delayed_pause; + __le32 hwsch_long_delayed_pause; + __le32 sch_rx_ppdu_no_response; + __le32 sch_selfgen_response; + __le32 sch_rx_sifs_resp_trigger; +} __packed; + +struct ath12k_htt_hw_war_stats_tlv { + __le32 mac_id__word; + DECLARE_FLEX_ARRAY(__le32, hw_wars); +} __packed; + #endif From 5344fc76f8944249026884157f846f88489edfc0 Mon Sep 17 00:00:00 2001 From: Dinesh Karthikeyan Date: Tue, 25 Jun 2024 09:52:17 +0530 Subject: [PATCH 38/38] wifi: ath12k: Support TQM stats Add support to request pdev TQM stats from firmware through HTT stats type 6. This stat type gives information such as TQM error, MPDU related information and TQM pdev stats. Sample output: ------------- echo 6 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats_type cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats HTT_TX_TQM_ERROR_STATS_TLV: q_empty_failure = 0 q_not_empty_failure = 0 add_msdu_failure = 0 TQM_ERROR_RESET_STATS: tqm_cache_ctl_err = 0 tqm_soft_reset = 0 tqm_reset_total_num_in_use_link_descs = 0 ..... HTT_TX_TQM_GEN_MPDU_STATS_TLV: gen_mpdu_end_reason = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0, 16:0 HTT_TX_TQM_LIST_MPDU_STATS_TLV: list_mpdu_end_reason = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0 HTT_TX_TQM_LIST_MPDU_CNT_TLV_V: list_mpdu_cnt_hist = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0 HTT_TX_TQM_PDEV_STATS_TLV_V: msdu_count = 0 mpdu_count = 0 remove_msdu = 0 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Dinesh Karthikeyan Signed-off-by: Roopni Devanathan Acked-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://patch.msgid.link/20240625042217.1303332-4-quic_rdevanat@quicinc.com --- .../wireless/ath/ath12k/debugfs_htt_stats.c | 289 ++++++++++++++++++ .../wireless/ath/ath12k/debugfs_htt_stats.h | 102 +++++++ 2 files changed, 391 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index 05bb0ea68e15..ce80e7b5175b 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -846,6 +846,277 @@ ath12k_htt_print_hw_war_tlv(const void *tag_buf, u16 tag_len, stats_req->buf_len = len; } +static void +ath12k_htt_print_tx_tqm_cmn_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_tqm_cmn_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_CMN_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "max_cmdq_id = %u\n", + le32_to_cpu(htt_stats_buf->max_cmdq_id)); + len += scnprintf(buf + len, buf_len - len, "list_mpdu_cnt_hist_intvl = %u\n", + le32_to_cpu(htt_stats_buf->list_mpdu_cnt_hist_intvl)); + len += scnprintf(buf + len, buf_len - len, "add_msdu = %u\n", + le32_to_cpu(htt_stats_buf->add_msdu)); + len += scnprintf(buf + len, buf_len - len, "q_empty = %u\n", + le32_to_cpu(htt_stats_buf->q_empty)); + len += scnprintf(buf + len, buf_len - len, "q_not_empty = %u\n", + le32_to_cpu(htt_stats_buf->q_not_empty)); + len += scnprintf(buf + len, buf_len - len, "drop_notification = %u\n", + le32_to_cpu(htt_stats_buf->drop_notification)); + len += scnprintf(buf + len, buf_len - len, "desc_threshold = %u\n", + le32_to_cpu(htt_stats_buf->desc_threshold)); + len += scnprintf(buf + len, buf_len - len, "hwsch_tqm_invalid_status = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_tqm_invalid_status)); + len += scnprintf(buf + len, buf_len - len, "missed_tqm_gen_mpdus = %u\n", + le32_to_cpu(htt_stats_buf->missed_tqm_gen_mpdus)); + len += scnprintf(buf + len, buf_len - len, + "total_msduq_timestamp_updates = %u\n", + le32_to_cpu(htt_stats_buf->msduq_timestamp_updates)); + len += scnprintf(buf + len, buf_len - len, + "total_msduq_timestamp_updates_by_get_mpdu_head_info_cmd = %u\n", + le32_to_cpu(htt_stats_buf->msduq_updates_mpdu_head_info_cmd)); + len += scnprintf(buf + len, buf_len - len, + "total_msduq_timestamp_updates_by_emp_to_nonemp_status = %u\n", + le32_to_cpu(htt_stats_buf->msduq_updates_emp_to_nonemp_status)); + len += scnprintf(buf + len, buf_len - len, + "total_get_mpdu_head_info_cmds_by_sched_algo_la_query = %u\n", + le32_to_cpu(htt_stats_buf->get_mpdu_head_info_cmds_by_query)); + len += scnprintf(buf + len, buf_len - len, + "total_get_mpdu_head_info_cmds_by_tac = %u\n", + le32_to_cpu(htt_stats_buf->get_mpdu_head_info_cmds_by_tac)); + len += scnprintf(buf + len, buf_len - len, + "total_gen_mpdu_cmds_by_sched_algo_la_query = %u\n", + le32_to_cpu(htt_stats_buf->gen_mpdu_cmds_by_query)); + len += scnprintf(buf + len, buf_len - len, "active_tqm_tids = %u\n", + le32_to_cpu(htt_stats_buf->tqm_active_tids)); + len += scnprintf(buf + len, buf_len - len, "inactive_tqm_tids = %u\n", + le32_to_cpu(htt_stats_buf->tqm_inactive_tids)); + len += scnprintf(buf + len, buf_len - len, "tqm_active_msduq_flows = %u\n", + le32_to_cpu(htt_stats_buf->tqm_active_msduq_flows)); + len += scnprintf(buf + len, buf_len - len, "hi_prio_q_not_empty = %u\n\n", + le32_to_cpu(htt_stats_buf->high_prio_q_not_empty)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_tqm_error_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_tqm_error_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_ERROR_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "q_empty_failure = %u\n", + le32_to_cpu(htt_stats_buf->q_empty_failure)); + len += scnprintf(buf + len, buf_len - len, "q_not_empty_failure = %u\n", + le32_to_cpu(htt_stats_buf->q_not_empty_failure)); + len += scnprintf(buf + len, buf_len - len, "add_msdu_failure = %u\n\n", + le32_to_cpu(htt_stats_buf->add_msdu_failure)); + + len += scnprintf(buf + len, buf_len - len, "TQM_ERROR_RESET_STATS:\n"); + len += scnprintf(buf + len, buf_len - len, "tqm_cache_ctl_err = %u\n", + le32_to_cpu(htt_stats_buf->tqm_cache_ctl_err)); + len += scnprintf(buf + len, buf_len - len, "tqm_soft_reset = %u\n", + le32_to_cpu(htt_stats_buf->tqm_soft_reset)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_total_num_in_use_link_descs = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_num_in_use_link_descs)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_worst_case_num_lost_link_descs = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_num_lost_link_descs)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_worst_case_num_lost_host_tx_bufs_count = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_num_lost_host_tx_buf_cnt)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_num_in_use_link_descs_internal_tqm = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_num_in_use_internal_tqm)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_num_in_use_link_descs_wbm_idle_link_ring = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_num_in_use_idle_link_rng)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_time_to_tqm_hang_delta_ms = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_time_to_tqm_hang_delta_ms)); + len += scnprintf(buf + len, buf_len - len, "tqm_reset_recovery_time_ms = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_recovery_time_ms)); + len += scnprintf(buf + len, buf_len - len, "tqm_reset_num_peers_hdl = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_num_peers_hdl)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_cumm_dirty_hw_mpduq_proc_cnt = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_cumm_dirty_hw_mpduq_cnt)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_cumm_dirty_hw_msduq_proc = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_cumm_dirty_hw_msduq_proc)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_flush_cache_cmd_su_cnt = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_flush_cache_cmd_su_cnt)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_flush_cache_cmd_other_cnt = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_flush_cache_cmd_other_cnt)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_flush_cache_cmd_trig_type = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_flush_cache_cmd_trig_type)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_flush_cache_cmd_trig_cfg = %u\n", + le32_to_cpu(htt_stats_buf->tqm_reset_flush_cache_cmd_trig_cfg)); + len += scnprintf(buf + len, buf_len - len, + "tqm_reset_flush_cache_cmd_skip_cmd_status_null = %u\n\n", + le32_to_cpu(htt_stats_buf->tqm_reset_flush_cmd_skp_status_null)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_tqm_gen_mpdu_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_tqm_gen_mpdu_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elements = tag_len >> 2; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_GEN_MPDU_STATS_TLV:\n"); + len += print_array_to_buf(buf, len, "gen_mpdu_end_reason", + htt_stats_buf->gen_mpdu_end_reason, num_elements, + "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_tqm_list_mpdu_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_tqm_list_mpdu_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elems = min_t(u16, (tag_len >> 2), + ATH12K_HTT_TX_TQM_MAX_LIST_MPDU_END_REASON); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_LIST_MPDU_STATS_TLV:\n"); + len += print_array_to_buf(buf, len, "list_mpdu_end_reason", + htt_stats_buf->list_mpdu_end_reason, num_elems, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_tqm_list_mpdu_cnt_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_tqm_list_mpdu_cnt_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u16 num_elems = min_t(u16, (tag_len >> 2), + ATH12K_HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_LIST_MPDU_CNT_TLV_V:\n"); + len += print_array_to_buf(buf, len, "list_mpdu_cnt_hist", + htt_stats_buf->list_mpdu_cnt_hist, num_elems, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_tx_tqm_pdev_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_tqm_pdev_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_TQM_PDEV_STATS_TLV_V:\n"); + len += scnprintf(buf + len, buf_len - len, "msdu_count = %u\n", + le32_to_cpu(htt_stats_buf->msdu_count)); + len += scnprintf(buf + len, buf_len - len, "mpdu_count = %u\n", + le32_to_cpu(htt_stats_buf->mpdu_count)); + len += scnprintf(buf + len, buf_len - len, "remove_msdu = %u\n", + le32_to_cpu(htt_stats_buf->remove_msdu)); + len += scnprintf(buf + len, buf_len - len, "remove_mpdu = %u\n", + le32_to_cpu(htt_stats_buf->remove_mpdu)); + len += scnprintf(buf + len, buf_len - len, "remove_msdu_ttl = %u\n", + le32_to_cpu(htt_stats_buf->remove_msdu_ttl)); + len += scnprintf(buf + len, buf_len - len, "send_bar = %u\n", + le32_to_cpu(htt_stats_buf->send_bar)); + len += scnprintf(buf + len, buf_len - len, "bar_sync = %u\n", + le32_to_cpu(htt_stats_buf->bar_sync)); + len += scnprintf(buf + len, buf_len - len, "notify_mpdu = %u\n", + le32_to_cpu(htt_stats_buf->notify_mpdu)); + len += scnprintf(buf + len, buf_len - len, "sync_cmd = %u\n", + le32_to_cpu(htt_stats_buf->sync_cmd)); + len += scnprintf(buf + len, buf_len - len, "write_cmd = %u\n", + le32_to_cpu(htt_stats_buf->write_cmd)); + len += scnprintf(buf + len, buf_len - len, "hwsch_trigger = %u\n", + le32_to_cpu(htt_stats_buf->hwsch_trigger)); + len += scnprintf(buf + len, buf_len - len, "ack_tlv_proc = %u\n", + le32_to_cpu(htt_stats_buf->ack_tlv_proc)); + len += scnprintf(buf + len, buf_len - len, "gen_mpdu_cmd = %u\n", + le32_to_cpu(htt_stats_buf->gen_mpdu_cmd)); + len += scnprintf(buf + len, buf_len - len, "gen_list_cmd = %u\n", + le32_to_cpu(htt_stats_buf->gen_list_cmd)); + len += scnprintf(buf + len, buf_len - len, "remove_mpdu_cmd = %u\n", + le32_to_cpu(htt_stats_buf->remove_mpdu_cmd)); + len += scnprintf(buf + len, buf_len - len, "remove_mpdu_tried_cmd = %u\n", + le32_to_cpu(htt_stats_buf->remove_mpdu_tried_cmd)); + len += scnprintf(buf + len, buf_len - len, "mpdu_queue_stats_cmd = %u\n", + le32_to_cpu(htt_stats_buf->mpdu_queue_stats_cmd)); + len += scnprintf(buf + len, buf_len - len, "mpdu_head_info_cmd = %u\n", + le32_to_cpu(htt_stats_buf->mpdu_head_info_cmd)); + len += scnprintf(buf + len, buf_len - len, "msdu_flow_stats_cmd = %u\n", + le32_to_cpu(htt_stats_buf->msdu_flow_stats_cmd)); + len += scnprintf(buf + len, buf_len - len, "remove_msdu_cmd = %u\n", + le32_to_cpu(htt_stats_buf->remove_msdu_cmd)); + len += scnprintf(buf + len, buf_len - len, "remove_msdu_ttl_cmd = %u\n", + le32_to_cpu(htt_stats_buf->remove_msdu_ttl_cmd)); + len += scnprintf(buf + len, buf_len - len, "flush_cache_cmd = %u\n", + le32_to_cpu(htt_stats_buf->flush_cache_cmd)); + len += scnprintf(buf + len, buf_len - len, "update_mpduq_cmd = %u\n", + le32_to_cpu(htt_stats_buf->update_mpduq_cmd)); + len += scnprintf(buf + len, buf_len - len, "enqueue = %u\n", + le32_to_cpu(htt_stats_buf->enqueue)); + len += scnprintf(buf + len, buf_len - len, "enqueue_notify = %u\n", + le32_to_cpu(htt_stats_buf->enqueue_notify)); + len += scnprintf(buf + len, buf_len - len, "notify_mpdu_at_head = %u\n", + le32_to_cpu(htt_stats_buf->notify_mpdu_at_head)); + len += scnprintf(buf + len, buf_len - len, "notify_mpdu_state_valid = %u\n", + le32_to_cpu(htt_stats_buf->notify_mpdu_state_valid)); + len += scnprintf(buf + len, buf_len - len, "sched_udp_notify1 = %u\n", + le32_to_cpu(htt_stats_buf->sched_udp_notify1)); + len += scnprintf(buf + len, buf_len - len, "sched_udp_notify2 = %u\n", + le32_to_cpu(htt_stats_buf->sched_udp_notify2)); + len += scnprintf(buf + len, buf_len - len, "sched_nonudp_notify1 = %u\n", + le32_to_cpu(htt_stats_buf->sched_nonudp_notify1)); + len += scnprintf(buf + len, buf_len - len, "sched_nonudp_notify2 = %u\n\n", + le32_to_cpu(htt_stats_buf->sched_nonudp_notify2)); + + stats_req->buf_len = len; +} + static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, u16 tag, u16 len, const void *tag_buf, void *user_data) @@ -909,6 +1180,24 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, case HTT_STATS_HW_WAR_TAG: ath12k_htt_print_hw_war_tlv(tag_buf, len, stats_req); break; + case HTT_STATS_TX_TQM_CMN_TAG: + ath12k_htt_print_tx_tqm_cmn_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_TQM_ERROR_STATS_TAG: + ath12k_htt_print_tx_tqm_error_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_TQM_GEN_MPDU_TAG: + ath12k_htt_print_tx_tqm_gen_mpdu_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_TQM_LIST_MPDU_TAG: + ath12k_htt_print_tx_tqm_list_mpdu_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_TQM_LIST_MPDU_CNT_TAG: + ath12k_htt_print_tx_tqm_list_mpdu_cnt_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_TQM_PDEV_TAG: + ath12k_htt_print_tx_tqm_pdev_stats_tlv(tag_buf, len, stats_req); + break; default: break; } diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index 777a2e067a04..6294a082cf8a 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -127,6 +127,7 @@ enum ath12k_dbg_htt_ext_stats_type { ATH12K_DBG_HTT_EXT_STATS_PDEV_TX = 1, ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED = 4, ATH12K_DBG_HTT_EXT_STATS_PDEV_ERROR = 5, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TQM = 6, /* keep this last */ ATH12K_DBG_HTT_NUM_EXT_STATS, @@ -137,9 +138,15 @@ enum ath12k_dbg_htt_tlv_tag { HTT_STATS_TX_PDEV_UNDERRUN_TAG = 1, HTT_STATS_TX_PDEV_SIFS_TAG = 2, HTT_STATS_TX_PDEV_FLUSH_TAG = 3, + HTT_STATS_TX_TQM_GEN_MPDU_TAG = 11, + HTT_STATS_TX_TQM_LIST_MPDU_TAG = 12, + HTT_STATS_TX_TQM_LIST_MPDU_CNT_TAG = 13, + HTT_STATS_TX_TQM_CMN_TAG = 14, + HTT_STATS_TX_TQM_PDEV_TAG = 15, HTT_STATS_TX_PDEV_SCHEDULER_TXQ_STATS_TAG = 36, HTT_STATS_TX_SCHED_CMN_TAG = 37, HTT_STATS_SCHED_TXQ_CMD_POSTED_TAG = 39, + HTT_STATS_TX_TQM_ERROR_STATS_TAG = 43, HTT_STATS_SCHED_TXQ_CMD_REAPED_TAG = 44, HTT_STATS_HW_INTR_MISC_TAG = 54, HTT_STATS_HW_PDEV_ERRS_TAG = 56, @@ -462,4 +469,99 @@ struct ath12k_htt_hw_war_stats_tlv { DECLARE_FLEX_ARRAY(__le32, hw_wars); } __packed; +struct ath12k_htt_tx_tqm_cmn_stats_tlv { + __le32 mac_id__word; + __le32 max_cmdq_id; + __le32 list_mpdu_cnt_hist_intvl; + __le32 add_msdu; + __le32 q_empty; + __le32 q_not_empty; + __le32 drop_notification; + __le32 desc_threshold; + __le32 hwsch_tqm_invalid_status; + __le32 missed_tqm_gen_mpdus; + __le32 tqm_active_tids; + __le32 tqm_inactive_tids; + __le32 tqm_active_msduq_flows; + __le32 msduq_timestamp_updates; + __le32 msduq_updates_mpdu_head_info_cmd; + __le32 msduq_updates_emp_to_nonemp_status; + __le32 get_mpdu_head_info_cmds_by_query; + __le32 get_mpdu_head_info_cmds_by_tac; + __le32 gen_mpdu_cmds_by_query; + __le32 high_prio_q_not_empty; +} __packed; + +struct ath12k_htt_tx_tqm_error_stats_tlv { + __le32 q_empty_failure; + __le32 q_not_empty_failure; + __le32 add_msdu_failure; + __le32 tqm_cache_ctl_err; + __le32 tqm_soft_reset; + __le32 tqm_reset_num_in_use_link_descs; + __le32 tqm_reset_num_lost_link_descs; + __le32 tqm_reset_num_lost_host_tx_buf_cnt; + __le32 tqm_reset_num_in_use_internal_tqm; + __le32 tqm_reset_num_in_use_idle_link_rng; + __le32 tqm_reset_time_to_tqm_hang_delta_ms; + __le32 tqm_reset_recovery_time_ms; + __le32 tqm_reset_num_peers_hdl; + __le32 tqm_reset_cumm_dirty_hw_mpduq_cnt; + __le32 tqm_reset_cumm_dirty_hw_msduq_proc; + __le32 tqm_reset_flush_cache_cmd_su_cnt; + __le32 tqm_reset_flush_cache_cmd_other_cnt; + __le32 tqm_reset_flush_cache_cmd_trig_type; + __le32 tqm_reset_flush_cache_cmd_trig_cfg; + __le32 tqm_reset_flush_cmd_skp_status_null; +} __packed; + +struct ath12k_htt_tx_tqm_gen_mpdu_stats_tlv { + DECLARE_FLEX_ARRAY(__le32, gen_mpdu_end_reason); +} __packed; + +#define ATH12K_HTT_TX_TQM_MAX_LIST_MPDU_END_REASON 16 +#define ATH12K_HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS 16 + +struct ath12k_htt_tx_tqm_list_mpdu_stats_tlv { + DECLARE_FLEX_ARRAY(__le32, list_mpdu_end_reason); +} __packed; + +struct ath12k_htt_tx_tqm_list_mpdu_cnt_tlv { + DECLARE_FLEX_ARRAY(__le32, list_mpdu_cnt_hist); +} __packed; + +struct ath12k_htt_tx_tqm_pdev_stats_tlv { + __le32 msdu_count; + __le32 mpdu_count; + __le32 remove_msdu; + __le32 remove_mpdu; + __le32 remove_msdu_ttl; + __le32 send_bar; + __le32 bar_sync; + __le32 notify_mpdu; + __le32 sync_cmd; + __le32 write_cmd; + __le32 hwsch_trigger; + __le32 ack_tlv_proc; + __le32 gen_mpdu_cmd; + __le32 gen_list_cmd; + __le32 remove_mpdu_cmd; + __le32 remove_mpdu_tried_cmd; + __le32 mpdu_queue_stats_cmd; + __le32 mpdu_head_info_cmd; + __le32 msdu_flow_stats_cmd; + __le32 remove_msdu_cmd; + __le32 remove_msdu_ttl_cmd; + __le32 flush_cache_cmd; + __le32 update_mpduq_cmd; + __le32 enqueue; + __le32 enqueue_notify; + __le32 notify_mpdu_at_head; + __le32 notify_mpdu_state_valid; + __le32 sched_udp_notify1; + __le32 sched_udp_notify2; + __le32 sched_nonudp_notify1; + __le32 sched_nonudp_notify2; +} __packed; + #endif