Loading drivers/soc/qcom/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -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 Loading drivers/soc/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading drivers/soc/qcom/remoteqdss.c 0 → 100644 +452 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015-2019 The Linux Foundation. All rights reserved. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/qtee_shmbridge.h> #include <linux/qcom_scm.h> #include <linux/debugfs.h> #include <linux/ratelimit.h> #include <linux/dma-direct.h> #include <linux/dma-mapping.h> #include <asm/cacheflush.h> #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); Loading
drivers/soc/qcom/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
drivers/soc/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
drivers/soc/qcom/remoteqdss.c 0 → 100644 +452 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015-2019 The Linux Foundation. All rights reserved. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/qtee_shmbridge.h> #include <linux/qcom_scm.h> #include <linux/debugfs.h> #include <linux/ratelimit.h> #include <linux/dma-direct.h> #include <linux/dma-mapping.h> #include <asm/cacheflush.h> #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);