mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-12-21 11:22:50 +00:00
1718 lines
52 KiB
Diff
1718 lines
52 KiB
Diff
From 99d27eb5bee5cd3049bea1f03f541a22114fdd22 Mon Sep 17 00:00:00 2001
|
|
From: Sowmiya Sree Elavalagan <ssreeela@codeaurora.org>
|
|
Date: Sat, 20 Aug 2022 20:33:58 +0530
|
|
Subject: [PATCH] ath12k: Add TPC stats support
|
|
|
|
TPC stats displays the per chain transmit power for a particular rate
|
|
index and chain mask configuration. Transmit power is minimum of
|
|
regulatory power, ctl power and rates defined for a particular rate idx.
|
|
|
|
Host sends a WMI request command to FW to display TPC stats. FW sends
|
|
the four values packed as a same event type. The fixed param tlv
|
|
indicates the event count and end of event. TPC config parameters along
|
|
with reg power array is the first event. Rates array is split as second
|
|
and third event since it can't be packed in single event. CTL power array
|
|
is the fourth event.
|
|
|
|
The array params which includes array length and type is present in the
|
|
master tlv. The actual content is present in a separate tlv as single buffer
|
|
packed one after the other. Parse the events and get the values for a
|
|
particular index.
|
|
|
|
TPC value is minimum of regulatory power limit, rates array and ctl array.
|
|
These arrays are obtained from FW in form of multiple(4) wmi events.
|
|
The events are parsed and saved. Min of these are computed based on
|
|
number of chains, nss, pream/modes, mcs and tx beamforming enabled/disabled.
|
|
Based on the above variant 4 sets of data are displayed.
|
|
0- SU
|
|
1- SU + TXBF
|
|
2- MU
|
|
3- MU + TXBF
|
|
Type of display required is set in /sys/kernel/debug/ieee80211/phyX/ath12k/tpc_stats_type
|
|
By default tpc_stats_type is set 0 and SU op is displayed
|
|
root@OpenWrt:/# cat sys/kernel/debug/ieee80211/phy2/ath12k/tpc_stats
|
|
*************** TPC config **************
|
|
* powers are in 0.25 dBm steps
|
|
reg domain-22 chan freq-2462
|
|
power limit-120 max reg-domain Power-120
|
|
No.of tx chain-4 No.of rates-1196
|
|
**************** MU ****************
|
|
TPC values for Active chains
|
|
Rate idx Preamble Rate code 1-Chain 2-Chain 3-Chain 4-Chain
|
|
76 VHT20 0x300 88 88 88 88
|
|
77 VHT20 0x301 88 88 88 88
|
|
78 VHT20 0x302 88 88 88 88
|
|
79 VHT20 0x303 88 88 88 88
|
|
80 VHT20 0x304 88 88 88 88
|
|
81 VHT20 0x305 88 88 88 88
|
|
82 VHT20 0x306 88 88 88 88
|
|
83 VHT20 0x307 84 84 84 84
|
|
84 VHT20 0x308 80 80 80 80
|
|
85 VHT20 0x309 76 76 76 76
|
|
....
|
|
|
|
Signed-off-by: Sowmiya Sree Elavalagan <ssreeela@codeaurora.org>
|
|
Signed-off-by: Sriram R <srirrama@codeaurora.org>
|
|
Signed-off-by: Sivashankari Madhavan <quic_sivamadh@quicinc.com>
|
|
---
|
|
drivers/net/wireless/ath/ath12k/core.h | 9 +-
|
|
drivers/net/wireless/ath/ath12k/debugfs.c | 746 ++++++++++++++++++++++
|
|
drivers/net/wireless/ath/ath12k/debugfs.h | 61 ++
|
|
drivers/net/wireless/ath/ath12k/wmi.c | 462 ++++++++++++++
|
|
drivers/net/wireless/ath/ath12k/wmi.h | 249 +++++++-
|
|
5 files changed, 1525 insertions(+), 2 deletions(-)
|
|
|
|
--- a/drivers/net/wireless/ath/ath12k/core.h
|
|
+++ b/drivers/net/wireless/ath/ath12k/core.h
|
|
@@ -68,8 +68,9 @@ enum wme_ac {
|
|
};
|
|
|
|
#define ATH12K_HT_MCS_MAX 7
|
|
-#define ATH12K_VHT_MCS_MAX 9
|
|
+#define ATH12K_VHT_MCS_MAX 11
|
|
#define ATH12K_HE_MCS_MAX 11
|
|
+#define ATH12K_HE_EXTRA_MCS_MAX 13
|
|
#define ATH12K_EHT_MCS_MAX 15
|
|
|
|
enum ath12k_crypt_mode {
|
|
@@ -784,6 +785,12 @@ struct ath12k {
|
|
struct cfg80211_chan_def awgn_chandef;
|
|
u32 chan_bw_interference_bitmap;
|
|
bool awgn_intf_handling_in_prog;
|
|
+
|
|
+ u8 tpc_stats_type;
|
|
+ /* tpc_stats ptr is protected by data lock */
|
|
+ struct wmi_tpc_stats_event *tpc_stats;
|
|
+ struct completion tpc_complete;
|
|
+ bool tpc_request;
|
|
};
|
|
|
|
struct ath12k_band_cap {
|
|
--- a/drivers/net/wireless/ath/ath12k/debugfs.c
|
|
+++ b/drivers/net/wireless/ath/ath12k/debugfs.c
|
|
@@ -1327,6 +1327,741 @@ static const struct file_operations fops
|
|
.open = simple_open
|
|
};
|
|
|
|
+static int ath12k_get_tpc_ctl_mode_idx(struct wmi_tpc_stats_event *tpc_stats,
|
|
+ enum wmi_tpc_pream_bw pream_bw, int *mode_idx)
|
|
+{
|
|
+ u8 band;
|
|
+
|
|
+ band = (((tpc_stats->tpc_config.chan_freq) > 5920) ? NL80211_BAND_6GHZ :
|
|
+ (((tpc_stats->tpc_config.chan_freq) > 4800) ? NL80211_BAND_5GHZ : NL80211_BAND_2GHZ));
|
|
+
|
|
+ if (band == NL80211_BAND_5GHZ || band == NL80211_BAND_6GHZ) {
|
|
+ switch (pream_bw) {
|
|
+ case WMI_TPC_PREAM_HT20:
|
|
+ case WMI_TPC_PREAM_VHT20:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT_VHT20_5G_6G;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_HE20:
|
|
+ case WMI_TPC_PREAM_EHT20:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT20_5G_6G;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_HT40:
|
|
+ case WMI_TPC_PREAM_VHT40:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT_VHT40_5G_6G;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_HE40:
|
|
+ case WMI_TPC_PREAM_EHT40:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT40_5G_6G;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_VHT80:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_VHT80_5G_6G;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_EHT60:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT80_SU_PUNC20;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_HE80:
|
|
+ case WMI_TPC_PREAM_EHT80:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT80_5G_6G;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_VHT160:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_VHT160_5G_6G;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_EHT120:
|
|
+ case WMI_TPC_PREAM_EHT140:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT160_SU_PUNC20;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_HE160:
|
|
+ case WMI_TPC_PREAM_EHT160:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT160_5G_6G;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_EHT200:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC120;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_EHT240:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC80;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_EHT280:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC40;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_EHT320:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT320_5G_6G;
|
|
+ break;
|
|
+ default:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_LEGACY_5G_6G; /* for 5G and 6G, default case will be for OFDM */
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ switch (pream_bw) {
|
|
+ case WMI_TPC_PREAM_OFDM:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_LEGACY_2G;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_HT20:
|
|
+ case WMI_TPC_PREAM_VHT20:
|
|
+ case WMI_TPC_PREAM_HE20:
|
|
+ case WMI_TPC_PREAM_EHT20:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT20_2G;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_HT40:
|
|
+ case WMI_TPC_PREAM_VHT40:
|
|
+ case WMI_TPC_PREAM_HE40:
|
|
+ case WMI_TPC_PREAM_EHT40:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT40_2G;
|
|
+ break;
|
|
+ default:
|
|
+ *mode_idx = ATH12K_TPC_STATS_CTL_MODE_CCK_2G; //for 2G, default case will be CCK
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static s16 ath12k_tpc_get_rate(struct ath12k *ar,
|
|
+ struct wmi_tpc_stats_event *tpc_stats,
|
|
+ u32 rate_idx, u32 num_chains, u32 rate_code,
|
|
+ enum wmi_tpc_pream_bw pream_bw,
|
|
+ enum ath12k_dbg_tpc_stats_type type,
|
|
+ u32 eht_rate_idx)
|
|
+{
|
|
+ s8 rates_ctl_min, tpc_ctl;
|
|
+ u8 chain_idx, stm_idx, num_streams;
|
|
+ s16 rates, tpc, reg_pwr;
|
|
+ u32 tot_nss, tot_modes, txbf_on_off;
|
|
+ u32 index_offset1, index_offset2, index_offset3;
|
|
+ int mode, ret, txbf_enabled;
|
|
+ bool is_mu;
|
|
+
|
|
+ num_streams = 1 + ATH12K_HW_NSS(rate_code);
|
|
+ chain_idx = num_chains - 1;
|
|
+ stm_idx = num_streams - 1;
|
|
+ mode = -1;
|
|
+
|
|
+ ret = ath12k_get_tpc_ctl_mode_idx(tpc_stats, pream_bw, &mode);
|
|
+ if (ret) {
|
|
+ ath12k_warn(ar->ab, "Invalid mode index received\n");
|
|
+ tpc = TPC_INVAL;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (num_chains < num_streams) {
|
|
+ tpc = TPC_INVAL;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (__le32_to_cpu(tpc_stats->tpc_config.num_tx_chain) <= 1) {
|
|
+ tpc = TPC_INVAL;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (type == ATH12K_DBG_TPC_STATS_MU_WITH_TXBF ||
|
|
+ type == ATH12K_DBG_TPC_STATS_SU_WITH_TXBF)
|
|
+ txbf_enabled = 1;
|
|
+ else
|
|
+ txbf_enabled = 0;
|
|
+
|
|
+ if (type == ATH12K_DBG_TPC_STATS_MU_WITH_TXBF ||
|
|
+ type == ATH12K_DBG_TPC_STATS_MU) {
|
|
+ is_mu = true;
|
|
+ } else {
|
|
+ is_mu = false;
|
|
+ }
|
|
+
|
|
+ /* Below is the min calculation of ctl array, rates array and
|
|
+ * regulator power table. tpc is minimum of all 3
|
|
+ */
|
|
+ if (pream_bw >= WMI_TPC_PREAM_EHT20 && pream_bw <= WMI_TPC_PREAM_EHT320) {
|
|
+ if (is_mu) {
|
|
+ rates = FIELD_GET(ATH12K_TPC_RATE_ARRAY_MU,
|
|
+ le16_to_cpu (tpc_stats->rates_array2.rate_array[eht_rate_idx]));
|
|
+ } else {
|
|
+ rates = FIELD_GET(ATH12K_TPC_RATE_ARRAY_SU,
|
|
+ le16_to_cpu (tpc_stats->rates_array2.rate_array[eht_rate_idx]));
|
|
+ }
|
|
+ } else {
|
|
+ if (is_mu) {
|
|
+ rates = FIELD_GET(ATH12K_TPC_RATE_ARRAY_MU,
|
|
+ le16_to_cpu (tpc_stats->rates_array1.rate_array[rate_idx]));
|
|
+ } else {
|
|
+ rates = FIELD_GET(ATH12K_TPC_RATE_ARRAY_SU,
|
|
+ le16_to_cpu (tpc_stats->rates_array1.rate_array[rate_idx]));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (tpc_stats->tlvs_rcvd & WMI_TPC_CTL_PWR_ARRAY) {
|
|
+ tot_nss = tpc_stats->ctl_array.tpc_ctl_pwr.d1;
|
|
+ tot_modes = tpc_stats->ctl_array.tpc_ctl_pwr.d2;
|
|
+ txbf_on_off = tpc_stats->ctl_array.tpc_ctl_pwr.d3;
|
|
+ index_offset1 = txbf_on_off * tot_modes * tot_nss;
|
|
+ index_offset2 = tot_modes * tot_nss;
|
|
+ index_offset3 = tot_nss;
|
|
+
|
|
+ tpc_ctl = *(tpc_stats->ctl_array.ctl_pwr_table +
|
|
+ chain_idx * index_offset1 + txbf_enabled * index_offset2
|
|
+ + mode * index_offset3 + stm_idx);
|
|
+ } else {
|
|
+ tpc_ctl = TPC_MAX;
|
|
+ ath12k_info(ar->ab,
|
|
+ "ctl array for tpc stats not received from fw\n");
|
|
+ }
|
|
+
|
|
+ rates_ctl_min = min_t(s16, rates, tpc_ctl);
|
|
+
|
|
+ reg_pwr = le16_to_cpu(tpc_stats->max_reg_allowed_power.reg_pwr_array[chain_idx]);
|
|
+
|
|
+ if (reg_pwr < 0)
|
|
+ reg_pwr = TPC_INVAL;
|
|
+
|
|
+ tpc = min_t(s16, rates_ctl_min, reg_pwr);
|
|
+
|
|
+ /* MODULATION_LIMIT is the maximum power limit,tpc should not exceed
|
|
+ * modulation limt even if min tpc of all three array is greater
|
|
+ * modulation limit
|
|
+ */
|
|
+ tpc = min_t(s16, tpc, MODULATION_LIMIT);
|
|
+
|
|
+out:
|
|
+ return tpc;
|
|
+}
|
|
+
|
|
+u16 ath12k_get_ratecode(u16 pream_idx, u16 nss, u16 mcs_rate)
|
|
+{
|
|
+ u16 mode_type = ~0;
|
|
+
|
|
+ /* Below assignments are just for printing purpose only */
|
|
+ switch (pream_idx) {
|
|
+ case WMI_TPC_PREAM_CCK:
|
|
+ mode_type = WMI_RATE_PREAMBLE_CCK;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_OFDM:
|
|
+ mode_type = WMI_RATE_PREAMBLE_OFDM;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_HT20:
|
|
+ case WMI_TPC_PREAM_HT40:
|
|
+ mode_type = WMI_RATE_PREAMBLE_HT;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_VHT20:
|
|
+ case WMI_TPC_PREAM_VHT40:
|
|
+ case WMI_TPC_PREAM_VHT80:
|
|
+ case WMI_TPC_PREAM_VHT160:
|
|
+ mode_type = WMI_RATE_PREAMBLE_VHT;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_HE20:
|
|
+ case WMI_TPC_PREAM_HE40:
|
|
+ case WMI_TPC_PREAM_HE80:
|
|
+ case WMI_TPC_PREAM_HE160:
|
|
+ mode_type = WMI_RATE_PREAMBLE_HE;
|
|
+ break;
|
|
+ case WMI_TPC_PREAM_EHT20:
|
|
+ case WMI_TPC_PREAM_EHT40:
|
|
+ case WMI_TPC_PREAM_EHT60:
|
|
+ case WMI_TPC_PREAM_EHT80:
|
|
+ case WMI_TPC_PREAM_EHT120:
|
|
+ case WMI_TPC_PREAM_EHT140:
|
|
+ case WMI_TPC_PREAM_EHT160:
|
|
+ case WMI_TPC_PREAM_EHT200:
|
|
+ case WMI_TPC_PREAM_EHT240:
|
|
+ case WMI_TPC_PREAM_EHT280:
|
|
+ case WMI_TPC_PREAM_EHT320:
|
|
+ mode_type = WMI_RATE_PREAMBLE_EHT;
|
|
+ if (mcs_rate == 0 || mcs_rate == 1)
|
|
+ mcs_rate += 14;
|
|
+ else
|
|
+ mcs_rate -= 2;
|
|
+ break;
|
|
+ default:
|
|
+ return mode_type;
|
|
+ }
|
|
+
|
|
+ return ((mode_type << 8) | ((nss & 0x7) << 5) | (mcs_rate & 0x1F));
|
|
+}
|
|
+
|
|
+static bool ath12k_he_supports_extra_mcs(struct ath12k *ar, int freq)
|
|
+{
|
|
+ struct ath12k_pdev_cap *cap = &ar->pdev->cap;
|
|
+ struct ath12k_band_cap *cap_band;
|
|
+ bool extra_mcs_supported;
|
|
+
|
|
+ if (freq <= ATH12K_2G_MAX_FREQUENCY)
|
|
+ cap_band = &cap->band[NL80211_BAND_2GHZ];
|
|
+ else if (freq <= ATH12K_5G_MAX_FREQUENCY)
|
|
+ cap_band = &cap->band[NL80211_BAND_5GHZ];
|
|
+ else
|
|
+ cap_band = &cap->band[NL80211_BAND_6GHZ];
|
|
+
|
|
+ extra_mcs_supported = FIELD_GET(HE_EXTRA_MCS_SUPPORT, cap_band->he_cap_info[1]);
|
|
+ return extra_mcs_supported;
|
|
+}
|
|
+
|
|
+static int ath12k_tpc_fill_pream(struct ath12k *ar, char *buf, int buf_len, int len,
|
|
+ enum wmi_tpc_pream_bw pream_bw, u32 max_rix, int max_nss, int max_rates,
|
|
+ int pream_type, enum ath12k_dbg_tpc_stats_type tpc_type,
|
|
+ int rate_idx, int eht_rate_idx)
|
|
+{
|
|
+ int nss, rates, chains;
|
|
+ u8 active_tx_chains;
|
|
+ u16 rate_code;
|
|
+ s16 tpc;
|
|
+ struct wmi_tpc_stats_event *tpc_stats = ar->tpc_stats;
|
|
+
|
|
+ static const char *const pream_str[] = {
|
|
+ [WMI_TPC_PREAM_CCK] = "CCK",
|
|
+ [WMI_TPC_PREAM_OFDM] = "OFDM",
|
|
+ [WMI_TPC_PREAM_HT20] = "HT20",
|
|
+ [WMI_TPC_PREAM_HT40] = "HT40",
|
|
+ [WMI_TPC_PREAM_VHT20] = "VHT20",
|
|
+ [WMI_TPC_PREAM_VHT40] = "VHT40",
|
|
+ [WMI_TPC_PREAM_VHT80] = "VHT80",
|
|
+ [WMI_TPC_PREAM_VHT160] = "VHT160",
|
|
+ [WMI_TPC_PREAM_HE20] = "HE20",
|
|
+ [WMI_TPC_PREAM_HE40] = "HE40",
|
|
+ [WMI_TPC_PREAM_HE80] = "HE80",
|
|
+ [WMI_TPC_PREAM_HE160] = "HE160",
|
|
+ [WMI_TPC_PREAM_EHT20] = "EHT20",
|
|
+ [WMI_TPC_PREAM_EHT40] = "EHT40",
|
|
+ [WMI_TPC_PREAM_EHT60] = "EHT60",
|
|
+ [WMI_TPC_PREAM_EHT80] = "EHT80",
|
|
+ [WMI_TPC_PREAM_EHT120] = "EHT120",
|
|
+ [WMI_TPC_PREAM_EHT140] = "EHT140",
|
|
+ [WMI_TPC_PREAM_EHT160] = "EHT160",
|
|
+ [WMI_TPC_PREAM_EHT200] = "EHT200",
|
|
+ [WMI_TPC_PREAM_EHT240] = "EHT240",
|
|
+ [WMI_TPC_PREAM_EHT280] = "EHT280",
|
|
+ [WMI_TPC_PREAM_EHT320] = "EHT320"};
|
|
+
|
|
+ active_tx_chains = ar->num_tx_chains;
|
|
+
|
|
+ for (nss = 0; nss < max_nss; nss++) {
|
|
+ for (rates = 0; rates < max_rates; rates++, rate_idx++, max_rix++) {
|
|
+ /* FW send extra MCS(10&11) for VHT and HE rates,
|
|
+ * this is not used. Hence skipping it here
|
|
+ */
|
|
+ if (pream_type == WMI_RATE_PREAMBLE_VHT &&
|
|
+ rates > ATH12K_VHT_MCS_MAX)
|
|
+ continue;
|
|
+
|
|
+ if (pream_type == WMI_RATE_PREAMBLE_HE &&
|
|
+ rates > ATH12K_HE_MCS_MAX)
|
|
+ continue;
|
|
+
|
|
+ if (pream_type == WMI_RATE_PREAMBLE_EHT &&
|
|
+ rates > ATH12K_EHT_MCS_MAX)
|
|
+ continue;
|
|
+
|
|
+ rate_code = ath12k_get_ratecode(pream_bw, nss, rates);
|
|
+ len += scnprintf(buf + len, buf_len - len,
|
|
+ "%d\t %s\t 0x%03x\t", max_rix,
|
|
+ pream_str[pream_bw], rate_code);
|
|
+
|
|
+ for (chains = 0; chains < active_tx_chains; chains++) {
|
|
+ if (nss > chains) {
|
|
+ len += scnprintf(buf + len,
|
|
+ buf_len - len,
|
|
+ "\t%s", "NA");
|
|
+ } else {
|
|
+ tpc = ath12k_tpc_get_rate(ar, tpc_stats, rate_idx,
|
|
+ chains + 1, rate_code,
|
|
+ pream_bw, tpc_type, eht_rate_idx);
|
|
+
|
|
+ if (tpc == TPC_INVAL) {
|
|
+ len += scnprintf(buf + len,
|
|
+ buf_len - len, "\tNA");
|
|
+ } else {
|
|
+ len += scnprintf(buf + len,
|
|
+ buf_len - len, "\t%d",
|
|
+ tpc);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ len += scnprintf(buf + len, buf_len - len, "\n");
|
|
+
|
|
+ if (pream_type == WMI_RATE_PREAMBLE_EHT)
|
|
+ /*For fetching the next eht rates pwr from rates array2*/
|
|
+ ++eht_rate_idx;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static int ath12k_tpc_stats_print(struct ath12k *ar,
|
|
+ struct wmi_tpc_stats_event *tpc_stats,
|
|
+ char *buf, size_t len, enum ath12k_dbg_tpc_stats_type type)
|
|
+{
|
|
+ u32 i, j = 1, eht_idx = 0, pream_idx = 0, rate_pream_idx = 0, total_rates = 0, max_rix = 0;
|
|
+ u8 nss, active_tx_chains;
|
|
+ size_t buf_len = ATH12K_TPC_STATS_BUF_SIZE;
|
|
+ bool he_ext_mcs;
|
|
+ static const char *const type_str[ATH12K_DBG_TPC_MAX_STATS] = {
|
|
+ [ATH12K_DBG_TPC_STATS_SU] = "SU",
|
|
+ [ATH12K_DBG_TPC_STATS_SU_WITH_TXBF] = "SU WITH TXBF",
|
|
+ [ATH12K_DBG_TPC_STATS_MU] = "MU",
|
|
+ [ATH12K_DBG_TPC_STATS_MU_WITH_TXBF] = "MU WITH TXBF"};
|
|
+
|
|
+ u8 max_rates[WMI_TPC_PREAM_MAX] = {
|
|
+ [WMI_TPC_PREAM_CCK] = ATH12K_CCK_RATES,
|
|
+ [WMI_TPC_PREAM_OFDM] = ATH12K_OFDM_RATES,
|
|
+ [WMI_TPC_PREAM_HT20] = ATH12K_HT_RATES,
|
|
+ [WMI_TPC_PREAM_HT40] = ATH12K_HT_RATES,
|
|
+ [WMI_TPC_PREAM_VHT20] = ATH12K_VHT_RATES,
|
|
+ [WMI_TPC_PREAM_VHT40] = ATH12K_VHT_RATES,
|
|
+ [WMI_TPC_PREAM_VHT80] = ATH12K_VHT_RATES,
|
|
+ [WMI_TPC_PREAM_VHT160] = ATH12K_VHT_RATES,
|
|
+ [WMI_TPC_PREAM_HE20] = ATH12K_HE_RATES,
|
|
+ [WMI_TPC_PREAM_HE40] = ATH12K_HE_RATES,
|
|
+ [WMI_TPC_PREAM_HE80] = ATH12K_HE_RATES,
|
|
+ [WMI_TPC_PREAM_HE160] = ATH12K_HE_RATES,
|
|
+ [WMI_TPC_PREAM_EHT20] = ATH12K_EHT_RATES,
|
|
+ [WMI_TPC_PREAM_EHT40] = ATH12K_EHT_RATES,
|
|
+ [WMI_TPC_PREAM_EHT60] = ATH12K_EHT_RATES,
|
|
+ [WMI_TPC_PREAM_EHT80] = ATH12K_EHT_RATES,
|
|
+ [WMI_TPC_PREAM_EHT120] = ATH12K_EHT_RATES,
|
|
+ [WMI_TPC_PREAM_EHT140] = ATH12K_EHT_RATES,
|
|
+ [WMI_TPC_PREAM_EHT160] = ATH12K_EHT_RATES,
|
|
+ [WMI_TPC_PREAM_EHT200] = ATH12K_EHT_RATES,
|
|
+ [WMI_TPC_PREAM_EHT240] = ATH12K_EHT_RATES,
|
|
+ [WMI_TPC_PREAM_EHT280] = ATH12K_EHT_RATES,
|
|
+ [WMI_TPC_PREAM_EHT320] = ATH12K_EHT_RATES};
|
|
+
|
|
+ u8 max_nss[WMI_TPC_PREAM_MAX] = {
|
|
+ [WMI_TPC_PREAM_CCK] = ATH12K_NSS_1,
|
|
+ [WMI_TPC_PREAM_OFDM] = ATH12K_NSS_1,
|
|
+ [WMI_TPC_PREAM_HT20] = ATH12K_NSS_4,
|
|
+ [WMI_TPC_PREAM_HT40] = ATH12K_NSS_4,
|
|
+ [WMI_TPC_PREAM_VHT20] = ATH12K_NSS_8,
|
|
+ [WMI_TPC_PREAM_VHT40] = ATH12K_NSS_8,
|
|
+ [WMI_TPC_PREAM_VHT80] = ATH12K_NSS_8,
|
|
+ [WMI_TPC_PREAM_VHT160] = ATH12K_NSS_4,
|
|
+ [WMI_TPC_PREAM_HE20] = ATH12K_NSS_8,
|
|
+ [WMI_TPC_PREAM_HE40] = ATH12K_NSS_8,
|
|
+ [WMI_TPC_PREAM_HE80] = ATH12K_NSS_8,
|
|
+ [WMI_TPC_PREAM_HE160] = ATH12K_NSS_4,
|
|
+ [WMI_TPC_PREAM_EHT20] = ATH12K_NSS_4,
|
|
+ [WMI_TPC_PREAM_EHT40] = ATH12K_NSS_4,
|
|
+ [WMI_TPC_PREAM_EHT60] = ATH12K_NSS_4,
|
|
+ [WMI_TPC_PREAM_EHT80] = ATH12K_NSS_4,
|
|
+ [WMI_TPC_PREAM_EHT120] = ATH12K_NSS_4,
|
|
+ [WMI_TPC_PREAM_EHT140] = ATH12K_NSS_4,
|
|
+ [WMI_TPC_PREAM_EHT160] = ATH12K_NSS_4,
|
|
+ [WMI_TPC_PREAM_EHT200] = ATH12K_NSS_4,
|
|
+ [WMI_TPC_PREAM_EHT240] = ATH12K_NSS_4,
|
|
+ [WMI_TPC_PREAM_EHT280] = ATH12K_NSS_4,
|
|
+ [WMI_TPC_PREAM_EHT320] = ATH12K_NSS_4};
|
|
+
|
|
+ u16 rate_idx[WMI_TPC_PREAM_MAX] = {0},
|
|
+ eht_rate_idx[WMI_TPC_PREAM_MAX] = {0};
|
|
+
|
|
+ static const u8 pream_type[WMI_TPC_PREAM_MAX] = {
|
|
+ [WMI_TPC_PREAM_CCK] = WMI_RATE_PREAMBLE_CCK,
|
|
+ [WMI_TPC_PREAM_OFDM] = WMI_RATE_PREAMBLE_OFDM,
|
|
+ [WMI_TPC_PREAM_HT20] = WMI_RATE_PREAMBLE_HT,
|
|
+ [WMI_TPC_PREAM_HT40] = WMI_RATE_PREAMBLE_HT,
|
|
+ [WMI_TPC_PREAM_VHT20] = WMI_RATE_PREAMBLE_VHT,
|
|
+ [WMI_TPC_PREAM_VHT40] = WMI_RATE_PREAMBLE_VHT,
|
|
+ [WMI_TPC_PREAM_VHT80] = WMI_RATE_PREAMBLE_VHT,
|
|
+ [WMI_TPC_PREAM_VHT160] = WMI_RATE_PREAMBLE_VHT,
|
|
+ [WMI_TPC_PREAM_HE20] = WMI_RATE_PREAMBLE_HE,
|
|
+ [WMI_TPC_PREAM_HE40] = WMI_RATE_PREAMBLE_HE,
|
|
+ [WMI_TPC_PREAM_HE80] = WMI_RATE_PREAMBLE_HE,
|
|
+ [WMI_TPC_PREAM_HE160] = WMI_RATE_PREAMBLE_HE,
|
|
+ [WMI_TPC_PREAM_EHT20] = WMI_RATE_PREAMBLE_EHT,
|
|
+ [WMI_TPC_PREAM_EHT40] = WMI_RATE_PREAMBLE_EHT,
|
|
+ [WMI_TPC_PREAM_EHT60] = WMI_RATE_PREAMBLE_EHT,
|
|
+ [WMI_TPC_PREAM_EHT80] = WMI_RATE_PREAMBLE_EHT,
|
|
+ [WMI_TPC_PREAM_EHT120] = WMI_RATE_PREAMBLE_EHT,
|
|
+ [WMI_TPC_PREAM_EHT140] = WMI_RATE_PREAMBLE_EHT,
|
|
+ [WMI_TPC_PREAM_EHT160] = WMI_RATE_PREAMBLE_EHT,
|
|
+ [WMI_TPC_PREAM_EHT200] = WMI_RATE_PREAMBLE_EHT,
|
|
+ [WMI_TPC_PREAM_EHT240] = WMI_RATE_PREAMBLE_EHT,
|
|
+ [WMI_TPC_PREAM_EHT280] = WMI_RATE_PREAMBLE_EHT,
|
|
+ [WMI_TPC_PREAM_EHT320] = WMI_RATE_PREAMBLE_EHT};
|
|
+
|
|
+ active_tx_chains = ar->num_tx_chains;
|
|
+ he_ext_mcs = ath12k_he_supports_extra_mcs(ar, tpc_stats->tpc_config.chan_freq);
|
|
+
|
|
+ /* mcs 12&13 is sent by FW for certain HWs in rate array, skipping it as
|
|
+ * it is not supported
|
|
+ */
|
|
+ if (he_ext_mcs) {
|
|
+ for (i = WMI_TPC_PREAM_HE20; i <= WMI_TPC_PREAM_HE160; ++i)
|
|
+ max_rates[i] = ATH12K_HE_RATES_WITH_EXTRA_MCS;
|
|
+ }
|
|
+
|
|
+ if (type == ATH12K_DBG_TPC_STATS_MU ||
|
|
+ type == ATH12K_DBG_TPC_STATS_MU_WITH_TXBF) {
|
|
+ pream_idx = WMI_TPC_PREAM_VHT20;
|
|
+
|
|
+ for (i = WMI_TPC_PREAM_CCK; i <= WMI_TPC_PREAM_HT40; ++i) {
|
|
+ max_rix += max_nss[i] * max_rates[i];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Enumerate all the rate indices */
|
|
+ for (i = rate_pream_idx + 1 ; i < WMI_TPC_PREAM_MAX; i++) {
|
|
+ nss = (max_nss[i - 1] < tpc_stats->tpc_config.num_tx_chain ?
|
|
+ max_nss[i - 1] : tpc_stats->tpc_config.num_tx_chain);
|
|
+
|
|
+ rate_idx[i] = rate_idx[i - 1] + max_rates[i - 1] * nss;
|
|
+
|
|
+ if (pream_type[i] == WMI_RATE_PREAMBLE_EHT) {
|
|
+ eht_rate_idx[j] = eht_rate_idx[j - 1] + max_rates[i] * nss;
|
|
+ /*For Filling the next eht_rate_idx for fetch rates pwr info rates array2*/
|
|
+ ++j;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (i = 0 ; i < WMI_TPC_PREAM_MAX; i++) {
|
|
+ nss = (max_nss[i] < tpc_stats->tpc_config.num_tx_chain ?
|
|
+ max_nss[i] : tpc_stats->tpc_config.num_tx_chain);
|
|
+ total_rates += max_rates[i] * nss;
|
|
+ }
|
|
+
|
|
+ len += scnprintf(buf + len, buf_len - len,
|
|
+ "No.of rates-%d\n", total_rates);
|
|
+
|
|
+ len += scnprintf(buf + len, buf_len - len,
|
|
+ "**************** %s ****************\n",
|
|
+ type_str[type]);
|
|
+ len += scnprintf(buf + len, buf_len - len,
|
|
+ "\t\t\t\tTPC values for Active chains\n");
|
|
+ len += scnprintf(buf + len, buf_len - len,
|
|
+ "Rate idx Preamble Rate code");
|
|
+
|
|
+ for (i = 1; i <= active_tx_chains; ++i) {
|
|
+ len += scnprintf(buf + len, buf_len - len,
|
|
+ "\t%d-Chain", i);
|
|
+ }
|
|
+
|
|
+ len += scnprintf(buf + len, buf_len - len, "\n");
|
|
+
|
|
+ for (i = pream_idx; i < WMI_TPC_PREAM_MAX; i++) {
|
|
+ if (tpc_stats->tpc_config.chan_freq <= 2483) {
|
|
+ if (i == WMI_TPC_PREAM_VHT80 ||
|
|
+ i == WMI_TPC_PREAM_VHT160 ||
|
|
+ i == WMI_TPC_PREAM_HE80 ||
|
|
+ i == WMI_TPC_PREAM_HE160 ||
|
|
+ (i >= WMI_TPC_PREAM_EHT60 &&
|
|
+ i <= WMI_TPC_PREAM_EHT320)) {
|
|
+ max_rix += max_nss[i] * max_rates[i];
|
|
+ continue;
|
|
+ }
|
|
+ } else {
|
|
+ if (i == WMI_TPC_PREAM_CCK) {
|
|
+ max_rix += max_rates[i];
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ nss = (max_nss[i] < ar->num_tx_chains ? max_nss[i] : ar->num_tx_chains);
|
|
+
|
|
+ if (!(tpc_stats->tpc_config.caps & (1 << ATH12K_TPC_STATS_SUPPORT_BE_PUNC))) {
|
|
+ if (i == WMI_TPC_PREAM_EHT60 || i == WMI_TPC_PREAM_EHT120 ||
|
|
+ i == WMI_TPC_PREAM_EHT140 || i == WMI_TPC_PREAM_EHT200 ||
|
|
+ i == WMI_TPC_PREAM_EHT240 || i == WMI_TPC_PREAM_EHT280) {
|
|
+ max_rix += max_nss[i] * max_rates[i];
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ len = ath12k_tpc_fill_pream(ar, buf, buf_len, len, i, max_rix, nss,
|
|
+ max_rates[i], pream_type[i],
|
|
+ type, rate_idx[i], eht_rate_idx[eht_idx]);
|
|
+
|
|
+ if (pream_type[i] == WMI_RATE_PREAMBLE_EHT)
|
|
+ /*For fetch the next index eht rates from rates array2*/
|
|
+ ++eht_idx;
|
|
+
|
|
+ max_rix += max_nss[i] * max_rates[i];
|
|
+ }
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static void ath12k_tpc_stats_fill(struct ath12k *ar,
|
|
+ struct wmi_tpc_stats_event *tpc_stats,
|
|
+ char *buf)
|
|
+{
|
|
+ struct wmi_tpc_configs *tpc;
|
|
+ size_t len = 0;
|
|
+ size_t buf_len = ATH12K_TPC_STATS_BUF_SIZE;
|
|
+
|
|
+ spin_lock_bh(&ar->data_lock);
|
|
+ if (!tpc_stats) {
|
|
+ ath12k_warn(ar->ab, "failed to find tpc stats\n");
|
|
+ goto unlock;
|
|
+ }
|
|
+
|
|
+ tpc = &tpc_stats->tpc_config;
|
|
+ len += scnprintf(buf + len, buf_len - len, "\n");
|
|
+ len += scnprintf(buf + len, buf_len - len,
|
|
+ "*************** TPC config **************\n");
|
|
+ len += scnprintf(buf + len, buf_len - len,
|
|
+ "* powers are in 0.25 dBm steps\n");
|
|
+ len += scnprintf(buf + len, buf_len - len,
|
|
+ "reg domain-%d\t\tchan freq-%d\n",
|
|
+ tpc->reg_domain, tpc->chan_freq);
|
|
+ len += scnprintf(buf + len, buf_len - len,
|
|
+ "power limit-%d\t\tmax reg-domain Power-%d\n",
|
|
+ (tpc->twice_max_reg_power) / 2, tpc->power_limit);
|
|
+ len += scnprintf(buf + len, buf_len - len,
|
|
+ "No.of tx chain-%d\t",
|
|
+ ar->num_tx_chains);
|
|
+
|
|
+ ath12k_tpc_stats_print(ar, tpc_stats, buf, len,
|
|
+ ar->tpc_stats_type);
|
|
+
|
|
+unlock:
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+}
|
|
+
|
|
+static int ath12k_debug_tpc_stats_request(struct ath12k *ar)
|
|
+{
|
|
+ int ret;
|
|
+ unsigned long time_left;
|
|
+ struct ath12k_base *ab = ar->ab;
|
|
+
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
+
|
|
+ reinit_completion(&ar->tpc_complete);
|
|
+
|
|
+ spin_lock_bh(&ar->data_lock);
|
|
+ ar->tpc_request = true;
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+
|
|
+ ret = ath12k_wmi_pdev_get_tpc_table_cmdid(ar);
|
|
+ if (ret) {
|
|
+ ath12k_warn(ab, "failed to request tpc table cmdid: %d\n", ret);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ time_left = wait_for_completion_timeout(&ar->tpc_complete,
|
|
+ TPC_STATS_WAIT_TIME);
|
|
+
|
|
+ if (time_left == 0)
|
|
+ ret = -ETIMEDOUT;
|
|
+
|
|
+out:
|
|
+ spin_lock_bh(&ar->data_lock);
|
|
+ ar->tpc_request = false;
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ath12k_tpc_stats_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ struct ath12k *ar = inode->i_private;
|
|
+ void *buf;
|
|
+ int ret;
|
|
+
|
|
+ mutex_lock(&ar->conf_mutex);
|
|
+
|
|
+ if (ar->state != ATH12K_STATE_ON) {
|
|
+ ath12k_warn(ar->ab, "Interface not up\n");
|
|
+ ret = -ENETDOWN;
|
|
+ goto err_unlock;
|
|
+ }
|
|
+
|
|
+ buf = kmalloc(ATH12K_TPC_STATS_BUF_SIZE, GFP_KERNEL);
|
|
+ if (!buf) {
|
|
+ ret = -ENOMEM;
|
|
+ goto err_unlock;
|
|
+ }
|
|
+
|
|
+ ret = ath12k_debug_tpc_stats_request(ar);
|
|
+ if (ret) {
|
|
+ ath12k_warn(ar->ab, "failed to request tpc stats: %d\n",
|
|
+ ret);
|
|
+ spin_lock_bh(&ar->data_lock);
|
|
+ ath12k_wmi_free_tpc_stats_mem(ar);
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+ goto err_free;
|
|
+ }
|
|
+
|
|
+ ath12k_tpc_stats_fill(ar, ar->tpc_stats, buf);
|
|
+ file->private_data = buf;
|
|
+
|
|
+ spin_lock_bh(&ar->data_lock);
|
|
+ ath12k_wmi_free_tpc_stats_mem(ar);
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_free:
|
|
+ kfree(buf);
|
|
+
|
|
+err_unlock:
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ath12k_tpc_stats_release(struct inode *inode,
|
|
+ struct file *file)
|
|
+{
|
|
+ kfree(file->private_data);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static ssize_t ath12k_tpc_stats_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ const char *buf = file->private_data;
|
|
+ unsigned int len = strlen(buf);
|
|
+
|
|
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
|
+}
|
|
+
|
|
+static ssize_t ath12k_read_tpc_stats_type(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath12k *ar = file->private_data;
|
|
+ char buf[32];
|
|
+ size_t len;
|
|
+
|
|
+ len = scnprintf(buf, sizeof(buf), "%u\n", ar->tpc_stats_type);
|
|
+
|
|
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
|
+}
|
|
+
|
|
+static ssize_t ath12k_write_tpc_stats_type(struct file *file,
|
|
+ const char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath12k *ar = file->private_data;
|
|
+ u8 type;
|
|
+ int ret;
|
|
+
|
|
+ ret = kstrtou8_from_user(user_buf, count, 0, &type);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (type >= ATH12K_DBG_TPC_MAX_STATS)
|
|
+ return -E2BIG;
|
|
+
|
|
+ ar->tpc_stats_type = type;
|
|
+
|
|
+ ret = count;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_tpc_stats_type = {
|
|
+ .read = ath12k_read_tpc_stats_type,
|
|
+ .write = ath12k_write_tpc_stats_type,
|
|
+ .open = simple_open,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
+static const struct file_operations fops_tpc_stats = {
|
|
+ .open = ath12k_tpc_stats_open,
|
|
+ .release = ath12k_tpc_stats_release,
|
|
+ .read = ath12k_tpc_stats_read,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
static ssize_t ath12k_write_simulate_awgn(struct file *file,
|
|
const char __user *user_buf,
|
|
size_t count, loff_t *ppos)
|
|
@@ -1384,6 +2119,8 @@ int ath12k_debugfs_register(struct ath12
|
|
ath12k_debugfs_fw_stats_init(ar);
|
|
ath12k_init_pktlog(ar);
|
|
|
|
+ init_completion(&ar->tpc_complete);
|
|
+
|
|
debugfs_create_file("ext_tx_stats", 0644,
|
|
ar->debug.debugfs_pdev, ar,
|
|
&fops_extd_tx_stats);
|
|
@@ -1409,6 +2146,15 @@ int ath12k_debugfs_register(struct ath12
|
|
&fops_simulate_awgn);
|
|
}
|
|
|
|
+
|
|
+ debugfs_create_file("tpc_stats", 0400,
|
|
+ ar->debug.debugfs_pdev, ar,
|
|
+ &fops_tpc_stats);
|
|
+
|
|
+ debugfs_create_file("tpc_stats_type", 0600,
|
|
+ ar->debug.debugfs_pdev, ar,
|
|
+ &fops_tpc_stats_type);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
--- a/drivers/net/wireless/ath/ath12k/debugfs.h
|
|
+++ b/drivers/net/wireless/ath/ath12k/debugfs.h
|
|
@@ -63,6 +63,67 @@ enum ath12k_dbg_htt_ext_stats_type {
|
|
ATH12K_DBG_HTT_NUM_EXT_STATS,
|
|
};
|
|
|
|
+#define ATH12K_CCK_RATES 4
|
|
+#define ATH12K_OFDM_RATES 8
|
|
+#define ATH12K_HT_RATES 8
|
|
+/* VHT rates includes extra MCS. sent by FW */
|
|
+#define ATH12K_VHT_RATES 12
|
|
+#define ATH12K_HE_RATES 12
|
|
+#define ATH12K_HE_RATES_WITH_EXTRA_MCS 14
|
|
+#define ATH12K_EHT_RATES 16
|
|
+#define ATH12K_NSS_1 1
|
|
+#define ATH12K_NSS_4 4
|
|
+#define ATH12K_NSS_8 8
|
|
+#define TPC_STATS_WAIT_TIME (1 * HZ)
|
|
+#define MAX_TPC_PREAM_STR_LEN 7
|
|
+/* Max negative power value to indicate error */
|
|
+#define TPC_INVAL -128
|
|
+#define TPC_MAX 127
|
|
+#define TPC_STATS_TOT_ROW 700
|
|
+#define TPC_STATS_TOT_COLUMN 100
|
|
+#define ATH12K_TPC_STATS_BUF_SIZE (TPC_STATS_TOT_ROW * TPC_STATS_TOT_COLUMN)
|
|
+
|
|
+enum ath12k_dbg_tpc_stats_type {
|
|
+ ATH12K_DBG_TPC_STATS_SU,
|
|
+ ATH12K_DBG_TPC_STATS_SU_WITH_TXBF,
|
|
+ ATH12K_DBG_TPC_STATS_MU,
|
|
+ ATH12K_DBG_TPC_STATS_MU_WITH_TXBF,
|
|
+ /*last*/
|
|
+ ATH12K_DBG_TPC_MAX_STATS,
|
|
+};
|
|
+
|
|
+enum ath12k_debug_tpc_stats_ctl_mode {
|
|
+ ATH12K_TPC_STATS_CTL_MODE_LEGACY_5G_6G,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_HT_VHT20_5G_6G,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_HE_EHT20_5G_6G,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_HT_VHT40_5G_6G,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_HE_EHT40_5G_6G,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_VHT80_5G_6G,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_HE_EHT80_5G_6G,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_VHT160_5G_6G,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_HE_EHT160_5G_6G,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_HE_EHT320_5G_6G,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_CCK_2G,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_LEGACY_2G,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_HT20_2G,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_HT40_2G,
|
|
+
|
|
+ ATH12K_TPC_STATS_CTL_MODE_EHT80_SU_PUNC20 = 23,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_EHT160_SU_PUNC20,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC40,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC80,
|
|
+ ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC120
|
|
+};
|
|
+
|
|
+enum ath12k_debug_tpc_stats_support_modes {
|
|
+ ATH12K_TPC_STATS_SUPPORT_160 = 0,
|
|
+ ATH12K_TPC_STATS_SUPPORT_320,
|
|
+ ATH12K_TPC_STATS_SUPPORT_AX,
|
|
+ ATH12K_TPC_STATS_SUPPORT_AX_EXTRA_MCS,
|
|
+ ATH12K_TPC_STATS_SUPPORT_BE,
|
|
+ ATH12K_TPC_STATS_SUPPORT_BE_PUNC,
|
|
+};
|
|
+
|
|
struct debug_htt_stats_req {
|
|
bool done;
|
|
bool override_cfg_param;
|
|
--- a/drivers/net/wireless/ath/ath12k/wmi.c
|
|
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
|
|
@@ -81,6 +81,8 @@ struct wmi_tlv_dma_buf_release_parse {
|
|
static const struct wmi_tlv_policy wmi_tlv_policies[] = {
|
|
[WMI_TAG_ARRAY_BYTE] = { .min_len = 0 },
|
|
[WMI_TAG_ARRAY_UINT32] = { .min_len = 0 },
|
|
+ [WMI_TAG_ARRAY_STRUCT] = { .min_len = 0 },
|
|
+ [WMI_TAG_ARRAY_INT16] = { .min_len = 0 },
|
|
[WMI_TAG_SERVICE_READY_EVENT] = {
|
|
.min_len = sizeof(struct wmi_service_ready_event) },
|
|
[WMI_TAG_SERVICE_READY_EXT_EVENT] = {
|
|
@@ -137,6 +139,8 @@ static const struct wmi_tlv_policy wmi_t
|
|
.min_len = sizeof(struct wmi_twt_add_dialog_event) },
|
|
[WMI_TAG_OBSS_COLOR_COLLISION_EVT]
|
|
= { .min_len = sizeof(struct wmi_obss_color_collision_event) },
|
|
+ [WMI_TAG_CTRL_PATH_EVENT_FIXED_PARAM]
|
|
+ = { .min_len = sizeof(struct wmi_cp_stats_event_fixed_param) },
|
|
};
|
|
|
|
static __le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len)
|
|
@@ -8305,6 +8309,459 @@ static void ath12k_wmi_event_wow_wakeup_
|
|
complete(&ab->wow.wakeup_completed);
|
|
}
|
|
|
|
+static int ath12k_tpc_get_reg_pwr(struct ath12k_base *ab,
|
|
+ struct wmi_tpc_stats_event *tpc_stats,
|
|
+ struct wmi_max_reg_power_fixed_param *ev)
|
|
+{
|
|
+ int total_size;
|
|
+ struct wmi_max_reg_power_allowed *reg_pwr;
|
|
+
|
|
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
|
|
+ "Received reg power array type %d length %d for tpc stats\n",
|
|
+ ev->reg_power_type, ev->reg_array_len);
|
|
+
|
|
+ switch (ev->reg_power_type) {
|
|
+ case TPC_STATS_REG_PWR_ALLOWED_TYPE:
|
|
+ reg_pwr = &tpc_stats->max_reg_allowed_power;
|
|
+ break;
|
|
+ default:
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* Each entry is 2 byte hence multiplying the indices with 2 */
|
|
+ total_size = (ev->d1 * ev->d2 * ev->d3 * ev->d4 * 2);
|
|
+ if (ev->reg_array_len != total_size) {
|
|
+ ath12k_warn(ab,
|
|
+ "Total size and reg_array_len doesn't match for tpc stats\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ memcpy(®_pwr->tpc_reg_pwr, ev, sizeof(struct wmi_max_reg_power_fixed_param));
|
|
+
|
|
+ reg_pwr->reg_pwr_array = kzalloc(reg_pwr->tpc_reg_pwr.reg_array_len,
|
|
+ GFP_ATOMIC);
|
|
+ if (!reg_pwr->reg_pwr_array)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ tpc_stats->tlvs_rcvd |= WMI_TPC_REG_PWR_ALLOWED;
|
|
+out:
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ath12k_tpc_get_rate_array(struct ath12k_base *ab,
|
|
+ struct wmi_tpc_stats_event *tpc_stats,
|
|
+ struct wmi_tpc_rates_array_fixed_param *ev)
|
|
+{
|
|
+ u32 flag = 0;
|
|
+ struct wmi_tpc_rates_array *rates_array;
|
|
+
|
|
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
|
|
+ "Received rates array type %d length %d for tpc stats\n",
|
|
+ ev->rate_array_type, ev->rate_array_len);
|
|
+
|
|
+ switch (ev->rate_array_type) {
|
|
+ case ATH12K_TPC_STATS_RATES_ARRAY1:
|
|
+ rates_array = &tpc_stats->rates_array1;
|
|
+ flag = WMI_TPC_RATES_ARRAY1;
|
|
+ break;
|
|
+ case ATH12K_TPC_STATS_RATES_ARRAY2:
|
|
+ rates_array = &tpc_stats->rates_array2;
|
|
+ flag = WMI_TPC_RATES_ARRAY2;
|
|
+ break;
|
|
+ default:
|
|
+ ath12k_warn(ab,
|
|
+ "Received invalid type of rates array for tpc stats\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ memcpy(&rates_array->tpc_rates_array, ev, sizeof(struct wmi_tpc_rates_array_fixed_param));
|
|
+ rates_array->rate_array = kzalloc(rates_array->tpc_rates_array.rate_array_len,
|
|
+ GFP_ATOMIC);
|
|
+ if (!rates_array->rate_array)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ tpc_stats->tlvs_rcvd |= flag;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ath12k_tpc_get_ctl_pwr_tbl(struct ath12k_base *ab,
|
|
+ struct wmi_tpc_stats_event *tpc_stats,
|
|
+ struct wmi_tpc_ctl_pwr_fixed_param *ev)
|
|
+{
|
|
+ int total_size, ret = 0;
|
|
+ u32 flag = 0;
|
|
+ struct wmi_tpc_ctl_pwr_table *ctl_array;
|
|
+
|
|
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
|
|
+ "Received ctl array type %d length %d for tpc stats\n",
|
|
+ ev->ctl_array_type, ev->ctl_array_len);
|
|
+
|
|
+ switch (ev->ctl_array_type) {
|
|
+ case ATH12K_TPC_STATS_CTL_ARRAY:
|
|
+ ctl_array = &tpc_stats->ctl_array;
|
|
+ flag = WMI_TPC_CTL_PWR_ARRAY;
|
|
+ break;
|
|
+ default:
|
|
+ ath12k_warn(ab,
|
|
+ "Received invalid type of ctl pwr table for tpc stats\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ total_size = (ev->d1 * ev->d2 * ev->d3 * ev->d4);
|
|
+ if (ev->ctl_array_len != total_size) {
|
|
+ ath12k_warn(ab,
|
|
+ "Total size and ctl_array_len doesn't match for tpc stats\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ memcpy(&ctl_array->tpc_ctl_pwr, ev, sizeof(struct wmi_tpc_ctl_pwr_fixed_param));
|
|
+
|
|
+ ctl_array->ctl_pwr_table = kzalloc(ctl_array->tpc_ctl_pwr.ctl_array_len,
|
|
+ GFP_ATOMIC);
|
|
+ if (!ctl_array->ctl_pwr_table)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ tpc_stats->tlvs_rcvd |= flag;
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ath12k_wmi_tpc_stats_subtlv_parser(struct ath12k_base *ab,
|
|
+ u16 tag, u16 len,
|
|
+ const void *ptr, void *data)
|
|
+{
|
|
+ int ret = 0;
|
|
+ struct wmi_tpc_stats_event *tpc_stats = (struct wmi_tpc_stats_event *)data;
|
|
+ struct wmi_tpc_configs *tpc_config;
|
|
+ struct wmi_max_reg_power_fixed_param *tpc_reg_pwr;
|
|
+ struct wmi_tpc_rates_array_fixed_param *tpc_rates_array;
|
|
+ struct wmi_tpc_ctl_pwr_fixed_param *tpc_ctl_pwr;
|
|
+
|
|
+ if (!tpc_stats) {
|
|
+ ath12k_warn(ab, "tpc stats memory unavailable\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ switch (tag) {
|
|
+ case WMI_TAG_TPC_STATS_CONFIG_EVENT:
|
|
+ tpc_config = (struct wmi_tpc_configs *)ptr;
|
|
+ memcpy(&tpc_stats->tpc_config, tpc_config,
|
|
+ sizeof(struct wmi_tpc_configs));
|
|
+ break;
|
|
+
|
|
+ case WMI_TAG_TPC_STATS_REG_PWR_ALLOWED:
|
|
+ tpc_reg_pwr = (struct wmi_max_reg_power_fixed_param *)ptr;
|
|
+ ret = ath12k_tpc_get_reg_pwr(ab, tpc_stats, tpc_reg_pwr);
|
|
+ break;
|
|
+
|
|
+ case WMI_TAG_TPC_STATS_RATES_ARRAY:
|
|
+ tpc_rates_array = (struct wmi_tpc_rates_array_fixed_param *)ptr;
|
|
+ ret = ath12k_tpc_get_rate_array(ab, tpc_stats, tpc_rates_array);
|
|
+ break;
|
|
+
|
|
+ case WMI_TAG_TPC_STATS_CTL_PWR_TABLE_EVENT:
|
|
+ tpc_ctl_pwr = (struct wmi_tpc_ctl_pwr_fixed_param *)ptr;
|
|
+ ret = ath12k_tpc_get_ctl_pwr_tbl(ab, tpc_stats, tpc_ctl_pwr);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ ath12k_warn(ab,
|
|
+ "Received invalid tag for tpc stats in subtlvs\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ath12k_wmi_tpc_stats_copy_buffer(struct ath12k_base *ab,
|
|
+ const void *ptr, u16 tag, u16 len,
|
|
+ struct wmi_tpc_stats_event *tpc_stats)
|
|
+{
|
|
+ s16 *reg_rates_src;
|
|
+ s8 *ctl_src;
|
|
+ s16 *dst_ptr;
|
|
+ s8 *dst_ptr_ctl;
|
|
+ int ret = 0;
|
|
+
|
|
+ if (tag == WMI_TAG_ARRAY_INT16)
|
|
+ reg_rates_src = (s16 *)ptr;
|
|
+ else
|
|
+ ctl_src = (s8 *)ptr;
|
|
+
|
|
+ switch (tpc_stats->event_count) {
|
|
+ case ATH12K_TPC_STATS_CONFIG_REG_PWR_EVENT:
|
|
+ if (tpc_stats->tlvs_rcvd & WMI_TPC_REG_PWR_ALLOWED) {
|
|
+ dst_ptr = tpc_stats->max_reg_allowed_power.reg_pwr_array;
|
|
+ memcpy(dst_ptr, reg_rates_src,
|
|
+ tpc_stats->max_reg_allowed_power.tpc_reg_pwr.reg_array_len);
|
|
+ reg_rates_src += tpc_stats->max_reg_allowed_power.tpc_reg_pwr.reg_array_len;
|
|
+ }
|
|
+ break;
|
|
+ case ATH12K_TPC_STATS_RATES_EVENT1:
|
|
+ if (tpc_stats->tlvs_rcvd & WMI_TPC_RATES_ARRAY1) {
|
|
+ dst_ptr = tpc_stats->rates_array1.rate_array;
|
|
+ memcpy(dst_ptr, reg_rates_src,
|
|
+ tpc_stats->rates_array1.tpc_rates_array.rate_array_len);
|
|
+ reg_rates_src += tpc_stats->rates_array1.tpc_rates_array.rate_array_len;
|
|
+ }
|
|
+ break;
|
|
+ case ATH12K_TPC_STATS_RATES_EVENT2:
|
|
+ if (tpc_stats->tlvs_rcvd & WMI_TPC_RATES_ARRAY2) {
|
|
+ dst_ptr = tpc_stats->rates_array2.rate_array;
|
|
+ memcpy(dst_ptr, reg_rates_src,
|
|
+ tpc_stats->rates_array2.tpc_rates_array.rate_array_len);
|
|
+ reg_rates_src += tpc_stats->rates_array2.tpc_rates_array.rate_array_len;
|
|
+ }
|
|
+ break;
|
|
+ case ATH12K_TPC_STATS_CTL_TABLE_EVENT:
|
|
+ if (tpc_stats->tlvs_rcvd & WMI_TPC_CTL_PWR_ARRAY) {
|
|
+ dst_ptr_ctl = tpc_stats->ctl_array.ctl_pwr_table;
|
|
+ memcpy(dst_ptr_ctl, ctl_src,
|
|
+ tpc_stats->ctl_array.tpc_ctl_pwr.ctl_array_len);
|
|
+ ctl_src += tpc_stats->ctl_array.tpc_ctl_pwr.ctl_array_len;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ath12k_wmi_tpc_stats_event_parser(struct ath12k_base *ab,
|
|
+ u16 tag, u16 len,
|
|
+ const void *ptr, void *data)
|
|
+{
|
|
+ struct wmi_tpc_stats_event *tpc_stats = (struct wmi_tpc_stats_event *)data;
|
|
+ int ret = 0;
|
|
+
|
|
+ ath12k_dbg(ab, ATH12K_DBG_WMI, "tpc stats tag 0x%x of len %d rcvd\n",
|
|
+ tag, len);
|
|
+ switch (tag) {
|
|
+ case WMI_TAG_CTRL_PATH_EVENT_FIXED_PARAM:
|
|
+ /* Fixed param is already processed*/
|
|
+ break;
|
|
+
|
|
+ case WMI_TAG_ARRAY_STRUCT:
|
|
+ /* len 0 is expected for array of struct when there
|
|
+ * is no content of that type to pack inside that tlv
|
|
+ */
|
|
+ if (len == 0)
|
|
+ return 0;
|
|
+ ret = ath12k_wmi_tlv_iter(ab, ptr, len,
|
|
+ ath12k_wmi_tpc_stats_subtlv_parser,
|
|
+ tpc_stats);
|
|
+ break;
|
|
+
|
|
+ case WMI_TAG_ARRAY_INT16:
|
|
+ if (len == 0)
|
|
+ return 0;
|
|
+ ret = ath12k_wmi_tpc_stats_copy_buffer(ab, ptr,
|
|
+ WMI_TAG_ARRAY_INT16,
|
|
+ len, tpc_stats);
|
|
+ break;
|
|
+
|
|
+ case WMI_TAG_ARRAY_BYTE:
|
|
+ if (len == 0)
|
|
+ return 0;
|
|
+ ret = ath12k_wmi_tpc_stats_copy_buffer(ab, ptr,
|
|
+ WMI_TAG_ARRAY_BYTE,
|
|
+ len, tpc_stats);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ ath12k_warn(ab, "Received invalid tag for tpc stats\n");
|
|
+ ret = -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void ath12k_wmi_free_tpc_stats_mem(struct ath12k *ar)
|
|
+{
|
|
+ struct wmi_tpc_stats_event *tpc_stats = ar->tpc_stats;
|
|
+
|
|
+ lockdep_assert_held(&ar->data_lock);
|
|
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "tpc stats mem free\n");
|
|
+ if (tpc_stats) {
|
|
+ kfree(tpc_stats->max_reg_allowed_power.reg_pwr_array);
|
|
+ kfree(tpc_stats->rates_array1.rate_array);
|
|
+ kfree(tpc_stats->rates_array2.rate_array);
|
|
+ kfree(tpc_stats->ctl_array.ctl_pwr_table);
|
|
+ kfree(tpc_stats);
|
|
+ ar->tpc_stats = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static struct ath12k *ath12k_wmi_tpc_process_fixed_param(struct ath12k_base *ab,
|
|
+ u8 *ptr, size_t len)
|
|
+{
|
|
+ struct ath12k *ar;
|
|
+ const struct wmi_tlv *tlv;
|
|
+ struct wmi_cp_stats_event_fixed_param *fixed_param;
|
|
+ struct wmi_tpc_stats_event *tpc_stats;
|
|
+ u16 tlv_tag;
|
|
+
|
|
+ if (!ptr) {
|
|
+ ath12k_warn(ab, "No data present in tpc stats event\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (len < (sizeof(*fixed_param) + TLV_HDR_SIZE)) {
|
|
+ ath12k_warn(ab, "tpc stats event size invalid\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ tlv = (struct wmi_tlv *)ptr;
|
|
+ tlv_tag = FIELD_GET(WMI_TLV_TAG, tlv->header);
|
|
+ ptr += sizeof(*tlv);
|
|
+
|
|
+ if (tlv_tag == WMI_TAG_CTRL_PATH_EVENT_FIXED_PARAM) {
|
|
+ fixed_param = (struct wmi_cp_stats_event_fixed_param *)ptr;
|
|
+
|
|
+ ar = ath12k_mac_get_ar_by_pdev_id(ab, fixed_param->pdev_id);
|
|
+ if (!ar) {
|
|
+ ath12k_warn(ab, "Failed to get ar for tpc stats\n");
|
|
+ return NULL;
|
|
+ }
|
|
+ } else {
|
|
+ ath12k_warn(ab, "TPC Stats received without fixed param tlv at start\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ spin_lock_bh(&ar->data_lock);
|
|
+ if (!ar->tpc_request) {
|
|
+ /* Event is received either without request or the
|
|
+ * timeout, if memory is already allocated free it
|
|
+ */
|
|
+ if (ar->tpc_stats) {
|
|
+ ath12k_warn(ab, "Freeing memory for tpc_stats\n");
|
|
+ ath12k_wmi_free_tpc_stats_mem(ar);
|
|
+ }
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (fixed_param->event_count == 0) {
|
|
+ if (ar->tpc_stats) {
|
|
+ ath12k_warn(ab,
|
|
+ "Invalid tpc memory present\n");
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+ return NULL;
|
|
+ }
|
|
+ ar->tpc_stats =
|
|
+ kzalloc(sizeof(struct wmi_tpc_stats_event),
|
|
+ GFP_ATOMIC);
|
|
+ }
|
|
+
|
|
+ if (!ar->tpc_stats) {
|
|
+ ath12k_warn(ab,
|
|
+ "Failed to allocate memory for tpc stats\n");
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ tpc_stats = ar->tpc_stats;
|
|
+
|
|
+ if (!(fixed_param->event_count == 0)) {
|
|
+ if (fixed_param->event_count != tpc_stats->event_count + 1) {
|
|
+ ath12k_warn(ab,
|
|
+ "Invalid tpc event received\n");
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+ return NULL;
|
|
+ }
|
|
+ }
|
|
+ tpc_stats->pdev_id = fixed_param->pdev_id;
|
|
+ tpc_stats->event_count = fixed_param->event_count;
|
|
+ tpc_stats->end_of_event = fixed_param->end_of_event;
|
|
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
|
|
+ "tpc stats event_count %d\n",
|
|
+ tpc_stats->event_count);
|
|
+
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+
|
|
+ return ar;
|
|
+}
|
|
+
|
|
+static void ath12k_process_tpc_stats(struct ath12k_base *ab,
|
|
+ struct sk_buff *skb)
|
|
+{
|
|
+ int ret;
|
|
+ struct ath12k *ar;
|
|
+ struct wmi_tpc_stats_event *tpc_stats = NULL;
|
|
+
|
|
+ rcu_read_lock();
|
|
+ ar = ath12k_wmi_tpc_process_fixed_param(ab, skb->data, skb->len);
|
|
+ if (!ar) {
|
|
+ ath12k_warn(ab, "Failed to get ar for tpc event\n");
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ spin_lock_bh(&ar->data_lock);
|
|
+ tpc_stats = ar->tpc_stats;
|
|
+ ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len,
|
|
+ ath12k_wmi_tpc_stats_event_parser,
|
|
+ tpc_stats);
|
|
+ if (ret) {
|
|
+ if (tpc_stats) {
|
|
+ ath12k_warn(ab, "Freeing memory for tpc_stats\n");
|
|
+ ath12k_wmi_free_tpc_stats_mem(ar);
|
|
+ }
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+ ath12k_warn(ab, "failed to parse tpc_stats tlv: %d\n", ret);
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ if (tpc_stats && tpc_stats->end_of_event)
|
|
+ complete(&ar->tpc_complete);
|
|
+
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+
|
|
+exit:
|
|
+ rcu_read_unlock();
|
|
+}
|
|
+
|
|
+int ath12k_wmi_pdev_get_tpc_table_cmdid(struct ath12k *ar)
|
|
+{
|
|
+ struct ath12k_pdev_wmi *wmi = ar->wmi;
|
|
+ struct wmi_request_ctrl_path_stats_cmd_fixed_param *cmd;
|
|
+ struct sk_buff *skb;
|
|
+ struct wmi_tlv *tlv;
|
|
+ void *ptr;
|
|
+ u32 *pdev_ids, buf_len;
|
|
+ int ret;
|
|
+
|
|
+ buf_len = sizeof(*cmd) + sizeof(u32) + TLV_HDR_SIZE;
|
|
+
|
|
+ skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, buf_len);
|
|
+ if (!skb)
|
|
+ return -ENOMEM;
|
|
+ cmd = (struct wmi_request_ctrl_path_stats_cmd_fixed_param *)skb->data;
|
|
+ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_CTRL_PATH_CMD_FIXED_PARAM) |
|
|
+ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
|
|
+
|
|
+ cmd->stats_id_mask = WMI_REQ_CTRL_PATH_PDEV_TX_STAT;
|
|
+ cmd->action = WMI_REQUEST_CTRL_PATH_STAT_GET;
|
|
+ cmd->subid = ar->tpc_stats_type;
|
|
+
|
|
+ ptr = skb->data + sizeof(*cmd);
|
|
+
|
|
+ tlv = ptr;
|
|
+ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_UINT32) |
|
|
+ FIELD_PREP(WMI_TLV_LEN, sizeof(u32));
|
|
+
|
|
+ ptr += TLV_HDR_SIZE;
|
|
+
|
|
+ pdev_ids = ptr;
|
|
+ pdev_ids[0] = ar->pdev->pdev_id;
|
|
+
|
|
+ ret = ath12k_wmi_cmd_send(wmi, skb, WMI_REQUEST_STATS_CTRL_PATH_CMDID);
|
|
+ if (ret) {
|
|
+ ath12k_warn(ar->ab,
|
|
+ "failed to submit WMI_REQUEST_STATS_CTRL_PATH_CMDID\n");
|
|
+ dev_kfree_skb(skb);
|
|
+ return ret;
|
|
+ }
|
|
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "WMI get TPC STATS sent on pdev %d\n",
|
|
+ ar->pdev->pdev_id);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
static void ath12k_wmi_diag_event(struct ath12k_base *ab, struct sk_buff *skb)
|
|
{
|
|
const struct wmi_tlv *tlv;
|
|
@@ -8691,6 +9148,9 @@ static void ath12k_wmi_tlv_op_rx(struct
|
|
case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID:
|
|
ath12k_wmi_obss_color_collision_event(ab, skb);
|
|
break;
|
|
+ case WMI_STATS_CTRL_PATH_EVENTID:
|
|
+ ath12k_process_tpc_stats(ab, skb);
|
|
+ break;
|
|
/* TODO: Add remaining events */
|
|
default:
|
|
ath12k_dbg(ab, ATH12K_DBG_WMI, "Unknown eventid: 0x%x\n", id);
|
|
--- a/drivers/net/wireless/ath/ath12k/wmi.h
|
|
+++ b/drivers/net/wireless/ath/ath12k/wmi.h
|
|
@@ -282,6 +282,7 @@ enum wmi_tlv_cmd_id {
|
|
WMI_PDEV_SET_SRG_OBSS_BSSID_ENABLE_BITMAP_CMDID,
|
|
WMI_PDEV_SET_NON_SRG_OBSS_COLOR_ENABLE_BITMAP_CMDID,
|
|
WMI_PDEV_SET_NON_SRG_OBSS_BSSID_ENABLE_BITMAP_CMDID,
|
|
+ WMI_PDEV_GET_TPC_STATS_CMDID,
|
|
WMI_VDEV_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_VDEV),
|
|
WMI_VDEV_DELETE_CMDID,
|
|
WMI_VDEV_START_REQUEST_CMDID,
|
|
@@ -470,6 +471,11 @@ enum wmi_tlv_cmd_id {
|
|
WMI_REQUEST_RCPI_CMDID,
|
|
WMI_REQUEST_PEER_STATS_INFO_CMDID,
|
|
WMI_REQUEST_RADIO_CHAN_STATS_CMDID,
|
|
+ WMI_REQUEST_WLM_STATS_CMDID,
|
|
+ WMI_REQUEST_CTRL_PATH_STATS_CMDID,
|
|
+ WMI_REQUEST_UNIFIED_LL_GET_STA_CMDID,
|
|
+ WMI_REQUEST_THERMAL_STATS_CMDID,
|
|
+ WMI_REQUEST_STATS_CTRL_PATH_CMDID,
|
|
WMI_SET_ARP_NS_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_GRP_ARP_NS_OFL),
|
|
WMI_ADD_PROACTIVE_ARP_RSP_PATTERN_CMDID,
|
|
WMI_DEL_PROACTIVE_ARP_RSP_PATTERN_CMDID,
|
|
@@ -740,6 +746,9 @@ enum wmi_tlv_event_id {
|
|
WMI_UPDATE_RCPI_EVENTID,
|
|
WMI_PEER_STATS_INFO_EVENTID,
|
|
WMI_RADIO_CHAN_STATS_EVENTID,
|
|
+ WMI_WLM_STATS_EVENTID,
|
|
+ WMI_CTRL_PATH_STATS_EVENTID,
|
|
+ WMI_STATS_CTRL_PATH_EVENTID,
|
|
WMI_NLO_MATCH_EVENTID = WMI_TLV_CMD(WMI_GRP_NLO_OFL),
|
|
WMI_NLO_SCAN_COMPLETE_EVENTID,
|
|
WMI_APFIND_EVENTID,
|
|
@@ -1151,6 +1160,7 @@ enum wmi_tlv_tag {
|
|
WMI_TAG_ARRAY_BYTE,
|
|
WMI_TAG_ARRAY_STRUCT,
|
|
WMI_TAG_ARRAY_FIXED_STRUCT,
|
|
+ WMI_TAG_ARRAY_INT16,
|
|
WMI_TAG_LAST_ARRAY_ENUM = 31,
|
|
WMI_TAG_SERVICE_READY_EVENT,
|
|
WMI_TAG_HAL_REG_CAPABILITIES,
|
|
@@ -1913,6 +1923,14 @@ enum wmi_tlv_tag {
|
|
WMI_TAG_EHT_RATE_SET = 0x3C4,
|
|
WMI_TAG_DCS_AWGN_INT_TYPE = 0x3C5,
|
|
WMI_TAG_PDEV_PKTLOG_DECODE_INFO = 0x414,
|
|
+ WMI_TAG_TPC_STATS_GET_CMD = 0x38B,
|
|
+ WMI_TAG_TPC_STATS_EVENT_FIXED_PARAM,
|
|
+ WMI_TAG_TPC_STATS_CONFIG_EVENT,
|
|
+ WMI_TAG_TPC_STATS_REG_PWR_ALLOWED,
|
|
+ WMI_TAG_TPC_STATS_RATES_ARRAY,
|
|
+ WMI_TAG_TPC_STATS_CTL_PWR_TABLE_EVENT,
|
|
+ WMI_TAG_CTRL_PATH_CMD_FIXED_PARAM = 0x442,
|
|
+ WMI_TAG_CTRL_PATH_EVENT_FIXED_PARAM,
|
|
WMI_TAG_MAX
|
|
};
|
|
|
|
@@ -5679,6 +5697,234 @@ struct target_resource_config {
|
|
u32 ema_max_profile_period;
|
|
};
|
|
|
|
+enum wmi_tpc_pream_bw {
|
|
+ WMI_TPC_PREAM_CCK,
|
|
+ WMI_TPC_PREAM_OFDM,
|
|
+ WMI_TPC_PREAM_HT20,
|
|
+ WMI_TPC_PREAM_HT40,
|
|
+ WMI_TPC_PREAM_VHT20,
|
|
+ WMI_TPC_PREAM_VHT40,
|
|
+ WMI_TPC_PREAM_VHT80,
|
|
+ WMI_TPC_PREAM_VHT160,
|
|
+ WMI_TPC_PREAM_HE20,
|
|
+ WMI_TPC_PREAM_HE40,
|
|
+ WMI_TPC_PREAM_HE80,
|
|
+ WMI_TPC_PREAM_HE160,
|
|
+ WMI_TPC_PREAM_EHT20,
|
|
+ WMI_TPC_PREAM_EHT40,
|
|
+ WMI_TPC_PREAM_EHT60,
|
|
+ WMI_TPC_PREAM_EHT80,
|
|
+ WMI_TPC_PREAM_EHT120,
|
|
+ WMI_TPC_PREAM_EHT140,
|
|
+ WMI_TPC_PREAM_EHT160,
|
|
+ WMI_TPC_PREAM_EHT200,
|
|
+ WMI_TPC_PREAM_EHT240,
|
|
+ WMI_TPC_PREAM_EHT280,
|
|
+ WMI_TPC_PREAM_EHT320,
|
|
+ WMI_TPC_PREAM_MAX
|
|
+};
|
|
+
|
|
+#define ATH12K_2G_MAX_FREQUENCY 2495
|
|
+#define ATH12K_5G_MAX_FREQUENCY 5920
|
|
+#define WMI_TPC_CONFIG BIT(1)
|
|
+#define WMI_TPC_REG_PWR_ALLOWED BIT(2)
|
|
+#define WMI_TPC_RATES_ARRAY1 BIT(3)
|
|
+#define WMI_TPC_RATES_ARRAY2 BIT(4)
|
|
+#define WMI_TPC_RATES_DL_OFDMA_ARRAY BIT(5)
|
|
+#define WMI_TPC_CTL_PWR_ARRAY BIT(6)
|
|
+#define WMI_TPC_CONFIG_PARAM 0x1
|
|
+#define ATH12K_TPC_RATE_ARRAY_MU GENMASK(15, 8)
|
|
+#define ATH12K_TPC_RATE_ARRAY_SU GENMASK(7, 0)
|
|
+#define ATH12K_HW_PREAMBLE(_rcode) (((_rcode) >> 8) & 0x7)
|
|
+#define ATH12K_HW_NSS(_rcode) (((_rcode) >> 5) & 0x7)
|
|
+#define MODULATION_LIMIT 126
|
|
+#define TPC_STATS_REG_PWR_ALLOWED_TYPE 0
|
|
+#define HE_EXTRA_MCS_SUPPORT GENMASK(31, 16)
|
|
+
|
|
+enum wmi_ctrl_path_stats_action {
|
|
+ WMI_REQUEST_CTRL_PATH_STAT_GET = 1,
|
|
+ WMI_REQUEST_CTRL_PATH_STAT_RESET = 2,
|
|
+ WMI_REQUEST_CTRL_PATH_STAT_START = 3,
|
|
+ WMI_REQUEST_CTRL_PATH_STAT_STOP = 4,
|
|
+};
|
|
+
|
|
+struct wmi_request_ctrl_path_stats_cmd_fixed_param {
|
|
+ u32 tlv_header;
|
|
+ /** Bitmask showing which of stats IDs 0-31 have been requested
|
|
+ * These stats ids are defined in enum wmi_ctrl_path_stats_id.
|
|
+ */
|
|
+ u32 stats_id_mask;
|
|
+ /** request ID to store the cookies in wifistats */
|
|
+ u32 request_id;
|
|
+ /** action
|
|
+ * get/reset/start/stop based on stats id
|
|
+ * defined as a part of wmi_ctrl_path_stats_action
|
|
+ */
|
|
+ u32 action;
|
|
+ /** Request Halphy subid stats
|
|
+ * According to the requested stats_id this halphy_subid varies
|
|
+ * For stats_id = 1, the possible values could be enum wmi_halphy_ctrl_path_statsid
|
|
+ */
|
|
+ u32 subid;
|
|
+} __packed;
|
|
+
|
|
+struct wmi_cp_stats_event_fixed_param {
|
|
+ /** Request ID */
|
|
+ u32 request_id;
|
|
+ /*end_of_event - single event or Multiple Event */
|
|
+ u32 end_of_event;
|
|
+ /*event_count - If Multiple Events are send, this is to identify particular
|
|
+ * event out of Multiple Events that are send to host
|
|
+ */
|
|
+ u32 event_count;
|
|
+ u32 pdev_id;
|
|
+} __packed;
|
|
+
|
|
+struct wmi_tpc_configs {
|
|
+ u32 reg_domain;
|
|
+ /* current channel in MHz */
|
|
+ u32 chan_freq;
|
|
+ /* current phy mode */
|
|
+ u32 phy_mode;
|
|
+ /* Max antenna gain for current regulatory in 0.25 dBm steps */
|
|
+ u32 twice_antenna_reduction;
|
|
+ /* Max transmit power allowed in regulatory domain in 0.25 dBm steps */
|
|
+ u32 twice_max_reg_power;
|
|
+ /* User specified antenna gain in 0.25 dBm steps */
|
|
+ s32 twice_antenna_gain;
|
|
+ /* The overall power limit in 0.25 dBm steps */
|
|
+ u32 power_limit;
|
|
+ /* The total number of rates supported */
|
|
+ u32 rate_max;
|
|
+ /* The total number of active chains */
|
|
+ u32 num_tx_chain;
|
|
+ /* not used for now */
|
|
+ u32 ctl;
|
|
+ u32 flags;
|
|
+ /* tells info abt BE, HE, HE_EXTRA_MCS, 160, 320, 11BE PUNC*/
|
|
+ u32 caps;
|
|
+} __packed;
|
|
+
|
|
+struct wmi_max_reg_power_fixed_param {
|
|
+ /* 0: maxRegAllowedPower[TX_NUM_CHAIN];
|
|
+ * 1:maxRegPowerAGCDD[TX_NUM_CHAIN - 1][TX_NUM_CHAIN - 1],
|
|
+ * 2:maxRegPowerAGSTBC[TX_NUM_CHAIN - 1][TX_NUM_CHAIN - 1],
|
|
+ * 3:maxRegPowerAGTXBF([TX_NUM_CHAIN - 1][TX_NUM_CHAIN - 1]
|
|
+ * type 1-3 no used, for future use
|
|
+ */
|
|
+ u32 reg_power_type;
|
|
+ /* Length of the regulatory power array being sent in bytes */
|
|
+ u32 reg_array_len;
|
|
+ /* dimensions below
|
|
+ * d1 - [TX_NUM_CHAIN - 1]
|
|
+ * d2- [TX_NUM_CHAIN - 1] for cdd, stbc, txbf
|
|
+ * d2 = 1 for maxRegAllowedPower
|
|
+ * d3 = 1 d4 = 1
|
|
+ */
|
|
+ u32 d1;
|
|
+ u32 d2;
|
|
+ u32 d3;
|
|
+ u32 d4;
|
|
+} __packed;
|
|
+
|
|
+struct wmi_max_reg_power_allowed {
|
|
+ struct wmi_max_reg_power_fixed_param tpc_reg_pwr;
|
|
+ s16 *reg_pwr_array;
|
|
+};
|
|
+
|
|
+struct wmi_tpc_rates_array_fixed_param {
|
|
+ /* 0: ratesArray[TPC_RATE_MAX], (for CCK, OFDM, HT, VHT and HE Rates info)
|
|
+ * 1: ratesArray2[TPC_RATE_MAX] (for EHT Rates info),
|
|
+ * 2: dl_ofdma rate array. Type 2 unsed, for future use
|
|
+ */
|
|
+ u32 rate_array_type;
|
|
+ u32 rate_array_len;
|
|
+} __packed;
|
|
+
|
|
+struct wmi_tpc_rates_array {
|
|
+ struct wmi_tpc_rates_array_fixed_param tpc_rates_array;
|
|
+ s16 *rate_array;
|
|
+};
|
|
+
|
|
+struct wmi_tpc_ctl_pwr_fixed_param {
|
|
+ /* 0: ctl_array; 1: ctl_160 array; 2: ctl_dlOfdma array
|
|
+ * Type 2 unsed, for future use
|
|
+ */
|
|
+ u32 ctl_array_type;
|
|
+ /* Length of the CTL array being sent in bytes */
|
|
+ u32 ctl_array_len;
|
|
+ /* Message MAY be split into smaller chunks to fit in the WMI svc msg
|
|
+ * not used for now
|
|
+ */
|
|
+ u32 end_of_ctl_pwr;
|
|
+ /* Incremented for every event chunk for Host to know the sequence
|
|
+ * not used for now
|
|
+ */
|
|
+ u32 ctl_pwr_count;
|
|
+ /* Dimensions below
|
|
+ * For ctl_array
|
|
+ * d4 = No of chains
|
|
+ * d3 = BF on/off = 2
|
|
+ * d2 = 28 which the number of different tx modes-legacy, HT20, HT40 etc
|
|
+ * d1 = NSS number of spatial streams
|
|
+ * s8 ctl_160array[WHAL_MAX_NUM_CHAINS][pri_or_sec][bf][nss].
|
|
+ * For ctl_160 array
|
|
+ * d4 = No of chains d3 = primary/secondary channel
|
|
+ * d2 = BF on/off d1 = NSS
|
|
+ */
|
|
+ u32 d1;
|
|
+ u32 d2;
|
|
+ u32 d3;
|
|
+ u32 d4;
|
|
+} __packed;
|
|
+
|
|
+struct wmi_tpc_ctl_pwr_table {
|
|
+ struct wmi_tpc_ctl_pwr_fixed_param tpc_ctl_pwr;
|
|
+ s8 *ctl_pwr_table;
|
|
+};
|
|
+
|
|
+struct wmi_tpc_stats_event {
|
|
+ u32 pdev_id;
|
|
+ u32 event_count;
|
|
+ u32 end_of_event;
|
|
+ /* Bitmap to set and use the received tlvs alone. Not all tlvs are
|
|
+ * supported for all chipset. For eg ctl_160 is not present for chipset
|
|
+ * which does not have 160Mhz support
|
|
+ */
|
|
+ u32 tlvs_rcvd;
|
|
+ struct wmi_max_reg_power_allowed max_reg_allowed_power;
|
|
+ struct wmi_tpc_rates_array rates_array1;
|
|
+ struct wmi_tpc_rates_array rates_array2;
|
|
+ struct wmi_tpc_configs tpc_config;
|
|
+ struct wmi_tpc_ctl_pwr_table ctl_array;
|
|
+};
|
|
+
|
|
+enum ath12k_wmi_tpc_stats_events {
|
|
+ ATH12K_TPC_STATS_CONFIG_REG_PWR_EVENT,
|
|
+ ATH12K_TPC_STATS_RATES_EVENT1,
|
|
+ ATH12K_TPC_STATS_RATES_EVENT2,
|
|
+ ATH12K_TPC_STATS_CTL_TABLE_EVENT
|
|
+};
|
|
+
|
|
+enum ath12k_wmi_tpc_stats_rates_array {
|
|
+ ATH12K_TPC_STATS_RATES_ARRAY1,
|
|
+ ATH12K_TPC_STATS_RATES_ARRAY2,
|
|
+};
|
|
+
|
|
+enum ath12k_wmi_tpc_stats_ctl_array {
|
|
+ ATH12K_TPC_STATS_CTL_ARRAY,
|
|
+ ATH12K_TPC_STATS_CTL_160ARRAY,
|
|
+};
|
|
+
|
|
+enum wmi_ctrl_path_stats_id {
|
|
+ /* bit 0 is currently unused / reserved */
|
|
+ WMI_REQ_CTRL_PATH_PDEV_TX_STAT = 1,
|
|
+ WMI_REQ_CTRL_PATH_VDEV_EXTD_STAT = 2,
|
|
+ WMI_REQ_CTRL_PATH_MEM_STAT = 3,
|
|
+ WMI_REQ_CTRL_PATH_CAL_STAT = 5,
|
|
+ WMI_REQ_CTRL_PATH_BTCOEX_STAT = 8,
|
|
+};
|
|
+
|
|
enum wmi_dblog_param {
|
|
WMI_DEBUG_LOG_PARAM_LOG_LEVEL = 0x1,
|
|
WMI_DEBUG_LOG_PARAM_VDEV_ENABLE,
|
|
@@ -6047,5 +6293,6 @@ int ath12k_wmi_send_vdev_set_tpc_power(s
|
|
u32 vdev_id,
|
|
struct ath12k_reg_tpc_power_info *param);
|
|
int ath12k_wmi_pdev_m3_dump_enable(struct ath12k *ar, u32 enable);
|
|
-
|
|
+int ath12k_wmi_pdev_get_tpc_table_cmdid(struct ath12k *ar);
|
|
+void ath12k_wmi_free_tpc_stats_mem(struct ath12k *ar);
|
|
#endif
|