diff mbox series

[RFC,v1,086/256] cl8k: add fw/fw_dbg.c

Message ID 20210617160223.160998-87-viktor.barna@celeno.com
State New
Headers show
Series wireless: cl8k driver for Celeno IEEE 802.11ax devices | expand

Commit Message

Viktor Barna June 17, 2021, 3:59 p.m. UTC
From: Viktor Barna <viktor.barna@celeno.com>

(Part of the split. Please, take a look at the cover letter for more
details).

Signed-off-by: Viktor Barna <viktor.barna@celeno.com>
---
 drivers/net/wireless/celeno/cl8k/fw/fw_dbg.c | 2686 ++++++++++++++++++
 1 file changed, 2686 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/fw/fw_dbg.c

--
2.30.0
diff mbox series

Patch

diff --git a/drivers/net/wireless/celeno/cl8k/fw/fw_dbg.c b/drivers/net/wireless/celeno/cl8k/fw/fw_dbg.c
new file mode 100644
index 000000000000..413f45b433c6
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/fw/fw_dbg.c
@@ -0,0 +1,2686 @@ 
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+#include <linux/utsname.h>
+
+#include "utils/utils.h"
+#include "fw/fw_dbg.h"
+#ifdef CONFIG_CL_PCIE
+#include "bus/pci/ipc.h"
+#endif
+#include "band.h"
+#include "chip.h"
+#include "coredump.h"
+#include "ela.h"
+#include "utils/file.h"
+#include "dbgfile.h"
+#include "rx/rx.h"
+#include "fw/msg_tx.h"
+
+/* Work struct wrapper for print statistics */
+struct cl_print_stats_work {
+       struct work_struct ws;
+       struct cl_hw *cl_hw;
+       u32 dbg_info_type;
+};
+
+#define FW_DBG_INVALID_SESSION U8_MAX
+
+#define PRINT_FW(cl_hw, fmt, ...) \
+       pr_debug("%cmac%u " fmt, (cl_hw)->fw_prefix, (cl_hw)->chip->idx, ##__VA_ARGS__)
+
+/*
+ * Display 2 digit decimal fraction.
+ * Example: x = 541, y = 19 = 28.47368
+ *   ==> 47
+ */
+#define DECIMAL_FRACTION_X2(x, y) (100 * ((x) - (y) * ((x) / (y))) / (y))
+
+static void cl_print_tx_stats(struct cl_hw *cl_hw, struct cl_txl_statistics *tx_stats)
+{
+       int i;
+       u8 per = 0;
+       u64 total_retry = 0;
+       u64 total_tx = 0;
+       u32 total_natt = 0;
+       u32 avg_backoff = 0;
+       u32 agg_size_total = 0;
+       u32 agg_size_x100 = 0;
+       u32 total_vns_off = 0;
+       u32 total_vns_on = 0;
+       bool is_agg_in_txop = false;
+       struct cl_txl_agg_statistics *agg = &tx_stats->agg;
+       struct cl_txl_htp_statistics *htp = &tx_stats->htp;
+       struct cl_txl_natt_statistics *natt = &tx_stats->natt;
+       struct cl_txl_vns_statistics *vns = &tx_stats->vns;
+       struct cl_txl_fec_statistics *fec = &tx_stats->fec;
+       struct cl_txl_backoff_params *backoff_params = &tx_stats->backoff_params;
+       struct cl_txl_rts_cts_statistics *rts_cts = &tx_stats->rts_cts;
+       struct cl_txl_underrun_statistics *underrun = &tx_stats->underrun;
+
+       const char *fw_tx_backoff_str[CL_MAX_FRM_TYPE] = {
+               [CE_BACKOFF_25]          = "<25",
+               [CE_BACKOFF_50]          = "50",
+               [CE_BACKOFF_100]         = "100",
+               [CE_BACKOFF_500]         = "500",
+               [CE_BACKOFF_1000]        = "1000",
+               [CE_BACKOFF_5000]        = "5000",
+               [CE_BACKOFF_10000]       = "10000",
+               [CE_BACKOFF_20000]       = "20000",
+               [CE_BACKOFF_20000_ABOVE] = ">20000",
+       };
+
+       /* Singles info */
+       PRINT_FW(cl_hw, "TX statistics - singles\n");
+       PRINT_FW(cl_hw, "------------------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "| q |sent      |retry     |lft exp   |ret limit |ret lim ps|per %%|\n");
+       PRINT_FW(cl_hw, "|---+----------+----------+----------+----------+----------+-----|\n");
+
+       for (i = 0; i < IPC_TX_QUEUE_CNT; i++) {
+               total_retry = tx_stats->single[i].total_rtx_cnt +
+                       tx_stats->single[i].total_lifetime_expired_cnt +
+                       tx_stats->single[i].total_rtx_limit_reached;
+
+               total_tx = tx_stats->single[i].total_cnt + total_retry;
+
+               if (total_tx == 0)
+                       continue;
+
+               per = (u8)div64_u64(total_retry * 100, total_tx);
+
+               PRINT_FW(cl_hw, "|%3u|%10u|%10u|%10u|%10u|%10u|%5u|\n",
+                        i,
+                        tx_stats->single[i].total_cnt,
+                        tx_stats->single[i].total_rtx_cnt,
+                        tx_stats->single[i].total_lifetime_expired_cnt,
+                        tx_stats->single[i].total_rtx_limit_reached,
+                        tx_stats->single[i].total_rtx_limit_reached_ps,
+                        per);
+       }
+
+       PRINT_FW(cl_hw, "------------------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "\n");
+
+       /* Aggregation info */
+       PRINT_FW(cl_hw, "TX statistics - aggregations - MAX [%u]\n", IPC_MAX_BA_SESSIONS);
+       PRINT_FW(cl_hw, "-----------------------------------------------------------------------------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "|Q |Total    |Total    |Lifetime|Retry |BA       |BA not   |BA     |BA     |BA    |Below BA|Above BA|ACK    |PS BA not|Total|\n");
+       PRINT_FW(cl_hw, "|  |sent     |retry    |expired |Limit |received |received |Cleared|Invalid|un-exp|window  |window  |inst BA|received |Per %%|\n");
+       PRINT_FW(cl_hw, "|--+---------+---------+--------+------+---------+---------+-------+-------+------+--------+--------+-------+---------+-----|\n");
+
+       for (i = 0; i < IPC_MAX_BA_SESSIONS; i++) {
+               total_retry = tx_stats->ba[i].total_rtx_cnt +
+                       tx_stats->ba[i].total_lifetime_expired_cnt +
+                       tx_stats->ba[i].total_rtx_limit_reached;
+
+               total_tx = tx_stats->ba[i].total_cnt;
+
+               if (total_tx == 0)
+                       continue;
+
+               per = (u8)div64_u64(total_retry * 100, total_tx);
+
+               PRINT_FW(cl_hw, "|%2u|%9u|%9u|%8u|%6u|%9u|%9u|%7u|%7u|%6u|%8u|%8u|%7u|%9u|%5u|\n",
+                        i,
+                        tx_stats->ba[i].total_cnt,
+                        tx_stats->ba[i].total_rtx_cnt,
+                        tx_stats->ba[i].total_lifetime_expired_cnt,
+                        tx_stats->ba[i].total_rtx_limit_reached,
+                        tx_stats->ba[i].total_ba_received,
+                        tx_stats->ba[i].total_ba_not_received_cnt,
+                        tx_stats->ba[i].total_cleard_ba,
+                        tx_stats->ba[i].total_invalid_ba,
+                        tx_stats->ba[i].total_unexpected_ba,
+                        tx_stats->ba[i].total_packets_below_baw,
+                        tx_stats->ba[i].total_packets_above_baw,
+                        tx_stats->ba[i].total_ack_instead_ba,
+                        tx_stats->ba[i].total_ba_not_received_cnt_ps,
+                        per);
+       }
+       PRINT_FW(cl_hw, "-----------------------------------------------------------------------------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "\n");
+
+       /* TID info */
+       PRINT_FW(cl_hw, "----------------\n");
+       PRINT_FW(cl_hw, "|TID|NumPackets|\n");
+       PRINT_FW(cl_hw, "|---+----------|\n");
+
+       for (i = 0; i < TID_MAX; i++) {
+               if (tx_stats->tid[i].total_tid_desc_cnt == 0)
+                       continue;
+
+               PRINT_FW(cl_hw, "|%3d|%10u|\n", i, tx_stats->tid[i].total_tid_desc_cnt);
+       }
+
+       PRINT_FW(cl_hw, "----------------\n");
+       PRINT_FW(cl_hw, "\n");
+
+       /* AC info */
+       PRINT_FW(cl_hw, "---------------------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "|     AC     |    0     |    1     |    2     |    3     |    4     |\n");
+       PRINT_FW(cl_hw, "|------------+----------+----------+----------+----------+----------|\n");
+       PRINT_FW(cl_hw, "|Num switches|%10u|%10u|%10u|%10u|%10u|\n",
+                tx_stats->ac[0].total_q_switch_cnt,
+                tx_stats->ac[1].total_q_switch_cnt,
+                tx_stats->ac[2].total_q_switch_cnt,
+                tx_stats->ac[3].total_q_switch_cnt,
+                tx_stats->ac[4].total_q_switch_cnt);
+       PRINT_FW(cl_hw, "|Num txdesc  |%10u|%10u|%10u|%10u|%10u|\n",
+                tx_stats->ac[0].total_ac_desc_cnt,
+                tx_stats->ac[1].total_ac_desc_cnt,
+                tx_stats->ac[2].total_ac_desc_cnt,
+                tx_stats->ac[3].total_ac_desc_cnt,
+                tx_stats->ac[4].total_ac_desc_cnt);
+       PRINT_FW(cl_hw, "---------------------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "\n");
+
+       /* Underrun info */
+       if (underrun->length_cnt || underrun->pattern_cnt) {
+               PRINT_FW(cl_hw, "======== Underrun recovery statistics =======\n");
+               PRINT_FW(cl_hw, "Length underrun %u\n",
+                        underrun->length_cnt);
+               PRINT_FW(cl_hw, "Pattern underrun %u\n",
+                        underrun->pattern_cnt);
+               PRINT_FW(cl_hw, "Total frames flushed in underrun %u\n",
+                        underrun->flushed_frames_cnt);
+               PRINT_FW(cl_hw, "\n");
+       }
+
+       /* BW drop fail info */
+       if (tx_stats->tx_obtain_bw_fail_cnt) {
+               PRINT_FW(cl_hw, "Failed to obtain BW count %u\n", tx_stats->tx_obtain_bw_fail_cnt);
+               PRINT_FW(cl_hw, "\n");
+       }
+
+       /* Backoff time info */
+       PRINT_FW(cl_hw, "Backoff Time [us]\n");
+       PRINT_FW(cl_hw, "------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "|Backoff |   AC 0   |   AC 1   |   AC 2   |   AC 3   |\n");
+
+       for (i = 0; i < CE_BACKOFF_MAX; i++) {
+               if (tx_stats->backoff_stats[AC_BK].backoff_hist[i] == 0 &&
+                   tx_stats->backoff_stats[AC_BE].backoff_hist[i] == 0 &&
+                   tx_stats->backoff_stats[AC_VI].backoff_hist[i] == 0 &&
+                   tx_stats->backoff_stats[AC_VO].backoff_hist[i] == 0)
+                       continue;
+
+               PRINT_FW(cl_hw, "|--------+----------+----------+----------+----------|\n");
+               PRINT_FW(cl_hw, "| %6s |%10u|%10u|%10u|%10u|\n",
+                        fw_tx_backoff_str[i],
+                        tx_stats->backoff_stats[AC_BK].backoff_hist[i],
+                        tx_stats->backoff_stats[AC_BE].backoff_hist[i],
+                        tx_stats->backoff_stats[AC_VI].backoff_hist[i],
+                        tx_stats->backoff_stats[AC_VO].backoff_hist[i]);
+       }
+
+       PRINT_FW(cl_hw, "------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "\n");
+
+       /* AMSDU Packet cnt */
+       PRINT_FW(cl_hw, "|--------------------|\n");
+       PRINT_FW(cl_hw, "|  AMSDU Packet cnt  |\n");
+       PRINT_FW(cl_hw, "|--------------------|\n");
+       for (i = 0; i < IPC_MAX_BA_SESSIONS; i++) {
+               if (agg->amsdu_stat[i].packet_cnt_2 == 0 &&
+                   agg->amsdu_stat[i].packet_cnt_3 == 0 &&
+                   agg->amsdu_stat[i].packet_cnt_4 == 0 &&
+                   agg->amsdu_stat[i].packet_cnt_5_or_more == 0)
+                       continue;
+
+               PRINT_FW(cl_hw, "# session=%u\n", i);
+               PRINT_FW(cl_hw, "packet_cnt  = 2: [%u]\n",
+                        agg->amsdu_stat[i].packet_cnt_2);
+               PRINT_FW(cl_hw, "packet_cnt  = 3: [%u]\n",
+                        agg->amsdu_stat[i].packet_cnt_3);
+               PRINT_FW(cl_hw, "packet_cnt  = 4: [%u]\n",
+                        agg->amsdu_stat[i].packet_cnt_4);
+               PRINT_FW(cl_hw, "packet_cnt >= 5: [%u]\n",
+                        agg->amsdu_stat[i].packet_cnt_5_or_more);
+               PRINT_FW(cl_hw, "\n");
+       }
+       PRINT_FW(cl_hw, "\n");
+
+       /* Agg statistics */
+       for (i = 1; i < CL_MAX_AGG_IN_TXOP; i++) {
+               if (agg->agg_in_txop_statistics[i]) {
+                       is_agg_in_txop = true;
+                       break;
+               }
+       }
+
+       if (is_agg_in_txop) {
+               /* Agg in TXOP */
+               PRINT_FW(cl_hw, "Agg in TXOP\n");
+               PRINT_FW(cl_hw, "|----------------|\n");
+               PRINT_FW(cl_hw, "| Agg |  Count   |\n");
+               PRINT_FW(cl_hw, "|-----+----------|\n");
+
+               for (i = 1; i < CL_MAX_AGG_IN_TXOP; i++) {
+                       if (!agg->agg_in_txop_statistics[i])
+                               continue;
+
+                       PRINT_FW(cl_hw, "|%5u|%10u|\n",
+                                i + 1, agg->agg_in_txop_statistics[i]);
+               }
+
+               PRINT_FW(cl_hw, "|----------------|\n");
+               PRINT_FW(cl_hw, "\n");
+
+               /* Agg close reason & Agg queue switch */
+               PRINT_FW(cl_hw, "Agg close reason:\n");
+               PRINT_FW(cl_hw, "  Not enough txdescs = %u\n",
+                        agg->agg_in_txop_close_reason[AGG_IN_TXOP_CLOSE_REASON_NO_TXDESC]);
+               PRINT_FW(cl_hw, "  TXOP expired       = %u\n",
+                        agg->agg_in_txop_close_reason[AGG_IN_TXOP_CLOSE_REASON_TXOP_EXPIRED]);
+               PRINT_FW(cl_hw, "  Delba in process   = %u\n",
+                        agg->agg_in_txop_close_reason[AGG_IN_TXOP_CLOSE_REASON_ACTIVE_DELBA]);
+               PRINT_FW(cl_hw, "Agg queue switch:\n");
+               PRINT_FW(cl_hw, "  Queue switch within TXOP               = %u\n",
+                        agg->agg_in_txop_queue_switch);
+               PRINT_FW(cl_hw, "  Queue switch abort due diff bw in TXOP = %u\n",
+                        agg->agg_in_txop_queue_switch_abort_bw);
+               PRINT_FW(cl_hw, "\n");
+       }
+
+       /* RTS-CTS statistics */
+       if (rts_cts->fw_rts_cnt ||
+           rts_cts->fw_cts_cnt ||
+           rts_cts->hw_rts_cnt ||
+           rts_cts->hw_cts_cnt) {
+               PRINT_FW(cl_hw, "RTS-CTS statistics\n");
+               PRINT_FW(cl_hw, "==================\n");
+               PRINT_FW(cl_hw, "FW RTS frame count = %u\n", rts_cts->fw_rts_cnt);
+               PRINT_FW(cl_hw, "FW CTS frame count = %u\n", rts_cts->fw_cts_cnt);
+               PRINT_FW(cl_hw, "HW RTS frame count = %u\n", rts_cts->hw_rts_cnt);
+               PRINT_FW(cl_hw, "HW CTS frame count = %u\n", rts_cts->hw_cts_cnt);
+               PRINT_FW(cl_hw, "\n");
+       }
+
+       /* Natt statistics */
+       PRINT_FW(cl_hw, "natt statistics\n");
+       PRINT_FW(cl_hw, "===============\n");
+       PRINT_FW(cl_hw, "agg size[0] = %u <singeltons>\n", agg->agg_size_statistics[0]);
+       PRINT_FW(cl_hw, "\n");
+
+       PRINT_FW(cl_hw, "-----------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "|agg | num sent |percent | pass per | drop per | PER per  |\n");
+       PRINT_FW(cl_hw, "|size| per size |per size| agg size | agg size | agg size |\n");
+       PRINT_FW(cl_hw, "|----+----------+--------+----------+----------+----------|\n");
+
+       for (i = 1; i < DBG_STATS_MAX_AGG_SIZE; i++)
+               agg_size_total += agg->agg_size_statistics[i];
+
+       for (i = 1; i < DBG_STATS_MAX_AGG_SIZE; i++) {
+               if (agg->agg_size_statistics[i] == 0)
+                       continue;
+
+               total_natt = agg->packet_failed_statistics[i] + agg->packet_passed_statistics[i];
+
+               if (total_natt == 0)
+                       continue;
+
+               agg_size_x100 = 100 * agg->agg_size_statistics[i];
+
+               PRINT_FW(cl_hw, "|%4u|%10u|%5u.%02u|%10u|%10u|%10u|\n",
+                        i,
+                        agg->agg_size_statistics[i],
+                        agg_size_x100 / agg_size_total,
+                        DECIMAL_FRACTION_X2(agg_size_x100, agg_size_total),
+                        agg->packet_passed_statistics[i],
+                        agg->packet_failed_statistics[i],
+                        ((agg->packet_failed_statistics[i] * 100) / total_natt));
+       }
+       PRINT_FW(cl_hw, "-----------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "Amount of all su aggregations: %u\n", agg_size_total);
+       PRINT_FW(cl_hw, "\n");
+
+       /* Natt statistics (HTP flows) */
+       PRINT_FW(cl_hw, "natt statistics HTP flows\n");
+       PRINT_FW(cl_hw, "=========================\n");
+       PRINT_FW(cl_hw, "-----------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "|agg |num sent| percent  | pass per | drop per | PER per  |\n");
+       PRINT_FW(cl_hw, "|size|per size| per size | agg size | agg size | agg size |\n");
+       PRINT_FW(cl_hw, "|----+--------+----------+----------+----------+----------|\n");
+
+       agg_size_total = 0;
+       for (i = 1; i < DBG_STATS_MAX_AGG_SIZE; i++)
+               agg_size_total += agg->htp_agg_size_statistics[i];
+
+       for (i = 1; i < DBG_STATS_MAX_AGG_SIZE; i++) {
+               if (agg->htp_agg_size_statistics[i] == 0)
+                       continue;
+
+               total_natt = agg->htp_packet_failed_statistics[i] +
+                       agg->htp_packet_passed_statistics[i];
+
+               if (total_natt == 0)
+                       continue;
+
+               agg_size_x100 = 100 * agg->htp_agg_size_statistics[i];
+
+               PRINT_FW(cl_hw, "|%4u|%10u|%5u.%02u|%10u|%10u|%10u|\n",
+                        i,
+                        agg->htp_agg_size_statistics[i],
+                        agg_size_x100 / agg_size_total,
+                        DECIMAL_FRACTION_X2(agg_size_x100, agg_size_total),
+                        agg->htp_packet_passed_statistics[i],
+                        agg->htp_packet_failed_statistics[i],
+                        (agg->htp_agg_size_statistics[i] * 100 / total_natt));
+       }
+       PRINT_FW(cl_hw, "-----------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "\n");
+
+       /* Chosen frame BW */
+       PRINT_FW(cl_hw, "Chosen frame BW\n");
+       for (i = 0; i < NATT_BW_MAX; i++)
+               PRINT_FW(cl_hw, "BW[%u] = %u\n", i, natt->chosen_frame_bw[i]);
+       PRINT_FW(cl_hw, "\n");
+
+       /* Natt operation mode */
+       PRINT_FW(cl_hw, "Natt operation mode\n");
+       for (i = 0; i < 8; i++)
+               if (natt->operation_mode[i] != 0)
+                       PRINT_FW(cl_hw, "[0x%x] = %u\n", i, natt->operation_mode[i]);
+       PRINT_FW(cl_hw, "\n");
+
+       /* Natt agg close reason */
+       PRINT_FW(cl_hw, "natt agg close reason\n");
+       PRINT_FW(cl_hw, "=====================\n");
+       PRINT_FW(cl_hw, "Max length exceed  %u\n", natt->agg_close_reason[NATT_REASON_MAX_LEN]);
+       PRINT_FW(cl_hw, "TXOP limit exceed  %u\n", natt->agg_close_reason[NATT_REASON_TXOP_LIMIT]);
+       PRINT_FW(cl_hw, "MPDU number exceed %u\n", natt->agg_close_reason[NATT_REASON_MPDU_NUM]);
+       PRINT_FW(cl_hw, "\n");
+
+       /* Recovery count */
+       if (tx_stats->recovery_count) {
+               PRINT_FW(cl_hw, "Recovery count\n");
+               PRINT_FW(cl_hw, "==============\n");
+               PRINT_FW(cl_hw, "Total: %u\n", tx_stats->recovery_count);
+               PRINT_FW(cl_hw, "\n");
+       }
+
+       /* Singelton backoff_params time */
+       PRINT_FW(cl_hw, "Singelton backoff_params time:\n");
+       for (i = 0; i < AC_MAX; i++) {
+               if (backoff_params->singelton_cnt[i]) {
+                       avg_backoff = backoff_params->singelton_total[i] /
+                               backoff_params->singelton_cnt[i];
+                       PRINT_FW(cl_hw, "ac%d avarage backoff_params %u\n", i, avg_backoff);
+               } else {
+                       PRINT_FW(cl_hw, "ac%d avarage backoff_params 0\n", i);
+               }
+       }
+       PRINT_FW(cl_hw, "\n");
+
+       /* Aggregation backoff_params time */
+       PRINT_FW(cl_hw, "Aggregation backoff_params time:\n");
+       for (i = 0; i < AC_VO; i++) {
+               if (backoff_params->agg_cnt[i]) {
+                       avg_backoff = backoff_params->agg_total[i] /
+                               backoff_params->agg_cnt[i];
+                       PRINT_FW(cl_hw, "ac%d avarage backoff_params %u\n", i, avg_backoff);
+               } else {
+                       PRINT_FW(cl_hw, "ac%d avarage backoff_params 0\n", i);
+               }
+       }
+       PRINT_FW(cl_hw, "\n");
+
+       /* Trigger Based traffic statistics */
+       PRINT_FW(cl_hw, "Trigger Based traffic statistics:\n");
+       for (i = 0; i < TID_MAX; i++)
+               if (htp->total_cnt[i])
+                       PRINT_FW(cl_hw, "TID%d total_cnt %u\n", i, htp->total_cnt[i]);
+       PRINT_FW(cl_hw, "\n");
+
+       if (htp->need_response || htp->tb_response_required) {
+               PRINT_FW(cl_hw, "need_response         = %u\n", htp->need_response);
+               PRINT_FW(cl_hw, "tb_response_required  = %u\n", htp->tb_response_required);
+               PRINT_FW(cl_hw, "ac_not_found          = %u\n", htp->ac_not_found);
+               PRINT_FW(cl_hw, "end_of_packet_int     = %u\n", htp->end_of_packet_int);
+               PRINT_FW(cl_hw, "tb_bw_decision        = %u\n", htp->tb_bw_decision);
+               PRINT_FW(cl_hw, "tb_ba_thd_removed     = %u\n", htp->tb_ba_thd_removed);
+               PRINT_FW(cl_hw, "tb_ac_unchain         = %u\n", htp->tb_ac_unchain);
+               PRINT_FW(cl_hw, "tb_htp_unchain        = %u\n", htp->tb_htp_unchain);
+               PRINT_FW(cl_hw, "tb_dummy_htp_tx       = %u\n", htp->tb_dummy_htp_tx);
+               PRINT_FW(cl_hw, "tb_dummy_no_tx        = %u\n", htp->tb_dummy_no_tx);
+               PRINT_FW(cl_hw, "msta_ba_received      = %u\n", htp->msta_ba_received);
+               PRINT_FW(cl_hw, "msta_ba_aid_not_found = %u\n", htp->msta_ba_aid_not_found);
+       }
+
+       total_vns_off = vns->off_cck + vns->off_ofdm + vns->off_ht_vht + vns->off_he;
+       total_vns_on = vns->on_cck + vns->on_ofdm + vns->on_ht_vht + vns->on_he;
+
+       if (total_vns_off || total_vns_on) {
+               PRINT_FW(cl_hw, "       -----------------------\n");
+               PRINT_FW(cl_hw, "       | VNS-OFF  |  VNS-ON  |\n");
+               PRINT_FW(cl_hw, "-------+----------+----------|\n");
+               PRINT_FW(cl_hw, "|CCK   |%10u|%10u|\n", vns->off_cck, vns->on_cck);
+               PRINT_FW(cl_hw, "|OFDM  |%10u|%10u|\n", vns->off_ofdm, vns->on_ofdm);
+               PRINT_FW(cl_hw, "|HT-VHT|%10u|%10u|\n", vns->off_ht_vht, vns->on_ht_vht);
+               PRINT_FW(cl_hw, "|HE    |%10u|%10u|\n", vns->off_he, vns->on_he);
+               PRINT_FW(cl_hw, "|------+----------+----------|\n");
+               PRINT_FW(cl_hw, "|TOTAL |%10u|%10u|\n", total_vns_off, total_vns_on);
+               PRINT_FW(cl_hw, "------------------------------\n");
+               PRINT_FW(cl_hw, "\n");
+       }
+
+       if (fec->ldpc || fec->bcc) {
+               PRINT_FW(cl_hw, "FEC Coding:\n");
+               PRINT_FW(cl_hw, "LDPC = %u\n", fec->ldpc);
+               PRINT_FW(cl_hw, "BCC  = %u\n", fec->bcc);
+               PRINT_FW(cl_hw, "\n");
+       }
+}
+
+static void cl_print_tx_mu_stats(struct cl_hw *cl_hw, struct cl_txl_statistics *tx_stats)
+{
+       int i;
+       struct cl_txl_agg_statistics *agg = &tx_stats->agg;
+
+       if (agg->mu_stats[CL_MU1_IDX].chain_cnt == 0) {
+               PRINT_FW(cl_hw, "~~~~~~~~~~~~~~~~~~  MU  statistics - EMPTY  ~~~~~~~~~~~~~~~~~~\n");
+               return;
+       }
+
+       PRINT_FW(cl_hw, "~~~~~~~~~~~~~~~~~~~~~~~~~  MU  statistics  ~~~~~~~~~~~~~~~~~~~~~~~~~\n");
+       PRINT_FW(cl_hw, "\n");
+
+#if (MU_MAX_STREAMS >= 4)
+       /* MU status statistics */
+       PRINT_FW(cl_hw, "--------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "|            | MU1 | MU2 | MU3 | MU4 | MU5 | MU6 | MU7 |\n");
+       PRINT_FW(cl_hw, "|------------+-----+-----+-----+-----+-----+-----+-----|\n");
+       PRINT_FW(cl_hw, "|Chain count |%5u|%5u|%5u|%5u|%5u|%5u|%5u|\n",
+                agg->mu_stats[CL_MU1_IDX].chain_cnt,
+                agg->mu_stats[CL_MU2_IDX].chain_cnt,
+                agg->mu_stats[CL_MU3_IDX].chain_cnt,
+                agg->mu_stats[CL_MU4_IDX].chain_cnt,
+                agg->mu_stats[CL_MU5_IDX].chain_cnt,
+                agg->mu_stats[CL_MU6_IDX].chain_cnt,
+                agg->mu_stats[CL_MU7_IDX].chain_cnt);
+       PRINT_FW(cl_hw, "|Status count|%5u|%5u|%5u|%5u|%5u|%5u|%5u|\n",
+                agg->mu_stats[CL_MU1_IDX].status_cnt,
+                agg->mu_stats[CL_MU2_IDX].status_cnt,
+                agg->mu_stats[CL_MU3_IDX].status_cnt,
+                agg->mu_stats[CL_MU4_IDX].status_cnt,
+                agg->mu_stats[CL_MU5_IDX].status_cnt,
+                agg->mu_stats[CL_MU6_IDX].status_cnt,
+                agg->mu_stats[CL_MU7_IDX].status_cnt);
+       PRINT_FW(cl_hw, "--------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "\n");
+
+       /* MU agg-size statistics */
+       PRINT_FW(cl_hw, "------------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "| agg size | MU0 | MU1 | MU2 | MU3 | MU4 | MU5 | MU6 | MU7 |\n");
+       PRINT_FW(cl_hw, "|----------+-----+-----+-----+-----+-----+-----+-----+-----|\n");
+       for (i = 1; i < DBG_STATS_MAX_AGG_SIZE; i++) {
+               if (agg->mu_agg_size_statistics[CL_MU0_IDX][i] ||
+                   agg->mu_agg_size_statistics[CL_MU1_IDX][i] ||
+                   agg->mu_agg_size_statistics[CL_MU2_IDX][i] ||
+                   agg->mu_agg_size_statistics[CL_MU3_IDX][i] ||
+                   agg->mu_agg_size_statistics[CL_MU4_IDX][i] ||
+                   agg->mu_agg_size_statistics[CL_MU5_IDX][i] ||
+                   agg->mu_agg_size_statistics[CL_MU6_IDX][i] ||
+                   agg->mu_agg_size_statistics[CL_MU7_IDX][i]) {
+                       PRINT_FW(cl_hw, "|%10u|%5u|%5u|%5u|%5u|%5u|%5u|%5u|%5u|\n",
+                                i,
+                                agg->mu_agg_size_statistics[CL_MU0_IDX][i],
+                                agg->mu_agg_size_statistics[CL_MU1_IDX][i],
+                                agg->mu_agg_size_statistics[CL_MU2_IDX][i],
+                                agg->mu_agg_size_statistics[CL_MU3_IDX][i],
+                                agg->mu_agg_size_statistics[CL_MU4_IDX][i],
+                                agg->mu_agg_size_statistics[CL_MU5_IDX][i],
+                                agg->mu_agg_size_statistics[CL_MU6_IDX][i],
+                                agg->mu_agg_size_statistics[CL_MU7_IDX][i]);
+               }
+       }
+       PRINT_FW(cl_hw, "------------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "\n");
+
+       /* MU BA statistics */
+       PRINT_FW(cl_hw, "--------------------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "| MU BA statistics | MU0 | MU1 | MU2 | MU3 | MU4 | MU5 | MU6 | MU7 |\n");
+       PRINT_FW(cl_hw, "|------------------+-----+-----+-----+-----+-----+-----+-----+-----|\n");
+       PRINT_FW(cl_hw, "|  BA Received     |%5u|%5u|%5u|%5u|%5u|%5u|%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].ba_received,
+                agg->mu_stats[CL_MU1_IDX].ba_received,
+                agg->mu_stats[CL_MU2_IDX].ba_received,
+                agg->mu_stats[CL_MU3_IDX].ba_received,
+                agg->mu_stats[CL_MU4_IDX].ba_received,
+                agg->mu_stats[CL_MU5_IDX].ba_received,
+                agg->mu_stats[CL_MU6_IDX].ba_received,
+                agg->mu_stats[CL_MU7_IDX].ba_received);
+       PRINT_FW(cl_hw, "|  --Unexpected BA |%5u|%5u|%5u|%5u|%5u|%5u|%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].unexpected_ba,
+                agg->mu_stats[CL_MU1_IDX].unexpected_ba,
+                agg->mu_stats[CL_MU2_IDX].unexpected_ba,
+                agg->mu_stats[CL_MU3_IDX].unexpected_ba,
+                agg->mu_stats[CL_MU4_IDX].unexpected_ba,
+                agg->mu_stats[CL_MU5_IDX].unexpected_ba,
+                agg->mu_stats[CL_MU6_IDX].unexpected_ba,
+                agg->mu_stats[CL_MU7_IDX].unexpected_ba);
+       PRINT_FW(cl_hw, "|  --Cleared    BA |%5u|%5u|%5u|%5u|%5u|%5u|%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].clear_ba,
+                agg->mu_stats[CL_MU1_IDX].clear_ba,
+                agg->mu_stats[CL_MU2_IDX].clear_ba,
+                agg->mu_stats[CL_MU3_IDX].clear_ba,
+                agg->mu_stats[CL_MU4_IDX].clear_ba,
+                agg->mu_stats[CL_MU5_IDX].clear_ba,
+                agg->mu_stats[CL_MU6_IDX].clear_ba,
+                agg->mu_stats[CL_MU7_IDX].clear_ba);
+       PRINT_FW(cl_hw, "|  --Invalid    BA |%5u|%5u|%5u|%5u|%5u|%5u|%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].invalid_ba,
+                agg->mu_stats[CL_MU1_IDX].invalid_ba,
+                agg->mu_stats[CL_MU2_IDX].invalid_ba,
+                agg->mu_stats[CL_MU3_IDX].invalid_ba,
+                agg->mu_stats[CL_MU4_IDX].invalid_ba,
+                agg->mu_stats[CL_MU5_IDX].invalid_ba,
+                agg->mu_stats[CL_MU6_IDX].invalid_ba,
+                agg->mu_stats[CL_MU7_IDX].invalid_ba);
+       PRINT_FW(cl_hw, "|  --Correct    BA |%5u|%5u|%5u|%5u|%5u|%5u|%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].correct_ba,
+                agg->mu_stats[CL_MU1_IDX].correct_ba,
+                agg->mu_stats[CL_MU2_IDX].correct_ba,
+                agg->mu_stats[CL_MU3_IDX].correct_ba,
+                agg->mu_stats[CL_MU4_IDX].correct_ba,
+                agg->mu_stats[CL_MU5_IDX].correct_ba,
+                agg->mu_stats[CL_MU6_IDX].correct_ba,
+                agg->mu_stats[CL_MU7_IDX].correct_ba);
+       PRINT_FW(cl_hw, "|  BA not Received |%5u|%5u|%5u|%5u|%5u|%5u|%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].ba_no_received,
+                agg->mu_stats[CL_MU1_IDX].ba_no_received,
+                agg->mu_stats[CL_MU2_IDX].ba_no_received,
+                agg->mu_stats[CL_MU3_IDX].ba_no_received,
+                agg->mu_stats[CL_MU4_IDX].ba_no_received,
+                agg->mu_stats[CL_MU5_IDX].ba_no_received,
+                agg->mu_stats[CL_MU6_IDX].ba_no_received,
+                agg->mu_stats[CL_MU7_IDX].ba_no_received);
+       PRINT_FW(cl_hw, "--------------------------------------------------------------------\n");
+#elif (MU_MAX_STREAMS == 3)
+       /* MU status statistics */
+       PRINT_FW(cl_hw, "--------------------------\n");
+       PRINT_FW(cl_hw, "|            | MU1 | MU2 |\n");
+       PRINT_FW(cl_hw, "|------------+-----+-----|\n");
+       PRINT_FW(cl_hw, "|Chain count |%5u|%5u|\n",
+                agg->mu_stats[CL_MU1_IDX].chain_cnt,
+                agg->mu_stats[CL_MU2_IDX].chain_cnt);
+       PRINT_FW(cl_hw, "|Status count|%5u|%5u|\n",
+                agg->mu_stats[CL_MU1_IDX].status_cnt,
+                agg->mu_stats[CL_MU2_IDX].status_cnt);
+       PRINT_FW(cl_hw, "--------------------------\n");
+       PRINT_FW(cl_hw, "\n");
+
+       /* MU agg-size statistics */
+       PRINT_FW(cl_hw, "------------------------------\n");
+       PRINT_FW(cl_hw, "| agg size | MU0 | MU1 | MU2 |\n");
+       PRINT_FW(cl_hw, "|----------+-----+-----+-----|\n");
+       for (i = 1; i < DBG_STATS_MAX_AGG_SIZE; i++) {
+               if (agg->mu_agg_size_statistics[CL_MU0_IDX][i] ||
+                   agg->mu_agg_size_statistics[CL_MU1_IDX][i] ||
+                   agg->mu_agg_size_statistics[CL_MU2_IDX][i]) {
+                       PRINT_FW(cl_hw, "|%10u|%5u|%5u|%5u|\n",
+                                i,
+                                agg->mu_agg_size_statistics[CL_MU0_IDX][i],
+                                agg->mu_agg_size_statistics[CL_MU1_IDX][i],
+                                agg->mu_agg_size_statistics[CL_MU2_IDX][i]);
+               }
+       }
+       PRINT_FW(cl_hw, "------------------------------\n");
+       PRINT_FW(cl_hw, "\n");
+
+       /* MU BA statistics */
+       PRINT_FW(cl_hw, "--------------------------------------\n");
+       PRINT_FW(cl_hw, "| MU BA statistics | MU0 | MU1 | MU2 |\n");
+       PRINT_FW(cl_hw, "|------------------+-----+-----+-----|\n");
+       PRINT_FW(cl_hw, "|  BA Received     |%5u|%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].ba_received,
+                agg->mu_stats[CL_MU1_IDX].ba_received,
+                agg->mu_stats[CL_MU2_IDX].ba_received);
+       PRINT_FW(cl_hw, "|  --Unexpected BA |%5u|%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].unexpected_ba,
+                agg->mu_stats[CL_MU1_IDX].unexpected_ba,
+                agg->mu_stats[CL_MU2_IDX].unexpected_ba);
+       PRINT_FW(cl_hw, "|  --Cleared    BA |%5u|%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].clear_ba,
+                agg->mu_stats[CL_MU1_IDX].clear_ba,
+                agg->mu_stats[CL_MU2_IDX].clear_ba);
+       PRINT_FW(cl_hw, "|  --Invalid    BA |%5u|%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].invalid_ba,
+                agg->mu_stats[CL_MU1_IDX].invalid_ba,
+                agg->mu_stats[CL_MU2_IDX].invalid_ba);
+       PRINT_FW(cl_hw, "|  --Correct    BA |%5u|%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].correct_ba,
+                agg->mu_stats[CL_MU1_IDX].correct_ba,
+                agg->mu_stats[CL_MU2_IDX].correct_ba);
+       PRINT_FW(cl_hw, "|  BA not Received |%5u|%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].ba_no_received,
+                agg->mu_stats[CL_MU1_IDX].ba_no_received,
+                agg->mu_stats[CL_MU2_IDX].ba_no_received);
+       PRINT_FW(cl_hw, "--------------------------------------\n");
+#else
+       /* MU status statistics */
+       PRINT_FW(cl_hw, "--------------------\n");
+       PRINT_FW(cl_hw, "|            | MU1 |\n");
+       PRINT_FW(cl_hw, "|------------+-----|\n");
+       PRINT_FW(cl_hw, "|Chain count |%5u|\n",
+                agg->mu_stats[CL_MU1_IDX].chain_cnt);
+       PRINT_FW(cl_hw, "|Status count|%5u|\n",
+                agg->mu_stats[CL_MU1_IDX].status_cnt);
+       PRINT_FW(cl_hw, "--------------------\n");
+       PRINT_FW(cl_hw, "\n");
+
+       /* MU agg-size statistics */
+       PRINT_FW(cl_hw, "------------------------\n");
+       PRINT_FW(cl_hw, "| agg size | MU0 | MU1 |\n");
+       PRINT_FW(cl_hw, "|----------+-----+-----|\n");
+       for (i = 1; i < DBG_STATS_MAX_AGG_SIZE; i++) {
+               if (agg->mu_agg_size_statistics[CL_MU0_IDX][i] ||
+                   agg->mu_agg_size_statistics[CL_MU1_IDX][i]) {
+                       PRINT_FW(cl_hw, "|%10u|%5u|%5u|\n",
+                                i,
+                                agg->mu_agg_size_statistics[CL_MU0_IDX][i],
+                                agg->mu_agg_size_statistics[CL_MU1_IDX][i]);
+               }
+       }
+       PRINT_FW(cl_hw, "------------------------\n");
+       PRINT_FW(cl_hw, "\n");
+
+       /* MU BA statistics */
+       PRINT_FW(cl_hw, "--------------------------------\n");
+       PRINT_FW(cl_hw, "| MU BA statistics | MU0 | MU1 |\n");
+       PRINT_FW(cl_hw, "|------------------+-----+-----|\n");
+       PRINT_FW(cl_hw, "|  BA Received     |%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].ba_received,
+                agg->mu_stats[CL_MU1_IDX].ba_received);
+       PRINT_FW(cl_hw, "|  --Unexpected BA |%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].unexpected_ba,
+                agg->mu_stats[CL_MU1_IDX].unexpected_ba);
+       PRINT_FW(cl_hw, "|  --Cleared    BA |%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].clear_ba,
+                agg->mu_stats[CL_MU1_IDX].clear_ba);
+       PRINT_FW(cl_hw, "|  --Invalid    BA |%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].invalid_ba,
+                agg->mu_stats[CL_MU1_IDX].invalid_ba);
+       PRINT_FW(cl_hw, "|  --Correct    BA |%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].correct_ba,
+                agg->mu_stats[CL_MU1_IDX].correct_ba);
+       PRINT_FW(cl_hw, "|  BA not Received |%5u|%5u|\n",
+                agg->mu_stats[CL_MU0_IDX].ba_no_received,
+                agg->mu_stats[CL_MU1_IDX].ba_no_received);
+       PRINT_FW(cl_hw, "--------------------------------\n");
+#endif
+}
+
+static void cl_print_bcn_stats(struct cl_hw *cl_hw, struct cl_bcn_statistics *bcn_stats)
+{
+       struct beacon_timing *bcn_timing_stats = &bcn_stats->beacon_timing;
+       struct beacon_counters *bcn_cnt_stats = &bcn_stats->beacon_counters;
+       struct bcn_backup_stats *bcn_backup_stats = &bcn_stats->bcn_backup_stats;
+       u32 avg_time_between_bcn = 0, avg_time_bcn_chain = 0;
+
+       if (bcn_cnt_stats->nof_time_intervals_between_beacons != 0)
+               avg_time_between_bcn = (bcn_timing_stats->total_bcn_time /
+                                       bcn_cnt_stats->nof_time_intervals_between_beacons);
+
+       if (bcn_cnt_stats->bcn_chain_total_cnt != 0)
+               avg_time_bcn_chain = ((bcn_timing_stats->bcn_chain_total_time) /
+                                     (bcn_cnt_stats->bcn_chain_total_cnt));
+
+       PRINT_FW(cl_hw, "----------------------------------------\n");
+       PRINT_FW(cl_hw, "Number of beacon flushed\n");
+       PRINT_FW(cl_hw, "+---------+-------------+--------------+\n");
+       PRINT_FW(cl_hw, "| pending | downloading | transmitting |\n");
+       PRINT_FW(cl_hw, "+---------+-------------+--------------+\n");
+       PRINT_FW(cl_hw, "|%-9u|%-13u|%-14u|\n",
+                bcn_cnt_stats->ce_txl_flushed_beacons[BCN_FLUSH_PENDING],
+                bcn_cnt_stats->ce_txl_flushed_beacons[BCN_FLUSH_DOWNLOADING],
+                bcn_cnt_stats->ce_txl_flushed_beacons[BCN_FLUSH_TRANSMITTING]);
+       PRINT_FW(cl_hw, "+---------+-------------+--------------+\n\n");
+
+       PRINT_FW(cl_hw, "----------------------------------------\n");
+       PRINT_FW(cl_hw, "Time between transmission of two beacons\n");
+       PRINT_FW(cl_hw, "+----------+----------+---------+----------------+\n");
+       PRINT_FW(cl_hw, "| min time | max time | bcn cnt | avg time[mSec] |\n");
+       PRINT_FW(cl_hw, "+----------+----------+---------+----------------+\n");
+       PRINT_FW(cl_hw, "|%-10u|%-10u|%-9u|%-16u|\n",
+                bcn_timing_stats->min_time_from_last_bcn,
+                bcn_timing_stats->max_time_from_last_bcn,
+                bcn_cnt_stats->total_cnt,
+                avg_time_between_bcn);
+       PRINT_FW(cl_hw, "+----------+----------+---------+----------------+\n\n");
+
+       PRINT_FW(cl_hw, "---------------------------------------------------------------\n");
+       PRINT_FW(cl_hw, "Time of beacon until chain\n");
+       PRINT_FW(cl_hw, "+----------+----------+---------------+----------+\n");
+       PRINT_FW(cl_hw, "| min time | max time | bcn chain cnt | avg time |\n");
+       PRINT_FW(cl_hw, "+----------+----------+---------------+----------+\n");
+       PRINT_FW(cl_hw, "|%-10u|%-10u|%-15u|%-10u|\n",
+                bcn_timing_stats->bcn_chain_min_time,
+                bcn_timing_stats->bcn_chain_max_time,
+                bcn_cnt_stats->bcn_chain_total_cnt,
+                avg_time_bcn_chain);
+       PRINT_FW(cl_hw, "+----------+----------+---------------+----------+\n\n");
+
+       PRINT_FW(cl_hw, "---------------------------------------------------------------------\n");
+       PRINT_FW(cl_hw, " beacon pending-chain path max time = %u\n",
+                bcn_timing_stats->bcn_pending_2_chain_max_time);
+       PRINT_FW(cl_hw, " beacon pending-chain not in time count = %u\n",
+                bcn_cnt_stats->pending2chain_not_in_threshold_cnt);
+       PRINT_FW(cl_hw, " Max time until recievd beacon from driver = %u\n",
+                bcn_timing_stats->max_bcn_time_until_get_beacon_from_driver_in_tbtt);
+       PRINT_FW(cl_hw, " Total count of beacon flushed because didn't received in time = %u\n",
+                bcn_cnt_stats->bcn_time_from_driver_not_in_threshold_cnt);
+       PRINT_FW(cl_hw, " Max num of beacon not received from driver = %u\n",
+                bcn_cnt_stats->max_bcn_not_received_from_host);
+       PRINT_FW(cl_hw, "---------------------------------------------------------------------\n");
+
+       PRINT_FW(cl_hw, "+--------------------------------------+\n");
+       PRINT_FW(cl_hw, "|        Backup beacon stats           |\n");
+       PRINT_FW(cl_hw, "+------+------+---------+--------------+\n");
+       PRINT_FW(cl_hw, "| Used |  TX  | flushed | Max in a row |\n");
+       PRINT_FW(cl_hw, "+------+------+---------+--------------+\n");
+       PRINT_FW(cl_hw, "|%6u|%6u|%9u|%14u|\n",
+                bcn_backup_stats->bcn_backup_used_cnt,
+                bcn_backup_stats->bcn_backup_tx_cnt,
+                bcn_backup_stats->bcn_backup_flushed_cnt,
+                bcn_backup_stats->bcn_backup_max_used_in_arow_cnt);
+       PRINT_FW(cl_hw, "+------+------+---------+--------------+\n");
+}
+
+static void cl_print_rate_fallback_stats(struct cl_hw *cl_hw,
+                                        struct cl_rate_drop_statistics *stats)
+{
+       PRINT_FW(cl_hw, "\n");
+       PRINT_FW(cl_hw, "---------------------------\n");
+       PRINT_FW(cl_hw, "   Fallback statistics\n");
+       PRINT_FW(cl_hw, "---------------------------\n");
+       PRINT_FW(cl_hw, "ba_per_stats          = %u\n",
+                stats->drop_reason[AGG_TX_RATE_DROP_MAX_BA_PER_REACHED]);
+       PRINT_FW(cl_hw, "ba_not_received_stats = %u\n",
+                stats->drop_reason[AGG_TX_RATE_DROP_MAX_BA_NOT_RECEIVED_REACHED]);
+       PRINT_FW(cl_hw, "max_retry_reached     = %u\n",
+                stats->drop_reason[AGG_TX_RATE_DROP_MAX_RETRY_REACHED]);
+}
+
+static void cl_print_rx_stats_precent(struct cl_hw *cl_hw, const char *str, u32 x, u32 y)
+{
+       /*
+        * Example:
+        * x = 541, y = 19
+        * Result 28.4736
+        */
+       u32 integer = x / y;
+       u32 fraction = 10000 * (x - y * (x / y)) / y;
+
+       PRINT_FW(cl_hw, "%s = %u.%04u\n", str, integer, fraction);
+}
+
+static void cl_print_rx_stats(struct cl_hw *cl_hw, struct cl_rxl_statistics *rx_stats)
+{
+       int i, mu_idx, total_rx = 0;
+       enum format_mode fm;
+
+       PRINT_FW(cl_hw, "=========================================\n");
+       PRINT_FW(cl_hw, "        Global RX stats\n");
+       PRINT_FW(cl_hw, "=========================================\n");
+       PRINT_FW(cl_hw, "host rxelem not ready      = %u\n",
+                rx_stats->host_rxelem_not_ready_cnt);
+       PRINT_FW(cl_hw, "MSDU host rxelem not ready = %u\n",
+                rx_stats->msdu_host_rxelem_not_ready_cnt);
+       PRINT_FW(cl_hw, "MSDU dma pool not ready    = %u\n",
+                rx_stats->dma_rx_pool_not_ready_cnt);
+       PRINT_FW(cl_hw, "Percent of Rx CCA busy      = %u\n",
+                rx_stats->cca_busy_percent);
+       PRINT_FW(cl_hw, "Percent of Rx mine CCA busy = %u\n",
+                rx_stats->rx_mine_busy_percent);
+       PRINT_FW(cl_hw, "Percent of Tx mine busy     = %u\n",
+                rx_stats->tx_mine_busy_percent);
+       PRINT_FW(cl_hw, "\n");
+
+       PRINT_FW(cl_hw, "=== Rx Format ==\n");
+       for (fm = 0; fm < FORMATMOD_MAX; fm++)
+               if (rx_stats->stats_rx_format[fm])
+                       PRINT_FW(cl_hw, "Rx Format[%d] = %u\n", fm, rx_stats->stats_rx_format[fm]);
+
+       PRINT_FW(cl_hw, "=== Rx Decryption errors ==\n");
+       for (i = RHD_DECR_ICVFAIL_IDX; i < RHD_DECR_IDX_MAX; i++)
+               if (rx_stats->decrypt_err[i])
+                       PRINT_FW(cl_hw, "decrypt_err[%d] = %u\n", i, rx_stats->decrypt_err[i]);
+
+       /* RX prints */
+       for (mu_idx = 0; mu_idx < MU_UL_MAX; mu_idx++) {
+               PRINT_FW(cl_hw, "============================================\n");
+               PRINT_FW(cl_hw, "=====         RX MAC HW MU [%2d]       =====\n", mu_idx);
+               PRINT_FW(cl_hw, "============================================\n");
+               total_rx = rx_stats->total_rx_packets[mu_idx] +
+                       rx_stats->fcs_error_counter[mu_idx] +
+                       rx_stats->phy_error_counter[mu_idx] +
+                       rx_stats->ampdu_incorrect_received_counter[mu_idx] +
+                       rx_stats->delimiter_error_counter[mu_idx] +
+                       rx_stats->rx_fifo_overflow_err_cnt[mu_idx];
+
+               if (total_rx == 0)
+                       continue;
+
+               for (i = 0; i < MAX_HANDLED_FRM_TYPE; i++) {
+                       if (!rx_stats->emb_ll1_handled_frame_counter[mu_idx][i])
+                               continue;
+
+                       PRINT_FW(cl_hw, "emb_handled_packet[%d] - %u\n",
+                                i, rx_stats->emb_ll1_handled_frame_counter[mu_idx][i]);
+               }
+
+               PRINT_FW(cl_hw, "Total packets dropped (pckt_len > %u) %u\n",
+                        rx_stats->max_mpdu_data_len[mu_idx],
+                        rx_stats->rx_pckt_exceed_max_len_cnt[mu_idx]);
+               PRINT_FW(cl_hw, "Number of bad formated BA frames = %u\n",
+                        rx_stats->rx_pckt_bad_ba_statinfo_cnt[mu_idx]);
+               PRINT_FW(cl_hw, "Max occupancy list2 = %u\n",
+                        rx_stats->rhd_ll2_max_cnt[mu_idx]);
+               PRINT_FW(cl_hw, "Max occupancy list1 = %u\n",
+                        rx_stats->rhd_ll1_max_cnt[mu_idx]);
+               PRINT_FW(cl_hw, "\n");
+               PRINT_FW(cl_hw, "Total Qos MPDU received    = %u\n",
+                        rx_stats->total_rx_packets[mu_idx]);
+               PRINT_FW(cl_hw, "Total Aggregation received = %u\n",
+                        rx_stats->total_agg_packets[mu_idx]);
+               PRINT_FW(cl_hw, "Number of Rx Fifo Overflow = %u\n",
+                        rx_stats->rx_fifo_overflow_err_cnt[mu_idx]);
+               PRINT_FW(cl_hw, "Number of FCS ERROR        = %u\n",
+                        rx_stats->fcs_error_counter[mu_idx]);
+               PRINT_FW(cl_hw, "Number of PHY ERROR        = %u\n",
+                        rx_stats->phy_error_counter[mu_idx]);
+               PRINT_FW(cl_hw, "Number of AMPDUS           = %u\n",
+                        rx_stats->ampdu_received_counter[mu_idx]);
+               PRINT_FW(cl_hw, "Number of Incorrect AMPDUS = %u\n",
+                        rx_stats->ampdu_incorrect_received_counter[mu_idx]);
+               PRINT_FW(cl_hw, "Number of Delimiter errors = %u\n",
+                        rx_stats->delimiter_error_counter[mu_idx]);
+
+               if (rx_stats->total_rx_packets[mu_idx]) {
+                       u32 total_rx_packets = rx_stats->total_rx_packets[mu_idx] +
+                               rx_stats->rx_fifo_overflow_err_cnt[mu_idx] +
+                               rx_stats->fcs_error_counter[mu_idx] +
+                               rx_stats->phy_error_counter[mu_idx] +
+                               rx_stats->delimiter_error_counter[mu_idx];
+
+                       cl_print_rx_stats_precent(cl_hw,
+                                                 "Rx Fifo Overflow percent  ",
+                                                 100 * rx_stats->rx_fifo_overflow_err_cnt[mu_idx],
+                                                 total_rx_packets);
+                       cl_print_rx_stats_precent(cl_hw,
+                                                 "FCS Error percent         ",
+                                                 100 * rx_stats->fcs_error_counter[mu_idx],
+                                                 total_rx_packets);
+                       cl_print_rx_stats_precent(cl_hw,
+                                                 "Phy Error percent         ",
+                                                 100 * rx_stats->phy_error_counter[mu_idx],
+                                                 total_rx_packets);
+                       cl_print_rx_stats_precent(cl_hw,
+                                                 "Delimiter Error percent   ",
+                                                 100 * rx_stats->delimiter_error_counter[mu_idx],
+                                                 total_rx_packets);
+               }
+
+               PRINT_FW(cl_hw, "Current NAV value          = %u\n", rx_stats->nav_value[mu_idx]);
+
+               PRINT_FW(cl_hw, "\n");
+               PRINT_FW(cl_hw, "Rx LL split stats: 1st LL interrupts = %u\n",
+                        rx_stats->counter_timer_trigger_ll1[mu_idx]);
+               PRINT_FW(cl_hw, "Rx LL split stats: 2nd LL interrupts = %u\n",
+                        rx_stats->counter_timer_trigger_ll2[mu_idx]);
+               PRINT_FW(cl_hw, "Number of incorrect format mode received = %u\n",
+                        rx_stats->rx_incorrect_format_mode[mu_idx]);
+
+               for (i = 0; i < RX_CLASSIFICATION_MAX; i++) {
+                       if (!rx_stats->rx_class_counter[mu_idx][i])
+                               continue;
+
+                       PRINT_FW(cl_hw, "Rx classification rules stats: Rx rule%d= %u\n",
+                                i, rx_stats->rx_class_counter[mu_idx][i]);
+               }
+
+               if (rx_stats->rx_class_int_counter[mu_idx])
+                       PRINT_FW(cl_hw, "Rx classification interrupts rules = %u\n",
+                                rx_stats->rx_class_int_counter[mu_idx]);
+
+               PRINT_FW(cl_hw, "\n");
+               PRINT_FW(cl_hw, "Rx Implicit BF statistics:      = %u\n",
+                        rx_stats->rx_imp_bf_counter[mu_idx]);
+               PRINT_FW(cl_hw, "Rx Implicit BF interrupts stats = %u\n",
+                        rx_stats->rx_imp_bf_int_counter[mu_idx]);
+               PRINT_FW(cl_hw, "RXM STATISTICS\n");
+               PRINT_FW(cl_hw, "rxm_stats_overflow      = %u\n",
+                        rx_stats->rxm_stats_overflow[mu_idx]);
+               PRINT_FW(cl_hw, "rx_incorrect_format_mode= %u\n",
+                        rx_stats->rx_incorrect_format_mode[mu_idx]);
+               PRINT_FW(cl_hw, "correct_received_mpdu   = %u\n",
+                        rx_stats->correct_received_mpdu[mu_idx]);
+               PRINT_FW(cl_hw, "incorrect_received_mpdu = %u\n",
+                        rx_stats->incorrect_received_mpdu[mu_idx]);
+               PRINT_FW(cl_hw, "discarded_mpdu          = %u\n",
+                        rx_stats->discarded_mpdu[mu_idx]);
+               PRINT_FW(cl_hw, "incorrect_delimiter     = %u\n",
+                        rx_stats->incorrect_delimiter[mu_idx]);
+               PRINT_FW(cl_hw, "rts_bar_cnt             = %u\n",
+                        rx_stats->rts_bar_cnt[mu_idx]);
+               PRINT_FW(cl_hw, "rxm_mpdu_cnt            = %u\n",
+                        rx_stats->rxm_mpdu_cnt[mu_idx]);
+
+               if (rx_stats->rxm_mpdu_cnt[mu_idx]) {
+                       PRINT_FW(cl_hw, "rxm_rule0_match        = %u\n",
+                                rx_stats->rxm_rule0_match[mu_idx]);
+                       PRINT_FW(cl_hw, "rxm_rule1_match        = %u\n",
+                                rx_stats->rxm_rule1_match[mu_idx]);
+                       PRINT_FW(cl_hw, "rxm_rule2_match        = %u\n",
+                                rx_stats->rxm_rule2_match[mu_idx]);
+                       PRINT_FW(cl_hw, "rxm_rule3_match        = %u\n",
+                                rx_stats->rxm_rule3_match[mu_idx]);
+                       PRINT_FW(cl_hw, "rxm_rule4_match        = %u\n",
+                                rx_stats->rxm_rule4_match[mu_idx]);
+                       PRINT_FW(cl_hw, "rxm_rule5_match        = %u\n",
+                                rx_stats->rxm_rule5_match[mu_idx]);
+                       PRINT_FW(cl_hw, "rxm_rule6_match        = %u\n",
+                                rx_stats->rxm_rule6_match[mu_idx]);
+                       PRINT_FW(cl_hw, "rxm_default_rule_match = %u\n",
+                                rx_stats->rxm_default_rule_match[mu_idx]);
+                       PRINT_FW(cl_hw, "RXM amsdu stat not supported. use iwcl stats instead\n");
+               }
+
+               /* RX AMSDU prints */
+               PRINT_FW(cl_hw, "\n");
+               PRINT_FW(cl_hw, "RX AMSDU STATS\n");
+
+               PRINT_FW(cl_hw, "AMSDU RX cnt  = %u\n",
+                        rx_stats->stats_tot_rx_amsdu_cnt[mu_idx]);
+
+               for (i = 0; i < ARRAY_SIZE(rx_stats->stats_rx_amsdu_cnt[mu_idx]); i++)
+                       if (rx_stats->stats_rx_amsdu_cnt[mu_idx][i])
+                               PRINT_FW(cl_hw, "A-MSDU of %d = %u\n",
+                                        i + 1, rx_stats->stats_rx_amsdu_cnt[mu_idx][i]);
+
+               PRINT_FW(cl_hw, "A-MSDU RX errors:\n");
+               for (i = 0; i < AMSDU_DEAGGREGATION_ERR_MAX; i++)
+                       if (rx_stats->stats_rx_amsdu_err[mu_idx][i])
+                               PRINT_FW(cl_hw, " err_id[%d] = %u\n",
+                                        i, rx_stats->stats_rx_amsdu_err[mu_idx][i]);
+       }
+
+       PRINT_FW(cl_hw, "Frequency offset:\n");
+       for (i = 0; i < FREQ_OFFSET_TABLE_IDX_MAX; i++)
+               if (rx_stats->frequency_offset[i])
+                       PRINT_FW(cl_hw, "frequency_offset = %u\n", rx_stats->frequency_offset[i]);
+}
+
+static void cl_print_trigger_flow_stats(struct cl_hw *cl_hw,
+                                       struct cl_trigger_flow_statistics *tf_stats)
+{
+       u16 idx;
+       struct cl_rx_trigger_based_stats *tb_stats = &cl_hw->tb_stats;
+
+       if (!tb_stats->enable) {
+               PRINT_FW(cl_hw, "WARNING: Trigger based statistics are disabled!\n");
+               return;
+       }
+
+       PRINT_FW(cl_hw, "--------------------------------\n");
+       PRINT_FW(cl_hw, "     Trigger flow statistics\n");
+       PRINT_FW(cl_hw, "--------------------------------\n");
+       PRINT_FW(cl_hw, "Sent trigger frames\n");
+       PRINT_FW(cl_hw, "---------------|---AC0---|---AC1---|---AC2---|---AC3---|\n");
+       PRINT_FW(cl_hw, "BASIC TRIGGER: |%9u|%9u|%9u|%9u|\n",
+                tf_stats->single_trigger_sent[TRIGGER_FLOW_BASIC_TRIGGER_TYPE][AC_BK],
+                tf_stats->single_trigger_sent[TRIGGER_FLOW_BASIC_TRIGGER_TYPE][AC_BE],
+                tf_stats->single_trigger_sent[TRIGGER_FLOW_BASIC_TRIGGER_TYPE][AC_VI],
+                tf_stats->single_trigger_sent[TRIGGER_FLOW_BASIC_TRIGGER_TYPE][AC_VO]);
+       PRINT_FW(cl_hw, "BSRP:          |%9u|%9u|%9u|%9u|\n",
+                tf_stats->single_trigger_sent[TRIGGER_FLOW_BSRP_TYPE][AC_BK],
+                tf_stats->single_trigger_sent[TRIGGER_FLOW_BSRP_TYPE][AC_BE],
+                tf_stats->single_trigger_sent[TRIGGER_FLOW_BSRP_TYPE][AC_VI],
+                tf_stats->single_trigger_sent[TRIGGER_FLOW_BSRP_TYPE][AC_VO]);
+       PRINT_FW(cl_hw, "BFRP:          |%9u|%9u|%9u|%9u|\n",
+                tf_stats->single_trigger_sent[TRIGGER_FLOW_BFRP_TYPE][AC_BK],
+                tf_stats->single_trigger_sent[TRIGGER_FLOW_BFRP_TYPE][AC_BE],
+                tf_stats->single_trigger_sent[TRIGGER_FLOW_BFRP_TYPE][AC_VI],
+                tf_stats->single_trigger_sent[TRIGGER_FLOW_BFRP_TYPE][AC_VO]);
+       PRINT_FW(cl_hw, "HTP FAILURE:   |%9u|%9u|%9u|%9u|\n",
+                tf_stats->htp_rx_failure[AC_BK],
+                tf_stats->htp_rx_failure[AC_BE],
+                tf_stats->htp_rx_failure[AC_VI],
+                tf_stats->htp_rx_failure[AC_VO]);
+
+       PRINT_FW(cl_hw, "--------------------------------\n");
+       PRINT_FW(cl_hw, "TRIGGER BASED MPDUs PER MAC HW\n");
+       PRINT_FW(cl_hw, "--------------------------------\n");
+       tf_stats->trigger_based_mpdu[0] = tb_stats->total;
+
+       for (idx = 1; idx < MU_UL_MAX; idx++)
+               tf_stats->trigger_based_mpdu[0] -= tf_stats->trigger_based_mpdu[idx];
+
+       for (idx = 0; idx < MU_UL_MAX; idx++)
+               PRINT_FW(cl_hw, "MAC HW %u - %10u\n", idx, tf_stats->trigger_based_mpdu[idx]);
+
+       PRINT_FW(cl_hw, "--------------------------------\n");
+       PRINT_FW(cl_hw, "TRIGGER BASED AGGREGATIONS SIZE\n");
+       PRINT_FW(cl_hw, "--------------------------------\n");
+       PRINT_FW(cl_hw, "|-SIZE-|---TB AGGS---|\n");
+
+       for (idx = 1; idx < DBG_STATS_MAX_AGG_SIZE; idx++) {
+               if (tb_stats->data[idx] == 0)
+                       continue;
+
+               PRINT_FW(cl_hw, "| %4u |%13u|\n", idx, tb_stats->data[idx]);
+       }
+
+       PRINT_FW(cl_hw, "--------------------------------\n");
+       PRINT_FW(cl_hw, "TRIGGER BASED QOS NULL AGGR SIZE\n");
+       PRINT_FW(cl_hw, "--------------------------------\n");
+       PRINT_FW(cl_hw, "|-SIZE-|---TB AGGS---|\n");
+
+       for (idx = 1; idx < TID_MAX + 1; idx++)
+               if (tb_stats->qos_null[idx] > 0)
+                       PRINT_FW(cl_hw, "| %4u |%13u|\n", idx, tb_stats->qos_null[idx]);
+
+       if (tb_stats->qos_null[TID_MAX + 1] > 0)
+               PRINT_FW(cl_hw, "|  >8  |%13u|\n", tb_stats->qos_null[TID_MAX + 1]);
+}
+
+static void cl_print_dyn_calib_stats(struct cl_hw *cl_hw,
+                                    struct cl_dyn_calib_statistics *dyn_cal_stats)
+{
+       u8 i, j;
+
+       PRINT_FW(cl_hw, "--------------------------------\n");
+       PRINT_FW(cl_hw, "Dynamic Calibration Information\n");
+       PRINT_FW(cl_hw, "--------------------------------\n\n");
+
+       PRINT_FW(cl_hw, "Default Dynamic Calibation Value = %u\n\n",
+                dyn_cal_stats->default_dyn_cal_val);
+
+       for (i = dyn_cal_stats->dyn_cal_debug_info_ix, j = 0;
+            j < DYN_CAL_DEBUG_NUM_ITER;
+            (i = ((i + 1) % 3)), j++) {
+               struct dyn_cal_debug_info_t *dyn_cal_debug_info =
+                       &dyn_cal_stats->dyn_cal_debug_info[i];
+
+               if (dyn_cal_stats->is_multi_client_mode)
+                       PRINT_FW(cl_hw,
+                                "calib_num = %u, min_val = %d, max_val = %d, min_config = %u, "
+                                "max_config = %u, curr_config = %u, new_config = %u\n",
+                                dyn_cal_debug_info->calib_num,
+                                (s32)dyn_cal_debug_info->dyn_cal_min_val,
+                                (s32)dyn_cal_debug_info->dyn_cal_max_val,
+                                dyn_cal_debug_info->min_config,
+                                dyn_cal_debug_info->max_config,
+                                dyn_cal_debug_info->curr_config,
+                                dyn_cal_debug_info->new_config);
+               else
+                       PRINT_FW(cl_hw,
+                                "calib_num = %u, iter_num = %u, config_val_prev = %u, "
+                                "measured_val = %u, new_config_val = %u\n",
+                                dyn_cal_debug_info->calib_num,
+                                dyn_cal_debug_info->iter_num,
+                                dyn_cal_debug_info->curr_config,
+                                dyn_cal_debug_info->measured_val,
+                                dyn_cal_debug_info->new_config);
+       }
+
+       if (dyn_cal_stats->mac_phy_sync_err_cnt)
+               PRINT_FW(cl_hw, "mac_phy_sync_err_cnt = %u\n\n",
+                        dyn_cal_stats->mac_phy_sync_err_cnt);
+
+       PRINT_FW(cl_hw, "\n-----------------------------------------\n\n");
+}
+
+static void cl_print_bf_stats(struct cl_hw *cl_hw, struct cl_bf_statistics *bf_stats)
+{
+       u32 idx;
+       bool should_print = false;
+       u16 *tx_bf_data_err;
+
+       for (idx = 0; idx < BF_DB_MAX; idx++)
+               if (bf_stats->print_active_free_list == bf_stats->stats_data[idx].is_active_list) {
+                       should_print = true;
+                       break;
+               }
+
+       if (!should_print)
+               return;
+
+       /* Info phase 1 */
+       PRINT_FW(cl_hw, "List of non active BFs:\n");
+       PRINT_FW(cl_hw, "============================\n");
+       PRINT_FW(cl_hw, "BF_CTRL statistics\n");
+       PRINT_FW(cl_hw, "+-----+----+----+-------+-------+----------------+-----------------+--------------+---------------+-----------------+---------------+--------------+------------+-------+\n");
+       PRINT_FW(cl_hw, "|INDEX|#NDP|#BFP|#SU BFR|#MU BFR|#BFR_BW_MISMATCH|#BFR_NSS_MISMATCH|#SOUNDING_CHBW|#TOKEN_MISMATCH|#NDP_NDPA_TX_DROP|#BFR_RX_ERR_ACK|#BFR SEGMENTED|#RESOURCE_NA|STA_IDX|\n");
+       PRINT_FW(cl_hw, "+-----+----+----+-------+-------+----------------+-----------------+--------------+---------------+-----------------+---------------+--------------+------------+-------+\n");
+
+       for (idx = 0; idx < BF_DB_MAX; idx++) {
+               if (bf_stats->print_active_free_list != bf_stats->stats_data[idx].is_active_list)
+                       continue;
+
+               PRINT_FW(cl_hw,
+                        "|%5u|%4u|%4u|%7u|%7u|%16u|%17u|%14u|%15u|%17u|%15u|%14u|%12u|%7u|\n",
+                        idx,
+                        bf_stats->stats_data[idx].dbg.ndp_cnt,
+                        bf_stats->stats_data[idx].dbg.bfp_cnt,
+                        bf_stats->stats_data[idx].dbg.su_bfr_cnt,
+                        bf_stats->stats_data[idx].dbg.mu_bfr_cnt,
+                        bf_stats->stats_data[idx].dbg.bf_invalid_cnt[BFR_RX_ERR_BW_MISMATCH],
+                        bf_stats->stats_data[idx].dbg.bf_invalid_cnt[BFR_RX_ERR_NSS_MISMATCH],
+                        bf_stats->stats_data[idx].dbg.bf_invalid_cnt[BFR_RX_ERR_SOUNDING_CHBW],
+                        bf_stats->stats_data[idx].dbg.bf_invalid_cnt[BFR_RX_ERR_TOKEN_MISMATCH],
+                        bf_stats->stats_data[idx].dbg.bf_invalid_cnt[BFR_RX_ERR_NDP_DROP],
+                        bf_stats->stats_data[idx].dbg.bf_invalid_cnt[BFR_RX_ERR_MISS_ACK],
+                        bf_stats->stats_data[idx].dbg.bf_invalid_cnt[BFR_SEGMENTED_DROP],
+                        bf_stats->stats_data[idx].dbg.bf_invalid_cnt[BFR_RX_ERR_RESOURCE_NA],
+                        bf_stats->stats_data[idx].sta_idx);
+       }
+
+       PRINT_FW(cl_hw, "+-----+----+----+-------+-------+----------------+-----------------+--------------+---------------+-----------------+---------------+--------------+------------+-------+\n");
+
+       /* Info phase 2 */
+       PRINT_FW(cl_hw, "statistic BF DATA FRAMESs:\n");
+       PRINT_FW(cl_hw, "============================\n");
+       PRINT_FW(cl_hw, "BF_CTRL statistics\n");
+       PRINT_FW(cl_hw, "+-----+-----------+------------+-------------+-----------------+----------------+-----------------+-----------+-----------+----------+------------------+-----------------+-------+\n");
+       PRINT_FW(cl_hw, "|INDEX|#ACTIVE_IDX|#PASSIVE_IDX|#ERR_BFR_MISS|#ERR_BFR_OUTDATED|#ERR_BW_MISMATCH|#ERR_NSS_MISMATCH|#BF_DATA_OK|#BUFF_IN_PS|#REL_IN_PS|#BUFF_RESOURCE_ERR|#REL_RESOURCE_ERR|STA_IDX|\n");
+       PRINT_FW(cl_hw, "+-----+-----------+------------+-------------+-----------------+----------------+-----------------+-----------+-----------+----------+------------------+-----------------+-------+\n");
+
+       for (idx = 0; idx < BF_DB_MAX; idx++) {
+               if (bf_stats->print_active_free_list != bf_stats->stats_data[idx].is_active_list)
+                       continue;
+
+               tx_bf_data_err = bf_stats->stats_data[idx].dbg.tx_bf_data_err;
+
+               PRINT_FW(cl_hw,
+                        "|%5u|%11u|%12u|%13u|%17u|%16u|%17u|%11u|%11u|%10u|%18u|%17u|%7u|\n",
+                        idx,
+                        bf_stats->stats_data[idx].active_dsp_idx,
+                        bf_stats->stats_data[idx].passive_dsp_idx,
+                        tx_bf_data_err[TX_BF_DATA_ERR_BFR_MISS],
+                        tx_bf_data_err[TX_BF_DATA_ERR_BFR_OUTDATED],
+                        tx_bf_data_err[TX_BF_DATA_ERR_MISMATCH_BW],
+                        tx_bf_data_err[TX_BF_DATA_ERR_MISMATCH_NSS],
+                        tx_bf_data_err[TX_BF_DATA_OK],
+                        tx_bf_data_err[TX_BF_DATA_BUFFERED_PS_STA],
+                        tx_bf_data_err[TX_BF_DATA_RELEASED_PS_STA],
+                        tx_bf_data_err[TX_BF_DATA_BUFFERED_RESOURCE_ERR],
+                        tx_bf_data_err[TX_BF_DATA_RELEASED_RESOURCE_ERR],
+                        bf_stats->stats_data[idx].sta_idx);
+       }
+
+       PRINT_FW(cl_hw, "+-----+-----------+------------+-------------+-----------------+----------------+-----------------+-----------+-----------+----------+------------------+-----------------+-------+\n");
+}
+
+static void cl_print_stats_handler(struct work_struct *ws)
+{
+       struct cl_print_stats_work *stats_work = container_of(ws, struct cl_print_stats_work, ws);
+       struct cl_hw *cl_hw = stats_work->cl_hw;
+       u32 dbg_info_type = stats_work->dbg_info_type;
+
+       if (dbg_info_type == DBG_INFO_TX_STATS) {
+               struct cl_txl_statistics *tx_stats =
+                       &(((struct dbg_info *)cl_hw->dbginfo.buf)->u.tx_stats);
+
+               cl_print_tx_stats(cl_hw, tx_stats);
+               cl_print_tx_mu_stats(cl_hw, tx_stats);
+       } else if (dbg_info_type == DBG_INFO_BCN_STATS) {
+               struct cl_bcn_statistics *bcn_stats =
+                       &(((struct dbg_info *)cl_hw->dbginfo.buf)->u.bcn_stats);
+
+               cl_print_bcn_stats(cl_hw, bcn_stats);
+       } else if (dbg_info_type == DBG_INFO_RX_STATS) {
+               struct cl_rxl_statistics *rx_stats =
+                       &(((struct dbg_info *)cl_hw->dbginfo.buf)->u.rx_stats);
+
+               cl_print_rx_stats(cl_hw, rx_stats);
+       } else if (dbg_info_type == DBG_INFO_DYN_CAL_STATS) {
+               struct cl_dyn_calib_statistics *dyn_cal_stats =
+                       &(((struct dbg_info *)cl_hw->dbginfo.buf)->u.dyn_calib_stats);
+
+               cl_print_dyn_calib_stats(cl_hw, dyn_cal_stats);
+       } else if (dbg_info_type == DBG_INFO_RATE_FALLBACK_STATS) {
+               struct cl_rate_drop_statistics *agg_rate_drop_stats =
+                       &(((struct dbg_info *)cl_hw->dbginfo.buf)->u.rate_drop_stats);
+
+               cl_print_rate_fallback_stats(cl_hw, agg_rate_drop_stats);
+       } else if (dbg_info_type == DBG_INFO_BF) {
+               struct cl_bf_statistics *bf_stats =
+                       &(((struct dbg_info *)cl_hw->dbginfo.buf)->u.bf_stats);
+
+               cl_print_bf_stats(cl_hw, bf_stats);
+       } else if (dbg_info_type == DBG_INFO_TRIGGER_FLOW) {
+               struct cl_trigger_flow_statistics *tf_stats =
+                       &(((struct dbg_info *)cl_hw->dbginfo.buf)->u.trigger_flow_stats);
+
+               cl_print_trigger_flow_stats(cl_hw, tf_stats);
+       }
+
+#ifdef CONFIG_CL_PCIE
+       cl_ipc_dbginfobuf_push(cl_hw->ipc_env, cl_hw->dbginfo.dma_addr);
+#endif
+       kfree(stats_work);
+}
+
+static void cl_schedule_print_stats(struct cl_hw *cl_hw, u32 dbg_info_type)
+{
+       struct cl_print_stats_work *stats_work =
+               kzalloc(sizeof(*stats_work), GFP_ATOMIC);
+
+       if (stats_work) {
+               INIT_WORK(&stats_work->ws, cl_print_stats_handler);
+               stats_work->cl_hw = cl_hw;
+               stats_work->dbg_info_type = dbg_info_type;
+
+               /* Schedule work, the work will be executed in the background */
+               queue_work(cl_hw->drv_workqueue, &stats_work->ws);
+       } else {
+               cl_dbg_err(cl_hw, "stats_work allocation failed\n");
+       }
+}
+
+void cl_fw_dbg_handler(struct cl_hw *cl_hw)
+{
+       struct dbg_info *dbg_info = NULL;
+
+       /* Function called upon DBG_INFO_IND message reception. */
+       dma_sync_single_for_device(cl_hw->chip->dev, cl_hw->dbginfo.dma_addr,
+                                  cl_hw->dbginfo.bufsz, DMA_FROM_DEVICE);
+       dbg_info = (struct dbg_info *)cl_hw->dbginfo.buf;
+
+       if (dbg_info->u.type == DBG_INFO_DUMP) {
+               cl_dbg_info(cl_hw, "type %u): dump received\n",
+                           cl_hw->dbginfo.buf->u.dump.general_data.error_type);
+               cl_coredump_trigger(cl_hw);
+       } else if (dbg_info->u.type < DBG_INFO_MAX) {
+               cl_schedule_print_stats(cl_hw, dbg_info->u.type);
+       } else {
+               cl_dbg_warn(cl_hw, "Debug info wrong type - %u\n", dbg_info->u.type);
+       }
+}
+
+static int cl_fw_dbg_cli_help(struct cl_hw *cl_hw)
+{
+       char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       int err = 0;
+
+       if (!buf)
+               return -ENOMEM;
+
+       snprintf(buf, PAGE_SIZE,
+                "fw usage:\n"
+                "-a : Trigger assert error (echo ASSERT_ERR > errsim)\n"
+                "-b : Trigger assert recovery (echo 1 > test_mode)\n"
+                "-d : Set trigger-based debug statistics [0-dis/1-en]\n"
+                "-m : Trigger firmware dump (echo 1 > mactrace)\n"
+                "-s : Print statistics (echo param > stat_print)\n"
+                "-t : Test mode command (cmd + 0 to 5 parameters)\n");
+
+       err = cl_vendor_reply(cl_hw, buf, strlen(buf));
+       kfree(buf);
+
+       return err;
+}
+
+int cl_fw_dbg_cli(struct cl_hw *cl_hw, struct cli_params *cli_params)
+{
+       u32 expected_params = 0;
+       bool assert_err = false;
+       bool assert_rec = false;
+       bool dbg_tb_stats = false;
+       bool mactrace = false;
+       bool stat_print = false;
+       bool test_mode = false;
+
+       switch (cli_params->option) {
+       case 'a':
+               assert_err = true;
+               expected_params = 0;
+               break;
+       case 'b':
+               assert_rec = true;
+               expected_params = 0;
+               break;
+       case 'd':
+               dbg_tb_stats = true;
+               expected_params = 1;
+               break;
+       case 'm':
+               mactrace = true;
+               expected_params = 0;
+               break;
+       case 's':
+               stat_print = true;
+               expected_params = 1;
+               break;
+       case 't':
+               test_mode = true;
+               break;
+       case '?':
+               return cl_fw_dbg_cli_help(cl_hw);
+       default:
+               cl_dbg_err(cl_hw, "Illegal option (%c) - try '?' for help\n", cli_params->option);
+               goto out_err;
+       }
+
+       if ((expected_params != cli_params->num_params) && !test_mode) {
+               cl_dbg_err(cl_hw, "Wrong number of arguments (expected %u) (actual %u)\n",
+                          expected_params, cli_params->num_params);
+               goto out_err;
+       }
+
+       if (assert_err) {
+               cl_msg_tx_key_del(cl_hw, 0xFF);
+               return 0;
+       }
+
+       if (assert_rec) {
+               u32 params[TEST_MODE_PARAM_MAX + 1] = {1, 0, 0, 0, 0, 0};
+
+               cl_msg_tx_dbg_test_mode(cl_hw, params);
+               return 0;
+       }
+
+       if (dbg_tb_stats) {
+               cl_hw->tb_stats.enable = (bool)cli_params->params[0];
+               pr_debug("TB statistics %s\n", cl_hw->tb_stats.enable ? "enable" : "disable");
+               return 0;
+       }
+
+       if (mactrace) {
+               cl_msg_tx_dbg_trigger(cl_hw, "Force trigger");
+               return 0;
+       }
+
+       if (stat_print) {
+               u32 bitmap = (u32)cli_params->params[0];
+
+               cl_msg_tx_dbg_print_stats(cl_hw, bitmap, 0, 0, 0, 0);
+               return 0;
+       }
+
+       if (test_mode) {
+               u32 params[TEST_MODE_PARAM_MAX + 1] = {0};
+               u8 i;
+
+               if (cli_params->num_params == 0 ||
+                   cli_params->num_params > TEST_MODE_PARAM_MAX + 1) {
+                       cl_dbg_err(cl_hw, "Test mode expects cmd + 0 to 5 parameters\n");
+                       goto out_err;
+               }
+
+               for (i = 0; i < cli_params->num_params; i++)
+                       params[i] = (u32)cli_params->params[i];
+
+               cl_msg_tx_dbg_test_mode(cl_hw, params);
+               return 0;
+       }
+
+out_err:
+       return -EIO;
+}
+
+static void cl_dbg_dump_check_params(struct cl_hw *cl_hw,
+                                    u8 *buf, int bufsz, int *pos,
+                                    struct dbg_error_trace_info_drv *dump)
+{
+       int is_mismatch = false;
+       struct dbg_meta_data *dbg_metadata = &dump->common_info.dbg_metadata;
+
+       if (dbg_metadata->lmac_req_buf_size != sizeof(struct dbg_error_trace_info_drv) ||
+           dbg_metadata->physical_queue_cnt != CL_MAX_BA_PHYSICAL_QUEUE_CNT ||
+           dbg_metadata->agg_index_max != AGG_IDX_MAX ||
+           dbg_metadata->ce_ac_max != CE_AC_MAX ||
+           dbg_metadata->mu_user_max != MU_MAX_STREAMS ||
+           dbg_metadata->txl_exch_trace_depth != DBG_TXL_FRAME_EXCH_TRACE_DEPTH ||
+           dbg_metadata->mac_hw_regs_max != HAL_MACHW_REG_NUM ||
+           dbg_metadata->phy_hw_regs_max != PHY_HW_DBG_REGS_CNT)
+               is_mismatch = true;
+
+       if (is_mismatch) {
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "\nWarning!!!!\n");
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "DBG metadata mismatch bwtween FW & DRV!!!!\n");
+       }
+
+       if (dbg_metadata->lmac_req_buf_size != (u32)(sizeof(struct dbg_error_trace_info_drv)))
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "FW buf size       %u expected %u\n",
+                                 dbg_metadata->lmac_req_buf_size,
+                                 (u32)(sizeof(struct dbg_error_trace_info_drv)));
+
+       if (dbg_metadata->physical_queue_cnt != CL_MAX_BA_PHYSICAL_QUEUE_CNT)
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "physical queue cn %u expected %u\n",
+                                 dbg_metadata->physical_queue_cnt,
+                                 CL_MAX_BA_PHYSICAL_QUEUE_CNT);
+
+       if (dbg_metadata->agg_index_max != AGG_IDX_MAX)
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "agg idx max       %u expected %u\n",
+                                 dbg_metadata->agg_index_max, AGG_IDX_MAX);
+
+       if (dbg_metadata->ce_ac_max != CE_AC_MAX)
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "ac max            %u expected %u\n",
+                                 dbg_metadata->ce_ac_max, CE_AC_MAX);
+
+       if (dbg_metadata->mu_user_max != MU_MAX_STREAMS)
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "MU MAX            %u expected %u\n",
+                                 dbg_metadata->mu_user_max, MU_MAX_STREAMS);
+
+       if (dbg_metadata->txl_exch_trace_depth != DBG_TXL_FRAME_EXCH_TRACE_DEPTH)
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "txl trace depth   %u expected %u\n",
+                                 dbg_metadata->txl_exch_trace_depth,
+                                 DBG_TXL_FRAME_EXCH_TRACE_DEPTH);
+
+       if (dbg_metadata->mac_hw_regs_max != HAL_MACHW_REG_NUM)
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "MAC HW regs cnt   %u expected %u\n",
+                                 dbg_metadata->mac_hw_regs_max,
+                                 HAL_MACHW_REG_NUM);
+
+       if (dbg_metadata->phy_hw_regs_max != PHY_HW_DBG_REGS_CNT)
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "PHY HW regs       %u expected %u\n",
+                                 dbg_metadata->phy_hw_regs_max,
+                                 PHY_HW_DBG_REGS_CNT);
+}
+
+static void cl_dbg_policy_table_print(struct cl_hw *cl_hw,
+                                     u8 *buf, int bufsz, int *pos,
+                                     struct tx_policy_tbl *policy_table_ptr,
+                                     u32 policy_table_addr)
+{
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "================================================="
+                         "= Policy Table 0x%x ============================="
+                         "============================\n",
+                        policy_table_addr);
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "| upatterntx         = 0x%08x| phycntrlinfo1      = 0x%08x"
+                         "| phycntrlinfo2     = 0x%08x| maccntrlinfo1      = 0x%08x|\n",
+                         policy_table_ptr->upatterntx,
+                         policy_table_ptr->phycntrlinfo1,
+                         policy_table_ptr->phycntrlinfo2,
+                         policy_table_ptr->maccntrlinfo1);
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "| maccntrlinfo2      = 0x%08x| ratecntrlinfo[0]   = 0x%08x"
+                         "| ratecntrlinfo[1]  = 0x%08x| ratecntrlinfo[2]   = 0x%08x|\n",
+                         policy_table_ptr->maccntrlinfo2,
+                         policy_table_ptr->ratecntrlinfo[0],
+                         policy_table_ptr->ratecntrlinfo[1],
+                         policy_table_ptr->ratecntrlinfo[2]);
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "| ratecntrlinfo[3]   = 0x%08x| phycntrlinfo3      = 0x%08x"
+                         "| phycntrlinfo4     = 0x%08x| phycntrlinfo5      = 0x%08x|\n",
+                         policy_table_ptr->ratecntrlinfo[3],
+                         policy_table_ptr->phycntrlinfo3,
+                         policy_table_ptr->phycntrlinfo4,
+                         policy_table_ptr->phycntrlinfo5);
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "| stationinfo        = 0x%08x| ratecntrlinfohe[0] = 0x%08x"
+                         "| ratecntrlinfohe[1]= 0x%08x| ratecntrlinfohe[2] = 0x%08x|\n",
+                         policy_table_ptr->stationinfo,
+                         policy_table_ptr->ratecntrlinfohe[0],
+                         policy_table_ptr->ratecntrlinfohe[1],
+                         policy_table_ptr->ratecntrlinfohe[2]);
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "| ratecntrlinfohe[3] = 0x%08x| maccntrlinfo3      = 0x%08x"
+                         "| triggercommoninfo = 0x%08x| trigperuserinfo[0] = 0x%08x|\n",
+                         policy_table_ptr->ratecntrlinfohe[3],
+                         policy_table_ptr->maccntrlinfo3,
+                         policy_table_ptr->triggercommoninfo,
+                         policy_table_ptr->triggerperuserinfo[0]);
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "| trigperuserinfo[1] = 0x%08x| trigperuserinfo[2] = 0x%08x"
+                         "| trigperuserinfo[3]= 0x%08x| trigperuserinfo[4] = 0x%08x|\n",
+                         policy_table_ptr->triggerperuserinfo[1],
+                         policy_table_ptr->triggerperuserinfo[2],
+                         policy_table_ptr->triggerperuserinfo[3],
+                         policy_table_ptr->triggerperuserinfo[4]);
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "| trigperuserinfo[5] = 0x%08x| trigperuserinfo[6] = 0x%08x"
+                         "| trigperuserinfo[7]= 0x%08x|\n",
+                         policy_table_ptr->triggerperuserinfo[5],
+                         policy_table_ptr->triggerperuserinfo[6],
+                         policy_table_ptr->triggerperuserinfo[7]);
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "| triginforallocau0u3= 0x%08x| triginforallocau4u7= 0x%08x |\n",
+                         policy_table_ptr->triggerinforuallocationu0u3,
+                         policy_table_ptr->triggerinforuallocationu4u7);
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "===================================================="
+                         "===================================================="
+                         "============================\n\n");
+}
+
+static void cl_dbg_thd_print(struct cl_hw *cl_hw,
+                            u8 *buf, int bufsz, int *pos,
+                            struct tx_hd *thd_ptr, u32 thd_addr)
+{
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "=============================================="
+                         "=       THD 0x%x      ========================"
+                         "=======================\n",
+                         thd_addr);
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "| upatterntx   = 0x%08x | nextfrmexseq_ptr = 0x%08x "
+                         "| nextmpdudesc_ptr = 0x%08x | first_pbd_ptr = 0x%08x|\n",
+                        thd_ptr->upatterntx,
+                        thd_ptr->nextfrmexseq_ptr,
+                        thd_ptr->nextmpdudesc_ptr,
+                        thd_ptr->first_pbd_ptr);
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "| datastartptr = 0x%08x | dataendptr       = 0x%08x "
+                         "| frmlen           = 0x%08x | spacinginfo   = 0x%08x|\n",
+                         thd_ptr->datastartptr,
+                         thd_ptr->dataendptr,
+                         thd_ptr->frmlen,
+                         thd_ptr->spacinginfo);
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "| phyctrlinfo1 = 0x%08x | policyentryaddr  = 0x%08x "
+                         "| macctrlinfo1     = 0x%08x | macctrlinfo2  = 0x%08x|\n",
+                         thd_ptr->phyctrlinfo1,
+                         thd_ptr->policyentryaddr,
+                         thd_ptr->macctrlinfo1,
+                         thd_ptr->macctrlinfo2);
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "| statinfo     = 0x%08x | phyctrlinfo2     = 0x%08x |\n",
+                         thd_ptr->statinfo,
+                         thd_ptr->phyctrlinfo2);
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "=============================================="
+                         "=============================================="
+                         "=============================\n\n");
+}
+
+static void cl_dbg_pbd_print(struct cl_hw *cl_hw,
+                            u8 *buf, int bufsz, int *pos,
+                            struct tx_pbd *pbd_ptr, u32 pbd_addr)
+{
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                        "==============================================="
+                        "===      PBD 0x%x     ========================="
+                        "========================\n",
+                        pbd_addr);
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "| upatterntx = 0x%08x | next = 0x%08x| datastartptr = 0x%08x"
+                         "| dataendptr = 0x%08x| bufctrlinfo = 0x%08x|\n",
+                         pbd_ptr->upatterntx,
+                         pbd_ptr->next,
+                         pbd_ptr->datastartptr,
+                         pbd_ptr->dataendptr,
+                         pbd_ptr->bufctrlinfo);
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "=============================================="
+                         "=============================================="
+                         "================================\n\n");
+}
+
+static void cl_dbg_dump_txm_regs(struct cl_hw *cl_hw,
+                                u8 *buf, int bufsz, int *pos,
+                                struct dbg_error_trace_info_drv *dump)
+{
+       int stream_idx;
+       struct dbg_fw_info *fw_info = &dump->common_info.fw_info;
+
+       for (stream_idx = 0; stream_idx < ARRAY_SIZE(fw_info->txlist_info_agg); ++stream_idx) {
+               struct dbg_txm_regs *txm_regs;
+
+               if (!fw_info->txlist_info_agg[stream_idx].curr_session_idx)
+                       continue;
+
+               txm_regs = &dump->common_info.hw_info.txm_regs[stream_idx];
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "#### TXM stream %u Registers\n", stream_idx);
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "|--------------------------------------------------------------------|\n");
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "| HW state = %3u FW state = %3u SPX state = %3u free buff state = %3u|\n",
+                                 txm_regs->hw_state, txm_regs->fw_state,
+                                 txm_regs->spx_state, txm_regs->free_buf_state);
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "| MPDU cnt = %3u LLI cnt  = %3u LLI done mpdu num = %3u  reason = %3u|\n",
+                                 txm_regs->mpdu_cnt, txm_regs->lli_cnt,
+                                 txm_regs->lli_done_mpdu_num, txm_regs->lli_done_reason);
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "| active bytes   = 0x%08x prefetch bytes = 0x%08x            |\n",
+                                 txm_regs->active_bytes, txm_regs->prefetch_bytes);
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "| last THD: addr = 0x%x MPDU number = %3u underrun cnt = %3u   |\n",
+                                 txm_regs->last_thd_done_addr,
+                                 txm_regs->last_thd_done_mpdu_num, txm_regs->underrun_cnt);
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                "|--------------------------------------------------------------------|\n\n");
+       }
+}
+
+static void cl_dbg_dump_machw_regs(struct cl_hw *cl_hw,
+                                  u8 *buf, int bufsz, int *pos,
+                                  struct dbg_error_trace_info_drv *dump)
+{
+       u8 i = 0, mu_idx;
+       struct dbg_hw_reg_info *hw_info = &dump->common_info.hw_info;
+
+       const char *hal_machw_reg_str[HAL_MACHW_REG_NUM] = {
+               [HAL_MACHW_AGGR_STATUS]                    = "AGGR_STATUS",
+               [HAL_MACHW_DEBUG_HWSM_1]                   = "DBG_HWSM_1",
+               [HAL_MACHW_DEBUG_HWSM_2]                   = "DBG_HWSM_2",
+               [HAL_MACHW_DEBUG_HWSM_3]                   = "DBG_HWSM_3",
+               [HAL_MACHW_DMA_STATUS_1]                   = "DMA_STATUS_1",
+               [HAL_MACHW_DMA_STATUS_2]                   = "DMA_STATUS_2",
+               [HAL_MACHW_DMA_STATUS_3]                   = "DMA_STATUS_3",
+               [HAL_MACHW_DMA_STATUS_4]                   = "DMA_STATUS_4",
+               [HAL_MACHW_RX_HEADER_H_PTR]                = "RX_HEADER_HEAD_PTR",
+               [HAL_MACHW_RX_PAYLOAD_H_PTR]               = "RX_PAYLOAD_HEAD_PTR",
+               [HAL_MACHW_DEBUG_BCN_S_PTR]                = "DBG_BCN_STATUS_PTR",
+               [HAL_MACHW_DEBUG_AC0_S_PTR]                = "DBG_AC_0_STATUS_PTR",
+               [HAL_MACHW_DEBUG_AC1_S_PTR]                = "DBG_AC_1_STATUS_PTR",
+               [HAL_MACHW_DEBUG_AC2_S_PTR]                = "DBG_AC_2_STATUS_PTR",
+               [HAL_MACHW_DEBUG_AC3_S_PTR]                = "DBG_AC_3_STATUS_PTR",
+               [HAL_MACHW_DEBUG_HTP_S_PTR]                = "DBG_HTP_STATUS_PTR",
+               [HAL_MACHW_DEBUG_TX_C_PTR]                 = "DBG_TX_CURRENT_PTR",
+               [HAL_MACHW_DEBUG_RX_HDR_C_PTR]             = "DBG_RX_HDR_CURRENT_PTR",
+               [HAL_MACHW_DEBUG_RX_PAY_C_PTR]             = "DBG_RX_PAY_CURRENT_PTR",
+               [HAL_MACHW_MU0_TX_POWER_LEVEL_DELTA_1]     = "DBG_MU0_TX_PWR_LEVEL_DELTA_1",
+               [HAL_MACHW_MU0_TX_POWER_LEVEL_DELTA_2]     = "DBG_MU0_TX_PWR_LEVEL_DELTA_2",
+               [HAL_MACHW_POWER_BW_CALIB_FACTOR]          = "DBG_TX_POWER_BW_CALIB_FACTOR",
+               [HAL_MACHW_TX_POWER_ANTENNA_FACTOR_1_ADDR] = "DBG_tX_POWER_ANT_FACTOR_1",
+               [HAL_MACHW_TX_POWER_ANTENNA_FACTOR_2_ADDR] = "DBG_TX_POWER_ANT_FACTOR_2"
+       };
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos, "\n##### MAC HW regs ####\n");
+
+       for (i = 0; i < ARRAY_SIZE(hw_info->mac_hw_reg); ++i)
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "%30s = 0x%08x\n",
+                                 hal_machw_reg_str[i], hw_info->mac_hw_reg[i]);
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "#########################\n\n");
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "############# MAC HW Secondary FSMs #############\n");
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "|---------------------------------------------------------|\n");
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                        "|MU IDX|%12s|%12s|%12s|%11s|\n",
+                         hal_machw_reg_str[HAL_MACHW_AGGR_STATUS],
+                         hal_machw_reg_str[HAL_MACHW_DEBUG_HWSM_1],
+                         hal_machw_reg_str[HAL_MACHW_DEBUG_HWSM_2],
+                         hal_machw_reg_str[HAL_MACHW_DEBUG_HWSM_3]);
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "|---------------------------------------------------------|\n");
+
+       for (i = 0; i < ARRAY_SIZE(hw_info->mac_hw_sec_fsm); ++i) {
+               mu_idx = CL_MU1_IDX + i;
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "| MU %u | 0x%08x | 0x%08x | 0x%08x | 0x%08x|\n",
+                                 mu_idx,
+                                 hw_info->mac_hw_sec_fsm[i][HAL_MACHW_AGGR_STATUS],
+                                 hw_info->mac_hw_sec_fsm[i][HAL_MACHW_DEBUG_HWSM_1],
+                                 hw_info->mac_hw_sec_fsm[i][HAL_MACHW_DEBUG_HWSM_2],
+                                 hw_info->mac_hw_sec_fsm[i][HAL_MACHW_DEBUG_HWSM_3]);
+       }
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "|---------------------------------------------------------|\n\n");
+
+       /* Write THD data if valid */
+       for (i = HAL_MACHW_DEBUG_BCN_S_PTR; i <= HAL_MACHW_DEBUG_TX_C_PTR; ++i)
+               if (hw_info->mac_hw_reg[i]) {
+                       u8 thd_idx = i - HAL_MACHW_DEBUG_BCN_S_PTR;
+
+                       cl_dbg_thd_print(cl_hw, buf, bufsz, pos,
+                                        &dump->machw_thd_info.thd[thd_idx],
+                                        hw_info->mac_hw_reg[i]);
+               }
+
+       cl_dbg_dump_txm_regs(cl_hw, buf, bufsz, pos, dump);
+}
+
+static void cl_dbg_dump_phyhw_regs(struct cl_hw *cl_hw,
+                                  u8 *buf, int bufsz, int *pos,
+                                  struct dbg_error_trace_info_drv *dump)
+{
+       int i = 0;
+       const char *phy_hw_mpu_reg_str[PHY_HW_DBG_REGS_CNT] = {
+               [MPU_COMMON_FORMAT]       = "MPU_COMMON_FORMAT",
+               [MPU_COMMON_FIELD_CTRL]   = "MPU_COMMON_FIELD_CTRL",
+               [MPU_COMMON_LEGACY_INFO]  = "MPU_COMMON_LEGACY_INFO",
+               [MPU_COMMON_COMMON_CFG_1] = "MPU_COMMON_COMMON_CFG_1",
+               [MPU_COMMON_COMMON_CFG_2] = "MPU_COMMON_COMMON_CFG_2",
+               [MPU_COMMON_COMMON_CFG_3] = "MPU_COMMON_COMMON_CFG_3",
+               [MPU_COMMON_HE_CFG_1]     = "MPU_COMMON_HE_CFG_1",
+               [MPU_COMMON_HE_CFG_2]     = "MPU_COMMON_HE_CFG_2",
+               [MPU_COMMON_INT_STAT_RAW] = "MPU_COMMON_INT_STAT_RAW",
+               [RIU_CCAGENSTAT]          = "RIU_CCAGENSTAT",
+       };
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos, "##### PHY HW regs ####\n");
+
+       for (i = 0; i < ARRAY_SIZE(dump->common_info.hw_info.phy_mpu_hw_reg); ++i)
+               *pos += scnprintf(buf + *pos, bufsz - *pos, "%25s = 0x%08x\n",
+                                 phy_hw_mpu_reg_str[i],
+                                 dump->common_info.hw_info.phy_mpu_hw_reg[i]);
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos, "#########################\n\n");
+}
+
+static void cl_dbg_dump_ac_info(struct cl_hw *cl_hw,
+                               u8 *buf, int bufsz, int *pos,
+                               struct dbg_error_trace_info_drv *dump)
+{
+       int i = 0;
+       struct dbg_fw_info *fw_info = &dump->common_info.fw_info;
+       const char *fw_tx_state_str[CE_TXL_TX_PATH_MAX] = {
+               [CE_TXL_TX_PATH_IDLE]                  = "PATH_IDLE",
+               [CE_TXL_TX_PATH_START]                 = "PATH_START",
+               [CE_TXL_TX_PATH_POST_START_DOWNLOAD]   = "POST_START_DOWNLOAD",
+               [CE_TXL_TX_PATH_TX_DATA_DOWNLOADING]   = "TX_DATA_DOWNLOADING",
+               [CE_TXL_TX_PATH_MU_RECOVERY]           = "MU_RECOVERY",
+               [CE_TXL_TX_PATH_LAST_DOWNLOADING]      = "LAST_DOWNLOADING",
+               [CE_TXL_TX_PATH_NEXT_SESSION_PREPARED] = "NEXT_SESSION_PREP",
+               [CE_TXL_TX_PATH_MU_NEXT_JOB_READY]     = "MU_NEXT_JOB_READY",
+       };
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "\n#####  Per AC info  ####\n");
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "|--------------------------------------------------------|\n");
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "|AC|MU|session|phys queue|check_state|   TX path state   |\n");
+
+       for (i = 0; i < ARRAY_SIZE(fw_info->ac_info); ++i) {
+               u32 mu_idx = (i >= IPC_TX_QUEUE_CNT) ? (i - IPC_TX_QUEUE_CNT + 1) : 0;
+               u32 session_idx = (fw_info->ac_info[i].active_session != FW_DBG_INVALID_SESSION) ?
+                       fw_info->ac_info[i].active_session : 0;
+
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "|--+--+-------+----------+-----------+-------------------|\n");
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "|%2u|%2u|%7u|%10u|%11u|%19s|\n",
+                                 i,
+                                 mu_idx,
+                                 session_idx,
+                                 fw_info->ac_info[i].physical_queue_idx,
+                                 fw_info->ac_info[i].chk_state,
+                                 fw_tx_state_str[fw_info->ac_info[i].tx_path_state]);
+       }
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "|--------------------------------------------------------|\n");
+}
+
+static void cl_dbg_dump_single_tx_list_info(struct cl_hw *cl_hw,
+                                           u8 *buf, int bufsz, int *pos,
+                                           struct dbg_error_trace_info_drv *dump)
+{
+       int i = 0;
+       struct dbg_fw_info *fw_info = &dump->common_info.fw_info;
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "\n##### Singles txdesc lists info ####\n");
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "|----|---------|-------------|--------------|\n");
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "| AC | pending | downloading | transmitting |\n");
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "|----|---------|-------------|--------------|\n");
+
+       for (i = 0; i < ARRAY_SIZE(fw_info->txlist_info_singles); ++i) {
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "|%4u|%9u|%13u|%14u|\n",
+                                 i,
+                                 fw_info->txlist_info_singles[i].pending_cnt,
+                                 fw_info->txlist_info_singles[i].download_cnt,
+                                 fw_info->txlist_info_singles[i].transmit_cnt);
+
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "|----|---------|-------------|--------------|\n");
+       }
+}
+
+static void cl_dbg_dump_agg_tx_list_info(struct cl_hw *cl_hw,
+                                        u8 *buf, int bufsz, int *pos,
+                                        struct dbg_error_trace_info_drv *dump)
+{
+       int i = 0;
+       u32 mu_idx;
+       struct dbg_fw_info *fw_info = &dump->common_info.fw_info;
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "\n##### Agg txdesc lists info ####\n");
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "|----------------------------------------------------------------|\n");
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "|idx|mu|session|pending|download|transmit|wait4ba|next   |next   |\n");
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "|   |  |       |       |        |        |       |session|pending|\n");
+
+       for (i = 0; i < ARRAY_SIZE(fw_info->txlist_info_agg); ++i) {
+               mu_idx = ((i >= AGG_MU1_IDX) && (i <= AGG_MU7_IDX)) ? (i - AGG_MU1_IDX + 1) : 0;
+
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "|---+--+-------+-------+--------+--------+--"
+                                 "-----+-------+-------|\n");
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "|%3u|%2u|%7u|%7u|%8u|%8u|%7u|%7u|%7u|\n",
+                                 i,
+                                 mu_idx,
+                                 fw_info->txlist_info_agg[i].curr_session_idx,
+                                 fw_info->txlist_info_agg[i].pending_cnt,
+                                 fw_info->txlist_info_agg[i].download_cnt,
+                                 fw_info->txlist_info_agg[i].transmit_cnt,
+                                 fw_info->txlist_info_agg[i].wait_for_ba_cnt,
+                                 fw_info->txlist_info_agg[i].next_session_idx,
+                                fw_info->txlist_info_agg[i].next_pending_cnt);
+       }
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "|----------------------------------------------------------------|\n");
+}
+
+static void cl_dbg_dump_thd_chains_info(struct cl_hw *cl_hw,
+                                       u8 *buf, int bufsz, int *pos,
+                                       struct dbg_error_trace_info_drv *dump, u8 ac)
+{
+       int i = 0;
+       u32 data_offset = 0;
+       struct dbg_thd_chains_info *chain_info = &dump->thd_chains_info[ac];
+       struct dbg_thd_chains_data *chain_data = &dump->thd_chains_data[ac];
+       struct tx_hd *hd;
+       struct tx_policy_tbl *policy_tbl;
+       struct tx_pbd *pbd;
+
+       while (chain_info->type_array[i] != DBG_CHAINS_INFO_EMPTY) {
+               switch (chain_info->type_array[i]) {
+               case DBG_CHAINS_INFO_THD:
+                       hd = (struct tx_hd *)&chain_data->data[data_offset];
+                       cl_dbg_thd_print(cl_hw, buf, bufsz, pos, hd,
+                                        chain_info->elem_address[i]);
+                       data_offset += sizeof(struct tx_hd);
+                       break;
+
+               case DBG_CHAINS_INFO_PT:
+                       policy_tbl = (struct tx_policy_tbl *)&chain_data->data[data_offset];
+                       cl_dbg_policy_table_print(cl_hw, buf, bufsz, pos,
+                                                 policy_tbl,
+                                                 chain_info->elem_address[i]);
+                       data_offset += sizeof(struct tx_policy_tbl);
+                       break;
+
+               case DBG_CHAINS_INFO_PBD:
+                       pbd = (struct tx_pbd *)&chain_data->data[data_offset];
+                       cl_dbg_pbd_print(cl_hw, buf, bufsz, pos, pbd,
+                                        chain_info->elem_address[i]);
+                       data_offset += sizeof(struct tx_pbd);
+                       break;
+
+               default:
+                       return;
+               }
+
+               i++;
+               if (i >= DBG_CHAINS_INFO_ELEM_CNT)
+                       break;
+       }
+}
+
+static void cl_dbg_dump_agg_thd_info(struct cl_hw *cl_hw,
+                                    u8 *buf, int bufsz, int *pos,
+                                    struct dbg_error_trace_info_drv *dump,
+                                    u8 ac, u8 mu_idx)
+{
+       u8 agg_idx = (ac < AGG_MU1_IDX) ? ac : (mu_idx + AGG_MU1_IDX - 1);
+       u32 addr = dump->common_info.agg_thds_addr[agg_idx].rts_cts_thd_addr;
+
+       if (addr) {
+               /* RTS CTS THD print */
+               *pos += scnprintf(buf + *pos, bufsz - *pos, "\n  RTS CTS THD 0x%x\n", addr);
+               cl_dbg_thd_print(cl_hw, buf, bufsz, pos,
+                                &dump->agg_thd_info[agg_idx].rts_cts_thd, addr);
+       }
+
+       addr = dump->common_info.agg_thds_addr[agg_idx].athd_addr;
+       if (addr) {
+               /* ATHD print */
+               *pos += scnprintf(buf + *pos, bufsz - *pos, "\n  ATHD 0x%x\n", addr);
+               cl_dbg_thd_print(cl_hw, buf, bufsz, pos,
+                                &dump->agg_thd_info[agg_idx].athd, addr);
+       }
+
+       addr = dump->common_info.agg_thds_addr[agg_idx].policy_table_addr;
+       if (addr) {
+               /* Policy Table print */
+               *pos += scnprintf(buf + *pos, bufsz - *pos, "  Policy Table 0x%x\n", addr);
+               cl_dbg_policy_table_print(cl_hw, buf, bufsz, pos,
+                                         &dump->agg_thd_info[agg_idx].policy_table,
+                                         addr);
+       }
+
+       addr = dump->common_info.agg_thds_addr[agg_idx].tf_thd_addr;
+       if (addr) {
+               /* TF-THD print */
+               *pos += scnprintf(buf + *pos, bufsz - *pos, "  TF-THD 0x%x\n", addr);
+               cl_dbg_thd_print(cl_hw, buf, bufsz, pos,
+                                &dump->agg_thd_info[agg_idx].tf_thd, addr);
+       }
+
+       cl_dbg_dump_thd_chains_info(cl_hw, buf, bufsz, pos, dump, ac);
+
+       addr = dump->common_info.agg_thds_addr[agg_idx].bar_thd_addr;
+       if (addr) {
+               /* BAR THD print */
+               *pos += scnprintf(buf + *pos, bufsz - *pos, "  BAR THD 0x%x\n", addr);
+               cl_dbg_thd_print(cl_hw, buf, bufsz, pos,
+                                &dump->agg_thd_info[agg_idx].bar_thd, addr);
+       }
+}
+
+static void cl_dbg_dump_thd_info(struct cl_hw *cl_hw,
+                                u8 *buf, int bufsz, int *pos,
+                                struct dbg_error_trace_info_drv *dump,
+                                u8 ac, u8 mu_idx)
+{
+       u32 session_idx = dump->common_info.fw_info.ac_info[ac].active_session;
+
+       if (session_idx != FW_DBG_INVALID_SESSION) {
+               bool is_agg = ((mu_idx > 0) || (session_idx >= IPC_TX_QUEUE_CNT)) ? true : false;
+
+               if (is_agg)
+                       cl_dbg_dump_agg_thd_info(cl_hw, buf, bufsz, pos, dump, ac, mu_idx);
+               else
+                       cl_dbg_dump_thd_chains_info(cl_hw, buf, bufsz, pos, dump, ac);
+       }
+}
+
+static void cl_dbg_dump_tx_trace_info(struct cl_hw *cl_hw,
+                                     u8 *buf, int bufsz, int *pos,
+                                     struct dbg_error_trace_info_drv *dump)
+{
+       int i = 0;
+       const char *fw_tx_frame_type_str[CL_MAX_FRM_TYPE] = {
+               [SING_FRM_TYPE]             = "MPDU",
+               [AGG_FRM_TYPE]              = "AMPDU",
+               [AGG_NEXT_IN_TXOP_FRM_TYPE] = "TXOP",
+               [INT_FRM_TYPE]              = "INTERNAL",
+               [BCN_FRM_TYPE]              = "BCN",
+               [MU_FRM_TYPE]               = "MU_AMPDU",
+               [FRM_TYPE_BASIC_TRIGGER]    = "BASIC_TF",
+               [FRM_TYPE_MU_BAR_TRIGGER]   = "MU_BAR",
+               [BCK_BCN_TYPE]              = "BCN_BCK",
+               [QOS_NULL]                  = "QOSNULL",
+               [AGG_TB]                    = "AGG_TB",
+               [RTS_TYPE]                  = "RTS_FW",
+               [CTS_TYPE]                  = "CTS_FW",
+               [TB_SINGLE_FRM_TYPE]        = "TB_SMPDU",
+               [TF_AMPDU_TYPE]             = "TF_AMPDU"
+       };
+
+       *pos += scnprintf(buf + *pos,
+                         bufsz - *pos,
+                         "\n#### TX Trace ####\n");
+
+       for (i = 0; i < ARRAY_SIZE(dump->common_info.fw_info.txl_ac_chain_trace); ++i) {
+               u8 trace_idx = 0, table_idx = 0;
+               u32 mu_idx;
+               struct dbg_txl_ac_chain_trace *trace_ptr =
+                       &dump->common_info.fw_info.txl_ac_chain_trace[i];
+               struct cl_dbg_txl_chain_info *data;
+
+               if (trace_ptr->next_chain_index == 0)
+                       table_idx = DBG_TXL_FRAME_EXCH_TRACE_DEPTH - 1;
+               else
+                       table_idx = trace_ptr->next_chain_index - 1;
+
+               data = &trace_ptr->data[table_idx];
+
+               if (data->count == 0)
+                       continue;
+
+               mu_idx = (i >= IPC_TX_QUEUE_CNT) ? (i - IPC_TX_QUEUE_CNT + 1) : 0;
+
+               *pos += scnprintf(buf + *pos, bufsz - *pos, "\n AC %u MU idx %u:\n", i, mu_idx);
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "|========|========|==========|==========|=========="
+                                 "|======|==========|==========|=====|======="
+                                 "|======|=========|=======|==========|\n"
+                                 "|#PPDU   | Type   |First THD | Last THD | Prev THD "
+                                 "|Req BW| PTalbe   |PTalbe_HE |queue|Length "
+                                 "|#MPDU |Chosen BW|TX time| txstatus |\n"
+                                 "|========|========|==========|==========|=========="
+                                 "|======|==========|==========|=====|======="
+                                 "|======|=========|=======|==========|\n");
+
+               for (trace_idx = 0; trace_idx < DBG_TXL_FRAME_EXCH_TRACE_DEPTH; ++trace_idx) {
+                       *pos += scnprintf(buf + *pos,
+                                         bufsz - *pos,
+                                         "|%8u|%8s|0x%08x|0x%08x|0x%08x|%6u|0x%08x"
+                                         "|0x%08x|%5u|%7u|%6u|%9u|%7u|0x%08x|\n",
+                                         data->count,
+                                         fw_tx_frame_type_str[data->frm_type],
+                                         data->first_thd_ptr,
+                                         data->last_thd_ptr,
+                                         data->prev_thd_ptr,
+                                         data->reqbw,
+                                         data->rate_ctrl_info,
+                                         data->rate_ctrl_info_he,
+                                         data->ce_txq_idx,
+                                         data->length,
+                                         data->mpdu_count,
+                                         data->chbw,
+                                         data->tx_time,
+                                         data->txstatus);
+
+                       if (!table_idx)
+                               table_idx = DBG_TXL_FRAME_EXCH_TRACE_DEPTH - 1;
+                       else
+                               table_idx--;
+
+                       data = &trace_ptr->data[table_idx];
+               }
+
+               *pos += scnprintf(buf + *pos,
+                                 bufsz - *pos,
+                                 "|========|========|==========|==========|==="
+                                 "=======|======|==========|==========|=====|="
+                                 "======|======|=========|=======|==========|\n");
+
+               cl_dbg_dump_thd_info(cl_hw, buf, bufsz, pos, dump, i, mu_idx);
+       }
+}
+
+static void cl_dbg_dump_error_info(struct cl_hw *cl_hw,
+                                  u8 *buf, int bufsz, int *pos,
+                                  struct dbg_error_trace_info_drv *trace)
+{
+       struct dbg_print_ind *ind = &trace->common_info.error_info;
+       const char *assert_string;
+       u16 file_id = le16_to_cpu(ind->file_id);
+       u16 line = le16_to_cpu(ind->line);
+       u16 has_param = le16_to_cpu(ind->has_param);
+       u32 param = has_param ? le32_to_cpu(ind->param) : 0;
+
+       if (file_id && line) {
+               *pos += scnprintf(buf + *pos,
+                                bufsz - *pos,
+                                "ASSERT_TCV%u @ FILE=%hu LINE=%hu param=0x%08X\n",
+                                cl_hw->idx, file_id, line, param);
+
+               /* Get assert string */
+               assert_string = cl_dbgfile_get_msg_txt(&cl_hw->dbg_data, file_id, line);
+               if (!assert_string)
+                       assert_string = "ASSERT STRING NOT FOUND";
+
+               *pos += scnprintf(buf + *pos,
+                                 bufsz - *pos,
+                                 "%s\n", assert_string);
+       } else {
+               struct dbg_info *dbg_info = (struct dbg_info *)cl_hw->dbginfo.buf;
+
+               *pos += snprintf(buf + *pos,
+                                bufsz - *pos,
+                                "%s\n", dbg_info->u.dump.general_data.error);
+       }
+}
+
+static void cl_dbg_dump_fw_trace_info(struct cl_hw *cl_hw,
+                                     u8 *buf, int bufsz, int *pos,
+                                     struct dbg_error_trace_info_drv *dump)
+{
+       int i = 0;
+
+       *pos += scnprintf(buf + *pos,
+                         bufsz - *pos,
+                         "\n###   FW trace dump   ###\n"
+                         "------------------------------------------------------"
+                         "-----------------------------------\n"
+                         "|idx|   String        | value 1  | value 2  | value 3 "
+                         "| value 4  | value 5  | value 6  |\n"
+                         "|---+-----------------+----------+----------+---------"
+                         "-+----------+----------+----------|\n");
+
+       for (i = 0; i < ARRAY_SIZE(dump->common_info.fw_info.fw_trace); ++i) {
+               u8 dbg_idx = (dump->common_info.fw_info.fw_trace_idx + i) % DBG_FW_TRACE_SIZE;
+               struct dbg_fw_trace *trace = &dump->common_info.fw_info.fw_trace[dbg_idx];
+               char *str = trace->string_ptr ? trace->string_char : "NULL";
+
+               *pos += scnprintf(buf + *pos,
+                                 bufsz - *pos,
+                                 "|%3u|%17s|0x%08x|0x%08x|0x%08x|0x%08x|0x%08x|0x%08x|\n",
+                                 i,
+                                 str,
+                                 trace->var_1,
+                                 trace->var_2,
+                                 trace->var_3,
+                                 trace->var_4,
+                                 trace->var_5,
+                                 trace->var_6);
+       }
+
+       *pos += scnprintf(buf + *pos,
+                         bufsz - *pos,
+                         "----------------------------------------------------"
+                         "-------------------------------------\n");
+}
+
+static int cl_dbg_dump_host_descr(struct cl_hw *cl_hw,
+                                 u8 *buf, int bufsz, int *pos)
+{
+       struct new_utsname *nu = init_utsname();
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "#### KERNEL ####\n"
+                         "release: %s\n"
+                         "version: %s\n"
+                         "machine: %s\n",
+                         nu->release,
+                         nu->version,
+                         nu->machine);
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "#### CPUs ####\n"
+                         "num online  : %d\n"
+                         "num possible: %d\n"
+                         "num present : %d\n"
+                         "num active  : %d\n",
+                         num_online_cpus(),
+                         num_possible_cpus(),
+                         num_present_cpus(),
+                         num_active_cpus());
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "#### ENDIANNESS ####\n"
+                         "LE (byte) : %u\n"
+                         "LE (bits) : %u\n"
+                         "BE (byte) : %u\n"
+                         "BE (bits) : %u\n",
+                         cl_are_host_bytes_le(),
+                         cl_are_host_bits_le(),
+                         cl_are_host_bytes_be(),
+                         cl_are_host_bits_be());
+       return 0;
+}
+
+static int cl_dbg_dump_chip_descr(struct cl_hw *cl_hw,
+                                 u8 *buf, int bufsz, int *pos)
+{
+       struct cl_version_db *vd = &cl_hw->version_db;
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "#### BASE ####\n"
+                         "chip : %u\n"
+                         "TCV  : %u\n"
+                         "bus  : %u\n",
+                         cl_hw->chip->idx,
+                         cl_hw->tcv_idx,
+                         cl_hw->chip->bus_type);
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "#### VERSIONS ####\n"
+                         "drv     : %s\n"
+                         "FW      : %s\n"
+                         "DSP     : 0x%-.8X\n"
+                         "RFIC SW : %u\n"
+                         "RFIC HW : 0x%X\n",
+                         vd->drv,
+                         vd->fw,
+                         vd->dsp,
+                         vd->rfic_sw,
+                         vd->rfic_hw);
+       /* TODO: AGC info */
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "#### STATE ####\n"
+                         "recoveries : %u\n"
+                         "DRV flags  : %lu\n"
+                         "TCV-0 en   : %u\n"
+                         "TCV-1 en   : %u\n",
+                         cl_hw->fw_recovery_cntr,
+                         cl_hw->drv_flags,
+                         cl_chip_is_tcv0_enabled(cl_hw->chip),
+                         cl_chip_is_tcv1_enabled(cl_hw->chip));
+       return 0;
+}
+
+static int cl_dbg_dump_ela_descr(struct cl_hw *cl_hw,
+                                u8 *buf, int bufsz, int *pos)
+{
+       struct cl_chip *chip = cl_hw->chip;
+       struct cl_ela_db *ed = &chip->ela_db;
+       int ret = 0;
+
+       if (cl_ela_is_on(chip)) {
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "#### CONF SOURCE ####\n"
+                                 "ELA mode: %s #\n",
+                                 chip->conf->ce_ela_mode);
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "#### CONF LIFETIME ####\n"
+                                 "adaptations  : %u #\n"
+                                 "applications : %u #\n"
+                                 "error state  : %d #\n",
+                                 ed->stats.adaptations_cnt,
+                                 ed->stats.applications_cnt,
+                                 ed->error_state);
+       } else {
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "#### DISABLED ####\n");
+       }
+       return ret;
+}
+
+static int cl_dbg_dump_raw_lcu_conf(struct cl_hw *cl_hw,
+                                   u8 *buf, int bufsz, int *pos)
+{
+       struct cl_chip *chip = cl_hw->chip;
+       struct cl_ela_db *ed = &chip->ela_db;
+       int ret = 0;
+
+       if (cl_ela_is_on(chip) && ed->raw_lcu_config)
+               *pos += scnprintf(buf + *pos, bufsz - *pos, "%s", ed->raw_lcu_config);
+       else
+               ret = -ENODATA;
+       return ret;
+}
+
+static int cl_dbg_dump_adapted_lcu_conf(struct cl_hw *cl_hw,
+                                       u8 *buf, int bufsz, int *pos)
+{
+       struct cl_chip *chip = cl_hw->chip;
+       struct cl_ela_db *ed = &chip->ela_db;
+       struct cl_lcu_cmd *cmd = NULL, *cmd_tmp = NULL;
+       int ret = 0;
+
+       if (cl_ela_is_on(chip)) {
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "#### CONF SOURCE ####\n"
+                                 "# %s #\n",
+                                 cl_ela_lcu_config_name(chip));
+               *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                 "#### CONF COMMANDS ####\n");
+
+               list_for_each_entry_safe(cmd, cmd_tmp, &ed->cmd_head, cmd_list)
+                       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                                         "%s 0x%X 0x%X\n",
+                                         cl_ela_lcu_cmd_str(cmd->type),
+                                         cmd->offset,
+                                         cmd->value);
+       } else {
+               ret = -ENODATA;
+       }
+       return ret;
+}
+
+static int cl_dbg_dump_la_mem(struct cl_hw *cl_hw,
+                             u8 *buf, int bufsz, int *pos,
+                             int idx, struct dbg_info *dbg_info)
+{
+       size_t la_size = 0;
+
+       if (ARRAY_SIZE(dbg_info->u.dump.la_mem) < idx + 1)
+               return -EINVAL;
+
+       la_size = ARRAY_SIZE(dbg_info->u.dump.la_mem[idx]);
+       if (la_size > (bufsz - *pos))
+               return -ENOBUFS;
+
+       memcpy(buf + *pos, &dbg_info->u.dump.la_mem[idx], la_size);
+       *pos += la_size;
+
+       return 0;
+}
+
+static int cl_dbg_dump_la_conf(struct cl_hw *cl_hw,
+                              u8 *buf, int bufsz, int *pos,
+                              int idx, struct dbg_debug_info_tag *gdata)
+{
+       size_t la_size = 0;
+
+       if (ARRAY_SIZE(gdata->la_conf) < idx + 1)
+               return -EINVAL;
+
+       la_size = sizeof(gdata->la_conf[idx]);
+       if (la_size > (bufsz - *pos))
+               return -ENOBUFS;
+
+       memcpy(buf + *pos, &gdata->la_conf[idx], la_size);
+       *pos += la_size;
+
+       return 0;
+}
+
+static int cl_dbg_dump_mac_diags(struct cl_hw *cl_hw,
+                                u8 *buf, int bufsz, int *pos,
+                                struct dbg_debug_info_tag *gdata)
+{
+       size_t la_size = ARRAY_SIZE(gdata->diags_mac);
+
+       if (la_size > (bufsz - *pos))
+               return -ENOBUFS;
+
+       memcpy(buf + *pos, &gdata->diags_mac, la_size);
+       *pos += la_size;
+
+       return 0;
+}
+
+static int cl_dbg_dump_hw_diags(struct cl_hw *cl_hw,
+                               u8 *buf, int bufsz, int *pos,
+                               struct dbg_debug_info_tag *gdata)
+{
+       *pos += scnprintf(buf + *pos, bufsz - *pos, "%08X\n", gdata->hw_diag);
+
+       return 0;
+}
+
+static int cl_dbg_dump_sw_diags(struct cl_hw *cl_hw,
+                               u8 *buf, int bufsz, int *pos,
+                               struct dbg_debug_info_tag *gdata)
+{
+       size_t la_size = min_t(size_t, ARRAY_SIZE(gdata->sw_diag),
+                              gdata->sw_diag_len);
+
+       if (la_size > (bufsz - *pos))
+               return -ENOBUFS;
+
+       memcpy(buf + *pos, &gdata->sw_diag, la_size);
+       *pos += la_size;
+
+       return 0;
+}
+
+static int cl_dbg_dump_chan_info(struct cl_hw *cl_hw,
+                                u8 *buf, int bufsz, int *pos,
+                                struct dbg_debug_info_tag *gdata)
+{
+       u32 info1 = le32_to_cpu(gdata->chan_info.info1);
+       u32 info2 = le32_to_cpu(gdata->chan_info.info2);
+       u8 band = info1 & 0xFF;
+       u8 type = (info1 >> 8) & 0xFF;
+       u16 prim20 = (info1 >> 16) & 0xFFFF;
+       u16 center1 = info2 & 0xFFFF;
+
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "#### FW ####\n"
+                         "band         : %u\n"
+                         "type         : %u\n"
+                         "prim20_freq  : %u.%u MHz\n"
+                         "center1_freq : %u.%u MHz\n",
+                         band,
+                         type,
+                         Q2_TO_FREQ(prim20), Q2_TO_FREQ_FRAC(prim20),
+                         Q2_TO_FREQ(center1), Q2_TO_FREQ_FRAC(center1));
+       *pos += scnprintf(buf + *pos, bufsz - *pos,
+                         "#### DRIVER ####\n"
+                         "channel      : %u\n"
+                         "bw           : %u\n"
+                         "primary_freq : %u MHz\n"
+                         "center_freq  : %u MHz\n",
+                         cl_hw->channel,
+                         cl_hw->bw,
+                         cl_hw->primary_freq,
+                         cl_hw->center_freq);
+       return 0;
+}
+
+/**
+ * pre_fill_hook - initialize record data in coredump container
+ *
+ * Each record presents some section of information. Since, at this stage we
+ * have no idea about estimated data length, we shift "pos" only for size
+ * of header information, actual number of written bytes should be calculated
+ * in the post_fill_hook.
+ *
+ * @record: (NLEV) Name-Length-Error-Values record.
+ * @d: Coredump to fill in.
+ * @name: Name to set for this specific record;
+ * @pos: Position of last filled in element in the coredump (equal prev_pos till
+ *  fill in).
+ * @prev_pos: Position of the previous filled in element in the coredump.
+ *
+ * Returns nothing.
+ */
+static void pre_fill_hook(struct cl_nlev **record, struct cl_coredump *cd,
+                         char *name, int *pos, int *prev_pos)
+{
+       size_t coredump_space = le32_to_cpu(cd->len) - sizeof(*cd);
+       size_t free_space = coredump_space - *pos;
+
+       /* Save prev postion for NLEV length calculation in "post" hook */
+       *prev_pos = *pos;
+
+       /* Check if there is enough space for NLE (w/o V) */
+       if (free_space < sizeof(**record))
+               return;
+
+       /* Set pointer of the record to proper buffer place */
+       *record = (struct cl_nlev *)(cd->data + *pos);
+
+       /* Fill type info string */
+       scnprintf(cd->data + *pos, free_space, "%s", name);
+
+       /* Adjust position as like as new NLEV was added */
+       *pos += sizeof(**record);
+}
+
+/**
+ * post_fill_hook - finalize record data in the coredump container
+ *
+ * @record: (NLEV) Name-Length-Error-Values record.
+ * @err_code: processing error indication, typical errno
+ * @pos: Position of last filled in element in the coredump.
+ * @prev_pos: Position of the previous filled in element in the coredump,
+ *  in conjunction with "pos" is used to calculated filled in data and set
+ *  it as "Length" in the NLEV header.
+ *
+ * Returns nothing.
+ */
+static void post_fill_hook(struct cl_nlev **record, int *err_code, int *pos,
+                          int *prev_pos)
+{
+       /* Finalize what we know about NLEV - size (without headers)
+        * and processing error codeA
+        */
+       (**record).l = cpu_to_le32(*pos - *prev_pos - sizeof(**record));
+       (**record).e = cpu_to_le32(*err_code);
+
+       /* Reset error code for further usage */
+       *err_code = 0;
+}
+
+struct cl_coredump *cl_fw_dbg_prepare_coredump(struct cl_hw *cl_hw)
+{
+       struct dbg_info *dbg_info = (struct dbg_info *)cl_hw->dbginfo.buf;
+       struct dbg_error_trace_info_drv *fdump = &dbg_info->u.dump.fw_dump;
+       struct dbg_debug_info_tag *gdata = &dbg_info->u.dump.general_data;
+       struct cl_coredump *cd;
+       unsigned char *buf;
+       /* TODO: Make this size dynamic */
+       size_t len = PAGE_SIZE * 100;
+       size_t data_len = len - sizeof(*cd);
+       int pos = 0;
+       int prev_pos = 0;
+       int e = 0;
+       struct cl_nlev *rec = NULL;
+
+       buf = vzalloc(len);
+       if (!buf)
+               goto out;
+
+       cd = (typeof(cd))buf;
+       cd->len = cpu_to_le32(len);
+       cd->self_version = cpu_to_le32((CL_COREDUMP_V1 << 28) +
+                                      sizeof(*cd) + sizeof(*rec));
+       /* TODO: Mask support */
+       cd->dump_mask = cpu_to_le32(0);
+       cd->trig_tv_sec = cpu_to_le64(cl_hw->dbginfo.trigger_tstamp.tv_sec);
+       cd->trig_tv_nsec = cpu_to_le64(cl_hw->dbginfo.trigger_tstamp.tv_nsec);
+       scnprintf(cd->magic, sizeof(cd->magic), "CE_CL8K_DUMP");
+
+       /* Main section with dynamic dump data filling */
+       mutex_lock(&cl_hw->dbginfo.mutex);
+
+       pre_fill_hook(&rec, cd, "chip_descr", &pos, &prev_pos);
+       e = cl_dbg_dump_chip_descr(cl_hw, cd->data, data_len, &pos);
+       post_fill_hook(&rec, &e, &pos, &prev_pos);
+
+       pre_fill_hook(&rec, cd, "host_descr", &pos, &prev_pos);
+       e = cl_dbg_dump_host_descr(cl_hw, cd->data, data_len, &pos);
+       post_fill_hook(&rec, &e, &pos, &prev_pos);
+
+       pre_fill_hook(&rec, cd, "error", &pos, &prev_pos);
+       cl_dbg_dump_error_info(cl_hw, cd->data, data_len, &pos, fdump);
+       post_fill_hook(&rec, &e, &pos, &prev_pos);
+
+       /* Embedded logic analyzer info */
+       pre_fill_hook(&rec, cd, "ela_descr", &pos, &prev_pos);
+       e = cl_dbg_dump_ela_descr(cl_hw, cd->data, data_len, &pos);
+       post_fill_hook(&rec, &e, &pos, &prev_pos);
+
+       pre_fill_hook(&rec, cd, "raw_lcu_conf", &pos, &prev_pos);
+       e = cl_dbg_dump_raw_lcu_conf(cl_hw, cd->data, data_len, &pos);
+       post_fill_hook(&rec, &e, &pos, &prev_pos);
+
+       pre_fill_hook(&rec, cd, "adapted_lcu_conf", &pos, &prev_pos);
+       e = cl_dbg_dump_adapted_lcu_conf(cl_hw, cd->data, data_len, &pos);
+       post_fill_hook(&rec, &e, &pos, &prev_pos);
+
+       /* Stringified FW state conents */
+       pre_fill_hook(&rec, cd, "fw_dump", &pos, &prev_pos);
+       cl_dbg_dump_check_params(cl_hw, cd->data, data_len, &pos, fdump);
+       cl_dbg_dump_machw_regs(cl_hw, cd->data, data_len, &pos, fdump);
+       cl_dbg_dump_phyhw_regs(cl_hw, cd->data, data_len, &pos, fdump);
+       cl_dbg_dump_ac_info(cl_hw, cd->data, data_len, &pos, fdump);
+       cl_dbg_dump_single_tx_list_info(cl_hw, cd->data, data_len, &pos, fdump);
+       cl_dbg_dump_agg_tx_list_info(cl_hw, cd->data, data_len, &pos, fdump);
+       cl_dbg_dump_tx_trace_info(cl_hw, cd->data, data_len, &pos, fdump);
+       cl_dbg_dump_fw_trace_info(cl_hw, cd->data, data_len, &pos, fdump);
+       post_fill_hook(&rec, &e, &pos, &prev_pos);
+
+       pre_fill_hook(&rec, cd, "hw_diags", &pos, &prev_pos);
+       e = cl_dbg_dump_hw_diags(cl_hw, cd->data, data_len, &pos, gdata);
+       post_fill_hook(&rec, &e, &pos, &prev_pos);
+
+       /* FW/DRV-view of channel and freqs */
+       pre_fill_hook(&rec, cd, "chan_info", &pos, &prev_pos);
+       e = cl_dbg_dump_chan_info(cl_hw, cd->data, data_len, &pos, gdata);
+       post_fill_hook(&rec, &e, &pos, &prev_pos);
+
+       /* Logic analyzer (0) memory - MAC */
+       pre_fill_hook(&rec, cd, "la_mac_trace", &pos, &prev_pos);
+       e = cl_dbg_dump_la_mem(cl_hw, cd->data, data_len, &pos, LA_MAC_IDX, dbg_info);
+       post_fill_hook(&rec, &e, &pos, &prev_pos);
+
+       /* Logic analyzer (0) conf - MAC */
+       pre_fill_hook(&rec, cd, "la_mac_conf", &pos, &prev_pos);
+       e = cl_dbg_dump_la_conf(cl_hw, cd->data, data_len, &pos, LA_MAC_IDX, gdata);
+       post_fill_hook(&rec, &e, &pos, &prev_pos);
+
+       /* Logic analyzer (1) memory - PHY */
+       pre_fill_hook(&rec, cd, "la_phy_trace", &pos, &prev_pos);
+       e = cl_dbg_dump_la_mem(cl_hw, cd->data, data_len, &pos, LA_PHY_IDX, dbg_info);
+       post_fill_hook(&rec, &e, &pos, &prev_pos);
+
+       /* Logic analyzer (1) conf - PHY */
+       pre_fill_hook(&rec, cd, "la_phy_conf", &pos, &prev_pos);
+       e = cl_dbg_dump_la_conf(cl_hw, cd->data, data_len, &pos, LA_PHY_IDX, gdata);
+       post_fill_hook(&rec, &e, &pos, &prev_pos);
+
+       /* Diagnotics port - MAC */
+       pre_fill_hook(&rec, cd, "mac_diags", &pos, &prev_pos);
+       e = cl_dbg_dump_mac_diags(cl_hw, cd->data, data_len, &pos, gdata);
+       post_fill_hook(&rec, &e, &pos, &prev_pos);
+
+       /* SW diagnostics port */
+       pre_fill_hook(&rec, cd, "sw_diags", &pos, &prev_pos);
+       e = cl_dbg_dump_sw_diags(cl_hw, cd->data, data_len, &pos, gdata);
+       post_fill_hook(&rec, &e, &pos, &prev_pos);
+
+       mutex_unlock(&cl_hw->dbginfo.mutex);
+
+       /* devcoredump will take care of memory free process */
+       return cd;
+out:
+       return NULL;
+}
+
+#define INVALID_AMPDU_CNT U8_MAX
+
+void cl_fw_dbg_trigger_based_init(struct cl_hw *cl_hw)
+{
+       memset(&cl_hw->tb_stats, 0, sizeof(cl_hw->tb_stats));
+       cl_hw->tb_stats.ampdu_cnt = INVALID_AMPDU_CNT;
+}
+
+void cl_fw_dbg_trigger_based_update(struct cl_hw *cl_hw, struct hw_rxhdr *rxhdr,
+                                   struct ieee80211_hdr *hdr)
+{
+       struct cl_rx_trigger_based_stats *tb_stats = &cl_hw->tb_stats;
+
+       if (!tb_stats->enable)
+               return;
+
+       if (tb_stats->ampdu_cnt == INVALID_AMPDU_CNT) {
+               tb_stats->ampdu_cnt = rxhdr->ampdu_cnt;
+               if (rxhdr->format_mod == FORMATMOD_HE_TRIG) {
+                       if (ieee80211_is_qos_nullfunc(hdr->frame_control))
+                               tb_stats->qos_null_per_agg += rxhdr->frm_successful_rx;
+                       else
+                               tb_stats->data_per_agg += rxhdr->frm_successful_rx;
+
+                       tb_stats->total += rxhdr->frm_successful_rx;
+               }
+       } else if (tb_stats->ampdu_cnt == rxhdr->ampdu_cnt) {
+               if (rxhdr->format_mod == FORMATMOD_HE_TRIG) {
+                       if (ieee80211_is_qos_nullfunc(hdr->frame_control))
+                               tb_stats->qos_null_per_agg += rxhdr->frm_successful_rx;
+                       else
+                               tb_stats->data_per_agg += rxhdr->frm_successful_rx;
+
+                       tb_stats->total += rxhdr->frm_successful_rx;
+               }
+       } else {
+               tb_stats->ampdu_cnt = rxhdr->ampdu_cnt;
+               if (unlikely(tb_stats->data_per_agg >= DBG_STATS_MAX_AGG_SIZE))
+                       cl_dbg_err(cl_hw, "rx trigger_based agg size %u > 256\n",
+                                  tb_stats->data_per_agg);
+               else
+                       tb_stats->data[tb_stats->data_per_agg]++;
+
+               if (unlikely(tb_stats->qos_null_per_agg > TID_MAX))
+                       tb_stats->qos_null[TID_MAX + 1]++;
+               else
+                       tb_stats->qos_null[tb_stats->qos_null_per_agg]++;
+
+               tb_stats->data_per_agg = 0;
+               tb_stats->qos_null_per_agg = 0;
+
+               if (rxhdr->format_mod == FORMATMOD_HE_TRIG) {
+                       if (ieee80211_is_qos_nullfunc(hdr->frame_control))
+                               tb_stats->qos_null_per_agg += rxhdr->frm_successful_rx;
+                       else
+                               tb_stats->data_per_agg += rxhdr->frm_successful_rx;
+
+                       tb_stats->total += rxhdr->frm_successful_rx;
+               }
+       }
+}
+
+void cl_fw_dbg_trigger_based_reset(struct cl_hw *cl_hw)
+{
+       u32 idx;
+       struct cl_rx_trigger_based_stats *tb_stats = &cl_hw->tb_stats;
+
+       tb_stats->total = 0;
+
+       for (idx = 0; idx < ARRAY_SIZE(tb_stats->data); idx++)
+               tb_stats->data[idx] = 0;
+
+       for (idx = 0; idx < ARRAY_SIZE(tb_stats->qos_null); idx++)
+               tb_stats->qos_null[idx] = 0;
+}