wlan-ap-Telecominfraproject/feeds/ipq95xx/mac80211/patches/qca/661-ath12k-Add-TPC-stats-support.patch
John Crispin 144c5d00f4 ipq95xx/mac80211: update to ATH12.3-CS
Signed-off-by: John Crispin <john@phrozen.org>
2024-02-28 18:56:21 +01:00

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(&reg_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