new file mode 100644
@@ -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;
+}