wifi: ath11k: implement SRAM dump debugfs interface
authorBaochen Qiang <quic_bqiang@quicinc.com>
Wed, 7 Sep 2022 08:31:13 +0000 (11:31 +0300)
committerKalle Valo <quic_kvalo@quicinc.com>
Sat, 10 Sep 2022 06:26:18 +0000 (09:26 +0300)
On-board SRAM contains valuable information for firmware debugging so
add a new file named "sram" to debugfs with which we can dump SRAM
content using following the following:

cp /sys/kernel/debug/ath11k/wcn6855\ hw2.0/sram /tmp/sram

Currently this feature is enabled for QCA6390 and WCN6855.

Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20220802075533.1744-3-quic_bqiang@quicinc.com
drivers/net/wireless/ath/ath11k/ahb.c
drivers/net/wireless/ath/ath11k/core.c
drivers/net/wireless/ath/ath11k/debugfs.c
drivers/net/wireless/ath/ath11k/hif.h
drivers/net/wireless/ath/ath11k/hw.h
drivers/net/wireless/ath/ath11k/pci.c
drivers/net/wireless/ath/ath11k/pcic.c
drivers/net/wireless/ath/ath11k/pcic.h

index 29c40c6..31e9cf3 100644 (file)
@@ -691,6 +691,7 @@ static const struct ath11k_hif_ops ath11k_ahb_hif_ops_ipq8074 = {
        .stop = ath11k_ahb_stop,
        .read32 = ath11k_ahb_read32,
        .write32 = ath11k_ahb_write32,
+       .read = NULL,
        .irq_enable = ath11k_ahb_ext_irq_enable,
        .irq_disable = ath11k_ahb_ext_irq_disable,
        .map_service_to_pipe = ath11k_ahb_map_service_to_pipe,
@@ -703,6 +704,7 @@ static const struct ath11k_hif_ops ath11k_ahb_hif_ops_wcn6750 = {
        .stop = ath11k_pcic_stop,
        .read32 = ath11k_pcic_read32,
        .write32 = ath11k_pcic_write32,
+       .read = NULL,
        .irq_enable = ath11k_pcic_ext_irq_enable,
        .irq_disable = ath11k_pcic_ext_irq_disable,
        .get_msi_address =  ath11k_pcic_get_msi_address,
index 7f2d625..7f6cf5f 100644 (file)
@@ -108,6 +108,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
                .fixed_fw_mem = false,
                .support_off_channel_tx = false,
                .supports_multi_bssid = false,
+
+               .sram_dump = {},
        },
        {
                .hw_rev = ATH11K_HW_IPQ6018_HW10,
@@ -181,6 +183,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
                .fixed_fw_mem = false,
                .support_off_channel_tx = false,
                .supports_multi_bssid = false,
+
+               .sram_dump = {},
        },
        {
                .name = "qca6390 hw2.0",
@@ -253,6 +257,11 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
                .fixed_fw_mem = false,
                .support_off_channel_tx = true,
                .supports_multi_bssid = true,
+
+               .sram_dump = {
+                       .start = 0x01400000,
+                       .end = 0x0171ffff,
+               },
        },
        {
                .name = "qcn9074 hw1.0",
@@ -325,6 +334,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
                .fixed_fw_mem = false,
                .support_off_channel_tx = false,
                .supports_multi_bssid = false,
+
+               .sram_dump = {},
        },
        {
                .name = "wcn6855 hw2.0",
@@ -397,6 +408,11 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
                .fixed_fw_mem = false,
                .support_off_channel_tx = true,
                .supports_multi_bssid = true,
+
+               .sram_dump = {
+                       .start = 0x01400000,
+                       .end = 0x0177ffff,
+               },
        },
        {
                .name = "wcn6855 hw2.1",
@@ -468,6 +484,11 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
                .fixed_fw_mem = false,
                .support_off_channel_tx = true,
                .supports_multi_bssid = true,
+
+               .sram_dump = {
+                       .start = 0x01400000,
+                       .end = 0x0177ffff,
+               },
        },
        {
                .name = "wcn6750 hw1.0",
@@ -539,6 +560,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
                .fixed_fw_mem = true,
                .support_off_channel_tx = true,
                .supports_multi_bssid = true,
+
+               .sram_dump = {},
        },
 };
 
index 8dcc5f7..47113ab 100644 (file)
@@ -14,6 +14,7 @@
 #include "dp_tx.h"
 #include "debugfs_htt_stats.h"
 #include "peer.h"
+#include "hif.h"
 
 static const char *htt_bp_umac_ring[HTT_SW_UMAC_RING_IDX_MAX] = {
        "REO2SW1_RING",
@@ -982,6 +983,63 @@ static const struct file_operations fops_fw_dbglog = {
        .llseek = default_llseek,
 };
 
+static int ath11k_open_sram_dump(struct inode *inode, struct file *file)
+{
+       struct ath11k_base *ab = inode->i_private;
+       u8 *buf;
+       u32 start, end;
+       int ret;
+
+       start = ab->hw_params.sram_dump.start;
+       end = ab->hw_params.sram_dump.end;
+
+       buf = vmalloc(end - start + 1);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = ath11k_hif_read(ab, buf, start, end);
+       if (ret) {
+               ath11k_warn(ab, "failed to dump sram: %d\n", ret);
+               vfree(buf);
+               return ret;
+       }
+
+       file->private_data = buf;
+       return 0;
+}
+
+static ssize_t ath11k_read_sram_dump(struct file *file,
+                                    char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       struct ath11k_base *ab = file->f_inode->i_private;
+       const char *buf = file->private_data;
+       int len;
+       u32 start, end;
+
+       start = ab->hw_params.sram_dump.start;
+       end = ab->hw_params.sram_dump.end;
+       len = end - start + 1;
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static int ath11k_release_sram_dump(struct inode *inode, struct file *file)
+{
+       vfree(file->private_data);
+       file->private_data = NULL;
+
+       return 0;
+}
+
+static const struct file_operations fops_sram_dump = {
+       .open = ath11k_open_sram_dump,
+       .read = ath11k_read_sram_dump,
+       .release = ath11k_release_sram_dump,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
 int ath11k_debugfs_pdev_create(struct ath11k_base *ab)
 {
        if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags))
@@ -997,6 +1055,10 @@ int ath11k_debugfs_pdev_create(struct ath11k_base *ab)
        debugfs_create_file("soc_dp_stats", 0600, ab->debugfs_soc, ab,
                            &fops_soc_dp_stats);
 
+       if (ab->hw_params.sram_dump.start != 0)
+               debugfs_create_file("sram", 0400, ab->debugfs_soc, ab,
+                                   &fops_sram_dump);
+
        return 0;
 }
 
index e9366f7..659b80d 100644 (file)
@@ -11,6 +11,7 @@
 struct ath11k_hif_ops {
        u32 (*read32)(struct ath11k_base *sc, u32 address);
        void (*write32)(struct ath11k_base *sc, u32 address, u32 data);
+       int (*read)(struct ath11k_base *ab, void *buf, u32 start, u32 end);
        void (*irq_enable)(struct ath11k_base *sc);
        void (*irq_disable)(struct ath11k_base *sc);
        int (*start)(struct ath11k_base *sc);
@@ -99,6 +100,15 @@ static inline void ath11k_hif_write32(struct ath11k_base *sc, u32 address, u32 d
        sc->hif.ops->write32(sc, address, data);
 }
 
+static inline int ath11k_hif_read(struct ath11k_base *ab, void *buf,
+                                 u32 start, u32 end)
+{
+       if (!ab->hif.ops->read)
+               return -EOPNOTSUPP;
+
+       return ab->hif.ops->read(ab, buf, start, end);
+}
+
 static inline int ath11k_hif_map_service_to_pipe(struct ath11k_base *sc, u16 service_id,
                                                 u8 *ul_pipe, u8 *dl_pipe)
 {
@@ -134,4 +144,5 @@ static inline void ath11k_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id,
        else
                *msi_data_idx = ce_id;
 }
+
 #endif /* _HIF_H_ */
index 26d0b7f..0921bb6 100644 (file)
@@ -202,6 +202,11 @@ struct ath11k_hw_params {
        bool fixed_fw_mem;
        bool support_off_channel_tx;
        bool supports_multi_bssid;
+
+       struct {
+               u32 start;
+               u32 end;
+       } sram_dump;
 };
 
 struct ath11k_hw_ops {
@@ -399,4 +404,5 @@ static inline const char *ath11k_bd_ie_type_str(enum ath11k_bd_ie_type type)
 }
 
 extern const struct cfg80211_sar_capa ath11k_hw_sar_capa_wcn6855;
+
 #endif
index 5bd34a6..99cf335 100644 (file)
@@ -685,6 +685,7 @@ static const struct ath11k_hif_ops ath11k_pci_hif_ops = {
        .stop = ath11k_pcic_stop,
        .read32 = ath11k_pcic_read32,
        .write32 = ath11k_pcic_write32,
+       .read = ath11k_pcic_read,
        .power_down = ath11k_pci_power_down,
        .power_up = ath11k_pci_power_up,
        .suspend = ath11k_pci_hif_suspend,
index 4d0c039..926b0cb 100644 (file)
@@ -203,6 +203,37 @@ u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset)
 }
 EXPORT_SYMBOL(ath11k_pcic_read32);
 
+int ath11k_pcic_read(struct ath11k_base *ab, void *buf, u32 start, u32 end)
+{
+       int ret = 0;
+       bool wakeup_required;
+       u32 *data = buf;
+       u32 i;
+
+       /* for offset beyond BAR + 4K - 32, may
+        * need to wakeup the device to access.
+        */
+       wakeup_required = test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&
+                         end >= ATH11K_PCI_ACCESS_ALWAYS_OFF;
+       if (wakeup_required && ab->pci.ops->wakeup) {
+               ret = ab->pci.ops->wakeup(ab);
+               if (ret) {
+                       ath11k_warn(ab, "failed to wakeup for read from 0x%x: %d\n",
+                                   start, ret);
+                       return ret;
+               }
+       }
+
+       for (i = start; i < end + 1; i += 4)
+               *data++ = __ath11k_pcic_read32(ab, i);
+
+       if (wakeup_required && ab->pci.ops->release)
+               ab->pci.ops->release(ab);
+
+       return 0;
+}
+EXPORT_SYMBOL(ath11k_pcic_read);
+
 void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo,
                                 u32 *msi_addr_hi)
 {
index 0afbb34..f350661 100644 (file)
@@ -45,4 +45,6 @@ void ath11k_pcic_ce_irq_disable_sync(struct ath11k_base *ab);
 int ath11k_pcic_init_msi_config(struct ath11k_base *ab);
 int ath11k_pcic_register_pci_ops(struct ath11k_base *ab,
                                 const struct ath11k_pci_ops *pci_ops);
+int ath11k_pcic_read(struct ath11k_base *ab, void *buf, u32 start, u32 end);
+
 #endif