wlan-ap-Telecominfraproject/feeds/ipq95xx/mac80211/patches/qca/528-ath12K-Enable-RAM-dump-support.patch
John Crispin 144c5d00f4 ipq95xx/mac80211: update to ATH12.3-CS
Signed-off-by: John Crispin <john@phrozen.org>
2024-02-28 18:56:21 +01:00

433 lines
12 KiB
Diff

From 30f9030503fa82b63f49909f98ac0d9f91b98096 Mon Sep 17 00:00:00 2001
From: Aishwarya R <quic_aisr@quicinc.com>
Date: Mon, 14 Mar 2022 23:54:14 +0530
Subject: [PATCH] ath12K: Enable RAM dump support
This commit brings in inital coredump support
In order to get full dump, set_fw_recovery in
debugfs flag should be set to 1.
Based on set_fw_recovery the functionality decides
whether firmware / full core needs to be dumped.
Signed-off-by: Aishwarya R <quic_aisr@quicinc.com>
Signed-off-by: Balamurugan Selvarajan <quic_bselvara@quicinc.com>
---
drivers/net/wireless/ath/ath12k/Makefile | 1 +
drivers/net/wireless/ath/ath12k/core.c | 4 +
drivers/net/wireless/ath/ath12k/core.h | 13 +-
drivers/net/wireless/ath/ath12k/coredump.c | 222 +++++++++++++++++++++
drivers/net/wireless/ath/ath12k/coredump.h | 87 ++++++++
drivers/net/wireless/ath/ath12k/mhi.c | 6 +
drivers/net/wireless/ath/ath12k/mhi.h | 2 +
7 files changed, 328 insertions(+), 7 deletions(-)
create mode 100644 drivers/net/wireless/ath/ath12k/coredump.c
create mode 100644 drivers/net/wireless/ath/ath12k/coredump.h
--- a/drivers/net/wireless/ath/ath12k/Makefile
+++ b/drivers/net/wireless/ath/ath12k/Makefile
@@ -27,6 +27,7 @@ ath12k-$(CPTCFG_NL80211_TESTMODE) += tes
ath12k-$(CPTCFG_ATH12K_TRACING) += trace.o
ath12k-$(CONFIG_THERMAL) += thermal.o
ath12k-$(CPTCFG_ATH12K_SPECTRAL) += spectral.o
+ath12k-$(CPTCFG_WANT_DEV_COREDUMP) += coredump.o
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -10,6 +10,7 @@
#include <linux/firmware.h>
#include <linux/of.h>
#include "core.h"
+#include "coredump.h"
#include "dp_tx.h"
#include "dp_rx.h"
#include "debug.h"
@@ -925,6 +926,9 @@ static void ath12k_core_reset(struct wor
ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset starting\n");
ab->is_reset = true;
+ /* prepare coredump */
+ ath12k_coredump_download_rddm(ab);
+
atomic_set(&ab->recovery_count, 0);
ath12k_core_pre_reconfigure_recovery(ab);
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -503,6 +503,14 @@ struct ath12k_per_peer_tx_stats {
bool is_ampdu;
};
+enum ath12k_fw_recovery_option {
+ ATH12K_FW_RECOVERY_DISABLE = 0,
+ ATH12K_FW_RECOVERY_ENABLE_AUTO, /* Automatically recover after FW assert */
+ /* Enable only recovery. Send MPD SSR WMI */
+ /* command to unlink UserPD assert from RootPD */
+ ATH12K_FW_RECOVERY_ENABLE_SSR_ONLY,
+};
+
#define ATH12K_FLUSH_TIMEOUT (5 * HZ)
#define ATH12K_VDEV_DELETE_TIMEOUT_HZ (5 * HZ)
@@ -833,6 +841,7 @@ struct ath12k_base {
struct timer_list mon_reap_timer;
struct completion htc_suspend;
+ bool fw_recovery_support;
u64 fw_soc_drop_count;
--- /dev/null
+++ b/drivers/net/wireless/ath/ath12k/coredump.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#include <linux/devcoredump.h>
+#include <linux/platform_device.h>
+#include <linux/dma-direction.h>
+#include <linux/pci.h>
+#include <linux/mm.h>
+#include <linux/uuid.h>
+#include <linux/time.h>
+#include "core.h"
+#include "coredump.h"
+#include "pci.h"
+#include "mhi.h"
+#include "debug.h"
+
+struct ath12k_coredump_segment_info ath12k_coredump_seg_info;
+EXPORT_SYMBOL(ath12k_coredump_seg_info);
+
+static void *ath12k_coredump_find_segment(loff_t user_offset,
+ struct ath12k_dump_segment *segment,
+ int num_seg, size_t *data_left)
+{
+ int i;
+
+ for (i = 0; i < num_seg; i++, segment++) {
+ if (user_offset < segment->len) {
+ *data_left = user_offset;
+ return segment;
+ }
+ user_offset -= segment->len;
+ }
+
+ *data_left = 0;
+ return NULL;
+}
+
+static ssize_t ath12k_coredump_read_q6dump(char *buffer, loff_t offset, size_t count,
+ void *data, size_t header_size)
+{
+ struct ath12k_coredump_state *dump_state = data;
+ struct ath12k_dump_segment *segments = dump_state->segments;
+ struct ath12k_dump_segment *seg;
+ void *elfcore = dump_state->header;
+ size_t data_left, copy_size, bytes_left = count;
+ void __iomem *addr;
+
+ /* Copy the header first */
+ if (offset < header_size) {
+ copy_size = header_size - offset;
+ copy_size = min(copy_size, bytes_left);
+
+ memcpy(buffer, elfcore + offset, copy_size);
+ offset += copy_size;
+ bytes_left -= copy_size;
+ buffer += copy_size;
+
+ return copy_size;
+ }
+
+ while (bytes_left) {
+ seg = ath12k_coredump_find_segment(offset - header_size, segments,
+ dump_state->num_seg, &data_left);
+ /* End of segments check */
+ if (!seg) {
+ pr_info("Ramdump complete %lld bytes read\n", offset);
+ return 0;
+ }
+
+ if (data_left)
+ copy_size = min_t(size_t, bytes_left, data_left);
+ else
+ copy_size = bytes_left;
+
+ addr = (void __iomem *)seg->vaddr;
+ addr += data_left;
+ memcpy_fromio(buffer, addr, copy_size);
+
+ offset += copy_size;
+ buffer += copy_size;
+ bytes_left -= copy_size;
+ }
+
+ return count - bytes_left;
+}
+
+static void ath12k_coredump_free_q6dump(void *data)
+{
+ struct ath12k_coredump_state *dump_state = data;
+
+ complete(&dump_state->dump_done);
+}
+
+void ath12k_coredump_build_inline(struct ath12k_base *ab,
+ struct ath12k_dump_segment *segments, int num_seg)
+{
+ struct ath12k_coredump_state dump_state;
+ struct timespec64 timestamp;
+ struct ath12k_dump_file_data *file_data;
+ size_t header_size;
+ struct ath12k_pci *ar_pci = (struct ath12k_pci *)ab->drv_priv;
+ struct device dev;
+ u8 *buf;
+
+ header_size = sizeof(*file_data);
+ header_size += num_seg * sizeof(*segments);
+ header_size = PAGE_ALIGN(header_size);
+ buf = kzalloc(header_size, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ file_data = (struct ath12k_dump_file_data *)buf;
+ strscpy(file_data->df_magic, "ATH12K-FW-DUMP",
+ sizeof(file_data->df_magic));
+ file_data->len = cpu_to_le32(header_size);
+ file_data->version = cpu_to_le32(ATH12K_FW_CRASH_DUMP_VERSION);
+ file_data->chip_id = cpu_to_le32(ar_pci->dev_id);
+ file_data->qrtr_id = cpu_to_le32(ar_pci->ab->qmi.service_ins_id);
+ file_data->bus_id = pci_domain_nr(ar_pci->pdev->bus);
+ dev = ar_pci->pdev->dev;
+
+ guid_gen(&file_data->guid);
+ ktime_get_real_ts64(&timestamp);
+ file_data->tv_sec = cpu_to_le64(timestamp.tv_sec);
+ file_data->tv_nsec = cpu_to_le64(timestamp.tv_nsec);
+ file_data->num_seg = cpu_to_le32(num_seg);
+ file_data->seg_size = cpu_to_le32(sizeof(*segments));
+
+ /* copy segment details to file */
+ buf += offsetof(struct ath12k_dump_file_data, seg);
+ file_data->seg = (struct ath12k_dump_segment *)buf;
+ memcpy(file_data->seg, segments, num_seg * sizeof(*segments));
+
+ dump_state.header = file_data;
+ dump_state.num_seg = num_seg;
+ dump_state.segments = segments;
+ init_completion(&dump_state.dump_done);
+
+ dev_coredumpm(&dev, NULL, &dump_state, header_size, GFP_KERNEL,
+ ath12k_coredump_read_q6dump, ath12k_coredump_free_q6dump);
+
+ /* Wait until the dump is read and free is called */
+ wait_for_completion(&dump_state.dump_done);
+ kfree(file_data);
+}
+
+void ath12k_coredump_download_rddm(struct ath12k_base *ab)
+{
+ struct ath12k_pci *ar_pci = (struct ath12k_pci *)ab->drv_priv;
+ struct mhi_controller *mhi_ctrl = ar_pci->mhi_ctrl;
+ struct image_info *rddm_img, *fw_img;
+ struct ath12k_dump_segment *segment, *seg_info;
+ int i, rem_seg_cnt = 0, len, num_seg, seg_sz;
+
+ ath12k_mhi_coredump(mhi_ctrl, false);
+
+ rddm_img = mhi_ctrl->rddm_image;
+ 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 ||
+ 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;
+ len = num_seg * sizeof(*segment);
+
+ segment = kzalloc(len, GFP_KERNEL);
+ if (!segment)
+ return;
+
+ seg_info = segment;
+ for (i = 0; i < fw_img->entries ; i++) {
+ seg_sz = fw_img->mhi_buf[i].len;
+ seg_info->len = PAGE_ALIGN(seg_sz);
+ seg_info->addr = fw_img->mhi_buf[i].dma_addr;
+ seg_info->vaddr = fw_img->mhi_buf[i].buf;
+ seg_info->type = ATH12K_FW_CRASH_PAGING_DATA;
+ seg_info++;
+ }
+
+ for (i = 0; i < rddm_img->entries; i++) {
+ seg_sz = rddm_img->mhi_buf[i].len;
+ seg_info->len = PAGE_ALIGN(seg_sz);
+ seg_info->addr = rddm_img->mhi_buf[i].dma_addr;
+ seg_info->vaddr = rddm_img->mhi_buf[i].buf;
+ seg_info->type = ATH12K_FW_CRASH_RDDM_DATA;
+ seg_info++;
+ }
+
+ for (i = 0; i < ab->qmi.mem_seg_count; i++) {
+ if (ab->qmi.target_mem[i].type == CALDB_MEM_REGION_TYPE) {
+ if (ath12k_cold_boot_cal ||
+ ab->hw_params->cold_boot_calib) {
+ 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].v.addr;
+ seg_info->type = ATH12K_FW_REMOTE_MEM_DATA;
+ seg_info++;
+ }
+
+ if(!ab->fw_recovery_support) {
+ ath12k_coredump_seg_info.chip_id = ar_pci->dev_id;
+ ath12k_coredump_seg_info.qrtr_id = ar_pci->ab->qmi.service_ins_id;
+ ath12k_coredump_seg_info.bus_id = pci_domain_nr(ar_pci->pdev->bus);
+ ath12k_coredump_seg_info.num_seg = num_seg;
+ ath12k_coredump_seg_info.seg = segment;
+
+ BUG_ON(1);
+ } else {
+ ath12k_coredump_build_inline(ab, segment, num_seg);
+ }
+
+ kfree(segment);
+}
--- /dev/null
+++ b/drivers/net/wireless/ath/ath12k/coredump.h
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#ifndef _COREDUMP_H_
+#define _COREDUMP_H_
+
+#define ATH12K_FW_CRASH_DUMP_VERSION 1
+
+enum ath12k_fw_crash_dump_type {
+ ATH12K_FW_CRASH_PAGING_DATA,
+ ATH12K_FW_CRASH_RDDM_DATA,
+ ATH12K_FW_REMOTE_MEM_DATA,
+ ATH12K_FW_QDSS_DATA,
+};
+
+struct ath12k_dump_segment {
+ unsigned long addr;
+ void *vaddr;
+ unsigned int len;
+ unsigned int type;
+};
+
+struct ath12k_dump_file_data {
+ /* "ATH12K-FW-DUMP" */
+ char df_magic[16];
+ __le32 len;
+ /* file dump version */
+ __le32 version;
+ /* pci device id */
+ __le32 chip_id;
+ /* qrtr instance id */
+ __le32 qrtr_id;
+ /* pci domain id */
+ u8 bus_id;
+ guid_t guid;
+ /* time-of-day stamp */
+ __le64 tv_sec;
+ /* time-of-day stamp, nano-seconds */
+ __le64 tv_nsec;
+ /* room for growth w/out changing binary format */
+ u8 unused[8];
+ /* number of segments */
+ __le32 num_seg;
+ /* ath12k_dump_segment struct size */
+ __le32 seg_size;
+
+ struct ath12k_dump_segment *seg;
+ /* struct ath12k_dump_segment + more */
+
+ u8 data[0];
+} __packed;
+
+struct ath12k_coredump_state {
+ struct ath12k_dump_file_data *header;
+ struct ath12k_dump_segment *segments;
+ struct completion dump_done;
+ u32 num_seg;
+};
+
+struct ath12k_coredump_segment_info {
+ u32 chip_id;
+ u32 qrtr_id;
+ u32 num_seg;
+ struct ath12k_dump_segment *seg;
+ u8 bus_id;
+};
+
+#ifdef CONFIG_WANT_DEV_COREDUMP
+void ath12k_coredump_download_rddm(struct ath12k_base *ab);
+void ath12k_coredump_build_inline(struct ath12k_base *ab,
+ struct ath12k_dump_segment *segments, int num_seg);
+#else
+static inline void ath12k_coredump_download_rddm(struct ath12k_base *ab)
+{
+}
+
+static inline void ath12k_coredump_build_inline(struct ath12k_base *ab,
+ struct ath12k_dump_segment *segments,
+ int num_seg)
+{
+}
+#endif
+
+#endif
+
--- a/drivers/net/wireless/ath/ath12k/mhi.c
+++ b/drivers/net/wireless/ath/ath12k/mhi.c
@@ -392,6 +392,7 @@ int ath12k_mhi_register(struct ath12k_pc
#if LINUX_VERSION_IS_GEQ(5,10,0)
mhi_ctrl->reg_len = ab->mem_len;
#endif
+ mhi_ctrl->rddm_size = ATH12K_PCI_FW_RDDM_SZ;
ret = ath12k_mhi_get_msi(ab_pci);
if (ret) {
@@ -648,3 +649,8 @@ void ath12k_mhi_resume(struct ath12k_pci
{
ath12k_mhi_set_state(ab_pci, ATH12K_MHI_RESUME);
}
+
+void ath12k_mhi_coredump(struct mhi_controller *mhi_ctrl, bool state)
+{
+ mhi_download_rddm_image(mhi_ctrl, false);
+}
--- a/drivers/net/wireless/ath/ath12k/mhi.h
+++ b/drivers/net/wireless/ath/ath12k/mhi.h
@@ -16,6 +16,7 @@
#define MHISTATUS 0x48
#define MHICTRL 0x38
#define MHICTRL_RESET_MASK 0x2
+#define ATH12K_PCI_FW_RDDM_SZ 0x600000
enum ath12k_mhi_state {
ATH12K_MHI_INIT,
@@ -42,5 +43,6 @@ void ath12k_mhi_clear_vector(struct ath1
void ath12k_mhi_suspend(struct ath12k_pci *ar_pci);
void ath12k_mhi_resume(struct ath12k_pci *ar_pci);
+void ath12k_mhi_coredump(struct mhi_controller *mhi_ctrl, bool state);
#endif