coresight: add remote etm driver support in upstream implementation

Add remote etm driver in upstream implementation of Coresight
driver.
This change copies coresight-remote-etm.c file from drivers/coresight
(commit c1fe9ac38d93 ("input: touchscreen: correct function and variable
names in ITE tech driver"))
to driver/hwtracing/coresight directory.

Change-Id: I77a1aaf10aaf3f3010ab19d5878bb53dcc504c29
Signed-off-by: Shashank Mittal <mittals@codeaurora.org>
This commit is contained in:
Shashank Mittal 2016-03-14 11:55:11 -07:00 committed by Jeevan Shriram
parent 966e92d21f
commit 93e852721d
5 changed files with 533 additions and 0 deletions

View file

@ -47,6 +47,7 @@ its hardware characteristcs.
- "arm,coresight-cti"
- "qcom,coresight-tpda"
- "qcom,coresight-tpdm"
- "qcom,coresight-remote-etm"
* port or ports: same as above.
@ -97,6 +98,10 @@ its hardware characteristcs.
* qcom,msr-fix-req: boolean, indicating if MSRs need to be programmed
after enabling the subunit.
* Required property for Remote ETMs:
* qcom,inst-id: must be present. QMI instance id for remote ETMs.
Example:
1. Sinks

View file

@ -79,6 +79,22 @@ config CORESIGHT_SOURCE_ETM4X
for instruction level tracing. Depending on the implemented version
data tracing may also be available.
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 bits for Remote processor ETM"
depends on CORESIGHT_REMOTE_ETM
help
Support for enabling separated Remote processor ETM tracing. Depends
on if instance id bit is set.
config CORESIGHT_QCOM_REPLICATOR
bool "Qualcomm CoreSight Replicator driver"
depends on CORESIGHT_LINKS_AND_SINKS

View file

@ -16,3 +16,4 @@ obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o
obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
obj-$(CONFIG_CORESIGHT_TPDA) += coresight-tpda.o
obj-$(CONFIG_CORESIGHT_TPDM) += coresight-tpdm.o
obj-$(CONFIG_CORESIGHT_REMOTE_ETM) += coresight-remote-etm.o

View file

@ -0,0 +1,116 @@
/* Copyright (c) 2014-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.
*/
#ifndef _CORESIGHT_QMI_H
#define _CORESIGHT_QMI_H
#include <soc/qcom/msm_qmi_interface.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 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),
.is_array = 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,
.is_array = NO_ARRAY,
.tlv_type = 0,
.offset = 0,
.ei_array = NULL,
},
};
static struct elem_info coresight_set_etm_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.is_array = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct coresight_set_etm_resp_msg_v01,
resp),
.ei_array = get_qmi_response_type_v01_ei(),
},
{
.data_type = QMI_EOTI,
.elem_len = 0,
.elem_size = 0,
.is_array = NO_ARRAY,
.tlv_type = 0,
.offset = 0,
.ei_array = NULL,
},
};
#endif

View file

@ -0,0 +1,395 @@
/* Copyright (c) 2013-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/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, S_IRUGO
);
struct remote_etm_drvdata {
struct device *dev;
struct coresight_device *csdev;
struct mutex mutex;
struct workqueue_struct *wq;
struct qmi_handle *handle;
struct work_struct work_svc_arrive;
struct work_struct work_svc_exit;
struct work_struct work_rcv_msg;
struct notifier_block nb;
uint32_t inst_id;
struct delayed_work work_delay_enable;
bool enable;
int traceid;
};
static int remote_etm_enable(struct coresight_device *csdev)
{
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 msg_desc req_desc, resp_desc;
int ret;
mutex_lock(&drvdata->mutex);
/*
* 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.
*/
if (!drvdata->handle) {
dev_info(drvdata->dev,
"%s: QMI service unavailable\n", __func__);
ret = -EINVAL;
goto err;
}
req.state = CORESIGHT_ETM_STATE_ENABLED_V01;
req_desc.msg_id = CORESIGHT_QMI_SET_ETM_REQ_V01;
req_desc.max_msg_len = CORESIGHT_QMI_SET_ETM_REQ_MAX_LEN;
req_desc.ei_array = coresight_set_etm_req_msg_v01_ei;
resp_desc.msg_id = CORESIGHT_QMI_SET_ETM_RESP_V01;
resp_desc.max_msg_len = CORESIGHT_QMI_SET_ETM_RESP_MAX_LEN;
resp_desc.ei_array = coresight_set_etm_resp_msg_v01_ei;
ret = qmi_send_req_wait(drvdata->handle, &req_desc, &req, sizeof(req),
&resp_desc, &resp, sizeof(resp), TIMEOUT_MS);
if (ret < 0) {
dev_err(drvdata->dev, "%s: QMI send req failed %d\n", __func__,
ret);
goto err;
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
dev_err(drvdata->dev, "%s: QMI request failed %d %d\n",
__func__, resp.resp.result, resp.resp.error);
ret = -EREMOTEIO;
goto err;
}
drvdata->enable = true;
mutex_unlock(&drvdata->mutex);
dev_info(drvdata->dev, "Remote ETM tracing enabled\n");
return 0;
err:
mutex_unlock(&drvdata->mutex);
return ret;
}
static void remote_etm_disable(struct coresight_device *csdev)
{
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 msg_desc req_desc, resp_desc;
int ret;
mutex_lock(&drvdata->mutex);
if (!drvdata->handle) {
dev_info(drvdata->dev,
"%s: QMI service unavailable\n", __func__);
goto err;
}
req.state = CORESIGHT_ETM_STATE_DISABLED_V01;
req_desc.msg_id = CORESIGHT_QMI_SET_ETM_REQ_V01;
req_desc.max_msg_len = CORESIGHT_QMI_SET_ETM_REQ_MAX_LEN;
req_desc.ei_array = coresight_set_etm_req_msg_v01_ei;
resp_desc.msg_id = CORESIGHT_QMI_SET_ETM_RESP_V01;
resp_desc.max_msg_len = CORESIGHT_QMI_SET_ETM_RESP_MAX_LEN;
resp_desc.ei_array = coresight_set_etm_resp_msg_v01_ei;
ret = qmi_send_req_wait(drvdata->handle, &req_desc, &req, sizeof(req),
&resp_desc, &resp, sizeof(resp), TIMEOUT_MS);
if (ret < 0) {
dev_err(drvdata->dev, "%s: QMI send req failed %d\n", __func__,
ret);
goto err;
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
dev_err(drvdata->dev, "%s: QMI request failed %d %d\n",
__func__, resp.resp.result, resp.resp.error);
goto err;
}
drvdata->enable = false;
mutex_unlock(&drvdata->mutex);
dev_info(drvdata->dev, "Remote ETM tracing disabled\n");
return;
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 void remote_etm_rcv_msg(struct work_struct *work)
{
struct remote_etm_drvdata *drvdata = container_of(work,
struct remote_etm_drvdata,
work_rcv_msg);
if (qmi_recv_msg(drvdata->handle) < 0)
dev_err(drvdata->dev, "%s: Error receiving QMI message\n",
__func__);
}
static void remote_etm_notify(struct qmi_handle *handle,
enum qmi_event_type event, void *notify_priv)
{
struct remote_etm_drvdata *drvdata =
(struct remote_etm_drvdata *)notify_priv;
switch (event) {
case QMI_RECV_MSG:
queue_work(drvdata->wq, &drvdata->work_rcv_msg);
break;
default:
break;
}
}
static void remote_delay_enable_handler(struct work_struct *work)
{
struct remote_etm_drvdata *drvdata = container_of(work,
struct remote_etm_drvdata,
work_delay_enable.work);
coresight_enable(drvdata->csdev);
}
static void remote_etm_svc_arrive(struct work_struct *work)
{
struct remote_etm_drvdata *drvdata = container_of(work,
struct remote_etm_drvdata,
work_svc_arrive);
drvdata->handle = qmi_handle_create(remote_etm_notify, drvdata);
if (!drvdata->handle) {
dev_err(drvdata->dev, "%s: QMI client handle alloc failed\n",
__func__);
return;
}
if (qmi_connect_to_service(drvdata->handle, CORESIGHT_QMI_SVC_ID,
CORESIGHT_QMI_VERSION,
drvdata->inst_id) < 0) {
dev_err(drvdata->dev,
"%s: Could not connect handle to service\n", __func__);
qmi_handle_destroy(drvdata->handle);
drvdata->handle = NULL;
}
mutex_lock(&drvdata->mutex);
if (drvdata->inst_id < sizeof(int)*BITS_PER_BYTE
&& (boot_enable & BIT(drvdata->inst_id))) {
if (!drvdata->enable)
schedule_delayed_work(&drvdata->work_delay_enable,
msecs_to_jiffies(TIMEOUT_MS));
}
mutex_unlock(&drvdata->mutex);
}
static void remote_etm_svc_exit(struct work_struct *work)
{
struct remote_etm_drvdata *drvdata = container_of(work,
struct remote_etm_drvdata,
work_svc_exit);
qmi_handle_destroy(drvdata->handle);
drvdata->handle = NULL;
}
static int remote_etm_svc_event_notify(struct notifier_block *this,
unsigned long event,
void *data)
{
struct remote_etm_drvdata *drvdata = container_of(this,
struct remote_etm_drvdata,
nb);
switch (event) {
case QMI_SERVER_ARRIVE:
queue_work(drvdata->wq, &drvdata->work_svc_arrive);
break;
case QMI_SERVER_EXIT:
queue_work(drvdata->wq, &drvdata->work_svc_exit);
break;
default:
break;
}
return 0;
}
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);
drvdata->nb.notifier_call = remote_etm_svc_event_notify;
drvdata->wq = create_singlethread_workqueue(dev_name(dev));
if (!drvdata->wq)
return -EFAULT;
INIT_WORK(&drvdata->work_svc_arrive, remote_etm_svc_arrive);
INIT_WORK(&drvdata->work_svc_exit, remote_etm_svc_exit);
INIT_WORK(&drvdata->work_rcv_msg, remote_etm_rcv_msg);
INIT_DELAYED_WORK(&drvdata->work_delay_enable,
remote_delay_enable_handler);
ret = qmi_svc_event_notifier_register(CORESIGHT_QMI_SVC_ID,
CORESIGHT_QMI_VERSION,
drvdata->inst_id,
&drvdata->nb);
if (ret < 0)
goto err0;
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 err1;
}
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;
err1:
qmi_svc_event_notifier_unregister(CORESIGHT_QMI_SVC_ID,
CORESIGHT_QMI_VERSION,
drvdata->inst_id,
&drvdata->nb);
err0:
destroy_workqueue(drvdata->wq);
return ret;
}
static int remote_etm_remove(struct platform_device *pdev)
{
struct remote_etm_drvdata *drvdata = platform_get_drvdata(pdev);
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",
.owner = THIS_MODULE,
.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");