summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHsiu-Chang Chen <hsiuchangchen@google.com>2021-11-23 19:05:16 +0800
committerVictor Hsu <hsuvictor@google.com>2021-12-13 17:02:33 +0800
commitaa8d11a216b3aa69ba637fc2118c50fb91cd09ab (patch)
tree2df1873f801feb777b64cdb03cf934647a9cc1dc
parent749813d67d19088575e70e142343b60ffc1bd482 (diff)
downloadcnss2-aa8d11a216b3aa69ba637fc2118c50fb91cd09ab.tar.gz
wcn6740: Implement wlan coredump feature
Bug: 204519778 Change-Id: Ic85052cd12d8e5b02637044998267a39795631b8
-rw-r--r--cnss2/Makefile1
-rw-r--r--cnss2/main.c28
-rw-r--r--cnss2/main.h9
-rw-r--r--cnss2/pci.c17
-rw-r--r--cnss2/pci_platform_google.c132
-rw-r--r--inc/memory_dump.h135
-rw-r--r--inc/qcom_ramdump.h49
7 files changed, 364 insertions, 7 deletions
diff --git a/cnss2/Makefile b/cnss2/Makefile
index f85e728..244fbc7 100644
--- a/cnss2/Makefile
+++ b/cnss2/Makefile
@@ -6,6 +6,7 @@ ifeq ($(CONFIG_CNSS_OUT_OF_TREE),y)
ccflags-y += -I$(WLAN_PLATFORM_ROOT)/inc
ccflags-y += -I$(WLAN_PLATFORM_ROOT)/cnss_utils
ccflags-y += -DCONFIG_WCN_GOOGLE
+ccflags-y += -DCONFIG_QCOM_RAMDUMP -DCONFIG_QCOM_MEMORY_DUMP_V2
else
ccflags-y += -I$(srctree)/drivers/net/wireless/cnss_utils/
endif
diff --git a/cnss2/main.c b/cnss2/main.c
index 73e253f..050d911 100644
--- a/cnss2/main.c
+++ b/cnss2/main.c
@@ -2225,6 +2225,13 @@ static void cnss_destroy_ramdump_device(struct cnss_plat_data *plat_priv,
#endif
#if IS_ENABLED(CONFIG_QCOM_RAMDUMP)
+
+#if IS_ENABLED(CONFIG_WCN_GOOGLE)
+int cnss_do_ramdump(struct cnss_plat_data *plat_priv)
+{
+ return 0;
+}
+#else
int cnss_do_ramdump(struct cnss_plat_data *plat_priv)
{
struct cnss_ramdump_info *ramdump_info = &plat_priv->ramdump_info;
@@ -2239,7 +2246,7 @@ int cnss_do_ramdump(struct cnss_plat_data *plat_priv)
return qcom_dump(&head, ramdump_info->ramdump_dev);
}
-
+#endif
int cnss_do_elf_ramdump(struct cnss_plat_data *plat_priv)
{
struct cnss_ramdump_info_v2 *info_v2 = &plat_priv->ramdump_info_v2;
@@ -2314,7 +2321,9 @@ int cnss_do_elf_ramdump(struct cnss_plat_data *plat_priv)
static int cnss_init_dump_entry(struct cnss_plat_data *plat_priv)
{
struct cnss_ramdump_info *ramdump_info;
+#if !IS_ENABLED(CONFIG_WCN_GOOGLE)
struct msm_dump_entry dump_entry;
+#endif
ramdump_info = &plat_priv->ramdump_info;
ramdump_info->dump_data.addr = ramdump_info->ramdump_pa;
@@ -2323,11 +2332,15 @@ static int cnss_init_dump_entry(struct cnss_plat_data *plat_priv)
ramdump_info->dump_data.magic = CNSS_DUMP_MAGIC_VER_V2;
strlcpy(ramdump_info->dump_data.name, CNSS_DUMP_NAME,
sizeof(ramdump_info->dump_data.name));
+#if IS_ENABLED(CONFIG_WCN_GOOGLE)
+ return 0;
+#else
dump_entry.id = MSM_DUMP_DATA_CNSS_WLAN;
dump_entry.addr = virt_to_phys(&ramdump_info->dump_data);
return msm_dump_data_register_nominidump(MSM_DUMP_TABLE_APPS,
&dump_entry);
+#endif
}
static int cnss_register_ramdump_v1(struct cnss_plat_data *plat_priv)
@@ -2398,6 +2411,7 @@ static void cnss_unregister_ramdump_v1(struct cnss_plat_data *plat_priv)
ramdump_info->ramdump_pa);
}
+#if !IS_ENABLED(CONFIG_WCN_GOOGLE)
/**
* cnss_ignore_dump_data_reg_fail - Ignore Ramdump table register failure
* @ret: Error returned by msm_dump_data_register_nominidump
@@ -2411,13 +2425,17 @@ static int cnss_ignore_dump_data_reg_fail(int ret)
{
return ret;
}
+#endif
static int cnss_register_ramdump_v2(struct cnss_plat_data *plat_priv)
{
int ret = 0;
struct cnss_ramdump_info_v2 *info_v2;
struct cnss_dump_data *dump_data;
+
+#if !IS_ENABLED(CONFIG_WCN_GOOGLE)
struct msm_dump_entry dump_entry;
+#endif
struct device *dev = &plat_priv->plat_dev->dev;
u32 ramdump_size = 0;
@@ -2440,6 +2458,9 @@ static int cnss_register_ramdump_v2(struct cnss_plat_data *plat_priv)
dump_data->seg_version = CNSS_DUMP_SEG_VER;
strlcpy(dump_data->name, CNSS_DUMP_NAME,
sizeof(dump_data->name));
+
+#if !IS_ENABLED(CONFIG_WCN_GOOGLE)
+
dump_entry.id = MSM_DUMP_DATA_CNSS_WLAN;
dump_entry.addr = virt_to_phys(dump_data);
@@ -2451,7 +2472,7 @@ static int cnss_register_ramdump_v2(struct cnss_plat_data *plat_priv)
ret ? "Error" : "Ignoring", ret);
goto free_ramdump;
}
-
+#endif
info_v2->ramdump_dev = cnss_create_ramdump_device(plat_priv);
if (!info_v2->ramdump_dev) {
cnss_pr_err("Failed to create ramdump device!\n");
@@ -3270,6 +3291,9 @@ static int cnss_probe(struct platform_device *plat_dev)
cnss_get_cpr_info(plat_priv);
cnss_aop_mbox_init(plat_priv);
cnss_init_control_params(plat_priv);
+#if IS_ENABLED(CONFIG_WCN_GOOGLE)
+ plat_priv->recovery_enabled = true;
+#endif //CONFIG_WCN_GOOGLE
ret = cnss_get_resources(plat_priv);
if (ret)
diff --git a/cnss2/main.h b/cnss2/main.h
index 924ce1e..8f7f849 100644
--- a/cnss2/main.h
+++ b/cnss2/main.h
@@ -21,9 +21,15 @@
#include <linux/time64.h>
#ifdef CONFIG_CNSS_OUT_OF_TREE
#include "cnss2.h"
+#if IS_ENABLED(CONFIG_QCOM_MEMORY_DUMP_V2)
+#include "memory_dump.h"
+#endif
+#if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) || \
+ IS_ENABLED(CONFIG_QCOM_RAMDUMP)
+#include "qcom_ramdump.h"
+#endif
#else
#include <net/cnss2.h>
-#endif
#if IS_ENABLED(CONFIG_QCOM_MEMORY_DUMP_V2)
#include <soc/qcom/memory_dump.h>
#endif
@@ -31,6 +37,7 @@
IS_ENABLED(CONFIG_QCOM_RAMDUMP)
#include <soc/qcom/qcom_ramdump.h>
#endif
+#endif /* CONFIG_CNSS_OUT_OF_TREE */
#if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART)
#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/subsystem_restart.h>
diff --git a/cnss2/pci.c b/cnss2/pci.c
index 4b764b1..f84a86f 100644
--- a/cnss2/pci.c
+++ b/cnss2/pci.c
@@ -4401,22 +4401,24 @@ static void cnss_pci_add_dump_seg(struct cnss_pci_data *pci_priv,
enum cnss_fw_dump_type type, int seg_no,
void *va, dma_addr_t dma, size_t size)
{
+#if !IS_ENABLED(CONFIG_WCN_GOOGLE)
struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
struct device *dev = &pci_priv->pci_dev->dev;
phys_addr_t pa;
-
+#endif
dump_seg->address = dma;
dump_seg->v_address = va;
dump_seg->size = size;
dump_seg->type = type;
- cnss_pr_dbg("Seg: %x, va: %pK, dma: %pa, size: 0x%zx\n",
+ cnss_pr_dbg("Seg: %x, va: %x, dma: %pa, size: 0x%zx\n",
seg_no, va, &dma, size);
-
+#if !IS_ENABLED(CONFIG_WCN_GOOGLE)
if (cnss_va_to_pa(dev, size, va, dma, &pa, DMA_ATTR_FORCE_CONTIGUOUS))
return;
cnss_minidump_add_region(plat_priv, type, seg_no, va, pa, size);
+#endif
}
static void cnss_pci_remove_dump_seg(struct cnss_pci_data *pci_priv,
@@ -5187,6 +5189,9 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
ret = cnss_register_ramdump(plat_priv);
if (ret)
goto unregister_subsys;
+#if IS_ENABLED(CONFIG_WCN_GOOGLE)
+ cnss_register_sscd();
+#endif /* CONFIG_WCN_GOOGLE */
ret = cnss_pci_init_smmu(pci_priv);
if (ret)
@@ -5258,6 +5263,9 @@ dereg_pci_event:
deinit_smmu:
cnss_pci_deinit_smmu(pci_priv);
unregister_ramdump:
+#if IS_ENABLED(CONFIG_WCN_GOOGLE)
+ cnss_unregister_sscd();
+#endif /* CONFIG_WCN_GOOGLE */
cnss_unregister_ramdump(plat_priv);
unregister_subsys:
cnss_unregister_subsys(plat_priv);
@@ -5295,6 +5303,9 @@ static void cnss_pci_remove(struct pci_dev *pci_dev)
cnss_pci_disable_bus(pci_priv);
cnss_dereg_pci_event(pci_priv);
cnss_pci_deinit_smmu(pci_priv);
+#if IS_ENABLED(CONFIG_WCN_GOOGLE)
+ cnss_unregister_sscd();
+#endif /* CONFIG_WCN_GOOGLE */
if (plat_priv) {
cnss_unregister_ramdump(plat_priv);
cnss_unregister_subsys(plat_priv);
diff --git a/cnss2/pci_platform_google.c b/cnss2/pci_platform_google.c
index 27b19e9..6446d3c 100644
--- a/cnss2/pci_platform_google.c
+++ b/cnss2/pci_platform_google.c
@@ -5,6 +5,7 @@
#include <linux/of_gpio.h>
#include <linux/of_reserved_mem.h>
#include <linux/exynos-pci-ctrl.h>
+#include <linux/platform_data/sscoredump.h>
#include "pci_platform.h"
#include "debug.h"
#include "bus.h"
@@ -302,4 +303,133 @@ int cnss_pci_set_link_bandwidth(struct cnss_pci_data *pci_priv,
// static int cnss_pci_set_link_down(struct cnss_pci_data *pci_priv)
// {
// return 0;
-// } \ No newline at end of file
+// }
+
+/*
+ * The following functions are for ssrdump.
+ */
+
+#define DEVICE_NAME "wlan"
+
+static struct sscd_platform_data sscd_pdata;
+
+static struct platform_device sscd_dev = {
+ .name = DEVICE_NAME,
+ .driver_override = SSCD_NAME,
+ .id = -1,
+ .dev = {
+ .platform_data = &sscd_pdata,
+ .release = sscd_release,
+ },
+};
+
+void cnss_register_sscd(void)
+{
+ platform_device_register(&sscd_dev);
+}
+
+void cnss_unregister_sscd(void)
+{
+ platform_device_unregister(&sscd_dev);
+}
+
+void sscd_release(struct device *dev)
+{
+ cnss_pr_info("%s: enter\n", __FUNCTION__);
+}
+
+static void sscd_set_coredump(void *buf, int buf_len, const char *info)
+{
+ struct sscd_platform_data *pdata = dev_get_platdata(&sscd_dev.dev);
+ struct sscd_segment seg;
+
+ if (pdata->sscd_report) {
+ memset(&seg, 0, sizeof(seg));
+ seg.addr = buf;
+ seg.size = buf_len;
+ pdata->sscd_report(&sscd_dev, &seg, 1, 0, info);
+ }
+}
+
+int qcom_elf_dump(struct list_head *segs, struct device *dev)
+{
+ struct qcom_dump_segment *segment;
+ struct elf32_phdr *phdr;
+ struct elf32_hdr *ehdr;
+ size_t data_size;
+ size_t offset;
+ int phnum = 0;
+ void *data;
+ void __iomem *ptr;
+
+ if (!segs || list_empty(segs))
+ return -EINVAL;
+
+ data_size = sizeof(*ehdr);
+ list_for_each_entry(segment, segs, node) {
+ data_size += sizeof(*phdr) + segment->size;
+
+ phnum++;
+ }
+
+ data = vmalloc(data_size);
+ if (!data)
+ return -ENOMEM;
+
+ cnss_pr_info("Creating elf with size %d\n", data_size);
+ ehdr = data;
+
+ memset(ehdr, 0, sizeof(*ehdr));
+ memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+ ehdr->e_ident[EI_CLASS] = ELFCLASS32;
+ ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+ ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+ ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE;
+ ehdr->e_type = ET_CORE;
+ ehdr->e_machine = EM_NONE;
+ ehdr->e_version = EV_CURRENT;
+ ehdr->e_entry = 0;
+ ehdr->e_phoff = sizeof(*ehdr);
+ ehdr->e_ehsize = sizeof(*ehdr);
+ ehdr->e_phentsize = sizeof(*phdr);
+ ehdr->e_phnum = phnum;
+
+ phdr = data + ehdr->e_phoff;
+ offset = ehdr->e_phoff + sizeof(*phdr) * ehdr->e_phnum;
+ list_for_each_entry(segment, segs, node) {
+ memset(phdr, 0, sizeof(*phdr));
+ phdr->p_type = PT_LOAD;
+ phdr->p_offset = offset;
+ phdr->p_vaddr = segment->da;
+ phdr->p_paddr = segment->da;
+ phdr->p_filesz = segment->size;
+ phdr->p_memsz = segment->size;
+ phdr->p_flags = PF_R | PF_W | PF_X;
+ phdr->p_align = 0;
+
+ if (segment->va) {
+ memcpy(data + offset, segment->va, segment->size);
+ } else {
+ ptr = devm_ioremap(dev, segment->da, segment->size);
+ if (!ptr) {
+ dev_err(dev,
+ "invalid coredump segment (%pad, %zu)\n",
+ &segment->da, segment->size);
+ memset(data + offset, 0xff, segment->size);
+ } else
+ memcpy_fromio(data + offset, ptr,
+ segment->size);
+ }
+
+ offset += phdr->p_filesz;
+ phdr++;
+ }
+
+ /*
+ * SSCD integration
+ */
+ sscd_set_coredump(data, data_size, "Test Crash Info");
+
+ vfree(data);
+ return 0;
+}
diff --git a/inc/memory_dump.h b/inc/memory_dump.h
new file mode 100644
index 0000000..6db796b
--- /dev/null
+++ b/inc/memory_dump.h
@@ -0,0 +1,135 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2012, 2014-2017, 2019-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __MSM_MEMORY_DUMP_H
+#define __MSM_MEMORY_DUMP_H
+
+#include <linux/errno.h>
+#include <linux/types.h>
+
+enum dump_client_type {
+ MSM_CPU_CTXT = 0,
+ MSM_L1_CACHE,
+ MSM_L2_CACHE,
+ MSM_OCMEM,
+ MSM_TMC_ETFETB,
+ MSM_ETM0_REG,
+ MSM_ETM1_REG,
+ MSM_ETM2_REG,
+ MSM_ETM3_REG,
+ MSM_TMC0_REG, /* TMC_ETR */
+ MSM_TMC1_REG, /* TMC_ETF */
+ MSM_LOG_BUF,
+ MSM_LOG_BUF_FIRST_IDX,
+ MAX_NUM_CLIENTS,
+};
+
+struct msm_client_dump {
+ enum dump_client_type id;
+ unsigned long start_addr;
+ unsigned long end_addr;
+};
+
+#ifdef CONFIG_QCOM_MEMORY_DUMP
+extern int msm_dump_tbl_register(struct msm_client_dump *client_entry);
+#else
+static inline int msm_dump_tbl_register(struct msm_client_dump *entry)
+{
+ return -EIO;
+}
+#endif
+
+
+#if IS_ENABLED(CONFIG_QCOM_MEMORY_DUMP_V2)
+extern uint32_t msm_dump_table_version(void);
+#else
+static inline uint32_t msm_dump_table_version(void)
+{
+ return 0;
+}
+#endif
+
+#define MSM_DUMP_MAKE_VERSION(ma, mi) ((ma << 20) | mi)
+#define MSM_DUMP_MAJOR(val) (val >> 20)
+#define MSM_DUMP_MINOR(val) (val & 0xFFFFF)
+
+
+#define MAX_NUM_ENTRIES 0x150
+
+enum msm_dump_data_ids {
+ MSM_DUMP_DATA_CPU_CTX = 0x00,
+ MSM_DUMP_DATA_L1_INST_CACHE = 0x60,
+ MSM_DUMP_DATA_L1_DATA_CACHE = 0x80,
+ MSM_DUMP_DATA_ETM_REG = 0xA0,
+ MSM_DUMP_DATA_L2_CACHE = 0xC0,
+ MSM_DUMP_DATA_L3_CACHE = 0xD0,
+ MSM_DUMP_DATA_OCMEM = 0xE0,
+ MSM_DUMP_DATA_CNSS_WLAN = 0xE1,
+ MSM_DUMP_DATA_WIGIG = 0xE2,
+ MSM_DUMP_DATA_PMIC = 0xE4,
+ MSM_DUMP_DATA_DBGUI_REG = 0xE5,
+ MSM_DUMP_DATA_DCC_REG = 0xE6,
+ MSM_DUMP_DATA_DCC_SRAM = 0xE7,
+ MSM_DUMP_DATA_MISC = 0xE8,
+ MSM_DUMP_DATA_VSENSE = 0xE9,
+ MSM_DUMP_DATA_RPM = 0xEA,
+ MSM_DUMP_DATA_SCANDUMP = 0xEB,
+ MSM_DUMP_DATA_RPMH = 0xEC,
+ MSM_DUMP_DATA_TMC_ETF = 0xF0,
+ MSM_DUMP_DATA_TMC_ETF_SWAO = 0xF1,
+ MSM_DUMP_DATA_TMC_REG = 0x100,
+ MSM_DUMP_DATA_TMC_ETF_SWAO_REG = 0x102,
+ MSM_DUMP_DATA_LOG_BUF = 0x110,
+ MSM_DUMP_DATA_LOG_BUF_FIRST_IDX = 0x111,
+ MSM_DUMP_DATA_SCANDUMP_PER_CPU = 0x130,
+ MSM_DUMP_DATA_LLCC_PER_INSTANCE = 0x140,
+ MSM_DUMP_DATA_MAX = MAX_NUM_ENTRIES,
+};
+
+enum msm_dump_table_ids {
+ MSM_DUMP_TABLE_APPS,
+ MSM_DUMP_TABLE_MAX = MAX_NUM_ENTRIES,
+};
+
+enum msm_dump_type {
+ MSM_DUMP_TYPE_DATA,
+ MSM_DUMP_TYPE_TABLE,
+};
+
+struct msm_dump_data {
+ uint32_t version;
+ uint32_t magic;
+ char name[32];
+ uint64_t addr;
+ uint64_t len;
+ uint32_t reserved;
+};
+
+struct msm_dump_entry {
+ uint32_t id;
+ char name[32];
+ uint32_t type;
+ uint64_t addr;
+};
+
+#if IS_ENABLED(CONFIG_QCOM_MEMORY_DUMP_V2)
+extern int msm_dump_data_register(enum msm_dump_table_ids id,
+ struct msm_dump_entry *entry);
+extern int msm_dump_data_register_nominidump(enum msm_dump_table_ids id,
+ struct msm_dump_entry *entry);
+#else
+static inline int msm_dump_data_register(enum msm_dump_table_ids id,
+ struct msm_dump_entry *entry)
+{
+ return -EINVAL;
+}
+static inline int msm_dump_data_register_nominidump(enum msm_dump_table_ids id,
+ struct msm_dump_entry *entry)
+{
+ return -EINVAL;
+}
+#endif
+
+#endif
diff --git a/inc/qcom_ramdump.h b/inc/qcom_ramdump.h
new file mode 100644
index 0000000..9b56dc3
--- /dev/null
+++ b/inc/qcom_ramdump.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _QCOM_RAMDUMP_HEADER
+#define _QCOM_RAMDUMP_HEADER
+#include <linux/kernel.h>
+#include <linux/firmware.h>
+
+struct device;
+
+struct qcom_dump_segment {
+ struct list_head node;
+ dma_addr_t da;
+ void *va;
+ size_t size;
+};
+
+#if IS_ENABLED(CONFIG_QCOM_RAMDUMP)
+extern int qcom_elf_dump(struct list_head *segs, struct device *dev);
+extern int qcom_dump(struct list_head *head, struct device *dev);
+extern int qcom_fw_elf_dump(struct firmware *fw, struct device *dev);
+extern bool dump_enabled(void);
+#ifdef CONFIG_WCN_GOOGLE
+extern void cnss_register_sscd(void);
+extern void cnss_unregister_sscd(void);
+extern void sscd_release(struct device *dev);
+#endif
+#else
+static inline int qcom_elf_dump(struct list_head *segs, struct device *dev)
+{
+ return -ENODEV;
+}
+static inline int qcom_dump(struct list_head *head, struct device *dev)
+{
+ return -ENODEV;
+}
+static inline int qcom_fw_elf_dump(struct firmware *fw, struct device *dev)
+{
+ return -ENODEV;
+}
+static inline bool dump_enabled(void)
+{
+ return false;
+}
+#endif /* CONFIG_QCOM_RAMDUMP */
+
+#endif