The initial debugfs infra bringup in ath12k driver and create the ath12k debugfs
and soc-specific directories in /sys/kernel/debug/
For each ath12k device, directory will be created in <bus>-<devname>
schema under ath12k root directory.
Example with one ath12k device:
/sys/kernel/debug/ath12k/pci-0000:06:00.0
ath12k
`-- pci-0000:06:00.0
    |-- mac0
To enable ath12k debugfs support (CONFIG_ATH12K_DEBUGFS=y)
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1
Signed-off-by: Ramasamy Kaliappan <quic_rkaliapp@quicinc.com>
Signed-off-by: Ramya Gnanasekar <quic_rgnanase@quicinc.com>
Acked-by: Jeff Johnson <quic_jjohnson@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://msgid.link/20240320171305.655288-2-quic_rgnanase@quicinc.com
          If unsure, say Y to make it easier to debug problems. But if
          you want optimal performance choose N.
 
+config ATH12K_DEBUGFS
+       bool "QTI ath12k debugfs support"
+       depends on ATH12K && MAC80211_DEBUGFS
+       help
+         Enable ath12k debugfs support
+
+         If unsure, say Y to make it easier to debug problems. But if
+         you want optimal performance choose N.
+
 config ATH12K_TRACING
        bool "ath12k tracing support"
        depends on ATH12K && EVENT_TRACING
 
            fw.o \
            p2p.o
 
+ath12k-$(CONFIG_ATH12K_DEBUGFS) += debugfs.o
 ath12k-$(CONFIG_ATH12K_TRACING) += trace.o
 
 # for tracing framework to find trace.h
 
 #include "debug.h"
 #include "hif.h"
 #include "fw.h"
+#include "debugfs.h"
 
 unsigned int ath12k_debug_mask;
 module_param_named(debug_mask, ath12k_debug_mask, uint, 0644);
                return ret;
        }
 
+       ath12k_debugfs_soc_create(ab);
+
        ret = ath12k_hif_power_up(ab);
        if (ret) {
                ath12k_err(ab, "failed to power up :%d\n", ret);
        return 0;
 
 err_qmi_deinit:
+       ath12k_debugfs_soc_destroy(ab);
        ath12k_qmi_deinit_service(ab);
        return ret;
 }
 {
        ath12k_dp_free(ab);
        ath12k_reg_free(ab);
+       ath12k_debugfs_soc_destroy(ab);
        ath12k_qmi_deinit_service(ab);
 }
 
 
        struct list_head bcn;
 };
 
+struct ath12k_debug {
+       struct dentry *debugfs_pdev;
+};
+
 struct ath12k_per_peer_tx_stats {
        u32 succ_bytes;
        u32 retry_bytes;
        struct ath12k_per_peer_tx_stats cached_stats;
        u32 last_ppdu_id;
        u32 cached_ppdu_id;
+#ifdef CONFIG_ATH12K_DEBUGFS
+       struct ath12k_debug debug;
+#endif
 
        bool dfs_block_radar_events;
        bool monitor_conf_enabled;
        /* Current DFS Regulatory */
        enum ath12k_dfs_region dfs_region;
        struct ath12k_soc_dp_stats soc_stats;
+#ifdef CONFIG_ATH12K_DEBUGFS
+       struct dentry *debugfs_soc;
+#endif
 
        unsigned long dev_flags;
        struct completion driver_recovery;
 
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "core.h"
+#include "debugfs.h"
+
+void ath12k_debugfs_soc_create(struct ath12k_base *ab)
+{
+       bool dput_needed;
+       char soc_name[64] = { 0 };
+       struct dentry *debugfs_ath12k;
+
+       debugfs_ath12k = debugfs_lookup("ath12k", NULL);
+       if (debugfs_ath12k) {
+               /* a dentry from lookup() needs dput() after we don't use it */
+               dput_needed = true;
+       } else {
+               debugfs_ath12k = debugfs_create_dir("ath12k", NULL);
+               if (IS_ERR_OR_NULL(debugfs_ath12k))
+                       return;
+               dput_needed = false;
+       }
+
+       scnprintf(soc_name, sizeof(soc_name), "%s-%s", ath12k_bus_str(ab->hif.bus),
+                 dev_name(ab->dev));
+
+       ab->debugfs_soc = debugfs_create_dir(soc_name, debugfs_ath12k);
+
+       if (dput_needed)
+               dput(debugfs_ath12k);
+}
+
+void ath12k_debugfs_soc_destroy(struct ath12k_base *ab)
+{
+       debugfs_remove_recursive(ab->debugfs_soc);
+       ab->debugfs_soc = NULL;
+       /* We are not removing ath12k directory on purpose, even if it
+        * would be empty. This simplifies the directory handling and it's
+        * a minor cosmetic issue to leave an empty ath12k directory to
+        * debugfs.
+        */
+}
+
+void ath12k_debugfs_register(struct ath12k *ar)
+{
+       struct ath12k_base *ab = ar->ab;
+       struct ieee80211_hw *hw = ar->ah->hw;
+       char pdev_name[5];
+       char buf[100] = {0};
+
+       scnprintf(pdev_name, sizeof(pdev_name), "%s%d", "mac", ar->pdev_idx);
+
+       ar->debug.debugfs_pdev = debugfs_create_dir(pdev_name, ab->debugfs_soc);
+
+       /* Create a symlink under ieee80211/phy* */
+       scnprintf(buf, sizeof(buf), "../../ath12k/%pd2", ar->debug.debugfs_pdev);
+       debugfs_create_symlink("ath12k", hw->wiphy->debugfsdir, buf);
+}
 
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _ATH12K_DEBUGFS_H_
+#define _ATH12K_DEBUGFS_H_
+
+#ifdef CONFIG_ATH12K_DEBUGFS
+void ath12k_debugfs_soc_create(struct ath12k_base *ab);
+void ath12k_debugfs_soc_destroy(struct ath12k_base *ab);
+void ath12k_debugfs_register(struct ath12k *ar);
+
+#else
+static inline void ath12k_debugfs_soc_create(struct ath12k_base *ab)
+{
+}
+
+static inline void ath12k_debugfs_soc_destroy(struct ath12k_base *ab)
+{
+}
+
+static inline void ath12k_debugfs_register(struct ath12k *ar)
+{
+}
+
+#endif /* CONFIG_ATH12K_DEBUGFS */
+
+#endif /* _ATH12K_DEBUGFS_H_ */
 
 #include "dp_tx.h"
 #include "dp_rx.h"
 #include "peer.h"
+#include "debugfs.h"
 
 #define CHAN2G(_channel, _freq, _flags) { \
        .band                   = NL80211_BAND_2GHZ, \
                goto err_unregister_hw;
        }
 
+       ath12k_debugfs_register(ar);
+
        return 0;
 
 err_unregister_hw: