From 93e852721d8ae4f918e7830f9ee872ef6c9e091c Mon Sep 17 00:00:00 2001 From: Shashank Mittal Date: Mon, 14 Mar 2016 11:55:11 -0700 Subject: [PATCH] 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 --- .../devicetree/bindings/arm/coresight.txt | 5 + drivers/hwtracing/coresight/Kconfig | 16 + drivers/hwtracing/coresight/Makefile | 1 + drivers/hwtracing/coresight/coresight-qmi.h | 116 +++++ .../coresight/coresight-remote-etm.c | 395 ++++++++++++++++++ 5 files changed, 533 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-qmi.h create mode 100644 drivers/hwtracing/coresight/coresight-remote-etm.c diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index 12a7a6633f5b..4ea7d165090a 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -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 diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index d440db8ab78a..bc40a1496214 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -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 diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 33f8680c06f4..f5c0fc456747 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -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 diff --git a/drivers/hwtracing/coresight/coresight-qmi.h b/drivers/hwtracing/coresight/coresight-qmi.h new file mode 100644 index 000000000000..baf408a75fcd --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-qmi.h @@ -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 + +#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 diff --git a/drivers/hwtracing/coresight/coresight-remote-etm.c b/drivers/hwtracing/coresight/coresight-remote-etm.c new file mode 100644 index 000000000000..9015015381d5 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-remote-etm.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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");