mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-12-21 19:31:55 +00:00
309 lines
10 KiB
Diff
309 lines
10 KiB
Diff
From b3213496ab8688c6ef6f92be1dce0d1ed32ca9e3 Mon Sep 17 00:00:00 2001
|
|
From: Ramya Gnanasekar <rgnanase@codeaurora.org>
|
|
Date: Thu, 9 Jul 2020 10:48:15 +0530
|
|
Subject: [PATCH] ath11k: add support to collect q6mem dump
|
|
|
|
q6mem dump is not collected when fw crashes due to system restart.
|
|
Implemented a knob in debugfs to enable/disable fw recovery from
|
|
qsdk to collect q6mem dump .
|
|
|
|
root@OpenWrt:~# echo 1 > /sys/kernel/debug/ath11k/ipq6018_2/set_fw_recovery
|
|
root@OpenWrt:~# cat /sys/kernel/debug/ath11k/ipq6018_2/set_fw_recovery
|
|
1
|
|
|
|
Signed-off-by: Ramya Gnanasekar <rgnanase@codeaurora.org>
|
|
---
|
|
drivers/net/wireless/ath/ath11k/core.c | 3 ++-
|
|
drivers/net/wireless/ath/ath11k/core.h | 1 +
|
|
drivers/net/wireless/ath/ath11k/debugfs.c | 46 +++++++++++++++++++++++++++++++++
|
|
3 files changed, 49 insertions(+), 1 deletion(-)
|
|
|
|
--- a/drivers/net/wireless/ath/ath11k/core.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/core.c
|
|
@@ -860,7 +860,8 @@ int ath11k_core_ssr_notifier_cb(struct n
|
|
/* TODO Add more driver stats */
|
|
|
|
/* Crash the system once all the stats are dumped */
|
|
- BUG_ON(1);
|
|
+ if(!qmi->ab->fw_recovery_support)
|
|
+ BUG_ON(1);
|
|
|
|
return 0;
|
|
}
|
|
@@ -943,6 +944,13 @@ static int ath11k_core_reconfigure_on_cr
|
|
int ret;
|
|
|
|
mutex_lock(&ab->core_lock);
|
|
+#ifdef CONFIG_QCOM_QMI_HELPERS
|
|
+ /* Unregister the ssr notifier as we are not interested
|
|
+ * 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_thermal_unregister(ab);
|
|
ath11k_hif_irq_disable(ab);
|
|
ath11k_dp_pdev_free(ab);
|
|
--- a/drivers/net/wireless/ath/ath11k/core.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/core.h
|
|
@@ -845,6 +845,8 @@ struct ath11k_base {
|
|
|
|
struct completion htc_suspend;
|
|
|
|
+ bool fw_recovery_support;
|
|
+
|
|
/* must be last */
|
|
u8 drv_priv[0] __aligned(sizeof(void *));
|
|
};
|
|
@@ -972,6 +974,14 @@ struct ath11k_fw_stats_bcn {
|
|
u32 tx_bcn_outage_cnt;
|
|
};
|
|
|
|
+enum ath11k_fw_recovery_option {
|
|
+ ATH11K_FW_RECOVERY_DISABLE = 0,
|
|
+ ATH11K_FW_RECOVERY_ENABLE_AUTO, /* Automatically recover after FW assert */
|
|
+ /* Enable only recovery. Send MPD SSR WMI */
|
|
+ /* command to unlink UserPD assert from RootPD */
|
|
+ ATH11K_FW_RECOVERY_ENABLE_SSR_ONLY,
|
|
+};
|
|
+
|
|
extern const struct ce_pipe_config ath11k_target_ce_config_wlan_ipq8074[];
|
|
extern const struct service_to_pipe ath11k_target_service_to_ce_map_wlan_ipq8074[];
|
|
extern const struct service_to_pipe ath11k_target_service_to_ce_map_wlan_ipq6018[];
|
|
@@ -999,6 +1009,7 @@ void ath11k_core_halt(struct ath11k *ar)
|
|
int ath11k_core_resume(struct ath11k_base *ab);
|
|
int ath11k_core_suspend(struct ath11k_base *ab);
|
|
|
|
+void ath11k_core_dump_bp_stats(struct ath11k_base *ab);
|
|
void ath11k_coredump_qdss_dump(struct ath11k_base *ab,
|
|
struct ath11k_qmi_event_qdss_trace_save_data *event_data);
|
|
const struct firmware *ath11k_core_firmware_request(struct ath11k_base *ab,
|
|
--- a/drivers/net/wireless/ath/ath11k/debugfs.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/debugfs.c
|
|
@@ -1110,6 +1110,73 @@ static const struct file_operations fops
|
|
.llseek = default_llseek,
|
|
};
|
|
|
|
+static ssize_t ath11k_debug_write_fw_recovery(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k_base *ab = file->private_data;
|
|
+ struct ath11k *ar;
|
|
+ struct ath11k_pdev *pdev;
|
|
+ struct device *dev = ab->dev;
|
|
+ bool multi_pd_arch = false;
|
|
+ unsigned int value;
|
|
+ int ret, i;
|
|
+
|
|
+ if (kstrtouint_from_user(user_buf, count, 0, &value))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (value < ATH11K_FW_RECOVERY_DISABLE ||
|
|
+ value > ATH11K_FW_RECOVERY_ENABLE_SSR_ONLY) {
|
|
+ ath11k_warn(ab, "Please enter: 0 = Disable, 1 = Enable (auto recover),"
|
|
+ "2 = Enable SSR only");
|
|
+ ret = -EINVAL;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < ab->num_radios; i++) {
|
|
+ pdev = &ab->pdevs[i];
|
|
+ ar = pdev->ar;
|
|
+ if (ar && ar->state == ATH11K_STATE_ON)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ multi_pd_arch = of_property_read_bool(dev->of_node, "qcom,multipd_arch");
|
|
+ if (multi_pd_arch) {
|
|
+ if (value == ATH11K_FW_RECOVERY_DISABLE ||
|
|
+ value == ATH11K_FW_RECOVERY_ENABLE_SSR_ONLY) {
|
|
+ ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_MPD_USERPD_SSR,
|
|
+ 0, ar->pdev->pdev_id);
|
|
+ } else if (value == ATH11K_FW_RECOVERY_ENABLE_AUTO)
|
|
+ ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_MPD_USERPD_SSR,
|
|
+ 1, ar->pdev->pdev_id);
|
|
+ }
|
|
+ ab->fw_recovery_support = value ? true : false;
|
|
+
|
|
+ ret = count;
|
|
+
|
|
+exit:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static ssize_t ath11k_debug_read_fw_recovery(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k_base *ab = file->private_data;
|
|
+ char buf[32];
|
|
+ size_t len;
|
|
+
|
|
+ len = scnprintf(buf, sizeof(buf), "%u\n", ab->fw_recovery_support);
|
|
+
|
|
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_fw_recovery = {
|
|
+ .read = ath11k_debug_read_fw_recovery,
|
|
+ .write = ath11k_debug_write_fw_recovery,
|
|
+ .open = simple_open,
|
|
+};
|
|
+
|
|
int ath11k_debugfs_pdev_create(struct ath11k_base *ab)
|
|
{
|
|
if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags))
|
|
@@ -1124,6 +1191,10 @@ int ath11k_debugfs_pdev_create(struct at
|
|
debugfs_create_file("soc_dp_stats", 0600, ab->debugfs_soc, ab,
|
|
&fops_soc_dp_stats);
|
|
|
|
+ debugfs_create_file("set_fw_recovery", 0600, ab->debugfs_soc, ab,
|
|
+ &fops_fw_recovery);
|
|
+
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
--- a/drivers/net/wireless/ath/ath11k/pci.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/pci.c
|
|
@@ -1206,6 +1206,8 @@ static int ath11k_pci_probe(struct pci_d
|
|
ab_pci->pdev = pdev;
|
|
ab->hif.ops = &ath11k_pci_hif_ops;
|
|
pci_set_drvdata(pdev, ab);
|
|
+ ab->fw_recovery_support = false;
|
|
+
|
|
spin_lock_init(&ab_pci->window_lock);
|
|
|
|
/* IPQ8074 reserves memory for FW, ath11k does not need to
|
|
--- a/drivers/net/wireless/ath/ath11k/coredump.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/coredump.c
|
|
@@ -163,13 +163,16 @@ void ath11k_coredump_download_rddm(struc
|
|
fw_img = mhi_ctrl->fbc_image;
|
|
|
|
for (i = 0; i < ab->qmi.mem_seg_count; i++) {
|
|
- if (ab->qmi.target_mem[i].type == HOST_DDR_REGION_TYPE)
|
|
+ if (ab->qmi.target_mem[i].type == HOST_DDR_REGION_TYPE ||
|
|
+ ab->qmi.target_mem[i].type == CALDB_MEM_REGION_TYPE ||
|
|
+ ab->qmi.target_mem[i].type == M3_DUMP_REGION_TYPE)
|
|
rem_seg_cnt++;
|
|
}
|
|
|
|
num_seg = fw_img->entries + rddm_img->entries + rem_seg_cnt;
|
|
if (ab->is_qdss_tracing)
|
|
num_seg += qdss_seg_cnt;
|
|
+
|
|
len = num_seg * sizeof(*segment);
|
|
|
|
seg_info = segment = (struct ath11k_dump_segment *)vzalloc(len);
|
|
@@ -196,31 +199,46 @@ void ath11k_coredump_download_rddm(struc
|
|
ath11k_info(ab, "seg vaddr is %px len is 0x%x type %d\n",
|
|
seg_info->vaddr, seg_info->len, seg_info->type);
|
|
seg_info->type = ATH11K_FW_CRASH_RDDM_DATA;
|
|
+ ath11k_info(ab, "seg vaddr is %px len is 0x%x type %d\n",
|
|
+ seg_info->vaddr, seg_info->len, seg_info->type);
|
|
seg_info++;
|
|
}
|
|
|
|
for (i = 0; i < ab->qmi.mem_seg_count; i++) {
|
|
- if (ab->qmi.target_mem[i].type != HOST_DDR_REGION_TYPE)
|
|
- continue;
|
|
- seg_info->len = ab->qmi.target_mem[i].size;
|
|
- seg_info->addr = ab->qmi.target_mem[i].paddr;
|
|
- seg_info->vaddr = ab->qmi.target_mem[i].vaddr;
|
|
- ath11k_info(ab, "seg vaddr is %px len is 0x%x type %d\n",
|
|
- seg_info->vaddr, seg_info->len, seg_info->type);
|
|
- seg_info->type = ATH11K_FW_REMOTE_MEM_DATA;
|
|
- seg_info++;
|
|
+ if (ab->qmi.target_mem[i].type == HOST_DDR_REGION_TYPE ||
|
|
+ ab->qmi.target_mem[i].type == M3_DUMP_REGION_TYPE) {
|
|
+ seg_info->len = ab->qmi.target_mem[i].size;
|
|
+ seg_info->addr = ab->qmi.target_mem[i].paddr;
|
|
+ seg_info->vaddr = ab->qmi.target_mem[i].vaddr;
|
|
+ seg_info->type = ATH11K_FW_REMOTE_MEM_DATA;
|
|
+ ath11k_info(ab, "seg vaddr is %px len is 0x%x type %d\n",
|
|
+ seg_info->vaddr, seg_info->len, seg_info->type);
|
|
+ seg_info++;
|
|
+ }
|
|
}
|
|
|
|
if (ab->is_qdss_tracing) {
|
|
seg_info->len = ab->qmi.qdss_mem[0].size;
|
|
seg_info->addr = ab->qmi.qdss_mem[0].paddr;
|
|
seg_info->vaddr = ab->qmi.qdss_mem[0].vaddr;
|
|
- ath11k_dbg(ab, ATH11K_DBG_QMI, "seg vaddr is %px len is 0x%x type %d\n",
|
|
- seg_info->vaddr, seg_info->len, seg_info->type);
|
|
- seg_info->type = ATH11K_FW_QDSS_DATA;
|
|
+ seg_info->type = ATH11K_FW_REMOTE_MEM_DATA;
|
|
+ ath11k_info(ab, "seg vaddr is %px len is 0x%x type %d\n",
|
|
+ seg_info->vaddr, seg_info->len, seg_info->type);
|
|
seg_info++;
|
|
}
|
|
|
|
+ for (i = 0; i < ab->qmi.mem_seg_count; i++) {
|
|
+ if (ab->qmi.target_mem[i].type == CALDB_MEM_REGION_TYPE) {
|
|
+ seg_info->len = ab->qmi.target_mem[i].size;
|
|
+ seg_info->addr = ab->qmi.target_mem[i].paddr;
|
|
+ seg_info->vaddr = ab->qmi.target_mem[i].vaddr;
|
|
+ seg_info->type = ATH11K_FW_REMOTE_MEM_DATA;
|
|
+ ath11k_info(ab, "seg vaddr is %px len is 0x%x type %d\n",
|
|
+ seg_info->vaddr, seg_info->len, seg_info->type);
|
|
+ seg_info++;
|
|
+ }
|
|
+ }
|
|
+
|
|
/* Crash the system once all the stats are dumped */
|
|
if(!ab->fw_recovery_support) {
|
|
ath11k_core_dump_bp_stats(ab);
|
|
--- a/drivers/net/wireless/ath/ath11k/qmi.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/qmi.c
|
|
@@ -2581,6 +2581,10 @@ static int ath11k_qmi_assign_target_mem_
|
|
ab->qmi.target_mem[idx].paddr = ab->hw_params.m3_addr;
|
|
else
|
|
ab->qmi.target_mem[idx].paddr = (phys_addr_t)addr;
|
|
+
|
|
+ ab->qmi.target_mem[idx].vaddr =
|
|
+ ioremap(ab->qmi.target_mem[idx].paddr,
|
|
+ ab->qmi.target_mem[i].size);
|
|
idx++;
|
|
break;
|
|
default:
|
|
--- a/drivers/net/wireless/ath/ath11k/wmi.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
|
|
@@ -949,6 +949,7 @@ enum wmi_tlv_pdev_param {
|
|
WMI_PDEV_PARAM_SET_CMD_OBSS_PD_THRESHOLD = 0xbc,
|
|
WMI_PDEV_PARAM_SET_CMD_OBSS_PD_PER_AC = 0xbe,
|
|
WMI_PDEV_PARAM_ENABLE_SR_PROHIBIT = 0xc6,
|
|
+ WMI_PDEV_PARAM_MPD_USERPD_SSR = 0xce,
|
|
};
|
|
|
|
enum wmi_tlv_vdev_param {
|
|
--- a/drivers/net/wireless/ath/ath11k/mac.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/mac.c
|
|
@@ -5146,6 +5146,7 @@ static int ath11k_mac_op_start(struct ie
|
|
struct ath11k *ar = hw->priv;
|
|
struct ath11k_base *ab = ar->ab;
|
|
struct ath11k_pdev *pdev = ar->pdev;
|
|
+ struct device *dev = ab->dev;
|
|
int ret;
|
|
|
|
ath11k_mac_drain_tx(ar);
|
|
@@ -5212,6 +5213,17 @@ static int ath11k_mac_op_start(struct ie
|
|
goto err;
|
|
}
|
|
|
|
+ if (ab->fw_recovery_support &&
|
|
+ of_property_read_bool(dev->of_node, "qcom,multipd_arch"))
|
|
+ {
|
|
+ ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_MPD_USERPD_SSR,
|
|
+ 1, pdev->pdev_id);
|
|
+ if (ret) {
|
|
+ ath11k_err(ab, "failed to enable firmware SSR"
|
|
+ "recovery:%d\n", ret);
|
|
+ }
|
|
+ }
|
|
+
|
|
__ath11k_set_antenna(ar, ar->cfg_tx_chainmask, ar->cfg_rx_chainmask);
|
|
|
|
/* TODO: Do we need to enable ANI? */
|