diff options
-rw-r--r-- | cnss2/Makefile | 1 | ||||
-rw-r--r-- | cnss2/main.c | 28 | ||||
-rw-r--r-- | cnss2/main.h | 9 | ||||
-rw-r--r-- | cnss2/pci.c | 17 | ||||
-rw-r--r-- | cnss2/pci_platform_google.c | 132 | ||||
-rw-r--r-- | inc/memory_dump.h | 135 | ||||
-rw-r--r-- | inc/qcom_ramdump.h | 49 |
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 |