From 876eb84882a8b2fc4036e735a2de52141aebb6a5 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 7 Sep 2022 11:31:13 +0300 Subject: [PATCH] wifi: ath11k: implement SRAM dump debugfs interface 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 Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220802075533.1744-3-quic_bqiang@quicinc.com --- drivers/net/wireless/ath/ath11k/ahb.c | 2 + drivers/net/wireless/ath/ath11k/core.c | 23 ++++++++++++ drivers/net/wireless/ath/ath11k/debugfs.c | 62 +++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath11k/hif.h | 11 ++++++ drivers/net/wireless/ath/ath11k/hw.h | 6 +++ drivers/net/wireless/ath/ath11k/pci.c | 1 + drivers/net/wireless/ath/ath11k/pcic.c | 31 ++++++++++++++++ drivers/net/wireless/ath/ath11k/pcic.h | 2 + 8 files changed, 138 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index 29c40c6..31e9cf3 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -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, diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 7f2d625..7f6cf5f 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -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 = {}, }, }; diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c index 8dcc5f7..47113ab 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.c +++ b/drivers/net/wireless/ath/ath11k/debugfs.c @@ -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; } diff --git a/drivers/net/wireless/ath/ath11k/hif.h b/drivers/net/wireless/ath/ath11k/hif.h index e9366f7..659b80d 100644 --- a/drivers/net/wireless/ath/ath11k/hif.h +++ b/drivers/net/wireless/ath/ath11k/hif.h @@ -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_ */ diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h index 26d0b7f..0921bb6 100644 --- a/drivers/net/wireless/ath/ath11k/hw.h +++ b/drivers/net/wireless/ath/ath11k/hw.h @@ -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 diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index 5bd34a6..99cf335 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -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, diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c index 4d0c039..926b0cb 100644 --- a/drivers/net/wireless/ath/ath11k/pcic.c +++ b/drivers/net/wireless/ath/ath11k/pcic.c @@ -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) { diff --git a/drivers/net/wireless/ath/ath11k/pcic.h b/drivers/net/wireless/ath/ath11k/pcic.h index 0afbb34..f350661 100644 --- a/drivers/net/wireless/ath/ath11k/pcic.h +++ b/drivers/net/wireless/ath/ath11k/pcic.h @@ -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 -- 2.7.4