Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 43e27702 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "coresight: Add snapshot of remote qdss driver"

parents 72cb2da4 f2c6121f
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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

+1 −0
Original line number Diff line number Diff line
@@ -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
+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);