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

Commit 6ffc8e15 authored by Mulu He's avatar Mulu He
Browse files

coresight: Remote-etm: Add snapshot of Remote ETM driver



This is a snapshot of the coresight-cti driver as of msm-4.14
commit '7d42028b99d32ad6ec82aa429378fba0280a1c4a'.byte-cntr: Read 64bit rwp
TMC RWP has two 32bit registers which combine to one 64bit register.
Read correct RWP register in byte counter driver.

Change-Id: I0e145d65d0f7155b623e2a79e46fe3ae649a2ee3
Signed-off-by: default avatarMulu He <muluhe@codeaurora.org>
parent 4fb8e084
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -185,4 +185,20 @@ config CORESIGHT_DUMMY
	  CoreSight sources/sinks that are owned and configured by some
	  other subsystem and use Linux drivers to configure rest of trace
	  path.

config CORESIGHT_REMOTE_ETM
	bool "Remote processor ETM trace support"
	depends on MSM_QMI_INTERFACE
	help
	  Enables support for ETM trace collection on remote processor using
	  CoreSight framework. Enabling this will allow turning on ETM
	  tracing on remote processor via sysfs by configuring the required
	  CoreSight components.

config CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE
	int "default enable ETM for Remote processor based on instance id"
	depends on CORESIGHT_REMOTE_ETM
	help
	  Support for enabling separated Remote processor ETM tracing. Depends
	  on if instance id bit is set.
endif
+1 −0
Original line number Diff line number Diff line
@@ -26,3 +26,4 @@ obj-$(CONFIG_CORESIGHT_TPDM) += coresight-tpdm.o
obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o
obj-$(CONFIG_CORESIGHT_HWEVENT) += coresight-hwevent.o
obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o
obj-$(CONFIG_CORESIGHT_REMOTE_ETM) += coresight-remote-etm.o
+109 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
 */

#ifndef _CORESIGHT_QMI_H
#define _CORESIGHT_QMI_H

#include <linux/soc/qcom/qmi.h>

#define CORESIGHT_QMI_SVC_ID			(0x33)
#define CORESIGHT_QMI_VERSION			(1)

#define CORESIGHT_QMI_GET_ETM_REQ_V01		(0x002B)
#define CORESIGHT_QMI_GET_ETM_RESP_V01		(0x002B)
#define CORESIGHT_QMI_SET_ETM_REQ_V01		(0x002C)
#define CORESIGHT_QMI_SET_ETM_RESP_V01		(0x002C)

#define CORESIGHT_QMI_GET_ETM_REQ_MAX_LEN	(0)
#define CORESIGHT_QMI_GET_ETM_RESP_MAX_LEN	(14)
#define CORESIGHT_QMI_SET_ETM_REQ_MAX_LEN	(7)
#define CORESIGHT_QMI_SET_ETM_RESP_MAX_LEN	(7)

#define TIMEOUT_MS				(5000)

enum coresight_etm_state_enum_type_v01 {
	/* To force a 32 bit signed enum. Do not change or use */
	CORESIGHT_ETM_STATE_ENUM_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
	CORESIGHT_ETM_STATE_DISABLED_V01 = 0,
	CORESIGHT_ETM_STATE_ENABLED_V01 = 1,
	CORESIGHT_ETM_STATE_ENUM_TYPE_MAX_ENUM_VAL_01 = INT_MAX,
};

struct coresight_get_etm_req_msg_v01 {
	/*
	 * This element is a placeholder to prevent declaration of
	 * empty struct. Do not change.
	 */
	char __placeholder;
};

struct coresight_get_etm_resp_msg_v01 {
	/* Mandatory */
	/* QMI result Code */
	struct qmi_response_type_v01 resp;

	/* Optional */
	/* ETM output state, must be set to true if state is being passed */
	uint8_t state_valid;
	/* Present when result code is QMI_RESULT_SUCCESS */
	enum coresight_etm_state_enum_type_v01 state;
};

struct coresight_set_etm_req_msg_v01 {
	/* Mandatory */
	/* ETM output state */
	enum coresight_etm_state_enum_type_v01 state;
};

struct coresight_set_etm_resp_msg_v01 {
	/* Mandatory */
	struct qmi_response_type_v01 resp;
};

static struct qmi_elem_info coresight_set_etm_req_msg_v01_ei[] = {
	{
		.data_type = QMI_UNSIGNED_4_BYTE,
		.elem_len  = 1,
		.elem_size = sizeof(enum coresight_etm_state_enum_type_v01),
		.array_type  = NO_ARRAY,
		.tlv_type  = 0x01,
		.offset    = offsetof(struct coresight_set_etm_req_msg_v01,
				      state),
		.ei_array  = NULL,
	},
	{
		.data_type = QMI_EOTI,
		.elem_len  = 0,
		.elem_size = 0,
		.array_type  = NO_ARRAY,
		.tlv_type  = 0,
		.offset    = 0,
		.ei_array  = NULL,
	},
};

static struct qmi_elem_info coresight_set_etm_resp_msg_v01_ei[] = {
	{
		.data_type = QMI_STRUCT,
		.elem_len  = 1,
		.elem_size = sizeof(struct qmi_response_type_v01),
		.array_type  = NO_ARRAY,
		.tlv_type  = 0x02,
		.offset    = offsetof(struct coresight_set_etm_resp_msg_v01,
				      resp),
		.ei_array  = qmi_response_type_v01_ei,
	},
	{
		.data_type = QMI_EOTI,
		.elem_len  = 0,
		.elem_size = 0,
		.array_type  = NO_ARRAY,
		.tlv_type  = 0,
		.offset    = 0,
		.ei_array  = NULL,
	},
};

#endif
+335 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/sysfs.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/coresight.h>
#include "coresight-qmi.h"

#define REMOTE_ETM_TRACE_ID_START	192

#ifdef CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE
static int boot_enable = CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE;
#else
static int boot_enable;
#endif

module_param_named(
	boot_enable, boot_enable, int, 0444
);

struct remote_etm_drvdata {
	struct device			*dev;
	struct coresight_device		*csdev;
	struct mutex			mutex;
	struct qmi_handle		handle;
	uint32_t			inst_id;
	bool				enable;
	int				traceid;
	bool service_connected;
	struct sockaddr_qrtr s_addr;
};

static int service_remote_etm_new_server(struct qmi_handle *qmi,
		struct qmi_service *svc)
{
	struct remote_etm_drvdata *drvdata = container_of(qmi,
					struct remote_etm_drvdata, handle);

	drvdata->s_addr.sq_family = AF_QIPCRTR;
	drvdata->s_addr.sq_node = svc->node;
	drvdata->s_addr.sq_port = svc->port;
	drvdata->service_connected = true;
	dev_info(drvdata->dev,
		"Connection established between QMI handle and %d service\n",
		drvdata->inst_id);

	return 0;
}

static void service_remote_etm_del_server(struct qmi_handle *qmi,
		struct qmi_service *svc)
{
	struct remote_etm_drvdata *drvdata = container_of(qmi,
					struct remote_etm_drvdata, handle);
	drvdata->service_connected = false;
	dev_info(drvdata->dev,
		"Connection disconnected between QMI handle and %d service\n",
		drvdata->inst_id);
}

static struct qmi_ops server_ops = {
	.new_server = service_remote_etm_new_server,
	.del_server = service_remote_etm_del_server,
};

static int remote_etm_enable(struct coresight_device *csdev,
			     struct perf_event *event, u32 mode)
{
	struct remote_etm_drvdata *drvdata =
		dev_get_drvdata(csdev->dev.parent);
	struct coresight_set_etm_req_msg_v01 req;
	struct coresight_set_etm_resp_msg_v01 resp = { { 0, 0 } };
	struct qmi_txn txn;
	int ret;

	mutex_lock(&drvdata->mutex);

	if (!drvdata->service_connected) {
		dev_err(drvdata->dev, "QMI service not connected!\n");
		ret = EINVAL;
		goto err;
	}
	/*
	 * The QMI handle may be NULL in the following scenarios:
	 * 1. QMI service is not present
	 * 2. QMI service is present but attempt to enable remote ETM is earlier
	 *    than service is ready to handle request
	 * 3. Connection between QMI client and QMI service failed
	 *
	 * Enable CoreSight without processing further QMI commands which
	 * provides the option to enable remote ETM by other means.
	 */
	req.state = CORESIGHT_ETM_STATE_ENABLED_V01;

	ret = qmi_txn_init(&drvdata->handle, &txn,
			coresight_set_etm_resp_msg_v01_ei,
			&resp);

	if (ret < 0) {
		dev_err(drvdata->dev, "QMI tx init failed , ret:%d\n",
				ret);
		goto err;
	}

	ret = qmi_send_request(&drvdata->handle, &drvdata->s_addr,
			&txn, CORESIGHT_QMI_SET_ETM_REQ_V01,
			CORESIGHT_QMI_SET_ETM_REQ_MAX_LEN,
			coresight_set_etm_req_msg_v01_ei,
			&req);
	if (ret < 0) {
		dev_err(drvdata->dev, "QMI send ACK failed, ret:%d\n",
				ret);
		qmi_txn_cancel(&txn);
		goto err;
	}

	ret = qmi_txn_wait(&txn, msecs_to_jiffies(TIMEOUT_MS));
	if (ret < 0) {
		dev_err(drvdata->dev, "QMI qmi txn wait failed, ret:%d\n",
				ret);
		goto err;
	}

	/* Check the response */
	if (resp.resp.result != QMI_RESULT_SUCCESS_V01)
		dev_err(drvdata->dev, "QMI request failed 0x%x\n",
				resp.resp.error);

	drvdata->enable = true;
	mutex_unlock(&drvdata->mutex);

	dev_info(drvdata->dev, "Remote ETM tracing enabled for instance %d\n",
				drvdata->inst_id);
	return 0;
err:
	mutex_unlock(&drvdata->mutex);
	return ret;
}

static void remote_etm_disable(struct coresight_device *csdev,
			       struct perf_event *event)
{
	struct remote_etm_drvdata *drvdata =
		 dev_get_drvdata(csdev->dev.parent);
	struct coresight_set_etm_req_msg_v01 req;
	struct coresight_set_etm_resp_msg_v01 resp = { { 0, 0 } };
	struct qmi_txn txn;
	int ret;

	mutex_lock(&drvdata->mutex);
	if (!drvdata->service_connected) {
		dev_err(drvdata->dev, "QMI service not connected!\n");
		goto err;
	}

	req.state = CORESIGHT_ETM_STATE_DISABLED_V01;

	ret = qmi_txn_init(&drvdata->handle, &txn,
			coresight_set_etm_resp_msg_v01_ei,
			&resp);

	if (ret < 0) {
		dev_err(drvdata->dev, "QMI tx init failed , ret:%d\n",
				ret);
		goto err;
	}

	ret = qmi_send_request(&drvdata->handle, &drvdata->s_addr,
			&txn, CORESIGHT_QMI_SET_ETM_REQ_V01,
			CORESIGHT_QMI_SET_ETM_REQ_MAX_LEN,
			coresight_set_etm_req_msg_v01_ei,
			&req);
	if (ret < 0) {
		dev_err(drvdata->dev, "QMI send req failed, ret:%d\n",
				 ret);
		qmi_txn_cancel(&txn);
		goto err;
	}

	ret = qmi_txn_wait(&txn, msecs_to_jiffies(TIMEOUT_MS));
	if (ret < 0) {
		dev_err(drvdata->dev, "QMI qmi txn wait failed, ret:%d\n",
				ret);
		goto err;
	}

	/* Check the response */
	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
		dev_err(drvdata->dev, "QMI request failed 0x%x\n",
				resp.resp.error);
		goto err;
	}

	drvdata->enable = false;
	dev_info(drvdata->dev, "Remote ETM tracing disabled for instance %d\n",
				drvdata->inst_id);
err:
	mutex_unlock(&drvdata->mutex);
}

static int remote_etm_trace_id(struct coresight_device *csdev)
{
	struct remote_etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);

	return drvdata->traceid;
}

static const struct coresight_ops_source remote_etm_source_ops = {
	.trace_id	= remote_etm_trace_id,
	.enable		= remote_etm_enable,
	.disable	= remote_etm_disable,
};

static const struct coresight_ops remote_cs_ops = {
	.source_ops	= &remote_etm_source_ops,
};

static int remote_etm_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct coresight_platform_data *pdata;
	struct remote_etm_drvdata *drvdata;
	struct coresight_desc *desc;
	int ret;
	static int traceid = REMOTE_ETM_TRACE_ID_START;

	pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
	if (IS_ERR(pdata))
		return PTR_ERR(pdata);
	pdev->dev.platform_data = pdata;

	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
	if (!drvdata)
		return -ENOMEM;

	drvdata->dev = &pdev->dev;
	platform_set_drvdata(pdev, drvdata);

	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
	if (!desc)
		return -ENOMEM;

	ret = of_property_read_u32(pdev->dev.of_node, "qcom,inst-id",
			&drvdata->inst_id);
	if (ret)
		return ret;

	mutex_init(&drvdata->mutex);

	ret = qmi_handle_init(&drvdata->handle,
			CORESIGHT_QMI_SET_ETM_REQ_MAX_LEN,
			&server_ops, NULL);
	if (ret < 0) {
		dev_err(dev, "Remote ETM client init failed ret:%d\n", ret);
		return ret;
	}

	qmi_add_lookup(&drvdata->handle,
			CORESIGHT_QMI_SVC_ID,
			CORESIGHT_QMI_VERSION,
			drvdata->inst_id);

	drvdata->traceid = traceid++;

	desc->type = CORESIGHT_DEV_TYPE_SOURCE;
	desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
	desc->ops = &remote_cs_ops;
	desc->pdata = pdev->dev.platform_data;
	desc->dev = &pdev->dev;
	drvdata->csdev = coresight_register(desc);
	if (IS_ERR(drvdata->csdev)) {
		ret = PTR_ERR(drvdata->csdev);
		goto err;
	}
	dev_info(dev, "Remote ETM initialized\n");

	if (drvdata->inst_id >= sizeof(int)*BITS_PER_BYTE)
		dev_err(dev, "inst_id greater than boot_enable bit mask\n");
	else if (boot_enable & BIT(drvdata->inst_id))
		coresight_enable(drvdata->csdev);

	return 0;
err:
	qmi_handle_release(&drvdata->handle);
	return ret;
}

static int remote_etm_remove(struct platform_device *pdev)
{
	struct remote_etm_drvdata *drvdata = platform_get_drvdata(pdev);

	qmi_handle_release(&drvdata->handle);
	coresight_unregister(drvdata->csdev);
	return 0;
}

static const struct of_device_id remote_etm_match[] = {
	{.compatible = "qcom,coresight-remote-etm"},
	{}
};

static struct platform_driver remote_etm_driver = {
	.probe          = remote_etm_probe,
	.remove         = remote_etm_remove,
	.driver         = {
		.name   = "coresight-remote-etm",
		.of_match_table = remote_etm_match,
	},
};

int __init remote_etm_init(void)
{
	return platform_driver_register(&remote_etm_driver);
}
module_init(remote_etm_init);

void __exit remote_etm_exit(void)
{
	platform_driver_unregister(&remote_etm_driver);
}
module_exit(remote_etm_exit);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CoreSight Remote ETM driver");