mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 13:49:24 -04:00
soc: qcom: Add snapshot of the memory dump v2 driver
Add memory dump v2 driver from msm-4.19
commit 034b7f3bfb2c ("msm: cvp: Fix page fault when open 16 cvp sessions").
Remove dmac_flush_range since it's deprecated.
Change-Id: Ibcc42c0d737abbb304a222ac51cfdd94019af534
Signed-off-by: Tingwei Zhang <tingwei@codeaurora.org>
This commit is contained in:
parent
961790582c
commit
8aad5e04ed
4 changed files with 474 additions and 0 deletions
|
|
@ -269,6 +269,14 @@ config QCOM_SMD_RPM
|
|||
Say M here if you want to include support for the Qualcomm RPM as a
|
||||
module. This will build a module called "qcom-smd-rpm".
|
||||
|
||||
config QCOM_MEMORY_DUMP_V2
|
||||
bool "QCOM Memory Dump V2 Support"
|
||||
help
|
||||
This enables memory dump feature. It allows various client
|
||||
subsystems to register respective dump regions. At the time
|
||||
of deadlocks or cpu hangs these dump regions are captured to
|
||||
give a snapshot of the system at the time of the crash.
|
||||
|
||||
config QCOM_SMEM_STATE
|
||||
bool
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ obj-$(CONFIG_QCOM_IPCC) += qcom_ipcc.o
|
|||
obj-$(CONFIG_QCOM_APR) += apr.o
|
||||
obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o
|
||||
obj-$(CONFIG_QCOM_LAHAINA_LLCC) += llcc-lahaina.o
|
||||
obj-$(CONFIG_QCOM_MEMORY_DUMP_V2) += memory_dump_v2.o
|
||||
obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o
|
||||
obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o
|
||||
obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o
|
||||
|
|
|
|||
331
drivers/soc/qcom/memory_dump_v2.c
Normal file
331
drivers/soc/qcom/memory_dump_v2.c
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2014-2017, 2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <soc/qcom/minidump.h>
|
||||
#include <soc/qcom/memory_dump.h>
|
||||
#include <soc/qcom/scm.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define MSM_DUMP_TABLE_VERSION MSM_DUMP_MAKE_VERSION(2, 0)
|
||||
|
||||
#define SCM_CMD_DEBUG_LAR_UNLOCK 0x4
|
||||
|
||||
struct msm_dump_table {
|
||||
uint32_t version;
|
||||
uint32_t num_entries;
|
||||
struct msm_dump_entry entries[MAX_NUM_ENTRIES];
|
||||
};
|
||||
|
||||
struct msm_memory_dump {
|
||||
uint64_t table_phys;
|
||||
struct msm_dump_table *table;
|
||||
};
|
||||
|
||||
static struct msm_memory_dump memdump;
|
||||
|
||||
uint32_t msm_dump_table_version(void)
|
||||
{
|
||||
return MSM_DUMP_TABLE_VERSION;
|
||||
}
|
||||
EXPORT_SYMBOL(msm_dump_table_version);
|
||||
|
||||
static int msm_dump_table_register(struct msm_dump_entry *entry)
|
||||
{
|
||||
struct msm_dump_entry *e;
|
||||
struct msm_dump_table *table = memdump.table;
|
||||
|
||||
if (!table || table->num_entries >= MAX_NUM_ENTRIES)
|
||||
return -EINVAL;
|
||||
|
||||
e = &table->entries[table->num_entries];
|
||||
e->id = entry->id;
|
||||
e->type = MSM_DUMP_TYPE_TABLE;
|
||||
e->addr = entry->addr;
|
||||
table->num_entries++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct msm_dump_table *msm_dump_get_table(enum msm_dump_table_ids id)
|
||||
{
|
||||
struct msm_dump_table *table = memdump.table;
|
||||
int i;
|
||||
|
||||
if (!table) {
|
||||
pr_err("mem dump base table does not exist\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_NUM_ENTRIES; i++) {
|
||||
if (table->entries[i].id == id)
|
||||
break;
|
||||
}
|
||||
if (i == MAX_NUM_ENTRIES || !table->entries[i].addr) {
|
||||
pr_err("mem dump base table entry %d invalid\n", id);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
/* Get the apps table pointer */
|
||||
table = phys_to_virt(table->entries[i].addr);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
static int msm_dump_data_add_minidump(struct msm_dump_entry *entry)
|
||||
{
|
||||
struct msm_dump_data *data;
|
||||
struct md_region md_entry;
|
||||
|
||||
data = (struct msm_dump_data *)(phys_to_virt(entry->addr));
|
||||
|
||||
if (!data->addr || !data->len)
|
||||
return -EINVAL;
|
||||
|
||||
if (!strcmp(data->name, "")) {
|
||||
pr_debug("Entry name is NULL, Use ID %d for minidump\n",
|
||||
entry->id);
|
||||
snprintf(md_entry.name, sizeof(md_entry.name), "KMDT0x%X",
|
||||
entry->id);
|
||||
} else {
|
||||
strlcpy(md_entry.name, data->name, sizeof(md_entry.name));
|
||||
}
|
||||
|
||||
md_entry.phys_addr = data->addr;
|
||||
md_entry.virt_addr = (uintptr_t)phys_to_virt(data->addr);
|
||||
md_entry.size = data->len;
|
||||
md_entry.id = entry->id;
|
||||
|
||||
return msm_minidump_add_region(&md_entry);
|
||||
}
|
||||
|
||||
static int register_dump_table_entry(enum msm_dump_table_ids id,
|
||||
struct msm_dump_entry *entry)
|
||||
{
|
||||
struct msm_dump_entry *e;
|
||||
struct msm_dump_table *table;
|
||||
|
||||
table = msm_dump_get_table(id);
|
||||
if (IS_ERR(table))
|
||||
return PTR_ERR(table);
|
||||
|
||||
if (!table || table->num_entries >= MAX_NUM_ENTRIES)
|
||||
return -EINVAL;
|
||||
|
||||
e = &table->entries[table->num_entries];
|
||||
e->id = entry->id;
|
||||
e->type = MSM_DUMP_TYPE_DATA;
|
||||
e->addr = entry->addr;
|
||||
table->num_entries++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* msm_dump_data_register - register to dump data and minidump framework
|
||||
* @id: ID of the dump table.
|
||||
* @entry: dump entry to be registered
|
||||
* This api will register the entry passed to dump table and minidump table
|
||||
*/
|
||||
int msm_dump_data_register(enum msm_dump_table_ids id,
|
||||
struct msm_dump_entry *entry)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = register_dump_table_entry(id, entry);
|
||||
if (!ret)
|
||||
if (msm_dump_data_add_minidump(entry))
|
||||
pr_err("Failed to add entry in Minidump table\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(msm_dump_data_register);
|
||||
|
||||
/**
|
||||
* msm_dump_data_register_nominidump - register to dump data framework
|
||||
* @id: ID of the dump table.
|
||||
* @entry: dump entry to be registered
|
||||
* This api will register the entry passed to dump table only
|
||||
*/
|
||||
int msm_dump_data_register_nominidump(enum msm_dump_table_ids id,
|
||||
struct msm_dump_entry *entry)
|
||||
{
|
||||
return register_dump_table_entry(id, entry);
|
||||
}
|
||||
EXPORT_SYMBOL(msm_dump_data_register_nominidump);
|
||||
|
||||
static int __init init_memory_dump(void)
|
||||
{
|
||||
struct msm_dump_table *table;
|
||||
struct msm_dump_entry entry;
|
||||
struct device_node *np;
|
||||
void __iomem *imem_base;
|
||||
int ret;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL,
|
||||
"qcom,msm-imem-mem_dump_table");
|
||||
if (!np) {
|
||||
pr_err("mem dump base table DT node does not exist\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
imem_base = of_iomap(np, 0);
|
||||
if (!imem_base) {
|
||||
pr_err("mem dump base table imem offset mapping failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memdump.table = kzalloc(sizeof(struct msm_dump_table), GFP_KERNEL);
|
||||
if (!memdump.table) {
|
||||
ret = -ENOMEM;
|
||||
goto err0;
|
||||
}
|
||||
memdump.table->version = MSM_DUMP_TABLE_VERSION;
|
||||
memdump.table_phys = virt_to_phys(memdump.table);
|
||||
memcpy_toio(imem_base, &memdump.table_phys, sizeof(memdump.table_phys));
|
||||
/* Ensure write to imem_base is complete before unmapping */
|
||||
mb();
|
||||
pr_info("MSM Memory Dump base table set up\n");
|
||||
|
||||
iounmap(imem_base);
|
||||
|
||||
table = kzalloc(sizeof(struct msm_dump_table), GFP_KERNEL);
|
||||
if (!table) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
table->version = MSM_DUMP_TABLE_VERSION;
|
||||
|
||||
entry.id = MSM_DUMP_TABLE_APPS;
|
||||
entry.addr = virt_to_phys(table);
|
||||
ret = msm_dump_table_register(&entry);
|
||||
if (ret) {
|
||||
pr_info("mem dump apps data table register failed\n");
|
||||
goto err2;
|
||||
}
|
||||
pr_info("MSM Memory Dump apps data table set up\n");
|
||||
|
||||
return 0;
|
||||
err2:
|
||||
kfree(table);
|
||||
err1:
|
||||
kfree(memdump.table);
|
||||
return ret;
|
||||
err0:
|
||||
iounmap(imem_base);
|
||||
return ret;
|
||||
}
|
||||
early_initcall(init_memory_dump);
|
||||
|
||||
#ifdef CONFIG_MSM_DEBUG_LAR_UNLOCK
|
||||
static int __init init_debug_lar_unlock(void)
|
||||
{
|
||||
int ret;
|
||||
uint32_t argument = 0;
|
||||
struct scm_desc desc = {0};
|
||||
|
||||
if (!is_scm_armv8())
|
||||
ret = scm_call(SCM_SVC_TZ, SCM_CMD_DEBUG_LAR_UNLOCK, &argument,
|
||||
sizeof(argument), NULL, 0);
|
||||
else
|
||||
ret = scm_call2(SCM_SIP_FNID(SCM_SVC_TZ,
|
||||
SCM_CMD_DEBUG_LAR_UNLOCK), &desc);
|
||||
if (ret)
|
||||
pr_err("Core Debug Lock unlock failed, ret: %d\n", ret);
|
||||
else
|
||||
pr_info("Core Debug Lock unlocked\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
early_initcall(init_debug_lar_unlock);
|
||||
#endif
|
||||
|
||||
static int mem_dump_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *child_node;
|
||||
const struct device_node *node = pdev->dev.of_node;
|
||||
static dma_addr_t dump_addr;
|
||||
static void *dump_vaddr;
|
||||
struct msm_dump_data *dump_data;
|
||||
struct msm_dump_entry dump_entry;
|
||||
int ret;
|
||||
u32 size, id;
|
||||
|
||||
for_each_available_child_of_node(node, child_node) {
|
||||
ret = of_property_read_u32(child_node, "qcom,dump-size", &size);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Unable to find size for %s\n",
|
||||
child_node->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(child_node, "qcom,dump-id", &id);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Unable to find id for %s\n",
|
||||
child_node->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
dump_vaddr = dma_alloc_coherent(&pdev->dev, size,
|
||||
&dump_addr, GFP_KERNEL);
|
||||
|
||||
if (!dump_vaddr) {
|
||||
dev_err(&pdev->dev, "Couldn't get memory for dumping\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
dump_data = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct msm_dump_data), GFP_KERNEL);
|
||||
if (!dump_data) {
|
||||
dma_free_coherent(&pdev->dev, size, dump_vaddr,
|
||||
dump_addr);
|
||||
continue;
|
||||
}
|
||||
|
||||
dump_data->addr = dump_addr;
|
||||
dump_data->len = size;
|
||||
dump_entry.id = id;
|
||||
strlcpy(dump_data->name, child_node->name,
|
||||
sizeof(dump_data->name));
|
||||
dump_entry.addr = virt_to_phys(dump_data);
|
||||
ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Data dump setup failed, id = %d\n",
|
||||
id);
|
||||
dma_free_coherent(&pdev->dev, size, dump_vaddr,
|
||||
dump_addr);
|
||||
devm_kfree(&pdev->dev, dump_data);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mem_dump_match_table[] = {
|
||||
{.compatible = "qcom,mem-dump",},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver mem_dump_driver = {
|
||||
.probe = mem_dump_probe,
|
||||
.driver = {
|
||||
.name = "msm_mem_dump",
|
||||
.of_match_table = mem_dump_match_table,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init mem_dump_init(void)
|
||||
{
|
||||
return platform_driver_register(&mem_dump_driver);
|
||||
}
|
||||
|
||||
pure_initcall(mem_dump_init);
|
||||
134
include/soc/qcom/memory_dump.h
Normal file
134
include/soc/qcom/memory_dump.h
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2012, 2014-2017, 2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __MSM_MEMORY_DUMP_H
|
||||
#define __MSM_MEMORY_DUMP_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 defined(CONFIG_QCOM_MEMORY_DUMP) || defined(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;
|
||||
};
|
||||
|
||||
#ifdef 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
|
||||
Loading…
Reference in a new issue