From patchwork Thu Jun 17 15:59:33 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viktor Barna X-Patchwork-Id: 462735 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI,MSGID_FROM_MTA_HEADER,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7C433C49EA7 for ; Thu, 17 Jun 2021 16:06:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4A9FE61003 for ; Thu, 17 Jun 2021 16:06:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229972AbhFQQJD (ORCPT ); Thu, 17 Jun 2021 12:09:03 -0400 Received: from mail-db8eur05on2048.outbound.protection.outlook.com ([40.107.20.48]:5985 "EHLO EUR05-DB8-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S229805AbhFQQIC (ORCPT ); Thu, 17 Jun 2021 12:08:02 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=dwG3CwBofSEaonW7oyhPAqflszx+4urSVxKexf+G8li8PGxQMK2HJKWPlXojFn7XNelrRE+z3HS7YpLYy8nY/s4Uuy2XX9Sv1SPntTWCmhjn3SEU+69ZcoiTTa/vD8F9fpP9LUB4AaaI1NNXe5AIKIexGJKmKbWcAmAYgHKVquLmSFyMJFH6ZhWbMax1OUfzn2pu+dqJgwQEHObnv8dT5i5CgUlzo8gwcrieXvXIe88thDWGFJWgMl3G+2QCe18zuV6gkFThxj4RQdnca9oyrkiGGyHNDsPWyLlP/+CmSMUBAIC0y0GPFP6+4wP+26jn6h5cKDMZ7BGZsNOpOLtBWw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=nWW59LN2ZDQGaa5RSVtdOC16w8Ffj+cuIFYvIYC8uro=; b=AnopNszkU+HWqWR6oj6qJzxLM6oOTDCPyWVGV+X+O1SwNsFq8c4+Ez28JP9e872UwYRBjxuRoWwrtZXLUG2lDp3SoaoWe2qWmvKsMOZypNxPuphE5GeeAIzTpBDgkzV9bySKoDoJ4P8pW7ZjsbrS87WM63Su5HIyouOCA7cINlUJPtSOE7flqwYoercHWOO3rWTCirRyFeei179SZ1dvh3c1Zi7pnudmEho68BNWmWG7bOeV/K/bML04qol/BhUQqeP/yQDXYXXDWyIa7jl+3w/7DRAewbj9FxZiOIbJ5UOxvwEAOpo2weMxjoI0fe6HYcJ6MRvXZVk+49QBmtLZDA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=celeno.com; dmarc=pass action=none header.from=celeno.com; dkim=pass header.d=celeno.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=celeno.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=nWW59LN2ZDQGaa5RSVtdOC16w8Ffj+cuIFYvIYC8uro=; b=q0PKbS6rHSb7Gn81IA1yRhuZSKWD6pZkq7Yxk5NX9lgUPFdbW4ApIt7GJQ+lxwAMdo8Uc9BYhsMxcR2WloKt1OgeQtOtexoWefIoh4C7znNz64/4ILB6NNA7tEy+EUI5xQOPGKzAdI9N8/fRMyTd3hhTL4ZVpl/FhoHJ6Kxn01U= Authentication-Results: vger.kernel.org; dkim=none (message not signed) header.d=none; vger.kernel.org; dmarc=none action=none header.from=celeno.com; Received: from AM9P192MB1412.EURP192.PROD.OUTLOOK.COM (2603:10a6:20b:38b::16) by AM9P192MB0871.EURP192.PROD.OUTLOOK.COM (2603:10a6:20b:1fb::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4242.21; Thu, 17 Jun 2021 16:04:28 +0000 Received: from AM9P192MB1412.EURP192.PROD.OUTLOOK.COM ([fe80::1847:5583:4db7:102f]) by AM9P192MB1412.EURP192.PROD.OUTLOOK.COM ([fe80::1847:5583:4db7:102f%4]) with mapi id 15.20.4242.021; Thu, 17 Jun 2021 16:04:28 +0000 From: viktor.barna@celeno.com To: linux-wireless@vger.kernel.org Cc: Kalle Valo , "David S . Miller" , Jakub Kicinski , Aviad Brikman , Eliav Farber , Oleksandr Savchenko , Shay Bar , Viktor Barna Subject: [RFC v1 086/256] cl8k: add fw/fw_dbg.c Date: Thu, 17 Jun 2021 15:59:33 +0000 Message-Id: <20210617160223.160998-87-viktor.barna@celeno.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210617160223.160998-1-viktor.barna@celeno.com> References: <20210617160223.160998-1-viktor.barna@celeno.com> X-Originating-IP: [62.216.42.54] X-ClientProxiedBy: PR3PR09CA0018.eurprd09.prod.outlook.com (2603:10a6:102:b7::23) To AM9P192MB1412.EURP192.PROD.OUTLOOK.COM (2603:10a6:20b:38b::16) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from localhost.localdomain (62.216.42.54) by PR3PR09CA0018.eurprd09.prod.outlook.com (2603:10a6:102:b7::23) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4242.18 via Frontend Transport; Thu, 17 Jun 2021 16:04:07 +0000 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 04f886ee-9d3b-4f62-fe95-08d931a989e8 X-MS-TrafficTypeDiagnostic: AM9P192MB0871: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:222; X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: tkgmy34eEN31rmpjoLP9tNIQBHL7UT1MuhyAz/G+twqbOZRhivhjDeGxnDng1WFrOxPxRCH8SGr4Ae3k3ithFm+Gq2u+V0P9VdK60mYmO+8nFcA+l9PWXiFTxHH/Siio9WQP4GfB/EMkuzMof9RmRaxPMrsxgQGSMwgWTS+cNOcu+2qFybaAhHZu1DTDJ7wE7L/poevvT8RoQjtfH9/d1CWe/qI63t5UKLLC9QBDnF/C2YE6sWCykg8G3ETh9u3gmUyYhQR8hu7lO7wUtRa3k8umdppy5KqRKXp6QURRhLbphsG+6Y3f5nkA+aEEYRj0uJC5bI0bP87O91JruqPOiytnjGUBCGa3wVI2TSgLw1V7ArZU6wSQSojBmaR9mYL3hjI7VYSyEkOJf4lM7SNxTR4Ktsa7mJXPSVlzyKZuX92qSFFaEj6dQGB5Mr2PT4qsVXzwBCsG8o88UPYVgBc6rEvwoa05b9/FPL+FvC6caAEjhuCEffPWEATpQO+7tdjRDQmVYmvj9rN7eOPi3EFclEG+Sy27y2059utY5OgbT1AomX6Xyo9HD4tfKSnEP2vpkvTCncuAJWzWAY1Wlv2J7yHr6MjKDur6+qxcJqmGzS6YvjcjhsqtgcqQQcGamrZA5oPaDVLgAw9yhdrXi1K6kolbC7GVHcAb4L7E0g72SB80t3/g9YHmWzt8V5MouUhEN9727PP41xTv2gLEd15iIw== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:AM9P192MB1412.EURP192.PROD.OUTLOOK.COM; PTR:; CAT:NONE; SFS:(136003)(366004)(376002)(346002)(396003)(39840400004)(16526019)(66946007)(478600001)(26005)(66476007)(30864003)(6486002)(186003)(66556008)(9686003)(316002)(86362001)(6506007)(6666004)(6512007)(55236004)(38350700002)(1076003)(38100700002)(4326008)(8676002)(83380400001)(5660300002)(52116002)(2616005)(2906002)(6916009)(956004)(8936002)(107886003)(54906003)(36756003)(69590400013)(32563001)(579004)(559001); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: tLXKPHBllQL13gTyrCM1G8sJrfXbxEOcrR4v9Ywdhs6CDeuIwbm28XHcER+A4IyH7IHuyx0QHBg2UeopyrgrL4RvvPQjq4rhJWngy2WX0hw4inVpUMtjdfciuFJCxi7y7ZcHLIMB7FEe5gh9ERBen+GdsKwEM0sN1r5xEZtMIojGnioHhmIjAzMG17UgUgtvHdUK/3DP5CxmRLtmUoCIioCspIVkzqEjYts7uKqeU/wUjKPOr8OqBVlsapZaMzFKEfMhYC2UqIpNGiGEojLU482/PgbylcEpSHdyfp7LABwMskvbzfW5nMy8E3Ouj2NYwIq99vhIdsFEj0ZWo9bsDsr8K+RcbkKF7IKHleZDCil7gvF2+WoEfHfGx8H44lpuTJz/uO0Y/lqiLOU08tgMS9pQCcA5hl0eX1qTxw7nLUeY/ujK0CWErPpPAyA5mvXDNRAsU+zQGlFSFsdNZpwewhTVMZSykoVA4XmAa+Efgla/mqa7uZmoFJEimCGLmh8yqUV/xtjy9Jmiv55YWaTbyb8HTvC/jRMVDZSfRLkt9nyguHpVLIUV/qtmccxND8ZSGIo5A/m38k30AQjfMJM6R/9TO4BF5w2p3lkBwoNhaozpF85r0DeAO65FhpjZi2i3v9bGwlD9Zz3NAu1Vv3AHcXi8mUSanf7BahX9osWp1/CN2sMDrOuaBSGGUl/HrCiSSaBHpEo2hqTYMkbS5R2qgY+h18uDvpXRQecMcsX8MK1cEtWVROutyGerW6cHudREgxxrJWvPTLSoOq1WNCV8a3hPQY2yjIuRP9W+30VmwWNqxUNfjDHua9N3wKtgGOu+BJeYZHFChh00YIf5VW+dt+vSMHEOSB7ts7N6ZDMqYmuJcgPOmquhdxPVu7Y42yuy30p5QtM/7f76FMLTe3o8OKtH37NfN4cFvlyiJj1FogE29dJAShu6xugDo1Z8L83o9HOssX2R2r69IvSHqdgvz8P8ZVZK1FTOWbMjQIuco3dtPuhrtZq6gCAAVIXh9McL8K07RLgz8ecUMj9GTy8zNg5sOqM59VdwB1pXctv0vdK9vFj+BMLtCYNILk8FuzO4XwprIIYJ4F87zW6XihQ147ZJatI0GOd0oQwraRdfcPeQzddt2N4TPKYSf+WnDjwc5UH98fn6bmeEXbJSnVCLAHmmLMJ7ZED4E/M4+FPPqN/S+t3FbDkMb5N9/TYI493vhYpft7P/9poGXIU850H8qBICWaU2mPwqfG/eVyI2fKj5rk09Sf4Dp8tgMsCta52y2EeM78QyJoOmMk927zgFZUW/JMsPVOEhcraw8yHgbfKVZP9lZNpTuV+K+duc37/q X-OriginatorOrg: celeno.com X-MS-Exchange-CrossTenant-Network-Message-Id: 04f886ee-9d3b-4f62-fe95-08d931a989e8 X-MS-Exchange-CrossTenant-AuthSource: AM9P192MB1412.EURP192.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 17 Jun 2021 16:04:08.9677 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: f313103b-4c9f-4fd3-b5cf-b97f91c4afa8 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: O7uOVgeZRhyWsAKe0R+nP1v9ntFy0FZcojvJUGngmMkor5azIsTqvgmpLzfjliAtlzWexmDf4PhLVDKj2vV7Pw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM9P192MB0871 Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Viktor Barna (Part of the split. Please, take a look at the cover letter for more details). Signed-off-by: Viktor Barna --- drivers/net/wireless/celeno/cl8k/fw/fw_dbg.c | 2686 ++++++++++++++++++ 1 file changed, 2686 insertions(+) create mode 100644 drivers/net/wireless/celeno/cl8k/fw/fw_dbg.c -- 2.30.0 diff --git a/drivers/net/wireless/celeno/cl8k/fw/fw_dbg.c b/drivers/net/wireless/celeno/cl8k/fw/fw_dbg.c new file mode 100644 index 000000000000..413f45b433c6 --- /dev/null +++ b/drivers/net/wireless/celeno/cl8k/fw/fw_dbg.c @@ -0,0 +1,2686 @@ +// SPDX-License-Identifier: MIT +/* Copyright(c) 2019-2021, Celeno Communications Ltd. */ +#include + +#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 \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; +}