From f2c6121f25278fcf0f480daba6627d7f0d7daac4 Mon Sep 17 00:00:00 2001 From: Tingwei Zhang Date: Fri, 6 Dec 2019 03:17:18 -0800 Subject: [PATCH] coresight: Add snapshot of remote qdss driver Add snapshot for coresight remote qdss driver from msm-4.19 commit c953c9b6eb1f ("include/linux: add notifier for fps change"). Make necessary change to adopt scm framework change. Change-Id: Id22379bf396d6533ab1aaa36a75219b1cfc0154b Signed-off-by: Tingwei Zhang --- drivers/soc/qcom/Kconfig | 9 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/remoteqdss.c | 452 ++++++++++++++++++++++++++++++++++ 3 files changed, 462 insertions(+) create mode 100644 drivers/soc/qcom/remoteqdss.c diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index f3d2924f07fc3..d28387d87b2a6 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -315,6 +315,15 @@ config QCOM_MEMORY_DUMP_V2 of deadlocks or cpu hangs these dump regions are captured to give a snapshot of the system at the time of the crash. +config MSM_REMOTEQDSS + bool "Allow debug tools to enable events on other processors" + depends on QCOM_SCM && DEBUG_FS + help + Other onchip processors/execution environments may support debug + events. Provide a sysfs interface for debug tools to dynamically + enable/disable these events. Interface located in + /sys/class/remoteqdss. + config QCOM_SMEM_STATE bool diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 43efcd69ff284..b019bcbe1aaaa 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_MSM_GLINK_SSR) += msm_glink_ssr.o obj-$(CONFIG_QTI_PMIC_GLINK) += pmic_glink.o obj-$(CONFIG_QTI_DDR_STATS_LOG) += ddr_stats.o obj-$(CONFIG_QTI_SYSTEM_PM) += system_pm.o +obj-$(CONFIG_MSM_REMOTEQDSS) += remoteqdss.o obj-$(CONFIG_MSM_SPSS_UTILS) += spss_utils.o obj-$(CONFIG_MSM_IDLE_STATS) += lpm-stats.o obj-$(CONFIG_QTI_RPM_STATS_LOG) += rpmh_master_stat.o diff --git a/drivers/soc/qcom/remoteqdss.c b/drivers/soc/qcom/remoteqdss.c new file mode 100644 index 0000000000000..c8e6318b7633e --- /dev/null +++ b/drivers/soc/qcom/remoteqdss.c @@ -0,0 +1,452 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2019 The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REMOTEQDSS_FLAG_QUIET (BIT(0)) + +static unsigned long remoteqdss_dbg_flags; + +static struct dentry *remoteqdss_dir; + +#define REMOTEQDSS_ERR(fmt, ...) \ + pr_debug("%s: " fmt, __func__, ## __VA_ARGS__) + +#define REMOTEQDSS_ERR_CALLER(fmt, caller, ...) \ + pr_debug("%pf: " fmt, caller, ## __VA_ARGS__) + +struct qdss_msg_translation { + u64 val; + char *msg; +}; + +/* + * id Unique identifier + * sw_entity_group Array index + * sw_event_group Array index + * dir Parent debugfs directory + */ +struct remoteqdss_data { + uint32_t id; + uint32_t sw_entity_group; + uint32_t sw_event_group; + struct dentry *dir; +}; + +static struct device dma_dev; + +/* Allowed message formats */ + +enum remoteqdss_cmd_id { + CMD_ID_QUERY_SWEVENT_TAG, + CMD_ID_FILTER_SWTRACE_STATE, + CMD_ID_QUERY_SWTRACE_STATE, + CMD_ID_FILTER_SWEVENT, + CMD_ID_QUERY_SWEVENT, + CMD_ID_FILTER_SWENTITY, + CMD_ID_QUERY_SWENTITY, +}; + +struct remoteqdss_header_fmt { + uint32_t subsys_id; + uint32_t cmd_id; +}; + +struct remoteqdss_filter_swtrace_state_fmt { + struct remoteqdss_header_fmt h; + uint32_t state; +}; + +struct remoteqdss_filter_swevent_fmt { + struct remoteqdss_header_fmt h; + uint32_t event_group; + uint32_t event_mask; +}; + +struct remoteqdss_query_swevent_fmt { + struct remoteqdss_header_fmt h; + uint32_t event_group; +}; + +struct remoteqdss_filter_swentity_fmt { + struct remoteqdss_header_fmt h; + uint32_t entity_group; + uint32_t entity_mask; +}; + +struct remoteqdss_query_swentity_fmt { + struct remoteqdss_header_fmt h; + uint32_t entity_group; +}; + +/* msgs is a null terminated array */ +static void remoteqdss_err_translation(struct qdss_msg_translation *msgs, + u64 err, const void *caller) +{ + static DEFINE_RATELIMIT_STATE(rl, 5 * HZ, 2); + struct qdss_msg_translation *msg; + + if (!err) + return; + + if (remoteqdss_dbg_flags & REMOTEQDSS_FLAG_QUIET) + return; + + for (msg = msgs; msg->msg; msg++) { + if (err == msg->val && __ratelimit(&rl)) { + REMOTEQDSS_ERR_CALLER("0x%llx: %s\n", caller, err, + msg->msg); + return; + } + } + + REMOTEQDSS_ERR_CALLER("Error 0x%llx\n", caller, err); +} + +/* Shared across all remoteqdss scm functions */ +#define SCM_CMD_ID (0x1) + +/* Response Values */ +#define SCM_CMD_FAIL (0x80) +#define SCM_QDSS_UNAVAILABLE (0x81) +#define SCM_UNINITIALIZED (0x82) +#define SCM_BAD_ARG (0x83) +#define SCM_BAD_SUBSYS (0x85) + +static struct qdss_msg_translation remoteqdss_scm_msgs[] = { + {SCM_CMD_FAIL, + "Command failed"}, + {SCM_QDSS_UNAVAILABLE, + "QDSS not available or cannot turn QDSS (clock) on"}, + {SCM_UNINITIALIZED, + "Tracer not initialized or unable to initialize"}, + {SCM_BAD_ARG, + "Invalid parameter value"}, + {SCM_BAD_SUBSYS, + "Incorrect subsys ID"}, + {} +}; + +static struct remoteqdss_data *create_remoteqdss_data(u32 id) +{ + struct remoteqdss_data *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return NULL; + + data->id = id; + return data; +} + +static void free_remoteqdss_data(struct remoteqdss_data *data) +{ + kfree(data); +} + +static int remoteqdss_do_scm_call(dma_addr_t addr, size_t size, + struct qtee_shm *shm, const void *caller, u64 *out) +{ + int ret; + phys_addr_t paddr = qtee_shmbridge_is_enabled() ? + shm->paddr : dma_to_phys(&dma_dev, addr); + + ret = qcom_scm_qdss_invoke(paddr, size, out); + + remoteqdss_err_translation(remoteqdss_scm_msgs, ret, caller); + return ret ? -EINVAL : 0; +} + +static void *alloc_from_dma_or_shmbridge(size_t size, dma_addr_t *dma_handle, + struct qtee_shm *shm) +{ + int ret; + void *p; + + if (!qtee_shmbridge_is_enabled()) { + p = dma_alloc_coherent(&dma_dev, size, dma_handle, GFP_KERNEL); + } else { + ret = qtee_shmbridge_allocate_shm(size, shm); + p = ret ? NULL : shm->vaddr; + } + return p; +} + +static void free_dma_or_shmbridge(size_t size, void *addr, + dma_addr_t dma_handle, struct qtee_shm *shm) +{ + if (!qtee_shmbridge_is_enabled()) + dma_free_coherent(&dma_dev, size, addr, dma_handle); + else + qtee_shmbridge_free_shm(shm); +} + +static int remoteqdss_scm_query_swtrace(void *priv, u64 *val) +{ + struct remoteqdss_data *data = priv; + int ret; + struct remoteqdss_header_fmt *fmt; + dma_addr_t addr; + struct qtee_shm shm; + + fmt = alloc_from_dma_or_shmbridge(sizeof(*fmt), &addr, &shm); + if (!fmt) + return -ENOMEM; + fmt->subsys_id = data->id; + fmt->cmd_id = CMD_ID_QUERY_SWTRACE_STATE; + + ret = remoteqdss_do_scm_call(addr, sizeof(*fmt), &shm, + __builtin_return_address(0), val); + + free_dma_or_shmbridge(sizeof(*fmt), fmt, addr, &shm); + return ret; +} + +static int remoteqdss_scm_filter_swtrace(void *priv, u64 val) +{ + struct remoteqdss_data *data = priv; + int ret; + struct remoteqdss_filter_swtrace_state_fmt *fmt; + dma_addr_t addr; + struct qtee_shm shm; + + fmt = alloc_from_dma_or_shmbridge(sizeof(*fmt), &addr, &shm); + if (!fmt) + return -ENOMEM; + fmt->h.subsys_id = data->id; + fmt->h.cmd_id = CMD_ID_FILTER_SWTRACE_STATE; + fmt->state = (uint32_t)val; + + ret = remoteqdss_do_scm_call(addr, sizeof(*fmt), &shm, + __builtin_return_address(0), NULL); + + free_dma_or_shmbridge(sizeof(*fmt), fmt, addr, &shm); + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_sw_trace_output, + remoteqdss_scm_query_swtrace, + remoteqdss_scm_filter_swtrace, + "0x%llx\n"); + +static int remoteqdss_scm_query_tag(void *priv, u64 *val) +{ + struct remoteqdss_data *data = priv; + int ret; + struct remoteqdss_header_fmt *fmt; + dma_addr_t addr; + struct qtee_shm shm; + + fmt = alloc_from_dma_or_shmbridge(sizeof(*fmt), &addr, &shm); + if (!fmt) + return -ENOMEM; + fmt->subsys_id = data->id; + fmt->cmd_id = CMD_ID_QUERY_SWEVENT_TAG; + + ret = remoteqdss_do_scm_call(addr, sizeof(*fmt), &shm, + __builtin_return_address(0), val); + + free_dma_or_shmbridge(sizeof(*fmt), fmt, addr, &shm); + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_tag, + remoteqdss_scm_query_tag, + NULL, + "0x%llx\n"); + +static int remoteqdss_scm_query_swevent(void *priv, u64 *val) +{ + struct remoteqdss_data *data = priv; + int ret; + struct remoteqdss_query_swevent_fmt *fmt; + dma_addr_t addr; + struct qtee_shm shm; + + fmt = alloc_from_dma_or_shmbridge(sizeof(*fmt), &addr, &shm); + if (!fmt) + return -ENOMEM; + fmt->h.subsys_id = data->id; + fmt->h.cmd_id = CMD_ID_QUERY_SWEVENT; + fmt->event_group = data->sw_event_group; + + ret = remoteqdss_do_scm_call(addr, sizeof(*fmt), &shm, + __builtin_return_address(0), val); + + free_dma_or_shmbridge(sizeof(*fmt), fmt, addr, &shm); + return ret; +} + +static int remoteqdss_scm_filter_swevent(void *priv, u64 val) +{ + struct remoteqdss_data *data = priv; + int ret; + struct remoteqdss_filter_swevent_fmt *fmt; + dma_addr_t addr; + struct qtee_shm shm; + + fmt = alloc_from_dma_or_shmbridge(sizeof(*fmt), &addr, &shm); + if (!fmt) + return -ENOMEM; + fmt->h.subsys_id = data->id; + fmt->h.cmd_id = CMD_ID_FILTER_SWEVENT; + fmt->event_group = data->sw_event_group; + fmt->event_mask = (uint32_t)val; + + ret = remoteqdss_do_scm_call(addr, sizeof(*fmt), &shm, + __builtin_return_address(0), NULL); + + free_dma_or_shmbridge(sizeof(*fmt), fmt, addr, &shm); + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_swevent, + remoteqdss_scm_query_swevent, + remoteqdss_scm_filter_swevent, + "0x%llx\n"); + +static int remoteqdss_scm_query_swentity(void *priv, u64 *val) +{ + struct remoteqdss_data *data = priv; + int ret; + struct remoteqdss_query_swentity_fmt *fmt; + dma_addr_t addr; + struct qtee_shm shm; + + fmt = alloc_from_dma_or_shmbridge(sizeof(*fmt), &addr, &shm); + if (!fmt) + return -ENOMEM; + fmt->h.subsys_id = data->id; + fmt->h.cmd_id = CMD_ID_QUERY_SWENTITY; + fmt->entity_group = data->sw_entity_group; + + ret = remoteqdss_do_scm_call(addr, sizeof(*fmt), &shm, + __builtin_return_address(0), val); + + free_dma_or_shmbridge(sizeof(*fmt), fmt, addr, &shm); + return ret; +} + +static int remoteqdss_scm_filter_swentity(void *priv, u64 val) +{ + struct remoteqdss_data *data = priv; + int ret; + struct remoteqdss_filter_swentity_fmt *fmt; + dma_addr_t addr; + struct qtee_shm shm; + + fmt = alloc_from_dma_or_shmbridge(sizeof(*fmt), &addr, &shm); + if (!fmt) + return -ENOMEM; + fmt->h.subsys_id = data->id; + fmt->h.cmd_id = CMD_ID_FILTER_SWENTITY; + fmt->entity_group = data->sw_entity_group; + fmt->entity_mask = (uint32_t)val; + + ret = remoteqdss_do_scm_call(addr, sizeof(*fmt), &shm, + __builtin_return_address(0), NULL); + + free_dma_or_shmbridge(sizeof(*fmt), fmt, addr, &shm); + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_swentity, + remoteqdss_scm_query_swentity, + remoteqdss_scm_filter_swentity, + "0x%llx\n"); + +static void __init enumerate_scm_devices(struct dentry *parent) +{ + u64 unused; + int ret; + struct remoteqdss_data *data; + struct dentry *dentry; + + data = create_remoteqdss_data(0); + if (!data) + return; + + /* Assume failure means device not present */ + ret = remoteqdss_scm_query_swtrace(data, &unused); + if (ret) + goto out; + + data->dir = debugfs_create_dir("tz", parent); + if (IS_ERR_OR_NULL(data->dir)) + goto out; + + dentry = debugfs_create_file_unsafe("sw_trace_output", 0644, + data->dir, data, &fops_sw_trace_output); + if (IS_ERR_OR_NULL(dentry)) + goto out; + + dentry = debugfs_create_u32("sw_entity_group", 0644, + data->dir, &data->sw_entity_group); + if (IS_ERR_OR_NULL(dentry)) + goto out; + + dentry = debugfs_create_u32("sw_event_group", 0644, + data->dir, &data->sw_event_group); + if (IS_ERR_OR_NULL(dentry)) + goto out; + + dentry = debugfs_create_file_unsafe("tag", 0444, + data->dir, data, &fops_tag); + if (IS_ERR_OR_NULL(dentry)) + goto out; + + dentry = debugfs_create_file_unsafe("swevent", 0644, + data->dir, data, &fops_swevent); + if (IS_ERR_OR_NULL(dentry)) + goto out; + + dentry = debugfs_create_file_unsafe("swentity", 0644, + data->dir, data, &fops_swentity); + if (IS_ERR_OR_NULL(dentry)) + goto out; + + return; + +out: + debugfs_remove_recursive(data->dir); + free_remoteqdss_data(data); +} + +static int __init remoteqdss_init(void) +{ + unsigned long old_flags = remoteqdss_dbg_flags; + int ret; + + /* Set up DMA */ + arch_setup_dma_ops(&dma_dev, 0, U64_MAX, NULL, false); + ret = dma_coerce_mask_and_coherent(&dma_dev, DMA_BIT_MASK(64)); + if (ret) + return ret; + + /* + * disable normal error messages while checking + * if support is present. + */ + remoteqdss_dbg_flags |= REMOTEQDSS_FLAG_QUIET; + + remoteqdss_dir = debugfs_create_dir("remoteqdss", NULL); + if (!remoteqdss_dir) + return 0; + + enumerate_scm_devices(remoteqdss_dir); + + remoteqdss_dbg_flags = old_flags; + return 0; +} +late_initcall(remoteqdss_init);