--- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -30,6 +30,8 @@ module_param_named(frame_mode, ath11k_fr MODULE_PARM_DESC(frame_mode, "Datapath frame mode (0: raw, 1: native wifi (default), 2: ethernet)"); +struct ath11k_base *ath11k_soc; + static const struct ath11k_hw_params ath11k_hw_params[] = { { .hw_rev = ATH11K_HW_IPQ8074, @@ -803,6 +805,62 @@ err_firmware_stop: return ret; } +void ath11k_core_dump_bp_stats(struct ath11k_base *ab) +{ + int len = 0; + const int size = 4096; + char *buf; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return; + + len = ath11k_debugfs_dump_soc_ring_bp_stats(ab, buf, size - 1); + + buf[len] = '\0'; + + ath11k_info(ab, "ATH11K Driver Stats\n%s\n", buf); + + kfree(buf); +} + +#ifdef CONFIG_QCOM_QMI_HELPERS + +/* Print the driver stats and crash the system on receving this notification */ +int ath11k_core_ssr_notifier_cb(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct ath11k_qmi *qmi = container_of(nb, struct ath11k_qmi, ssr_nb); + struct ath11k_base *ab = qmi->ab; + struct device *dev = ab->dev; + bool multi_pd_arch = false; + const char *name; + + if (test_bit(ATH11K_FLAG_FW_RESTART_FOR_HOST, &qmi->ab->dev_flags)) { + return 0; + } + + /* Print the stats only if notification is received for expected PD*/ + multi_pd_arch = of_property_read_bool(dev->of_node, "qcom,multipd_arch"); + if (multi_pd_arch) { + if (of_property_read_string(dev->of_node, "qcom,userpd-subsys-name", &name)) + return 0; + + if (strcmp((char*)data, name) != 0) + return 0; + } + + ath11k_core_dump_bp_stats(qmi->ab); + ath11k_hal_dump_srng_stats(qmi->ab); + /* TODO Add more driver stats */ + + /* Crash the system once all the stats are dumped */ + BUG_ON(1); + + return 0; +} +#endif + int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab) { int ret; @@ -848,6 +906,16 @@ int ath11k_core_qmi_firmware_ready(struc ath11k_err(ab, "failed to create pdev core: %d\n", ret); goto err_core_stop; } + +#ifdef CONFIG_QCOM_QMI_HELPERS + /* Register a notifier after mac registration + * to be called on fw crash + */ + if (ab->hif.bus == ATH11K_BUS_AHB) { + ab->qmi.ssr_nb.notifier_call = ath11k_core_ssr_notifier_cb; + qcom_register_ssr_notifier(&ab->qmi.ssr_nb); + } +#endif ath11k_hif_irq_enable(ab); mutex_unlock(&ab->core_lock); @@ -1061,6 +1129,13 @@ void ath11k_core_deinit(struct ath11k_ba { mutex_lock(&ab->core_lock); +#ifdef CONFIG_QCOM_QMI_HELPERS + /* Unregister the ssr notifier as we are not intersted + * in receving these notifications after mac is unregistered. + */ + if (ab->hif.bus == ATH11K_BUS_AHB) + qcom_unregister_ssr_notifier(&ab->qmi.ssr_nb); +#endif ath11k_core_pdev_destroy(ab); ath11k_core_stop(ab); @@ -1109,6 +1184,7 @@ struct ath11k_base *ath11k_core_alloc(st ab->dev = dev; ab->bus_params = *bus_params; ab->hif.bus = bus; + ath11k_soc = ab; return ab; --- a/drivers/net/wireless/ath/ath11k/debugfs.c +++ b/drivers/net/wireless/ath/ath11k/debugfs.c @@ -706,8 +706,8 @@ static int ath11k_fill_bp_stats(struct a return len; } -static ssize_t ath11k_debugfs_dump_soc_ring_bp_stats(struct ath11k_base *ab, - char *buf, int size) +ssize_t ath11k_debugfs_dump_soc_ring_bp_stats(struct ath11k_base *ab, + char *buf, int size) { struct ath11k_bp_stats *bp_stats; bool stats_rxd = false; --- a/drivers/net/wireless/ath/ath11k/debugfs.h +++ b/drivers/net/wireless/ath/ath11k/debugfs.h @@ -113,6 +113,8 @@ void ath11k_debugfs_unregister(struct at void ath11k_debugfs_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb); void ath11k_debugfs_fw_stats_init(struct ath11k *ar); +ssize_t ath11k_debugfs_dump_soc_ring_bp_stats(struct ath11k_base *ab, + char *buf, int size); static inline bool ath11k_debugfs_is_pktlog_lite_mode_enabled(struct ath11k *ar) { @@ -146,6 +148,12 @@ static inline int ath11k_debugfs_rx_filt } #else +ssize_t ath11k_debugfs_dump_soc_ring_bp_stats(struct ath11k_base *ab, + char *buf, int size) +{ + return 0; +} + static inline int ath11k_debugfs_soc_create(struct ath11k_base *ab) { return 0; --- a/drivers/net/wireless/ath/ath11k/qmi.h +++ b/drivers/net/wireless/ath/ath11k/qmi.h @@ -7,7 +7,9 @@ #define ATH11K_QMI_H #include +#include #include +#include #define ATH11K_HOST_VERSION_STRING "WIN" #define ATH11K_QMI_WLANFW_TIMEOUT_MS 5000 @@ -123,6 +125,7 @@ struct ath11k_qmi { struct workqueue_struct *event_wq; struct list_head event_list; spinlock_t event_lock; /* spinlock for qmi event list */ + struct notifier_block ssr_nb; struct ath11k_qmi_ce_cfg ce_cfg; struct target_mem_chunk target_mem[ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01]; u32 mem_seg_count;