From e4f16128c53b48f166301085cecc23f77bf3ff8e Mon Sep 17 00:00:00 2001
From: Miles Hu <milehu@codeaurora.org>
Date: Fri, 11 Oct 2019 19:24:06 -0700
Subject: [PATCH] ath11k: add HE stats in peer stats packet counters for MIMO
 and OFDMA

Signed-off-by: Miles Hu <milehu@codeaurora.org>
---
 drivers/net/wireless/ath/ath11k/core.h        |  23 ++++-
 drivers/net/wireless/ath/ath11k/debugfs_sta.c | 127 +++++++++++++++++++++++++-
 drivers/net/wireless/ath/ath11k/dp.h          |  21 ++++-
 drivers/net/wireless/ath/ath11k/dp_rx.c       |  17 +++-
 drivers/net/wireless/ath/ath11k/rx_desc.h     |   5 +
 5 files changed, 185 insertions(+), 8 deletions(-)

--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -25,6 +25,7 @@
 #include "dbring.h"
 #include "spectral.h"
 #include "vendor.h"
+#include "rx_desc.h"
 
 #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
 
@@ -345,6 +346,8 @@ struct ath11k_htt_data_stats {
 	u64 bw[ATH11K_COUNTER_TYPE_MAX][ATH11K_BW_NUM];
 	u64 nss[ATH11K_COUNTER_TYPE_MAX][ATH11K_NSS_NUM];
 	u64 gi[ATH11K_COUNTER_TYPE_MAX][ATH11K_GI_NUM];
+	u64 transmit_type[ATH11K_COUNTER_TYPE_MAX][HAL_RX_RECEPTION_TYPE_MAX];
+	u64 ru_loc[ATH11K_COUNTER_TYPE_MAX][HAL_RX_RU_ALLOC_TYPE_MAX];
 };
 
 struct ath11k_htt_tx_stats {
@@ -352,6 +355,9 @@ struct ath11k_htt_tx_stats {
 	u64 tx_duration;
 	u64 ba_fails;
 	u64 ack_fails;
+	u16 ru_start;
+	u16 ru_tones;
+	u32 mu_group[MAX_MU_GROUP_ID];
 };
 
 struct ath11k_per_ppdu_tx_stats {
@@ -454,11 +460,16 @@ struct ath11k_per_peer_tx_stats {
 	u32 succ_bytes;
 	u32 retry_bytes;
 	u32 failed_bytes;
+	u32 duration;
 	u16 succ_pkts;
 	u16 retry_pkts;
 	u16 failed_pkts;
-	u32 duration;
+	u16 ru_start;
+	u16 ru_tones;
 	u8 ba_fails;
+	u8 ppdu_type;
+	u32 mu_grpid;
+	u32 mu_pos;
 	bool is_ampdu;
 };
 
--- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c
+++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c
@@ -13,13 +13,39 @@
 #include "dp_rx.h"
 #include "debugfs_htt_stats.h"
 
+static inline u32 ath11k_he_tones_in_ru_to_nl80211_he_ru_alloc(u16 ru_tones)
+{
+	u32 ret = 0;
+	switch (ru_tones) {
+	case 26:
+		ret = NL80211_RATE_INFO_HE_RU_ALLOC_26;
+		break;
+	case 52:
+		ret = NL80211_RATE_INFO_HE_RU_ALLOC_52;
+		break;
+	case 106:
+		ret = NL80211_RATE_INFO_HE_RU_ALLOC_106;
+		break;
+	case 242:
+		ret = NL80211_RATE_INFO_HE_RU_ALLOC_242;
+		break;
+	case 484:
+		ret = NL80211_RATE_INFO_HE_RU_ALLOC_484;
+		break;
+	case 996:
+		ret = NL80211_RATE_INFO_HE_RU_ALLOC_996;
+		break;
+	}
+	return ret;
+}
+
 void ath11k_debugfs_sta_add_tx_stats(struct ath11k_sta *arsta,
 				     struct ath11k_per_peer_tx_stats *peer_stats,
 				     u8 legacy_rate_idx)
 {
 	struct rate_info *txrate = &arsta->txrate;
 	struct ath11k_htt_tx_stats *tx_stats;
-	int gi, mcs, bw, nss;
+	int gi, mcs, bw, nss, ru_type, ppdu_type;
 
 	if (!arsta->tx_stats)
 		return;
@@ -64,6 +90,43 @@ void ath11k_debugfs_sta_add_tx_stats(str
 		STATS_OP_FMT(RETRY).legacy[1][mcs] += peer_stats->retry_pkts;
 	}
 
+	ppdu_type = peer_stats->ppdu_type;
+	if ((ppdu_type == HTT_PPDU_STATS_PPDU_TYPE_MU_OFDMA ||
+	    ppdu_type == HTT_PPDU_STATS_PPDU_TYPE_MU_MIMO_OFDMA) &&
+	    (txrate->flags & RATE_INFO_FLAGS_HE_MCS)){
+		ru_type = peer_stats->ru_tones;
+
+		if (ru_type <= NL80211_RATE_INFO_HE_RU_ALLOC_996) {
+			STATS_OP_FMT(SUCC).ru_loc[0][ru_type] += peer_stats->succ_bytes;
+			STATS_OP_FMT(SUCC).ru_loc[1][ru_type] += peer_stats->succ_pkts;
+			STATS_OP_FMT(FAIL).ru_loc[0][ru_type] += peer_stats->failed_bytes;
+			STATS_OP_FMT(FAIL).ru_loc[1][ru_type] += peer_stats->failed_pkts;
+			STATS_OP_FMT(RETRY).ru_loc[0][ru_type] += peer_stats->retry_bytes;
+			STATS_OP_FMT(RETRY).ru_loc[1][ru_type] += peer_stats->retry_pkts;
+			if (peer_stats->is_ampdu) {
+				STATS_OP_FMT(AMPDU).ru_loc[0][ru_type] +=
+					peer_stats->succ_bytes + peer_stats->retry_bytes;
+				STATS_OP_FMT(AMPDU).ru_loc[1][ru_type] +=
+					peer_stats->succ_pkts + peer_stats->retry_pkts;
+			}
+		}
+	}
+
+	if (ppdu_type < HTT_PPDU_STATS_PPDU_TYPE_MAX) {
+		STATS_OP_FMT(SUCC).transmit_type[0][ppdu_type] += peer_stats->succ_bytes;
+		STATS_OP_FMT(SUCC).transmit_type[1][ppdu_type] += peer_stats->succ_pkts;
+		STATS_OP_FMT(FAIL).transmit_type[0][ppdu_type] += peer_stats->failed_bytes;
+		STATS_OP_FMT(FAIL).transmit_type[1][ppdu_type] += peer_stats->failed_pkts;
+		STATS_OP_FMT(RETRY).transmit_type[0][ppdu_type] += peer_stats->retry_bytes;
+		STATS_OP_FMT(RETRY).transmit_type[1][ppdu_type] += peer_stats->retry_pkts;
+		if (peer_stats->is_ampdu) {
+			STATS_OP_FMT(AMPDU).transmit_type[0][ppdu_type] +=
+				peer_stats->succ_bytes + peer_stats->retry_bytes;
+			STATS_OP_FMT(AMPDU).transmit_type[1][ppdu_type] +=
+				peer_stats->succ_pkts + peer_stats->retry_pkts;
+		}
+	}
+
 	if (peer_stats->is_ampdu) {
 		tx_stats->ba_fails += peer_stats->ba_fails;
 
@@ -124,6 +187,17 @@ void ath11k_debugfs_sta_add_tx_stats(str
 	STATS_OP_FMT(RETRY).gi[1][gi] += peer_stats->retry_pkts;
 
 	tx_stats->tx_duration += peer_stats->duration;
+
+	tx_stats->ru_start = peer_stats->ru_start;
+	tx_stats->ru_tones = peer_stats->ru_tones;
+
+	if (peer_stats->mu_grpid <= MAX_MU_GROUP_ID &&
+	    peer_stats->ppdu_type != HTT_PPDU_STATS_PPDU_TYPE_SU) {
+		if (peer_stats->mu_grpid & (MAX_MU_GROUP_ID - 1))
+			tx_stats->mu_group[peer_stats->mu_grpid] =
+						(peer_stats->mu_pos + 1);
+	}
+
 }
 
 void ath11k_debugfs_sta_update_txcompl(struct ath11k *ar,
@@ -138,6 +212,7 @@ void ath11k_debugfs_sta_update_txcompl(s
 	struct ath11k_peer *peer;
 	struct ath11k_sta *arsta;
 	struct ieee80211_sta *sta;
+	u16 num_tones_in_ru;
 	u16 rate;
 	u8 rate_idx = 0;
 	int ret;
@@ -164,6 +239,9 @@ void ath11k_debugfs_sta_update_txcompl(s
 	sgi = FIELD_GET(HAL_TX_RATE_STATS_INFO0_SGI,
 			ts->rate_stats);
 	bw = FIELD_GET(HAL_TX_RATE_STATS_INFO0_BW, ts->rate_stats);
+	num_tones_in_ru = FIELD_GET(HAL_TX_RATE_STATS_INFO0_TONES_IN_RU,
+				    ts->rate_stats);
+
 
 	if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11A ||
 	    pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11B) {
@@ -195,16 +273,27 @@ void ath11k_debugfs_sta_update_txcompl(s
 		if (sgi)
 			arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
 	} else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) {
-		arsta->txrate.mcs = ts->mcs;
+		arsta->txrate.mcs = mcs;
 		arsta->txrate.flags = RATE_INFO_FLAGS_HE_MCS;
-		arsta->txrate.he_gi = ath11k_he_gi_to_nl80211_he_gi(ts->sgi);
+		arsta->txrate.he_gi = ath11k_he_gi_to_nl80211_he_gi(sgi);
 		arsta->txrate.he_ru_alloc = ath11k_he_ru_tones_to_nl80211_he_ru_alloc(
-						ts->num_tones_in_ru);
+						num_tones_in_ru);
 	}
 
 	arsta->txrate.nss = arsta->last_txrate.nss;
 	arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw);
 
+	/* Currently only OFDMA flag is available in tx complettion status
+	 *  to indicate MUOFDMA ppdu type. Use SU ppdu type as of now to
+	 * indicate both SU/MU MIMO for failed/retry count.
+	 */
+	if (ts->flags & HAL_TX_STATUS_FLAGS_OFDMA)
+		peer_stats->ppdu_type = HTT_PPDU_STATS_PPDU_TYPE_MU_OFDMA;
+	else
+		peer_stats->ppdu_type = HTT_PPDU_STATS_PPDU_TYPE_SU;
+
+	peer_stats->ru_tones = arsta->txrate.he_ru_alloc;
+
 	ath11k_debugfs_sta_add_tx_stats(arsta, peer_stats, rate_idx);
 
 err_out:
@@ -220,12 +309,13 @@ static ssize_t ath11k_dbg_sta_dump_tx_st
 	struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
 	struct ath11k *ar = arsta->arvif->ar;
 	struct ath11k_htt_data_stats *stats;
-	static const char *str_name[ATH11K_STATS_TYPE_MAX] = {"succ", "fail",
+	static const char *str_name[ATH11K_STATS_TYPE_MAX] = {"success", "fail",
 							      "retry", "ampdu"};
 	static const char *str[ATH11K_COUNTER_TYPE_MAX] = {"bytes", "packets"};
 	int len = 0, i, j, k, retval = 0;
 	const int size = 2 * 4096;
-	char *buf;
+	char *buf, mu_group_id[MAX_MU_GROUP_LENGTH] = {0};
+	u32 index;
 
 	if (!arsta->tx_stats)
 		return -ENOENT;
@@ -243,45 +333,46 @@ static ssize_t ath11k_dbg_sta_dump_tx_st
 			len += scnprintf(buf + len, size - len, "%s_%s\n",
 					 str_name[k],
 					 str[j]);
+			len += scnprintf(buf + len, size - len, "==========\n");
 			len += scnprintf(buf + len, size - len,
-					 " HE MCS %s\n",
+					 " HE MCS %s\n\t",
 					 str[j]);
 			for (i = 0; i < ATH11K_HE_MCS_NUM; i++)
 				len += scnprintf(buf + len, size - len,
-						 "  %llu ",
+						 "%llu ",
 						 stats->he[j][i]);
 			len += scnprintf(buf + len, size - len, "\n");
 			len += scnprintf(buf + len, size - len,
-					 " VHT MCS %s\n",
+					 " VHT MCS %s\n\t",
 					 str[j]);
 			for (i = 0; i < ATH11K_VHT_MCS_NUM; i++)
 				len += scnprintf(buf + len, size - len,
-						 "  %llu ",
+						 "%llu ",
 						 stats->vht[j][i]);
 			len += scnprintf(buf + len, size - len, "\n");
-			len += scnprintf(buf + len, size - len, " HT MCS %s\n",
+			len += scnprintf(buf + len, size - len, " HT MCS %s\n\t",
 					 str[j]);
 			for (i = 0; i < ATH11K_HT_MCS_NUM; i++)
 				len += scnprintf(buf + len, size - len,
-						 "  %llu ", stats->ht[j][i]);
+						 "%llu ", stats->ht[j][i]);
 			len += scnprintf(buf + len, size - len, "\n");
 			len += scnprintf(buf + len, size - len,
 					" BW %s (20,40,80,160 MHz)\n", str[j]);
 			len += scnprintf(buf + len, size - len,
-					 "  %llu %llu %llu %llu\n",
+					 "\t%llu %llu %llu %llu\n",
 					 stats->bw[j][0], stats->bw[j][1],
 					 stats->bw[j][2], stats->bw[j][3]);
 			len += scnprintf(buf + len, size - len,
 					 " NSS %s (1x1,2x2,3x3,4x4)\n", str[j]);
 			len += scnprintf(buf + len, size - len,
-					 "  %llu %llu %llu %llu\n",
+					 "\t%llu %llu %llu %llu\n",
 					 stats->nss[j][0], stats->nss[j][1],
 					 stats->nss[j][2], stats->nss[j][3]);
 			len += scnprintf(buf + len, size - len,
 					 " GI %s (0.4us,0.8us,1.6us,3.2us)\n",
 					 str[j]);
 			len += scnprintf(buf + len, size - len,
-					 "  %llu %llu %llu %llu\n",
+					 "\t%llu %llu %llu %llu\n",
 					 stats->gi[j][0], stats->gi[j][1],
 					 stats->gi[j][2], stats->gi[j][3]);
 			len += scnprintf(buf + len, size - len,
@@ -290,9 +381,67 @@ static ssize_t ath11k_dbg_sta_dump_tx_st
 			for (i = 0; i < ATH11K_LEGACY_NUM; i++)
 				len += scnprintf(buf + len, size - len, "%llu ",
 						 stats->legacy[j][i]);
-			len += scnprintf(buf + len, size - len, "\n");
+
+			len += scnprintf(buf + len, size - len, "\n ru %s: \n", str[j]);
+			len += scnprintf(buf + len, size - len,
+					 "\tru 26: %llu\n", stats->ru_loc[j][0]);
+			len += scnprintf(buf + len, size - len,
+					 "\tru 52: %llu \n", stats->ru_loc[j][1]);
+			len += scnprintf(buf + len, size - len,
+					 "\tru 106: %llu \n", stats->ru_loc[j][2]);
+			len += scnprintf(buf + len, size - len,
+					 "\tru 242: %llu \n", stats->ru_loc[j][3]);
+			len += scnprintf(buf + len, size - len,
+					 "\tru 484: %llu \n", stats->ru_loc[j][4]);
+			len += scnprintf(buf + len, size - len,
+					 "\tru 996: %llu \n", stats->ru_loc[j][5]);
+
+			len += scnprintf(buf + len, size - len,
+					 " ppdu type %s: \n", str[j]);
+			if (k == ATH11K_STATS_TYPE_FAIL ||
+			    k == ATH11K_STATS_TYPE_RETRY) {
+				len += scnprintf(buf + len, size - len,
+						 "\tSU/MIMO: %llu\n",
+						 stats->transmit_type[j][0]);
+				len += scnprintf(buf + len, size - len,
+						 "\tOFDMA/OFDMA_MIMO: %llu\n",
+				 		 stats->transmit_type[j][2]);
+			} else {
+				len += scnprintf(buf + len, size - len,
+						 "\tSU: %llu\n",
+						 stats->transmit_type[j][0]);
+				len += scnprintf(buf + len, size - len,
+						 "\tMIMO: %llu\n",
+						 stats->transmit_type[j][1]);
+				len += scnprintf(buf + len, size - len,
+						 "\tOFDMA: %llu\n",
+				 		 stats->transmit_type[j][2]);
+				len += scnprintf(buf + len, size - len,
+						 "\tOFDMA_MIMO: %llu\n",
+						 stats->transmit_type[j][3]);
+			}
+		}
+	}
+
+	len += scnprintf(buf + len, size - len, "\n");
+
+	for (i = 0; i < MAX_MU_GROUP_ID;) {
+		index = 0;
+		for (j = 0; j < MAX_MU_GROUP_SHOW && i < MAX_MU_GROUP_ID;
+		     j++) {
+			index += snprintf(&mu_group_id[index],
+					     MAX_MU_GROUP_LENGTH - index,
+					     " %d",
+					     arsta->tx_stats->mu_group[i]);
+			i++;
 		}
+		len += scnprintf(buf + len, size - len,
+				  "User position list for GID %02d->%d: [%s]\n",
+				  i - MAX_MU_GROUP_SHOW, i - 1, mu_group_id);
 	}
+	len += scnprintf(buf + len, size - len,
+			  "\nLast Packet RU index [%d], Size [%d]\n",
+			  arsta->tx_stats->ru_start, arsta->tx_stats->ru_tones);
 
 	len += scnprintf(buf + len, size - len,
 			 "\nTX duration\n %llu usecs\n",
@@ -301,6 +450,7 @@ static ssize_t ath11k_dbg_sta_dump_tx_st
 			"BA fails\n %llu\n", arsta->tx_stats->ba_fails);
 	len += scnprintf(buf + len, size - len,
 			"ack fails\n %llu\n", arsta->tx_stats->ack_fails);
+
 	spin_unlock_bh(&ar->data_lock);
 
 	if (len > size)
--- a/drivers/net/wireless/ath/ath11k/dp.h
+++ b/drivers/net/wireless/ath/ath11k/dp.h
@@ -555,6 +555,45 @@ enum htt_ppdu_stats_tag_type {
 				    BIT(HTT_PPDU_STATS_TAG_TX_MGMTCTRL_PAYLOAD) | \
 				    HTT_PPDU_STATS_TAG_DEFAULT)
 
+#define HTT_STATS_FRAMECTRL_TYPE_MASK 0x0C
+#define HTT_STATS_GET_FRAME_CTRL_TYPE(_val)	\
+		(((_val) & HTT_STATS_FRAMECTRL_TYPE_MASK) >> 2)
+#define HTT_STATS_FRAME_CTRL_TYPE_MGMT	0x0
+#define HTT_STATS_FRAME_CTRL_TYPE_CTRL	0x1
+#define HTT_STATS_FRAME_CTRL_TYPE_DATA	0x2
+#define HTT_STATS_FRAME_CTRL_TYPE_RESV	0x3
+
+enum htt_stats_frametype {
+	HTT_STATS_FTYPE_SGEN_NDPA = 0,
+	HTT_STATS_FTYPE_SGEN_NDP,
+	HTT_STATS_FTYPE_SGEN_BRP,
+	HTT_STATS_FTYPE_SGEN_BAR,
+	HTT_STATS_FTYPE_SGEN_RTS,
+	HTT_STATS_FTYPE_SGEN_CTS,
+	HTT_STATS_FTYPE_SGEN_CFEND,
+	HTT_STATS_FTYPE_SGEN_AX_NDPA,
+	HTT_STATS_FTYPE_SGEN_AX_NDP,
+	HTT_STATS_FTYPE_SGEN_MU_TRIG,
+	HTT_STATS_FTYPE_SGEN_MU_BAR,
+	HTT_STATS_FTYPE_SGEN_MU_BRP,
+	HTT_STATS_FTYPE_SGEN_MU_RTS,
+	HTT_STATS_FTYPE_SGEN_MU_BSR,
+	HTT_STATS_FTYPE_SGEN_UL_BSR,
+	HTT_STATS_FTYPE_SGEN_UL_BSR_TRIGGER = HTT_STATS_FTYPE_SGEN_UL_BSR,
+	HTT_STATS_FTYPE_TIDQ_DATA_SU,
+	HTT_STATS_FTYPE_TIDQ_DATA_MU,
+	HTT_STATS_FTYPE_SGEN_UL_BSR_RESP,
+	HTT_STATS_FTYPE_SGEN_QOS_NULL,
+	HTT_STATS_FTYPE_MAX,
+};
+
+enum htt_stats_internal_ppdu_frametype {
+	HTT_STATS_PPDU_FTYPE_CTRL,
+	HTT_STATS_PPDU_FTYPE_DATA,
+	HTT_STATS_PPDU_FTYPE_BAR,
+	HTT_STATS_PPDU_FTYPE_MAX
+};
+
 /* HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG Message
  *
  * details:
@@ -1168,6 +1207,19 @@ enum htt_ppdu_stats_gi {
 #define HTT_PPDU_STATS_USER_RATE_INFO0_USER_POS_M	GENMASK(3, 0)
 #define HTT_PPDU_STATS_USER_RATE_INFO0_MU_GROUP_ID_M	GENMASK(11, 4)
 
+enum HTT_PPDU_STATS_PPDU_TYPE {
+	HTT_PPDU_STATS_PPDU_TYPE_SU,
+	HTT_PPDU_STATS_PPDU_TYPE_MU_MIMO,
+	HTT_PPDU_STATS_PPDU_TYPE_MU_OFDMA,
+	HTT_PPDU_STATS_PPDU_TYPE_MU_MIMO_OFDMA,
+	HTT_PPDU_STATS_PPDU_TYPE_UL_TRIG,
+	HTT_PPDU_STATS_PPDU_TYPE_BURST_BCN,
+	HTT_PPDU_STATS_PPDU_TYPE_UL_BSR_RESP,
+	HTT_PPDU_STATS_PPDU_TYPE_UL_BSR_TRIG,
+	HTT_PPDU_STATS_PPDU_TYPE_UL_RESP,
+	HTT_PPDU_STATS_PPDU_TYPE_MAX
+};
+
 #define HTT_PPDU_STATS_USER_RATE_INFO1_RESP_TYPE_VALD_M	BIT(0)
 #define HTT_PPDU_STATS_USER_RATE_INFO1_PPDU_TYPE_M	GENMASK(5, 1)
 
@@ -1195,6 +1247,12 @@ enum htt_ppdu_stats_gi {
 		FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_GI_M, _val)
 #define HTT_USR_RATE_DCM(_val) \
 		FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_DCM_M, _val)
+#define HTT_USR_RATE_PPDU_TYPE(_val) \
+		FIELD_GET(HTT_PPDU_STATS_USER_RATE_INFO1_PPDU_TYPE_M, _val)
+#define HTT_USR_RATE_MU_GRPID(_val) \
+		FIELD_GET(HTT_PPDU_STATS_USER_RATE_INFO0_MU_GROUP_ID_M, _val)
+#define HTT_USR_RATE_USR_POS(_val) \
+		FIELD_GET(HTT_PPDU_STATS_USER_RATE_INFO0_USER_POS_M, _val)
 
 #define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_LTF_SIZE_M		GENMASK(1, 0)
 #define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_STBC_M		BIT(2)
@@ -1298,6 +1356,21 @@ struct htt_ppdu_stats_usr_cmpltn_ack_ba_
 	u32 success_bytes;
 } __packed;
 
+#define HTT_PPDU_STATS_USR_CMN_FLAG_DELAYBA	BIT(14)
+#define HTT_PPDU_STATS_USR_CMN_HDR_SW_PEERID	GENMASK(31, 16)
+#define HTT_PPDU_STATS_USR_CMN_CTL_FRM_CTRL	GENMASK(15, 0)
+
+struct  htt_ppdu_stats_user_common {
+	u8 tid_num;
+	u8 vdev_id;
+	u16 sw_peer_id;
+	u32 info;
+	u32 ctrl;
+	u32 buffer_paddr_31_0;
+	u32 buffer_paddr_39_32;
+	u32 host_opaque_cookie;
+} __packed;
+
 struct htt_ppdu_stats_usr_cmn_array {
 	struct htt_tlv tlv_hdr;
 	u32 num_ppdu_stats;
@@ -1311,14 +1384,16 @@ struct htt_ppdu_stats_usr_cmn_array {
 
 struct htt_ppdu_user_stats {
 	u16 peer_id;
+	u16 delay_ba;
 	u32 tlv_flags;
 	bool is_valid_peer_id;
 	struct htt_ppdu_stats_user_rate rate;
 	struct htt_ppdu_stats_usr_cmpltn_cmn cmpltn_cmn;
 	struct htt_ppdu_stats_usr_cmpltn_ack_ba_status ack_ba;
+	struct htt_ppdu_stats_user_common common;
 };
 
-#define HTT_PPDU_STATS_MAX_USERS	8
+#define HTT_PPDU_STATS_MAX_USERS	37
 #define HTT_PPDU_DESC_MAX_DEPTH	16
 
 struct htt_ppdu_stats {
@@ -1327,7 +1402,7 @@ struct htt_ppdu_stats {
 };
 
 struct htt_ppdu_stats_info {
-	u32 ppdu_id;
+	u32 tlv_bitmap, ppdu_id, frame_type, frame_ctrl, delay_ba, bar_num_users;
 	struct htt_ppdu_stats ppdu_stats;
 	struct list_head list;
 };
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -1188,9 +1188,10 @@ static int ath11k_htt_tlv_ppdu_stats_par
 					   void *data)
 {
 	struct htt_ppdu_stats_info *ppdu_info;
-	struct htt_ppdu_user_stats *user_stats;
+	struct htt_ppdu_user_stats *user_stats = NULL;
 	int cur_user;
 	u16 peer_id;
+	u32 frame_type;
 
 	ppdu_info = (struct htt_ppdu_stats_info *)data;
 
@@ -1203,6 +1204,26 @@ static int ath11k_htt_tlv_ppdu_stats_par
 		}
 		memcpy((void *)&ppdu_info->ppdu_stats.common, ptr,
 		       sizeof(struct htt_ppdu_stats_common));
+		frame_type =
+			FIELD_GET(HTT_PPDU_STATS_CMN_FLAGS_FRAME_TYPE_M,
+				  ppdu_info->ppdu_stats.common.flags);
+		switch (frame_type) {
+		case HTT_STATS_FTYPE_TIDQ_DATA_SU:
+		case HTT_STATS_FTYPE_TIDQ_DATA_MU:
+			if (HTT_STATS_GET_FRAME_CTRL_TYPE(ppdu_info->frame_ctrl) <= HTT_STATS_FRAME_CTRL_TYPE_CTRL)
+				ppdu_info->frame_type = HTT_STATS_PPDU_FTYPE_CTRL;
+			else
+				ppdu_info->frame_type = HTT_STATS_PPDU_FTYPE_DATA;
+		break;
+		case HTT_STATS_FTYPE_SGEN_MU_BAR:
+		case HTT_STATS_FTYPE_SGEN_BAR:
+			ppdu_info->frame_type = HTT_STATS_PPDU_FTYPE_BAR;
+		break;
+		default:
+			ppdu_info->frame_type = HTT_STATS_PPDU_FTYPE_CTRL;
+		break;
+		}
+
 		break;
 	case HTT_PPDU_STATS_TAG_USR_RATE:
 		if (len < sizeof(struct htt_ppdu_stats_user_rate)) {
@@ -1235,6 +1256,7 @@ static int ath11k_htt_tlv_ppdu_stats_par
 						      peer_id);
 		if (cur_user < 0)
 			return -EINVAL;
+		ppdu_info->bar_num_users += 1;
 		user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user];
 		user_stats->peer_id = peer_id;
 		user_stats->is_valid_peer_id = true;
@@ -1263,44 +1285,30 @@ static int ath11k_htt_tlv_ppdu_stats_par
 		       sizeof(struct htt_ppdu_stats_usr_cmpltn_ack_ba_status));
 		user_stats->tlv_flags |= BIT(tag);
 		break;
-	}
-	return 0;
-}
-
-int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len,
-			   int (*iter)(struct ath11k_base *ar, u16 tag, u16 len,
-				       const void *ptr, void *data),
-			   void *data)
-{
-	const struct htt_tlv *tlv;
-	const void *begin = ptr;
-	u16 tlv_tag, tlv_len;
-	int ret = -EINVAL;
-
-	while (len > 0) {
-		if (len < sizeof(*tlv)) {
-			ath11k_err(ab, "htt tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n",
-				   ptr - begin, len, sizeof(*tlv));
+	case HTT_PPDU_STATS_TAG_USR_COMMON:
+		if (len < sizeof(struct htt_ppdu_stats_user_common)) {
+			ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n",
+				    len, tag);
 			return -EINVAL;
 		}
-		tlv = (struct htt_tlv *)ptr;
-		tlv_tag = FIELD_GET(HTT_TLV_TAG, tlv->header);
-		tlv_len = FIELD_GET(HTT_TLV_LEN, tlv->header);
-		ptr += sizeof(*tlv);
-		len -= sizeof(*tlv);
-
-		if (tlv_len > len) {
-			ath11k_err(ab, "htt tlv parse failure of tag %u at byte %zd (%zu bytes left, %u expected)\n",
-				   tlv_tag, ptr - begin, len, tlv_len);
+		peer_id = ((struct htt_ppdu_stats_user_common *)ptr)->sw_peer_id;
+		cur_user = ath11k_get_ppdu_user_index(&ppdu_info->ppdu_stats,
+						      peer_id);
+		if (cur_user < 0)
 			return -EINVAL;
-		}
-		ret = iter(ab, tlv_tag, tlv_len, ptr, data);
-		if (ret == -ENOMEM)
-			return ret;
-
-		ptr += tlv_len;
-		len -= tlv_len;
+		user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user];
+		memcpy(&user_stats->common, ptr,
+		       sizeof(struct htt_ppdu_stats_user_common));
+		ppdu_info->frame_ctrl = FIELD_GET(HTT_PPDU_STATS_USR_CMN_CTL_FRM_CTRL,
+						  user_stats->common.ctrl);
+		user_stats->delay_ba = FIELD_GET(HTT_PPDU_STATS_USR_CMN_FLAG_DELAYBA,
+						  user_stats->common.info);
+		ppdu_info->delay_ba = user_stats->delay_ba;
+		break;
+	default:
+		break;
 	}
+	ppdu_info->tlv_bitmap |= BIT(tag);
 	return 0;
 }
 
@@ -1318,8 +1326,8 @@ ath11k_update_per_peer_tx_stats(struct a
 	struct htt_ppdu_stats_common *common = &ppdu_stats->common;
 	int ret;
 	u8 flags, mcs, nss, bw, sgi, dcm, rate_idx = 0;
-	u32 succ_bytes = 0;
-	u16 rate = 0, succ_pkts = 0;
+	u32 succ_bytes = 0, ppdu_type, mu_grpid, mu_pos;
+	u16 rate = 0, succ_pkts = 0, ru_tone, ru_start;
 	u32 tx_duration = 0;
 	u8 tid = HTT_PPDU_STATS_NON_QOS_TID;
 	bool is_ampdu = false;
@@ -1353,6 +1361,11 @@ ath11k_update_per_peer_tx_stats(struct a
 	mcs = HTT_USR_RATE_MCS(user_rate->rate_flags);
 	sgi = HTT_USR_RATE_GI(user_rate->rate_flags);
 	dcm = HTT_USR_RATE_DCM(user_rate->rate_flags);
+	ppdu_type = HTT_USR_RATE_PPDU_TYPE(user_rate->info1);
+	mu_grpid = HTT_USR_RATE_MU_GRPID(user_rate->info0);
+	mu_pos = HTT_USR_RATE_USR_POS(user_rate->info0);
+	ru_start = user_rate->ru_start;
+	ru_tone = user_rate->ru_end;
 
 	/* Note: If host configured fixed rates and in some other special
 	 * cases, the broadcast/management frames are sent in different rates.
@@ -1451,6 +1464,12 @@ ath11k_update_per_peer_tx_stats(struct a
 		peer_stats->ba_fails =
 			HTT_USR_CMPLTN_LONG_RETRY(usr_stats->cmpltn_cmn.flags) +
 			HTT_USR_CMPLTN_SHORT_RETRY(usr_stats->cmpltn_cmn.flags);
+		peer_stats->ppdu_type = ppdu_type;
+		peer_stats->ru_tones = ru_tone;
+		peer_stats->ru_start = ru_start;
+		peer_stats->mu_grpid = mu_grpid;
+		peer_stats->mu_pos   = mu_pos;
+		peer_stats->ru_tones = arsta->txrate.he_ru_alloc;
 
 		if (ath11k_debugfs_is_extd_tx_stats_enabled(ar))
 			ath11k_debugfs_sta_add_tx_stats(arsta, peer_stats, rate_idx);
@@ -1502,13 +1521,87 @@ struct htt_ppdu_stats_info *ath11k_dp_ht
 	return ppdu_info;
 }
 
+void ath11k_copy_to_delay_stats(struct ath11k_peer *peer,
+				  struct htt_ppdu_user_stats* usr_stats)
+{
+	peer->ppdu_stats_delayba.reserved0 = usr_stats->rate.reserved0;
+	peer->ppdu_stats_delayba.sw_peer_id = usr_stats->rate.sw_peer_id;
+	peer->ppdu_stats_delayba.info0 = usr_stats->rate.info0;
+	peer->ppdu_stats_delayba.ru_end = usr_stats->rate.ru_end;
+	peer->ppdu_stats_delayba.ru_start = usr_stats->rate.ru_start;
+	peer->ppdu_stats_delayba.info1 = usr_stats->rate.info1;
+	peer->ppdu_stats_delayba.rate_flags = usr_stats->rate.rate_flags;
+	peer->ppdu_stats_delayba.resp_rate_flags = usr_stats->rate.resp_rate_flags;
+
+	peer->delayba_flag = true;
+}
+
+void ath11k_copy_to_bar(struct ath11k_peer *peer,
+			struct htt_ppdu_user_stats* usr_stats)
+{
+	usr_stats->rate.reserved0 = peer->ppdu_stats_delayba.reserved0;
+	usr_stats->rate.sw_peer_id = peer->ppdu_stats_delayba.sw_peer_id;
+	usr_stats->rate.info0 = peer->ppdu_stats_delayba.info0;
+	usr_stats->rate.ru_end = peer->ppdu_stats_delayba.ru_end;
+	usr_stats->rate.ru_start = peer->ppdu_stats_delayba.ru_start;
+	usr_stats->rate.info1 = peer->ppdu_stats_delayba.info1;
+	usr_stats->rate.rate_flags = peer->ppdu_stats_delayba.rate_flags;
+	usr_stats->rate.resp_rate_flags = peer->ppdu_stats_delayba.resp_rate_flags;
+
+	peer->delayba_flag = false;
+}
+
+int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len,
+			   int (*iter)(struct ath11k_base *ar, u16 tag, u16 len,
+				       const void *ptr, void *data),
+			   void *data)
+{
+	const struct htt_tlv *tlv;
+	const void *begin = ptr;
+	u16 tlv_tag, tlv_len;
+	int ret = -EINVAL;
+	struct htt_ppdu_stats_info * ppdu_info = NULL;
+
+	ppdu_info = (struct htt_ppdu_stats_info *)data;
+	ppdu_info->tlv_bitmap = 0;
+	while (len > 0) {
+		if (len < sizeof(*tlv)) {
+			ath11k_err(ab, "htt tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n",
+				   ptr - begin, len, sizeof(*tlv));
+			return -EINVAL;
+		}
+		tlv = (struct htt_tlv *)ptr;
+		tlv_tag = FIELD_GET(HTT_TLV_TAG, tlv->header);
+		tlv_len = FIELD_GET(HTT_TLV_LEN, tlv->header);
+		ptr += sizeof(*tlv);
+		len -= sizeof(*tlv);
+
+		if (tlv_len > len) {
+			ath11k_err(ab, "htt tlv parse failure of tag %hhu at byte %zd (%zu bytes left, %hhu expected)\n",
+				   tlv_tag, ptr - begin, len, tlv_len);
+			return -EINVAL;
+		}
+
+		ret = iter(ab, tlv_tag, tlv_len, ptr, ppdu_info);
+		if (ret == -ENOMEM)
+			return ret;
+
+		ptr += tlv_len;
+		len -= tlv_len;
+	}
+	return 0;
+}
+
 static int ath11k_htt_pull_ppdu_stats(struct ath11k_base *ab,
 				      struct sk_buff *skb)
 {
 	struct ath11k_htt_ppdu_stats_msg *msg;
 	struct htt_ppdu_stats_info *ppdu_info;
+	struct ath11k_peer *peer = NULL;
+	struct htt_ppdu_user_stats* usr_stats = NULL;
+	u32 peer_id = 0;
 	struct ath11k *ar;
-	int ret;
+	int ret, i;
 	u8 pdev_id;
 	u32 ppdu_id, len;
 
@@ -1547,6 +1640,47 @@ static int ath11k_htt_pull_ppdu_stats(st
 		goto exit;
 	}
 
+	/* back up data rate tlv for all peers */
+	if (ppdu_info->frame_type == HTT_STATS_PPDU_FTYPE_DATA &&
+	    (ppdu_info->tlv_bitmap & (1 << HTT_PPDU_STATS_TAG_USR_COMMON)) &&
+	    ppdu_info->delay_ba) {
+
+		for (i = 0; i < ppdu_info->ppdu_stats.common.num_users; i++) {
+			peer_id = ppdu_info->ppdu_stats.user_stats[i].peer_id;
+			spin_lock_bh(&ab->base_lock);
+			peer = ath11k_peer_find_by_id(ab, peer_id);
+			if (!peer) {
+				spin_unlock_bh(&ab->base_lock);
+				continue;
+			}
+
+			usr_stats = &ppdu_info->ppdu_stats.user_stats[i];
+			if (usr_stats->delay_ba)
+				ath11k_copy_to_delay_stats(peer, usr_stats);
+			spin_unlock_bh(&ab->base_lock);
+		}
+	}
+
+	/* restore all peers' data rate tlv to mu-bar tlv */
+	if (ppdu_info->frame_type == HTT_STATS_PPDU_FTYPE_BAR &&
+	    (ppdu_info->tlv_bitmap & (1 << HTT_PPDU_STATS_TAG_USR_COMMON))) {
+
+		for (i = 0; i < ppdu_info->bar_num_users; i++) {
+			peer_id = ppdu_info->ppdu_stats.user_stats[i].peer_id;
+			spin_lock_bh(&ab->base_lock);
+			peer = ath11k_peer_find_by_id(ab, peer_id);
+			if (!peer) {
+				spin_unlock_bh(&ab->base_lock);
+				continue;
+			}
+
+			usr_stats = &ppdu_info->ppdu_stats.user_stats[i];
+			if (peer->delayba_flag)
+				ath11k_copy_to_bar(peer, usr_stats);
+			spin_unlock_bh(&ab->base_lock);
+		}
+	}
+
 	spin_unlock_bh(&ar->data_lock);
 exit:
 	rcu_read_unlock();
--- a/drivers/net/wireless/ath/ath11k/rx_desc.h
+++ b/drivers/net/wireless/ath/ath11k/rx_desc.h
@@ -1407,6 +1407,11 @@ struct hal_rx_desc {
 	} u;
 } __packed;
 
+#define MAX_USER_POS 8
+#define MAX_MU_GROUP_ID 64
+#define MAX_MU_GROUP_SHOW 16
+#define MAX_MU_GROUP_LENGTH (6 * MAX_MU_GROUP_SHOW)
+
 #define HAL_RX_RU_ALLOC_TYPE_MAX 6
 #define RU_26  1
 #define RU_52  2
--- a/drivers/net/wireless/ath/ath11k/peer.h
+++ b/drivers/net/wireless/ath/ath11k/peer.h
@@ -6,6 +6,17 @@
 #ifndef ATH11K_PEER_H
 #define ATH11K_PEER_H
 
+struct ppdu_user_delayba {
+	u8 reserved0;
+	u16 sw_peer_id;
+	u32 info0;
+	u16 ru_end;
+	u16 ru_start;
+	u32 info1;
+	u32 rate_flags;
+	u32 resp_rate_flags;
+};
+
 struct ath11k_peer {
 	struct list_head list;
 	struct ieee80211_sta *sta;
@@ -28,6 +39,9 @@ struct ath11k_peer {
 	u8 ucast_keyidx;
 	u16 sec_type;
 	u16 sec_type_grp;
+
+	struct ppdu_user_delayba ppdu_stats_delayba;
+	bool delayba_flag;
 };
 
 void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id);
