diff options
author | Patrick Daly <pdaly@codeaurora.org> | 2015-10-22 19:54:55 -0700 |
---|---|---|
committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 21:18:32 -0700 |
commit | f232630e10f1c1b5f94c450aab500200d4930999 (patch) | |
tree | aa06c68bbe962269e522ed5a6ccdb27c8575f05c | |
parent | 5fda6f8960fcd9529ee7672e795cea9fd0b27d0f (diff) |
soc: qcom: Add debugfs interface for enabling SOC debug events
Other processors/execution environments may support debug events. Implement
an 'smc instruction' based communications protocol and hook it up to
debugfs.
Change-Id: I3a99dfdac5df7edb874bd449d9abc5a929787758
Signed-off-by: Patrick Daly <pdaly@codeaurora.org>
-rw-r--r-- | drivers/soc/qcom/Kconfig | 9 | ||||
-rw-r--r-- | drivers/soc/qcom/Makefile | 1 | ||||
-rw-r--r-- | drivers/soc/qcom/remoteqdss.c | 228 | ||||
-rw-r--r-- | include/soc/qcom/scm.h | 4 |
4 files changed, 241 insertions, 1 deletions
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 99f625d18ea9..d238b725b834 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -582,4 +582,13 @@ config MSM_CORE_CTL_HELPER system load and state. It also supports limiting min and max online CPUs from userspace. +config MSM_REMOTEQDSS + bool "Allow debug tools to enable events on other processors" + depends on MSM_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. + source "drivers/soc/qcom/memshare/Kconfig" diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index d03598a342d8..75f80e19029a 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -79,3 +79,4 @@ obj-$(CONFIG_MSM_AVTIMER) += avtimer.o obj-$(CONFIG_HW_PERF_EVENTS) += perf_event_kryo.o obj-$(CONFIG_MSM_KERNEL_PROTECT) += kernel_protect.o obj-$(CONFIG_MSM_RTB) += msm_rtb-hotplug.o +obj-$(CONFIG_MSM_REMOTEQDSS) += remoteqdss.o diff --git a/drivers/soc/qcom/remoteqdss.c b/drivers/soc/qcom/remoteqdss.c new file mode 100644 index 000000000000..bb10099db83f --- /dev/null +++ b/drivers/soc/qcom/remoteqdss.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <soc/qcom/scm.h> +#include <linux/debugfs.h> +#include <linux/ratelimit.h> + +#define REMOTEQDSS_FLAG_QUIET (BIT(0)) + +static unsigned long remoteqdss_dbg_flags; +module_param_named(dbg_flags, remoteqdss_dbg_flags, ulong, 0644); + +static struct dentry *remoteqdss_dir; + +#define REMOTEQDSS_ERR(fmt, ...) \ + pr_err("%s: " fmt, __func__, ## __VA_ARGS__) + +#define REMOTEQDSS_ERR_CALLER(fmt, ...) \ + pr_err("%pf: " fmt, __builtin_return_address(0), ## __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 { + u8 id; + u64 sw_entity_group; + u64 sw_event_group; + struct dentry *dir; +}; + +/* msgs is a null terminated array */ +static void remoteqdss_err_translation(struct qdss_msg_translation *msgs, + u64 err) +{ + 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", err, msg->msg); + return; + } + } + + REMOTEQDSS_ERR_CALLER("Error 0x%llx\n", err); +} + +/* SCM based devices */ +#define SCM_FILTER_SWTRACE_ID (0x1) +#define SCM_QUERY_SWTRACE_ID (0x2) + +/* 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_scm_query_swtrace(void *priv, u64 *val) +{ + struct remoteqdss_data *data = priv; + int ret; + struct scm_desc desc; + + memset(&desc, 0, sizeof(desc)); + desc.args[0] = data->id; + desc.arginfo = SCM_ARGS(1, SCM_VAL); + + ret = scm_call2( + SCM_SIP_FNID(SCM_SVC_QDSS, SCM_QUERY_SWTRACE_ID), + &desc); + if (ret) + return ret; + + remoteqdss_err_translation(remoteqdss_scm_msgs, desc.ret[0]); + ret = desc.ret[0] ? -EINVAL : 0; + *val = desc.ret[1]; + return ret; +} + +static int remoteqdss_scm_filter_swtrace(void *priv, u64 val) +{ + struct remoteqdss_data *data = priv; + int ret; + struct scm_desc desc; + + memset(&desc, 0, sizeof(desc)); + desc.args[0] = data->id; + desc.args[1] = val; + desc.arginfo = SCM_ARGS(2, SCM_VAL, SCM_VAL); + + ret = scm_call2( + SCM_SIP_FNID(SCM_SVC_QDSS, SCM_FILTER_SWTRACE_ID), + &desc); + if (ret) + return ret; + + remoteqdss_err_translation(remoteqdss_scm_msgs, desc.ret[0]); + ret = desc.ret[0] ? -EINVAL : 0; + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(fops_sw_trace_output, + remoteqdss_scm_query_swtrace, + remoteqdss_scm_filter_swtrace, + "%llu\n"); + +static void __init enumerate_scm_devices(struct dentry *parent) +{ + u64 unused; + int ret; + struct remoteqdss_data *data; + struct dentry *dentry; + + if (!is_scm_armv8()) + return; + + 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("sw_trace_output", S_IRUGO | S_IWUSR, + data->dir, data, &fops_sw_trace_output); + if (IS_ERR_OR_NULL(dentry)) + goto out; + + dentry = debugfs_create_u64("sw_entity_group", S_IRUGO | S_IWUSR, + data->dir, &data->sw_entity_group); + if (IS_ERR_OR_NULL(dentry)) + goto out; + + dentry = debugfs_create_u64("sw_event_group", S_IRUGO | S_IWUSR, + data->dir, &data->sw_event_group); + 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; + + /* + * 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; +} +module_init(remoteqdss_init); diff --git a/include/soc/qcom/scm.h b/include/soc/qcom/scm.h index 97cea4792528..f6791ac9dc32 100644 --- a/include/soc/qcom/scm.h +++ b/include/soc/qcom/scm.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -27,6 +27,8 @@ #define SCM_SVC_HDCP 0x11 #define SCM_SVC_MDTP 0x12 #define SCM_SVC_LMH 0x13 +#define SCM_SVC_SMMU_PROGRAM 0x15 +#define SCM_SVC_QDSS 0x16 #define SCM_SVC_TZSCHEDULER 0xFC #define SCM_FUSE_READ 0x7 |