CTIs are defined in the device tree and associated with other CoreSight devices. The core CoreSight code has been modified to enable the registration of the CTI devices on the same bus as the other CoreSight components, but as these are not actually trace generation / capture devices, they are not part of the Coresight path when generating trace.
However, the definition of the standard CoreSight device has been extended to include a reference to an associated CTI device, and the enable / disable trace path operations will auto enable/disable any associated CTI devices at the same time.
Programming is at present via sysfs - a full API is provided to utilise the hardware capabilities. As CTI devices are unprogrammed by default, the auto enable describe above will have no effect until explicit programming takes place.
A set of device tree bindings specific to the CTI topology has been defined.
Documentation has been updated to describe both the CTI hardware, its use and programming in sysfs, and the new dts bindings required.
Tested on DB410 board, 5.1-rc5
Changes since v1: 1) Significant restructuring of the source code. Adds cti-sysfs file and cti device tree file. Patches add per feature rather than per source file. 2) CPU type power event handling for hotplug moved to CoreSight core, with generic registration interface provided for all CPU bound CS devices to use. 3) CTI signal interconnection details in sysfs now generated dynamically from connection lists in driver. This to fix issue with multi-line sysfs output in previous version. 4) Full device tree bindings for DB410 and Juno provided (to the extent that CTI information is available). 5) AMBA driver update for UCI IDs are now upstream so no longer included in this set.
Mike Leach (13): drivers: coresight: cti: Initial CoreSight CTI Driver drivers: coresight: cti: Adds sysfs functionality to CTI driver. drivers: coresight: cti: Add device tree support for v8 arch CTI drivers: coresight: cti: Add device tree support for impdef CTI. drivers: coresight: cti: Enable CTI associated with devices. drivers: coresight: cti: Add connection information to sysfs drivers: coresight: cti: Add CoreSight cpu power notifications. devicetree: bindings: Documentation for CTI bindings. devicetree: bindings: Add header file with CTI trigger signal type constants. drivers: dts: Add CTI options for qcom msm8916 drivers: dts: Juno platform - add CTI entries to device tree. docs: coresight: Update documentation for CoreSight to cover CTI. docs: sysfs: coresight: Add sysfs documentation for CTI
.../testing/sysfs-bus-coresight-devices-cti | 225 +++ .../bindings/arm/coresight-ect-cti.txt | 203 +++ .../devicetree/bindings/arm/coresight.txt | 7 + Documentation/trace/coresight.txt | 139 ++ arch/arm64/boot/dts/arm/juno-base.dtsi | 149 +- arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi | 31 +- arch/arm64/boot/dts/arm/juno-r1.dts | 25 + arch/arm64/boot/dts/arm/juno-r2.dts | 25 + arch/arm64/boot/dts/arm/juno.dts | 25 + arch/arm64/boot/dts/qcom/msm8916.dtsi | 102 +- drivers/hwtracing/coresight/Kconfig | 13 + drivers/hwtracing/coresight/Makefile | 4 + .../hwtracing/coresight/coresight-cti-sysfs.c | 1250 +++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.c | 853 +++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 280 ++++ drivers/hwtracing/coresight/coresight-priv.h | 37 + drivers/hwtracing/coresight/coresight.c | 185 ++- .../hwtracing/coresight/of_coresight-cti.c | 447 ++++++ include/dt-bindings/arm/coresight-cti-dt.h | 36 + include/linux/coresight.h | 30 + 20 files changed, 4056 insertions(+), 10 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-coresight-devices-cti create mode 100644 Documentation/devicetree/bindings/arm/coresight-ect-cti.txt create mode 100644 drivers/hwtracing/coresight/coresight-cti-sysfs.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.h create mode 100644 drivers/hwtracing/coresight/of_coresight-cti.c create mode 100644 include/dt-bindings/arm/coresight-cti-dt.h
This introduces a baseline CTI driver and associated configuration files.
Driver will probe for the device on the AMBA bus, and load the CTI driver on CoreSight ID match to CTI IDs in tables.
Initial sysfs support for enable / disable provided.
Default CTI interconnection data is generated based on hardware register signal counts, with no additional connection information
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/Kconfig | 13 + drivers/hwtracing/coresight/Makefile | 4 + .../hwtracing/coresight/coresight-cti-sysfs.c | 97 ++++ drivers/hwtracing/coresight/coresight-cti.c | 522 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 228 ++++++++ drivers/hwtracing/coresight/coresight-priv.h | 7 + drivers/hwtracing/coresight/coresight.c | 8 + .../hwtracing/coresight/of_coresight-cti.c | 81 +++ include/linux/coresight.h | 26 + 9 files changed, 986 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti-sysfs.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.h create mode 100644 drivers/hwtracing/coresight/of_coresight-cti.c
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index ad34380cac49..9f1cac64f357 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -114,4 +114,17 @@ config CORESIGHT_CPU_DEBUG properly, please refer Documentation/trace/coresight-cpu-debug.txt for detailed description and the example for usage.
+config CORESIGHT_CTI + bool "CoreSight Cross Trigger Interface (CTI) driver" + depends on ARM || ARM64 + select CORESIGHT_LINKS_AND_SINKS + help + This driver provides support for CoreSight CTI and CTM components. + These provide hardware triggering events between CoreSight trace + source and sink components. These can be used to halt trace or + inject events into the trace stream. CTI also provides a software + control to trigger same halt events. This can provide fast trace + halt compared to disabling sources and sinks normally in driver + software. + endif diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 41870ded51a3..77402cfc7174 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -19,3 +19,7 @@ obj-$(CONFIG_CORESIGHT_DYNAMIC_REPLICATOR) += coresight-dynamic-replicator.o obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o +obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o \ + of_coresight-cti.o \ + coresight-cti-sysfs.o + diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c new file mode 100644 index 000000000000..9b6749621dcb --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Linaro Limited, All rights reserved. + * Author: Mike Leach mike.leach@linaro.org + */ + +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/cpu.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/pm_wakeup.h> +#include <linux/seq_file.h> +#include <linux/smp.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/uaccess.h> + +#include "coresight-cti.h" + +/* basic attributes */ +static ssize_t enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int enable_req; + bool enabled, powered, cpuid; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + ssize_t size = 0; + + enable_req = atomic_read(&drvdata->config.enable_req_count); + spin_lock(&drvdata->spinlock); + powered = drvdata->config.hw_powered; + enabled = drvdata->config.hw_enabled; + cpuid = drvdata->ctidev.cpu; + spin_unlock(&drvdata->spinlock); + + if (powered) { + size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n", + enabled ? "enabled" : "disabled", cpuid); + } else if (cpuid >= 0) { + size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n", + enable_req ? "enable req" : "disable req", cpuid); + } else { + size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n", + enabled ? "enabled" : "disabled"); + } + return size; +} + +static ssize_t enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret = 0; + unsigned long val; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val) + ret = cti_enable(drvdata->csdev, NULL); + else + ret = cti_disable(drvdata->csdev, NULL); + if (ret) + return ret; + return size; +} +static DEVICE_ATTR_RW(enable); + +/* attribute and group sysfs tables. */ +static struct attribute *coresight_cti_attrs[] = { + &dev_attr_enable.attr, + NULL, +}; + +static const struct attribute_group coresight_cti_group = { + .attrs = coresight_cti_attrs, +}; + +const struct attribute_group *coresight_cti_groups[] = { + &coresight_cti_group, + NULL, +}; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c new file mode 100644 index 000000000000..0a0fb6d48237 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 Linaro Limited, All rights reserved. + * Author: Mike Leach mike.leach@linaro.org + */ + +#include <linux/amba/bus.h> +#include <linux/clk.h> +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/cpu.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/pm_wakeup.h> +#include <linux/seq_file.h> +#include <linux/smp.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <asm/local.h> +#include <asm/sections.h> + +#include "coresight-cti.h" + +/** + * CTI devices can be associated with a PE, or be connected to CoreSight + * hardware. We have a list of all CTIs, and a subset array associated + * with individual PEs for pwr management as they will power up in the + * PE power domain. + * + * At this point we assume that the none CPU CTIs are always powered as + * we do with sinks etc. + * + * We leave the client to figure out if all the CTIs are interconnected with + * the same CTM, in general this is the case but does not always have to be. + */ +struct ect_node { + struct cti_drvdata *cti_drv; + struct list_head next; +}; + +/* net of CTI devices connected via CTM */ +LIST_HEAD(ect_net); + +/* protect the list */ +static DEFINE_MUTEX(ect_mutex); + +/* quick reference for CPU CTIs */ +static struct cti_drvdata *cti_cpu_drv[NR_CPUS]; + +/* number of registered CTI devices */ +static int cti_count; + +/* number of cpu associated CTI devices in use */ +static int cti_cpu_count; + +#define csdev_to_cti_drvdata(csdev) \ + dev_get_drvdata(csdev->dev.parent) + +/* write set of regs to hardware - call with spinlock claimed */ +static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +{ + struct cti_config *config = &drvdata->config; + int i; + + CS_UNLOCK(drvdata->base); + + /* disable CTI before writing registers */ + writel_relaxed(0, drvdata->base + CTICONTROL); + + /* write the CTI trigger registers */ + for (i = 0; i < config->nr_trig_max; i++) { + writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i)); + writel_relaxed(config->ctiouten[i], + drvdata->base + CTIOUTEN(i)); + } + + /* other regs */ + writel_relaxed(config->ctigate, drvdata->base + CTIGATE); + writel_relaxed(config->asicctl, drvdata->base + ASICCTL); + writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET); + + /* re-enable CTI */ + writel_relaxed(1, drvdata->base + CTICONTROL); + + CS_LOCK(drvdata->base); +} + +static void cti_enable_hw_smp_call(void *info) +{ + struct cti_drvdata *drvdata = info; + + cti_write_all_hw_regs(drvdata); +} + +/* write regs to hardware and enable */ +static int cti_enable_hw(void *info) +{ + struct cti_drvdata *drvdata = info; + struct cti_config *config = &drvdata->config; + int rc = 0; + + pm_runtime_get_sync(drvdata->dev); + spin_lock(&drvdata->spinlock); + + /* no need to do anything if enabled or unpowered*/ + if (config->hw_enabled || !config->hw_powered) + goto cti_not_enabled; + + /* claim the device */ + rc = coresight_claim_device(drvdata->base); + if (rc) + goto cti_not_enabled; + + if (drvdata->ctidev.cpu >= 0) { + dev_info(drvdata->dev, "cti enable smp call for cpu %d\n", + drvdata->ctidev.cpu); + rc = smp_call_function_single(drvdata->ctidev.cpu, + cti_enable_hw_smp_call, + drvdata, 1); + if (rc) + goto cti_not_enabled; + } else { + dev_info(drvdata->dev, "cti enable not cpu call\n"); + cti_write_all_hw_regs(drvdata); + } + + config->hw_enabled = true; + spin_unlock(&drvdata->spinlock); + return rc; + + /* not enabled in this call */ +cti_not_enabled: + spin_unlock(&drvdata->spinlock); + pm_runtime_put(drvdata->dev); + return rc; +} + +/* disable hardware */ +static int cti_disable_hw(void *info) +{ + struct cti_drvdata *drvdata = info; + struct cti_config *config = &drvdata->config; + + spin_lock(&drvdata->spinlock); + /* no need to do anything if disabled or cpu unpowered*/ + if (!config->hw_enabled || !config->hw_powered) { + spin_unlock(&drvdata->spinlock); + return 0; + } + + CS_UNLOCK(drvdata->base); + + /* disable CTI */ + writel_relaxed(0, drvdata->base + CTICONTROL); + config->hw_enabled = false; + + coresight_disclaim_device_unlocked(drvdata->base); + CS_LOCK(drvdata->base); + spin_unlock(&drvdata->spinlock); + pm_runtime_put(drvdata->dev); + return 0; +} + +static void cti_set_default_config(struct cti_config *config) +{ + /* Most regs default to 0 as zalloc'ed except...*/ + config->trig_filter_enable = 1; + config->ctigate = ((u32)0x1 << config->nr_ctm_channels) - 1; + atomic_set(&config->enable_req_count, 0); +} + +/* + * Add a connection entry to the list of connections for this + * CTI device. + */ +static int cti_add_connection_entry(struct cti_drvdata *drvdata, + struct cti_trig_grp *in_info, + struct cti_trig_grp *out_info, + struct coresight_device *csdev, + const char *assoc_dev_name) +{ + struct cti_trig_con *tc; + struct cti_device *cti_dev = &drvdata->ctidev; + + tc = kzalloc(sizeof(struct cti_trig_con), GFP_KERNEL); + if (tc == 0) + return -ENOMEM; + + tc->con_dev = csdev; + /* + * Prefer actual associated CS device dev name to supplied value - + * which is likely to be node name / other conn name. + */ + if (csdev) + tc->con_dev_name = kstrdup(dev_name(&csdev->dev), GFP_KERNEL); + else if (assoc_dev_name != NULL) + tc->con_dev_name = kstrdup(assoc_dev_name, GFP_KERNEL); + tc->con_in = in_info; + tc->con_out = out_info; + list_add_tail(&tc->node, &cti_dev->trig_cons); + cti_dev->nr_trig_con++; + + /* add connection usage bit info to overall info */ + drvdata->config.trig_in_use |= in_info->used_mask; + drvdata->config.trig_out_use |= out_info->used_mask; + + return 0; +} + +/* + * Add a default connection if nothing else is specified. + * single connection based on max in/out info, no assoc device + */ +int cti_add_default_connection(struct cti_drvdata *drvdata) +{ + int ret = 0, idx; + struct cti_trig_grp *in = 0, *out = 0; + int n_trigs = drvdata->config.nr_trig_max; + u32 n_trig_mask = (0x1 << n_trigs) - 1; + int *in_sig_types = 0, *out_sig_types = 0; + + in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!in) { + ret = -ENOMEM; + goto conn_add_err; + } + + out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!out) { + ret = -ENOMEM; + goto conn_add_err; + } + + in_sig_types = kzalloc(sizeof(int) * n_trigs, GFP_KERNEL); + if (!in_sig_types) { + ret = -ENOMEM; + goto conn_add_err; + } + + out_sig_types = kzalloc(sizeof(int) * n_trigs, GFP_KERNEL); + if (!out_sig_types) { + ret = -ENOMEM; + goto conn_add_err; + } + + /* signal types marked as default CTITRIG_GEN_IO */ + for (idx = 0; idx < n_trigs; idx++) { + in_sig_types[idx] = CTITRIG_GEN_IO; + out_sig_types[idx] = CTITRIG_GEN_IO; + } + + /* + * Assume max trigs for in and out, + * all used, default sig types allocated + */ + in->nr_sigs = n_trigs; + in->used_mask = n_trig_mask; + in->sig_types = in_sig_types; + out->nr_sigs = n_trigs; + out->used_mask = n_trig_mask; + out->sig_types = out_sig_types; + ret = cti_add_connection_entry(drvdata, in, out, NULL, "default"); + if (ret) + goto conn_add_err; + + return ret; + +conn_add_err: + kfree(in); + kfree(out); + kfree(out_sig_types); + kfree(in_sig_types); + return ret; +} + +/** cti ect operations **/ +int cti_enable(struct coresight_device *csdev, void *__unused) +{ + int rc; + struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev); + + atomic_inc(&drvdata->config.enable_req_count); + rc = cti_enable_hw(drvdata); + if (rc) + atomic_dec(&drvdata->config.enable_req_count); + + return rc; +} + +int cti_disable(struct coresight_device *csdev, void *__unused) +{ + int rc = 0; + struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev); + + if (!atomic_dec_return(&drvdata->config.enable_req_count)) { + rc = cti_disable_hw(drvdata); + if (rc) + atomic_inc(&drvdata->config.enable_req_count); + } + return rc; +} + +const struct coresight_ops_ect cti_ops_ect = { + .enable = cti_enable, + .disable = cti_disable, +}; + +const struct coresight_ops cti_ops = { + .ect_ops = &cti_ops_ect, +}; + +static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{ + int ret = 0; + u32 devid; + void __iomem *base; + struct device *dev = &adev->dev; + struct cti_drvdata *drvdata = NULL; + struct coresight_desc cti_desc; + struct coresight_platform_data *pdata = NULL; + struct resource *res = &adev->res; + struct device_node *np = adev->dev.of_node; + struct ect_node *ect_nd = NULL; + + /* boilerplate code to set up the basics */ + if (np) { + pdata = of_get_coresight_cti_platform_data(dev, np); + if (IS_ERR(pdata)) { + dev_info(dev, "of_get_coresight_ect_platform err\n"); + return PTR_ERR(pdata); + } + dev->platform_data = pdata; + } + + /* node to keep track of CTI net */ + ect_nd = devm_kzalloc(dev, sizeof(struct ect_node), GFP_KERNEL); + if (!ect_nd) { + ret = -ENOMEM; + goto err_out; + } + + /* driver data*/ + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) { + ret = -ENOMEM; + dev_info(dev, "%s, mem err\n", __func__); + goto err_out; + } + + /* Validity for the resource is already checked by the AMBA core */ + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) { + ret = PTR_ERR(base); + dev_info(dev, "%s, remap err\n", __func__); + goto err_out; + } + drvdata->base = base; + + /* links between dev and drvdata*/ + drvdata->dev = dev; + dev_set_drvdata(dev, drvdata); + + /* default CTI device info */ + drvdata->ctidev.cpu = pdata->cpu; + drvdata->ctidev.nr_trig_con = 0; + drvdata->ctidev.ctm_id = 0; + INIT_LIST_HEAD(&drvdata->ctidev.trig_cons); + + spin_lock_init(&drvdata->spinlock); + + /* look at the HW DEVID register for some of the HW settings */ + devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); + drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8); + /* + * no current hardware should exceed this, but protect the driver + * in case of fault / out of spec hw + */ + if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) { + dev_warn_once(dev, + "Limiting HW MaxTrig value(%d) to driver max(%d)\n", + drvdata->config.nr_trig_max, CTIINOUTEN_MAX); + drvdata->config.nr_trig_max = CTIINOUTEN_MAX; + } + + drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16); + + /* additional parse the .dts for connections and signals */ + of_cti_get_hw_data(dev, np, drvdata); + + /* initialise default driver values */ + cti_set_default_config(&drvdata->config); + + /* setup cpu related CTI devices, otherwise assume powered */ + drvdata->config.hw_powered = true; + + /* set up coresight component description */ + cti_desc.pdata = pdata; + cti_desc.type = CORESIGHT_DEV_TYPE_ECT; + cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI; + cti_desc.ops = &cti_ops; + cti_desc.groups = coresight_cti_groups; + cti_desc.dev = dev; + drvdata->csdev = coresight_register(&cti_desc); + if (IS_ERR(drvdata->csdev)) { + ret = PTR_ERR(drvdata->csdev); + pr_err("%s: CS register failed\n", pdata->name); + goto err_out; + } + + /* add to list of CTI devices */ + mutex_lock(&ect_mutex); + ect_nd->cti_drv = drvdata; + list_add(&ect_nd->next, &ect_net); + /* if cpu bound add to CPU array */ + if (drvdata->ctidev.cpu >= 0) { + cti_cpu_drv[drvdata->ctidev.cpu] = drvdata; + cti_cpu_count++; + } + mutex_unlock(&ect_mutex); + + /* all done - dec pm refcount */ + pm_runtime_put(&adev->dev); + dev_info(dev, "%s: initialized\n", pdata->name); + cti_count++; + return 0; + +err_out: + kfree(drvdata); + kfree(ect_nd); + return ret; +} + +/* free all connection info from the driver */ +void cti_free_conn_info(struct cti_drvdata *drvdata) +{ + struct cti_trig_con *tc, *tc_tmp; + + list_for_each_entry_safe(tc, tc_tmp, + &drvdata->ctidev.trig_cons, node) { + kfree(tc->con_in->sig_types); + kfree(tc->con_in); + kfree(tc->con_out->sig_types); + kfree(tc->con_out); + kfree(tc->con_dev_name); + list_del(&tc->node); + kfree(tc); + } +} + +/* + * Free up CTI specific resources + * - called from coresight_device_release on coresight_unregister. + */ +void cti_device_release(struct device *dev) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct ect_node *ect_item, *ect_tmp; + + /* free up resources associated with the cti connections */ + if (drvdata->ctidev.cpu >= 0) + cti_cpu_drv[drvdata->ctidev.cpu] = 0; + + /* clear the connection list items */ + cti_free_conn_info(drvdata); + + /* remove from the list */ + mutex_lock(&ect_mutex); + list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) { + if (ect_item->cti_drv == drvdata) { + list_del(&ect_item->next); + kfree(ect_item); + goto ect_list_item_removed; + } + } +ect_list_item_removed: + mutex_unlock(&ect_mutex); + + kfree(drvdata); +} +EXPORT_SYMBOL_GPL(cti_device_release); + +static struct amba_cs_uci_id uci_id_cti[] = { + { + /* CTI UCI data */ + .devarch = 0x47701a14, /* devarch value for CTI v2 */ + .devarch_mask = 0xfff0ffff, + .devtype = 0x00000014, /* maj(0x4-debug) min(0x1-ECT) */ + } +}; + +static const struct amba_id cti_ids[] = { + CS_AMBA_ID(0x000bb906), /* Coresight CTI (SoC 400), C-A72, C-A57 */ + CS_AMBA_ID(0x000bb922), /* CTI - C-A8 */ + CS_AMBA_ID(0x000bb9a8), /* CTI - C-A53 */ + CS_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */ + CS_AMBA_UCI_ID(0x000bb9da, uci_id_cti), /* CTI - C-A35 */ + CS_AMBA_UCI_ID(0x000bb9ed, uci_id_cti), /* Coresight CTI (SoC 600) */ + { 0, 0}, +}; + +static struct amba_driver cti_driver = { + .drv = { + .name = "coresight-cti", + .owner = THIS_MODULE, + .suppress_bind_attrs = true, + }, + .probe = cti_probe, + .id_table = cti_ids, +}; +builtin_amba_driver(cti_driver); diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h new file mode 100644 index 000000000000..c1af6cf9c2fe --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 Linaro Limited, All rights reserved. + * Author: Mike Leach mike.leach@linaro.org + */ + +#ifndef _CORESIGHT_CORESIGHT_CTI_H +#define _CORESIGHT_CORESIGHT_CTI_H + +#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h" + +/* + * Device registers + * 0x000 - 0x144: CTI programming and status + * 0xEDC - 0xEF8: CTI integration test. + * 0xF00 - 0xFFC: Coresight management registers. + */ +/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */ +/* management registers */ +#define CTIDEVAFF0 0xFA8 +#define CTIDEVAFF1 0xFAC + + +/* + * CTI CSSoc 600 has a max of 32 trigger signals per direction. + * CTI CSSoc 400 has 8 IO triggers - other CTIs can be impl def. + * Max of in and out defined in the DEVID register. + * - pick up actual number used from .dts parameters if present. + */ +#define CTIINOUTEN_MAX 32 + +/* + * CTI Trigger signal type definitions. + * Labels for certain trigger interconnections between the CTI + * and standard CS components. + * + * Must match values in ./include/dt-bindings/arm/coresight-cti.h + */ +#define CTITRIG_GEN_IO 0 +#define CTITRIG_GEN_INTREQ 1 +#define CTITRIG_GEN_INTACK 2 +#define CTITRIG_GEN_HALTREQ 3 +#define CTITRIG_GEN_RESTARTREQ 4 +#define CTITRIG_PE_EDBGREQ 5 +#define CTITRIG_PE_DBGRESTART 6 +#define CTITRIG_PE_CTIIRQ 7 +#define CTITRIG_PE_PMUIRQ 8 +#define CTITRIG_PE_DBGTRIGGER 9 +#define CTITRIG_ETM_EXTOUT 10 +#define CTITRIG_ETM_EXTIN 11 +#define CTITRIG_SNK_FULL 12 +#define CTITRIG_SNK_ACQCOMP 13 +#define CTITRIG_SNK_FLUSHCOMP 14 +#define CTITRIG_SNK_FLUSHIN 15 +#define CTITRIG_SNK_TRIGIN 16 +#define CTITRIG_STM_ASYNCOUT 17 +#define CTITRIG_STM_TOUT_SPTE 18 +#define CTITRIG_STM_TOUT_SW 19 +#define CTITRIG_STM_TOUT_HETE 20 +#define CTITRIG_STM_HWEVENT 21 +#define CTITRIG_ELA_TSTART 22 +#define CTITRIG_ELA_TSTOP 23 +#define CTITRIG_ELA_DBGREQ 24 + +/** + * Group of related trigger signals + * + * @nr_sigs: number of signals in the group. + * @used_mask: bitmask representing the signal indexes in the group. + * @sig_types: array of types for the signals, length nr_sigs. + */ +struct cti_trig_grp { + int nr_sigs; + u32 used_mask; + int *sig_types; +}; + +/** + * Trigger connection - connection between a CTI and other (coresight) device + * lists input and output trigger signals for the device + * + * @con_in: connected CTIIN signals for the device. + * @con_out: connected CTIOUT signals for the device. + * @con_dev: coresight device connected to the CTI, NULL if not CS device + * @con_dev_name: name of connected device (CS or CPU) + * @node: entry node in list of connections. + */ +struct cti_trig_con { + struct cti_trig_grp *con_in; + struct cti_trig_grp *con_out; + struct coresight_device *con_dev; + char *con_dev_name; + struct list_head node; +}; + +/** + * struct cti_device - description of CTI device properties. + * + * @nt_trig_con: Number of external devices connected to this device. + * @ctm_id: which CTM this device is connected to (by default it is + * assumed there is a single CTM per SoC, ID 0). + * @trig_cons: list of connections to this device. + * @cpu: CPU ID if associated with CPU, -1 otherwise. + * + */ +struct cti_device { + int nr_trig_con; + u32 ctm_id; + struct list_head trig_cons; + int cpu; +}; + +/** + * struct cti_config - configuration of the CTI device hardware + * + * hardware description from RO ID regs + * @nr_trig_max: Max number of trigger signals implemented on device. + * (max of trig_in or trig_out) + * @nr_ctm_channels: number of available CTM channels + * + * cti enable control + * @enable_req_count: CTI is enabled alongside >=1 associated devices. + * @hw_enabled: true if hw is currently enabled. + * @hw_powered: true if associated cpu powered on, or no cpu. + * + * registered triggers and filtering + * @trig_in_use: bitfield of in triggers registered as in use. + * @trig_out_use: bitfield of out triggers registered as in use. + * @trig_out_filter: bitfield of out triggers that are blocked if filter + * enabled. Typically this would be dbgreq / restart on a core CTI. + * @trig_filter_enable: 1 if filtering enabled. + * + * cti software programmable regs: + * @ctiappset: CTI Software application channel set. + * @ctiinout_sel: register selector for INEN and OUTEN regs. + * @ctiinen: enable input trigger to a channel. + * @ctiouten: enable output trigger from a channel. + * @ctigate: gate channel output from CTI to CTM. + */ +struct cti_config { + /* hardware description */ + int nr_ctm_channels; + int nr_trig_max; + /* cti enable control */ + atomic_t enable_req_count; + bool hw_enabled; + bool hw_powered; + /* registered triggers and filtering */ + u32 trig_in_use; + u32 trig_out_use; + u32 trig_out_filter; + int trig_filter_enable; + /* cti cross trig programmable regs */ + u32 ctiappset; + u8 ctiinout_sel; + u32 ctiinen[CTIINOUTEN_MAX]; + u32 ctiouten[CTIINOUTEN_MAX]; + u32 ctigate; + u32 asicctl; +}; + +/** + * struct cti_drvdata - specifics for the CTI device + * @base: Memory mapped base address for this component. + * @dev: The device entity associated to this component. + * @csdev: Standard CoreSight device information. + * @ctidev: Extra information needed by the CTI/CTM framework. + * @spinlock: Control data access to one at a time. + * @config: Configuration data for this CTI device. + * + */ +struct cti_drvdata { + void __iomem *base; + struct device *dev; + struct coresight_device *csdev; + struct cti_device ctidev; + spinlock_t spinlock; + struct cti_config config; +}; + +/* private cti driver fns & vars */ +extern const struct attribute_group *coresight_cti_groups[]; +int cti_add_default_connection(struct cti_drvdata *drvdata); +int cti_enable(struct coresight_device *csdev, void *__unused); +int cti_disable(struct coresight_device *csdev, void *__unused); + +#ifdef CONFIG_OF +extern int of_cti_get_hw_data(struct device *dev, + struct device_node *np, + struct cti_drvdata *drvdata); +extern struct coresight_platform_data * +of_get_coresight_cti_platform_data(struct device *dev, + const struct device_node *node); +#else +static inline int of_cti_get_hw_data(struct device *dev, + struct device_node *np, + struct cti_drvdata *drvdata) +{ return 0; }; +static inline struct coresight_platform_data * +of_get_coresight_cti_platform_data(struct device *dev, + const struct device_node *node) { return NULL; } +#endif + +#endif /* _CORESIGHT_CORESIGHT_CTI_H */ diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index e0684d06e9ee..8e9e5b6ad866 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -161,6 +161,13 @@ static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; } static inline int etm_writel_cp14(u32 off, u32 val) { return 0; } #endif
+#ifdef CONFIG_CORESIGHT_CTI +extern void cti_device_release(struct device *dev); +#else +static inline void cti_device_release(struct device *dev) {}; +#endif + + /* * Macros and inline functions to handle CoreSight UCI data and driver * private data in AMBA ID table entries, and extract data values. diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 29cef898afba..8db3d1a5314f 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -967,12 +967,20 @@ static struct device_type coresight_dev_type[] = { { .name = "helper", }, + { + .name = "ect", + }, };
static void coresight_device_release(struct device *dev) { struct coresight_device *csdev = to_coresight_device(dev);
+ /* additional info to release for CTI */ + if ((csdev->type == CORESIGHT_DEV_TYPE_ECT) && + (csdev->subtype.ect_subtype == CORESIGHT_DEV_SUBTYPE_ECT_CTI)) + cti_device_release(dev); + kfree(csdev->conns); kfree(csdev->refcnt); kfree(csdev); diff --git a/drivers/hwtracing/coresight/of_coresight-cti.c b/drivers/hwtracing/coresight/of_coresight-cti.c new file mode 100644 index 000000000000..379ca1113deb --- /dev/null +++ b/drivers/hwtracing/coresight/of_coresight-cti.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + */ + +#include <linux/amba/bus.h> +#include <linux/clk.h> +#include <linux/coresight.h> +#include <linux/cpumask.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_graph.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "coresight-cti.h" + +#ifdef CONFIG_OF + +/* + * CTI can be bound to a CPU, or a system device. + * Reflect this in the return value and do not default to cpu 0 + */ +static int of_cti_get_cpu(const struct device_node *node) +{ + int cpu; + struct device_node *dn; + + dn = of_parse_phandle(node, "cpu", 0); + /* CTI Affinity defaults to no cpu */ + if (!dn) + return -1; + cpu = of_cpu_node_to_id(dn); + of_node_put(dn); + + /* No Affinity if no cpu nodes are found */ + return (cpu < 0) ? -1 : cpu; +} + +/* get the hardware configuration & connection data. */ +int of_cti_get_hw_data(struct device *dev, + struct device_node *np, + struct cti_drvdata *drvdata) +{ + int rc = 0; + struct cti_device *cti_dev = &drvdata->ctidev; + + /* if no connections, just add a single default based on max IN-OUT */ + if (cti_dev->nr_trig_con == 0) + rc = cti_add_default_connection(drvdata); + return rc; +} + +/* + * Platform data for the CTI does not have the same connection info + * as the trace path components. Implement specific function to + * avoid warnings of missing connections. + */ +struct coresight_platform_data * +of_get_coresight_cti_platform_data(struct device *dev, + const struct device_node *node) +{ + struct coresight_platform_data *pdata; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + /* Use device name as sysfs handle */ + pdata->name = dev_name(dev); + + /* Look for a CPU ID at the current node level */ + pdata->cpu = of_cti_get_cpu(node); + + return pdata; +} + +#endif diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 7b87965f7a65..d1c1ed17d2ca 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -41,6 +41,7 @@ enum coresight_dev_type { CORESIGHT_DEV_TYPE_LINKSINK, CORESIGHT_DEV_TYPE_SOURCE, CORESIGHT_DEV_TYPE_HELPER, + CORESIGHT_DEV_TYPE_ECT, };
enum coresight_dev_subtype_sink { @@ -68,6 +69,12 @@ enum coresight_dev_subtype_helper { CORESIGHT_DEV_SUBTYPE_HELPER_CATU, };
+/* Embedded Cross Trigger (ECT) sub-types */ +enum coresight_dev_subtype_ect { + CORESIGHT_DEV_SUBTYPE_ECT_NONE, + CORESIGHT_DEV_SUBTYPE_ECT_CTI, +}; + /** * union coresight_dev_subtype - further characterisation of a type * @sink_subtype: type of sink this component is, as defined @@ -78,6 +85,8 @@ enum coresight_dev_subtype_helper { * by @coresight_dev_subtype_source. * @helper_subtype: type of helper this component is, as defined * by @coresight_dev_subtype_helper. + * @ect_subtype: type of cross trigger this component is, as + * defined by @coresight_dev_subtype_ect */ union coresight_dev_subtype { /* We have some devices which acts as LINK and SINK */ @@ -87,6 +96,7 @@ union coresight_dev_subtype { }; enum coresight_dev_subtype_source source_subtype; enum coresight_dev_subtype_helper helper_subtype; + enum coresight_dev_subtype_ect ect_subtype; };
/** @@ -180,6 +190,7 @@ struct coresight_device { #define sink_ops(csdev) csdev->ops->sink_ops #define link_ops(csdev) csdev->ops->link_ops #define helper_ops(csdev) csdev->ops->helper_ops +#define ect_ops(csdev) csdev->ops->ect_ops
/** * struct coresight_ops_sink - basic operations for a sink @@ -245,11 +256,26 @@ struct coresight_ops_helper { int (*disable)(struct coresight_device *csdev, void *data); };
+/** + * struct coresight_ops_ect - Ops for an embedded cross trigger device + * + * All operations could pass in a device specific data, which could + * help the ect device to determine what to do. + * + * @enable : Enable the device + * @disable : Disable the device + */ +struct coresight_ops_ect { + int (*enable)(struct coresight_device *csdev, void *data); + int (*disable)(struct coresight_device *csdev, void *data); +}; + struct coresight_ops { const struct coresight_ops_sink *sink_ops; const struct coresight_ops_link *link_ops; const struct coresight_ops_source *source_ops; const struct coresight_ops_helper *helper_ops; + const struct coresight_ops_ect *ect_ops; };
#ifdef CONFIG_CORESIGHT
On Wed, May 01, 2019 at 09:49:33AM +0100, Mike Leach wrote:
This introduces a baseline CTI driver and associated configuration files.
Driver will probe for the device on the AMBA bus, and load the CTI driver on CoreSight ID match to CTI IDs in tables.
Initial sysfs support for enable / disable provided.
Default CTI interconnection data is generated based on hardware register signal counts, with no additional connection information
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/Kconfig | 13 + drivers/hwtracing/coresight/Makefile | 4 + .../hwtracing/coresight/coresight-cti-sysfs.c | 97 ++++ drivers/hwtracing/coresight/coresight-cti.c | 522 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 228 ++++++++ drivers/hwtracing/coresight/coresight-priv.h | 7 + drivers/hwtracing/coresight/coresight.c | 8 + .../hwtracing/coresight/of_coresight-cti.c | 81 +++ include/linux/coresight.h | 26 + 9 files changed, 986 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti-sysfs.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.h create mode 100644 drivers/hwtracing/coresight/of_coresight-cti.c
Compiling this patch gave me the following compilation warning:
/home/mpoirier/work/coresight/kernel-maint/drivers/hwtracing/coresight/coresight-cti-sysfs.c: In function ‘enable_show’: /home/mpoirier/work/coresight/kernel-maint/drivers/hwtracing/coresight/coresight-cti-sysfs.c:52:19: warning: comparison of constant ‘0’ with boolean expression is always true [-Wbool-compare] } else if (cpuid >= 0) {
mpoirier@xps15:~/work/coresight/kernel-maint$ aarch64-linux-gnu-gcc --version aarch64-linux-gnu-gcc (Linaro GCC 7.2-2017.11) 7.2.1 20171011 Copyright (C) 2017 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Checkpatch.pl also found problems with several patches and I had to rebase your work on the latest coresight-next tree.
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index ad34380cac49..9f1cac64f357 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -114,4 +114,17 @@ config CORESIGHT_CPU_DEBUG properly, please refer Documentation/trace/coresight-cpu-debug.txt for detailed description and the example for usage. +config CORESIGHT_CTI
- bool "CoreSight Cross Trigger Interface (CTI) driver"
- depends on ARM || ARM64
- select CORESIGHT_LINKS_AND_SINKS
- help
This driver provides support for CoreSight CTI and CTM components.
These provide hardware triggering events between CoreSight trace
source and sink components. These can be used to halt trace or
s/"used to halt"/"used to halt"
inject events into the trace stream. CTI also provides a software
control to trigger same halt events. This can provide fast trace
s/"to trigger same halt"/"to trigger the same halt"
halt compared to disabling sources and sinks normally in driver
software.
endif diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 41870ded51a3..77402cfc7174 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -19,3 +19,7 @@ obj-$(CONFIG_CORESIGHT_DYNAMIC_REPLICATOR) += coresight-dynamic-replicator.o obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o +obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o \
of_coresight-cti.o \
coresight-cti-sysfs.o
It is probably a good idea to base your work on Suzuki's ACPI pathset [1] where he got rid of of_coresight.c to favour a firmware agnostic naming convention. While doing so skip patches 34 to 36 as they relate to sysfs topology and we need to deal with that separately. Better yet, include patches 34 to 36 and propose a sysfs topology representation that works for both "data" and "signal" components.
[1]. https://patchwork.kernel.org/cover/10901109/
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c new file mode 100644 index 000000000000..9b6749621dcb --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2019 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/cpu.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/pm_wakeup.h> +#include <linux/seq_file.h> +#include <linux/smp.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/uaccess.h>
As far as I can tell this file can be compile with the following headers:
coresight.h device.h kernel.h spinlock.h sysfs.h types.h
Everything else can be removed and re-introduced as you need them.
+#include "coresight-cti.h"
+/* basic attributes */ +static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
Once applied the second and third line don't line up with the 's' of the first line.
+{
- int enable_req;
- bool enabled, powered, cpuid;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- ssize_t size = 0;
- enable_req = atomic_read(&drvdata->config.enable_req_count);
- spin_lock(&drvdata->spinlock);
- powered = drvdata->config.hw_powered;
- enabled = drvdata->config.hw_enabled;
- cpuid = drvdata->ctidev.cpu;
- spin_unlock(&drvdata->spinlock);
- if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
- } else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
- } else {
size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n",
enabled ? "enabled" : "disabled");
- }
This interface seems a little complex to me. I will wait to see how things evolved in the coming patches - maybe I'll understand better then.
- return size;
+}
+static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int ret = 0;
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- ret = kstrtoul(buf, 16, &val);
- if (ret)
return ret;
- if (val)
ret = cti_enable(drvdata->csdev, NULL);
- else
ret = cti_disable(drvdata->csdev, NULL);
- if (ret)
return ret;
- return size;
+} +static DEVICE_ATTR_RW(enable);
+/* attribute and group sysfs tables. */ +static struct attribute *coresight_cti_attrs[] = {
- &dev_attr_enable.attr,
- NULL,
+};
+static const struct attribute_group coresight_cti_group = {
- .attrs = coresight_cti_attrs,
+};
+const struct attribute_group *coresight_cti_groups[] = {
- &coresight_cti_group,
- NULL,
+}; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c new file mode 100644 index 000000000000..0a0fb6d48237 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
s/2018/2019
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/amba/bus.h> +#include <linux/clk.h> +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/cpu.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/pm_wakeup.h> +#include <linux/seq_file.h> +#include <linux/smp.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <asm/local.h> +#include <asm/sections.h>
+#include "coresight-cti.h"
Same comment as above - please get rid of all the headers that aren't currenlty needed.
+/**
- CTI devices can be associated with a PE, or be connected to CoreSight
- hardware. We have a list of all CTIs, and a subset array associated
- with individual PEs for pwr management as they will power up in the
- PE power domain.
- At this point we assume that the none CPU CTIs are always powered as
- we do with sinks etc.
s/none/non
- We leave the client to figure out if all the CTIs are interconnected with
- the same CTM, in general this is the case but does not always have to be.
- */
While reading the bindings and the documentation included in this set (many thanks for that), I was wondering if you have seen and tested your work on systems where more than one CTM is present.
+struct ect_node {
- struct cti_drvdata *cti_drv;
- struct list_head next;
+};
+/* net of CTI devices connected via CTM */ +LIST_HEAD(ect_net);
+/* protect the list */ +static DEFINE_MUTEX(ect_mutex);
+/* quick reference for CPU CTIs */ +static struct cti_drvdata *cti_cpu_drv[NR_CPUS];
+/* number of registered CTI devices */ +static int cti_count;
+/* number of cpu associated CTI devices in use */ +static int cti_cpu_count;
+#define csdev_to_cti_drvdata(csdev) \
- dev_get_drvdata(csdev->dev.parent)
+/* write set of regs to hardware - call with spinlock claimed */ +static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +{
- struct cti_config *config = &drvdata->config;
- int i;
- CS_UNLOCK(drvdata->base);
- /* disable CTI before writing registers */
- writel_relaxed(0, drvdata->base + CTICONTROL);
- /* write the CTI trigger registers */
- for (i = 0; i < config->nr_trig_max; i++) {
writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i));
writel_relaxed(config->ctiouten[i],
drvdata->base + CTIOUTEN(i));
- }
- /* other regs */
- writel_relaxed(config->ctigate, drvdata->base + CTIGATE);
- writel_relaxed(config->asicctl, drvdata->base + ASICCTL);
- writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET);
- /* re-enable CTI */
- writel_relaxed(1, drvdata->base + CTICONTROL);
- CS_LOCK(drvdata->base);
+}
+static void cti_enable_hw_smp_call(void *info) +{
- struct cti_drvdata *drvdata = info;
- cti_write_all_hw_regs(drvdata);
+}
+/* write regs to hardware and enable */ +static int cti_enable_hw(void *info) +{
- struct cti_drvdata *drvdata = info;
- struct cti_config *config = &drvdata->config;
- int rc = 0;
- pm_runtime_get_sync(drvdata->dev);
- spin_lock(&drvdata->spinlock);
- /* no need to do anything if enabled or unpowered*/
- if (config->hw_enabled || !config->hw_powered)
goto cti_not_enabled;
- /* claim the device */
- rc = coresight_claim_device(drvdata->base);
- if (rc)
goto cti_not_enabled;
- if (drvdata->ctidev.cpu >= 0) {
dev_info(drvdata->dev, "cti enable smp call for cpu %d\n",
drvdata->ctidev.cpu);
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
if (rc)
goto cti_not_enabled;
- } else {
dev_info(drvdata->dev, "cti enable not cpu call\n");
cti_write_all_hw_regs(drvdata);
- }
- config->hw_enabled = true;
- spin_unlock(&drvdata->spinlock);
- return rc;
- /* not enabled in this call */
+cti_not_enabled:
- spin_unlock(&drvdata->spinlock);
- pm_runtime_put(drvdata->dev);
- return rc;
+}
+/* disable hardware */ +static int cti_disable_hw(void *info) +{
- struct cti_drvdata *drvdata = info;
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* no need to do anything if disabled or cpu unpowered*/
- if (!config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
return 0;
- }
- CS_UNLOCK(drvdata->base);
- /* disable CTI */
- writel_relaxed(0, drvdata->base + CTICONTROL);
- config->hw_enabled = false;
- coresight_disclaim_device_unlocked(drvdata->base);
- CS_LOCK(drvdata->base);
- spin_unlock(&drvdata->spinlock);
- pm_runtime_put(drvdata->dev);
- return 0;
+}
+static void cti_set_default_config(struct cti_config *config) +{
- /* Most regs default to 0 as zalloc'ed except...*/
- config->trig_filter_enable = 1;
- config->ctigate = ((u32)0x1 << config->nr_ctm_channels) - 1;
Please use the macro GENMASK().
- atomic_set(&config->enable_req_count, 0);
+}
+/*
- Add a connection entry to the list of connections for this
- CTI device.
- */
+static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+{
- struct cti_trig_con *tc;
- struct cti_device *cti_dev = &drvdata->ctidev;
- tc = kzalloc(sizeof(struct cti_trig_con), GFP_KERNEL);
- if (tc == 0)
return -ENOMEM;
- tc->con_dev = csdev;
- /*
* Prefer actual associated CS device dev name to supplied value -
* which is likely to be node name / other conn name.
*/
- if (csdev)
tc->con_dev_name = kstrdup(dev_name(&csdev->dev), GFP_KERNEL);
- else if (assoc_dev_name != NULL)
tc->con_dev_name = kstrdup(assoc_dev_name, GFP_KERNEL);
- tc->con_in = in_info;
- tc->con_out = out_info;
- list_add_tail(&tc->node, &cti_dev->trig_cons);
- cti_dev->nr_trig_con++;
- /* add connection usage bit info to overall info */
- drvdata->config.trig_in_use |= in_info->used_mask;
- drvdata->config.trig_out_use |= out_info->used_mask;
- return 0;
+}
+/*
- Add a default connection if nothing else is specified.
- single connection based on max in/out info, no assoc device
- */
+int cti_add_default_connection(struct cti_drvdata *drvdata) +{
- int ret = 0, idx;
- struct cti_trig_grp *in = 0, *out = 0;
- int n_trigs = drvdata->config.nr_trig_max;
- u32 n_trig_mask = (0x1 << n_trigs) - 1;
GENMASK().
- int *in_sig_types = 0, *out_sig_types = 0;
- in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!in) {
ret = -ENOMEM;
goto conn_add_err;
- }
- out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!out) {
ret = -ENOMEM;
goto conn_add_err;
- }
- in_sig_types = kzalloc(sizeof(int) * n_trigs, GFP_KERNEL);
- if (!in_sig_types) {
ret = -ENOMEM;
goto conn_add_err;
- }
- out_sig_types = kzalloc(sizeof(int) * n_trigs, GFP_KERNEL);
- if (!out_sig_types) {
ret = -ENOMEM;
goto conn_add_err;
- }
- /* signal types marked as default CTITRIG_GEN_IO */
- for (idx = 0; idx < n_trigs; idx++) {
in_sig_types[idx] = CTITRIG_GEN_IO;
out_sig_types[idx] = CTITRIG_GEN_IO;
- }
- /*
* Assume max trigs for in and out,
* all used, default sig types allocated
*/
- in->nr_sigs = n_trigs;
- in->used_mask = n_trig_mask;
- in->sig_types = in_sig_types;
- out->nr_sigs = n_trigs;
- out->used_mask = n_trig_mask;
- out->sig_types = out_sig_types;
- ret = cti_add_connection_entry(drvdata, in, out, NULL, "default");
- if (ret)
goto conn_add_err;
- return ret;
+conn_add_err:
- kfree(in);
- kfree(out);
- kfree(out_sig_types);
- kfree(in_sig_types);
- return ret;
+}
+/** cti ect operations **/ +int cti_enable(struct coresight_device *csdev, void *__unused) +{
- int rc;
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- atomic_inc(&drvdata->config.enable_req_count);
- rc = cti_enable_hw(drvdata);
- if (rc)
atomic_dec(&drvdata->config.enable_req_count);
- return rc;
+}
+int cti_disable(struct coresight_device *csdev, void *__unused) +{
- int rc = 0;
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- if (!atomic_dec_return(&drvdata->config.enable_req_count)) {
rc = cti_disable_hw(drvdata);
if (rc)
atomic_inc(&drvdata->config.enable_req_count);
- }
- return rc;
+}
+const struct coresight_ops_ect cti_ops_ect = {
- .enable = cti_enable,
- .disable = cti_disable,
+};
+const struct coresight_ops cti_ops = {
- .ect_ops = &cti_ops_ect,
+};
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
- int ret = 0;
- u32 devid;
- void __iomem *base;
- struct device *dev = &adev->dev;
- struct cti_drvdata *drvdata = NULL;
- struct coresight_desc cti_desc;
- struct coresight_platform_data *pdata = NULL;
- struct resource *res = &adev->res;
- struct device_node *np = adev->dev.of_node;
- struct ect_node *ect_nd = NULL;
- /* boilerplate code to set up the basics */
- if (np) {
pdata = of_get_coresight_cti_platform_data(dev, np);
if (IS_ERR(pdata)) {
dev_info(dev, "of_get_coresight_ect_platform err\n");
return PTR_ERR(pdata);
}
dev->platform_data = pdata;
- }
- /* node to keep track of CTI net */
- ect_nd = devm_kzalloc(dev, sizeof(struct ect_node), GFP_KERNEL);
- if (!ect_nd) {
ret = -ENOMEM;
goto err_out;
- }
- /* driver data*/
- drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
goto err_out;
- }
- /* Validity for the resource is already checked by the AMBA core */
- base = devm_ioremap_resource(dev, res);
- if (IS_ERR(base)) {
ret = PTR_ERR(base);
dev_info(dev, "%s, remap err\n", __func__);
goto err_out;
- }
- drvdata->base = base;
- /* links between dev and drvdata*/
- drvdata->dev = dev;
- dev_set_drvdata(dev, drvdata);
- /* default CTI device info */
- drvdata->ctidev.cpu = pdata->cpu;
- drvdata->ctidev.nr_trig_con = 0;
- drvdata->ctidev.ctm_id = 0;
- INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
- spin_lock_init(&drvdata->spinlock);
- /* look at the HW DEVID register for some of the HW settings */
- devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
- drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8);
Every time you use hard coded values like this, please introduce a comments that explains what you are doing. For example:
"DEVID.NUM_TRIG [15:8] : Indicates the maximum number of triggers."
That way we don't always have to go back to the TRM to understand the code.
- /*
* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
- if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
drvdata->config.nr_trig_max, CTIINOUTEN_MAX);
drvdata->config.nr_trig_max = CTIINOUTEN_MAX;
- }
- drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
- /* additional parse the .dts for connections and signals */
- of_cti_get_hw_data(dev, np, drvdata);
- /* initialise default driver values */
- cti_set_default_config(&drvdata->config);
- /* setup cpu related CTI devices, otherwise assume powered */
- drvdata->config.hw_powered = true;
- /* set up coresight component description */
- cti_desc.pdata = pdata;
- cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
- cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;
Out of curiosity, do you expect other ECT device types?
- cti_desc.ops = &cti_ops;
- cti_desc.groups = coresight_cti_groups;
- cti_desc.dev = dev;
- drvdata->csdev = coresight_register(&cti_desc);
- if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
pr_err("%s: CS register failed\n", pdata->name);
None of the other drivers have this - please remove.
goto err_out;
- }
- /* add to list of CTI devices */
- mutex_lock(&ect_mutex);
- ect_nd->cti_drv = drvdata;
- list_add(&ect_nd->next, &ect_net);
- /* if cpu bound add to CPU array */
- if (drvdata->ctidev.cpu >= 0) {
cti_cpu_drv[drvdata->ctidev.cpu] = drvdata;
cti_cpu_count++;
- }
- mutex_unlock(&ect_mutex);
- /* all done - dec pm refcount */
- pm_runtime_put(&adev->dev);
- dev_info(dev, "%s: initialized\n", pdata->name);
- cti_count++;
- return 0;
+err_out:
- kfree(drvdata);
- kfree(ect_nd);
Because devm_kzalloc() was used to allocate the memory, the device core will automagically take care of releasing the memory for you upon error.
- return ret;
+}
+/* free all connection info from the driver */ +void cti_free_conn_info(struct cti_drvdata *drvdata) +{
- struct cti_trig_con *tc, *tc_tmp;
- list_for_each_entry_safe(tc, tc_tmp,
&drvdata->ctidev.trig_cons, node) {
kfree(tc->con_in->sig_types);
kfree(tc->con_in);
kfree(tc->con_out->sig_types);
kfree(tc->con_out);
kfree(tc->con_dev_name);
list_del(&tc->node);
kfree(tc);
- }
+}
+/*
- Free up CTI specific resources
- called from coresight_device_release on coresight_unregister.
- */
+void cti_device_release(struct device *dev) +{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct ect_node *ect_item, *ect_tmp;
- /* free up resources associated with the cti connections */
- if (drvdata->ctidev.cpu >= 0)
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
Since cti_cpu_drv[] is global it needs to be protected by ect_mutex.
- /* clear the connection list items */
- cti_free_conn_info(drvdata);
- /* remove from the list */
- mutex_lock(&ect_mutex);
- list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
kfree(ect_item);
goto ect_list_item_removed;
}
- }
+ect_list_item_removed:
- mutex_unlock(&ect_mutex);
- kfree(drvdata);
+} +EXPORT_SYMBOL_GPL(cti_device_release);
+static struct amba_cs_uci_id uci_id_cti[] = {
- {
/* CTI UCI data */
.devarch = 0x47701a14, /* devarch value for CTI v2 */
.devarch_mask = 0xfff0ffff,
.devtype = 0x00000014, /* maj(0x4-debug) min(0x1-ECT) */
- }
+};
+static const struct amba_id cti_ids[] = {
- CS_AMBA_ID(0x000bb906), /* Coresight CTI (SoC 400), C-A72, C-A57 */
- CS_AMBA_ID(0x000bb922), /* CTI - C-A8 */
- CS_AMBA_ID(0x000bb9a8), /* CTI - C-A53 */
- CS_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */
- CS_AMBA_UCI_ID(0x000bb9da, uci_id_cti), /* CTI - C-A35 */
- CS_AMBA_UCI_ID(0x000bb9ed, uci_id_cti), /* Coresight CTI (SoC 600) */
- { 0, 0},
+};
+static struct amba_driver cti_driver = {
- .drv = {
.name = "coresight-cti",
.owner = THIS_MODULE,
.suppress_bind_attrs = true,
- },
- .probe = cti_probe,
- .id_table = cti_ids,
+}; +builtin_amba_driver(cti_driver); diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h new file mode 100644 index 000000000000..c1af6cf9c2fe --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CTI_H +#define _CORESIGHT_CORESIGHT_CTI_H
+#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h"
+/*
- Device registers
- 0x000 - 0x144: CTI programming and status
- 0xEDC - 0xEF8: CTI integration test.
- 0xF00 - 0xFFC: Coresight management registers.
- */
+/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */ +/* management registers */ +#define CTIDEVAFF0 0xFA8 +#define CTIDEVAFF1 0xFAC
On my side some of the defines above are misaligned - is it the same on your side?
+/*
- CTI CSSoc 600 has a max of 32 trigger signals per direction.
- CTI CSSoc 400 has 8 IO triggers - other CTIs can be impl def.
- Max of in and out defined in the DEVID register.
- pick up actual number used from .dts parameters if present.
- */
+#define CTIINOUTEN_MAX 32
+/*
- CTI Trigger signal type definitions.
- Labels for certain trigger interconnections between the CTI
- and standard CS components.
- Must match values in ./include/dt-bindings/arm/coresight-cti.h
- */
+#define CTITRIG_GEN_IO 0 +#define CTITRIG_GEN_INTREQ 1 +#define CTITRIG_GEN_INTACK 2 +#define CTITRIG_GEN_HALTREQ 3 +#define CTITRIG_GEN_RESTARTREQ 4 +#define CTITRIG_PE_EDBGREQ 5 +#define CTITRIG_PE_DBGRESTART 6 +#define CTITRIG_PE_CTIIRQ 7 +#define CTITRIG_PE_PMUIRQ 8 +#define CTITRIG_PE_DBGTRIGGER 9 +#define CTITRIG_ETM_EXTOUT 10 +#define CTITRIG_ETM_EXTIN 11 +#define CTITRIG_SNK_FULL 12 +#define CTITRIG_SNK_ACQCOMP 13 +#define CTITRIG_SNK_FLUSHCOMP 14 +#define CTITRIG_SNK_FLUSHIN 15 +#define CTITRIG_SNK_TRIGIN 16 +#define CTITRIG_STM_ASYNCOUT 17 +#define CTITRIG_STM_TOUT_SPTE 18 +#define CTITRIG_STM_TOUT_SW 19 +#define CTITRIG_STM_TOUT_HETE 20 +#define CTITRIG_STM_HWEVENT 21 +#define CTITRIG_ELA_TSTART 22 +#define CTITRIG_ELA_TSTOP 23 +#define CTITRIG_ELA_DBGREQ 24
+/**
- Group of related trigger signals
- @nr_sigs: number of signals in the group.
- @used_mask: bitmask representing the signal indexes in the group.
- @sig_types: array of types for the signals, length nr_sigs.
- */
+struct cti_trig_grp {
- int nr_sigs;
- u32 used_mask;
- int *sig_types;
+};
+/**
- Trigger connection - connection between a CTI and other (coresight) device
- lists input and output trigger signals for the device
- @con_in: connected CTIIN signals for the device.
- @con_out: connected CTIOUT signals for the device.
- @con_dev: coresight device connected to the CTI, NULL if not CS device
- @con_dev_name: name of connected device (CS or CPU)
- @node: entry node in list of connections.
- */
+struct cti_trig_con {
- struct cti_trig_grp *con_in;
- struct cti_trig_grp *con_out;
- struct coresight_device *con_dev;
- char *con_dev_name;
- struct list_head node;
+};
+/**
- struct cti_device - description of CTI device properties.
- @nt_trig_con: Number of external devices connected to this device.
- @ctm_id: which CTM this device is connected to (by default it is
assumed there is a single CTM per SoC, ID 0).
- @trig_cons: list of connections to this device.
- @cpu: CPU ID if associated with CPU, -1 otherwise.
One line too many.
- */
+struct cti_device {
- int nr_trig_con;
- u32 ctm_id;
- struct list_head trig_cons;
- int cpu;
+};
The structures defined above don't have a tabulation between their type and variable name - but this one (and the following) do. Though the community tends to lean toward the "no tab" format I don't think there is an enforced rule. As such I will leave it to you to choose what you prefer. On the flip side I would ask that you enact the same pattern within the file.
+/**
- struct cti_config - configuration of the CTI device hardware
- hardware description from RO ID regs
- @nr_trig_max: Max number of trigger signals implemented on device.
(max of trig_in or trig_out)
- @nr_ctm_channels: number of available CTM channels
- cti enable control
- @enable_req_count: CTI is enabled alongside >=1 associated devices.
- @hw_enabled: true if hw is currently enabled.
- @hw_powered: true if associated cpu powered on, or no cpu.
- registered triggers and filtering
- @trig_in_use: bitfield of in triggers registered as in use.
- @trig_out_use: bitfield of out triggers registered as in use.
- @trig_out_filter: bitfield of out triggers that are blocked if filter
- enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
- @ctiinout_sel: register selector for INEN and OUTEN regs.
- @ctiinen: enable input trigger to a channel.
- @ctiouten: enable output trigger from a channel.
- @ctigate: gate channel output from CTI to CTM.
- */
+struct cti_config {
- /* hardware description */
- int nr_ctm_channels;
- int nr_trig_max;
- /* cti enable control */
- atomic_t enable_req_count;
- bool hw_enabled;
- bool hw_powered;
- /* registered triggers and filtering */
- u32 trig_in_use;
- u32 trig_out_use;
- u32 trig_out_filter;
- int trig_filter_enable;
- /* cti cross trig programmable regs */
- u32 ctiappset;
- u8 ctiinout_sel;
- u32 ctiinen[CTIINOUTEN_MAX];
- u32 ctiouten[CTIINOUTEN_MAX];
- u32 ctigate;
- u32 asicctl;
+};
+/**
- struct cti_drvdata - specifics for the CTI device
- @base: Memory mapped base address for this component.
- @dev: The device entity associated to this component.
- @csdev: Standard CoreSight device information.
- @ctidev: Extra information needed by the CTI/CTM framework.
- @spinlock: Control data access to one at a time.
- @config: Configuration data for this CTI device.
- */
+struct cti_drvdata {
- void __iomem *base;
- struct device *dev;
- struct coresight_device *csdev;
- struct cti_device ctidev;
- spinlock_t spinlock;
- struct cti_config config;
+};
+/* private cti driver fns & vars */ +extern const struct attribute_group *coresight_cti_groups[]; +int cti_add_default_connection(struct cti_drvdata *drvdata); +int cti_enable(struct coresight_device *csdev, void *__unused); +int cti_disable(struct coresight_device *csdev, void *__unused);
+#ifdef CONFIG_OF +extern int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata);
+extern struct coresight_platform_data * +of_get_coresight_cti_platform_data(struct device *dev,
const struct device_node *node);
+#else +static inline int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{ return 0; }; +static inline struct coresight_platform_data * +of_get_coresight_cti_platform_data(struct device *dev,
const struct device_node *node) { return NULL; }
+#endif
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */ diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index e0684d06e9ee..8e9e5b6ad866 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -161,6 +161,13 @@ static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; } static inline int etm_writel_cp14(u32 off, u32 val) { return 0; } #endif +#ifdef CONFIG_CORESIGHT_CTI +extern void cti_device_release(struct device *dev); +#else +static inline void cti_device_release(struct device *dev) {}; +#endif
/*
- Macros and inline functions to handle CoreSight UCI data and driver
- private data in AMBA ID table entries, and extract data values.
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 29cef898afba..8db3d1a5314f 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -967,12 +967,20 @@ static struct device_type coresight_dev_type[] = { { .name = "helper", },
- {
.name = "ect",
- },
}; static void coresight_device_release(struct device *dev) { struct coresight_device *csdev = to_coresight_device(dev);
- /* additional info to release for CTI */
- if ((csdev->type == CORESIGHT_DEV_TYPE_ECT) &&
(csdev->subtype.ect_subtype == CORESIGHT_DEV_SUBTYPE_ECT_CTI))
cti_device_release(dev);
- kfree(csdev->conns); kfree(csdev->refcnt); kfree(csdev);
diff --git a/drivers/hwtracing/coresight/of_coresight-cti.c b/drivers/hwtracing/coresight/of_coresight-cti.c new file mode 100644 index 000000000000..379ca1113deb --- /dev/null +++ b/drivers/hwtracing/coresight/of_coresight-cti.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2019, The Linux Foundation. All rights reserved.
- */
+#include <linux/amba/bus.h> +#include <linux/clk.h> +#include <linux/coresight.h> +#include <linux/cpumask.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_graph.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h>
+#include "coresight-cti.h"
+#ifdef CONFIG_OF
+/*
- CTI can be bound to a CPU, or a system device.
- Reflect this in the return value and do not default to cpu 0
- */
+static int of_cti_get_cpu(const struct device_node *node) +{
- int cpu;
- struct device_node *dn;
- dn = of_parse_phandle(node, "cpu", 0);
- /* CTI Affinity defaults to no cpu */
- if (!dn)
return -1;
- cpu = of_cpu_node_to_id(dn);
- of_node_put(dn);
- /* No Affinity if no cpu nodes are found */
- return (cpu < 0) ? -1 : cpu;
+}
+/* get the hardware configuration & connection data. */ +int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
- int rc = 0;
- struct cti_device *cti_dev = &drvdata->ctidev;
- /* if no connections, just add a single default based on max IN-OUT */
- if (cti_dev->nr_trig_con == 0)
rc = cti_add_default_connection(drvdata);
- return rc;
+}
+/*
- Platform data for the CTI does not have the same connection info
- as the trace path components. Implement specific function to
- avoid warnings of missing connections.
- */
+struct coresight_platform_data * +of_get_coresight_cti_platform_data(struct device *dev,
const struct device_node *node)
+{
- struct coresight_platform_data *pdata;
- pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
return ERR_PTR(-ENOMEM);
- /* Use device name as sysfs handle */
- pdata->name = dev_name(dev);
- /* Look for a CPU ID at the current node level */
- pdata->cpu = of_cti_get_cpu(node);
- return pdata;
+}
+#endif diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 7b87965f7a65..d1c1ed17d2ca 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -41,6 +41,7 @@ enum coresight_dev_type { CORESIGHT_DEV_TYPE_LINKSINK, CORESIGHT_DEV_TYPE_SOURCE, CORESIGHT_DEV_TYPE_HELPER,
- CORESIGHT_DEV_TYPE_ECT,
}; enum coresight_dev_subtype_sink { @@ -68,6 +69,12 @@ enum coresight_dev_subtype_helper { CORESIGHT_DEV_SUBTYPE_HELPER_CATU, }; +/* Embedded Cross Trigger (ECT) sub-types */ +enum coresight_dev_subtype_ect {
- CORESIGHT_DEV_SUBTYPE_ECT_NONE,
- CORESIGHT_DEV_SUBTYPE_ECT_CTI
+};
/**
- union coresight_dev_subtype - further characterisation of a type
- @sink_subtype: type of sink this component is, as defined
@@ -78,6 +85,8 @@ enum coresight_dev_subtype_helper {
by @coresight_dev_subtype_source.
- @helper_subtype: type of helper this component is, as defined
by @coresight_dev_subtype_helper.
- @ect_subtype: type of cross trigger this component is, as
*/
defined by @coresight_dev_subtype_ect
union coresight_dev_subtype { /* We have some devices which acts as LINK and SINK */ @@ -87,6 +96,7 @@ union coresight_dev_subtype { }; enum coresight_dev_subtype_source source_subtype; enum coresight_dev_subtype_helper helper_subtype;
- enum coresight_dev_subtype_ect ect_subtype;
}; /** @@ -180,6 +190,7 @@ struct coresight_device { #define sink_ops(csdev) csdev->ops->sink_ops #define link_ops(csdev) csdev->ops->link_ops #define helper_ops(csdev) csdev->ops->helper_ops +#define ect_ops(csdev) csdev->ops->ect_ops /**
- struct coresight_ops_sink - basic operations for a sink
@@ -245,11 +256,26 @@ struct coresight_ops_helper { int (*disable)(struct coresight_device *csdev, void *data); }; +/**
- struct coresight_ops_ect - Ops for an embedded cross trigger device
- All operations could pass in a device specific data, which could
- help the ect device to determine what to do.
- @enable : Enable the device
- @disable : Disable the device
- */
+struct coresight_ops_ect {
- int (*enable)(struct coresight_device *csdev, void *data);
- int (*disable)(struct coresight_device *csdev, void *data);
+};
struct coresight_ops { const struct coresight_ops_sink *sink_ops; const struct coresight_ops_link *link_ops; const struct coresight_ops_source *source_ops; const struct coresight_ops_helper *helper_ops;
- const struct coresight_ops_ect *ect_ops;
};
#ifdef CONFIG_CORESIGHT
2.20.1
Hi Mathieu,
On Mon, 6 May 2019 at 22:50, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, May 01, 2019 at 09:49:33AM +0100, Mike Leach wrote:
This introduces a baseline CTI driver and associated configuration files.
Driver will probe for the device on the AMBA bus, and load the CTI driver on CoreSight ID match to CTI IDs in tables.
Initial sysfs support for enable / disable provided.
Default CTI interconnection data is generated based on hardware register signal counts, with no additional connection information
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/Kconfig | 13 + drivers/hwtracing/coresight/Makefile | 4 + .../hwtracing/coresight/coresight-cti-sysfs.c | 97 ++++ drivers/hwtracing/coresight/coresight-cti.c | 522 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 228 ++++++++ drivers/hwtracing/coresight/coresight-priv.h | 7 + drivers/hwtracing/coresight/coresight.c | 8 + .../hwtracing/coresight/of_coresight-cti.c | 81 +++ include/linux/coresight.h | 26 + 9 files changed, 986 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti-sysfs.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.h create mode 100644 drivers/hwtracing/coresight/of_coresight-cti.c
Compiling this patch gave me the following compilation warning:
/home/mpoirier/work/coresight/kernel-maint/drivers/hwtracing/coresight/coresight-cti-sysfs.c: In function ‘enable_show’: /home/mpoirier/work/coresight/kernel-maint/drivers/hwtracing/coresight/coresight-cti-sysfs.c:52:19: warning: comparison of constant ‘0’ with boolean expression is always true [-Wbool-compare] } else if (cpuid >= 0) {
mpoirier@xps15:~/work/coresight/kernel-maint$ aarch64-linux-gnu-gcc --version aarch64-linux-gnu-gcc (Linaro GCC 7.2-2017.11) 7.2.1 20171011 Copyright (C) 2017 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
OK - looks like it was fixed in the next patch, will fix here.
Checkpatch.pl also found problems with several patches and I had to rebase your work on the latest coresight-next tree.
The set was base on upstream Linux 5.1-rc5. Passed through checkpatch.pl with no errors on my side. (exception being Kconfig where it complained about lack of decsription even though one present ) I'll recheck next release.
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index ad34380cac49..9f1cac64f357 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -114,4 +114,17 @@ config CORESIGHT_CPU_DEBUG properly, please refer Documentation/trace/coresight-cpu-debug.txt for detailed description and the example for usage.
+config CORESIGHT_CTI
bool "CoreSight Cross Trigger Interface (CTI) driver"
depends on ARM || ARM64
select CORESIGHT_LINKS_AND_SINKS
help
This driver provides support for CoreSight CTI and CTM components.
These provide hardware triggering events between CoreSight trace
source and sink components. These can be used to halt trace or
s/"used to halt"/"used to halt"
inject events into the trace stream. CTI also provides a software
control to trigger same halt events. This can provide fast trace
s/"to trigger same halt"/"to trigger the same halt"
halt compared to disabling sources and sinks normally in driver
software.
endif diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 41870ded51a3..77402cfc7174 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -19,3 +19,7 @@ obj-$(CONFIG_CORESIGHT_DYNAMIC_REPLICATOR) += coresight-dynamic-replicator.o obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o +obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o \
of_coresight-cti.o \
coresight-cti-sysfs.o
It is probably a good idea to base your work on Suzuki's ACPI pathset [1] where he got rid of of_coresight.c to favour a firmware agnostic naming convention. While doing so skip patches 34 to 36 as they relate to sysfs topology and we need to deal with that separately. Better yet, include patches 34 to 36 and propose a sysfs topology representation that works for both "data" and "signal" components.
I am aware of Suzukis work and the implications for re-work - but was not keen on developing against a moving target, rather look to more stable upstream - i.e. rc5 as mentioned above. There is nothing in suzukis patch set that this set _needs_ at present. Yes, there is the question of connection representations - the problem is the stark difference between simple links that are dependent on the trace data path connection that suzuki proposes - where the connection information is implied as a trace bus, and the requirement for multiple data entries per CTI connection. i.e. the CTI connection data includes not only the device connected, but details of input signals, output signals and purposes of those signals on a per connected device basis.
My preference is to do the update work once - especially as the latest of Suzukis patch sets drops all the sysfs work.
[1]. https://patchwork.kernel.org/cover/10901109/
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c new file mode 100644 index 000000000000..9b6749621dcb --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2019 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/cpu.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/pm_wakeup.h> +#include <linux/seq_file.h> +#include <linux/smp.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/uaccess.h>
As far as I can tell this file can be compile with the following headers:
coresight.h device.h kernel.h spinlock.h sysfs.h types.h
Everything else can be removed and re-introduced as you need them.
Will do.
+#include "coresight-cti.h"
+/* basic attributes */ +static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
Once applied the second and third line don't line up with the 's' of the first line.
will fix
+{
int enable_req;
bool enabled, powered, cpuid;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
ssize_t size = 0;
enable_req = atomic_read(&drvdata->config.enable_req_count);
spin_lock(&drvdata->spinlock);
powered = drvdata->config.hw_powered;
enabled = drvdata->config.hw_enabled;
cpuid = drvdata->ctidev.cpu;
spin_unlock(&drvdata->spinlock);
if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
} else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
} else {
size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n",
enabled ? "enabled" : "disabled");
}
This interface seems a little complex to me. I will wait to see how things evolved in the coming patches - maybe I'll understand better then.
The CTI driver maintains a powered state./ enable ref count to know if it is safe to write through any programming - otherwise the internal state is updated but not written to the hardware till powered and enabled. this data here makes it easy to see that state - though it is a little different from the other drivers. At this point handy for testing, but the cpu / powered info could appear elsewhere / in there RO ovn sysfs attributes?
return size;
+}
+static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int ret = 0;
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
if (val)
ret = cti_enable(drvdata->csdev, NULL);
else
ret = cti_disable(drvdata->csdev, NULL);
if (ret)
return ret;
return size;
+} +static DEVICE_ATTR_RW(enable);
+/* attribute and group sysfs tables. */ +static struct attribute *coresight_cti_attrs[] = {
&dev_attr_enable.attr,
NULL,
+};
+static const struct attribute_group coresight_cti_group = {
.attrs = coresight_cti_attrs,
+};
+const struct attribute_group *coresight_cti_groups[] = {
&coresight_cti_group,
NULL,
+}; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c new file mode 100644 index 000000000000..0a0fb6d48237 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
s/2018/2019
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/amba/bus.h> +#include <linux/clk.h> +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/cpu.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/pm_wakeup.h> +#include <linux/seq_file.h> +#include <linux/smp.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <asm/local.h> +#include <asm/sections.h>
+#include "coresight-cti.h"
Same comment as above - please get rid of all the headers that aren't currenlty needed.
+/**
- CTI devices can be associated with a PE, or be connected to CoreSight
- hardware. We have a list of all CTIs, and a subset array associated
- with individual PEs for pwr management as they will power up in the
- PE power domain.
- At this point we assume that the none CPU CTIs are always powered as
- we do with sinks etc.
s/none/non
- We leave the client to figure out if all the CTIs are interconnected with
- the same CTM, in general this is the case but does not always have to be.
- */
While reading the bindings and the documentation included in this set (many thanks for that), I was wondering if you have seen and tested your work on systems where more than one CTM is present.
I believe there are multi-socket server systems that will have separate CTM connections ( and separate trace subsystems for that matter). Not got access to such a system - but driver functional testing is trivial - simply temporarily assign CTIs on a one CTM system to different virtual CTMs. Which I have yet to do.
+struct ect_node {
struct cti_drvdata *cti_drv;
struct list_head next;
+};
+/* net of CTI devices connected via CTM */ +LIST_HEAD(ect_net);
+/* protect the list */ +static DEFINE_MUTEX(ect_mutex);
+/* quick reference for CPU CTIs */ +static struct cti_drvdata *cti_cpu_drv[NR_CPUS];
+/* number of registered CTI devices */ +static int cti_count;
+/* number of cpu associated CTI devices in use */ +static int cti_cpu_count;
+#define csdev_to_cti_drvdata(csdev) \
dev_get_drvdata(csdev->dev.parent)
+/* write set of regs to hardware - call with spinlock claimed */ +static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +{
struct cti_config *config = &drvdata->config;
int i;
CS_UNLOCK(drvdata->base);
/* disable CTI before writing registers */
writel_relaxed(0, drvdata->base + CTICONTROL);
/* write the CTI trigger registers */
for (i = 0; i < config->nr_trig_max; i++) {
writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i));
writel_relaxed(config->ctiouten[i],
drvdata->base + CTIOUTEN(i));
}
/* other regs */
writel_relaxed(config->ctigate, drvdata->base + CTIGATE);
writel_relaxed(config->asicctl, drvdata->base + ASICCTL);
writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET);
/* re-enable CTI */
writel_relaxed(1, drvdata->base + CTICONTROL);
CS_LOCK(drvdata->base);
+}
+static void cti_enable_hw_smp_call(void *info) +{
struct cti_drvdata *drvdata = info;
cti_write_all_hw_regs(drvdata);
+}
+/* write regs to hardware and enable */ +static int cti_enable_hw(void *info) +{
struct cti_drvdata *drvdata = info;
struct cti_config *config = &drvdata->config;
int rc = 0;
pm_runtime_get_sync(drvdata->dev);
spin_lock(&drvdata->spinlock);
/* no need to do anything if enabled or unpowered*/
if (config->hw_enabled || !config->hw_powered)
goto cti_not_enabled;
/* claim the device */
rc = coresight_claim_device(drvdata->base);
if (rc)
goto cti_not_enabled;
if (drvdata->ctidev.cpu >= 0) {
dev_info(drvdata->dev, "cti enable smp call for cpu %d\n",
drvdata->ctidev.cpu);
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
if (rc)
goto cti_not_enabled;
} else {
dev_info(drvdata->dev, "cti enable not cpu call\n");
cti_write_all_hw_regs(drvdata);
}
config->hw_enabled = true;
spin_unlock(&drvdata->spinlock);
return rc;
/* not enabled in this call */
+cti_not_enabled:
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return rc;
+}
+/* disable hardware */ +static int cti_disable_hw(void *info) +{
struct cti_drvdata *drvdata = info;
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* no need to do anything if disabled or cpu unpowered*/
if (!config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
return 0;
}
CS_UNLOCK(drvdata->base);
/* disable CTI */
writel_relaxed(0, drvdata->base + CTICONTROL);
config->hw_enabled = false;
coresight_disclaim_device_unlocked(drvdata->base);
CS_LOCK(drvdata->base);
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return 0;
+}
+static void cti_set_default_config(struct cti_config *config) +{
/* Most regs default to 0 as zalloc'ed except...*/
config->trig_filter_enable = 1;
config->ctigate = ((u32)0x1 << config->nr_ctm_channels) - 1;
Please use the macro GENMASK().
Will do
atomic_set(&config->enable_req_count, 0);
+}
+/*
- Add a connection entry to the list of connections for this
- CTI device.
- */
+static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+{
struct cti_trig_con *tc;
struct cti_device *cti_dev = &drvdata->ctidev;
tc = kzalloc(sizeof(struct cti_trig_con), GFP_KERNEL);
if (tc == 0)
return -ENOMEM;
tc->con_dev = csdev;
/*
* Prefer actual associated CS device dev name to supplied value -
* which is likely to be node name / other conn name.
*/
if (csdev)
tc->con_dev_name = kstrdup(dev_name(&csdev->dev), GFP_KERNEL);
else if (assoc_dev_name != NULL)
tc->con_dev_name = kstrdup(assoc_dev_name, GFP_KERNEL);
tc->con_in = in_info;
tc->con_out = out_info;
list_add_tail(&tc->node, &cti_dev->trig_cons);
cti_dev->nr_trig_con++;
/* add connection usage bit info to overall info */
drvdata->config.trig_in_use |= in_info->used_mask;
drvdata->config.trig_out_use |= out_info->used_mask;
return 0;
+}
+/*
- Add a default connection if nothing else is specified.
- single connection based on max in/out info, no assoc device
- */
+int cti_add_default_connection(struct cti_drvdata *drvdata) +{
int ret = 0, idx;
struct cti_trig_grp *in = 0, *out = 0;
int n_trigs = drvdata->config.nr_trig_max;
u32 n_trig_mask = (0x1 << n_trigs) - 1;
GENMASK().
int *in_sig_types = 0, *out_sig_types = 0;
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in) {
ret = -ENOMEM;
goto conn_add_err;
}
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out) {
ret = -ENOMEM;
goto conn_add_err;
}
in_sig_types = kzalloc(sizeof(int) * n_trigs, GFP_KERNEL);
if (!in_sig_types) {
ret = -ENOMEM;
goto conn_add_err;
}
out_sig_types = kzalloc(sizeof(int) * n_trigs, GFP_KERNEL);
if (!out_sig_types) {
ret = -ENOMEM;
goto conn_add_err;
}
/* signal types marked as default CTITRIG_GEN_IO */
for (idx = 0; idx < n_trigs; idx++) {
in_sig_types[idx] = CTITRIG_GEN_IO;
out_sig_types[idx] = CTITRIG_GEN_IO;
}
/*
* Assume max trigs for in and out,
* all used, default sig types allocated
*/
in->nr_sigs = n_trigs;
in->used_mask = n_trig_mask;
in->sig_types = in_sig_types;
out->nr_sigs = n_trigs;
out->used_mask = n_trig_mask;
out->sig_types = out_sig_types;
ret = cti_add_connection_entry(drvdata, in, out, NULL, "default");
if (ret)
goto conn_add_err;
return ret;
+conn_add_err:
kfree(in);
kfree(out);
kfree(out_sig_types);
kfree(in_sig_types);
return ret;
+}
+/** cti ect operations **/ +int cti_enable(struct coresight_device *csdev, void *__unused) +{
int rc;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
atomic_inc(&drvdata->config.enable_req_count);
rc = cti_enable_hw(drvdata);
if (rc)
atomic_dec(&drvdata->config.enable_req_count);
return rc;
+}
+int cti_disable(struct coresight_device *csdev, void *__unused) +{
int rc = 0;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
if (!atomic_dec_return(&drvdata->config.enable_req_count)) {
rc = cti_disable_hw(drvdata);
if (rc)
atomic_inc(&drvdata->config.enable_req_count);
}
return rc;
+}
+const struct coresight_ops_ect cti_ops_ect = {
.enable = cti_enable,
.disable = cti_disable,
+};
+const struct coresight_ops cti_ops = {
.ect_ops = &cti_ops_ect,
+};
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
int ret = 0;
u32 devid;
void __iomem *base;
struct device *dev = &adev->dev;
struct cti_drvdata *drvdata = NULL;
struct coresight_desc cti_desc;
struct coresight_platform_data *pdata = NULL;
struct resource *res = &adev->res;
struct device_node *np = adev->dev.of_node;
struct ect_node *ect_nd = NULL;
/* boilerplate code to set up the basics */
if (np) {
pdata = of_get_coresight_cti_platform_data(dev, np);
if (IS_ERR(pdata)) {
dev_info(dev, "of_get_coresight_ect_platform err\n");
return PTR_ERR(pdata);
}
dev->platform_data = pdata;
}
/* node to keep track of CTI net */
ect_nd = devm_kzalloc(dev, sizeof(struct ect_node), GFP_KERNEL);
if (!ect_nd) {
ret = -ENOMEM;
goto err_out;
}
/* driver data*/
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
goto err_out;
}
/* Validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base)) {
ret = PTR_ERR(base);
dev_info(dev, "%s, remap err\n", __func__);
goto err_out;
}
drvdata->base = base;
/* links between dev and drvdata*/
drvdata->dev = dev;
dev_set_drvdata(dev, drvdata);
/* default CTI device info */
drvdata->ctidev.cpu = pdata->cpu;
drvdata->ctidev.nr_trig_con = 0;
drvdata->ctidev.ctm_id = 0;
INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
spin_lock_init(&drvdata->spinlock);
/* look at the HW DEVID register for some of the HW settings */
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8);
Every time you use hard coded values like this, please introduce a comments that explains what you are doing. For example:
"DEVID.NUM_TRIG [15:8] : Indicates the maximum number of triggers."
That way we don't always have to go back to the TRM to understand the code.
/*
* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
drvdata->config.nr_trig_max, CTIINOUTEN_MAX);
drvdata->config.nr_trig_max = CTIINOUTEN_MAX;
}
drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
/* additional parse the .dts for connections and signals */
of_cti_get_hw_data(dev, np, drvdata);
/* initialise default driver values */
cti_set_default_config(&drvdata->config);
/* setup cpu related CTI devices, otherwise assume powered */
drvdata->config.hw_powered = true;
/* set up coresight component description */
cti_desc.pdata = pdata;
cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;
Out of curiosity, do you expect other ECT device types?
CTM is another ECT device type though currently does not require a driver. There are a number of ARM designed CTIs - which are all currently covered by this CTI driver, though we cannot exclude future / partner designed devices needing there own. Finally - this is the correct terminology defined by the ARM TRM - so I feel it is best to use it accurately.
cti_desc.ops = &cti_ops;
cti_desc.groups = coresight_cti_groups;
cti_desc.dev = dev;
drvdata->csdev = coresight_register(&cti_desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
pr_err("%s: CS register failed\n", pdata->name);
None of the other drivers have this - please remove.
Done.
goto err_out;
}
/* add to list of CTI devices */
mutex_lock(&ect_mutex);
ect_nd->cti_drv = drvdata;
list_add(&ect_nd->next, &ect_net);
/* if cpu bound add to CPU array */
if (drvdata->ctidev.cpu >= 0) {
cti_cpu_drv[drvdata->ctidev.cpu] = drvdata;
cti_cpu_count++;
}
mutex_unlock(&ect_mutex);
/* all done - dec pm refcount */
pm_runtime_put(&adev->dev);
dev_info(dev, "%s: initialized\n", pdata->name);
cti_count++;
return 0;
+err_out:
kfree(drvdata);
kfree(ect_nd);
Because devm_kzalloc() was used to allocate the memory, the device core will automagically take care of releasing the memory for you upon error.
OK - didn't realise.
return ret;
+}
+/* free all connection info from the driver */ +void cti_free_conn_info(struct cti_drvdata *drvdata) +{
struct cti_trig_con *tc, *tc_tmp;
list_for_each_entry_safe(tc, tc_tmp,
&drvdata->ctidev.trig_cons, node) {
kfree(tc->con_in->sig_types);
kfree(tc->con_in);
kfree(tc->con_out->sig_types);
kfree(tc->con_out);
kfree(tc->con_dev_name);
list_del(&tc->node);
kfree(tc);
}
+}
+/*
- Free up CTI specific resources
- called from coresight_device_release on coresight_unregister.
- */
+void cti_device_release(struct device *dev) +{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct ect_node *ect_item, *ect_tmp;
/* free up resources associated with the cti connections */
if (drvdata->ctidev.cpu >= 0)
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
Since cti_cpu_drv[] is global it needs to be protected by ect_mutex.
/* clear the connection list items */
cti_free_conn_info(drvdata);
/* remove from the list */
mutex_lock(&ect_mutex);
list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
kfree(ect_item);
goto ect_list_item_removed;
}
}
+ect_list_item_removed:
mutex_unlock(&ect_mutex);
kfree(drvdata);
+} +EXPORT_SYMBOL_GPL(cti_device_release);
+static struct amba_cs_uci_id uci_id_cti[] = {
{
/* CTI UCI data */
.devarch = 0x47701a14, /* devarch value for CTI v2 */
.devarch_mask = 0xfff0ffff,
.devtype = 0x00000014, /* maj(0x4-debug) min(0x1-ECT) */
}
+};
+static const struct amba_id cti_ids[] = {
CS_AMBA_ID(0x000bb906), /* Coresight CTI (SoC 400), C-A72, C-A57 */
CS_AMBA_ID(0x000bb922), /* CTI - C-A8 */
CS_AMBA_ID(0x000bb9a8), /* CTI - C-A53 */
CS_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */
CS_AMBA_UCI_ID(0x000bb9da, uci_id_cti), /* CTI - C-A35 */
CS_AMBA_UCI_ID(0x000bb9ed, uci_id_cti), /* Coresight CTI (SoC 600) */
{ 0, 0},
+};
+static struct amba_driver cti_driver = {
.drv = {
.name = "coresight-cti",
.owner = THIS_MODULE,
.suppress_bind_attrs = true,
},
.probe = cti_probe,
.id_table = cti_ids,
+}; +builtin_amba_driver(cti_driver); diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h new file mode 100644 index 000000000000..c1af6cf9c2fe --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CTI_H +#define _CORESIGHT_CORESIGHT_CTI_H
+#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h"
+/*
- Device registers
- 0x000 - 0x144: CTI programming and status
- 0xEDC - 0xEF8: CTI integration test.
- 0xF00 - 0xFFC: Coresight management registers.
- */
+/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */ +/* management registers */ +#define CTIDEVAFF0 0xFA8 +#define CTIDEVAFF1 0xFAC
On my side some of the defines above are misaligned - is it the same on your side?
Yes if reading file loaded into emacs normally, no if emacs in "whitespace-mode" which I use to correct this sort of errror. Which makes no sense whatsoever. I'll format outside of whitespace mode.
+/*
- CTI CSSoc 600 has a max of 32 trigger signals per direction.
- CTI CSSoc 400 has 8 IO triggers - other CTIs can be impl def.
- Max of in and out defined in the DEVID register.
- pick up actual number used from .dts parameters if present.
- */
+#define CTIINOUTEN_MAX 32
+/*
- CTI Trigger signal type definitions.
- Labels for certain trigger interconnections between the CTI
- and standard CS components.
- Must match values in ./include/dt-bindings/arm/coresight-cti.h
- */
+#define CTITRIG_GEN_IO 0 +#define CTITRIG_GEN_INTREQ 1 +#define CTITRIG_GEN_INTACK 2 +#define CTITRIG_GEN_HALTREQ 3 +#define CTITRIG_GEN_RESTARTREQ 4 +#define CTITRIG_PE_EDBGREQ 5 +#define CTITRIG_PE_DBGRESTART 6 +#define CTITRIG_PE_CTIIRQ 7 +#define CTITRIG_PE_PMUIRQ 8 +#define CTITRIG_PE_DBGTRIGGER 9 +#define CTITRIG_ETM_EXTOUT 10 +#define CTITRIG_ETM_EXTIN 11 +#define CTITRIG_SNK_FULL 12 +#define CTITRIG_SNK_ACQCOMP 13 +#define CTITRIG_SNK_FLUSHCOMP 14 +#define CTITRIG_SNK_FLUSHIN 15 +#define CTITRIG_SNK_TRIGIN 16 +#define CTITRIG_STM_ASYNCOUT 17 +#define CTITRIG_STM_TOUT_SPTE 18 +#define CTITRIG_STM_TOUT_SW 19 +#define CTITRIG_STM_TOUT_HETE 20 +#define CTITRIG_STM_HWEVENT 21 +#define CTITRIG_ELA_TSTART 22 +#define CTITRIG_ELA_TSTOP 23 +#define CTITRIG_ELA_DBGREQ 24
+/**
- Group of related trigger signals
- @nr_sigs: number of signals in the group.
- @used_mask: bitmask representing the signal indexes in the group.
- @sig_types: array of types for the signals, length nr_sigs.
- */
+struct cti_trig_grp {
int nr_sigs;
u32 used_mask;
int *sig_types;
+};
+/**
- Trigger connection - connection between a CTI and other (coresight) device
- lists input and output trigger signals for the device
- @con_in: connected CTIIN signals for the device.
- @con_out: connected CTIOUT signals for the device.
- @con_dev: coresight device connected to the CTI, NULL if not CS device
- @con_dev_name: name of connected device (CS or CPU)
- @node: entry node in list of connections.
- */
+struct cti_trig_con {
struct cti_trig_grp *con_in;
struct cti_trig_grp *con_out;
struct coresight_device *con_dev;
char *con_dev_name;
struct list_head node;
+};
+/**
- struct cti_device - description of CTI device properties.
- @nt_trig_con: Number of external devices connected to this device.
- @ctm_id: which CTM this device is connected to (by default it is
assumed there is a single CTM per SoC, ID 0).
- @trig_cons: list of connections to this device.
- @cpu: CPU ID if associated with CPU, -1 otherwise.
One line too many.
- */
+struct cti_device {
int nr_trig_con;
u32 ctm_id;
struct list_head trig_cons;
int cpu;
+};
The structures defined above don't have a tabulation between their type and variable name - but this one (and the following) do. Though the community tends to lean toward the "no tab" format I don't think there is an enforced rule. As such I will leave it to you to choose what you prefer. On the flip side I would ask that you enact the same pattern within the file.
OK
+/**
- struct cti_config - configuration of the CTI device hardware
- hardware description from RO ID regs
- @nr_trig_max: Max number of trigger signals implemented on device.
(max of trig_in or trig_out)
- @nr_ctm_channels: number of available CTM channels
- cti enable control
- @enable_req_count: CTI is enabled alongside >=1 associated devices.
- @hw_enabled: true if hw is currently enabled.
- @hw_powered: true if associated cpu powered on, or no cpu.
- registered triggers and filtering
- @trig_in_use: bitfield of in triggers registered as in use.
- @trig_out_use: bitfield of out triggers registered as in use.
- @trig_out_filter: bitfield of out triggers that are blocked if filter
- enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
- @ctiinout_sel: register selector for INEN and OUTEN regs.
- @ctiinen: enable input trigger to a channel.
- @ctiouten: enable output trigger from a channel.
- @ctigate: gate channel output from CTI to CTM.
- */
+struct cti_config {
/* hardware description */
int nr_ctm_channels;
int nr_trig_max;
/* cti enable control */
atomic_t enable_req_count;
bool hw_enabled;
bool hw_powered;
/* registered triggers and filtering */
u32 trig_in_use;
u32 trig_out_use;
u32 trig_out_filter;
int trig_filter_enable;
/* cti cross trig programmable regs */
u32 ctiappset;
u8 ctiinout_sel;
u32 ctiinen[CTIINOUTEN_MAX];
u32 ctiouten[CTIINOUTEN_MAX];
u32 ctigate;
u32 asicctl;
+};
+/**
- struct cti_drvdata - specifics for the CTI device
- @base: Memory mapped base address for this component.
- @dev: The device entity associated to this component.
- @csdev: Standard CoreSight device information.
- @ctidev: Extra information needed by the CTI/CTM framework.
- @spinlock: Control data access to one at a time.
- @config: Configuration data for this CTI device.
- */
+struct cti_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct cti_device ctidev;
spinlock_t spinlock;
struct cti_config config;
+};
+/* private cti driver fns & vars */ +extern const struct attribute_group *coresight_cti_groups[]; +int cti_add_default_connection(struct cti_drvdata *drvdata); +int cti_enable(struct coresight_device *csdev, void *__unused); +int cti_disable(struct coresight_device *csdev, void *__unused);
+#ifdef CONFIG_OF +extern int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata);
+extern struct coresight_platform_data * +of_get_coresight_cti_platform_data(struct device *dev,
const struct device_node *node);
+#else +static inline int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{ return 0; }; +static inline struct coresight_platform_data * +of_get_coresight_cti_platform_data(struct device *dev,
const struct device_node *node) { return NULL; }
+#endif
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */ diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index e0684d06e9ee..8e9e5b6ad866 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -161,6 +161,13 @@ static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; } static inline int etm_writel_cp14(u32 off, u32 val) { return 0; } #endif
+#ifdef CONFIG_CORESIGHT_CTI +extern void cti_device_release(struct device *dev); +#else +static inline void cti_device_release(struct device *dev) {}; +#endif
/*
- Macros and inline functions to handle CoreSight UCI data and driver
- private data in AMBA ID table entries, and extract data values.
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 29cef898afba..8db3d1a5314f 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -967,12 +967,20 @@ static struct device_type coresight_dev_type[] = { { .name = "helper", },
{
.name = "ect",
},
};
static void coresight_device_release(struct device *dev) { struct coresight_device *csdev = to_coresight_device(dev);
/* additional info to release for CTI */
if ((csdev->type == CORESIGHT_DEV_TYPE_ECT) &&
(csdev->subtype.ect_subtype == CORESIGHT_DEV_SUBTYPE_ECT_CTI))
cti_device_release(dev);
kfree(csdev->conns); kfree(csdev->refcnt); kfree(csdev);
diff --git a/drivers/hwtracing/coresight/of_coresight-cti.c b/drivers/hwtracing/coresight/of_coresight-cti.c new file mode 100644 index 000000000000..379ca1113deb --- /dev/null +++ b/drivers/hwtracing/coresight/of_coresight-cti.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2019, The Linux Foundation. All rights reserved.
- */
+#include <linux/amba/bus.h> +#include <linux/clk.h> +#include <linux/coresight.h> +#include <linux/cpumask.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_graph.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h>
+#include "coresight-cti.h"
+#ifdef CONFIG_OF
+/*
- CTI can be bound to a CPU, or a system device.
- Reflect this in the return value and do not default to cpu 0
- */
+static int of_cti_get_cpu(const struct device_node *node) +{
int cpu;
struct device_node *dn;
dn = of_parse_phandle(node, "cpu", 0);
/* CTI Affinity defaults to no cpu */
if (!dn)
return -1;
cpu = of_cpu_node_to_id(dn);
of_node_put(dn);
/* No Affinity if no cpu nodes are found */
return (cpu < 0) ? -1 : cpu;
+}
+/* get the hardware configuration & connection data. */ +int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
int rc = 0;
struct cti_device *cti_dev = &drvdata->ctidev;
/* if no connections, just add a single default based on max IN-OUT */
if (cti_dev->nr_trig_con == 0)
rc = cti_add_default_connection(drvdata);
return rc;
+}
+/*
- Platform data for the CTI does not have the same connection info
- as the trace path components. Implement specific function to
- avoid warnings of missing connections.
- */
+struct coresight_platform_data * +of_get_coresight_cti_platform_data(struct device *dev,
const struct device_node *node)
+{
struct coresight_platform_data *pdata;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return ERR_PTR(-ENOMEM);
/* Use device name as sysfs handle */
pdata->name = dev_name(dev);
/* Look for a CPU ID at the current node level */
pdata->cpu = of_cti_get_cpu(node);
return pdata;
+}
+#endif diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 7b87965f7a65..d1c1ed17d2ca 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -41,6 +41,7 @@ enum coresight_dev_type { CORESIGHT_DEV_TYPE_LINKSINK, CORESIGHT_DEV_TYPE_SOURCE, CORESIGHT_DEV_TYPE_HELPER,
CORESIGHT_DEV_TYPE_ECT,
};
enum coresight_dev_subtype_sink { @@ -68,6 +69,12 @@ enum coresight_dev_subtype_helper { CORESIGHT_DEV_SUBTYPE_HELPER_CATU, };
+/* Embedded Cross Trigger (ECT) sub-types */ +enum coresight_dev_subtype_ect {
CORESIGHT_DEV_SUBTYPE_ECT_NONE,
CORESIGHT_DEV_SUBTYPE_ECT_CTI
+};
/**
- union coresight_dev_subtype - further characterisation of a type
- @sink_subtype: type of sink this component is, as defined
@@ -78,6 +85,8 @@ enum coresight_dev_subtype_helper {
by @coresight_dev_subtype_source.
- @helper_subtype: type of helper this component is, as defined
by @coresight_dev_subtype_helper.
- @ect_subtype: type of cross trigger this component is, as
*/
defined by @coresight_dev_subtype_ect
union coresight_dev_subtype { /* We have some devices which acts as LINK and SINK */ @@ -87,6 +96,7 @@ union coresight_dev_subtype { }; enum coresight_dev_subtype_source source_subtype; enum coresight_dev_subtype_helper helper_subtype;
enum coresight_dev_subtype_ect ect_subtype;
};
/** @@ -180,6 +190,7 @@ struct coresight_device { #define sink_ops(csdev) csdev->ops->sink_ops #define link_ops(csdev) csdev->ops->link_ops #define helper_ops(csdev) csdev->ops->helper_ops +#define ect_ops(csdev) csdev->ops->ect_ops
/**
- struct coresight_ops_sink - basic operations for a sink
@@ -245,11 +256,26 @@ struct coresight_ops_helper { int (*disable)(struct coresight_device *csdev, void *data); };
+/**
- struct coresight_ops_ect - Ops for an embedded cross trigger device
- All operations could pass in a device specific data, which could
- help the ect device to determine what to do.
- @enable : Enable the device
- @disable : Disable the device
- */
+struct coresight_ops_ect {
int (*enable)(struct coresight_device *csdev, void *data);
int (*disable)(struct coresight_device *csdev, void *data);
+};
struct coresight_ops { const struct coresight_ops_sink *sink_ops; const struct coresight_ops_link *link_ops; const struct coresight_ops_source *source_ops; const struct coresight_ops_helper *helper_ops;
const struct coresight_ops_ect *ect_ops;
};
#ifdef CONFIG_CORESIGHT
2.20.1
Redone per your suggestions.
thanks for the review
Mike
Mike
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
Good day Mike,
On Thu, 23 May 2019 at 09:18, Mike Leach mike.leach@linaro.org wrote:
Hi Mathieu,
On Mon, 6 May 2019 at 22:50, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, May 01, 2019 at 09:49:33AM +0100, Mike Leach wrote:
This introduces a baseline CTI driver and associated configuration files.
Driver will probe for the device on the AMBA bus, and load the CTI driver on CoreSight ID match to CTI IDs in tables.
Initial sysfs support for enable / disable provided.
Default CTI interconnection data is generated based on hardware register signal counts, with no additional connection information
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/Kconfig | 13 + drivers/hwtracing/coresight/Makefile | 4 + .../hwtracing/coresight/coresight-cti-sysfs.c | 97 ++++ drivers/hwtracing/coresight/coresight-cti.c | 522 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 228 ++++++++ drivers/hwtracing/coresight/coresight-priv.h | 7 + drivers/hwtracing/coresight/coresight.c | 8 + .../hwtracing/coresight/of_coresight-cti.c | 81 +++ include/linux/coresight.h | 26 + 9 files changed, 986 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti-sysfs.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.h create mode 100644 drivers/hwtracing/coresight/of_coresight-cti.c
Compiling this patch gave me the following compilation warning:
/home/mpoirier/work/coresight/kernel-maint/drivers/hwtracing/coresight/coresight-cti-sysfs.c: In function ‘enable_show’: /home/mpoirier/work/coresight/kernel-maint/drivers/hwtracing/coresight/coresight-cti-sysfs.c:52:19: warning: comparison of constant ‘0’ with boolean expression is always true [-Wbool-compare] } else if (cpuid >= 0) {
mpoirier@xps15:~/work/coresight/kernel-maint$ aarch64-linux-gnu-gcc --version aarch64-linux-gnu-gcc (Linaro GCC 7.2-2017.11) 7.2.1 20171011 Copyright (C) 2017 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
OK - looks like it was fixed in the next patch, will fix here.
Checkpatch.pl also found problems with several patches and I had to rebase your work on the latest coresight-next tree.
The set was base on upstream Linux 5.1-rc5. Passed through checkpatch.pl with no errors on my side. (exception being Kconfig where it complained about lack of decsription even though one present ) I'll recheck next release.
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index ad34380cac49..9f1cac64f357 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -114,4 +114,17 @@ config CORESIGHT_CPU_DEBUG properly, please refer Documentation/trace/coresight-cpu-debug.txt for detailed description and the example for usage.
+config CORESIGHT_CTI
bool "CoreSight Cross Trigger Interface (CTI) driver"
depends on ARM || ARM64
select CORESIGHT_LINKS_AND_SINKS
help
This driver provides support for CoreSight CTI and CTM components.
These provide hardware triggering events between CoreSight trace
source and sink components. These can be used to halt trace or
s/"used to halt"/"used to halt"
inject events into the trace stream. CTI also provides a software
control to trigger same halt events. This can provide fast trace
s/"to trigger same halt"/"to trigger the same halt"
halt compared to disabling sources and sinks normally in driver
software.
endif diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 41870ded51a3..77402cfc7174 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -19,3 +19,7 @@ obj-$(CONFIG_CORESIGHT_DYNAMIC_REPLICATOR) += coresight-dynamic-replicator.o obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o +obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o \
of_coresight-cti.o \
coresight-cti-sysfs.o
It is probably a good idea to base your work on Suzuki's ACPI pathset [1] where he got rid of of_coresight.c to favour a firmware agnostic naming convention. While doing so skip patches 34 to 36 as they relate to sysfs topology and we need to deal with that separately. Better yet, include patches 34 to 36 and propose a sysfs topology representation that works for both "data" and "signal" components.
I am aware of Suzukis work and the implications for re-work - but was not keen on developing against a moving target, rather look to more stable upstream - i.e. rc5 as mentioned above. There is nothing in suzukis patch set that this set _needs_ at present. Yes, there is the question of connection representations - the problem is the stark difference between simple links that are dependent on the trace data path connection that suzuki proposes - where the connection information is implied as a trace bus, and the requirement for multiple data entries per CTI connection. i.e. the CTI connection data includes not only the device connected, but details of input signals, output signals and purposes of those signals on a per connected device basis.
My preference is to do the update work once - especially as the latest of Suzukis patch sets drops all the sysfs work.
Though I haven't had a chance to reviewed it yet, I consider Suzuki's ACPI work to be pretty much done and will likely include his V4 in my tree sometime next week. As such it is probably worth rebasing your work on top of that. If anything changes it will be very small.
[1]. https://patchwork.kernel.org/cover/10901109/
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c new file mode 100644 index 000000000000..9b6749621dcb --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2019 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/cpu.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/pm_wakeup.h> +#include <linux/seq_file.h> +#include <linux/smp.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/uaccess.h>
As far as I can tell this file can be compile with the following headers:
coresight.h device.h kernel.h spinlock.h sysfs.h types.h
Everything else can be removed and re-introduced as you need them.
Will do.
+#include "coresight-cti.h"
+/* basic attributes */ +static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
Once applied the second and third line don't line up with the 's' of the first line.
will fix
+{
int enable_req;
bool enabled, powered, cpuid;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
ssize_t size = 0;
enable_req = atomic_read(&drvdata->config.enable_req_count);
spin_lock(&drvdata->spinlock);
powered = drvdata->config.hw_powered;
enabled = drvdata->config.hw_enabled;
cpuid = drvdata->ctidev.cpu;
spin_unlock(&drvdata->spinlock);
if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
} else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
} else {
size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n",
enabled ? "enabled" : "disabled");
}
This interface seems a little complex to me. I will wait to see how things evolved in the coming patches - maybe I'll understand better then.
The CTI driver maintains a powered state./ enable ref count to know if it is safe to write through any programming - otherwise the internal state is updated but not written to the hardware till powered and enabled.
I had the same dilemma when working on the etmv3/4 drivers. I ended up choosing to write the configuration to the HW the next time it would be possible to do so. For example, if device enabled write new config, if not simply cache the new value and write to HW when possible.
this data here makes it easy to see that state - though it is a little different from the other drivers. At this point handy for testing, but the cpu / powered info could appear elsewhere / in there RO ovn sysfs attributes?
return size;
+}
+static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int ret = 0;
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
if (val)
ret = cti_enable(drvdata->csdev, NULL);
else
ret = cti_disable(drvdata->csdev, NULL);
if (ret)
return ret;
return size;
+} +static DEVICE_ATTR_RW(enable);
+/* attribute and group sysfs tables. */ +static struct attribute *coresight_cti_attrs[] = {
&dev_attr_enable.attr,
NULL,
+};
+static const struct attribute_group coresight_cti_group = {
.attrs = coresight_cti_attrs,
+};
+const struct attribute_group *coresight_cti_groups[] = {
&coresight_cti_group,
NULL,
+}; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c new file mode 100644 index 000000000000..0a0fb6d48237 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
s/2018/2019
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/amba/bus.h> +#include <linux/clk.h> +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/cpu.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/pm_wakeup.h> +#include <linux/seq_file.h> +#include <linux/smp.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <asm/local.h> +#include <asm/sections.h>
+#include "coresight-cti.h"
Same comment as above - please get rid of all the headers that aren't currenlty needed.
+/**
- CTI devices can be associated with a PE, or be connected to CoreSight
- hardware. We have a list of all CTIs, and a subset array associated
- with individual PEs for pwr management as they will power up in the
- PE power domain.
- At this point we assume that the none CPU CTIs are always powered as
- we do with sinks etc.
s/none/non
- We leave the client to figure out if all the CTIs are interconnected with
- the same CTM, in general this is the case but does not always have to be.
- */
While reading the bindings and the documentation included in this set (many thanks for that), I was wondering if you have seen and tested your work on systems where more than one CTM is present.
I believe there are multi-socket server systems that will have separate CTM connections ( and separate trace subsystems for that matter). Not got access to such a system - but driver functional testing is trivial - simply temporarily assign CTIs on a one CTM system to different virtual CTMs. Which I have yet to do.
Since predicting how things will work is guaranteed to fail, I am weary of adding code we don't use or hasn't been tested on a real HW platform.
+struct ect_node {
struct cti_drvdata *cti_drv;
struct list_head next;
+};
+/* net of CTI devices connected via CTM */ +LIST_HEAD(ect_net);
+/* protect the list */ +static DEFINE_MUTEX(ect_mutex);
+/* quick reference for CPU CTIs */ +static struct cti_drvdata *cti_cpu_drv[NR_CPUS];
+/* number of registered CTI devices */ +static int cti_count;
+/* number of cpu associated CTI devices in use */ +static int cti_cpu_count;
+#define csdev_to_cti_drvdata(csdev) \
dev_get_drvdata(csdev->dev.parent)
+/* write set of regs to hardware - call with spinlock claimed */ +static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +{
struct cti_config *config = &drvdata->config;
int i;
CS_UNLOCK(drvdata->base);
/* disable CTI before writing registers */
writel_relaxed(0, drvdata->base + CTICONTROL);
/* write the CTI trigger registers */
for (i = 0; i < config->nr_trig_max; i++) {
writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i));
writel_relaxed(config->ctiouten[i],
drvdata->base + CTIOUTEN(i));
}
/* other regs */
writel_relaxed(config->ctigate, drvdata->base + CTIGATE);
writel_relaxed(config->asicctl, drvdata->base + ASICCTL);
writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET);
/* re-enable CTI */
writel_relaxed(1, drvdata->base + CTICONTROL);
CS_LOCK(drvdata->base);
+}
+static void cti_enable_hw_smp_call(void *info) +{
struct cti_drvdata *drvdata = info;
cti_write_all_hw_regs(drvdata);
+}
+/* write regs to hardware and enable */ +static int cti_enable_hw(void *info) +{
struct cti_drvdata *drvdata = info;
struct cti_config *config = &drvdata->config;
int rc = 0;
pm_runtime_get_sync(drvdata->dev);
spin_lock(&drvdata->spinlock);
/* no need to do anything if enabled or unpowered*/
if (config->hw_enabled || !config->hw_powered)
goto cti_not_enabled;
/* claim the device */
rc = coresight_claim_device(drvdata->base);
if (rc)
goto cti_not_enabled;
if (drvdata->ctidev.cpu >= 0) {
dev_info(drvdata->dev, "cti enable smp call for cpu %d\n",
drvdata->ctidev.cpu);
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
if (rc)
goto cti_not_enabled;
} else {
dev_info(drvdata->dev, "cti enable not cpu call\n");
cti_write_all_hw_regs(drvdata);
}
config->hw_enabled = true;
spin_unlock(&drvdata->spinlock);
return rc;
/* not enabled in this call */
+cti_not_enabled:
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return rc;
+}
+/* disable hardware */ +static int cti_disable_hw(void *info) +{
struct cti_drvdata *drvdata = info;
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* no need to do anything if disabled or cpu unpowered*/
if (!config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
return 0;
}
CS_UNLOCK(drvdata->base);
/* disable CTI */
writel_relaxed(0, drvdata->base + CTICONTROL);
config->hw_enabled = false;
coresight_disclaim_device_unlocked(drvdata->base);
CS_LOCK(drvdata->base);
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return 0;
+}
+static void cti_set_default_config(struct cti_config *config) +{
/* Most regs default to 0 as zalloc'ed except...*/
config->trig_filter_enable = 1;
config->ctigate = ((u32)0x1 << config->nr_ctm_channels) - 1;
Please use the macro GENMASK().
Will do
atomic_set(&config->enable_req_count, 0);
+}
+/*
- Add a connection entry to the list of connections for this
- CTI device.
- */
+static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+{
struct cti_trig_con *tc;
struct cti_device *cti_dev = &drvdata->ctidev;
tc = kzalloc(sizeof(struct cti_trig_con), GFP_KERNEL);
if (tc == 0)
return -ENOMEM;
tc->con_dev = csdev;
/*
* Prefer actual associated CS device dev name to supplied value -
* which is likely to be node name / other conn name.
*/
if (csdev)
tc->con_dev_name = kstrdup(dev_name(&csdev->dev), GFP_KERNEL);
else if (assoc_dev_name != NULL)
tc->con_dev_name = kstrdup(assoc_dev_name, GFP_KERNEL);
tc->con_in = in_info;
tc->con_out = out_info;
list_add_tail(&tc->node, &cti_dev->trig_cons);
cti_dev->nr_trig_con++;
/* add connection usage bit info to overall info */
drvdata->config.trig_in_use |= in_info->used_mask;
drvdata->config.trig_out_use |= out_info->used_mask;
return 0;
+}
+/*
- Add a default connection if nothing else is specified.
- single connection based on max in/out info, no assoc device
- */
+int cti_add_default_connection(struct cti_drvdata *drvdata) +{
int ret = 0, idx;
struct cti_trig_grp *in = 0, *out = 0;
int n_trigs = drvdata->config.nr_trig_max;
u32 n_trig_mask = (0x1 << n_trigs) - 1;
GENMASK().
int *in_sig_types = 0, *out_sig_types = 0;
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in) {
ret = -ENOMEM;
goto conn_add_err;
}
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out) {
ret = -ENOMEM;
goto conn_add_err;
}
in_sig_types = kzalloc(sizeof(int) * n_trigs, GFP_KERNEL);
if (!in_sig_types) {
ret = -ENOMEM;
goto conn_add_err;
}
out_sig_types = kzalloc(sizeof(int) * n_trigs, GFP_KERNEL);
if (!out_sig_types) {
ret = -ENOMEM;
goto conn_add_err;
}
/* signal types marked as default CTITRIG_GEN_IO */
for (idx = 0; idx < n_trigs; idx++) {
in_sig_types[idx] = CTITRIG_GEN_IO;
out_sig_types[idx] = CTITRIG_GEN_IO;
}
/*
* Assume max trigs for in and out,
* all used, default sig types allocated
*/
in->nr_sigs = n_trigs;
in->used_mask = n_trig_mask;
in->sig_types = in_sig_types;
out->nr_sigs = n_trigs;
out->used_mask = n_trig_mask;
out->sig_types = out_sig_types;
ret = cti_add_connection_entry(drvdata, in, out, NULL, "default");
if (ret)
goto conn_add_err;
return ret;
+conn_add_err:
kfree(in);
kfree(out);
kfree(out_sig_types);
kfree(in_sig_types);
return ret;
+}
+/** cti ect operations **/ +int cti_enable(struct coresight_device *csdev, void *__unused) +{
int rc;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
atomic_inc(&drvdata->config.enable_req_count);
rc = cti_enable_hw(drvdata);
if (rc)
atomic_dec(&drvdata->config.enable_req_count);
return rc;
+}
+int cti_disable(struct coresight_device *csdev, void *__unused) +{
int rc = 0;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
if (!atomic_dec_return(&drvdata->config.enable_req_count)) {
rc = cti_disable_hw(drvdata);
if (rc)
atomic_inc(&drvdata->config.enable_req_count);
}
return rc;
+}
+const struct coresight_ops_ect cti_ops_ect = {
.enable = cti_enable,
.disable = cti_disable,
+};
+const struct coresight_ops cti_ops = {
.ect_ops = &cti_ops_ect,
+};
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
int ret = 0;
u32 devid;
void __iomem *base;
struct device *dev = &adev->dev;
struct cti_drvdata *drvdata = NULL;
struct coresight_desc cti_desc;
struct coresight_platform_data *pdata = NULL;
struct resource *res = &adev->res;
struct device_node *np = adev->dev.of_node;
struct ect_node *ect_nd = NULL;
/* boilerplate code to set up the basics */
if (np) {
pdata = of_get_coresight_cti_platform_data(dev, np);
if (IS_ERR(pdata)) {
dev_info(dev, "of_get_coresight_ect_platform err\n");
return PTR_ERR(pdata);
}
dev->platform_data = pdata;
}
/* node to keep track of CTI net */
ect_nd = devm_kzalloc(dev, sizeof(struct ect_node), GFP_KERNEL);
if (!ect_nd) {
ret = -ENOMEM;
goto err_out;
}
/* driver data*/
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
goto err_out;
}
/* Validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base)) {
ret = PTR_ERR(base);
dev_info(dev, "%s, remap err\n", __func__);
goto err_out;
}
drvdata->base = base;
/* links between dev and drvdata*/
drvdata->dev = dev;
dev_set_drvdata(dev, drvdata);
/* default CTI device info */
drvdata->ctidev.cpu = pdata->cpu;
drvdata->ctidev.nr_trig_con = 0;
drvdata->ctidev.ctm_id = 0;
INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
spin_lock_init(&drvdata->spinlock);
/* look at the HW DEVID register for some of the HW settings */
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8);
Every time you use hard coded values like this, please introduce a comments that explains what you are doing. For example:
"DEVID.NUM_TRIG [15:8] : Indicates the maximum number of triggers."
That way we don't always have to go back to the TRM to understand the code.
/*
* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
drvdata->config.nr_trig_max, CTIINOUTEN_MAX);
drvdata->config.nr_trig_max = CTIINOUTEN_MAX;
}
drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
/* additional parse the .dts for connections and signals */
of_cti_get_hw_data(dev, np, drvdata);
/* initialise default driver values */
cti_set_default_config(&drvdata->config);
/* setup cpu related CTI devices, otherwise assume powered */
drvdata->config.hw_powered = true;
/* set up coresight component description */
cti_desc.pdata = pdata;
cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;
Out of curiosity, do you expect other ECT device types?
CTM is another ECT device type though currently does not require a driver.
Right right - we've been there with funnel and replicators. It is only a matter of time before CTMs to require a driver.
There are a number of ARM designed CTIs - which are all currently covered by this CTI driver, though we cannot exclude future / partner designed devices needing there own. Finally - this is the correct terminology defined by the ARM TRM - so I feel it is best to use it accurately.
Ok
cti_desc.ops = &cti_ops;
cti_desc.groups = coresight_cti_groups;
cti_desc.dev = dev;
drvdata->csdev = coresight_register(&cti_desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
pr_err("%s: CS register failed\n", pdata->name);
Hi Mike,
Except Mathieu's comments, below are my some minor comments.
On Wed, May 01, 2019 at 09:49:33AM +0100, Mike Leach wrote:
[...]
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c new file mode 100644 index 000000000000..0a0fb6d48237 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/amba/bus.h> +#include <linux/clk.h> +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/cpu.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/pm_wakeup.h> +#include <linux/seq_file.h> +#include <linux/smp.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <asm/local.h> +#include <asm/sections.h>
+#include "coresight-cti.h"
+/**
- CTI devices can be associated with a PE, or be connected to CoreSight
- hardware. We have a list of all CTIs, and a subset array associated
- with individual PEs for pwr management as they will power up in the
- PE power domain.
- At this point we assume that the none CPU CTIs are always powered as
- we do with sinks etc.
- We leave the client to figure out if all the CTIs are interconnected with
- the same CTM, in general this is the case but does not always have to be.
- */
+struct ect_node {
- struct cti_drvdata *cti_drv;
- struct list_head next;
+};
+/* net of CTI devices connected via CTM */ +LIST_HEAD(ect_net);
+/* protect the list */ +static DEFINE_MUTEX(ect_mutex);
+/* quick reference for CPU CTIs */ +static struct cti_drvdata *cti_cpu_drv[NR_CPUS];
+/* number of registered CTI devices */ +static int cti_count;
+/* number of cpu associated CTI devices in use */ +static int cti_cpu_count;
+#define csdev_to_cti_drvdata(csdev) \
- dev_get_drvdata(csdev->dev.parent)
+/* write set of regs to hardware - call with spinlock claimed */ +static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +{
- struct cti_config *config = &drvdata->config;
- int i;
- CS_UNLOCK(drvdata->base);
- /* disable CTI before writing registers */
- writel_relaxed(0, drvdata->base + CTICONTROL);
- /* write the CTI trigger registers */
- for (i = 0; i < config->nr_trig_max; i++) {
writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i));
writel_relaxed(config->ctiouten[i],
drvdata->base + CTIOUTEN(i));
- }
- /* other regs */
- writel_relaxed(config->ctigate, drvdata->base + CTIGATE);
- writel_relaxed(config->asicctl, drvdata->base + ASICCTL);
- writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET);
- /* re-enable CTI */
- writel_relaxed(1, drvdata->base + CTICONTROL);
- CS_LOCK(drvdata->base);
+}
+static void cti_enable_hw_smp_call(void *info) +{
- struct cti_drvdata *drvdata = info;
- cti_write_all_hw_regs(drvdata);
+}
+/* write regs to hardware and enable */ +static int cti_enable_hw(void *info) +{
- struct cti_drvdata *drvdata = info;
- struct cti_config *config = &drvdata->config;
- int rc = 0;
- pm_runtime_get_sync(drvdata->dev);
- spin_lock(&drvdata->spinlock);
- /* no need to do anything if enabled or unpowered*/
- if (config->hw_enabled || !config->hw_powered)
goto cti_not_enabled;
'config->hw_powered' has been assigned to 'true' in the function cti_probe(), thus the checking at here is redundant?
- /* claim the device */
- rc = coresight_claim_device(drvdata->base);
- if (rc)
goto cti_not_enabled;
- if (drvdata->ctidev.cpu >= 0) {
dev_info(drvdata->dev, "cti enable smp call for cpu %d\n",
drvdata->ctidev.cpu);
Maybe we can remove the debugging logs, same as other CoreSight drivers?
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
if (rc)
goto cti_not_enabled;
- } else {
dev_info(drvdata->dev, "cti enable not cpu call\n");
cti_write_all_hw_regs(drvdata);
- }
- config->hw_enabled = true;
- spin_unlock(&drvdata->spinlock);
- return rc;
- /* not enabled in this call */
+cti_not_enabled:
- spin_unlock(&drvdata->spinlock);
- pm_runtime_put(drvdata->dev);
- return rc;
+}
+/* disable hardware */ +static int cti_disable_hw(void *info) +{
- struct cti_drvdata *drvdata = info;
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* no need to do anything if disabled or cpu unpowered*/
- if (!config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
return 0;
- }
- CS_UNLOCK(drvdata->base);
- /* disable CTI */
- writel_relaxed(0, drvdata->base + CTICONTROL);
- config->hw_enabled = false;
- coresight_disclaim_device_unlocked(drvdata->base);
- CS_LOCK(drvdata->base);
- spin_unlock(&drvdata->spinlock);
- pm_runtime_put(drvdata->dev);
- return 0;
+}
+static void cti_set_default_config(struct cti_config *config) +{
- /* Most regs default to 0 as zalloc'ed except...*/
- config->trig_filter_enable = 1;
- config->ctigate = ((u32)0x1 << config->nr_ctm_channels) - 1;
- atomic_set(&config->enable_req_count, 0);
+}
+/*
- Add a connection entry to the list of connections for this
- CTI device.
- */
+static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+{
- struct cti_trig_con *tc;
- struct cti_device *cti_dev = &drvdata->ctidev;
- tc = kzalloc(sizeof(struct cti_trig_con), GFP_KERNEL);
- if (tc == 0)
return -ENOMEM;
- tc->con_dev = csdev;
- /*
* Prefer actual associated CS device dev name to supplied value -
* which is likely to be node name / other conn name.
*/
- if (csdev)
tc->con_dev_name = kstrdup(dev_name(&csdev->dev), GFP_KERNEL);
- else if (assoc_dev_name != NULL)
tc->con_dev_name = kstrdup(assoc_dev_name, GFP_KERNEL);
- tc->con_in = in_info;
- tc->con_out = out_info;
- list_add_tail(&tc->node, &cti_dev->trig_cons);
- cti_dev->nr_trig_con++;
- /* add connection usage bit info to overall info */
- drvdata->config.trig_in_use |= in_info->used_mask;
- drvdata->config.trig_out_use |= out_info->used_mask;
- return 0;
+}
+/*
- Add a default connection if nothing else is specified.
- single connection based on max in/out info, no assoc device
- */
+int cti_add_default_connection(struct cti_drvdata *drvdata) +{
- int ret = 0, idx;
- struct cti_trig_grp *in = 0, *out = 0;
- int n_trigs = drvdata->config.nr_trig_max;
- u32 n_trig_mask = (0x1 << n_trigs) - 1;
- int *in_sig_types = 0, *out_sig_types = 0;
- in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!in) {
ret = -ENOMEM;
goto conn_add_err;
- }
- out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!out) {
ret = -ENOMEM;
goto conn_add_err;
- }
- in_sig_types = kzalloc(sizeof(int) * n_trigs, GFP_KERNEL);
- if (!in_sig_types) {
ret = -ENOMEM;
goto conn_add_err;
- }
- out_sig_types = kzalloc(sizeof(int) * n_trigs, GFP_KERNEL);
- if (!out_sig_types) {
ret = -ENOMEM;
goto conn_add_err;
- }
- /* signal types marked as default CTITRIG_GEN_IO */
- for (idx = 0; idx < n_trigs; idx++) {
in_sig_types[idx] = CTITRIG_GEN_IO;
out_sig_types[idx] = CTITRIG_GEN_IO;
- }
- /*
* Assume max trigs for in and out,
* all used, default sig types allocated
*/
- in->nr_sigs = n_trigs;
- in->used_mask = n_trig_mask;
- in->sig_types = in_sig_types;
- out->nr_sigs = n_trigs;
- out->used_mask = n_trig_mask;
- out->sig_types = out_sig_types;
- ret = cti_add_connection_entry(drvdata, in, out, NULL, "default");
- if (ret)
goto conn_add_err;
- return ret;
+conn_add_err:
- kfree(in);
- kfree(out);
- kfree(out_sig_types);
- kfree(in_sig_types);
- return ret;
+}
+/** cti ect operations **/ +int cti_enable(struct coresight_device *csdev, void *__unused) +{
- int rc;
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- atomic_inc(&drvdata->config.enable_req_count);
- rc = cti_enable_hw(drvdata);
- if (rc)
atomic_dec(&drvdata->config.enable_req_count);
Seems to me we can increment the counter after enabling hardware successfully, thus we can avoid to decrease it for failed case:
rc = cti_enable_hw(drvdata); if (rc) return rc;
atomic_inc(&drvdata->config.enable_req_count); return 0;
BTW, please consider to use spinlock to protect the flow, e.g. if use sysfs node to enable/disable CTI device concurrently, it might introduce mistaching between the counter and hardware enabling.
- return rc;
+}
+int cti_disable(struct coresight_device *csdev, void *__unused) +{
- int rc = 0;
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- if (!atomic_dec_return(&drvdata->config.enable_req_count)) {
rc = cti_disable_hw(drvdata);
if (rc)
atomic_inc(&drvdata->config.enable_req_count);
- }
- return rc;
Same at here, should we return -EBUSY if counter is not zero?
+}
+const struct coresight_ops_ect cti_ops_ect = {
- .enable = cti_enable,
- .disable = cti_disable,
+};
+const struct coresight_ops cti_ops = {
- .ect_ops = &cti_ops_ect,
+};
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
- int ret = 0;
- u32 devid;
- void __iomem *base;
- struct device *dev = &adev->dev;
- struct cti_drvdata *drvdata = NULL;
- struct coresight_desc cti_desc;
- struct coresight_platform_data *pdata = NULL;
- struct resource *res = &adev->res;
- struct device_node *np = adev->dev.of_node;
- struct ect_node *ect_nd = NULL;
- /* boilerplate code to set up the basics */
- if (np) {
pdata = of_get_coresight_cti_platform_data(dev, np);
if (IS_ERR(pdata)) {
dev_info(dev, "of_get_coresight_ect_platform err\n");
return PTR_ERR(pdata);
}
dev->platform_data = pdata;
- }
- /* node to keep track of CTI net */
- ect_nd = devm_kzalloc(dev, sizeof(struct ect_node), GFP_KERNEL);
- if (!ect_nd) {
ret = -ENOMEM;
goto err_out;
- }
- /* driver data*/
- drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
goto err_out;
- }
- /* Validity for the resource is already checked by the AMBA core */
- base = devm_ioremap_resource(dev, res);
- if (IS_ERR(base)) {
ret = PTR_ERR(base);
dev_info(dev, "%s, remap err\n", __func__);
goto err_out;
- }
- drvdata->base = base;
- /* links between dev and drvdata*/
- drvdata->dev = dev;
- dev_set_drvdata(dev, drvdata);
- /* default CTI device info */
- drvdata->ctidev.cpu = pdata->cpu;
- drvdata->ctidev.nr_trig_con = 0;
- drvdata->ctidev.ctm_id = 0;
- INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
- spin_lock_init(&drvdata->spinlock);
- /* look at the HW DEVID register for some of the HW settings */
- devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
- drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8);
- /*
* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
- if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
drvdata->config.nr_trig_max, CTIINOUTEN_MAX);
drvdata->config.nr_trig_max = CTIINOUTEN_MAX;
- }
- drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
- /* additional parse the .dts for connections and signals */
- of_cti_get_hw_data(dev, np, drvdata);
I don't see any additional parsing dts for connections in this patch, will assume the sequential patches will add related code.
- /* initialise default driver values */
- cti_set_default_config(&drvdata->config);
- /* setup cpu related CTI devices, otherwise assume powered */
- drvdata->config.hw_powered = true;
- /* set up coresight component description */
- cti_desc.pdata = pdata;
- cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
- cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;
- cti_desc.ops = &cti_ops;
- cti_desc.groups = coresight_cti_groups;
- cti_desc.dev = dev;
- drvdata->csdev = coresight_register(&cti_desc);
- if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
pr_err("%s: CS register failed\n", pdata->name);
goto err_out;
- }
- /* add to list of CTI devices */
- mutex_lock(&ect_mutex);
- ect_nd->cti_drv = drvdata;
- list_add(&ect_nd->next, &ect_net);
- /* if cpu bound add to CPU array */
- if (drvdata->ctidev.cpu >= 0) {
cti_cpu_drv[drvdata->ctidev.cpu] = drvdata;
cti_cpu_count++;
- }
- mutex_unlock(&ect_mutex);
- /* all done - dec pm refcount */
- pm_runtime_put(&adev->dev);
- dev_info(dev, "%s: initialized\n", pdata->name);
- cti_count++;
Can we remove 'cti_count'?
- return 0;
+err_out:
- kfree(drvdata);
- kfree(ect_nd);
- return ret;
+}
+/* free all connection info from the driver */ +void cti_free_conn_info(struct cti_drvdata *drvdata) +{
- struct cti_trig_con *tc, *tc_tmp;
- list_for_each_entry_safe(tc, tc_tmp,
&drvdata->ctidev.trig_cons, node) {
kfree(tc->con_in->sig_types);
kfree(tc->con_in);
kfree(tc->con_out->sig_types);
kfree(tc->con_out);
kfree(tc->con_dev_name);
list_del(&tc->node);
kfree(tc);
We can use a bit more neat code as below?
list_del_init(&tc->node); kfree(tc->con_in->sig_types); kfree(tc->con_in); kfree(tc->con_out->sig_types); kfree(tc->con_out); kfree(tc->con_dev_name); kfree(tc);
- }
+}
+/*
- Free up CTI specific resources
- called from coresight_device_release on coresight_unregister.
- */
+void cti_device_release(struct device *dev) +{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct ect_node *ect_item, *ect_tmp;
- /* free up resources associated with the cti connections */
- if (drvdata->ctidev.cpu >= 0)
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
- /* clear the connection list items */
- cti_free_conn_info(drvdata);
- /* remove from the list */
- mutex_lock(&ect_mutex);
- list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
kfree(ect_item);
goto ect_list_item_removed;
}
- }
+ect_list_item_removed:
- mutex_unlock(&ect_mutex);
- kfree(drvdata);
+} +EXPORT_SYMBOL_GPL(cti_device_release);
[...]
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h
[...]
+/**
- struct cti_config - configuration of the CTI device hardware
- hardware description from RO ID regs
- @nr_trig_max: Max number of trigger signals implemented on device.
(max of trig_in or trig_out)
- @nr_ctm_channels: number of available CTM channels
- cti enable control
- @enable_req_count: CTI is enabled alongside >=1 associated devices.
- @hw_enabled: true if hw is currently enabled.
- @hw_powered: true if associated cpu powered on, or no cpu.
- registered triggers and filtering
- @trig_in_use: bitfield of in triggers registered as in use.
- @trig_out_use: bitfield of out triggers registered as in use.
- @trig_out_filter: bitfield of out triggers that are blocked if filter
- enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
- @ctiinout_sel: register selector for INEN and OUTEN regs.
- @ctiinen: enable input trigger to a channel.
- @ctiouten: enable output trigger from a channel.
- @ctigate: gate channel output from CTI to CTM.
Please add comment for @asicctl.
- */
+struct cti_config {
- /* hardware description */
- int nr_ctm_channels;
- int nr_trig_max;
- /* cti enable control */
- atomic_t enable_req_count;
- bool hw_enabled;
- bool hw_powered;
- /* registered triggers and filtering */
- u32 trig_in_use;
- u32 trig_out_use;
- u32 trig_out_filter;
- int trig_filter_enable;
- /* cti cross trig programmable regs */
- u32 ctiappset;
- u8 ctiinout_sel;
- u32 ctiinen[CTIINOUTEN_MAX];
- u32 ctiouten[CTIINOUTEN_MAX];
- u32 ctigate;
- u32 asicctl;
+};
[...]
Thanks, Leo Yan
Hi Leo,
On Fri, 24 May 2019 at 09:44, Leo Yan leo.yan@linaro.org wrote:
Hi Mike,
Except Mathieu's comments, below are my some minor comments.
On Wed, May 01, 2019 at 09:49:33AM +0100, Mike Leach wrote:
[...]
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c new file mode 100644 index 000000000000..0a0fb6d48237 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/amba/bus.h> +#include <linux/clk.h> +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/cpu.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/pm_wakeup.h> +#include <linux/seq_file.h> +#include <linux/smp.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <asm/local.h> +#include <asm/sections.h>
+#include "coresight-cti.h"
+/**
- CTI devices can be associated with a PE, or be connected to CoreSight
- hardware. We have a list of all CTIs, and a subset array associated
- with individual PEs for pwr management as they will power up in the
- PE power domain.
- At this point we assume that the none CPU CTIs are always powered as
- we do with sinks etc.
- We leave the client to figure out if all the CTIs are interconnected with
- the same CTM, in general this is the case but does not always have to be.
- */
+struct ect_node {
struct cti_drvdata *cti_drv;
struct list_head next;
+};
+/* net of CTI devices connected via CTM */ +LIST_HEAD(ect_net);
+/* protect the list */ +static DEFINE_MUTEX(ect_mutex);
+/* quick reference for CPU CTIs */ +static struct cti_drvdata *cti_cpu_drv[NR_CPUS];
+/* number of registered CTI devices */ +static int cti_count;
+/* number of cpu associated CTI devices in use */ +static int cti_cpu_count;
+#define csdev_to_cti_drvdata(csdev) \
dev_get_drvdata(csdev->dev.parent)
+/* write set of regs to hardware - call with spinlock claimed */ +static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +{
struct cti_config *config = &drvdata->config;
int i;
CS_UNLOCK(drvdata->base);
/* disable CTI before writing registers */
writel_relaxed(0, drvdata->base + CTICONTROL);
/* write the CTI trigger registers */
for (i = 0; i < config->nr_trig_max; i++) {
writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i));
writel_relaxed(config->ctiouten[i],
drvdata->base + CTIOUTEN(i));
}
/* other regs */
writel_relaxed(config->ctigate, drvdata->base + CTIGATE);
writel_relaxed(config->asicctl, drvdata->base + ASICCTL);
writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET);
/* re-enable CTI */
writel_relaxed(1, drvdata->base + CTICONTROL);
CS_LOCK(drvdata->base);
+}
+static void cti_enable_hw_smp_call(void *info) +{
struct cti_drvdata *drvdata = info;
cti_write_all_hw_regs(drvdata);
+}
+/* write regs to hardware and enable */ +static int cti_enable_hw(void *info) +{
struct cti_drvdata *drvdata = info;
struct cti_config *config = &drvdata->config;
int rc = 0;
pm_runtime_get_sync(drvdata->dev);
spin_lock(&drvdata->spinlock);
/* no need to do anything if enabled or unpowered*/
if (config->hw_enabled || !config->hw_powered)
goto cti_not_enabled;
'config->hw_powered' has been assigned to 'true' in the function cti_probe(), thus the checking at here is redundant?
This flag can be altered in the cpu hotplug callbacks in a later patch.
/* claim the device */
rc = coresight_claim_device(drvdata->base);
if (rc)
goto cti_not_enabled;
if (drvdata->ctidev.cpu >= 0) {
dev_info(drvdata->dev, "cti enable smp call for cpu %d\n",
drvdata->ctidev.cpu);
Maybe we can remove the debugging logs, same as other CoreSight drivers?
Will do.
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
if (rc)
goto cti_not_enabled;
} else {
dev_info(drvdata->dev, "cti enable not cpu call\n");
cti_write_all_hw_regs(drvdata);
}
config->hw_enabled = true;
spin_unlock(&drvdata->spinlock);
return rc;
/* not enabled in this call */
+cti_not_enabled:
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return rc;
+}
+/* disable hardware */ +static int cti_disable_hw(void *info) +{
struct cti_drvdata *drvdata = info;
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* no need to do anything if disabled or cpu unpowered*/
if (!config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
return 0;
}
CS_UNLOCK(drvdata->base);
/* disable CTI */
writel_relaxed(0, drvdata->base + CTICONTROL);
config->hw_enabled = false;
coresight_disclaim_device_unlocked(drvdata->base);
CS_LOCK(drvdata->base);
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return 0;
+}
+static void cti_set_default_config(struct cti_config *config) +{
/* Most regs default to 0 as zalloc'ed except...*/
config->trig_filter_enable = 1;
config->ctigate = ((u32)0x1 << config->nr_ctm_channels) - 1;
atomic_set(&config->enable_req_count, 0);
+}
+/*
- Add a connection entry to the list of connections for this
- CTI device.
- */
+static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+{
struct cti_trig_con *tc;
struct cti_device *cti_dev = &drvdata->ctidev;
tc = kzalloc(sizeof(struct cti_trig_con), GFP_KERNEL);
if (tc == 0)
return -ENOMEM;
tc->con_dev = csdev;
/*
* Prefer actual associated CS device dev name to supplied value -
* which is likely to be node name / other conn name.
*/
if (csdev)
tc->con_dev_name = kstrdup(dev_name(&csdev->dev), GFP_KERNEL);
else if (assoc_dev_name != NULL)
tc->con_dev_name = kstrdup(assoc_dev_name, GFP_KERNEL);
tc->con_in = in_info;
tc->con_out = out_info;
list_add_tail(&tc->node, &cti_dev->trig_cons);
cti_dev->nr_trig_con++;
/* add connection usage bit info to overall info */
drvdata->config.trig_in_use |= in_info->used_mask;
drvdata->config.trig_out_use |= out_info->used_mask;
return 0;
+}
+/*
- Add a default connection if nothing else is specified.
- single connection based on max in/out info, no assoc device
- */
+int cti_add_default_connection(struct cti_drvdata *drvdata) +{
int ret = 0, idx;
struct cti_trig_grp *in = 0, *out = 0;
int n_trigs = drvdata->config.nr_trig_max;
u32 n_trig_mask = (0x1 << n_trigs) - 1;
int *in_sig_types = 0, *out_sig_types = 0;
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in) {
ret = -ENOMEM;
goto conn_add_err;
}
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out) {
ret = -ENOMEM;
goto conn_add_err;
}
in_sig_types = kzalloc(sizeof(int) * n_trigs, GFP_KERNEL);
if (!in_sig_types) {
ret = -ENOMEM;
goto conn_add_err;
}
out_sig_types = kzalloc(sizeof(int) * n_trigs, GFP_KERNEL);
if (!out_sig_types) {
ret = -ENOMEM;
goto conn_add_err;
}
/* signal types marked as default CTITRIG_GEN_IO */
for (idx = 0; idx < n_trigs; idx++) {
in_sig_types[idx] = CTITRIG_GEN_IO;
out_sig_types[idx] = CTITRIG_GEN_IO;
}
/*
* Assume max trigs for in and out,
* all used, default sig types allocated
*/
in->nr_sigs = n_trigs;
in->used_mask = n_trig_mask;
in->sig_types = in_sig_types;
out->nr_sigs = n_trigs;
out->used_mask = n_trig_mask;
out->sig_types = out_sig_types;
ret = cti_add_connection_entry(drvdata, in, out, NULL, "default");
if (ret)
goto conn_add_err;
return ret;
+conn_add_err:
kfree(in);
kfree(out);
kfree(out_sig_types);
kfree(in_sig_types);
return ret;
+}
+/** cti ect operations **/ +int cti_enable(struct coresight_device *csdev, void *__unused) +{
int rc;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
atomic_inc(&drvdata->config.enable_req_count);
rc = cti_enable_hw(drvdata);
if (rc)
atomic_dec(&drvdata->config.enable_req_count);
Seems to me we can increment the counter after enabling hardware successfully, thus we can avoid to decrease it for failed case:
rc = cti_enable_hw(drvdata); if (rc) return rc; atomic_inc(&drvdata->config.enable_req_count); return 0;
BTW, please consider to use spinlock to protect the flow, e.g. if use sysfs node to enable/disable CTI device concurrently, it might introduce mistaching between the counter and hardware enabling.
A spinlock is set in the cti_enable_hw() call, which does nothing if the hw is already enabled. While I was testing I was getting warnings from the kernel if I manipulate the atomic inside the spinlock - but I think I have to revisit this flow to re-test this.
return rc;
+}
+int cti_disable(struct coresight_device *csdev, void *__unused) +{
int rc = 0;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
if (!atomic_dec_return(&drvdata->config.enable_req_count)) {
rc = cti_disable_hw(drvdata);
if (rc)
atomic_inc(&drvdata->config.enable_req_count);
}
return rc;
Same at here, should we return -EBUSY if counter is not zero?
CTI is reference counted as it can be associated with more than one coresight device. e.g. in Juno r1/2 CTI0 takes triggers from the ETR, TPIU, STM and ETF0. So as each device in the path is enabled, then the count is incremented, and then decremented as each device is disabled. Thus the code is designed to enable the CTI when the first device requests enabling, and disable after the last device is disabled. Thus if the count is non zero then this is not an error condition - so we don't want to propagate an error code.
+}
+const struct coresight_ops_ect cti_ops_ect = {
.enable = cti_enable,
.disable = cti_disable,
+};
+const struct coresight_ops cti_ops = {
.ect_ops = &cti_ops_ect,
+};
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
int ret = 0;
u32 devid;
void __iomem *base;
struct device *dev = &adev->dev;
struct cti_drvdata *drvdata = NULL;
struct coresight_desc cti_desc;
struct coresight_platform_data *pdata = NULL;
struct resource *res = &adev->res;
struct device_node *np = adev->dev.of_node;
struct ect_node *ect_nd = NULL;
/* boilerplate code to set up the basics */
if (np) {
pdata = of_get_coresight_cti_platform_data(dev, np);
if (IS_ERR(pdata)) {
dev_info(dev, "of_get_coresight_ect_platform err\n");
return PTR_ERR(pdata);
}
dev->platform_data = pdata;
}
/* node to keep track of CTI net */
ect_nd = devm_kzalloc(dev, sizeof(struct ect_node), GFP_KERNEL);
if (!ect_nd) {
ret = -ENOMEM;
goto err_out;
}
/* driver data*/
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
goto err_out;
}
/* Validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base)) {
ret = PTR_ERR(base);
dev_info(dev, "%s, remap err\n", __func__);
goto err_out;
}
drvdata->base = base;
/* links between dev and drvdata*/
drvdata->dev = dev;
dev_set_drvdata(dev, drvdata);
/* default CTI device info */
drvdata->ctidev.cpu = pdata->cpu;
drvdata->ctidev.nr_trig_con = 0;
drvdata->ctidev.ctm_id = 0;
INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
spin_lock_init(&drvdata->spinlock);
/* look at the HW DEVID register for some of the HW settings */
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8);
/*
* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
drvdata->config.nr_trig_max, CTIINOUTEN_MAX);
drvdata->config.nr_trig_max = CTIINOUTEN_MAX;
}
drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
/* additional parse the .dts for connections and signals */
of_cti_get_hw_data(dev, np, drvdata);
I don't see any additional parsing dts for connections in this patch, will assume the sequential patches will add related code.
correct - at this point a minimal CTI device is set up based on the hardware registers probed. Later we develop connection associations.
/* initialise default driver values */
cti_set_default_config(&drvdata->config);
/* setup cpu related CTI devices, otherwise assume powered */
drvdata->config.hw_powered = true;
/* set up coresight component description */
cti_desc.pdata = pdata;
cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;
cti_desc.ops = &cti_ops;
cti_desc.groups = coresight_cti_groups;
cti_desc.dev = dev;
drvdata->csdev = coresight_register(&cti_desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
pr_err("%s: CS register failed\n", pdata->name);
goto err_out;
}
/* add to list of CTI devices */
mutex_lock(&ect_mutex);
ect_nd->cti_drv = drvdata;
list_add(&ect_nd->next, &ect_net);
/* if cpu bound add to CPU array */
if (drvdata->ctidev.cpu >= 0) {
cti_cpu_drv[drvdata->ctidev.cpu] = drvdata;
cti_cpu_count++;
}
mutex_unlock(&ect_mutex);
/* all done - dec pm refcount */
pm_runtime_put(&adev->dev);
dev_info(dev, "%s: initialized\n", pdata->name);
cti_count++;
Can we remove 'cti_count'?
yes - for this patch - and re-introduce where it is used.
return 0;
+err_out:
kfree(drvdata);
kfree(ect_nd);
return ret;
+}
+/* free all connection info from the driver */ +void cti_free_conn_info(struct cti_drvdata *drvdata) +{
struct cti_trig_con *tc, *tc_tmp;
list_for_each_entry_safe(tc, tc_tmp,
&drvdata->ctidev.trig_cons, node) {
kfree(tc->con_in->sig_types);
kfree(tc->con_in);
kfree(tc->con_out->sig_types);
kfree(tc->con_out);
kfree(tc->con_dev_name);
list_del(&tc->node);
kfree(tc);
We can use a bit more neat code as below?
list_del_init(&tc->node); kfree(tc->con_in->sig_types); kfree(tc->con_in); kfree(tc->con_out->sig_types); kfree(tc->con_out); kfree(tc->con_dev_name); kfree(tc);
}
+}
+/*
- Free up CTI specific resources
- called from coresight_device_release on coresight_unregister.
- */
+void cti_device_release(struct device *dev) +{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct ect_node *ect_item, *ect_tmp;
/* free up resources associated with the cti connections */
if (drvdata->ctidev.cpu >= 0)
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
/* clear the connection list items */
cti_free_conn_info(drvdata);
/* remove from the list */
mutex_lock(&ect_mutex);
list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
kfree(ect_item);
goto ect_list_item_removed;
}
}
+ect_list_item_removed:
mutex_unlock(&ect_mutex);
kfree(drvdata);
+} +EXPORT_SYMBOL_GPL(cti_device_release);
[...]
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h
[...]
+/**
- struct cti_config - configuration of the CTI device hardware
- hardware description from RO ID regs
- @nr_trig_max: Max number of trigger signals implemented on device.
(max of trig_in or trig_out)
- @nr_ctm_channels: number of available CTM channels
- cti enable control
- @enable_req_count: CTI is enabled alongside >=1 associated devices.
- @hw_enabled: true if hw is currently enabled.
- @hw_powered: true if associated cpu powered on, or no cpu.
- registered triggers and filtering
- @trig_in_use: bitfield of in triggers registered as in use.
- @trig_out_use: bitfield of out triggers registered as in use.
- @trig_out_filter: bitfield of out triggers that are blocked if filter
- enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
- @ctiinout_sel: register selector for INEN and OUTEN regs.
- @ctiinen: enable input trigger to a channel.
- @ctiouten: enable output trigger from a channel.
- @ctigate: gate channel output from CTI to CTM.
Please add comment for @asicctl.
- */
+struct cti_config {
/* hardware description */
int nr_ctm_channels;
int nr_trig_max;
/* cti enable control */
atomic_t enable_req_count;
bool hw_enabled;
bool hw_powered;
/* registered triggers and filtering */
u32 trig_in_use;
u32 trig_out_use;
u32 trig_out_filter;
int trig_filter_enable;
/* cti cross trig programmable regs */
u32 ctiappset;
u8 ctiinout_sel;
u32 ctiinen[CTIINOUTEN_MAX];
u32 ctiouten[CTIINOUTEN_MAX];
u32 ctigate;
u32 asicctl;
+};
[...]
Thanks, Leo Yan
Thanks for the feedback,
Mike
Hi Mike,
On Fri, May 24, 2019 at 12:45:47PM +0100, Mike Leach wrote:
[...]
BTW, please consider to use spinlock to protect the flow, e.g. if use sysfs node to enable/disable CTI device concurrently, it might introduce mistaching between the counter and hardware enabling.
A spinlock is set in the cti_enable_hw() call, which does nothing if the hw is already enabled. While I was testing I was getting warnings from the kernel if I manipulate the atomic inside the spinlock - but I think I have to revisit this flow to re-test this.
Just remind, after acquire spinlock the kernel is preemption disabled thus cannot use mutex/semaphore, etc. But we still can use atomic APIs since mutex/semaphore might introduce sleeping but atomic APIs will not.
return rc;
+}
+int cti_disable(struct coresight_device *csdev, void *__unused) +{
int rc = 0;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
if (!atomic_dec_return(&drvdata->config.enable_req_count)) {
rc = cti_disable_hw(drvdata);
if (rc)
atomic_inc(&drvdata->config.enable_req_count);
}
return rc;
Same at here, should we return -EBUSY if counter is not zero?
CTI is reference counted as it can be associated with more than one coresight device. e.g. in Juno r1/2 CTI0 takes triggers from the ETR, TPIU, STM and ETF0. So as each device in the path is enabled, then the count is incremented, and then decremented as each device is disabled. Thus the code is designed to enable the CTI when the first device requests enabling, and disable after the last device is disabled. Thus if the count is non zero then this is not an error condition - so we don't want to propagate an error code.
Thanks for the info. This makes sense for me.
Thanks, Leo Yan
Hi Mike,
On 01/05/2019 09:49, Mike Leach wrote:
This introduces a baseline CTI driver and associated configuration files.
Driver will probe for the device on the AMBA bus, and load the CTI driver on CoreSight ID match to CTI IDs in tables.
Initial sysfs support for enable / disable provided.
Default CTI interconnection data is generated based on hardware register signal counts, with no additional connection information
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/Kconfig | 13 + drivers/hwtracing/coresight/Makefile | 4 + .../hwtracing/coresight/coresight-cti-sysfs.c | 97 ++++ drivers/hwtracing/coresight/coresight-cti.c | 522 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 228 ++++++++ drivers/hwtracing/coresight/coresight-priv.h | 7 + drivers/hwtracing/coresight/coresight.c | 8 + .../hwtracing/coresight/of_coresight-cti.c | 81 +++ include/linux/coresight.h | 26 + 9 files changed, 986 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti-sysfs.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.h create mode 100644 drivers/hwtracing/coresight/of_coresight-cti.c
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index ad34380cac49..9f1cac64f357 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -114,4 +114,17 @@ config CORESIGHT_CPU_DEBUG properly, please refer Documentation/trace/coresight-cpu-debug.txt for detailed description and the example for usage. +config CORESIGHT_CTI
- bool "CoreSight Cross Trigger Interface (CTI) driver"
- depends on ARM || ARM64
- select CORESIGHT_LINKS_AND_SINKS
- help
This driver provides support for CoreSight CTI and CTM components.
These provide hardware triggering events between CoreSight trace
source and sink components. These can be used to halt trace or
inject events into the trace stream. CTI also provides a software
control to trigger same halt events. This can provide fast trace
halt compared to disabling sources and sinks normally in driver
software.
- endif
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 41870ded51a3..77402cfc7174 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -19,3 +19,7 @@ obj-$(CONFIG_CORESIGHT_DYNAMIC_REPLICATOR) += coresight-dynamic-replicator.o obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o +obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o \
of_coresight-cti.o \
coresight-cti-sysfs.o
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c new file mode 100644 index 000000000000..9b6749621dcb --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2019 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/cpu.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/pm_wakeup.h> +#include <linux/seq_file.h> +#include <linux/smp.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/uaccess.h>
+#include "coresight-cti.h"
+/* basic attributes */
nit: pointless comment, could be dropped.
+static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int enable_req;
- bool enabled, powered, cpuid;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- ssize_t size = 0;
- enable_req = atomic_read(&drvdata->config.enable_req_count);
- spin_lock(&drvdata->spinlock);
- powered = drvdata->config.hw_powered;
- enabled = drvdata->config.hw_enabled;
- cpuid = drvdata->ctidev.cpu;
- spin_unlock(&drvdata->spinlock);
- if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
- } else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
- } else {
size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n",
enabled ? "enabled" : "disabled");
- }
- return size;
+}
+static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int ret = 0;
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- ret = kstrtoul(buf, 16, &val);
nit: I would leave it 0, instead of hard coding 16 to accept any base.
- if (ret)
return ret;
- if (val)
ret = cti_enable(drvdata->csdev, NULL);
- else
ret = cti_disable(drvdata->csdev, NULL);
- if (ret)
return ret;
- return size;
+} +static DEVICE_ATTR_RW(enable);
+/* attribute and group sysfs tables. */
nit: same as above, may be dropped.
+static struct attribute *coresight_cti_attrs[] = {
- &dev_attr_enable.attr,
- NULL,
+};
+static const struct attribute_group coresight_cti_group = {
- .attrs = coresight_cti_attrs,
+};
+const struct attribute_group *coresight_cti_groups[] = {
- &coresight_cti_group,
- NULL,
+}; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c new file mode 100644 index 000000000000..0a0fb6d48237 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,522 @@
...
+/**
- CTI devices can be associated with a PE, or be connected to CoreSight
- hardware. We have a list of all CTIs, and a subset array associated
- with individual PEs for pwr management as they will power up in the
nit: s/pwr/power/
- PE power domain.
- At this point we assume that the none CPU CTIs are always powered as
- we do with sinks etc.
nit: s/At this point//
- We leave the client to figure out if all the CTIs are interconnected with
- the same CTM, in general this is the case but does not always have to be.
- */
+struct ect_node {
- struct cti_drvdata *cti_drv;
- struct list_head next;
+};
+/* net of CTI devices connected via CTM */ +LIST_HEAD(ect_net);
+/* protect the list */ +static DEFINE_MUTEX(ect_mutex);
+/* quick reference for CPU CTIs */ +static struct cti_drvdata *cti_cpu_drv[NR_CPUS];
nit: Could this be made __percpu instead ?
+/* number of registered CTI devices */ +static int cti_count;
+/* number of cpu associated CTI devices in use */ +static int cti_cpu_count;
+#define csdev_to_cti_drvdata(csdev) \
- dev_get_drvdata(csdev->dev.parent)
+/* write set of regs to hardware - call with spinlock claimed */ +static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +{
- struct cti_config *config = &drvdata->config;
- int i;
- CS_UNLOCK(drvdata->base);
- /* disable CTI before writing registers */
- writel_relaxed(0, drvdata->base + CTICONTROL);
- /* write the CTI trigger registers */
- for (i = 0; i < config->nr_trig_max; i++) {
writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i));
writel_relaxed(config->ctiouten[i],
drvdata->base + CTIOUTEN(i));
- }
- /* other regs */
- writel_relaxed(config->ctigate, drvdata->base + CTIGATE);
- writel_relaxed(config->asicctl, drvdata->base + ASICCTL);
- writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET);
- /* re-enable CTI */
- writel_relaxed(1, drvdata->base + CTICONTROL);
- CS_LOCK(drvdata->base);
+}
+static void cti_enable_hw_smp_call(void *info) +{
- struct cti_drvdata *drvdata = info;
- cti_write_all_hw_regs(drvdata);
+}
+/* write regs to hardware and enable */ +static int cti_enable_hw(void *info) +{
- struct cti_drvdata *drvdata = info;
- struct cti_config *config = &drvdata->config;
- int rc = 0;
- pm_runtime_get_sync(drvdata->dev);
- spin_lock(&drvdata->spinlock);
- /* no need to do anything if enabled or unpowered*/
- if (config->hw_enabled || !config->hw_powered)
goto cti_not_enabled;
- /* claim the device */
- rc = coresight_claim_device(drvdata->base);
- if (rc)
goto cti_not_enabled;
- if (drvdata->ctidev.cpu >= 0) {
dev_info(drvdata->dev, "cti enable smp call for cpu %d\n",
Please could we make it dev_dbg() instead ?
drvdata->ctidev.cpu);
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
Is there any reason why these need to be bound to the CPU ?
if (rc)
goto cti_not_enabled;
- } else {
dev_info(drvdata->dev, "cti enable not cpu call\n");
Same as above, dev_dbg() ?
cti_write_all_hw_regs(drvdata);
- }
--- Cut here ---
- config->hw_enabled = true;
- spin_unlock(&drvdata->spinlock);
- return rc;
- /* not enabled in this call */
+cti_not_enabled:
- spin_unlock(&drvdata->spinlock);
- pm_runtime_put(drvdata->dev);
nit: You may be able to consolidate the unlock/exit above as:
config->hw_enabled = true;
unlock: spin_unlock(&drvdata->spinlock); if (rc) pm_runtime_put(drvdata->dev); return rc;
- return rc;
+}
+/* disable hardware */ +static int cti_disable_hw(void *info) +{
- struct cti_drvdata *drvdata = info;
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* no need to do anything if disabled or cpu unpowered*/
- if (!config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
return 0;
- }
- CS_UNLOCK(drvdata->base);
- /* disable CTI */
- writel_relaxed(0, drvdata->base + CTICONTROL);
- config->hw_enabled = false;
- coresight_disclaim_device_unlocked(drvdata->base);
- CS_LOCK(drvdata->base);
- spin_unlock(&drvdata->spinlock);
- pm_runtime_put(drvdata->dev);
- return 0;
+}
+static void cti_set_default_config(struct cti_config *config) +{
- /* Most regs default to 0 as zalloc'ed except...*/
- config->trig_filter_enable = 1;
- config->ctigate = ((u32)0x1 << config->nr_ctm_channels) - 1;
- atomic_set(&config->enable_req_count, 0);
+}
+/*
- Add a connection entry to the list of connections for this
- CTI device.
- */
+static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+{
- struct cti_trig_con *tc;
- struct cti_device *cti_dev = &drvdata->ctidev;
- tc = kzalloc(sizeof(struct cti_trig_con), GFP_KERNEL);
- if (tc == 0)
Does this need to be NULL explicitly for consistency ?
return -ENOMEM;
- tc->con_dev = csdev;
- /*
* Prefer actual associated CS device dev name to supplied value -
* which is likely to be node name / other conn name.
*/
- if (csdev)
tc->con_dev_name = kstrdup(dev_name(&csdev->dev), GFP_KERNEL);
- else if (assoc_dev_name != NULL)
tc->con_dev_name = kstrdup(assoc_dev_name, GFP_KERNEL);
- tc->con_in = in_info;
- tc->con_out = out_info;
- list_add_tail(&tc->node, &cti_dev->trig_cons);
- cti_dev->nr_trig_con++;
- /* add connection usage bit info to overall info */
- drvdata->config.trig_in_use |= in_info->used_mask;
- drvdata->config.trig_out_use |= out_info->used_mask;
- return 0;
+}
+/*
- Add a default connection if nothing else is specified.
- single connection based on max in/out info, no assoc device
- */
+int cti_add_default_connection(struct cti_drvdata *drvdata) +{
- int ret = 0, idx;
nit: If you set ret = -ENOMEM here or further down before the mem allocs you could save some lines and make it easier to follow.
- struct cti_trig_grp *in = 0, *out = 0;
Please could we use NULL instead ?
- int n_trigs = drvdata->config.nr_trig_max;
- u32 n_trig_mask = (0x1 << n_trigs) - 1;
nit: n_trig_mask = GENMASK(n_trigs - 1, 0);
- int *in_sig_types = 0, *out_sig_types = 0;
Same here.
- in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!in) {
ret = -ENOMEM;
goto conn_add_err;
- }
- out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!out) {
ret = -ENOMEM;
goto conn_add_err;
- }
- in_sig_types = kzalloc(sizeof(int) * n_trigs, GFP_KERNEL);
- if (!in_sig_types) {
ret = -ENOMEM;
goto conn_add_err;
- }
- out_sig_types = kzalloc(sizeof(int) * n_trigs, GFP_KERNEL);
- if (!out_sig_types) {
ret = -ENOMEM;
goto conn_add_err;
- }
- /* signal types marked as default CTITRIG_GEN_IO */
- for (idx = 0; idx < n_trigs; idx++) {
in_sig_types[idx] = CTITRIG_GEN_IO;
out_sig_types[idx] = CTITRIG_GEN_IO;
- }
- /*
* Assume max trigs for in and out,
* all used, default sig types allocated
*/
- in->nr_sigs = n_trigs;
- in->used_mask = n_trig_mask;
- in->sig_types = in_sig_types;
- out->nr_sigs = n_trigs;
- out->used_mask = n_trig_mask;
- out->sig_types = out_sig_types;
- ret = cti_add_connection_entry(drvdata, in, out, NULL, "default");
- if (ret)
goto conn_add_err;
- return ret;
+conn_add_err:
- kfree(in);
- kfree(out);
- kfree(out_sig_types);
- kfree(in_sig_types);
- return ret;
+}
+/** cti ect operations **/ +int cti_enable(struct coresight_device *csdev, void *__unused) +{
- int rc;
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- atomic_inc(&drvdata->config.enable_req_count);
- rc = cti_enable_hw(drvdata);
- if (rc)
atomic_dec(&drvdata->config.enable_req_count);
- return rc;
+}
+int cti_disable(struct coresight_device *csdev, void *__unused) +{
- int rc = 0;
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- if (!atomic_dec_return(&drvdata->config.enable_req_count)) {
rc = cti_disable_hw(drvdata);
if (rc)
atomic_inc(&drvdata->config.enable_req_count);
- }
- return rc;
+}
+const struct coresight_ops_ect cti_ops_ect = {
- .enable = cti_enable,
- .disable = cti_disable,
+};
+const struct coresight_ops cti_ops = {
- .ect_ops = &cti_ops_ect,
+};
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
- int ret = 0;
- u32 devid;
- void __iomem *base;
- struct device *dev = &adev->dev;
- struct cti_drvdata *drvdata = NULL;
- struct coresight_desc cti_desc;
- struct coresight_platform_data *pdata = NULL;
- struct resource *res = &adev->res;
- struct device_node *np = adev->dev.of_node;
- struct ect_node *ect_nd = NULL;
- /* boilerplate code to set up the basics */
- if (np) {
pdata = of_get_coresight_cti_platform_data(dev, np);
if (IS_ERR(pdata)) {
dev_info(dev, "of_get_coresight_ect_platform err\n");
return PTR_ERR(pdata);
}
dev->platform_data = pdata;
- }
- /* node to keep track of CTI net */
- ect_nd = devm_kzalloc(dev, sizeof(struct ect_node), GFP_KERNEL);
nit: If you move the ect_node under the drvdata, you could save some lines of code and runtime cost of looking up the ect_node while removing the device.
- if (!ect_nd) {
ret = -ENOMEM;
goto err_out;
- }
- /* driver data*/
- drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
goto err_out;
- }
- /* Validity for the resource is already checked by the AMBA core */
- base = devm_ioremap_resource(dev, res);
- if (IS_ERR(base)) {
ret = PTR_ERR(base);
dev_info(dev, "%s, remap err\n", __func__);
goto err_out;
- }
- drvdata->base = base;
- /* links between dev and drvdata*/
- drvdata->dev = dev;
- dev_set_drvdata(dev, drvdata);
- /* default CTI device info */
- drvdata->ctidev.cpu = pdata->cpu;
- drvdata->ctidev.nr_trig_con = 0;
- drvdata->ctidev.ctm_id = 0;
- INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
- spin_lock_init(&drvdata->spinlock);
- /* look at the HW DEVID register for some of the HW settings */
- devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
- drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8);
Please could we have proper macros to do these exraction ? e.g,
CTI_DEVID_NTRIG(devid)
- /*
* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
- if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
drvdata->config.nr_trig_max, CTIINOUTEN_MAX);
drvdata->config.nr_trig_max = CTIINOUTEN_MAX;
- }
- drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
CTI_DEVID_NCTM_CHANS(devid)
- /* additional parse the .dts for connections and signals */
- of_cti_get_hw_data(dev, np, drvdata);
- /* initialise default driver values */
- cti_set_default_config(&drvdata->config);
- /* setup cpu related CTI devices, otherwise assume powered */
- drvdata->config.hw_powered = true;
- /* set up coresight component description */
- cti_desc.pdata = pdata;
- cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
- cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;
- cti_desc.ops = &cti_ops;
- cti_desc.groups = coresight_cti_groups;
- cti_desc.dev = dev;
- drvdata->csdev = coresight_register(&cti_desc);
- if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
pr_err("%s: CS register failed\n", pdata->name);
goto err_out;
- }
- /* add to list of CTI devices */
- mutex_lock(&ect_mutex);
- ect_nd->cti_drv = drvdata;
- list_add(&ect_nd->next, &ect_net);
- /* if cpu bound add to CPU array */
- if (drvdata->ctidev.cpu >= 0) {
cti_cpu_drv[drvdata->ctidev.cpu] = drvdata;
cti_cpu_count++;
- }
- mutex_unlock(&ect_mutex);
- /* all done - dec pm refcount */
- pm_runtime_put(&adev->dev);
- dev_info(dev, "%s: initialized\n", pdata->name);
- cti_count++;
- return 0;
+err_out:
- kfree(drvdata);
- kfree(ect_nd);
- return ret;
+}
+/* free all connection info from the driver */ +void cti_free_conn_info(struct cti_drvdata *drvdata) +{
- struct cti_trig_con *tc, *tc_tmp;
- list_for_each_entry_safe(tc, tc_tmp,
&drvdata->ctidev.trig_cons, node) {
kfree(tc->con_in->sig_types);
kfree(tc->con_in);
kfree(tc->con_out->sig_types);
kfree(tc->con_out);
kfree(tc->con_dev_name);
list_del(&tc->node);
kfree(tc);
- }
+}
+/*
- Free up CTI specific resources
- called from coresight_device_release on coresight_unregister.
- */
+void cti_device_release(struct device *dev) +{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct ect_node *ect_item, *ect_tmp;
- /* free up resources associated with the cti connections */
- if (drvdata->ctidev.cpu >= 0)
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
- /* clear the connection list items */
- cti_free_conn_info(drvdata);
- /* remove from the list */
- mutex_lock(&ect_mutex);
- list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
As mentioned above, if ect_node is embedded in drvdata, you could avoid the list walk.
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
kfree(ect_item);
goto ect_list_item_removed;
nit : break; ?
}
- }
+ect_list_item_removed:
- mutex_unlock(&ect_mutex);
- kfree(drvdata);
+} +EXPORT_SYMBOL_GPL(cti_device_release);
Why does this need to be exported ? May be I will find the answer in later patches. I will keep an eye.
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h new file mode 100644 index 000000000000..c1af6cf9c2fe --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CTI_H +#define _CORESIGHT_CORESIGHT_CTI_H
+#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h"
+/*
- Device registers
- 0x000 - 0x144: CTI programming and status
- 0xEDC - 0xEF8: CTI integration test.
- 0xF00 - 0xFFC: Coresight management registers.
- */
+/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */
Do we use the Integration Test registers at all ? If not we could remove them.
+/* management registers */ +#define CTIDEVAFF0 0xFA8 +#define CTIDEVAFF1 0xFAC
+/*
- CTI CSSoc 600 has a max of 32 trigger signals per direction.
- CTI CSSoc 400 has 8 IO triggers - other CTIs can be impl def.
- Max of in and out defined in the DEVID register.
- pick up actual number used from .dts parameters if present.
- */
+#define CTIINOUTEN_MAX 32
+/*
- CTI Trigger signal type definitions.
- Labels for certain trigger interconnections between the CTI
- and standard CS components.
- Must match values in ./include/dt-bindings/arm/coresight-cti.h
Why not have these common bits in a shared header file ?
- */
+#define CTITRIG_GEN_IO 0 +#define CTITRIG_GEN_INTREQ 1 +#define CTITRIG_GEN_INTACK 2 +#define CTITRIG_GEN_HALTREQ 3 +#define CTITRIG_GEN_RESTARTREQ 4 +#define CTITRIG_PE_EDBGREQ 5 +#define CTITRIG_PE_DBGRESTART 6 +#define CTITRIG_PE_CTIIRQ 7 +#define CTITRIG_PE_PMUIRQ 8 +#define CTITRIG_PE_DBGTRIGGER 9 +#define CTITRIG_ETM_EXTOUT 10 +#define CTITRIG_ETM_EXTIN 11 +#define CTITRIG_SNK_FULL 12 +#define CTITRIG_SNK_ACQCOMP 13 +#define CTITRIG_SNK_FLUSHCOMP 14 +#define CTITRIG_SNK_FLUSHIN 15 +#define CTITRIG_SNK_TRIGIN 16 +#define CTITRIG_STM_ASYNCOUT 17 +#define CTITRIG_STM_TOUT_SPTE 18 +#define CTITRIG_STM_TOUT_SW 19 +#define CTITRIG_STM_TOUT_HETE 20 +#define CTITRIG_STM_HWEVENT 21 +#define CTITRIG_ELA_TSTART 22 +#define CTITRIG_ELA_TSTOP 23 +#define CTITRIG_ELA_DBGREQ 24
+/**
- Group of related trigger signals
- @nr_sigs: number of signals in the group.
- @used_mask: bitmask representing the signal indexes in the group.
- @sig_types: array of types for the signals, length nr_sigs.
- */
+struct cti_trig_grp {
- int nr_sigs;
- u32 used_mask;
- int *sig_types;
minor nit: If you make this: int sig_types[0];
You may be able to allocate in one shot and freeing up becomes easier.
cti_trig_grp *grp = kzalloc(sizeof(cti_trig_grp) + sizeof(int) * nr_trigs, GFP_KERNEL);
+};
+/**
- Trigger connection - connection between a CTI and other (coresight) device
- lists input and output trigger signals for the device
- @con_in: connected CTIIN signals for the device.
- @con_out: connected CTIOUT signals for the device.
- @con_dev: coresight device connected to the CTI, NULL if not CS device
- @con_dev_name: name of connected device (CS or CPU)
- @node: entry node in list of connections.
- */
+struct cti_trig_con {
- struct cti_trig_grp *con_in;
- struct cti_trig_grp *con_out;
- struct coresight_device *con_dev;
- char *con_dev_name;
- struct list_head node;
+};
+/**
- struct cti_device - description of CTI device properties.
- @nt_trig_con: Number of external devices connected to this device.
- @ctm_id: which CTM this device is connected to (by default it is
assumed there is a single CTM per SoC, ID 0).
- @trig_cons: list of connections to this device.
- @cpu: CPU ID if associated with CPU, -1 otherwise.
- */
+struct cti_device {
- int nr_trig_con;
- u32 ctm_id;
- struct list_head trig_cons;
- int cpu;
+};
+/**
- struct cti_config - configuration of the CTI device hardware
- hardware description from RO ID regs
nit: s/hardware/Hardware
nit: Please could you leave a TAB at the beginning to show that it is a subtopic. And also, if you align the field description by TABs, it will be better readable:
e.g * Hardware description from ID regs * @nr_trig_max: Max number of .... * and the description continues here. * ... * * CTI Enable Control * @enable_req_count: ....
- @nr_trig_max: Max number of trigger signals implemented on device.
(max of trig_in or trig_out)
- @nr_ctm_channels: number of available CTM channels
- cti enable control
- @enable_req_count: CTI is enabled alongside >=1 associated devices.
- @hw_enabled: true if hw is currently enabled.
- @hw_powered: true if associated cpu powered on, or no cpu.
- registered triggers and filtering
- @trig_in_use: bitfield of in triggers registered as in use.
- @trig_out_use: bitfield of out triggers registered as in use.
- @trig_out_filter: bitfield of out triggers that are blocked if filter
- enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
- @ctiinout_sel: register selector for INEN and OUTEN regs.
- @ctiinen: enable input trigger to a channel.
- @ctiouten: enable output trigger from a channel.
- @ctigate: gate channel output from CTI to CTM.
- */
...
+/**
- struct cti_drvdata - specifics for the CTI device
- @base: Memory mapped base address for this component.
- @dev: The device entity associated to this component.
- @csdev: Standard CoreSight device information.
- @ctidev: Extra information needed by the CTI/CTM framework.
- @spinlock: Control data access to one at a time.
- @config: Configuration data for this CTI device.
- */
+struct cti_drvdata {
- void __iomem *base;
- struct device *dev;
- struct coresight_device *csdev;
- struct cti_device ctidev;
- spinlock_t spinlock;
- struct cti_config config;
+};
+/* private cti driver fns & vars */ +extern const struct attribute_group *coresight_cti_groups[]; +int cti_add_default_connection(struct cti_drvdata *drvdata); +int cti_enable(struct coresight_device *csdev, void *__unused); +int cti_disable(struct coresight_device *csdev, void *__unused);
+#ifdef CONFIG_OF +extern int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata);
+extern struct coresight_platform_data * +of_get_coresight_cti_platform_data(struct device *dev,
const struct device_node *node);
+#else +static inline int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{ return 0; }; +static inline struct coresight_platform_data * +of_get_coresight_cti_platform_data(struct device *dev,
const struct device_node *node) { return NULL; }
+#endif
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */ diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index e0684d06e9ee..8e9e5b6ad866 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -161,6 +161,13 @@ static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; } static inline int etm_writel_cp14(u32 off, u32 val) { return 0; } #endif +#ifdef CONFIG_CORESIGHT_CTI +extern void cti_device_release(struct device *dev); +#else +static inline void cti_device_release(struct device *dev) {}; +#endif
/*
- Macros and inline functions to handle CoreSight UCI data and driver
- private data in AMBA ID table entries, and extract data values.
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 29cef898afba..8db3d1a5314f 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -967,12 +967,20 @@ static struct device_type coresight_dev_type[] = { { .name = "helper", },
- {
.name = "ect",
- }, };
static void coresight_device_release(struct device *dev) { struct coresight_device *csdev = to_coresight_device(dev);
- /* additional info to release for CTI */
- if ((csdev->type == CORESIGHT_DEV_TYPE_ECT) &&
(csdev->subtype.ect_subtype == CORESIGHT_DEV_SUBTYPE_ECT_CTI))
cti_device_release(dev);
I would prefer, you add a new csdev->release hook instead of hard coding it like this and then do :
if (csdev->release) csdev->release(csdev);
kfree(csdev->conns); kfree(csdev->refcnt); kfree(csdev); diff --git a/drivers/hwtracing/coresight/of_coresight-cti.c b/drivers/hwtracing/coresight/of_coresight-cti.c new file mode 100644 index 000000000000..379ca1113deb --- /dev/null +++ b/drivers/hwtracing/coresight/of_coresight-cti.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2019, The Linux Foundation. All rights reserved.
- */
+#include <linux/amba/bus.h> +#include <linux/clk.h> +#include <linux/coresight.h> +#include <linux/cpumask.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_graph.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h>
+#include "coresight-cti.h"
+#ifdef CONFIG_OF
+/*
- CTI can be bound to a CPU, or a system device.
- Reflect this in the return value and do not default to cpu 0
- */
+static int of_cti_get_cpu(const struct device_node *node) +{
- int cpu;
- struct device_node *dn;
- dn = of_parse_phandle(node, "cpu", 0);
- /* CTI Affinity defaults to no cpu */
- if (!dn)
return -1;
- cpu = of_cpu_node_to_id(dn);
- of_node_put(dn);
- /* No Affinity if no cpu nodes are found */
- return (cpu < 0) ? -1 : cpu;
I think it is time to make the of_coresigth_get_cpu() return the real result than defaulting it to 0. The callers can decide what they want to do about it. And thus you may be able to reuse it here.
+}
+/* get the hardware configuration & connection data. */ +int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
- int rc = 0;
- struct cti_device *cti_dev = &drvdata->ctidev;
- /* if no connections, just add a single default based on max IN-OUT */
- if (cti_dev->nr_trig_con == 0)
rc = cti_add_default_connection(drvdata);
- return rc;
+}
This looks out of place to me, but may be valid in the following patches.
Suzuki
Hi Suzuki,
Thanks for reviewing this but v3 came out last night. I'll transfer over comments where relevant but some stuff has changed. More detail inline,
On Thu, 6 Jun 2019 at 12:02, Suzuki K Poulose suzuki.poulose@arm.com wrote:
Hi Mike,
On 01/05/2019 09:49, Mike Leach wrote:
This introduces a baseline CTI driver and associated configuration files.
Driver will probe for the device on the AMBA bus, and load the CTI driver on CoreSight ID match to CTI IDs in tables.
Initial sysfs support for enable / disable provided.
Default CTI interconnection data is generated based on hardware register signal counts, with no additional connection information
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/Kconfig | 13 + drivers/hwtracing/coresight/Makefile | 4 + .../hwtracing/coresight/coresight-cti-sysfs.c | 97 ++++ drivers/hwtracing/coresight/coresight-cti.c | 522 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 228 ++++++++ drivers/hwtracing/coresight/coresight-priv.h | 7 + drivers/hwtracing/coresight/coresight.c | 8 + .../hwtracing/coresight/of_coresight-cti.c | 81 +++ include/linux/coresight.h | 26 + 9 files changed, 986 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti-sysfs.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.h create mode 100644 drivers/hwtracing/coresight/of_coresight-cti.c
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index ad34380cac49..9f1cac64f357 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -114,4 +114,17 @@ config CORESIGHT_CPU_DEBUG properly, please refer Documentation/trace/coresight-cpu-debug.txt for detailed description and the example for usage.
+config CORESIGHT_CTI
bool "CoreSight Cross Trigger Interface (CTI) driver"
depends on ARM || ARM64
select CORESIGHT_LINKS_AND_SINKS
help
This driver provides support for CoreSight CTI and CTM components.
These provide hardware triggering events between CoreSight trace
source and sink components. These can be used to halt trace or
inject events into the trace stream. CTI also provides a software
control to trigger same halt events. This can provide fast trace
halt compared to disabling sources and sinks normally in driver
software.
- endif
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 41870ded51a3..77402cfc7174 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -19,3 +19,7 @@ obj-$(CONFIG_CORESIGHT_DYNAMIC_REPLICATOR) += coresight-dynamic-replicator.o obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o +obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o \
of_coresight-cti.o \
coresight-cti-sysfs.o
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c new file mode 100644 index 000000000000..9b6749621dcb --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2019 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/cpu.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/pm_wakeup.h> +#include <linux/seq_file.h> +#include <linux/smp.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/uaccess.h>
+#include "coresight-cti.h"
+/* basic attributes */
nit: pointless comment, could be dropped.
As later patches add attrib sections I add comments as the start of the sections to try to improve readablity. But could certainly drop this one.
+static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int enable_req;
bool enabled, powered, cpuid;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
ssize_t size = 0;
enable_req = atomic_read(&drvdata->config.enable_req_count);
spin_lock(&drvdata->spinlock);
powered = drvdata->config.hw_powered;
enabled = drvdata->config.hw_enabled;
cpuid = drvdata->ctidev.cpu;
spin_unlock(&drvdata->spinlock);
if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
} else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
} else {
size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n",
enabled ? "enabled" : "disabled");
}
return size;
+}
+static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int ret = 0;
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = kstrtoul(buf, 16, &val);
nit: I would leave it 0, instead of hard coding 16 to accept any base.
if (ret)
return ret;
if (val)
ret = cti_enable(drvdata->csdev, NULL);
else
ret = cti_disable(drvdata->csdev, NULL);
if (ret)
return ret;
return size;
+} +static DEVICE_ATTR_RW(enable);
+/* attribute and group sysfs tables. */
nit: same as above, may be dropped.
+static struct attribute *coresight_cti_attrs[] = {
&dev_attr_enable.attr,
NULL,
+};
+static const struct attribute_group coresight_cti_group = {
.attrs = coresight_cti_attrs,
+};
+const struct attribute_group *coresight_cti_groups[] = {
&coresight_cti_group,
NULL,
+}; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c new file mode 100644 index 000000000000..0a0fb6d48237 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,522 @@
...
+/**
- CTI devices can be associated with a PE, or be connected to CoreSight
- hardware. We have a list of all CTIs, and a subset array associated
- with individual PEs for pwr management as they will power up in the
nit: s/pwr/power/
- PE power domain.
- At this point we assume that the none CPU CTIs are always powered as
- we do with sinks etc.
nit: s/At this point//
- We leave the client to figure out if all the CTIs are interconnected with
- the same CTM, in general this is the case but does not always have to be.
- */
+struct ect_node {
struct cti_drvdata *cti_drv;
struct list_head next;
+};
+/* net of CTI devices connected via CTM */ +LIST_HEAD(ect_net);
+/* protect the list */ +static DEFINE_MUTEX(ect_mutex);
+/* quick reference for CPU CTIs */ +static struct cti_drvdata *cti_cpu_drv[NR_CPUS];
nit: Could this be made __percpu instead ?
array dropped in the next set.
+/* number of registered CTI devices */ +static int cti_count;
+/* number of cpu associated CTI devices in use */ +static int cti_cpu_count;
+#define csdev_to_cti_drvdata(csdev) \
dev_get_drvdata(csdev->dev.parent)
+/* write set of regs to hardware - call with spinlock claimed */ +static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +{
struct cti_config *config = &drvdata->config;
int i;
CS_UNLOCK(drvdata->base);
/* disable CTI before writing registers */
writel_relaxed(0, drvdata->base + CTICONTROL);
/* write the CTI trigger registers */
for (i = 0; i < config->nr_trig_max; i++) {
writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i));
writel_relaxed(config->ctiouten[i],
drvdata->base + CTIOUTEN(i));
}
/* other regs */
writel_relaxed(config->ctigate, drvdata->base + CTIGATE);
writel_relaxed(config->asicctl, drvdata->base + ASICCTL);
writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET);
/* re-enable CTI */
writel_relaxed(1, drvdata->base + CTICONTROL);
CS_LOCK(drvdata->base);
+}
+static void cti_enable_hw_smp_call(void *info) +{
struct cti_drvdata *drvdata = info;
cti_write_all_hw_regs(drvdata);
+}
+/* write regs to hardware and enable */ +static int cti_enable_hw(void *info) +{
struct cti_drvdata *drvdata = info;
struct cti_config *config = &drvdata->config;
int rc = 0;
pm_runtime_get_sync(drvdata->dev);
spin_lock(&drvdata->spinlock);
/* no need to do anything if enabled or unpowered*/
if (config->hw_enabled || !config->hw_powered)
goto cti_not_enabled;
/* claim the device */
rc = coresight_claim_device(drvdata->base);
if (rc)
goto cti_not_enabled;
if (drvdata->ctidev.cpu >= 0) {
dev_info(drvdata->dev, "cti enable smp call for cpu %d\n",
Please could we make it dev_dbg() instead ?
debug comment dropped in next set.
drvdata->ctidev.cpu);
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
Is there any reason why these need to be bound to the CPU ?
This is following the practice of the ETM - where the device is CPU bound - run the code on the associated cpu.
if (rc)
goto cti_not_enabled;
} else {
dev_info(drvdata->dev, "cti enable not cpu call\n");
Same as above, dev_dbg() ?
cti_write_all_hw_regs(drvdata);
}
--- Cut here ---
config->hw_enabled = true;
spin_unlock(&drvdata->spinlock);
return rc;
/* not enabled in this call */
+cti_not_enabled:
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
nit: You may be able to consolidate the unlock/exit above as:
config->hw_enabled = true;
unlock: spin_unlock(&drvdata->spinlock); if (rc) pm_runtime_put(drvdata->dev); return rc;
This code has been re-worked but I'll look again.
return rc;
+}
+/* disable hardware */ +static int cti_disable_hw(void *info) +{
struct cti_drvdata *drvdata = info;
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* no need to do anything if disabled or cpu unpowered*/
if (!config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
return 0;
}
CS_UNLOCK(drvdata->base);
/* disable CTI */
writel_relaxed(0, drvdata->base + CTICONTROL);
config->hw_enabled = false;
coresight_disclaim_device_unlocked(drvdata->base);
CS_LOCK(drvdata->base);
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return 0;
+}
+static void cti_set_default_config(struct cti_config *config) +{
/* Most regs default to 0 as zalloc'ed except...*/
config->trig_filter_enable = 1;
config->ctigate = ((u32)0x1 << config->nr_ctm_channels) - 1;
atomic_set(&config->enable_req_count, 0);
+}
+/*
- Add a connection entry to the list of connections for this
- CTI device.
- */
+static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+{
struct cti_trig_con *tc;
struct cti_device *cti_dev = &drvdata->ctidev;
tc = kzalloc(sizeof(struct cti_trig_con), GFP_KERNEL);
if (tc == 0)
Does this need to be NULL explicitly for consistency ?
yes.
return -ENOMEM;
tc->con_dev = csdev;
/*
* Prefer actual associated CS device dev name to supplied value -
* which is likely to be node name / other conn name.
*/
if (csdev)
tc->con_dev_name = kstrdup(dev_name(&csdev->dev), GFP_KERNEL);
else if (assoc_dev_name != NULL)
tc->con_dev_name = kstrdup(assoc_dev_name, GFP_KERNEL);
tc->con_in = in_info;
tc->con_out = out_info;
list_add_tail(&tc->node, &cti_dev->trig_cons);
cti_dev->nr_trig_con++;
/* add connection usage bit info to overall info */
drvdata->config.trig_in_use |= in_info->used_mask;
drvdata->config.trig_out_use |= out_info->used_mask;
return 0;
+}
+/*
- Add a default connection if nothing else is specified.
- single connection based on max in/out info, no assoc device
- */
+int cti_add_default_connection(struct cti_drvdata *drvdata) +{
int ret = 0, idx;
nit: If you set ret = -ENOMEM here or further down before the mem allocs you could save some lines and make it easier to follow.
agreed.
struct cti_trig_grp *in = 0, *out = 0;
Please could we use NULL instead ?
int n_trigs = drvdata->config.nr_trig_max;
u32 n_trig_mask = (0x1 << n_trigs) - 1;
nit: n_trig_mask = GENMASK(n_trigs - 1, 0);
int *in_sig_types = 0, *out_sig_types = 0;
Same here.
Next set uses NULL, & function split into 2.
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in) {
ret = -ENOMEM;
goto conn_add_err;
}
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out) {
ret = -ENOMEM;
goto conn_add_err;
}
in_sig_types = kzalloc(sizeof(int) * n_trigs, GFP_KERNEL);
if (!in_sig_types) {
ret = -ENOMEM;
goto conn_add_err;
}
out_sig_types = kzalloc(sizeof(int) * n_trigs, GFP_KERNEL);
if (!out_sig_types) {
ret = -ENOMEM;
goto conn_add_err;
}
/* signal types marked as default CTITRIG_GEN_IO */
for (idx = 0; idx < n_trigs; idx++) {
in_sig_types[idx] = CTITRIG_GEN_IO;
out_sig_types[idx] = CTITRIG_GEN_IO;
}
/*
* Assume max trigs for in and out,
* all used, default sig types allocated
*/
in->nr_sigs = n_trigs;
in->used_mask = n_trig_mask;
in->sig_types = in_sig_types;
out->nr_sigs = n_trigs;
out->used_mask = n_trig_mask;
out->sig_types = out_sig_types;
ret = cti_add_connection_entry(drvdata, in, out, NULL, "default");
if (ret)
goto conn_add_err;
return ret;
+conn_add_err:
kfree(in);
kfree(out);
kfree(out_sig_types);
kfree(in_sig_types);
return ret;
+}
+/** cti ect operations **/ +int cti_enable(struct coresight_device *csdev, void *__unused) +{
int rc;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
atomic_inc(&drvdata->config.enable_req_count);
rc = cti_enable_hw(drvdata);
if (rc)
atomic_dec(&drvdata->config.enable_req_count);
return rc;
+}
+int cti_disable(struct coresight_device *csdev, void *__unused) +{
int rc = 0;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
if (!atomic_dec_return(&drvdata->config.enable_req_count)) {
rc = cti_disable_hw(drvdata);
if (rc)
atomic_inc(&drvdata->config.enable_req_count);
}
return rc;
+}
+const struct coresight_ops_ect cti_ops_ect = {
.enable = cti_enable,
.disable = cti_disable,
+};
+const struct coresight_ops cti_ops = {
.ect_ops = &cti_ops_ect,
+};
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
int ret = 0;
u32 devid;
void __iomem *base;
struct device *dev = &adev->dev;
struct cti_drvdata *drvdata = NULL;
struct coresight_desc cti_desc;
struct coresight_platform_data *pdata = NULL;
struct resource *res = &adev->res;
struct device_node *np = adev->dev.of_node;
struct ect_node *ect_nd = NULL;
/* boilerplate code to set up the basics */
if (np) {
pdata = of_get_coresight_cti_platform_data(dev, np);
if (IS_ERR(pdata)) {
dev_info(dev, "of_get_coresight_ect_platform err\n");
return PTR_ERR(pdata);
}
dev->platform_data = pdata;
}
/* node to keep track of CTI net */
ect_nd = devm_kzalloc(dev, sizeof(struct ect_node), GFP_KERNEL);
nit: If you move the ect_node under the drvdata, you could save some lines of code and runtime cost of looking up the ect_node while removing the device.
Good point - will do.
if (!ect_nd) {
ret = -ENOMEM;
goto err_out;
}
/* driver data*/
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
goto err_out;
}
/* Validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base)) {
ret = PTR_ERR(base);
dev_info(dev, "%s, remap err\n", __func__);
goto err_out;
}
drvdata->base = base;
/* links between dev and drvdata*/
drvdata->dev = dev;
dev_set_drvdata(dev, drvdata);
/* default CTI device info */
drvdata->ctidev.cpu = pdata->cpu;
drvdata->ctidev.nr_trig_con = 0;
drvdata->ctidev.ctm_id = 0;
INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
spin_lock_init(&drvdata->spinlock);
/* look at the HW DEVID register for some of the HW settings */
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8);
Please could we have proper macros to do these exraction ? e.g,
CTI_DEVID_NTRIG(devid)
/*
* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
drvdata->config.nr_trig_max, CTIINOUTEN_MAX);
drvdata->config.nr_trig_max = CTIINOUTEN_MAX;
}
drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
CTI_DEVID_NCTM_CHANS(devid)
OK.
/* additional parse the .dts for connections and signals */
of_cti_get_hw_data(dev, np, drvdata);
/* initialise default driver values */
cti_set_default_config(&drvdata->config);
/* setup cpu related CTI devices, otherwise assume powered */
drvdata->config.hw_powered = true;
/* set up coresight component description */
cti_desc.pdata = pdata;
cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;
cti_desc.ops = &cti_ops;
cti_desc.groups = coresight_cti_groups;
cti_desc.dev = dev;
drvdata->csdev = coresight_register(&cti_desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
pr_err("%s: CS register failed\n", pdata->name);
goto err_out;
}
/* add to list of CTI devices */
mutex_lock(&ect_mutex);
ect_nd->cti_drv = drvdata;
list_add(&ect_nd->next, &ect_net);
/* if cpu bound add to CPU array */
if (drvdata->ctidev.cpu >= 0) {
cti_cpu_drv[drvdata->ctidev.cpu] = drvdata;
cti_cpu_count++;
}
mutex_unlock(&ect_mutex);
/* all done - dec pm refcount */
pm_runtime_put(&adev->dev);
dev_info(dev, "%s: initialized\n", pdata->name);
cti_count++;
return 0;
+err_out:
kfree(drvdata);
kfree(ect_nd);
return ret;
+}
+/* free all connection info from the driver */ +void cti_free_conn_info(struct cti_drvdata *drvdata) +{
struct cti_trig_con *tc, *tc_tmp;
list_for_each_entry_safe(tc, tc_tmp,
&drvdata->ctidev.trig_cons, node) {
kfree(tc->con_in->sig_types);
kfree(tc->con_in);
kfree(tc->con_out->sig_types);
kfree(tc->con_out);
kfree(tc->con_dev_name);
list_del(&tc->node);
kfree(tc);
}
+}
+/*
- Free up CTI specific resources
- called from coresight_device_release on coresight_unregister.
- */
+void cti_device_release(struct device *dev) +{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct ect_node *ect_item, *ect_tmp;
/* free up resources associated with the cti connections */
if (drvdata->ctidev.cpu >= 0)
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
/* clear the connection list items */
cti_free_conn_info(drvdata);
/* remove from the list */
mutex_lock(&ect_mutex);
list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
As mentioned above, if ect_node is embedded in drvdata, you could avoid the list walk.
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
kfree(ect_item);
goto ect_list_item_removed;
nit : break; ?
}
}
+ect_list_item_removed:
mutex_unlock(&ect_mutex);
kfree(drvdata);
+} +EXPORT_SYMBOL_GPL(cti_device_release);
Why does this need to be exported ? May be I will find the answer in later patches. I will keep an eye.
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h new file mode 100644 index 000000000000..c1af6cf9c2fe --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CTI_H +#define _CORESIGHT_CORESIGHT_CTI_H
+#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h"
+/*
- Device registers
- 0x000 - 0x144: CTI programming and status
- 0xEDC - 0xEF8: CTI integration test.
- 0xF00 - 0xFFC: Coresight management registers.
- */
+/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */
Do we use the Integration Test registers at all ? If not we could remove them.
They are useful on occasion - CTI connections tend to be the poorest documented. At Mathieu's suggestion these are conditionally compiled in the next set.
+/* management registers */ +#define CTIDEVAFF0 0xFA8 +#define CTIDEVAFF1 0xFAC
+/*
- CTI CSSoc 600 has a max of 32 trigger signals per direction.
- CTI CSSoc 400 has 8 IO triggers - other CTIs can be impl def.
- Max of in and out defined in the DEVID register.
- pick up actual number used from .dts parameters if present.
- */
+#define CTIINOUTEN_MAX 32
+/*
- CTI Trigger signal type definitions.
- Labels for certain trigger interconnections between the CTI
- and standard CS components.
- Must match values in ./include/dt-bindings/arm/coresight-cti.h
Why not have these common bits in a shared header file ?
Or simply include dt-bindings/arm/coresight-cti.h directly?
- */
+#define CTITRIG_GEN_IO 0 +#define CTITRIG_GEN_INTREQ 1 +#define CTITRIG_GEN_INTACK 2 +#define CTITRIG_GEN_HALTREQ 3 +#define CTITRIG_GEN_RESTARTREQ 4 +#define CTITRIG_PE_EDBGREQ 5 +#define CTITRIG_PE_DBGRESTART 6 +#define CTITRIG_PE_CTIIRQ 7 +#define CTITRIG_PE_PMUIRQ 8 +#define CTITRIG_PE_DBGTRIGGER 9 +#define CTITRIG_ETM_EXTOUT 10 +#define CTITRIG_ETM_EXTIN 11 +#define CTITRIG_SNK_FULL 12 +#define CTITRIG_SNK_ACQCOMP 13 +#define CTITRIG_SNK_FLUSHCOMP 14 +#define CTITRIG_SNK_FLUSHIN 15 +#define CTITRIG_SNK_TRIGIN 16 +#define CTITRIG_STM_ASYNCOUT 17 +#define CTITRIG_STM_TOUT_SPTE 18 +#define CTITRIG_STM_TOUT_SW 19 +#define CTITRIG_STM_TOUT_HETE 20 +#define CTITRIG_STM_HWEVENT 21 +#define CTITRIG_ELA_TSTART 22 +#define CTITRIG_ELA_TSTOP 23 +#define CTITRIG_ELA_DBGREQ 24
+/**
- Group of related trigger signals
- @nr_sigs: number of signals in the group.
- @used_mask: bitmask representing the signal indexes in the group.
- @sig_types: array of types for the signals, length nr_sigs.
- */
+struct cti_trig_grp {
int nr_sigs;
u32 used_mask;
int *sig_types;
minor nit: If you make this: int sig_types[0];
You may be able to allocate in one shot and freeing up becomes easier.
cti_trig_grp *grp = kzalloc(sizeof(cti_trig_grp) + sizeof(int) * nr_trigs,
GFP_KERNEL);
good point - will fix.
+};
+/**
- Trigger connection - connection between a CTI and other (coresight) device
- lists input and output trigger signals for the device
- @con_in: connected CTIIN signals for the device.
- @con_out: connected CTIOUT signals for the device.
- @con_dev: coresight device connected to the CTI, NULL if not CS device
- @con_dev_name: name of connected device (CS or CPU)
- @node: entry node in list of connections.
- */
+struct cti_trig_con {
struct cti_trig_grp *con_in;
struct cti_trig_grp *con_out;
struct coresight_device *con_dev;
char *con_dev_name;
struct list_head node;
+};
+/**
- struct cti_device - description of CTI device properties.
- @nt_trig_con: Number of external devices connected to this device.
- @ctm_id: which CTM this device is connected to (by default it is
assumed there is a single CTM per SoC, ID 0).
- @trig_cons: list of connections to this device.
- @cpu: CPU ID if associated with CPU, -1 otherwise.
- */
+struct cti_device {
int nr_trig_con;
u32 ctm_id;
struct list_head trig_cons;
int cpu;
+};
+/**
- struct cti_config - configuration of the CTI device hardware
- hardware description from RO ID regs
nit: s/hardware/Hardware
nit: Please could you leave a TAB at the beginning to show that it is a subtopic. And also, if you align the field description by TABs, it will be better readable:
OK
e.g * Hardware description from ID regs * @nr_trig_max: Max number of .... * and the description continues here. * ... * * CTI Enable Control * @enable_req_count: ....
- @nr_trig_max: Max number of trigger signals implemented on device.
(max of trig_in or trig_out)
- @nr_ctm_channels: number of available CTM channels
- cti enable control
- @enable_req_count: CTI is enabled alongside >=1 associated devices.
- @hw_enabled: true if hw is currently enabled.
- @hw_powered: true if associated cpu powered on, or no cpu.
- registered triggers and filtering
- @trig_in_use: bitfield of in triggers registered as in use.
- @trig_out_use: bitfield of out triggers registered as in use.
- @trig_out_filter: bitfield of out triggers that are blocked if filter
- enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
- @ctiinout_sel: register selector for INEN and OUTEN regs.
- @ctiinen: enable input trigger to a channel.
- @ctiouten: enable output trigger from a channel.
- @ctigate: gate channel output from CTI to CTM.
- */
...
+/**
- struct cti_drvdata - specifics for the CTI device
- @base: Memory mapped base address for this component.
- @dev: The device entity associated to this component.
- @csdev: Standard CoreSight device information.
- @ctidev: Extra information needed by the CTI/CTM framework.
- @spinlock: Control data access to one at a time.
- @config: Configuration data for this CTI device.
- */
+struct cti_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct cti_device ctidev;
spinlock_t spinlock;
struct cti_config config;
+};
+/* private cti driver fns & vars */ +extern const struct attribute_group *coresight_cti_groups[]; +int cti_add_default_connection(struct cti_drvdata *drvdata); +int cti_enable(struct coresight_device *csdev, void *__unused); +int cti_disable(struct coresight_device *csdev, void *__unused);
+#ifdef CONFIG_OF +extern int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata);
+extern struct coresight_platform_data * +of_get_coresight_cti_platform_data(struct device *dev,
const struct device_node *node);
+#else +static inline int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{ return 0; }; +static inline struct coresight_platform_data * +of_get_coresight_cti_platform_data(struct device *dev,
const struct device_node *node) { return NULL; }
+#endif
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */ diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index e0684d06e9ee..8e9e5b6ad866 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -161,6 +161,13 @@ static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; } static inline int etm_writel_cp14(u32 off, u32 val) { return 0; } #endif
+#ifdef CONFIG_CORESIGHT_CTI +extern void cti_device_release(struct device *dev); +#else +static inline void cti_device_release(struct device *dev) {}; +#endif
/*
- Macros and inline functions to handle CoreSight UCI data and driver
- private data in AMBA ID table entries, and extract data values.
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 29cef898afba..8db3d1a5314f 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -967,12 +967,20 @@ static struct device_type coresight_dev_type[] = { { .name = "helper", },
{
.name = "ect",
},
};
static void coresight_device_release(struct device *dev) { struct coresight_device *csdev = to_coresight_device(dev);
/* additional info to release for CTI */
if ((csdev->type == CORESIGHT_DEV_TYPE_ECT) &&
(csdev->subtype.ect_subtype == CORESIGHT_DEV_SUBTYPE_ECT_CTI))
cti_device_release(dev);
I would prefer, you add a new csdev->release hook instead of hard coding it like this and then do :
if (csdev->release) csdev->release(csdev);
OK.
kfree(csdev->conns); kfree(csdev->refcnt); kfree(csdev);
diff --git a/drivers/hwtracing/coresight/of_coresight-cti.c b/drivers/hwtracing/coresight/of_coresight-cti.c new file mode 100644 index 000000000000..379ca1113deb --- /dev/null +++ b/drivers/hwtracing/coresight/of_coresight-cti.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2019, The Linux Foundation. All rights reserved.
- */
+#include <linux/amba/bus.h> +#include <linux/clk.h> +#include <linux/coresight.h> +#include <linux/cpumask.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_graph.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h>
+#include "coresight-cti.h"
+#ifdef CONFIG_OF
+/*
- CTI can be bound to a CPU, or a system device.
- Reflect this in the return value and do not default to cpu 0
- */
+static int of_cti_get_cpu(const struct device_node *node) +{
int cpu;
struct device_node *dn;
dn = of_parse_phandle(node, "cpu", 0);
/* CTI Affinity defaults to no cpu */
if (!dn)
return -1;
cpu = of_cpu_node_to_id(dn);
of_node_put(dn);
/* No Affinity if no cpu nodes are found */
return (cpu < 0) ? -1 : cpu;
I think it is time to make the of_coresigth_get_cpu() return the real result than defaulting it to 0. The callers can decide what they want to do about it. And thus you may be able to reuse it here.
I considered that - I was concerned about breaking stuff if I missed a use case. Seemed simpler and more contained to implement something new.
+}
+/* get the hardware configuration & connection data. */ +int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
int rc = 0;
struct cti_device *cti_dev = &drvdata->ctidev;
/* if no connections, just add a single default based on max IN-OUT */
if (cti_dev->nr_trig_con == 0)
rc = cti_add_default_connection(drvdata);
return rc;
+}
This looks out of place to me, but may be valid in the following patches.
It expands as we support more CTI cases in the device tree.
Suzuki
Thanks
Mike
Hi Mike,
On 06/06/2019 13:59, Mike Leach wrote:
Hi Suzuki,
Thanks for reviewing this but v3 came out last night. I'll transfer over comments where relevant but some stuff has changed.
Apologies, I missed the v2 and realized it only very late. Thanks, I will look at the new version. Some comments inline.
rc = coresight_claim_device(drvdata->base);
if (rc)
goto cti_not_enabled;
if (drvdata->ctidev.cpu >= 0) {
dev_info(drvdata->dev, "cti enable smp call for cpu %d\n",
Please could we make it dev_dbg() instead ?
debug comment dropped in next set.
drvdata->ctidev.cpu);
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
Is there any reason why these need to be bound to the CPU ?
This is following the practice of the ETM - where the device is CPU bound - run the code on the associated cpu.
OK
+#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h"
+/*
- Device registers
- 0x000 - 0x144: CTI programming and status
- 0xEDC - 0xEF8: CTI integration test.
- 0xF00 - 0xFFC: Coresight management registers.
- */
+/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */
Do we use the Integration Test registers at all ? If not we could remove them.
They are useful on occasion - CTI connections tend to be the poorest documented. At Mathieu's suggestion these are conditionally compiled in the next set.
OK.
+#define CTIINOUTEN_MAX 32
+/*
- CTI Trigger signal type definitions.
- Labels for certain trigger interconnections between the CTI
- and standard CS components.
- Must match values in ./include/dt-bindings/arm/coresight-cti.h
Why not have these common bits in a shared header file ?
Or simply include dt-bindings/arm/coresight-cti.h directly?
Thats fine too
+/*
- CTI can be bound to a CPU, or a system device.
- Reflect this in the return value and do not default to cpu 0
- */
+static int of_cti_get_cpu(const struct device_node *node) +{
int cpu;
struct device_node *dn;
dn = of_parse_phandle(node, "cpu", 0);
/* CTI Affinity defaults to no cpu */
if (!dn)
return -1;
cpu = of_cpu_node_to_id(dn);
of_node_put(dn);
/* No Affinity if no cpu nodes are found */
return (cpu < 0) ? -1 : cpu;
I think it is time to make the of_coresigth_get_cpu() return the real result than defaulting it to 0. The callers can decide what they want to do about it. And thus you may be able to reuse it here.
I considered that - I was concerned about breaking stuff if I missed a use case. Seemed simpler and more contained to implement something new.
Going forward, when we get to ACPI support, this would be another PITA. So lets fix it. I could send a separate patch if needed.
This looks out of place to me, but may be valid in the following patches.
It expands as we support more CTI cases in the device tree.
OK. It may be good to have the file named cti-platform.c instead of hardcoding of_cti etc, given the new changes upstream with ACPI series.
Suzuki
Hi Suzuki,
On Thu, 6 Jun 2019 at 14:17, Suzuki K Poulose suzuki.poulose@arm.com wrote:
Hi Mike,
On 06/06/2019 13:59, Mike Leach wrote:
Hi Suzuki,
Thanks for reviewing this but v3 came out last night. I'll transfer over comments where relevant but some stuff has changed.
Apologies, I missed the v2 and realized it only very late. Thanks, I will look at the new version. Some comments inline.
rc = coresight_claim_device(drvdata->base);
if (rc)
goto cti_not_enabled;
if (drvdata->ctidev.cpu >= 0) {
dev_info(drvdata->dev, "cti enable smp call for cpu %d\n",
Please could we make it dev_dbg() instead ?
debug comment dropped in next set.
drvdata->ctidev.cpu);
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
Is there any reason why these need to be bound to the CPU ?
This is following the practice of the ETM - where the device is CPU bound - run the code on the associated cpu.
OK
+#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h"
+/*
- Device registers
- 0x000 - 0x144: CTI programming and status
- 0xEDC - 0xEF8: CTI integration test.
- 0xF00 - 0xFFC: Coresight management registers.
- */
+/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */
Do we use the Integration Test registers at all ? If not we could remove them.
They are useful on occasion - CTI connections tend to be the poorest documented. At Mathieu's suggestion these are conditionally compiled in the next set.
OK.
+#define CTIINOUTEN_MAX 32
+/*
- CTI Trigger signal type definitions.
- Labels for certain trigger interconnections between the CTI
- and standard CS components.
- Must match values in ./include/dt-bindings/arm/coresight-cti.h
Why not have these common bits in a shared header file ?
Or simply include dt-bindings/arm/coresight-cti.h directly?
Thats fine too
+/*
- CTI can be bound to a CPU, or a system device.
- Reflect this in the return value and do not default to cpu 0
- */
+static int of_cti_get_cpu(const struct device_node *node) +{
int cpu;
struct device_node *dn;
dn = of_parse_phandle(node, "cpu", 0);
/* CTI Affinity defaults to no cpu */
if (!dn)
return -1;
cpu = of_cpu_node_to_id(dn);
of_node_put(dn);
/* No Affinity if no cpu nodes are found */
return (cpu < 0) ? -1 : cpu;
I think it is time to make the of_coresigth_get_cpu() return the real result than defaulting it to 0. The callers can decide what they want to do about it. And thus you may be able to reuse it here.
I considered that - I was concerned about breaking stuff if I missed a use case. Seemed simpler and more contained to implement something new.
Going forward, when we get to ACPI support, this would be another PITA. So lets fix it. I could send a separate patch if needed.
CTI work is the trigger for this so I'll include a preparatory patch in my next set that fixes it.
This looks out of place to me, but may be valid in the following patches.
It expands as we support more CTI cases in the device tree.
OK. It may be good to have the file named cti-platform.c instead of hardcoding of_cti etc, given the new changes upstream with ACPI series.
Then you'll like v3 - it applies on top of your ACPI set on coresight/next, with the above mentioned change.
Mike
Suzuki
Adds in required sysfs access for:- - CoreSight management registers. - CTI device specific registers. - Channel Cross triggering API.
This provides the basic programming interface for the device.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../hwtracing/coresight/coresight-cti-sysfs.c | 831 +++++++++++++++++- drivers/hwtracing/coresight/coresight-cti.c | 188 +++- drivers/hwtracing/coresight/coresight-cti.h | 42 + drivers/hwtracing/coresight/coresight-priv.h | 2 + 4 files changed, 1053 insertions(+), 10 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 9b6749621dcb..ff8dad02a779 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -34,8 +34,8 @@ static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) { - int enable_req; - bool enabled, powered, cpuid; + int enable_req, cpuid; + bool enabled, powered; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); ssize_t size = 0;
@@ -46,12 +46,18 @@ static ssize_t enable_show(struct device *dev, cpuid = drvdata->ctidev.cpu; spin_unlock(&drvdata->spinlock);
- if (powered) { - size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n", - enabled ? "enabled" : "disabled", cpuid); - } else if (cpuid >= 0) { - size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n", - enable_req ? "enable req" : "disable req", cpuid); + if (cpuid >= 0) { + if (powered) { + size = scnprintf(buf, PAGE_SIZE, + "cti %s; cpu%d powered;\n", + enabled ? "enabled" : "disabled", + cpuid); + } else { + size = scnprintf(buf, PAGE_SIZE, + "cti %s; cpu%d unpowered;\n", + enable_req ? "enable req" : "disable req", + cpuid); + } } else { size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n", enabled ? "enabled" : "disabled"); @@ -81,17 +87,826 @@ static ssize_t enable_store(struct device *dev, } static DEVICE_ATTR_RW(enable);
+static ssize_t ctmid_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id); +} +static DEVICE_ATTR_RO(ctmid); + /* attribute and group sysfs tables. */ static struct attribute *coresight_cti_attrs[] = { &dev_attr_enable.attr, + &dev_attr_ctmid.attr, NULL, };
+/* register based attributes */ +#define coresight_cti_reg(name, offset) \ + coresight_simple_reg32(struct cti_drvdata, name, offset) + +/* + * Show a simple 32 bit value. If pval is NULL then live read, + * otherwise read from supplied pointer only. + */ +static ssize_t cti_reg32_show(struct device *dev, char *buf, + u32 *pval, int reg_offset) +{ + unsigned long val = 0; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + spin_lock(&drvdata->spinlock); + if (pval) { + val = (unsigned long)*pval; + } else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) { + CS_UNLOCK(drvdata->base); + val = readl_relaxed(drvdata->base + reg_offset); + CS_LOCK(drvdata->base); + } + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +/* + * Store a simple 32 bit value. + * If pval not NULL, then copy to here too, + * if reg_offset >= 0 then write through if enabled. + */ +static ssize_t cti_reg32_store(struct device *dev, const char *buf, + size_t size, u32 *pval, + int reg_offset) +{ + unsigned long val; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + /* local store */ + if (pval) + *pval = (u32)val; + + /* write through of offset and enabled */ + if ((reg_offset >= 0) && CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, reg_offset, val); + spin_unlock(&drvdata->spinlock); + return size; +} + +/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4); + +static struct attribute *coresight_cti_mgmt_attrs[] = { + &dev_attr_devaff0.attr, + &dev_attr_devaff1.attr, + &dev_attr_authstatus.attr, + &dev_attr_devarch.attr, + &dev_attr_devid.attr, + &dev_attr_devtype.attr, + &dev_attr_pidr0.attr, + &dev_attr_pidr1.attr, + &dev_attr_pidr2.attr, + &dev_attr_pidr3.attr, + &dev_attr_pidr4.attr, + NULL, +}; + +/* CTI low level programming registers */ +static ssize_t inout_sel_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 val; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = (u32)drvdata->config.ctiinout_sel; + return scnprintf(buf, PAGE_SIZE, "%#x\n", val); +} + +static ssize_t inout_sel_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val > (CTIINOUTEN_MAX-1)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + drvdata->config.ctiinout_sel = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(inout_sel); + +static ssize_t inen_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + int index; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + index = drvdata->config.ctiinout_sel; + val = drvdata->config.ctiinen[index]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "INEN%d %#lx\n", index, val); +} + +static ssize_t inen_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + int index; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + index = config->ctiinout_sel; + config->ctiinen[index] = val; + + /* write through if enabled */ + if (CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, CTIINEN(index), val); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(inen); + +static ssize_t outen_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + int index; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + index = drvdata->config.ctiinout_sel; + val = drvdata->config.ctiouten[index]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "OUTEN%d %#lx\n", index, val); +} + +static ssize_t outen_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + int index; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + index = config->ctiinout_sel; + config->ctiouten[index] = val; + + /* write through if enabled */ + if (CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, CTIOUTEN(index), val); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(outen); + +static ssize_t gate_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return cti_reg32_show(dev, buf, &drvdata->config.ctigate, -1); +} + +static ssize_t gate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return cti_reg32_store(dev, buf, size, + &drvdata->config.ctigate, CTIGATE); +} +static DEVICE_ATTR_RW(gate); + +static ssize_t asicctl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return cti_reg32_show(dev, buf, &drvdata->config.asicctl, -1); +} + +static ssize_t asicctl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return cti_reg32_store(dev, buf, size, + &drvdata->config.asicctl, ASICCTL); +} +static DEVICE_ATTR_RW(asicctl); + +static ssize_t intack_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + cti_write_intack(dev, val); + return size; +} +static DEVICE_ATTR_WO(intack); + +static ssize_t appset_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return cti_reg32_show(dev, buf, &drvdata->config.ctiappset, -1); +} + +static ssize_t appset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return cti_reg32_store(dev, buf, size, + &drvdata->config.ctiappset, CTIAPPSET); +} +static DEVICE_ATTR_RW(appset); + +static ssize_t appclear_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val, mask; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + + /* a 1'b1 in appclr clears down the same bit in appset*/ + mask = ~val; + config->ctiappset &= mask; + + /* write through if enabled */ + if (CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, CTIAPPCLEAR, val); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_WO(appclear); + +static ssize_t apppulse_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val, mask; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + + /* + * a 1'b1 in apppulse sets then clears the bit, + * effectively clears down the same bit in appset + */ + mask = ~val; + config->ctiappset &= mask; + + /* write through if enabled */ + if (CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, CTIAPPPULSE, val); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_WO(apppulse); + +static ssize_t itchout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return cti_reg32_show(dev, buf, NULL, ITCHOUT); +} + +static ssize_t itchout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_reg32_store(dev, buf, size, NULL, ITCHOUT); +} +static DEVICE_ATTR_RW(itchout); + +static ssize_t ittrigout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return cti_reg32_show(dev, buf, NULL, ITTRIGOUT); +} + +static ssize_t ittrigout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_reg32_store(dev, buf, size, NULL, ITTRIGOUT); +} +static DEVICE_ATTR_RW(ittrigout); + +static ssize_t itchinack_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_reg32_store(dev, buf, size, NULL, ITCHINACK); +} +static DEVICE_ATTR_WO(itchinack); + +static ssize_t ittriginack_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_reg32_store(dev, buf, size, NULL, ITTRIGINACK); +} +static DEVICE_ATTR_WO(ittriginack); + +static ssize_t itctrl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL); +} + +static ssize_t itctrl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL); +} +static DEVICE_ATTR_RW(itctrl); + +coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK); + +static struct attribute *coresight_cti_regs_attrs[] = { + &dev_attr_inout_sel.attr, + &dev_attr_inen.attr, + &dev_attr_outen.attr, + &dev_attr_gate.attr, + &dev_attr_asicctl.attr, + &dev_attr_intack.attr, + &dev_attr_appset.attr, + &dev_attr_appclear.attr, + &dev_attr_apppulse.attr, + &dev_attr_triginstatus.attr, + &dev_attr_trigoutstatus.attr, + &dev_attr_chinstatus.attr, + &dev_attr_choutstatus.attr, + &dev_attr_itctrl.attr, + &dev_attr_ittrigin.attr, + &dev_attr_itchin.attr, + &dev_attr_ittrigout.attr, + &dev_attr_itchout.attr, + &dev_attr_itchoutack.attr, + &dev_attr_ittrigoutack.attr, + &dev_attr_ittriginack.attr, + &dev_attr_itchinack.attr, + NULL, +}; + +/* CTI channel x-trigger programming */ +static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op, + enum cti_trig_dir dir, const char *buf, size_t size) +{ + u32 chan_idx; + u32 trig_idx; + int items, err = size; + + /* extract chan idx and trigger idx */ + items = sscanf(buf, "%d %d", &chan_idx, &trig_idx); + if (items) { + err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx); + if (!err) + err = size; + } else + err = -EINVAL; + return err; +} + +static ssize_t trigin_attach_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_IN, + buf, size); +} +static DEVICE_ATTR_WO(trigin_attach); + +static ssize_t trigin_detach_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_IN, + buf, size); +} +static DEVICE_ATTR_WO(trigin_detach); + +static ssize_t trigout_attach_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT, + buf, size); +} +static DEVICE_ATTR_WO(trigout_attach); + +static ssize_t trigout_detach_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT, + buf, size); +} +static DEVICE_ATTR_WO(trigout_detach); + + +static ssize_t gate_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int err = 0, channel = 0; + + if ((size >= 3) && (strncmp(buf, "all", 3) == 0)) { + err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE_ALL, 0); + } else { + if (kstrtoint(buf, 0, &channel)) + return -EINVAL; + + err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, channel); + } + return err ? err : size; +} +static DEVICE_ATTR_WO(gate_enable); + +static ssize_t gate_disable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int err = 0, channel = 0; + + if ((size >= 3) && (strncmp(buf, "all", 3) == 0)) { + err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE_ALL, 0); + } else { + if (kstrtoint(buf, 0, &channel)) + return -EINVAL; + + err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, channel); + } + return err ? err : size; +} +static DEVICE_ATTR_WO(gate_disable); + +static int +chan_op_parse(struct device *dev, enum cti_chan_set_op op, const char *buf) +{ + int err = 0, channel = 0; + + if (kstrtoint(buf, 0, &channel)) + return -EINVAL; + + err = cti_channel_setop(dev, op, channel); + return err; + +} + +static ssize_t chan_set_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int err = chan_op_parse(dev, CTI_CHAN_SET, buf); + + return err ? err : size; +} +static DEVICE_ATTR_WO(chan_set); + +static ssize_t chan_clear_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int err = chan_op_parse(dev, CTI_CHAN_CLR, buf); + + return err ? err : size; +} +static DEVICE_ATTR_WO(chan_clear); + +static ssize_t chan_pulse_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int err = chan_op_parse(dev, CTI_CHAN_PULSE, buf); + + return err ? err : size; +} +static DEVICE_ATTR_WO(chan_pulse); + +static ssize_t trig_filter_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->config.trig_filter_enable; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%ld (%s)\n", val, + val ? "enabled" : "disabled"); +} + +static ssize_t trig_filter_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + drvdata->config.trig_filter_enable = val ? 1 : 0; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(trig_filter_enable); + +/* clear all xtrigger / channel programming */ +static ssize_t reset_xtrigs_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int i; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + spin_lock(&drvdata->spinlock); + + /* clear the CTI trigger / channel programming registers */ + for (i = 0; i < config->nr_trig_max; i++) { + config->ctiinen[i] = 0; + config->ctiouten[i] = 0; + } + + /* clear the other regs */ + config->ctigate = (0x1 << config->nr_ctm_channels) - 1; + config->asicctl = 0; + config->ctiappset = 0; + config->ctiinout_sel = 0; + + /* if enabled then write through */ + if (CTI_PWR_ENA(config)) + cti_write_all_hw_regs(drvdata); + + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_WO(reset_xtrigs); + +static ssize_t show_chan_sel_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 val; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = (u32)drvdata->config.xtrig_rchan_sel; + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} + +/* select a channel for the show_chan_xtrig function */ +static ssize_t show_chan_sel_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val > (drvdata->config.nr_ctm_channels-1)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + drvdata->config.xtrig_rchan_sel = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(show_chan_sel); + +/* for the selected channel - show the signals attached. */ +static ssize_t show_chan_xtrigs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *cfg = &drvdata->config; + int used = 0, reg_idx; + int buf_sz = PAGE_SIZE; + u32 chan_mask = 0x1 << cfg->xtrig_rchan_sel; + + used += scnprintf(buf, buf_sz, "IN: "); + for (reg_idx = 0; + reg_idx < drvdata->config.nr_trig_max; + reg_idx++) { + if (chan_mask & cfg->ctiinen[reg_idx]) { + used += scnprintf(buf+used, buf_sz-used, "%d ", + reg_idx); + } + } + + used += scnprintf(buf+used, buf_sz-used, " OUT: "); + for (reg_idx = 0; + reg_idx < drvdata->config.nr_trig_max; + reg_idx++) { + if (chan_mask & cfg->ctiouten[reg_idx]) { + used += scnprintf(buf+used, buf_sz-used, "%d ", + reg_idx); + } + } + used += scnprintf(buf+used, buf_sz-used, "\n"); + return used; +} +static DEVICE_ATTR_RO(show_chan_xtrigs); + +static ssize_t print_chan_list(struct device *dev, + char *buf, bool inuse) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + int used = 0, i; + u32 inuse_bits = 0, chan_mask, chan_bit_mask; + + /* scan regs to get bitmap of channels in use. */ + spin_lock(&drvdata->spinlock); + for (i = 0; i < config->nr_trig_max; i++) { + inuse_bits |= config->ctiinen[i]; + inuse_bits |= config->ctiouten[i]; + } + spin_unlock(&drvdata->spinlock); + + /* inverse bits if printing free channels */ + if (!inuse) + inuse_bits = ~inuse_bits; + + /* list of channels, or 'none' */ + chan_mask = ((u32)(0x1 << config->nr_ctm_channels)) - 1; + if (inuse_bits & chan_mask) { + for (i = 0; i < config->nr_ctm_channels; i++) { + chan_bit_mask = 0x1 << i; + if (chan_bit_mask & inuse_bits) { + used += scnprintf(buf+used, PAGE_SIZE-used, + "%d ", i); + } + } + used += scnprintf(buf+used, PAGE_SIZE-used, "\n"); + } else { + used = scnprintf(buf, PAGE_SIZE, "none\n"); + } + return used; +} + +static ssize_t list_inuse_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return print_chan_list(dev, buf, true); +} +static DEVICE_ATTR_RO(list_inuse); + +static ssize_t list_free_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return print_chan_list(dev, buf, false); +} +static DEVICE_ATTR_RO(list_free); + +static ssize_t list_gate_ena_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *cfg = &drvdata->config; + int b_sz = PAGE_SIZE; + u32 chan_mask; + int chan, used = 0; + + if (cfg->ctigate == 0) { + used += scnprintf(buf, b_sz, "none\n"); + } else { + for (chan = 0; chan < cfg->nr_ctm_channels; chan++) { + chan_mask = 0x1 << chan; + if (chan_mask & cfg->ctigate) { + used += scnprintf(buf+used, b_sz-used, "%d ", + chan); + } + } + used += scnprintf(buf+used, b_sz-used, "\n"); + } + return used; +} +static DEVICE_ATTR_RO(list_gate_ena); + +static struct attribute *coresight_cti_channel_attrs[] = { + &dev_attr_trigin_attach.attr, + &dev_attr_trigin_detach.attr, + &dev_attr_trigout_attach.attr, + &dev_attr_trigout_detach.attr, + &dev_attr_gate_enable.attr, + &dev_attr_gate_disable.attr, + &dev_attr_chan_set.attr, + &dev_attr_chan_clear.attr, + &dev_attr_chan_pulse.attr, + &dev_attr_trig_filter_enable.attr, + &dev_attr_reset_xtrigs.attr, + &dev_attr_show_chan_sel.attr, + &dev_attr_show_chan_xtrigs.attr, + &dev_attr_list_inuse.attr, + &dev_attr_list_free.attr, + &dev_attr_list_gate_ena.attr, + NULL, +}; + +/* attribute and group sysfs tables. */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, };
+static const struct attribute_group coresight_cti_mgmt_group = { + .attrs = coresight_cti_mgmt_attrs, + .name = "mgmt", +}; + +static const struct attribute_group coresight_cti_regs_group = { + .attrs = coresight_cti_regs_attrs, + .name = "regs", +}; + +static const struct attribute_group coresight_cti_channels_group = { + .attrs = coresight_cti_channel_attrs, + .name = "channels", +}; + const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_group, + &coresight_cti_mgmt_group, + &coresight_cti_regs_group, + &coresight_cti_channels_group, NULL, }; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 0a0fb6d48237..06b1f84795a4 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -69,7 +69,7 @@ static int cti_cpu_count; dev_get_drvdata(csdev->dev.parent)
/* write set of regs to hardware - call with spinlock claimed */ -static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +void cti_write_all_hw_regs(struct cti_drvdata *drvdata) { struct cti_config *config = &drvdata->config; int i; @@ -173,6 +173,13 @@ static int cti_disable_hw(void *info) return 0; }
+void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) +{ + CS_UNLOCK(drvdata->base); + writel_relaxed(value, drvdata->base+offset); + CS_LOCK(drvdata->base); +} + static void cti_set_default_config(struct cti_config *config) { /* Most regs default to 0 as zalloc'ed except...*/ @@ -285,6 +292,178 @@ int cti_add_default_connection(struct cti_drvdata *drvdata) return ret; }
+/** cti channel api **/ +/* attach/detach channel from trigger - write through if enabled. */ +int cti_channel_trig_op(struct device *dev, + enum cti_chan_op op, + enum cti_trig_dir direction, + u32 channel_idx, + u32 trigger_idx) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + u32 trig_bitmask; + u32 chan_bitmask; + u32 reg_value; + int reg_offset; + + dev_info(dev, "chan_trig_op: op %d, dir %d, chan %d, trig %d\n", + op, direction, channel_idx, trigger_idx); + + /* ensure indexes in range */ + if ((channel_idx >= config->nr_ctm_channels) || + (trigger_idx >= config->nr_trig_max)) + return -EINVAL; + + trig_bitmask = 0x1 << trigger_idx; + + /* ensure registered triggers and not out filtered */ + if (direction == CTI_TRIG_IN) { + if (!(trig_bitmask & config->trig_in_use)) + return -EINVAL; + } else { + if (!(trig_bitmask & config->trig_out_use)) + return -EINVAL; + + if ((config->trig_filter_enable) && + (config->trig_out_filter & trig_bitmask)) + return -EINVAL; + } + + /* update the local register values */ + chan_bitmask = 0x1 << channel_idx; + reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) : + CTIOUTEN(trigger_idx)); + + spin_lock(&drvdata->spinlock); + + /* read - modify write - the trigger / channel enable value */ + reg_value = (direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] : + config->ctiouten[trigger_idx]); + reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask : + reg_value & (~chan_bitmask); + + /* write local copy */ + if (direction == CTI_TRIG_IN) + config->ctiinen[trigger_idx] = reg_value; + else + config->ctiouten[trigger_idx] = reg_value; + + /* write through if enabled */ + if (CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, reg_offset, reg_value); + spin_unlock(&drvdata->spinlock); + return 0; +} + +int cti_channel_gate_op(struct device *dev, + enum cti_chan_gate_op op, + u32 channel_idx) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + u32 chan_bitmask; + u32 reg_value; + int err = 0; + + if (channel_idx >= config->nr_ctm_channels) + return -EINVAL; + + chan_bitmask = 0x1 << channel_idx; + + spin_lock(&drvdata->spinlock); + reg_value = config->ctigate; + switch (op) { + case CTI_GATE_CHAN_ENABLE: + reg_value |= chan_bitmask; + break; + + case CTI_GATE_CHAN_DISABLE: + reg_value &= ~chan_bitmask; + break; + + case CTI_GATE_CHAN_ENABLE_ALL: + reg_value = (0x1 << config->nr_ctm_channels) - 1; + break; + + case CTI_GATE_CHAN_DISABLE_ALL: + reg_value = 0x0; + break; + + default: + err = -EINVAL; + break; + } + if (err == 0) { + config->ctigate = reg_value; + if (CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, CTIGATE, reg_value); + } + spin_unlock(&drvdata->spinlock); + return err; +} + +int cti_channel_setop(struct device *dev, + enum cti_chan_set_op op, + u32 channel_idx) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + u32 chan_bitmask; + u32 reg_value; + u32 reg_offset; + int err = 0; + + if (channel_idx >= config->nr_ctm_channels) + return -EINVAL; + + chan_bitmask = 0x1 << channel_idx; + + spin_lock(&drvdata->spinlock); + reg_value = config->ctiappset; + switch (op) { + case CTI_CHAN_SET: + config->ctiappset |= chan_bitmask; + reg_value = config->ctiappset; + reg_offset = CTIAPPSET; + break; + + case CTI_CHAN_CLR: + config->ctiappset &= ~chan_bitmask; + reg_value = chan_bitmask; + reg_offset = CTIAPPCLEAR; + break; + + case CTI_CHAN_PULSE: + config->ctiappset &= ~chan_bitmask; + reg_value = chan_bitmask; + reg_offset = CTIAPPPULSE; + break; + + default: + err = -EINVAL; + break; + } + + if ((err == 0) && CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, reg_offset, reg_value); + spin_unlock(&drvdata->spinlock); + + return err; +} + +void cti_write_intack(struct device *dev, u32 ackval) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + spin_lock(&drvdata->spinlock); + /* write if enabled */ + if (CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, CTIINTACK, ackval); + spin_unlock(&drvdata->spinlock); +} + /** cti ect operations **/ int cti_enable(struct coresight_device *csdev, void *__unused) { @@ -432,7 +611,12 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id)
/* all done - dec pm refcount */ pm_runtime_put(&adev->dev); - dev_info(dev, "%s: initialized\n", pdata->name); + if (drvdata->ctidev.cpu >= 0) { + dev_info(dev, "%s: cti-CPU%d initialized\n", pdata->name, + drvdata->ctidev.cpu); + } else { + dev_info(dev, "%s: cti-system initialized\n", pdata->name); + } cti_count++; return 0;
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index c1af6cf9c2fe..a5bf59155f62 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -153,6 +153,7 @@ struct cti_device { * @trig_out_filter: bitfield of out triggers that are blocked if filter * enabled. Typically this would be dbgreq / restart on a core CTI. * @trig_filter_enable: 1 if filtering enabled. + * @xtrig_rchan_sel: channel ID when reading set xtrig info * * cti software programmable regs: * @ctiappset: CTI Software application channel set. @@ -174,6 +175,7 @@ struct cti_config { u32 trig_out_use; u32 trig_out_filter; int trig_filter_enable; + u8 xtrig_rchan_sel; /* cti cross trig programmable regs */ u32 ctiappset; u8 ctiinout_sel; @@ -202,11 +204,51 @@ struct cti_drvdata { struct cti_config config; };
+/* + * Channel operation types. + */ +enum cti_chan_op { + CTI_CHAN_ATTACH, + CTI_CHAN_DETACH, +}; + +enum cti_trig_dir { + CTI_TRIG_IN, + CTI_TRIG_OUT, +}; + +enum cti_chan_gate_op { + CTI_GATE_CHAN_ENABLE, + CTI_GATE_CHAN_DISABLE, + CTI_GATE_CHAN_ENABLE_ALL, + CTI_GATE_CHAN_DISABLE_ALL, +}; + +enum cti_chan_set_op { + CTI_CHAN_SET, + CTI_CHAN_CLR, + CTI_CHAN_PULSE, +}; + /* private cti driver fns & vars */ extern const struct attribute_group *coresight_cti_groups[]; + int cti_add_default_connection(struct cti_drvdata *drvdata); int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); +void cti_write_all_hw_regs(struct cti_drvdata *drvdata); +void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value); +int cti_channel_trig_op(struct device *dev, enum cti_chan_op op, + enum cti_trig_dir direction, u32 channel_idx, + u32 trigger_idx); +int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op, + u32 channel_idx); +int cti_channel_setop(struct device *dev, enum cti_chan_set_op op, + u32 channel_idx); +void cti_write_intack(struct device *dev, u32 ackval); + +/* cti powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
#ifdef CONFIG_OF extern int of_cti_get_hw_data(struct device *dev, diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 8e9e5b6ad866..9b497e2f8725 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -23,10 +23,12 @@ #define CORESIGHT_LAR 0xfb0 #define CORESIGHT_LSR 0xfb4 #define CORESIGHT_AUTHSTATUS 0xfb8 +#define CORESIGHT_DEVARCH 0xfbc #define CORESIGHT_DEVID 0xfc8 #define CORESIGHT_DEVTYPE 0xfcc
+ /* * Coresight device CLAIM protocol. * See PSCI - ARM DEN 0022D, Section: 6.8.1 Debug and Trace save and restore.
On Wed, May 01, 2019 at 09:49:34AM +0100, Mike Leach wrote:
Adds in required sysfs access for:-
- CoreSight management registers.
- CTI device specific registers.
- Channel Cross triggering API.
I would really appreciate if you could split the patch along the line of the cathegories listed above. Otherwise it is really arduous to review.
This provides the basic programming interface for the device.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 831 +++++++++++++++++- drivers/hwtracing/coresight/coresight-cti.c | 188 +++- drivers/hwtracing/coresight/coresight-cti.h | 42 + drivers/hwtracing/coresight/coresight-priv.h | 2 + 4 files changed, 1053 insertions(+), 10 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 9b6749621dcb..ff8dad02a779 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -34,8 +34,8 @@ static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) {
- int enable_req;
- bool enabled, powered, cpuid;
- int enable_req, cpuid;
- bool enabled, powered; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); ssize_t size = 0;
@@ -46,12 +46,18 @@ static ssize_t enable_show(struct device *dev, cpuid = drvdata->ctidev.cpu; spin_unlock(&drvdata->spinlock);
- if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
- } else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
- if (cpuid >= 0) {
if (powered) {
size = scnprintf(buf, PAGE_SIZE,
"cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled",
cpuid);
} else {
size = scnprintf(buf, PAGE_SIZE,
"cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req",
cpuid);
}
This seems to be a re-arrangement of what was done in the previous patch - please move it there.
I am also not sure about keeping track of the device's power status but I will wait until I've seen the whole set to comment on that part.
} else { size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n", enabled ? "enabled" : "disabled"); @@ -81,17 +87,826 @@ static ssize_t enable_store(struct device *dev, } static DEVICE_ATTR_RW(enable); +static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
Indentation problems - the 's' and 'c' on the second and third line need to be aligned with the 's' of "struct" on the first line.
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id);
Please drop the '0x' in "0x%x".
+} +static DEVICE_ATTR_RO(ctmid);
/* attribute and group sysfs tables. */ static struct attribute *coresight_cti_attrs[] = { &dev_attr_enable.attr,
- &dev_attr_ctmid.attr, NULL,
}; +/* register based attributes */ +#define coresight_cti_reg(name, offset) \
- coresight_simple_reg32(struct cti_drvdata, name, offset)
+/*
- Show a simple 32 bit value. If pval is NULL then live read,
- otherwise read from supplied pointer only.
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)
+{
- unsigned long val = 0;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- if (pval) {
val = (unsigned long)*pval;
- } else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + reg_offset);
CS_LOCK(drvdata->base);
- }
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+/*
- Store a simple 32 bit value.
- If pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,
int reg_offset)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* local store */
- if (pval)
*pval = (u32)val;
- /* write through of offset and enabled */
- if ((reg_offset >= 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+}
+/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4);
+static struct attribute *coresight_cti_mgmt_attrs[] = {
- &dev_attr_devaff0.attr,
- &dev_attr_devaff1.attr,
- &dev_attr_authstatus.attr,
- &dev_attr_devarch.attr,
- &dev_attr_devid.attr,
- &dev_attr_devtype.attr,
- &dev_attr_pidr0.attr,
- &dev_attr_pidr1.attr,
- &dev_attr_pidr2.attr,
- &dev_attr_pidr3.attr,
- &dev_attr_pidr4.attr,
- NULL,
+};
+/* CTI low level programming registers */ +static ssize_t inout_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- u32 val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- val = (u32)drvdata->config.ctiinout_sel;
- return scnprintf(buf, PAGE_SIZE, "%#x\n", val);
+}
Function inen_show() and outen_show() are both rendering the value of ctiinout_sel as an integer but inout_sel_show() and inout_sel_store() are manipulating the same variable as an hexadecimal. I think the latter need to be integers as well.
+static ssize_t inout_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- if (val > (CTIINOUTEN_MAX-1))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- drvdata->config.ctiinout_sel = val;
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(inout_sel);
+static ssize_t inen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- index = drvdata->config.ctiinout_sel;
- val = drvdata->config.ctiinen[index];
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "INEN%d %#lx\n", index, val);
+}
+static ssize_t inen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- index = config->ctiinout_sel;
- config->ctiinen[index] = val;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINEN(index), val);
I'm confused here - can you write to the device when it is enabled? If that is the case it is weird that in cti_write_all_hw_regs() the CTI needs to be disabled before the configuration registers are written.
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(inen);
+static ssize_t outen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
Indentation
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- index = drvdata->config.ctiinout_sel;
- val = drvdata->config.ctiouten[index];
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "OUTEN%d %#lx\n", index, val);
+}
+static ssize_t outen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
Indentation
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- index = config->ctiinout_sel;
- config->ctiouten[index] = val;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIOUTEN(index), val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(outen);
+static ssize_t gate_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.ctigate, -1);
+}
+static ssize_t gate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.ctigate, CTIGATE);
+} +static DEVICE_ATTR_RW(gate);
+static ssize_t asicctl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
Indentation
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.asicctl, -1);
+}
+static ssize_t asicctl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
Indentation
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.asicctl, ASICCTL);
+} +static DEVICE_ATTR_RW(asicctl);
+static ssize_t intack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
Indentation
+{
- unsigned long val;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- cti_write_intack(dev, val);
- return size;
+} +static DEVICE_ATTR_WO(intack);
+static ssize_t appset_show(struct device *dev,
struct device_attribute *attr,
char *buf)
Indentation - there are too many of those to point out. Please address.
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.ctiappset, -1);
+}
+static ssize_t appset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.ctiappset, CTIAPPSET);
+} +static DEVICE_ATTR_RW(appset);
+static ssize_t appclear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val, mask;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* a 1'b1 in appclr clears down the same bit in appset*/
- mask = ~val;
- config->ctiappset &= mask;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(appclear);
+static ssize_t apppulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val, mask;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /*
* a 1'b1 in apppulse sets then clears the bit,
* effectively clears down the same bit in appset
*/
- mask = ~val;
- config->ctiappset &= mask;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPPULSE, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(apppulse);
+static ssize_t itchout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, ITCHOUT);
+}
+static ssize_t itchout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITCHOUT);
+} +static DEVICE_ATTR_RW(itchout);
+static ssize_t ittrigout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, ITTRIGOUT);
+}
+static ssize_t ittrigout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITTRIGOUT);
+} +static DEVICE_ATTR_RW(ittrigout);
+static ssize_t itchinack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITCHINACK);
+} +static DEVICE_ATTR_WO(itchinack);
+static ssize_t ittriginack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITTRIGINACK);
+} +static DEVICE_ATTR_WO(ittriginack);
+static ssize_t itctrl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL);
+}
+static ssize_t itctrl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL);
+} +static DEVICE_ATTR_RW(itctrl);
Was it useful to you to make the integration registers available from sysfs? If so keep them, otherwiser I suggest removing as none of the other drivers have them. I'm especially weary that people will want to play with them, rendering the devices unusable and generating unwarranted phone calls.
A good compromise could be to enclose the test register with a in-file defined CTI_DEBUG. That way they are not visible unless someone really intends to enable them by recompiling their kernel.
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+static struct attribute *coresight_cti_regs_attrs[] = {
- &dev_attr_inout_sel.attr,
- &dev_attr_inen.attr,
- &dev_attr_outen.attr,
- &dev_attr_gate.attr,
- &dev_attr_asicctl.attr,
- &dev_attr_intack.attr,
- &dev_attr_appset.attr,
- &dev_attr_appclear.attr,
- &dev_attr_apppulse.attr,
- &dev_attr_triginstatus.attr,
- &dev_attr_trigoutstatus.attr,
- &dev_attr_chinstatus.attr,
- &dev_attr_choutstatus.attr,
- &dev_attr_itctrl.attr,
- &dev_attr_ittrigin.attr,
- &dev_attr_itchin.attr,
- &dev_attr_ittrigout.attr,
- &dev_attr_itchout.attr,
- &dev_attr_itchoutack.attr,
- &dev_attr_ittrigoutack.attr,
- &dev_attr_ittriginack.attr,
- &dev_attr_itchinack.attr,
- NULL,
+};
+/* CTI channel x-trigger programming */
There is more comments at the bottom of the patch. I will review the x-trigger programming section tomorrow.
+static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir dir, const char *buf, size_t size)
+{
- u32 chan_idx;
- u32 trig_idx;
- int items, err = size;
- /* extract chan idx and trigger idx */
- items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);
- if (items) {
err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);
if (!err)
err = size;
- } else
err = -EINVAL;
- return err;
+}
+static ssize_t trigin_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_attach);
+static ssize_t trigin_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_detach);
+static ssize_t trigout_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_attach);
+static ssize_t trigout_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_detach);
+static ssize_t gate_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = 0, channel = 0;
- if ((size >= 3) && (strncmp(buf, "all", 3) == 0)) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE_ALL, 0);
- } else {
if (kstrtoint(buf, 0, &channel))
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, channel);
- }
- return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_enable);
+static ssize_t gate_disable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = 0, channel = 0;
- if ((size >= 3) && (strncmp(buf, "all", 3) == 0)) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE_ALL, 0);
- } else {
if (kstrtoint(buf, 0, &channel))
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, channel);
- }
- return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_disable);
+static int +chan_op_parse(struct device *dev, enum cti_chan_set_op op, const char *buf) +{
- int err = 0, channel = 0;
- if (kstrtoint(buf, 0, &channel))
return -EINVAL;
- err = cti_channel_setop(dev, op, channel);
- return err;
+}
+static ssize_t chan_set_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_SET, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_set);
+static ssize_t chan_clear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_CLR, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_clear);
+static ssize_t chan_pulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_PULSE, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_pulse);
+static ssize_t trig_filter_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- val = drvdata->config.trig_filter_enable;
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "%ld (%s)\n", val,
val ? "enabled" : "disabled");
+}
+static ssize_t trig_filter_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- drvdata->config.trig_filter_enable = val ? 1 : 0;
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(trig_filter_enable);
+/* clear all xtrigger / channel programming */ +static ssize_t reset_xtrigs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int i;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* clear the CTI trigger / channel programming registers */
- for (i = 0; i < config->nr_trig_max; i++) {
config->ctiinen[i] = 0;
config->ctiouten[i] = 0;
- }
- /* clear the other regs */
- config->ctigate = (0x1 << config->nr_ctm_channels) - 1;
- config->asicctl = 0;
- config->ctiappset = 0;
- config->ctiinout_sel = 0;
- /* if enabled then write through */
- if (CTI_PWR_ENA(config))
cti_write_all_hw_regs(drvdata);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(reset_xtrigs);
+static ssize_t show_chan_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- u32 val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- val = (u32)drvdata->config.xtrig_rchan_sel;
- return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+/* select a channel for the show_chan_xtrig function */ +static ssize_t show_chan_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- if (val > (drvdata->config.nr_ctm_channels-1))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- drvdata->config.xtrig_rchan_sel = val;
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(show_chan_sel);
+/* for the selected channel - show the signals attached. */ +static ssize_t show_chan_xtrigs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- int used = 0, reg_idx;
- int buf_sz = PAGE_SIZE;
- u32 chan_mask = 0x1 << cfg->xtrig_rchan_sel;
- used += scnprintf(buf, buf_sz, "IN: ");
- for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiinen[reg_idx]) {
used += scnprintf(buf+used, buf_sz-used, "%d ",
reg_idx);
}
- }
- used += scnprintf(buf+used, buf_sz-used, " OUT: ");
- for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiouten[reg_idx]) {
used += scnprintf(buf+used, buf_sz-used, "%d ",
reg_idx);
}
- }
- used += scnprintf(buf+used, buf_sz-used, "\n");
- return used;
+} +static DEVICE_ATTR_RO(show_chan_xtrigs);
+static ssize_t print_chan_list(struct device *dev,
char *buf, bool inuse)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- int used = 0, i;
- u32 inuse_bits = 0, chan_mask, chan_bit_mask;
- /* scan regs to get bitmap of channels in use. */
- spin_lock(&drvdata->spinlock);
- for (i = 0; i < config->nr_trig_max; i++) {
inuse_bits |= config->ctiinen[i];
inuse_bits |= config->ctiouten[i];
- }
- spin_unlock(&drvdata->spinlock);
- /* inverse bits if printing free channels */
- if (!inuse)
inuse_bits = ~inuse_bits;
- /* list of channels, or 'none' */
- chan_mask = ((u32)(0x1 << config->nr_ctm_channels)) - 1;
- if (inuse_bits & chan_mask) {
for (i = 0; i < config->nr_ctm_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used += scnprintf(buf+used, PAGE_SIZE-used,
"%d ", i);
}
}
used += scnprintf(buf+used, PAGE_SIZE-used, "\n");
- } else {
used = scnprintf(buf, PAGE_SIZE, "none\n");
- }
- return used;
+}
+static ssize_t list_inuse_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return print_chan_list(dev, buf, true);
+} +static DEVICE_ATTR_RO(list_inuse);
+static ssize_t list_free_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return print_chan_list(dev, buf, false);
+} +static DEVICE_ATTR_RO(list_free);
+static ssize_t list_gate_ena_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- int b_sz = PAGE_SIZE;
- u32 chan_mask;
- int chan, used = 0;
- if (cfg->ctigate == 0) {
used += scnprintf(buf, b_sz, "none\n");
- } else {
for (chan = 0; chan < cfg->nr_ctm_channels; chan++) {
chan_mask = 0x1 << chan;
if (chan_mask & cfg->ctigate) {
used += scnprintf(buf+used, b_sz-used, "%d ",
chan);
}
}
used += scnprintf(buf+used, b_sz-used, "\n");
- }
- return used;
+} +static DEVICE_ATTR_RO(list_gate_ena);
+static struct attribute *coresight_cti_channel_attrs[] = {
- &dev_attr_trigin_attach.attr,
- &dev_attr_trigin_detach.attr,
- &dev_attr_trigout_attach.attr,
- &dev_attr_trigout_detach.attr,
- &dev_attr_gate_enable.attr,
- &dev_attr_gate_disable.attr,
- &dev_attr_chan_set.attr,
- &dev_attr_chan_clear.attr,
- &dev_attr_chan_pulse.attr,
- &dev_attr_trig_filter_enable.attr,
- &dev_attr_reset_xtrigs.attr,
- &dev_attr_show_chan_sel.attr,
- &dev_attr_show_chan_xtrigs.attr,
- &dev_attr_list_inuse.attr,
- &dev_attr_list_free.attr,
- &dev_attr_list_gate_ena.attr,
- NULL,
+};
+/* attribute and group sysfs tables. */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, }; +static const struct attribute_group coresight_cti_mgmt_group = {
- .attrs = coresight_cti_mgmt_attrs,
- .name = "mgmt",
+};
+static const struct attribute_group coresight_cti_regs_group = {
- .attrs = coresight_cti_regs_attrs,
- .name = "regs",
+};
+static const struct attribute_group coresight_cti_channels_group = {
- .attrs = coresight_cti_channel_attrs,
- .name = "channels",
+};
const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_group,
- &coresight_cti_mgmt_group,
- &coresight_cti_regs_group,
- &coresight_cti_channels_group, NULL,
}; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 0a0fb6d48237..06b1f84795a4 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -69,7 +69,7 @@ static int cti_cpu_count; dev_get_drvdata(csdev->dev.parent) /* write set of regs to hardware - call with spinlock claimed */ -static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +void cti_write_all_hw_regs(struct cti_drvdata *drvdata) { struct cti_config *config = &drvdata->config; int i; @@ -173,6 +173,13 @@ static int cti_disable_hw(void *info) return 0; } +void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) +{
- CS_UNLOCK(drvdata->base);
- writel_relaxed(value, drvdata->base+offset);
- CS_LOCK(drvdata->base);
+}
static void cti_set_default_config(struct cti_config *config) { /* Most regs default to 0 as zalloc'ed except...*/ @@ -285,6 +292,178 @@ int cti_add_default_connection(struct cti_drvdata *drvdata) return ret; } +/** cti channel api **/ +/* attach/detach channel from trigger - write through if enabled. */ +int cti_channel_trig_op(struct device *dev,
enum cti_chan_op op,
enum cti_trig_dir direction,
u32 channel_idx,
u32 trigger_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 trig_bitmask;
- u32 chan_bitmask;
- u32 reg_value;
- int reg_offset;
- dev_info(dev, "chan_trig_op: op %d, dir %d, chan %d, trig %d\n",
op, direction, channel_idx, trigger_idx);
- /* ensure indexes in range */
- if ((channel_idx >= config->nr_ctm_channels) ||
(trigger_idx >= config->nr_trig_max))
return -EINVAL;
- trig_bitmask = 0x1 << trigger_idx;
- /* ensure registered triggers and not out filtered */
- if (direction == CTI_TRIG_IN) {
if (!(trig_bitmask & config->trig_in_use))
return -EINVAL;
- } else {
if (!(trig_bitmask & config->trig_out_use))
return -EINVAL;
if ((config->trig_filter_enable) &&
(config->trig_out_filter & trig_bitmask))
return -EINVAL;
- }
- /* update the local register values */
- chan_bitmask = 0x1 << channel_idx;
- reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :
CTIOUTEN(trigger_idx));
- spin_lock(&drvdata->spinlock);
- /* read - modify write - the trigger / channel enable value */
- reg_value = (direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] :
config->ctiouten[trigger_idx]);
- reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask :
reg_value & (~chan_bitmask);
- /* write local copy */
- if (direction == CTI_TRIG_IN)
config->ctiinen[trigger_idx] = reg_value;
- else
config->ctiouten[trigger_idx] = reg_value;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
- spin_unlock(&drvdata->spinlock);
- return 0;
+}
+int cti_channel_gate_op(struct device *dev,
enum cti_chan_gate_op op,
u32 channel_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 chan_bitmask;
- u32 reg_value;
- int err = 0;
- if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
- chan_bitmask = 0x1 << channel_idx;
- spin_lock(&drvdata->spinlock);
- reg_value = config->ctigate;
- switch (op) {
- case CTI_GATE_CHAN_ENABLE:
reg_value |= chan_bitmask;
break;
- case CTI_GATE_CHAN_DISABLE:
reg_value &= ~chan_bitmask;
break;
- case CTI_GATE_CHAN_ENABLE_ALL:
reg_value = (0x1 << config->nr_ctm_channels) - 1;
break;
- case CTI_GATE_CHAN_DISABLE_ALL:
reg_value = 0x0;
break;
- default:
err = -EINVAL;
break;
- }
- if (err == 0) {
config->ctigate = reg_value;
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIGATE, reg_value);
- }
- spin_unlock(&drvdata->spinlock);
- return err;
+}
+int cti_channel_setop(struct device *dev,
enum cti_chan_set_op op,
u32 channel_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 chan_bitmask;
- u32 reg_value;
- u32 reg_offset;
- int err = 0;
- if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
- chan_bitmask = 0x1 << channel_idx;
- spin_lock(&drvdata->spinlock);
- reg_value = config->ctiappset;
- switch (op) {
- case CTI_CHAN_SET:
config->ctiappset |= chan_bitmask;
reg_value = config->ctiappset;
reg_offset = CTIAPPSET;
break;
- case CTI_CHAN_CLR:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPCLEAR;
break;
- case CTI_CHAN_PULSE:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPPULSE;
break;
- default:
err = -EINVAL;
break;
- }
- if ((err == 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
- spin_unlock(&drvdata->spinlock);
- return err;
+}
+void cti_write_intack(struct device *dev, u32 ackval) +{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* write if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINTACK, ackval);
- spin_unlock(&drvdata->spinlock);
+}
/** cti ect operations **/ int cti_enable(struct coresight_device *csdev, void *__unused) { @@ -432,7 +611,12 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) /* all done - dec pm refcount */ pm_runtime_put(&adev->dev);
- dev_info(dev, "%s: initialized\n", pdata->name);
- if (drvdata->ctidev.cpu >= 0) {
dev_info(dev, "%s: cti-CPU%d initialized\n", pdata->name,
drvdata->ctidev.cpu);
- } else {
dev_info(dev, "%s: cti-system initialized\n", pdata->name);
- }
Please move this to the previous patch.
cti_count++; return 0; diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index c1af6cf9c2fe..a5bf59155f62 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -153,6 +153,7 @@ struct cti_device {
- @trig_out_filter: bitfield of out triggers that are blocked if filter
- enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- @xtrig_rchan_sel: channel ID when reading set xtrig info
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
@@ -174,6 +175,7 @@ struct cti_config { u32 trig_out_use; u32 trig_out_filter; int trig_filter_enable;
- u8 xtrig_rchan_sel;
This should go in the previous patch.
/* cti cross trig programmable regs */ u32 ctiappset; u8 ctiinout_sel; @@ -202,11 +204,51 @@ struct cti_drvdata { struct cti_config config; }; +/*
- Channel operation types.
- */
+enum cti_chan_op {
- CTI_CHAN_ATTACH,
- CTI_CHAN_DETACH,
+};
+enum cti_trig_dir {
- CTI_TRIG_IN,
- CTI_TRIG_OUT,
+};
+enum cti_chan_gate_op {
- CTI_GATE_CHAN_ENABLE,
- CTI_GATE_CHAN_DISABLE,
- CTI_GATE_CHAN_ENABLE_ALL,
- CTI_GATE_CHAN_DISABLE_ALL,
+};
+enum cti_chan_set_op {
- CTI_CHAN_SET,
- CTI_CHAN_CLR,
- CTI_CHAN_PULSE,
+};
/* private cti driver fns & vars */ extern const struct attribute_group *coresight_cti_groups[];
Extra newline
int cti_add_default_connection(struct cti_drvdata *drvdata); int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); +void cti_write_all_hw_regs(struct cti_drvdata *drvdata); +void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value); +int cti_channel_trig_op(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir direction, u32 channel_idx,
u32 trigger_idx);
+int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op,
u32 channel_idx);
+int cti_channel_setop(struct device *dev, enum cti_chan_set_op op,
u32 channel_idx);
+void cti_write_intack(struct device *dev, u32 ackval);
+/* cti powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered) #ifdef CONFIG_OF extern int of_cti_get_hw_data(struct device *dev, diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 8e9e5b6ad866..9b497e2f8725 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -23,10 +23,12 @@ #define CORESIGHT_LAR 0xfb0 #define CORESIGHT_LSR 0xfb4 #define CORESIGHT_AUTHSTATUS 0xfb8 +#define CORESIGHT_DEVARCH 0xfbc #define CORESIGHT_DEVID 0xfc8 #define CORESIGHT_DEVTYPE 0xfcc
Extra newline
/*
- Coresight device CLAIM protocol.
- See PSCI - ARM DEN 0022D, Section: 6.8.1 Debug and Trace save and restore.
-- 2.20.1
Hi Mathieu,
On Wed, 8 May 2019 at 21:40, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, May 01, 2019 at 09:49:34AM +0100, Mike Leach wrote:
Adds in required sysfs access for:-
- CoreSight management registers.
- CTI device specific registers.
- Channel Cross triggering API.
I would really appreciate if you could split the patch along the line of the cathegories listed above. Otherwise it is really arduous to review.
Will do.
This provides the basic programming interface for the device.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 831 +++++++++++++++++- drivers/hwtracing/coresight/coresight-cti.c | 188 +++- drivers/hwtracing/coresight/coresight-cti.h | 42 + drivers/hwtracing/coresight/coresight-priv.h | 2 + 4 files changed, 1053 insertions(+), 10 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 9b6749621dcb..ff8dad02a779 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -34,8 +34,8 @@ static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) {
int enable_req;
bool enabled, powered, cpuid;
int enable_req, cpuid;
bool enabled, powered; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); ssize_t size = 0;
@@ -46,12 +46,18 @@ static ssize_t enable_show(struct device *dev, cpuid = drvdata->ctidev.cpu; spin_unlock(&drvdata->spinlock);
if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
} else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
if (cpuid >= 0) {
if (powered) {
size = scnprintf(buf, PAGE_SIZE,
"cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled",
cpuid);
} else {
size = scnprintf(buf, PAGE_SIZE,
"cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req",
cpuid);
}
This seems to be a re-arrangement of what was done in the previous patch - please move it there.
done - now in prev patch.
I am also not sure about keeping track of the device's power status but I will wait until I've seen the whole set to comment on that part.
see comment in prev patch.
} else { size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n", enabled ? "enabled" : "disabled");
@@ -81,17 +87,826 @@ static ssize_t enable_store(struct device *dev, } static DEVICE_ATTR_RW(enable);
+static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
Indentation problems - the 's' and 'c' on the second and third line need to be aligned with the 's' of "struct" on the first line.
OK
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id);
Please drop the '0x' in "0x%x".
OK
+} +static DEVICE_ATTR_RO(ctmid);
/* attribute and group sysfs tables. */ static struct attribute *coresight_cti_attrs[] = { &dev_attr_enable.attr,
&dev_attr_ctmid.attr, NULL,
};
+/* register based attributes */ +#define coresight_cti_reg(name, offset) \
coresight_simple_reg32(struct cti_drvdata, name, offset)
+/*
- Show a simple 32 bit value. If pval is NULL then live read,
- otherwise read from supplied pointer only.
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)
+{
unsigned long val = 0;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
if (pval) {
val = (unsigned long)*pval;
} else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + reg_offset);
CS_LOCK(drvdata->base);
}
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+/*
- Store a simple 32 bit value.
- If pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,
int reg_offset)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* local store */
if (pval)
*pval = (u32)val;
/* write through of offset and enabled */
if ((reg_offset >= 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, val);
spin_unlock(&drvdata->spinlock);
return size;
+}
+/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4);
+static struct attribute *coresight_cti_mgmt_attrs[] = {
&dev_attr_devaff0.attr,
&dev_attr_devaff1.attr,
&dev_attr_authstatus.attr,
&dev_attr_devarch.attr,
&dev_attr_devid.attr,
&dev_attr_devtype.attr,
&dev_attr_pidr0.attr,
&dev_attr_pidr1.attr,
&dev_attr_pidr2.attr,
&dev_attr_pidr3.attr,
&dev_attr_pidr4.attr,
NULL,
+};
+/* CTI low level programming registers */ +static ssize_t inout_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
u32 val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
val = (u32)drvdata->config.ctiinout_sel;
return scnprintf(buf, PAGE_SIZE, "%#x\n", val);
+}
Function inen_show() and outen_show() are both rendering the value of ctiinout_sel as an integer but inout_sel_show() and inout_sel_store() are manipulating the same variable as an hexadecimal. I think the latter need to be integers as well.
Agreed
+static ssize_t inout_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (kstrtoul(buf, 16, &val))
return -EINVAL;
if (val > (CTIINOUTEN_MAX-1))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->config.ctiinout_sel = val;
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(inout_sel);
+static ssize_t inen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
index = drvdata->config.ctiinout_sel;
val = drvdata->config.ctiinen[index];
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "INEN%d %#lx\n", index, val);
+}
+static ssize_t inen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
index = config->ctiinout_sel;
config->ctiinen[index] = val;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINEN(index), val);
I'm confused here - can you write to the device when it is enabled? If that is the case it is weird that in cti_write_all_hw_regs() the CTI needs to be disabled before the configuration registers are written.
CTI can be programmed while enabled - indeed appset / appclr / apppulse make little sense otherwise.
When we are programming all the device, at enable / power up / etc, it is desirable to have an atomic set so that all the connections go live at the same time - i.e. if multiple signals a connect to a channel, we want the entirety of the connection matrix live at the same time.
Now if a user is programming on the fly using sysfs, it is up to them if they are programming while live, or they could disable manually. So here we will write through so that the device operates as the user should expect on an enabled device. If the device is not enabled or powered then the driver will remember the setting for when it is and setup via write_all_hw_regs().
Obviously this differs from other CS devices that cannot/should not be programmed while enabled.
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(inen);
+static ssize_t outen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
Indentation
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
index = drvdata->config.ctiinout_sel;
val = drvdata->config.ctiouten[index];
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "OUTEN%d %#lx\n", index, val);
+}
+static ssize_t outen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
Indentation
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
index = config->ctiinout_sel;
config->ctiouten[index] = val;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIOUTEN(index), val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(outen);
+static ssize_t gate_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.ctigate, -1);
+}
+static ssize_t gate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.ctigate, CTIGATE);
+} +static DEVICE_ATTR_RW(gate);
+static ssize_t asicctl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
Indentation
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.asicctl, -1);
+}
+static ssize_t asicctl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
Indentation
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.asicctl, ASICCTL);
+} +static DEVICE_ATTR_RW(asicctl);
+static ssize_t intack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
Indentation
+{
unsigned long val;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
cti_write_intack(dev, val);
return size;
+} +static DEVICE_ATTR_WO(intack);
+static ssize_t appset_show(struct device *dev,
struct device_attribute *attr,
char *buf)
Indentation - there are too many of those to point out. Please address.
OK
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.ctiappset, -1);
+}
+static ssize_t appset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.ctiappset, CTIAPPSET);
+} +static DEVICE_ATTR_RW(appset);
+static ssize_t appclear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val, mask;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* a 1'b1 in appclr clears down the same bit in appset*/
mask = ~val;
config->ctiappset &= mask;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(appclear);
+static ssize_t apppulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val, mask;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/*
* a 1'b1 in apppulse sets then clears the bit,
* effectively clears down the same bit in appset
*/
mask = ~val;
config->ctiappset &= mask;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPPULSE, val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(apppulse);
+static ssize_t itchout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, ITCHOUT);
+}
+static ssize_t itchout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITCHOUT);
+} +static DEVICE_ATTR_RW(itchout);
+static ssize_t ittrigout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, ITTRIGOUT);
+}
+static ssize_t ittrigout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITTRIGOUT);
+} +static DEVICE_ATTR_RW(ittrigout);
+static ssize_t itchinack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITCHINACK);
+} +static DEVICE_ATTR_WO(itchinack);
+static ssize_t ittriginack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITTRIGINACK);
+} +static DEVICE_ATTR_WO(ittriginack);
+static ssize_t itctrl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL);
+}
+static ssize_t itctrl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL);
+} +static DEVICE_ATTR_RW(itctrl);
Was it useful to you to make the integration registers available from sysfs? If so keep them, otherwiser I suggest removing as none of the other drivers have them. I'm especially weary that people will want to play with them, rendering the devices unusable and generating unwarranted phone calls.
A good compromise could be to enclose the test register with a in-file defined CTI_DEBUG. That way they are not visible unless someone really intends to enable them by recompiling their kernel.
They are useful as often CTI signal connections are the worst documented. That said - they are not for general use so encasing them in a compile time #define seems a good option.
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+static struct attribute *coresight_cti_regs_attrs[] = {
&dev_attr_inout_sel.attr,
&dev_attr_inen.attr,
&dev_attr_outen.attr,
&dev_attr_gate.attr,
&dev_attr_asicctl.attr,
&dev_attr_intack.attr,
&dev_attr_appset.attr,
&dev_attr_appclear.attr,
&dev_attr_apppulse.attr,
&dev_attr_triginstatus.attr,
&dev_attr_trigoutstatus.attr,
&dev_attr_chinstatus.attr,
&dev_attr_choutstatus.attr,
&dev_attr_itctrl.attr,
&dev_attr_ittrigin.attr,
&dev_attr_itchin.attr,
&dev_attr_ittrigout.attr,
&dev_attr_itchout.attr,
&dev_attr_itchoutack.attr,
&dev_attr_ittrigoutack.attr,
&dev_attr_ittriginack.attr,
&dev_attr_itchinack.attr,
NULL,
+};
+/* CTI channel x-trigger programming */
There is more comments at the bottom of the patch. I will review the x-trigger programming section tomorrow.
+static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir dir, const char *buf, size_t size)
+{
u32 chan_idx;
u32 trig_idx;
int items, err = size;
/* extract chan idx and trigger idx */
items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);
if (items) {
err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);
if (!err)
err = size;
} else
err = -EINVAL;
return err;
+}
+static ssize_t trigin_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_attach);
+static ssize_t trigin_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_detach);
+static ssize_t trigout_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_attach);
+static ssize_t trigout_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_detach);
+static ssize_t gate_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = 0, channel = 0;
if ((size >= 3) && (strncmp(buf, "all", 3) == 0)) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE_ALL, 0);
} else {
if (kstrtoint(buf, 0, &channel))
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, channel);
}
return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_enable);
+static ssize_t gate_disable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = 0, channel = 0;
if ((size >= 3) && (strncmp(buf, "all", 3) == 0)) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE_ALL, 0);
} else {
if (kstrtoint(buf, 0, &channel))
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, channel);
}
return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_disable);
+static int +chan_op_parse(struct device *dev, enum cti_chan_set_op op, const char *buf) +{
int err = 0, channel = 0;
if (kstrtoint(buf, 0, &channel))
return -EINVAL;
err = cti_channel_setop(dev, op, channel);
return err;
+}
+static ssize_t chan_set_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_SET, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_set);
+static ssize_t chan_clear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_CLR, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_clear);
+static ssize_t chan_pulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_PULSE, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_pulse);
+static ssize_t trig_filter_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
val = drvdata->config.trig_filter_enable;
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%ld (%s)\n", val,
val ? "enabled" : "disabled");
+}
+static ssize_t trig_filter_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->config.trig_filter_enable = val ? 1 : 0;
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(trig_filter_enable);
+/* clear all xtrigger / channel programming */ +static ssize_t reset_xtrigs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int i;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* clear the CTI trigger / channel programming registers */
for (i = 0; i < config->nr_trig_max; i++) {
config->ctiinen[i] = 0;
config->ctiouten[i] = 0;
}
/* clear the other regs */
config->ctigate = (0x1 << config->nr_ctm_channels) - 1;
config->asicctl = 0;
config->ctiappset = 0;
config->ctiinout_sel = 0;
/* if enabled then write through */
if (CTI_PWR_ENA(config))
cti_write_all_hw_regs(drvdata);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(reset_xtrigs);
+static ssize_t show_chan_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
u32 val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
val = (u32)drvdata->config.xtrig_rchan_sel;
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+/* select a channel for the show_chan_xtrig function */ +static ssize_t show_chan_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (kstrtoul(buf, 16, &val))
return -EINVAL;
if (val > (drvdata->config.nr_ctm_channels-1))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->config.xtrig_rchan_sel = val;
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(show_chan_sel);
+/* for the selected channel - show the signals attached. */ +static ssize_t show_chan_xtrigs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config;
int used = 0, reg_idx;
int buf_sz = PAGE_SIZE;
u32 chan_mask = 0x1 << cfg->xtrig_rchan_sel;
used += scnprintf(buf, buf_sz, "IN: ");
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiinen[reg_idx]) {
used += scnprintf(buf+used, buf_sz-used, "%d ",
reg_idx);
}
}
used += scnprintf(buf+used, buf_sz-used, " OUT: ");
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiouten[reg_idx]) {
used += scnprintf(buf+used, buf_sz-used, "%d ",
reg_idx);
}
}
used += scnprintf(buf+used, buf_sz-used, "\n");
return used;
+} +static DEVICE_ATTR_RO(show_chan_xtrigs);
+static ssize_t print_chan_list(struct device *dev,
char *buf, bool inuse)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
int used = 0, i;
u32 inuse_bits = 0, chan_mask, chan_bit_mask;
/* scan regs to get bitmap of channels in use. */
spin_lock(&drvdata->spinlock);
for (i = 0; i < config->nr_trig_max; i++) {
inuse_bits |= config->ctiinen[i];
inuse_bits |= config->ctiouten[i];
}
spin_unlock(&drvdata->spinlock);
/* inverse bits if printing free channels */
if (!inuse)
inuse_bits = ~inuse_bits;
/* list of channels, or 'none' */
chan_mask = ((u32)(0x1 << config->nr_ctm_channels)) - 1;
if (inuse_bits & chan_mask) {
for (i = 0; i < config->nr_ctm_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used += scnprintf(buf+used, PAGE_SIZE-used,
"%d ", i);
}
}
used += scnprintf(buf+used, PAGE_SIZE-used, "\n");
} else {
used = scnprintf(buf, PAGE_SIZE, "none\n");
}
return used;
+}
+static ssize_t list_inuse_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return print_chan_list(dev, buf, true);
+} +static DEVICE_ATTR_RO(list_inuse);
+static ssize_t list_free_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return print_chan_list(dev, buf, false);
+} +static DEVICE_ATTR_RO(list_free);
+static ssize_t list_gate_ena_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config;
int b_sz = PAGE_SIZE;
u32 chan_mask;
int chan, used = 0;
if (cfg->ctigate == 0) {
used += scnprintf(buf, b_sz, "none\n");
} else {
for (chan = 0; chan < cfg->nr_ctm_channels; chan++) {
chan_mask = 0x1 << chan;
if (chan_mask & cfg->ctigate) {
used += scnprintf(buf+used, b_sz-used, "%d ",
chan);
}
}
used += scnprintf(buf+used, b_sz-used, "\n");
}
return used;
+} +static DEVICE_ATTR_RO(list_gate_ena);
+static struct attribute *coresight_cti_channel_attrs[] = {
&dev_attr_trigin_attach.attr,
&dev_attr_trigin_detach.attr,
&dev_attr_trigout_attach.attr,
&dev_attr_trigout_detach.attr,
&dev_attr_gate_enable.attr,
&dev_attr_gate_disable.attr,
&dev_attr_chan_set.attr,
&dev_attr_chan_clear.attr,
&dev_attr_chan_pulse.attr,
&dev_attr_trig_filter_enable.attr,
&dev_attr_reset_xtrigs.attr,
&dev_attr_show_chan_sel.attr,
&dev_attr_show_chan_xtrigs.attr,
&dev_attr_list_inuse.attr,
&dev_attr_list_free.attr,
&dev_attr_list_gate_ena.attr,
NULL,
+};
+/* attribute and group sysfs tables. */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, };
+static const struct attribute_group coresight_cti_mgmt_group = {
.attrs = coresight_cti_mgmt_attrs,
.name = "mgmt",
+};
+static const struct attribute_group coresight_cti_regs_group = {
.attrs = coresight_cti_regs_attrs,
.name = "regs",
+};
+static const struct attribute_group coresight_cti_channels_group = {
.attrs = coresight_cti_channel_attrs,
.name = "channels",
+};
const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_group,
&coresight_cti_mgmt_group,
&coresight_cti_regs_group,
&coresight_cti_channels_group, NULL,
}; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 0a0fb6d48237..06b1f84795a4 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -69,7 +69,7 @@ static int cti_cpu_count; dev_get_drvdata(csdev->dev.parent)
/* write set of regs to hardware - call with spinlock claimed */ -static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +void cti_write_all_hw_regs(struct cti_drvdata *drvdata) { struct cti_config *config = &drvdata->config; int i; @@ -173,6 +173,13 @@ static int cti_disable_hw(void *info) return 0; }
+void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) +{
CS_UNLOCK(drvdata->base);
writel_relaxed(value, drvdata->base+offset);
CS_LOCK(drvdata->base);
+}
static void cti_set_default_config(struct cti_config *config) { /* Most regs default to 0 as zalloc'ed except...*/ @@ -285,6 +292,178 @@ int cti_add_default_connection(struct cti_drvdata *drvdata) return ret; }
+/** cti channel api **/ +/* attach/detach channel from trigger - write through if enabled. */ +int cti_channel_trig_op(struct device *dev,
enum cti_chan_op op,
enum cti_trig_dir direction,
u32 channel_idx,
u32 trigger_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 trig_bitmask;
u32 chan_bitmask;
u32 reg_value;
int reg_offset;
dev_info(dev, "chan_trig_op: op %d, dir %d, chan %d, trig %d\n",
op, direction, channel_idx, trigger_idx);
/* ensure indexes in range */
if ((channel_idx >= config->nr_ctm_channels) ||
(trigger_idx >= config->nr_trig_max))
return -EINVAL;
trig_bitmask = 0x1 << trigger_idx;
/* ensure registered triggers and not out filtered */
if (direction == CTI_TRIG_IN) {
if (!(trig_bitmask & config->trig_in_use))
return -EINVAL;
} else {
if (!(trig_bitmask & config->trig_out_use))
return -EINVAL;
if ((config->trig_filter_enable) &&
(config->trig_out_filter & trig_bitmask))
return -EINVAL;
}
/* update the local register values */
chan_bitmask = 0x1 << channel_idx;
reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :
CTIOUTEN(trigger_idx));
spin_lock(&drvdata->spinlock);
/* read - modify write - the trigger / channel enable value */
reg_value = (direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] :
config->ctiouten[trigger_idx]);
reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask :
reg_value & (~chan_bitmask);
/* write local copy */
if (direction == CTI_TRIG_IN)
config->ctiinen[trigger_idx] = reg_value;
else
config->ctiouten[trigger_idx] = reg_value;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
spin_unlock(&drvdata->spinlock);
return 0;
+}
+int cti_channel_gate_op(struct device *dev,
enum cti_chan_gate_op op,
u32 channel_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 chan_bitmask;
u32 reg_value;
int err = 0;
if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
chan_bitmask = 0x1 << channel_idx;
spin_lock(&drvdata->spinlock);
reg_value = config->ctigate;
switch (op) {
case CTI_GATE_CHAN_ENABLE:
reg_value |= chan_bitmask;
break;
case CTI_GATE_CHAN_DISABLE:
reg_value &= ~chan_bitmask;
break;
case CTI_GATE_CHAN_ENABLE_ALL:
reg_value = (0x1 << config->nr_ctm_channels) - 1;
break;
case CTI_GATE_CHAN_DISABLE_ALL:
reg_value = 0x0;
break;
default:
err = -EINVAL;
break;
}
if (err == 0) {
config->ctigate = reg_value;
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIGATE, reg_value);
}
spin_unlock(&drvdata->spinlock);
return err;
+}
+int cti_channel_setop(struct device *dev,
enum cti_chan_set_op op,
u32 channel_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 chan_bitmask;
u32 reg_value;
u32 reg_offset;
int err = 0;
if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
chan_bitmask = 0x1 << channel_idx;
spin_lock(&drvdata->spinlock);
reg_value = config->ctiappset;
switch (op) {
case CTI_CHAN_SET:
config->ctiappset |= chan_bitmask;
reg_value = config->ctiappset;
reg_offset = CTIAPPSET;
break;
case CTI_CHAN_CLR:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPCLEAR;
break;
case CTI_CHAN_PULSE:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPPULSE;
break;
default:
err = -EINVAL;
break;
}
if ((err == 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
spin_unlock(&drvdata->spinlock);
return err;
+}
+void cti_write_intack(struct device *dev, u32 ackval) +{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* write if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINTACK, ackval);
spin_unlock(&drvdata->spinlock);
+}
/** cti ect operations **/ int cti_enable(struct coresight_device *csdev, void *__unused) { @@ -432,7 +611,12 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id)
/* all done - dec pm refcount */ pm_runtime_put(&adev->dev);
dev_info(dev, "%s: initialized\n", pdata->name);
if (drvdata->ctidev.cpu >= 0) {
dev_info(dev, "%s: cti-CPU%d initialized\n", pdata->name,
drvdata->ctidev.cpu);
} else {
dev_info(dev, "%s: cti-system initialized\n", pdata->name);
}
Please move this to the previous patch.
cti_count++; return 0;
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index c1af6cf9c2fe..a5bf59155f62 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -153,6 +153,7 @@ struct cti_device {
- @trig_out_filter: bitfield of out triggers that are blocked if filter
- enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- @xtrig_rchan_sel: channel ID when reading set xtrig info
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
@@ -174,6 +175,7 @@ struct cti_config { u32 trig_out_use; u32 trig_out_filter; int trig_filter_enable;
u8 xtrig_rchan_sel;
This should go in the previous patch.
/* cti cross trig programmable regs */ u32 ctiappset; u8 ctiinout_sel;
@@ -202,11 +204,51 @@ struct cti_drvdata { struct cti_config config; };
+/*
- Channel operation types.
- */
+enum cti_chan_op {
CTI_CHAN_ATTACH,
CTI_CHAN_DETACH,
+};
+enum cti_trig_dir {
CTI_TRIG_IN,
CTI_TRIG_OUT,
+};
+enum cti_chan_gate_op {
CTI_GATE_CHAN_ENABLE,
CTI_GATE_CHAN_DISABLE,
CTI_GATE_CHAN_ENABLE_ALL,
CTI_GATE_CHAN_DISABLE_ALL,
+};
+enum cti_chan_set_op {
CTI_CHAN_SET,
CTI_CHAN_CLR,
CTI_CHAN_PULSE,
+};
/* private cti driver fns & vars */ extern const struct attribute_group *coresight_cti_groups[];
Extra newline
int cti_add_default_connection(struct cti_drvdata *drvdata); int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); +void cti_write_all_hw_regs(struct cti_drvdata *drvdata); +void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value); +int cti_channel_trig_op(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir direction, u32 channel_idx,
u32 trigger_idx);
+int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op,
u32 channel_idx);
+int cti_channel_setop(struct device *dev, enum cti_chan_set_op op,
u32 channel_idx);
+void cti_write_intack(struct device *dev, u32 ackval);
+/* cti powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
#ifdef CONFIG_OF extern int of_cti_get_hw_data(struct device *dev, diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 8e9e5b6ad866..9b497e2f8725 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -23,10 +23,12 @@ #define CORESIGHT_LAR 0xfb0 #define CORESIGHT_LSR 0xfb4 #define CORESIGHT_AUTHSTATUS 0xfb8 +#define CORESIGHT_DEVARCH 0xfbc #define CORESIGHT_DEVID 0xfc8 #define CORESIGHT_DEVTYPE 0xfcc
Extra newline
/*
- Coresight device CLAIM protocol.
- See PSCI - ARM DEN 0022D, Section: 6.8.1 Debug and Trace save and restore.
-- 2.20.1
Adjusted as requested. Will appear as 3 separate patches next release
thanks
Mike
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
On Wed, May 01, 2019 at 09:49:34AM +0100, Mike Leach wrote:
Adds in required sysfs access for:-
- CoreSight management registers.
- CTI device specific registers.
- Channel Cross triggering API.
This provides the basic programming interface for the device.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 831 +++++++++++++++++- drivers/hwtracing/coresight/coresight-cti.c | 188 +++- drivers/hwtracing/coresight/coresight-cti.h | 42 + drivers/hwtracing/coresight/coresight-priv.h | 2 + 4 files changed, 1053 insertions(+), 10 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 9b6749621dcb..ff8dad02a779 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -34,8 +34,8 @@ static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) {
- int enable_req;
- bool enabled, powered, cpuid;
- int enable_req, cpuid;
- bool enabled, powered; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); ssize_t size = 0;
@@ -46,12 +46,18 @@ static ssize_t enable_show(struct device *dev, cpuid = drvdata->ctidev.cpu; spin_unlock(&drvdata->spinlock);
- if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
- } else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
- if (cpuid >= 0) {
if (powered) {
size = scnprintf(buf, PAGE_SIZE,
"cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled",
cpuid);
} else {
size = scnprintf(buf, PAGE_SIZE,
"cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req",
cpuid);
} else { size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n", enabled ? "enabled" : "disabled");}
@@ -81,17 +87,826 @@ static ssize_t enable_store(struct device *dev, } static DEVICE_ATTR_RW(enable); +static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id);
+} +static DEVICE_ATTR_RO(ctmid);
/* attribute and group sysfs tables. */ static struct attribute *coresight_cti_attrs[] = { &dev_attr_enable.attr,
- &dev_attr_ctmid.attr, NULL,
}; +/* register based attributes */ +#define coresight_cti_reg(name, offset) \
- coresight_simple_reg32(struct cti_drvdata, name, offset)
+/*
- Show a simple 32 bit value. If pval is NULL then live read,
- otherwise read from supplied pointer only.
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)
+{
- unsigned long val = 0;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- if (pval) {
val = (unsigned long)*pval;
- } else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + reg_offset);
CS_LOCK(drvdata->base);
- }
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+/*
- Store a simple 32 bit value.
- If pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,
int reg_offset)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* local store */
- if (pval)
*pval = (u32)val;
- /* write through of offset and enabled */
- if ((reg_offset >= 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+}
+/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4);
+static struct attribute *coresight_cti_mgmt_attrs[] = {
- &dev_attr_devaff0.attr,
- &dev_attr_devaff1.attr,
- &dev_attr_authstatus.attr,
- &dev_attr_devarch.attr,
- &dev_attr_devid.attr,
- &dev_attr_devtype.attr,
- &dev_attr_pidr0.attr,
- &dev_attr_pidr1.attr,
- &dev_attr_pidr2.attr,
- &dev_attr_pidr3.attr,
- &dev_attr_pidr4.attr,
- NULL,
+};
+/* CTI low level programming registers */ +static ssize_t inout_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- u32 val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- val = (u32)drvdata->config.ctiinout_sel;
- return scnprintf(buf, PAGE_SIZE, "%#x\n", val);
+}
+static ssize_t inout_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- if (val > (CTIINOUTEN_MAX-1))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- drvdata->config.ctiinout_sel = val;
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(inout_sel);
+static ssize_t inen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- index = drvdata->config.ctiinout_sel;
- val = drvdata->config.ctiinen[index];
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "INEN%d %#lx\n", index, val);
+}
+static ssize_t inen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- index = config->ctiinout_sel;
- config->ctiinen[index] = val;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINEN(index), val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(inen);
+static ssize_t outen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- index = drvdata->config.ctiinout_sel;
- val = drvdata->config.ctiouten[index];
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "OUTEN%d %#lx\n", index, val);
+}
+static ssize_t outen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- index = config->ctiinout_sel;
- config->ctiouten[index] = val;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIOUTEN(index), val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(outen);
+static ssize_t gate_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.ctigate, -1);
+}
+static ssize_t gate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.ctigate, CTIGATE);
+} +static DEVICE_ATTR_RW(gate);
+static ssize_t asicctl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.asicctl, -1);
+}
+static ssize_t asicctl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.asicctl, ASICCTL);
+} +static DEVICE_ATTR_RW(asicctl);
+static ssize_t intack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- cti_write_intack(dev, val);
- return size;
+} +static DEVICE_ATTR_WO(intack);
+static ssize_t appset_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.ctiappset, -1);
+}
+static ssize_t appset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.ctiappset, CTIAPPSET);
+} +static DEVICE_ATTR_RW(appset);
+static ssize_t appclear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val, mask;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* a 1'b1 in appclr clears down the same bit in appset*/
- mask = ~val;
- config->ctiappset &= mask;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(appclear);
+static ssize_t apppulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val, mask;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /*
* a 1'b1 in apppulse sets then clears the bit,
* effectively clears down the same bit in appset
*/
- mask = ~val;
- config->ctiappset &= mask;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPPULSE, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(apppulse);
+static ssize_t itchout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, ITCHOUT);
+}
+static ssize_t itchout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITCHOUT);
+} +static DEVICE_ATTR_RW(itchout);
+static ssize_t ittrigout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, ITTRIGOUT);
+}
+static ssize_t ittrigout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITTRIGOUT);
+} +static DEVICE_ATTR_RW(ittrigout);
+static ssize_t itchinack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITCHINACK);
+} +static DEVICE_ATTR_WO(itchinack);
+static ssize_t ittriginack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITTRIGINACK);
+} +static DEVICE_ATTR_WO(ittriginack);
+static ssize_t itctrl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL);
+}
+static ssize_t itctrl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL);
+} +static DEVICE_ATTR_RW(itctrl);
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+static struct attribute *coresight_cti_regs_attrs[] = {
- &dev_attr_inout_sel.attr,
- &dev_attr_inen.attr,
- &dev_attr_outen.attr,
- &dev_attr_gate.attr,
- &dev_attr_asicctl.attr,
- &dev_attr_intack.attr,
- &dev_attr_appset.attr,
- &dev_attr_appclear.attr,
- &dev_attr_apppulse.attr,
- &dev_attr_triginstatus.attr,
- &dev_attr_trigoutstatus.attr,
- &dev_attr_chinstatus.attr,
- &dev_attr_choutstatus.attr,
- &dev_attr_itctrl.attr,
- &dev_attr_ittrigin.attr,
- &dev_attr_itchin.attr,
- &dev_attr_ittrigout.attr,
- &dev_attr_itchout.attr,
- &dev_attr_itchoutack.attr,
- &dev_attr_ittrigoutack.attr,
- &dev_attr_ittriginack.attr,
- &dev_attr_itchinack.attr,
- NULL,
+};
+/* CTI channel x-trigger programming */ +static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir dir, const char *buf, size_t size)
+{
- u32 chan_idx;
- u32 trig_idx;
- int items, err = size;
- /* extract chan idx and trigger idx */
- items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);
- if (items) {
err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);
if (!err)
err = size;
- } else
err = -EINVAL;
- return err;
+}
+static ssize_t trigin_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_attach);
+static ssize_t trigin_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_detach);
+static ssize_t trigout_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_attach);
+static ssize_t trigout_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_detach);
+static ssize_t gate_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = 0, channel = 0;
- if ((size >= 3) && (strncmp(buf, "all", 3) == 0)) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE_ALL, 0);
- } else {
if (kstrtoint(buf, 0, &channel))
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, channel);
- }
- return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_enable);
+static ssize_t gate_disable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = 0, channel = 0;
- if ((size >= 3) && (strncmp(buf, "all", 3) == 0)) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE_ALL, 0);
- } else {
if (kstrtoint(buf, 0, &channel))
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, channel);
- }
- return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_disable);
+static int +chan_op_parse(struct device *dev, enum cti_chan_set_op op, const char *buf) +{
- int err = 0, channel = 0;
- if (kstrtoint(buf, 0, &channel))
return -EINVAL;
- err = cti_channel_setop(dev, op, channel);
- return err;
+}
+static ssize_t chan_set_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_SET, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_set);
+static ssize_t chan_clear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_CLR, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_clear);
+static ssize_t chan_pulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_PULSE, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_pulse);
+static ssize_t trig_filter_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- val = drvdata->config.trig_filter_enable;
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "%ld (%s)\n", val,
val ? "enabled" : "disabled");
+}
+static ssize_t trig_filter_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- drvdata->config.trig_filter_enable = val ? 1 : 0;
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(trig_filter_enable);
+/* clear all xtrigger / channel programming */ +static ssize_t reset_xtrigs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int i;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* clear the CTI trigger / channel programming registers */
- for (i = 0; i < config->nr_trig_max; i++) {
config->ctiinen[i] = 0;
config->ctiouten[i] = 0;
- }
- /* clear the other regs */
- config->ctigate = (0x1 << config->nr_ctm_channels) - 1;
- config->asicctl = 0;
- config->ctiappset = 0;
- config->ctiinout_sel = 0;
confit->xtrig_rchan_sel = 0;
- /* if enabled then write through */
- if (CTI_PWR_ENA(config))
cti_write_all_hw_regs(drvdata);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(reset_xtrigs);
+static ssize_t show_chan_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- u32 val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- val = (u32)drvdata->config.xtrig_rchan_sel;
- return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+/* select a channel for the show_chan_xtrig function */ +static ssize_t show_chan_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, &val))
I think this should be base 10.
return -EINVAL;
- if (val > (drvdata->config.nr_ctm_channels-1))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- drvdata->config.xtrig_rchan_sel = val;
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(show_chan_sel);
+/* for the selected channel - show the signals attached. */ +static ssize_t show_chan_xtrigs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- int used = 0, reg_idx;
- int buf_sz = PAGE_SIZE;
- u32 chan_mask = 0x1 << cfg->xtrig_rchan_sel;
- used += scnprintf(buf, buf_sz, "IN: ");
- for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiinen[reg_idx]) {
used += scnprintf(buf+used, buf_sz-used, "%d ",
reg_idx);
}
- }
- used += scnprintf(buf+used, buf_sz-used, " OUT: ");
- for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiouten[reg_idx]) {
used += scnprintf(buf+used, buf_sz-used, "%d ",
reg_idx);
}
- }
- used += scnprintf(buf+used, buf_sz-used, "\n");
- return used;
+} +static DEVICE_ATTR_RO(show_chan_xtrigs);
+static ssize_t print_chan_list(struct device *dev,
char *buf, bool inuse)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- int used = 0, i;
- u32 inuse_bits = 0, chan_mask, chan_bit_mask;
- /* scan regs to get bitmap of channels in use. */
- spin_lock(&drvdata->spinlock);
- for (i = 0; i < config->nr_trig_max; i++) {
inuse_bits |= config->ctiinen[i];
inuse_bits |= config->ctiouten[i];
- }
- spin_unlock(&drvdata->spinlock);
- /* inverse bits if printing free channels */
- if (!inuse)
inuse_bits = ~inuse_bits;
- /* list of channels, or 'none' */
- chan_mask = ((u32)(0x1 << config->nr_ctm_channels)) - 1;
- if (inuse_bits & chan_mask) {
for (i = 0; i < config->nr_ctm_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used += scnprintf(buf+used, PAGE_SIZE-used,
"%d ", i);
}
}
used += scnprintf(buf+used, PAGE_SIZE-used, "\n");
- } else {
used = scnprintf(buf, PAGE_SIZE, "none\n");
- }
- return used;
+}
+static ssize_t list_inuse_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return print_chan_list(dev, buf, true);
+} +static DEVICE_ATTR_RO(list_inuse);
+static ssize_t list_free_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return print_chan_list(dev, buf, false);
+} +static DEVICE_ATTR_RO(list_free);
+static ssize_t list_gate_ena_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- int b_sz = PAGE_SIZE;
- u32 chan_mask;
- int chan, used = 0;
- if (cfg->ctigate == 0) {
used += scnprintf(buf, b_sz, "none\n");
- } else {
for (chan = 0; chan < cfg->nr_ctm_channels; chan++) {
chan_mask = 0x1 << chan;
if (chan_mask & cfg->ctigate) {
used += scnprintf(buf+used, b_sz-used, "%d ",
chan);
}
}
used += scnprintf(buf+used, b_sz-used, "\n");
- }
- return used;
+} +static DEVICE_ATTR_RO(list_gate_ena);
+static struct attribute *coresight_cti_channel_attrs[] = {
- &dev_attr_trigin_attach.attr,
- &dev_attr_trigin_detach.attr,
- &dev_attr_trigout_attach.attr,
- &dev_attr_trigout_detach.attr,
- &dev_attr_gate_enable.attr,
- &dev_attr_gate_disable.attr,
- &dev_attr_chan_set.attr,
- &dev_attr_chan_clear.attr,
- &dev_attr_chan_pulse.attr,
- &dev_attr_trig_filter_enable.attr,
- &dev_attr_reset_xtrigs.attr,
- &dev_attr_show_chan_sel.attr,
- &dev_attr_show_chan_xtrigs.attr,
- &dev_attr_list_inuse.attr,
- &dev_attr_list_free.attr,
- &dev_attr_list_gate_ena.attr,
- NULL,
+};
+/* attribute and group sysfs tables. */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, }; +static const struct attribute_group coresight_cti_mgmt_group = {
- .attrs = coresight_cti_mgmt_attrs,
- .name = "mgmt",
+};
+static const struct attribute_group coresight_cti_regs_group = {
- .attrs = coresight_cti_regs_attrs,
- .name = "regs",
+};
+static const struct attribute_group coresight_cti_channels_group = {
- .attrs = coresight_cti_channel_attrs,
- .name = "channels",
+};
const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_group,
- &coresight_cti_mgmt_group,
- &coresight_cti_regs_group,
- &coresight_cti_channels_group, NULL,
}; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 0a0fb6d48237..06b1f84795a4 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -69,7 +69,7 @@ static int cti_cpu_count; dev_get_drvdata(csdev->dev.parent) /* write set of regs to hardware - call with spinlock claimed */ -static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +void cti_write_all_hw_regs(struct cti_drvdata *drvdata) { struct cti_config *config = &drvdata->config; int i; @@ -173,6 +173,13 @@ static int cti_disable_hw(void *info) return 0; } +void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) +{
- CS_UNLOCK(drvdata->base);
- writel_relaxed(value, drvdata->base+offset);
- CS_LOCK(drvdata->base);
+}
static void cti_set_default_config(struct cti_config *config) { /* Most regs default to 0 as zalloc'ed except...*/ @@ -285,6 +292,178 @@ int cti_add_default_connection(struct cti_drvdata *drvdata) return ret; } +/** cti channel api **/ +/* attach/detach channel from trigger - write through if enabled. */ +int cti_channel_trig_op(struct device *dev,
enum cti_chan_op op,
enum cti_trig_dir direction,
u32 channel_idx,
u32 trigger_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 trig_bitmask;
- u32 chan_bitmask;
- u32 reg_value;
- int reg_offset;
- dev_info(dev, "chan_trig_op: op %d, dir %d, chan %d, trig %d\n",
op, direction, channel_idx, trigger_idx);
- /* ensure indexes in range */
- if ((channel_idx >= config->nr_ctm_channels) ||
(trigger_idx >= config->nr_trig_max))
return -EINVAL;
- trig_bitmask = 0x1 << trigger_idx;
- /* ensure registered triggers and not out filtered */
- if (direction == CTI_TRIG_IN) {
if (!(trig_bitmask & config->trig_in_use))
return -EINVAL;
- } else {
if (!(trig_bitmask & config->trig_out_use))
return -EINVAL;
if ((config->trig_filter_enable) &&
Do we need ->trig_filter_enable?
(config->trig_out_filter & trig_bitmask))
Wouldn't it be sufficient to check ->trig_out_filter like you're doing here?
return -EINVAL;
- }
- /* update the local register values */
- chan_bitmask = 0x1 << channel_idx;
- reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :
CTIOUTEN(trigger_idx));
- spin_lock(&drvdata->spinlock);
- /* read - modify write - the trigger / channel enable value */
- reg_value = (direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] :
config->ctiouten[trigger_idx]);
- reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask :
reg_value & (~chan_bitmask);
- /* write local copy */
- if (direction == CTI_TRIG_IN)
config->ctiinen[trigger_idx] = reg_value;
- else
config->ctiouten[trigger_idx] = reg_value;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
- spin_unlock(&drvdata->spinlock);
- return 0;
+}
+int cti_channel_gate_op(struct device *dev,
enum cti_chan_gate_op op,
u32 channel_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 chan_bitmask;
- u32 reg_value;
- int err = 0;
- if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
- chan_bitmask = 0x1 << channel_idx;
- spin_lock(&drvdata->spinlock);
- reg_value = config->ctigate;
- switch (op) {
- case CTI_GATE_CHAN_ENABLE:
reg_value |= chan_bitmask;
break;
- case CTI_GATE_CHAN_DISABLE:
reg_value &= ~chan_bitmask;
break;
- case CTI_GATE_CHAN_ENABLE_ALL:
reg_value = (0x1 << config->nr_ctm_channels) - 1;
break;
- case CTI_GATE_CHAN_DISABLE_ALL:
reg_value = 0x0;
break;
- default:
err = -EINVAL;
break;
- }
- if (err == 0) {
config->ctigate = reg_value;
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIGATE, reg_value);
- }
- spin_unlock(&drvdata->spinlock);
- return err;
+}
+int cti_channel_setop(struct device *dev,
enum cti_chan_set_op op,
u32 channel_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 chan_bitmask;
- u32 reg_value;
- u32 reg_offset;
- int err = 0;
- if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
- chan_bitmask = 0x1 << channel_idx;
- spin_lock(&drvdata->spinlock);
- reg_value = config->ctiappset;
- switch (op) {
- case CTI_CHAN_SET:
config->ctiappset |= chan_bitmask;
reg_value = config->ctiappset;
reg_offset = CTIAPPSET;
break;
- case CTI_CHAN_CLR:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPCLEAR;
break;
- case CTI_CHAN_PULSE:
config->ctiappset &= ~chan_bitmask;
Should this be "|= chan_bitmask" ?
reg_value = chan_bitmask;
reg_offset = CTIAPPPULSE;
break;
- default:
err = -EINVAL;
break;
- }
- if ((err == 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
- spin_unlock(&drvdata->spinlock);
- return err;
+}
+void cti_write_intack(struct device *dev, u32 ackval) +{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* write if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINTACK, ackval);
- spin_unlock(&drvdata->spinlock);
+}
/** cti ect operations **/ int cti_enable(struct coresight_device *csdev, void *__unused) { @@ -432,7 +611,12 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) /* all done - dec pm refcount */ pm_runtime_put(&adev->dev);
- dev_info(dev, "%s: initialized\n", pdata->name);
- if (drvdata->ctidev.cpu >= 0) {
dev_info(dev, "%s: cti-CPU%d initialized\n", pdata->name,
drvdata->ctidev.cpu);
- } else {
dev_info(dev, "%s: cti-system initialized\n", pdata->name);
- } cti_count++; return 0;
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index c1af6cf9c2fe..a5bf59155f62 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -153,6 +153,7 @@ struct cti_device {
- @trig_out_filter: bitfield of out triggers that are blocked if filter
- enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- @xtrig_rchan_sel: channel ID when reading set xtrig info
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
@@ -174,6 +175,7 @@ struct cti_config { u32 trig_out_use; u32 trig_out_filter; int trig_filter_enable;
- u8 xtrig_rchan_sel; /* cti cross trig programmable regs */ u32 ctiappset; u8 ctiinout_sel;
@@ -202,11 +204,51 @@ struct cti_drvdata { struct cti_config config; }; +/*
- Channel operation types.
- */
+enum cti_chan_op {
- CTI_CHAN_ATTACH,
- CTI_CHAN_DETACH,
+};
+enum cti_trig_dir {
- CTI_TRIG_IN,
- CTI_TRIG_OUT,
+};
+enum cti_chan_gate_op {
- CTI_GATE_CHAN_ENABLE,
- CTI_GATE_CHAN_DISABLE,
- CTI_GATE_CHAN_ENABLE_ALL,
- CTI_GATE_CHAN_DISABLE_ALL,
+};
+enum cti_chan_set_op {
- CTI_CHAN_SET,
- CTI_CHAN_CLR,
- CTI_CHAN_PULSE,
+};
/* private cti driver fns & vars */ extern const struct attribute_group *coresight_cti_groups[];
int cti_add_default_connection(struct cti_drvdata *drvdata); int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); +void cti_write_all_hw_regs(struct cti_drvdata *drvdata); +void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value); +int cti_channel_trig_op(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir direction, u32 channel_idx,
u32 trigger_idx);
+int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op,
u32 channel_idx);
+int cti_channel_setop(struct device *dev, enum cti_chan_set_op op,
u32 channel_idx);
+void cti_write_intack(struct device *dev, u32 ackval);
+/* cti powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered) #ifdef CONFIG_OF extern int of_cti_get_hw_data(struct device *dev, diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 8e9e5b6ad866..9b497e2f8725 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -23,10 +23,12 @@ #define CORESIGHT_LAR 0xfb0 #define CORESIGHT_LSR 0xfb4 #define CORESIGHT_AUTHSTATUS 0xfb8 +#define CORESIGHT_DEVARCH 0xfbc #define CORESIGHT_DEVID 0xfc8 #define CORESIGHT_DEVTYPE 0xfcc
/*
- Coresight device CLAIM protocol.
- See PSCI - ARM DEN 0022D, Section: 6.8.1 Debug and Trace save and restore.
-- 2.20.1
Hi Mathieu,
On Thu, 9 May 2019 at 20:36, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, May 01, 2019 at 09:49:34AM +0100, Mike Leach wrote:
Adds in required sysfs access for:-
- CoreSight management registers.
- CTI device specific registers.
- Channel Cross triggering API.
This provides the basic programming interface for the device.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 831 +++++++++++++++++- drivers/hwtracing/coresight/coresight-cti.c | 188 +++- drivers/hwtracing/coresight/coresight-cti.h | 42 + drivers/hwtracing/coresight/coresight-priv.h | 2 + 4 files changed, 1053 insertions(+), 10 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 9b6749621dcb..ff8dad02a779 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -34,8 +34,8 @@ static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) {
int enable_req;
bool enabled, powered, cpuid;
int enable_req, cpuid;
bool enabled, powered; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); ssize_t size = 0;
@@ -46,12 +46,18 @@ static ssize_t enable_show(struct device *dev, cpuid = drvdata->ctidev.cpu; spin_unlock(&drvdata->spinlock);
if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
} else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
if (cpuid >= 0) {
if (powered) {
size = scnprintf(buf, PAGE_SIZE,
"cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled",
cpuid);
} else {
size = scnprintf(buf, PAGE_SIZE,
"cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req",
cpuid);
} } else { size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n", enabled ? "enabled" : "disabled");
@@ -81,17 +87,826 @@ static ssize_t enable_store(struct device *dev, } static DEVICE_ATTR_RW(enable);
+static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id);
+} +static DEVICE_ATTR_RO(ctmid);
/* attribute and group sysfs tables. */ static struct attribute *coresight_cti_attrs[] = { &dev_attr_enable.attr,
&dev_attr_ctmid.attr, NULL,
};
+/* register based attributes */ +#define coresight_cti_reg(name, offset) \
coresight_simple_reg32(struct cti_drvdata, name, offset)
+/*
- Show a simple 32 bit value. If pval is NULL then live read,
- otherwise read from supplied pointer only.
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)
+{
unsigned long val = 0;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
if (pval) {
val = (unsigned long)*pval;
} else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + reg_offset);
CS_LOCK(drvdata->base);
}
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+/*
- Store a simple 32 bit value.
- If pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,
int reg_offset)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* local store */
if (pval)
*pval = (u32)val;
/* write through of offset and enabled */
if ((reg_offset >= 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, val);
spin_unlock(&drvdata->spinlock);
return size;
+}
+/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4);
+static struct attribute *coresight_cti_mgmt_attrs[] = {
&dev_attr_devaff0.attr,
&dev_attr_devaff1.attr,
&dev_attr_authstatus.attr,
&dev_attr_devarch.attr,
&dev_attr_devid.attr,
&dev_attr_devtype.attr,
&dev_attr_pidr0.attr,
&dev_attr_pidr1.attr,
&dev_attr_pidr2.attr,
&dev_attr_pidr3.attr,
&dev_attr_pidr4.attr,
NULL,
+};
+/* CTI low level programming registers */ +static ssize_t inout_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
u32 val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
val = (u32)drvdata->config.ctiinout_sel;
return scnprintf(buf, PAGE_SIZE, "%#x\n", val);
+}
+static ssize_t inout_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (kstrtoul(buf, 16, &val))
return -EINVAL;
if (val > (CTIINOUTEN_MAX-1))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->config.ctiinout_sel = val;
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(inout_sel);
+static ssize_t inen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
index = drvdata->config.ctiinout_sel;
val = drvdata->config.ctiinen[index];
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "INEN%d %#lx\n", index, val);
+}
+static ssize_t inen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
index = config->ctiinout_sel;
config->ctiinen[index] = val;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINEN(index), val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(inen);
+static ssize_t outen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
index = drvdata->config.ctiinout_sel;
val = drvdata->config.ctiouten[index];
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "OUTEN%d %#lx\n", index, val);
+}
+static ssize_t outen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
index = config->ctiinout_sel;
config->ctiouten[index] = val;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIOUTEN(index), val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(outen);
+static ssize_t gate_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.ctigate, -1);
+}
+static ssize_t gate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.ctigate, CTIGATE);
+} +static DEVICE_ATTR_RW(gate);
+static ssize_t asicctl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.asicctl, -1);
+}
+static ssize_t asicctl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.asicctl, ASICCTL);
+} +static DEVICE_ATTR_RW(asicctl);
+static ssize_t intack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
cti_write_intack(dev, val);
return size;
+} +static DEVICE_ATTR_WO(intack);
+static ssize_t appset_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.ctiappset, -1);
+}
+static ssize_t appset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.ctiappset, CTIAPPSET);
+} +static DEVICE_ATTR_RW(appset);
+static ssize_t appclear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val, mask;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* a 1'b1 in appclr clears down the same bit in appset*/
mask = ~val;
config->ctiappset &= mask;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(appclear);
+static ssize_t apppulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val, mask;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/*
* a 1'b1 in apppulse sets then clears the bit,
* effectively clears down the same bit in appset
*/
mask = ~val;
config->ctiappset &= mask;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPPULSE, val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(apppulse);
+static ssize_t itchout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, ITCHOUT);
+}
+static ssize_t itchout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITCHOUT);
+} +static DEVICE_ATTR_RW(itchout);
+static ssize_t ittrigout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, ITTRIGOUT);
+}
+static ssize_t ittrigout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITTRIGOUT);
+} +static DEVICE_ATTR_RW(ittrigout);
+static ssize_t itchinack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITCHINACK);
+} +static DEVICE_ATTR_WO(itchinack);
+static ssize_t ittriginack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITTRIGINACK);
+} +static DEVICE_ATTR_WO(ittriginack);
+static ssize_t itctrl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL);
+}
+static ssize_t itctrl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL);
+} +static DEVICE_ATTR_RW(itctrl);
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+static struct attribute *coresight_cti_regs_attrs[] = {
&dev_attr_inout_sel.attr,
&dev_attr_inen.attr,
&dev_attr_outen.attr,
&dev_attr_gate.attr,
&dev_attr_asicctl.attr,
&dev_attr_intack.attr,
&dev_attr_appset.attr,
&dev_attr_appclear.attr,
&dev_attr_apppulse.attr,
&dev_attr_triginstatus.attr,
&dev_attr_trigoutstatus.attr,
&dev_attr_chinstatus.attr,
&dev_attr_choutstatus.attr,
&dev_attr_itctrl.attr,
&dev_attr_ittrigin.attr,
&dev_attr_itchin.attr,
&dev_attr_ittrigout.attr,
&dev_attr_itchout.attr,
&dev_attr_itchoutack.attr,
&dev_attr_ittrigoutack.attr,
&dev_attr_ittriginack.attr,
&dev_attr_itchinack.attr,
NULL,
+};
+/* CTI channel x-trigger programming */ +static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir dir, const char *buf, size_t size)
+{
u32 chan_idx;
u32 trig_idx;
int items, err = size;
/* extract chan idx and trigger idx */
items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);
if (items) {
err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);
if (!err)
err = size;
} else
err = -EINVAL;
return err;
+}
+static ssize_t trigin_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_attach);
+static ssize_t trigin_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_detach);
+static ssize_t trigout_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_attach);
+static ssize_t trigout_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_detach);
+static ssize_t gate_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = 0, channel = 0;
if ((size >= 3) && (strncmp(buf, "all", 3) == 0)) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE_ALL, 0);
} else {
if (kstrtoint(buf, 0, &channel))
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, channel);
}
return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_enable);
+static ssize_t gate_disable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = 0, channel = 0;
if ((size >= 3) && (strncmp(buf, "all", 3) == 0)) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE_ALL, 0);
} else {
if (kstrtoint(buf, 0, &channel))
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, channel);
}
return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_disable);
+static int +chan_op_parse(struct device *dev, enum cti_chan_set_op op, const char *buf) +{
int err = 0, channel = 0;
if (kstrtoint(buf, 0, &channel))
return -EINVAL;
err = cti_channel_setop(dev, op, channel);
return err;
+}
+static ssize_t chan_set_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_SET, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_set);
+static ssize_t chan_clear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_CLR, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_clear);
+static ssize_t chan_pulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_PULSE, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_pulse);
+static ssize_t trig_filter_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
val = drvdata->config.trig_filter_enable;
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%ld (%s)\n", val,
val ? "enabled" : "disabled");
+}
+static ssize_t trig_filter_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->config.trig_filter_enable = val ? 1 : 0;
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(trig_filter_enable);
+/* clear all xtrigger / channel programming */ +static ssize_t reset_xtrigs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int i;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* clear the CTI trigger / channel programming registers */
for (i = 0; i < config->nr_trig_max; i++) {
config->ctiinen[i] = 0;
config->ctiouten[i] = 0;
}
/* clear the other regs */
config->ctigate = (0x1 << config->nr_ctm_channels) - 1;
config->asicctl = 0;
config->ctiappset = 0;
config->ctiinout_sel = 0;
confit->xtrig_rchan_sel = 0;
Agreed
/* if enabled then write through */
if (CTI_PWR_ENA(config))
cti_write_all_hw_regs(drvdata);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(reset_xtrigs);
+static ssize_t show_chan_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
u32 val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
val = (u32)drvdata->config.xtrig_rchan_sel;
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+/* select a channel for the show_chan_xtrig function */ +static ssize_t show_chan_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (kstrtoul(buf, 16, &val))
I think this should be base 10.
yes.
return -EINVAL;
if (val > (drvdata->config.nr_ctm_channels-1))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->config.xtrig_rchan_sel = val;
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(show_chan_sel);
+/* for the selected channel - show the signals attached. */ +static ssize_t show_chan_xtrigs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config;
int used = 0, reg_idx;
int buf_sz = PAGE_SIZE;
u32 chan_mask = 0x1 << cfg->xtrig_rchan_sel;
used += scnprintf(buf, buf_sz, "IN: ");
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiinen[reg_idx]) {
used += scnprintf(buf+used, buf_sz-used, "%d ",
reg_idx);
}
}
used += scnprintf(buf+used, buf_sz-used, " OUT: ");
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiouten[reg_idx]) {
used += scnprintf(buf+used, buf_sz-used, "%d ",
reg_idx);
}
}
used += scnprintf(buf+used, buf_sz-used, "\n");
return used;
+} +static DEVICE_ATTR_RO(show_chan_xtrigs);
+static ssize_t print_chan_list(struct device *dev,
char *buf, bool inuse)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
int used = 0, i;
u32 inuse_bits = 0, chan_mask, chan_bit_mask;
/* scan regs to get bitmap of channels in use. */
spin_lock(&drvdata->spinlock);
for (i = 0; i < config->nr_trig_max; i++) {
inuse_bits |= config->ctiinen[i];
inuse_bits |= config->ctiouten[i];
}
spin_unlock(&drvdata->spinlock);
/* inverse bits if printing free channels */
if (!inuse)
inuse_bits = ~inuse_bits;
/* list of channels, or 'none' */
chan_mask = ((u32)(0x1 << config->nr_ctm_channels)) - 1;
if (inuse_bits & chan_mask) {
for (i = 0; i < config->nr_ctm_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used += scnprintf(buf+used, PAGE_SIZE-used,
"%d ", i);
}
}
used += scnprintf(buf+used, PAGE_SIZE-used, "\n");
} else {
used = scnprintf(buf, PAGE_SIZE, "none\n");
}
return used;
+}
+static ssize_t list_inuse_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return print_chan_list(dev, buf, true);
+} +static DEVICE_ATTR_RO(list_inuse);
+static ssize_t list_free_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return print_chan_list(dev, buf, false);
+} +static DEVICE_ATTR_RO(list_free);
+static ssize_t list_gate_ena_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config;
int b_sz = PAGE_SIZE;
u32 chan_mask;
int chan, used = 0;
if (cfg->ctigate == 0) {
used += scnprintf(buf, b_sz, "none\n");
} else {
for (chan = 0; chan < cfg->nr_ctm_channels; chan++) {
chan_mask = 0x1 << chan;
if (chan_mask & cfg->ctigate) {
used += scnprintf(buf+used, b_sz-used, "%d ",
chan);
}
}
used += scnprintf(buf+used, b_sz-used, "\n");
}
return used;
+} +static DEVICE_ATTR_RO(list_gate_ena);
+static struct attribute *coresight_cti_channel_attrs[] = {
&dev_attr_trigin_attach.attr,
&dev_attr_trigin_detach.attr,
&dev_attr_trigout_attach.attr,
&dev_attr_trigout_detach.attr,
&dev_attr_gate_enable.attr,
&dev_attr_gate_disable.attr,
&dev_attr_chan_set.attr,
&dev_attr_chan_clear.attr,
&dev_attr_chan_pulse.attr,
&dev_attr_trig_filter_enable.attr,
&dev_attr_reset_xtrigs.attr,
&dev_attr_show_chan_sel.attr,
&dev_attr_show_chan_xtrigs.attr,
&dev_attr_list_inuse.attr,
&dev_attr_list_free.attr,
&dev_attr_list_gate_ena.attr,
NULL,
+};
+/* attribute and group sysfs tables. */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, };
+static const struct attribute_group coresight_cti_mgmt_group = {
.attrs = coresight_cti_mgmt_attrs,
.name = "mgmt",
+};
+static const struct attribute_group coresight_cti_regs_group = {
.attrs = coresight_cti_regs_attrs,
.name = "regs",
+};
+static const struct attribute_group coresight_cti_channels_group = {
.attrs = coresight_cti_channel_attrs,
.name = "channels",
+};
const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_group,
&coresight_cti_mgmt_group,
&coresight_cti_regs_group,
&coresight_cti_channels_group, NULL,
}; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 0a0fb6d48237..06b1f84795a4 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -69,7 +69,7 @@ static int cti_cpu_count; dev_get_drvdata(csdev->dev.parent)
/* write set of regs to hardware - call with spinlock claimed */ -static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +void cti_write_all_hw_regs(struct cti_drvdata *drvdata) { struct cti_config *config = &drvdata->config; int i; @@ -173,6 +173,13 @@ static int cti_disable_hw(void *info) return 0; }
+void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) +{
CS_UNLOCK(drvdata->base);
writel_relaxed(value, drvdata->base+offset);
CS_LOCK(drvdata->base);
+}
static void cti_set_default_config(struct cti_config *config) { /* Most regs default to 0 as zalloc'ed except...*/ @@ -285,6 +292,178 @@ int cti_add_default_connection(struct cti_drvdata *drvdata) return ret; }
+/** cti channel api **/ +/* attach/detach channel from trigger - write through if enabled. */ +int cti_channel_trig_op(struct device *dev,
enum cti_chan_op op,
enum cti_trig_dir direction,
u32 channel_idx,
u32 trigger_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 trig_bitmask;
u32 chan_bitmask;
u32 reg_value;
int reg_offset;
dev_info(dev, "chan_trig_op: op %d, dir %d, chan %d, trig %d\n",
op, direction, channel_idx, trigger_idx);
/* ensure indexes in range */
if ((channel_idx >= config->nr_ctm_channels) ||
(trigger_idx >= config->nr_trig_max))
return -EINVAL;
trig_bitmask = 0x1 << trigger_idx;
/* ensure registered triggers and not out filtered */
if (direction == CTI_TRIG_IN) {
if (!(trig_bitmask & config->trig_in_use))
return -EINVAL;
} else {
if (!(trig_bitmask & config->trig_out_use))
return -EINVAL;
if ((config->trig_filter_enable) &&
Do we need ->trig_filter_enable?
(config->trig_out_filter & trig_bitmask))
Wouldn't it be sufficient to check ->trig_out_filter like you're doing here?
config->trig_out_filter is set from parameters in the .dts file - this defines signals that would be rather bad to set - e.g. EDBGREQ, so is read-only for the user. ->trig_filter_enable controls if this filtering takes place - it is on by default, but if someone really wants to halt a core in debug mode, they can switch it off and we then assume they know what they are doing.
return -EINVAL;
}
/* update the local register values */
chan_bitmask = 0x1 << channel_idx;
reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :
CTIOUTEN(trigger_idx));
spin_lock(&drvdata->spinlock);
/* read - modify write - the trigger / channel enable value */
reg_value = (direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] :
config->ctiouten[trigger_idx]);
reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask :
reg_value & (~chan_bitmask);
/* write local copy */
if (direction == CTI_TRIG_IN)
config->ctiinen[trigger_idx] = reg_value;
else
config->ctiouten[trigger_idx] = reg_value;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
spin_unlock(&drvdata->spinlock);
return 0;
+}
+int cti_channel_gate_op(struct device *dev,
enum cti_chan_gate_op op,
u32 channel_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 chan_bitmask;
u32 reg_value;
int err = 0;
if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
chan_bitmask = 0x1 << channel_idx;
spin_lock(&drvdata->spinlock);
reg_value = config->ctigate;
switch (op) {
case CTI_GATE_CHAN_ENABLE:
reg_value |= chan_bitmask;
break;
case CTI_GATE_CHAN_DISABLE:
reg_value &= ~chan_bitmask;
break;
case CTI_GATE_CHAN_ENABLE_ALL:
reg_value = (0x1 << config->nr_ctm_channels) - 1;
break;
case CTI_GATE_CHAN_DISABLE_ALL:
reg_value = 0x0;
break;
default:
err = -EINVAL;
break;
}
if (err == 0) {
config->ctigate = reg_value;
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIGATE, reg_value);
}
spin_unlock(&drvdata->spinlock);
return err;
+}
+int cti_channel_setop(struct device *dev,
enum cti_chan_set_op op,
u32 channel_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 chan_bitmask;
u32 reg_value;
u32 reg_offset;
int err = 0;
if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
chan_bitmask = 0x1 << channel_idx;
spin_lock(&drvdata->spinlock);
reg_value = config->ctiappset;
switch (op) {
case CTI_CHAN_SET:
config->ctiappset |= chan_bitmask;
reg_value = config->ctiappset;
reg_offset = CTIAPPSET;
break;
case CTI_CHAN_CLR:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPCLEAR;
break;
case CTI_CHAN_PULSE:
config->ctiappset &= ~chan_bitmask;
Should this be "|= chan_bitmask" ?
No - the effect of pulse is to raise the channel for one clock cycle then lower it. So if we pulse a channel, the net effect is the bit is cleared.
reg_value = chan_bitmask;
reg_offset = CTIAPPPULSE;
break;
default:
err = -EINVAL;
break;
}
if ((err == 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
spin_unlock(&drvdata->spinlock);
return err;
+}
+void cti_write_intack(struct device *dev, u32 ackval) +{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* write if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINTACK, ackval);
spin_unlock(&drvdata->spinlock);
+}
/** cti ect operations **/ int cti_enable(struct coresight_device *csdev, void *__unused) { @@ -432,7 +611,12 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id)
/* all done - dec pm refcount */ pm_runtime_put(&adev->dev);
dev_info(dev, "%s: initialized\n", pdata->name);
if (drvdata->ctidev.cpu >= 0) {
dev_info(dev, "%s: cti-CPU%d initialized\n", pdata->name,
drvdata->ctidev.cpu);
} else {
dev_info(dev, "%s: cti-system initialized\n", pdata->name);
} cti_count++; return 0;
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index c1af6cf9c2fe..a5bf59155f62 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -153,6 +153,7 @@ struct cti_device {
- @trig_out_filter: bitfield of out triggers that are blocked if filter
- enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- @xtrig_rchan_sel: channel ID when reading set xtrig info
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
@@ -174,6 +175,7 @@ struct cti_config { u32 trig_out_use; u32 trig_out_filter; int trig_filter_enable;
u8 xtrig_rchan_sel; /* cti cross trig programmable regs */ u32 ctiappset; u8 ctiinout_sel;
@@ -202,11 +204,51 @@ struct cti_drvdata { struct cti_config config; };
+/*
- Channel operation types.
- */
+enum cti_chan_op {
CTI_CHAN_ATTACH,
CTI_CHAN_DETACH,
+};
+enum cti_trig_dir {
CTI_TRIG_IN,
CTI_TRIG_OUT,
+};
+enum cti_chan_gate_op {
CTI_GATE_CHAN_ENABLE,
CTI_GATE_CHAN_DISABLE,
CTI_GATE_CHAN_ENABLE_ALL,
CTI_GATE_CHAN_DISABLE_ALL,
+};
+enum cti_chan_set_op {
CTI_CHAN_SET,
CTI_CHAN_CLR,
CTI_CHAN_PULSE,
+};
/* private cti driver fns & vars */ extern const struct attribute_group *coresight_cti_groups[];
int cti_add_default_connection(struct cti_drvdata *drvdata); int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); +void cti_write_all_hw_regs(struct cti_drvdata *drvdata); +void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value); +int cti_channel_trig_op(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir direction, u32 channel_idx,
u32 trigger_idx);
+int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op,
u32 channel_idx);
+int cti_channel_setop(struct device *dev, enum cti_chan_set_op op,
u32 channel_idx);
+void cti_write_intack(struct device *dev, u32 ackval);
+/* cti powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
#ifdef CONFIG_OF extern int of_cti_get_hw_data(struct device *dev, diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 8e9e5b6ad866..9b497e2f8725 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -23,10 +23,12 @@ #define CORESIGHT_LAR 0xfb0 #define CORESIGHT_LSR 0xfb4 #define CORESIGHT_AUTHSTATUS 0xfb8 +#define CORESIGHT_DEVARCH 0xfbc #define CORESIGHT_DEVID 0xfc8 #define CORESIGHT_DEVTYPE 0xfcc
/*
- Coresight device CLAIM protocol.
- See PSCI - ARM DEN 0022D, Section: 6.8.1 Debug and Trace save and restore.
-- 2.20.1
Thanks
Mike
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
On Wed, May 01, 2019 at 09:49:34AM +0100, Mike Leach wrote:
[...]
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 9b6749621dcb..ff8dad02a779 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c
[...]
+/* register based attributes */ +#define coresight_cti_reg(name, offset) \
- coresight_simple_reg32(struct cti_drvdata, name, offset)
+/*
- Show a simple 32 bit value. If pval is NULL then live read,
- otherwise read from supplied pointer only.
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)
+{
- unsigned long val = 0;
Since this function is only used for 32 bit register, so could consider to use u32.
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- if (pval) {
val = (unsigned long)*pval;
- } else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + reg_offset);
CS_LOCK(drvdata->base);
- }
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+/*
- Store a simple 32 bit value.
- If pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,
int reg_offset)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* local store */
- if (pval)
*pval = (u32)val;
- /* write through of offset and enabled */
- if ((reg_offset >= 0) && CTI_PWR_ENA(config))
If CTI_PWR_ENA() returns false, should return back the failure to use space? This can avoid the confusion that the user will understand the register has been set but actually the kernel doesn't really set the register when the CTI is not powered on.
For this reason, it's good to check CTI is powered/enabled at the beginning for every xxx_store() functions.
cti_write_single_reg(drvdata, reg_offset, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+}
[...]
+static ssize_t appclear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val, mask;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* a 1'b1 in appclr clears down the same bit in appset*/
- mask = ~val;
- config->ctiappset &= mask;
nitpick: 'mask' variable is not necessary, we can use 'val':
config->ctiappset &= ~val;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(appclear);
+static ssize_t apppulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val, mask;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /*
* a 1'b1 in apppulse sets then clears the bit,
* effectively clears down the same bit in appset
*/
- mask = ~val;
- config->ctiappset &= mask;
Ditto.
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPPULSE, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(apppulse);
[...]
+/* for the selected channel - show the signals attached. */ +static ssize_t show_chan_xtrigs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- int used = 0, reg_idx;
- int buf_sz = PAGE_SIZE;
- u32 chan_mask = 0x1 << cfg->xtrig_rchan_sel;
- used += scnprintf(buf, buf_sz, "IN: ");
- for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiinen[reg_idx]) {
used += scnprintf(buf+used, buf_sz-used, "%d ",
reg_idx);
}
- }
- used += scnprintf(buf+used, buf_sz-used, " OUT: ");
Nitpick: remove the first space in " OUT: ".
- for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiouten[reg_idx]) {
used += scnprintf(buf+used, buf_sz-used, "%d ",
reg_idx);
}
- }
- used += scnprintf(buf+used, buf_sz-used, "\n");
- return used;
+} +static DEVICE_ATTR_RO(show_chan_xtrigs);
[...]
Thanks, Leo Yan
Hi Leo,
On Sat, 25 May 2019 at 09:31, Leo Yan leo.yan@linaro.org wrote:
On Wed, May 01, 2019 at 09:49:34AM +0100, Mike Leach wrote:
[...]
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 9b6749621dcb..ff8dad02a779 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c
[...]
+/* register based attributes */ +#define coresight_cti_reg(name, offset) \
coresight_simple_reg32(struct cti_drvdata, name, offset)
+/*
- Show a simple 32 bit value. If pval is NULL then live read,
- otherwise read from supplied pointer only.
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)
+{
unsigned long val = 0;
Since this function is only used for 32 bit register, so could consider to use u32.
OK.
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
if (pval) {
val = (unsigned long)*pval;
} else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + reg_offset);
CS_LOCK(drvdata->base);
}
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+/*
- Store a simple 32 bit value.
- If pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,
int reg_offset)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* local store */
if (pval)
*pval = (u32)val;
/* write through of offset and enabled */
if ((reg_offset >= 0) && CTI_PWR_ENA(config))
If CTI_PWR_ENA() returns false, should return back the failure to use space? This can avoid the confusion that the user will understand the register has been set but actually the kernel doesn't really set the register when the CTI is not powered on.
No - the purpose of the function is to store the value in the driver. If the user is programming when the device is disabled then the value will be successfully stored, and can be read back. The user may have disabled the device prior to programming a series of registers so we cannot error here. Similarly, unlike other CS devices, CTI can be programmed when enabled, so that is the purpose of the check - to see if it is permissible to write the value directly.
For this reason, it's good to check CTI is powered/enabled at the beginning for every xxx_store() functions.
cti_write_single_reg(drvdata, reg_offset, val);
spin_unlock(&drvdata->spinlock);
return size;
+}
[...]
+static ssize_t appclear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val, mask;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* a 1'b1 in appclr clears down the same bit in appset*/
mask = ~val;
config->ctiappset &= mask;
nitpick: 'mask' variable is not necessary, we can use 'val':
config->ctiappset &= ~val;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(appclear);
+static ssize_t apppulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val, mask;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/*
* a 1'b1 in apppulse sets then clears the bit,
* effectively clears down the same bit in appset
*/
mask = ~val;
config->ctiappset &= mask;
Ditto.
OK.
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPPULSE, val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(apppulse);
[...]
+/* for the selected channel - show the signals attached. */ +static ssize_t show_chan_xtrigs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config;
int used = 0, reg_idx;
int buf_sz = PAGE_SIZE;
u32 chan_mask = 0x1 << cfg->xtrig_rchan_sel;
used += scnprintf(buf, buf_sz, "IN: ");
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiinen[reg_idx]) {
used += scnprintf(buf+used, buf_sz-used, "%d ",
reg_idx);
}
}
used += scnprintf(buf+used, buf_sz-used, " OUT: ");
Nitpick: remove the first space in " OUT: ".
OK
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiouten[reg_idx]) {
used += scnprintf(buf+used, buf_sz-used, "%d ",
reg_idx);
}
}
used += scnprintf(buf+used, buf_sz-used, "\n");
return used;
+} +static DEVICE_ATTR_RO(show_chan_xtrigs);
[...]
Thanks, Leo Yan
Thanks
Mike
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
On 01/05/2019 09:49, Mike Leach wrote:
Adds in required sysfs access for:-
- CoreSight management registers.
- CTI device specific registers.
- Channel Cross triggering API.
This provides the basic programming interface for the device.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 831 +++++++++++++++++-
Please could you split this patch into smaller chunks ? This is slightly bigger patch for a healthy review. I have noted down some minor issues below. Otherwise, I look forward to the next version.
drivers/hwtracing/coresight/coresight-cti.c | 188 +++- drivers/hwtracing/coresight/coresight-cti.h | 42 + drivers/hwtracing/coresight/coresight-priv.h | 2 + 4 files changed, 1053 insertions(+), 10 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 9b6749621dcb..ff8dad02a779 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -34,8 +34,8 @@ static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) {
- int enable_req;
- bool enabled, powered, cpuid;
- int enable_req, cpuid;
- bool enabled, powered; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); ssize_t size = 0;
@@ -46,12 +46,18 @@ static ssize_t enable_show(struct device *dev, cpuid = drvdata->ctidev.cpu; spin_unlock(&drvdata->spinlock);
- if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
- } else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
- if (cpuid >= 0) {
if (powered) {
size = scnprintf(buf, PAGE_SIZE,
"cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled",
cpuid);
} else {
size = scnprintf(buf, PAGE_SIZE,
"cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req",
cpuid);
} else {}
Could we have started off with the above change in the first patch ?
size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n", enabled ? "enabled" : "disabled");
@@ -81,17 +87,826 @@ static ssize_t enable_store(struct device *dev, } static DEVICE_ATTR_RW(enable); +static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id);
+} +static DEVICE_ATTR_RO(ctmid);
- /* attribute and group sysfs tables. */ static struct attribute *coresight_cti_attrs[] = { &dev_attr_enable.attr,
- &dev_attr_ctmid.attr, NULL, };
+/* register based attributes */ +#define coresight_cti_reg(name, offset) \
- coresight_simple_reg32(struct cti_drvdata, name, offset)
+/*
- Show a simple 32 bit value. If pval is NULL then live read,
- otherwise read from supplied pointer only.
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)
+{
- unsigned long val = 0;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- if (pval) {
val = (unsigned long)*pval;
- } else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + reg_offset);
CS_LOCK(drvdata->base);
- }
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+/*
- Store a simple 32 bit value.
- If pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,
int reg_offset)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
Please use 0, instead of hardcoding 16.
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* local store */
- if (pval)
*pval = (u32)val;
- /* write through of offset and enabled */
- if ((reg_offset >= 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+}
+/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4);
Are the PIDs really needed ? If at all they are needed, could we expose the unified PID from these 4 registers ?
+static ssize_t itchout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, ITCHOUT);
+}
Whats the purpose of the exposing integration test registers ?
+static ssize_t itctrl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL);
+}
+static ssize_t itctrl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL);
+} +static DEVICE_ATTR_RW(itctrl);
You may be able to use a macro to make the above more compact.
#define cti_reg32_attr(name, offset) \ static ssize_t name##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ return cti_reg32_show(dev, buf, NULL, offset); \ } \ \ static ssize_t name##_store(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t size) \ { \ return cti_reg32_store(dev, buf, size, NULL, \ offset); \ \ } \ static DEVICE_ATTR_RW(name);
cti_reg32(itctrl, CORESIGHT_ITCTRL); cti_reg32(...., );
Or even use the extended attribute to store the "Offset" and then use a common routine to store/show the reg at the offset.
+/* CTI channel x-trigger programming */ +static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir dir, const char *buf, size_t size)
+{
- u32 chan_idx;
- u32 trig_idx;
- int items, err = size;
- /* extract chan idx and trigger idx */
- items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);
- if (items) {
Should we not match items == 2 to make sure we read a trig_idx as well ?
err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);
if (!err)
err = size;
- } else
err = -EINVAL;
minor nit: Please use :
else { err = -EINVAL; }
to match the coding style.
+static ssize_t trigout_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_attach);
+static ssize_t trigout_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_detach);
Again, as above you may be able to use a macro toe make it more easier.
+static ssize_t reset_xtrigs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int i;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* clear the CTI trigger / channel programming registers */
- for (i = 0; i < config->nr_trig_max; i++) {
config->ctiinen[i] = 0;
config->ctiouten[i] = 0;
- }
- /* clear the other regs */
- config->ctigate = (0x1 << config->nr_ctm_channels) - 1;
Please GENMASK();
- config->asicctl = 0;
- config->ctiappset = 0;
- config->ctiinout_sel = 0;
- /* if enabled then write through */
- if (CTI_PWR_ENA(config))
cti_write_all_hw_regs(drvdata);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(reset_xtrigs);
+static ssize_t show_chan_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- u32 val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- val = (u32)drvdata->config.xtrig_rchan_sel;
- return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+/* select a channel for the show_chan_xtrig function */ +static ssize_t show_chan_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, &val))
same as above.
return -EINVAL;
- if (val > (drvdata->config.nr_ctm_channels-1))
nit: coding style, missing space.
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- drvdata->config.xtrig_rchan_sel = val;
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(show_chan_sel);
Suzuki
Hi Suzuki,
On Thu, 6 Jun 2019 at 13:55, Suzuki K Poulose suzuki.poulose@arm.com wrote:
On 01/05/2019 09:49, Mike Leach wrote:
Adds in required sysfs access for:-
- CoreSight management registers.
- CTI device specific registers.
- Channel Cross triggering API.
This provides the basic programming interface for the device.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 831 +++++++++++++++++-
Please could you split this patch into smaller chunks ? This is slightly bigger patch for a healthy review. I have noted down some minor issues below. Otherwise, I look forward to the next version.
This has been split into 3 chunks in the next version. Will map your comments across to v3 where they still apply.
Thanks
Mike.
drivers/hwtracing/coresight/coresight-cti.c | 188 +++- drivers/hwtracing/coresight/coresight-cti.h | 42 + drivers/hwtracing/coresight/coresight-priv.h | 2 + 4 files changed, 1053 insertions(+), 10 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 9b6749621dcb..ff8dad02a779 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -34,8 +34,8 @@ static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) {
int enable_req;
bool enabled, powered, cpuid;
int enable_req, cpuid;
bool enabled, powered; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); ssize_t size = 0;
@@ -46,12 +46,18 @@ static ssize_t enable_show(struct device *dev, cpuid = drvdata->ctidev.cpu; spin_unlock(&drvdata->spinlock);
if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
} else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
if (cpuid >= 0) {
if (powered) {
size = scnprintf(buf, PAGE_SIZE,
"cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled",
cpuid);
} else {
size = scnprintf(buf, PAGE_SIZE,
"cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req",
cpuid);
} } else {
Could we have started off with the above change in the first patch ?
size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n", enabled ? "enabled" : "disabled");
@@ -81,17 +87,826 @@ static ssize_t enable_store(struct device *dev, } static DEVICE_ATTR_RW(enable);
+static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id);
+} +static DEVICE_ATTR_RO(ctmid);
- /* attribute and group sysfs tables. */ static struct attribute *coresight_cti_attrs[] = { &dev_attr_enable.attr,
};&dev_attr_ctmid.attr, NULL,
+/* register based attributes */ +#define coresight_cti_reg(name, offset) \
coresight_simple_reg32(struct cti_drvdata, name, offset)
+/*
- Show a simple 32 bit value. If pval is NULL then live read,
- otherwise read from supplied pointer only.
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)
+{
unsigned long val = 0;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
if (pval) {
val = (unsigned long)*pval;
} else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + reg_offset);
CS_LOCK(drvdata->base);
}
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+/*
- Store a simple 32 bit value.
- If pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,
int reg_offset)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
Please use 0, instead of hardcoding 16.
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* local store */
if (pval)
*pval = (u32)val;
/* write through of offset and enabled */
if ((reg_offset >= 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, val);
spin_unlock(&drvdata->spinlock);
return size;
+}
+/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4);
Are the PIDs really needed ? If at all they are needed, could we expose the unified PID from these 4 registers ?
+static ssize_t itchout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, ITCHOUT);
+}
Whats the purpose of the exposing integration test registers ?
+static ssize_t itctrl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL);
+}
+static ssize_t itctrl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL);
+} +static DEVICE_ATTR_RW(itctrl);
You may be able to use a macro to make the above more compact.
#define cti_reg32_attr(name, offset) \ static ssize_t name##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ return cti_reg32_show(dev, buf, NULL, offset); \ } \ \ static ssize_t name##_store(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t size) \ { \ return cti_reg32_store(dev, buf, size, NULL, \ offset); \ \ } \ static DEVICE_ATTR_RW(name);
cti_reg32(itctrl, CORESIGHT_ITCTRL); cti_reg32(...., );
Or even use the extended attribute to store the "Offset" and then use a common routine to store/show the reg at the offset.
+/* CTI channel x-trigger programming */ +static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir dir, const char *buf, size_t size)
+{
u32 chan_idx;
u32 trig_idx;
int items, err = size;
/* extract chan idx and trigger idx */
items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);
if (items) {
Should we not match items == 2 to make sure we read a trig_idx as well ?
err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);
if (!err)
err = size;
} else
err = -EINVAL;
minor nit: Please use :
else { err = -EINVAL; }
to match the coding style.
+static ssize_t trigout_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_attach);
+static ssize_t trigout_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_detach);
Again, as above you may be able to use a macro toe make it more easier.
+static ssize_t reset_xtrigs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int i;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* clear the CTI trigger / channel programming registers */
for (i = 0; i < config->nr_trig_max; i++) {
config->ctiinen[i] = 0;
config->ctiouten[i] = 0;
}
/* clear the other regs */
config->ctigate = (0x1 << config->nr_ctm_channels) - 1;
Please GENMASK();
config->asicctl = 0;
config->ctiappset = 0;
config->ctiinout_sel = 0;
/* if enabled then write through */
if (CTI_PWR_ENA(config))
cti_write_all_hw_regs(drvdata);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(reset_xtrigs);
+static ssize_t show_chan_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
u32 val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
val = (u32)drvdata->config.xtrig_rchan_sel;
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+/* select a channel for the show_chan_xtrig function */ +static ssize_t show_chan_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (kstrtoul(buf, 16, &val))
same as above.
return -EINVAL;
if (val > (drvdata->config.nr_ctm_channels-1))
nit: coding style, missing space.
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->config.xtrig_rchan_sel = val;
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(show_chan_sel);
Suzuki
The v8 architecture defines the relationship between a PE, its optional ETM and a CTI. Unlike non-architectural CTIs which are implementation defined, this has a fixed set of connections which can therefore be represented as a simple tag in the device tree.
This patch defines the tags needed to create an entry for this PE/ETM/CTI relationship, and provides functionality to implement the connection model in the CTI driver.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/coresight-cti.c | 10 +- drivers/hwtracing/coresight/coresight-cti.h | 5 + .../hwtracing/coresight/of_coresight-cti.c | 179 +++++++++++++++++- 3 files changed, 188 insertions(+), 6 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 06b1f84795a4..32d9bf0bf0b9 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -192,11 +192,11 @@ static void cti_set_default_config(struct cti_config *config) * Add a connection entry to the list of connections for this * CTI device. */ -static int cti_add_connection_entry(struct cti_drvdata *drvdata, - struct cti_trig_grp *in_info, - struct cti_trig_grp *out_info, - struct coresight_device *csdev, - const char *assoc_dev_name) +int cti_add_connection_entry(struct cti_drvdata *drvdata, + struct cti_trig_grp *in_info, + struct cti_trig_grp *out_info, + struct coresight_device *csdev, + const char *assoc_dev_name) { struct cti_trig_con *tc; struct cti_device *cti_dev = &drvdata->ctidev; diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index a5bf59155f62..df40ca2d9548 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -234,6 +234,11 @@ enum cti_chan_set_op { extern const struct attribute_group *coresight_cti_groups[];
int cti_add_default_connection(struct cti_drvdata *drvdata); +int cti_add_connection_entry(struct cti_drvdata *drvdata, + struct cti_trig_grp *in_info, + struct cti_trig_grp *out_info, + struct coresight_device *csdev, + const char *assoc_dev_name); int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); void cti_write_all_hw_regs(struct cti_drvdata *drvdata); diff --git a/drivers/hwtracing/coresight/of_coresight-cti.c b/drivers/hwtracing/coresight/of_coresight-cti.c index 379ca1113deb..ee9a5906dc37 100644 --- a/drivers/hwtracing/coresight/of_coresight-cti.c +++ b/drivers/hwtracing/coresight/of_coresight-cti.c @@ -18,6 +18,16 @@
#include "coresight-cti.h"
+/* Number of CTI signals in the v8 architecturally defined connection */ +#define NR_V8PE_IN_SIGS 2 +#define NR_V8PE_OUT_SIGS 3 +#define NR_V8ETM_INOUT_SIGS 4 + +/* CTI device tree connection property keywords */ +#define CTI_DT_V8ARCH "arm,cti-v8-arch" +#define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" +#define CTI_DT_CTM_ID "arm,cti-ctm-id" + #ifdef CONFIG_OF
/* @@ -40,6 +50,166 @@ static int of_cti_get_cpu(const struct device_node *node) return (cpu < 0) ? -1 : cpu; }
+/* match function to pass to find_device */ +static int of_dev_node_match(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +/* + * Find a registered coresight device from the device_node. + * The node info is associated with the AMBA parent, so we + * search this first to find a name, and use the name to find + * the device on the coresight bus as they are always named the same. + */ +static struct coresight_device * +of_cti_get_assoc_cs_dev_by_node(struct device_node *node) +{ + struct device *dev, *adev; + struct coresight_device *csdev = NULL; + const char *amba_dev_name = NULL; + + if (!node) + return NULL; + + adev = bus_find_device(&amba_bustype, NULL, node, of_dev_node_match); + if (adev) { + amba_dev_name = dev_name(adev); + dev = bus_find_device_by_name(&coresight_bustype, NULL, + amba_dev_name); + if (dev) { + csdev = to_coresight_device(dev); + put_device(dev); + } + put_device(adev); + } + return csdev; +} + +/* + * Create an architecturally defined v8 connection + * must have a cpu, can have an ETM. + */ +static int of_cti_create_v8_connections(struct cti_drvdata *drvdata, + struct device_node *np) +{ + struct cti_device *cti_dev = &drvdata->ctidev; + struct cti_trig_grp *in = 0, *out = 0, *etm_in = 0, *etm_out = 0; + int cpuid = 0, i; + struct device_node *cs_np; + struct coresight_device *csdev = NULL; + char cpu_name_str[16]; + const char *assoc_name = NULL; + int *sig_in_types = 0, *sig_out_types = 0; + int *etm_sig_in_types = 0, *etm_sig_out_types = 0; + + /* Must have a cpu node */ + cpuid = of_cti_get_cpu(np); + if (cpuid < 0) { + dev_warn(drvdata->dev, "CTI v8 DT binding no cpu\n"); + return -EINVAL; + } + cti_dev->cpu = cpuid; + + /* Can have an etm node */ + cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0); + + /* Allocate the v8 cpu memory */ + in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!in) + goto of_create_v8_mem_err; + + out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!out) + goto of_create_v8_mem_err; + + sig_in_types = kzalloc(sizeof(int) * NR_V8PE_IN_SIGS, GFP_KERNEL); + if (!sig_in_types) + goto of_create_v8_mem_err; + + sig_out_types = kzalloc(sizeof(int) * NR_V8PE_OUT_SIGS, GFP_KERNEL); + if (!sig_out_types) + goto of_create_v8_mem_err; + + /* Allocate the ETM connection memory if required */ + if (cs_np) { + etm_in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!etm_in) + goto of_create_v8_mem_err; + + etm_out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!etm_out) + goto of_create_v8_mem_err; + + etm_sig_in_types = + kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL); + if (!etm_sig_in_types) + goto of_create_v8_mem_err; + + etm_sig_out_types = + kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL); + if (!etm_sig_out_types) + goto of_create_v8_mem_err; + } + + /* Create the v8 PE CTI connection */ + in->nr_sigs = NR_V8PE_IN_SIGS; + in->used_mask = 0x3; /* sigs <0 1> */ + in->sig_types = sig_in_types; + sig_in_types[0] = CTITRIG_PE_DBGTRIGGER; + sig_in_types[1] = CTITRIG_PE_PMUIRQ; + out->nr_sigs = NR_V8PE_OUT_SIGS; + out->used_mask = 0x7; /* sigs <0 1 2 > */ + out->sig_types = sig_out_types; + sig_out_types[0] = CTITRIG_PE_EDBGREQ; + sig_out_types[1] = CTITRIG_PE_DBGRESTART; + sig_out_types[2] = CTITRIG_PE_CTIIRQ; + scnprintf(cpu_name_str, 16, "cpu%d", cpuid); + + cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str); + + /* filter pe_edbgreq */ + drvdata->config.trig_out_filter = 0x1; + + /* Create the v8 ETM associated connection */ + if (cs_np) { + etm_in->nr_sigs = NR_V8ETM_INOUT_SIGS; + etm_in->used_mask = 0xF0; + etm_in->sig_types = etm_sig_in_types; + etm_out->nr_sigs = NR_V8ETM_INOUT_SIGS; + etm_out->used_mask = 0xF0; + etm_out->sig_types = etm_sig_out_types; + + for (i = 0; i < NR_V8ETM_INOUT_SIGS; i++) { + etm_sig_in_types[i] = CTITRIG_ETM_EXTOUT; + etm_sig_out_types[i] = CTITRIG_ETM_EXTIN; + } + + csdev = of_cti_get_assoc_cs_dev_by_node(cs_np); + if (csdev) + assoc_name = dev_name(&csdev->dev); + else + assoc_name = cs_np->full_name; + cti_add_connection_entry(drvdata, etm_in, etm_out, + csdev, assoc_name); + of_node_put(cs_np); + } + return 0; + +of_create_v8_mem_err: + kfree(in); + kfree(out); + kfree(sig_in_types); + kfree(sig_out_types); + if (cs_np) { + kfree(etm_in); + kfree(etm_out); + kfree(etm_sig_in_types); + kfree(etm_sig_out_types); + } + return -ENOMEM; +} + /* get the hardware configuration & connection data. */ int of_cti_get_hw_data(struct device *dev, struct device_node *np, @@ -48,8 +218,15 @@ int of_cti_get_hw_data(struct device *dev, int rc = 0; struct cti_device *cti_dev = &drvdata->ctidev;
+ /* get any CTM ID - defaults to 0 */ + of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id); + + /* check for a v8 architectural CTI device */ + if (of_property_read_bool(np, CTI_DT_V8ARCH)) + rc = of_cti_create_v8_connections(drvdata, np); + /* if no connections, just add a single default based on max IN-OUT */ - if (cti_dev->nr_trig_con == 0) + if (!rc && (cti_dev->nr_trig_con == 0)) rc = cti_add_default_connection(drvdata); return rc; }
On Wed, May 01, 2019 at 09:49:35AM +0100, Mike Leach wrote:
The v8 architecture defines the relationship between a PE, its optional ETM and a CTI. Unlike non-architectural CTIs which are implementation defined, this has a fixed set of connections which can therefore be represented as a simple tag in the device tree.
This patch defines the tags needed to create an entry for this PE/ETM/CTI relationship, and provides functionality to implement the connection model in the CTI driver.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 10 +- drivers/hwtracing/coresight/coresight-cti.h | 5 + .../hwtracing/coresight/of_coresight-cti.c | 179 +++++++++++++++++- 3 files changed, 188 insertions(+), 6 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 06b1f84795a4..32d9bf0bf0b9 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -192,11 +192,11 @@ static void cti_set_default_config(struct cti_config *config)
- Add a connection entry to the list of connections for this
- CTI device.
*/ -static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
{ struct cti_trig_con *tc; struct cti_device *cti_dev = &drvdata->ctidev; diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index a5bf59155f62..df40ca2d9548 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -234,6 +234,11 @@ enum cti_chan_set_op { extern const struct attribute_group *coresight_cti_groups[]; int cti_add_default_connection(struct cti_drvdata *drvdata); +int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name);
int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); void cti_write_all_hw_regs(struct cti_drvdata *drvdata); diff --git a/drivers/hwtracing/coresight/of_coresight-cti.c b/drivers/hwtracing/coresight/of_coresight-cti.c index 379ca1113deb..ee9a5906dc37 100644 --- a/drivers/hwtracing/coresight/of_coresight-cti.c +++ b/drivers/hwtracing/coresight/of_coresight-cti.c @@ -18,6 +18,16 @@ #include "coresight-cti.h" +/* Number of CTI signals in the v8 architecturally defined connection */ +#define NR_V8PE_IN_SIGS 2 +#define NR_V8PE_OUT_SIGS 3 +#define NR_V8ETM_INOUT_SIGS 4
+/* CTI device tree connection property keywords */ +#define CTI_DT_V8ARCH "arm,cti-v8-arch" +#define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" +#define CTI_DT_CTM_ID "arm,cti-ctm-id"
#ifdef CONFIG_OF /* @@ -40,6 +50,166 @@ static int of_cti_get_cpu(const struct device_node *node) return (cpu < 0) ? -1 : cpu; } +/* match function to pass to find_device */ +static int of_dev_node_match(struct device *dev, void *data) +{
- return dev->of_node == data;
+}
If I recall properly Suzuki has introduced that at the framework level as part of is his ACPI patchset.
+/*
- Find a registered coresight device from the device_node.
- The node info is associated with the AMBA parent, so we
- search this first to find a name, and use the name to find
- the device on the coresight bus as they are always named the same.
- */
+static struct coresight_device * +of_cti_get_assoc_cs_dev_by_node(struct device_node *node) +{
- struct device *dev, *adev;
- struct coresight_device *csdev = NULL;
- const char *amba_dev_name = NULL;
- if (!node)
return NULL;
- adev = bus_find_device(&amba_bustype, NULL, node, of_dev_node_match);
- if (adev) {
amba_dev_name = dev_name(adev);
dev = bus_find_device_by_name(&coresight_bustype, NULL,
amba_dev_name);
if (dev) {
csdev = to_coresight_device(dev);
put_device(dev);
}
put_device(adev);
- }
- return csdev;
+}
+/*
- Create an architecturally defined v8 connection
- must have a cpu, can have an ETM.
- */
+static int of_cti_create_v8_connections(struct cti_drvdata *drvdata,
struct device_node *np)
Indentation problem
+{
- struct cti_device *cti_dev = &drvdata->ctidev;
- struct cti_trig_grp *in = 0, *out = 0, *etm_in = 0, *etm_out = 0;
- int cpuid = 0, i;
- struct device_node *cs_np;
- struct coresight_device *csdev = NULL;
- char cpu_name_str[16];
- const char *assoc_name = NULL;
- int *sig_in_types = 0, *sig_out_types = 0;
- int *etm_sig_in_types = 0, *etm_sig_out_types = 0;
I don't think the kernel community has a set rule for using '0' or NULL, but I'm pretty sure it leans toward the latter. In any case weaving both is guaranteed to be called out and should probably be remedied.
- /* Must have a cpu node */
- cpuid = of_cti_get_cpu(np);
- if (cpuid < 0) {
dev_warn(drvdata->dev, "CTI v8 DT binding no cpu\n");
return -EINVAL;
- }
- cti_dev->cpu = cpuid;
- /* Can have an etm node */
- cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
- /* Allocate the v8 cpu memory */
- in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!in)
goto of_create_v8_mem_err;
- out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!out)
goto of_create_v8_mem_err;
- sig_in_types = kzalloc(sizeof(int) * NR_V8PE_IN_SIGS, GFP_KERNEL);
- if (!sig_in_types)
goto of_create_v8_mem_err;
- sig_out_types = kzalloc(sizeof(int) * NR_V8PE_OUT_SIGS, GFP_KERNEL);
- if (!sig_out_types)
goto of_create_v8_mem_err;
- /* Allocate the ETM connection memory if required */
- if (cs_np) {
etm_in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!etm_in)
goto of_create_v8_mem_err;
etm_out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!etm_out)
goto of_create_v8_mem_err;
etm_sig_in_types =
kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL);
if (!etm_sig_in_types)
goto of_create_v8_mem_err;
etm_sig_out_types =
kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL);
if (!etm_sig_out_types)
goto of_create_v8_mem_err;
- }
- /* Create the v8 PE CTI connection */
- in->nr_sigs = NR_V8PE_IN_SIGS;
- in->used_mask = 0x3; /* sigs <0 1> */
- in->sig_types = sig_in_types;
- sig_in_types[0] = CTITRIG_PE_DBGTRIGGER;
- sig_in_types[1] = CTITRIG_PE_PMUIRQ;
- out->nr_sigs = NR_V8PE_OUT_SIGS;
- out->used_mask = 0x7; /* sigs <0 1 2 > */
- out->sig_types = sig_out_types;
- sig_out_types[0] = CTITRIG_PE_EDBGREQ;
- sig_out_types[1] = CTITRIG_PE_DBGRESTART;
- sig_out_types[2] = CTITRIG_PE_CTIIRQ;
- scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
- cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);
The error code for cti_add_connection_entry isn't checked.
- /* filter pe_edbgreq */
- drvdata->config.trig_out_filter = 0x1;
- /* Create the v8 ETM associated connection */
- if (cs_np) {
etm_in->nr_sigs = NR_V8ETM_INOUT_SIGS;
etm_in->used_mask = 0xF0;
etm_in->sig_types = etm_sig_in_types;
etm_out->nr_sigs = NR_V8ETM_INOUT_SIGS;
etm_out->used_mask = 0xF0;
etm_out->sig_types = etm_sig_out_types;
for (i = 0; i < NR_V8ETM_INOUT_SIGS; i++) {
etm_sig_in_types[i] = CTITRIG_ETM_EXTOUT;
etm_sig_out_types[i] = CTITRIG_ETM_EXTIN;
}
csdev = of_cti_get_assoc_cs_dev_by_node(cs_np);
if (csdev)
assoc_name = dev_name(&csdev->dev);
else
assoc_name = cs_np->full_name;
I think the probing of this CTI should be delayed (-EPROBE_DEFER) if the device associated with cs_np hasn't been registered with the core framework.
cti_add_connection_entry(drvdata, etm_in, etm_out,
csdev, assoc_name);
Same here, error code not checked.
of_node_put(cs_np);
- }
- return 0;
+of_create_v8_mem_err:
- kfree(in);
- kfree(out);
- kfree(sig_in_types);
- kfree(sig_out_types);
- if (cs_np) {
kfree(etm_in);
kfree(etm_out);
kfree(etm_sig_in_types);
kfree(etm_sig_out_types);
- }
- return -ENOMEM;
+}
/* get the hardware configuration & connection data. */ int of_cti_get_hw_data(struct device *dev, struct device_node *np, @@ -48,8 +218,15 @@ int of_cti_get_hw_data(struct device *dev, int rc = 0; struct cti_device *cti_dev = &drvdata->ctidev;
- /* get any CTM ID - defaults to 0 */
- of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
The above isn't related to the V8 architected CTI connections and should be moved elsewhere, preferably in a patch where it is used.
- /* check for a v8 architectural CTI device */
- if (of_property_read_bool(np, CTI_DT_V8ARCH))
rc = of_cti_create_v8_connections(drvdata, np);
The error code should be checked here and exit if something untoward happened. That will prevent having to deal with 'rc' below, which is a little messy.
- /* if no connections, just add a single default based on max IN-OUT */
- if (cti_dev->nr_trig_con == 0)
- if (!rc && (cti_dev->nr_trig_con == 0)) rc = cti_add_default_connection(drvdata); return rc;
}
2.20.1
Hi Mathieu,
On Fri, 10 May 2019 at 18:14, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, May 01, 2019 at 09:49:35AM +0100, Mike Leach wrote:
The v8 architecture defines the relationship between a PE, its optional ETM and a CTI. Unlike non-architectural CTIs which are implementation defined, this has a fixed set of connections which can therefore be represented as a simple tag in the device tree.
This patch defines the tags needed to create an entry for this PE/ETM/CTI relationship, and provides functionality to implement the connection model in the CTI driver.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 10 +- drivers/hwtracing/coresight/coresight-cti.h | 5 + .../hwtracing/coresight/of_coresight-cti.c | 179 +++++++++++++++++- 3 files changed, 188 insertions(+), 6 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 06b1f84795a4..32d9bf0bf0b9 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -192,11 +192,11 @@ static void cti_set_default_config(struct cti_config *config)
- Add a connection entry to the list of connections for this
- CTI device.
*/ -static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
{ struct cti_trig_con *tc; struct cti_device *cti_dev = &drvdata->ctidev; diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index a5bf59155f62..df40ca2d9548 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -234,6 +234,11 @@ enum cti_chan_set_op { extern const struct attribute_group *coresight_cti_groups[];
int cti_add_default_connection(struct cti_drvdata *drvdata); +int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name);
int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); void cti_write_all_hw_regs(struct cti_drvdata *drvdata); diff --git a/drivers/hwtracing/coresight/of_coresight-cti.c b/drivers/hwtracing/coresight/of_coresight-cti.c index 379ca1113deb..ee9a5906dc37 100644 --- a/drivers/hwtracing/coresight/of_coresight-cti.c +++ b/drivers/hwtracing/coresight/of_coresight-cti.c @@ -18,6 +18,16 @@
#include "coresight-cti.h"
+/* Number of CTI signals in the v8 architecturally defined connection */ +#define NR_V8PE_IN_SIGS 2 +#define NR_V8PE_OUT_SIGS 3 +#define NR_V8ETM_INOUT_SIGS 4
+/* CTI device tree connection property keywords */ +#define CTI_DT_V8ARCH "arm,cti-v8-arch" +#define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" +#define CTI_DT_CTM_ID "arm,cti-ctm-id"
#ifdef CONFIG_OF
/* @@ -40,6 +50,166 @@ static int of_cti_get_cpu(const struct device_node *node) return (cpu < 0) ? -1 : cpu; }
+/* match function to pass to find_device */ +static int of_dev_node_match(struct device *dev, void *data) +{
return dev->of_node == data;
+}
If I recall properly Suzuki has introduced that at the framework level as part of is his ACPI patchset.
Certainly something similar that is ACPI compliant using fwnode structures. I'll look into this when applying over suzukis set, though a quick search has revealed zero documentation on fwnode, so it is tricky to say if the replacement fn can be adapted.
+/*
- Find a registered coresight device from the device_node.
- The node info is associated with the AMBA parent, so we
- search this first to find a name, and use the name to find
- the device on the coresight bus as they are always named the same.
- */
+static struct coresight_device * +of_cti_get_assoc_cs_dev_by_node(struct device_node *node) +{
struct device *dev, *adev;
struct coresight_device *csdev = NULL;
const char *amba_dev_name = NULL;
if (!node)
return NULL;
adev = bus_find_device(&amba_bustype, NULL, node, of_dev_node_match);
if (adev) {
amba_dev_name = dev_name(adev);
dev = bus_find_device_by_name(&coresight_bustype, NULL,
amba_dev_name);
if (dev) {
csdev = to_coresight_device(dev);
put_device(dev);
}
put_device(adev);
}
return csdev;
+}
+/*
- Create an architecturally defined v8 connection
- must have a cpu, can have an ETM.
- */
+static int of_cti_create_v8_connections(struct cti_drvdata *drvdata,
struct device_node *np)
Indentation problem
OK
+{
struct cti_device *cti_dev = &drvdata->ctidev;
struct cti_trig_grp *in = 0, *out = 0, *etm_in = 0, *etm_out = 0;
int cpuid = 0, i;
struct device_node *cs_np;
struct coresight_device *csdev = NULL;
char cpu_name_str[16];
const char *assoc_name = NULL;
int *sig_in_types = 0, *sig_out_types = 0;
int *etm_sig_in_types = 0, *etm_sig_out_types = 0;
I don't think the kernel community has a set rule for using '0' or NULL, but I'm pretty sure it leans toward the latter. In any case weaving both is guaranteed to be called out and should probably be remedied.
will fixed & fn split into 2 per Leo's suggestion
/* Must have a cpu node */
cpuid = of_cti_get_cpu(np);
if (cpuid < 0) {
dev_warn(drvdata->dev, "CTI v8 DT binding no cpu\n");
return -EINVAL;
}
cti_dev->cpu = cpuid;
/* Can have an etm node */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
/* Allocate the v8 cpu memory */
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
goto of_create_v8_mem_err;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
goto of_create_v8_mem_err;
sig_in_types = kzalloc(sizeof(int) * NR_V8PE_IN_SIGS, GFP_KERNEL);
if (!sig_in_types)
goto of_create_v8_mem_err;
sig_out_types = kzalloc(sizeof(int) * NR_V8PE_OUT_SIGS, GFP_KERNEL);
if (!sig_out_types)
goto of_create_v8_mem_err;
/* Allocate the ETM connection memory if required */
if (cs_np) {
etm_in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!etm_in)
goto of_create_v8_mem_err;
etm_out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!etm_out)
goto of_create_v8_mem_err;
etm_sig_in_types =
kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL);
if (!etm_sig_in_types)
goto of_create_v8_mem_err;
etm_sig_out_types =
kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL);
if (!etm_sig_out_types)
goto of_create_v8_mem_err;
}
/* Create the v8 PE CTI connection */
in->nr_sigs = NR_V8PE_IN_SIGS;
in->used_mask = 0x3; /* sigs <0 1> */
in->sig_types = sig_in_types;
sig_in_types[0] = CTITRIG_PE_DBGTRIGGER;
sig_in_types[1] = CTITRIG_PE_PMUIRQ;
out->nr_sigs = NR_V8PE_OUT_SIGS;
out->used_mask = 0x7; /* sigs <0 1 2 > */
out->sig_types = sig_out_types;
sig_out_types[0] = CTITRIG_PE_EDBGREQ;
sig_out_types[1] = CTITRIG_PE_DBGRESTART;
sig_out_types[2] = CTITRIG_PE_CTIIRQ;
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);
The error code for cti_add_connection_entry isn't checked.
/* filter pe_edbgreq */
drvdata->config.trig_out_filter = 0x1;
/* Create the v8 ETM associated connection */
if (cs_np) {
etm_in->nr_sigs = NR_V8ETM_INOUT_SIGS;
etm_in->used_mask = 0xF0;
etm_in->sig_types = etm_sig_in_types;
etm_out->nr_sigs = NR_V8ETM_INOUT_SIGS;
etm_out->used_mask = 0xF0;
etm_out->sig_types = etm_sig_out_types;
for (i = 0; i < NR_V8ETM_INOUT_SIGS; i++) {
etm_sig_in_types[i] = CTITRIG_ETM_EXTOUT;
etm_sig_out_types[i] = CTITRIG_ETM_EXTIN;
}
csdev = of_cti_get_assoc_cs_dev_by_node(cs_np);
if (csdev)
assoc_name = dev_name(&csdev->dev);
else
assoc_name = cs_np->full_name;
I think the probing of this CTI should be delayed (-EPROBE_DEFER) if the device associated with cs_np hasn't been registered with the core framework.
As per the fixup of orphan connections in coresight there is a related fn in coresight register that handles the case where CTI appears first.
cti_add_connection_entry(drvdata, etm_in, etm_out,
csdev, assoc_name);
Same here, error code not checked.
fixed in next set
of_node_put(cs_np);
}
return 0;
+of_create_v8_mem_err:
kfree(in);
kfree(out);
kfree(sig_in_types);
kfree(sig_out_types);
if (cs_np) {
kfree(etm_in);
kfree(etm_out);
kfree(etm_sig_in_types);
kfree(etm_sig_out_types);
}
return -ENOMEM;
+}
/* get the hardware configuration & connection data. */ int of_cti_get_hw_data(struct device *dev, struct device_node *np, @@ -48,8 +218,15 @@ int of_cti_get_hw_data(struct device *dev, int rc = 0; struct cti_device *cti_dev = &drvdata->ctidev;
/* get any CTM ID - defaults to 0 */
of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
The above isn't related to the V8 architected CTI connections and should be moved elsewhere, preferably in a patch where it is used.
OK
/* check for a v8 architectural CTI device */
if (of_property_read_bool(np, CTI_DT_V8ARCH))
rc = of_cti_create_v8_connections(drvdata, np);
The error code should be checked here and exit if something untoward happened. That will prevent having to deal with 'rc' below, which is a little messy.
/* if no connections, just add a single default based on max IN-OUT */
if (cti_dev->nr_trig_con == 0)
if (!rc && (cti_dev->nr_trig_con == 0)) rc = cti_add_default_connection(drvdata); return rc;
}
2.20.1
Thanks for the feedback
Mike
On Wed, May 01, 2019 at 09:49:35AM +0100, Mike Leach wrote:
The v8 architecture defines the relationship between a PE, its optional ETM and a CTI. Unlike non-architectural CTIs which are implementation defined, this has a fixed set of connections which can therefore be represented as a simple tag in the device tree.
This patch defines the tags needed to create an entry for this PE/ETM/CTI relationship, and provides functionality to implement the connection model in the CTI driver.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 10 +- drivers/hwtracing/coresight/coresight-cti.h | 5 + .../hwtracing/coresight/of_coresight-cti.c | 179 +++++++++++++++++- 3 files changed, 188 insertions(+), 6 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 06b1f84795a4..32d9bf0bf0b9 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -192,11 +192,11 @@ static void cti_set_default_config(struct cti_config *config)
- Add a connection entry to the list of connections for this
- CTI device.
*/ -static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
{ struct cti_trig_con *tc; struct cti_device *cti_dev = &drvdata->ctidev; diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index a5bf59155f62..df40ca2d9548 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -234,6 +234,11 @@ enum cti_chan_set_op { extern const struct attribute_group *coresight_cti_groups[]; int cti_add_default_connection(struct cti_drvdata *drvdata); +int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name);
int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); void cti_write_all_hw_regs(struct cti_drvdata *drvdata); diff --git a/drivers/hwtracing/coresight/of_coresight-cti.c b/drivers/hwtracing/coresight/of_coresight-cti.c index 379ca1113deb..ee9a5906dc37 100644 --- a/drivers/hwtracing/coresight/of_coresight-cti.c +++ b/drivers/hwtracing/coresight/of_coresight-cti.c @@ -18,6 +18,16 @@ #include "coresight-cti.h" +/* Number of CTI signals in the v8 architecturally defined connection */ +#define NR_V8PE_IN_SIGS 2 +#define NR_V8PE_OUT_SIGS 3 +#define NR_V8ETM_INOUT_SIGS 4
+/* CTI device tree connection property keywords */ +#define CTI_DT_V8ARCH "arm,cti-v8-arch" +#define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" +#define CTI_DT_CTM_ID "arm,cti-ctm-id"
Quote from in the doc [1]:
3) The Documentation/ portion of the patch should come in the series before the code implementing the binding.
So please firstly give out the DT binding document patch before this patch in patch set.
[1] $KERNEL/Documentation/devicetree/bindings/submitting-patches.txt
#ifdef CONFIG_OF /* @@ -40,6 +50,166 @@ static int of_cti_get_cpu(const struct device_node *node) return (cpu < 0) ? -1 : cpu; } +/* match function to pass to find_device */ +static int of_dev_node_match(struct device *dev, void *data) +{
- return dev->of_node == data;
+}
+/*
- Find a registered coresight device from the device_node.
- The node info is associated with the AMBA parent, so we
- search this first to find a name, and use the name to find
- the device on the coresight bus as they are always named the same.
- */
+static struct coresight_device * +of_cti_get_assoc_cs_dev_by_node(struct device_node *node) +{
- struct device *dev, *adev;
- struct coresight_device *csdev = NULL;
- const char *amba_dev_name = NULL;
- if (!node)
return NULL;
- adev = bus_find_device(&amba_bustype, NULL, node, of_dev_node_match);
- if (adev) {
amba_dev_name = dev_name(adev);
dev = bus_find_device_by_name(&coresight_bustype, NULL,
amba_dev_name);
if (dev) {
csdev = to_coresight_device(dev);
put_device(dev);
}
put_device(adev);
- }
- return csdev;
+}
+/*
- Create an architecturally defined v8 connection
- must have a cpu, can have an ETM.
- */
+static int of_cti_create_v8_connections(struct cti_drvdata *drvdata,
struct device_node *np)
+{
- struct cti_device *cti_dev = &drvdata->ctidev;
- struct cti_trig_grp *in = 0, *out = 0, *etm_in = 0, *etm_out = 0;
- int cpuid = 0, i;
- struct device_node *cs_np;
- struct coresight_device *csdev = NULL;
- char cpu_name_str[16];
- const char *assoc_name = NULL;
- int *sig_in_types = 0, *sig_out_types = 0;
- int *etm_sig_in_types = 0, *etm_sig_out_types = 0;
- /* Must have a cpu node */
- cpuid = of_cti_get_cpu(np);
- if (cpuid < 0) {
dev_warn(drvdata->dev, "CTI v8 DT binding no cpu\n");
return -EINVAL;
- }
- cti_dev->cpu = cpuid;
- /* Can have an etm node */
- cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
For this patch, I have one main question: what's the relationship between one CTI with PE and ETM (optional)? Is it possible that CTI only connects to ETM but without PE?
And suggest this function can be divided into two sub functions:
of_cti_create_v8_pe_connections() of_cti_create_v8_etm_connections()
Thanks, Leo Yan
- /* Allocate the v8 cpu memory */
- in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!in)
goto of_create_v8_mem_err;
- out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!out)
goto of_create_v8_mem_err;
- sig_in_types = kzalloc(sizeof(int) * NR_V8PE_IN_SIGS, GFP_KERNEL);
- if (!sig_in_types)
goto of_create_v8_mem_err;
- sig_out_types = kzalloc(sizeof(int) * NR_V8PE_OUT_SIGS, GFP_KERNEL);
- if (!sig_out_types)
goto of_create_v8_mem_err;
- /* Allocate the ETM connection memory if required */
- if (cs_np) {
etm_in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!etm_in)
goto of_create_v8_mem_err;
etm_out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!etm_out)
goto of_create_v8_mem_err;
etm_sig_in_types =
kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL);
if (!etm_sig_in_types)
goto of_create_v8_mem_err;
etm_sig_out_types =
kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL);
if (!etm_sig_out_types)
goto of_create_v8_mem_err;
- }
- /* Create the v8 PE CTI connection */
- in->nr_sigs = NR_V8PE_IN_SIGS;
- in->used_mask = 0x3; /* sigs <0 1> */
- in->sig_types = sig_in_types;
- sig_in_types[0] = CTITRIG_PE_DBGTRIGGER;
- sig_in_types[1] = CTITRIG_PE_PMUIRQ;
- out->nr_sigs = NR_V8PE_OUT_SIGS;
- out->used_mask = 0x7; /* sigs <0 1 2 > */
- out->sig_types = sig_out_types;
- sig_out_types[0] = CTITRIG_PE_EDBGREQ;
- sig_out_types[1] = CTITRIG_PE_DBGRESTART;
- sig_out_types[2] = CTITRIG_PE_CTIIRQ;
- scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
- cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);
- /* filter pe_edbgreq */
- drvdata->config.trig_out_filter = 0x1;
- /* Create the v8 ETM associated connection */
- if (cs_np) {
etm_in->nr_sigs = NR_V8ETM_INOUT_SIGS;
etm_in->used_mask = 0xF0;
etm_in->sig_types = etm_sig_in_types;
etm_out->nr_sigs = NR_V8ETM_INOUT_SIGS;
etm_out->used_mask = 0xF0;
etm_out->sig_types = etm_sig_out_types;
for (i = 0; i < NR_V8ETM_INOUT_SIGS; i++) {
etm_sig_in_types[i] = CTITRIG_ETM_EXTOUT;
etm_sig_out_types[i] = CTITRIG_ETM_EXTIN;
}
csdev = of_cti_get_assoc_cs_dev_by_node(cs_np);
if (csdev)
assoc_name = dev_name(&csdev->dev);
else
assoc_name = cs_np->full_name;
cti_add_connection_entry(drvdata, etm_in, etm_out,
csdev, assoc_name);
of_node_put(cs_np);
- }
- return 0;
+of_create_v8_mem_err:
- kfree(in);
- kfree(out);
- kfree(sig_in_types);
- kfree(sig_out_types);
- if (cs_np) {
kfree(etm_in);
kfree(etm_out);
kfree(etm_sig_in_types);
kfree(etm_sig_out_types);
- }
- return -ENOMEM;
+}
/* get the hardware configuration & connection data. */ int of_cti_get_hw_data(struct device *dev, struct device_node *np, @@ -48,8 +218,15 @@ int of_cti_get_hw_data(struct device *dev, int rc = 0; struct cti_device *cti_dev = &drvdata->ctidev;
- /* get any CTM ID - defaults to 0 */
- of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
- /* check for a v8 architectural CTI device */
- if (of_property_read_bool(np, CTI_DT_V8ARCH))
rc = of_cti_create_v8_connections(drvdata, np);
- /* if no connections, just add a single default based on max IN-OUT */
- if (cti_dev->nr_trig_con == 0)
- if (!rc && (cti_dev->nr_trig_con == 0)) rc = cti_add_default_connection(drvdata); return rc;
}
2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Hi Leo,
On Sat, 25 May 2019 at 11:52, Leo Yan leo.yan@linaro.org wrote:
On Wed, May 01, 2019 at 09:49:35AM +0100, Mike Leach wrote:
The v8 architecture defines the relationship between a PE, its optional ETM and a CTI. Unlike non-architectural CTIs which are implementation defined, this has a fixed set of connections which can therefore be represented as a simple tag in the device tree.
This patch defines the tags needed to create an entry for this PE/ETM/CTI relationship, and provides functionality to implement the connection model in the CTI driver.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 10 +- drivers/hwtracing/coresight/coresight-cti.h | 5 + .../hwtracing/coresight/of_coresight-cti.c | 179 +++++++++++++++++- 3 files changed, 188 insertions(+), 6 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 06b1f84795a4..32d9bf0bf0b9 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -192,11 +192,11 @@ static void cti_set_default_config(struct cti_config *config)
- Add a connection entry to the list of connections for this
- CTI device.
*/ -static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
{ struct cti_trig_con *tc; struct cti_device *cti_dev = &drvdata->ctidev; diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index a5bf59155f62..df40ca2d9548 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -234,6 +234,11 @@ enum cti_chan_set_op { extern const struct attribute_group *coresight_cti_groups[];
int cti_add_default_connection(struct cti_drvdata *drvdata); +int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name);
int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); void cti_write_all_hw_regs(struct cti_drvdata *drvdata); diff --git a/drivers/hwtracing/coresight/of_coresight-cti.c b/drivers/hwtracing/coresight/of_coresight-cti.c index 379ca1113deb..ee9a5906dc37 100644 --- a/drivers/hwtracing/coresight/of_coresight-cti.c +++ b/drivers/hwtracing/coresight/of_coresight-cti.c @@ -18,6 +18,16 @@
#include "coresight-cti.h"
+/* Number of CTI signals in the v8 architecturally defined connection */ +#define NR_V8PE_IN_SIGS 2 +#define NR_V8PE_OUT_SIGS 3 +#define NR_V8ETM_INOUT_SIGS 4
+/* CTI device tree connection property keywords */ +#define CTI_DT_V8ARCH "arm,cti-v8-arch" +#define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" +#define CTI_DT_CTM_ID "arm,cti-ctm-id"
Quote from in the doc [1]:
- The Documentation/ portion of the patch should come in the series before the code implementing the binding.
So please firstly give out the DT binding document patch before this patch in patch set.
OK - thanks for spotting this - I'd missed it. I'll re-order the patches to fix.
[1] $KERNEL/Documentation/devicetree/bindings/submitting-patches.txt
#ifdef CONFIG_OF
/* @@ -40,6 +50,166 @@ static int of_cti_get_cpu(const struct device_node *node) return (cpu < 0) ? -1 : cpu; }
+/* match function to pass to find_device */ +static int of_dev_node_match(struct device *dev, void *data) +{
return dev->of_node == data;
+}
+/*
- Find a registered coresight device from the device_node.
- The node info is associated with the AMBA parent, so we
- search this first to find a name, and use the name to find
- the device on the coresight bus as they are always named the same.
- */
+static struct coresight_device * +of_cti_get_assoc_cs_dev_by_node(struct device_node *node) +{
struct device *dev, *adev;
struct coresight_device *csdev = NULL;
const char *amba_dev_name = NULL;
if (!node)
return NULL;
adev = bus_find_device(&amba_bustype, NULL, node, of_dev_node_match);
if (adev) {
amba_dev_name = dev_name(adev);
dev = bus_find_device_by_name(&coresight_bustype, NULL,
amba_dev_name);
if (dev) {
csdev = to_coresight_device(dev);
put_device(dev);
}
put_device(adev);
}
return csdev;
+}
+/*
- Create an architecturally defined v8 connection
- must have a cpu, can have an ETM.
- */
+static int of_cti_create_v8_connections(struct cti_drvdata *drvdata,
struct device_node *np)
+{
struct cti_device *cti_dev = &drvdata->ctidev;
struct cti_trig_grp *in = 0, *out = 0, *etm_in = 0, *etm_out = 0;
int cpuid = 0, i;
struct device_node *cs_np;
struct coresight_device *csdev = NULL;
char cpu_name_str[16];
const char *assoc_name = NULL;
int *sig_in_types = 0, *sig_out_types = 0;
int *etm_sig_in_types = 0, *etm_sig_out_types = 0;
/* Must have a cpu node */
cpuid = of_cti_get_cpu(np);
if (cpuid < 0) {
dev_warn(drvdata->dev, "CTI v8 DT binding no cpu\n");
return -EINVAL;
}
cti_dev->cpu = cpuid;
/* Can have an etm node */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
For this patch, I have one main question: what's the relationship between one CTI with PE and ETM (optional)? Is it possible that CTI only connects to ETM but without PE?
The CTI connected to a v8 PE and ETM is defined in the architecture as having specific trigger connections. The architecture requires that there must be a CTI connected to the PE. The same CTI must be connected to any ETM if it is specified for this PE. No other combinations are possible.
And suggest this function can be divided into two sub functions:
of_cti_create_v8_pe_connections() of_cti_create_v8_etm_connections()
I have moved the ETM connections into its own fn
Mike
Thanks, Leo Yan
/* Allocate the v8 cpu memory */
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
goto of_create_v8_mem_err;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
goto of_create_v8_mem_err;
sig_in_types = kzalloc(sizeof(int) * NR_V8PE_IN_SIGS, GFP_KERNEL);
if (!sig_in_types)
goto of_create_v8_mem_err;
sig_out_types = kzalloc(sizeof(int) * NR_V8PE_OUT_SIGS, GFP_KERNEL);
if (!sig_out_types)
goto of_create_v8_mem_err;
/* Allocate the ETM connection memory if required */
if (cs_np) {
etm_in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!etm_in)
goto of_create_v8_mem_err;
etm_out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!etm_out)
goto of_create_v8_mem_err;
etm_sig_in_types =
kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL);
if (!etm_sig_in_types)
goto of_create_v8_mem_err;
etm_sig_out_types =
kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL);
if (!etm_sig_out_types)
goto of_create_v8_mem_err;
}
/* Create the v8 PE CTI connection */
in->nr_sigs = NR_V8PE_IN_SIGS;
in->used_mask = 0x3; /* sigs <0 1> */
in->sig_types = sig_in_types;
sig_in_types[0] = CTITRIG_PE_DBGTRIGGER;
sig_in_types[1] = CTITRIG_PE_PMUIRQ;
out->nr_sigs = NR_V8PE_OUT_SIGS;
out->used_mask = 0x7; /* sigs <0 1 2 > */
out->sig_types = sig_out_types;
sig_out_types[0] = CTITRIG_PE_EDBGREQ;
sig_out_types[1] = CTITRIG_PE_DBGRESTART;
sig_out_types[2] = CTITRIG_PE_CTIIRQ;
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);
/* filter pe_edbgreq */
drvdata->config.trig_out_filter = 0x1;
/* Create the v8 ETM associated connection */
if (cs_np) {
etm_in->nr_sigs = NR_V8ETM_INOUT_SIGS;
etm_in->used_mask = 0xF0;
etm_in->sig_types = etm_sig_in_types;
etm_out->nr_sigs = NR_V8ETM_INOUT_SIGS;
etm_out->used_mask = 0xF0;
etm_out->sig_types = etm_sig_out_types;
for (i = 0; i < NR_V8ETM_INOUT_SIGS; i++) {
etm_sig_in_types[i] = CTITRIG_ETM_EXTOUT;
etm_sig_out_types[i] = CTITRIG_ETM_EXTIN;
}
csdev = of_cti_get_assoc_cs_dev_by_node(cs_np);
if (csdev)
assoc_name = dev_name(&csdev->dev);
else
assoc_name = cs_np->full_name;
cti_add_connection_entry(drvdata, etm_in, etm_out,
csdev, assoc_name);
of_node_put(cs_np);
}
return 0;
+of_create_v8_mem_err:
kfree(in);
kfree(out);
kfree(sig_in_types);
kfree(sig_out_types);
if (cs_np) {
kfree(etm_in);
kfree(etm_out);
kfree(etm_sig_in_types);
kfree(etm_sig_out_types);
}
return -ENOMEM;
+}
/* get the hardware configuration & connection data. */ int of_cti_get_hw_data(struct device *dev, struct device_node *np, @@ -48,8 +218,15 @@ int of_cti_get_hw_data(struct device *dev, int rc = 0; struct cti_device *cti_dev = &drvdata->ctidev;
/* get any CTM ID - defaults to 0 */
of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
/* check for a v8 architectural CTI device */
if (of_property_read_bool(np, CTI_DT_V8ARCH))
rc = of_cti_create_v8_connections(drvdata, np);
/* if no connections, just add a single default based on max IN-OUT */
if (cti_dev->nr_trig_con == 0)
if (!rc && (cti_dev->nr_trig_con == 0)) rc = cti_add_default_connection(drvdata); return rc;
}
2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
Adds support for CTIs that are implementation defined at design time, and not constrained by v8 architecture.
These CTIs have no standard connection setup, all the settings have to be defined in the device tree files. The patch creates a set of connections and trigger signals based on the information provided,
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/coresight-cti.h | 1 + .../hwtracing/coresight/of_coresight-cti.c | 191 +++++++++++++++++- 2 files changed, 191 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index df40ca2d9548..7342f998f4c5 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -239,6 +239,7 @@ int cti_add_connection_entry(struct cti_drvdata *drvdata, struct cti_trig_grp *out_info, struct coresight_device *csdev, const char *assoc_dev_name); +void cti_free_conn_info(struct cti_drvdata *drvdata); int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); void cti_write_all_hw_regs(struct cti_drvdata *drvdata); diff --git a/drivers/hwtracing/coresight/of_coresight-cti.c b/drivers/hwtracing/coresight/of_coresight-cti.c index ee9a5906dc37..486fa99813ae 100644 --- a/drivers/hwtracing/coresight/of_coresight-cti.c +++ b/drivers/hwtracing/coresight/of_coresight-cti.c @@ -23,10 +23,19 @@ #define NR_V8PE_OUT_SIGS 3 #define NR_V8ETM_INOUT_SIGS 4
+/* CTI device tree trigger connection node keyword */ +#define CTI_DT_CONNS "trig-conns" + /* CTI device tree connection property keywords */ #define CTI_DT_V8ARCH "arm,cti-v8-arch" #define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" #define CTI_DT_CTM_ID "arm,cti-ctm-id" +#define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs" +#define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs" +#define CTI_DT_TRIGIN_TYPES "arm,trig-in-types" +#define CTI_DT_TRIGOUT_TYPES "arm,trig-out-types" +#define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters" +#define CTI_DT_CONN_NAME "arm,trig-conn-name"
#ifdef CONFIG_OF
@@ -210,6 +219,181 @@ static int of_cti_create_v8_connections(struct cti_drvdata *drvdata, return -ENOMEM; }
+static int of_cti_read_trig_group(struct cti_trig_grp **trig_grp_ptr, + struct device_node *np, + const char *grp_name, + int max_trigs) +{ + int items, err = 0; + u32 value, pidx; + struct cti_trig_grp *grp; + + grp = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!grp) + return -ENOMEM; + *trig_grp_ptr = grp; + + items = of_property_count_elems_of_size(np, grp_name, 4); + /* if the property doesn't exist or has no values, then return + * an empty connection group + */ + if (items < 0) + return 0; + + if (items > max_trigs) + return -EINVAL; + + /* set the number of signals and usage mask */ + for (pidx = 0; pidx < items; pidx++) { + err = of_property_read_u32_index(np, grp_name, pidx, &value); + if (err) + return err; + grp->nr_sigs++; + grp->used_mask |= (0x1 << value); + } + return 0; +} + +static int of_cti_read_trig_types(struct cti_trig_grp *trig_grp_ptr, + struct device_node *np, + const char *type_name) +{ + int items, used = 0, err = 0, nr_sigs; + u32 value, i; + + /* allocate an array according to number of signals in connection */ + nr_sigs = trig_grp_ptr->nr_sigs; + if (!nr_sigs) + return 0; + trig_grp_ptr->sig_types = kzalloc(sizeof(int) * nr_sigs, GFP_KERNEL); + if (!trig_grp_ptr->sig_types) + return -ENOMEM; + + /* see if any types have been included in the device description */ + items = of_property_count_elems_of_size(np, type_name, 4); + + /* + * Match type id to signal index, + * too few - default to genio, + * too many - ignore excess. + */ + for (i = 0; i < nr_sigs; i++) { + if (used < items) { + err = of_property_read_u32_index(np, type_name, + i, &value); + if (!err) + trig_grp_ptr->sig_types[i] = value; + else + trig_grp_ptr->sig_types[i] = CTITRIG_GEN_IO; + used++; + } + else + trig_grp_ptr->sig_types[i] = CTITRIG_GEN_IO; + } + return 0; +} + +static void of_cti_free_trig_grp(struct cti_trig_grp *grp) +{ + if (grp) { + kfree(grp->sig_types); + kfree(grp); + } +} + +static int of_cti_create_connection(struct device *dev, + struct device_node *np, + struct cti_drvdata *drvdata) +{ + struct cti_trig_grp *in = 0, *out = 0, *filter = 0; + int cpuid = -1, err = 0, trig_max = drvdata->config.nr_trig_max; + struct device_node *cs_np; + struct coresight_device *csdev = NULL; + const char *assoc_name = "unknown"; + char cpu_name_str[16]; + + /* look for the signals properties. */ + err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max); + if (err) + goto of_create_con_err; + + err = of_cti_read_trig_types(in, np, CTI_DT_TRIGIN_TYPES); + if (err) + goto of_create_con_err; + + err = of_cti_read_trig_group(&out, np, CTI_DT_TRIGOUT_SIGS, trig_max); + if (err) + goto of_create_con_err; + + err = of_cti_read_trig_types(out, np, CTI_DT_TRIGOUT_TYPES); + if (err) + goto of_create_con_err; + + err = of_cti_read_trig_group(&filter, np, CTI_DT_FILTER_OUT_SIGS, + trig_max); + if (err) + goto of_create_con_err; + + /* read the connection name if set - may be overridden by later */ + of_property_read_string(np, CTI_DT_CONN_NAME, &assoc_name); + + /* associated cpu ? */ + cpuid = of_cti_get_cpu(np); + if (cpuid >= 0) { + drvdata->ctidev.cpu = cpuid; + scnprintf(cpu_name_str, 16, "cpu%d", cpuid); + assoc_name = cpu_name_str; + } else { + /* associated device ? */ + cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0); + if (cs_np) { + csdev = of_cti_get_assoc_cs_dev_by_node(cs_np); + if (csdev) /* use device name if csdev found */ + assoc_name = dev_name(&csdev->dev); + else /* otherwise node name for later association */ + assoc_name = cs_np->full_name; + of_node_put(cs_np); + } + } + /* set up a connection */ + err = cti_add_connection_entry(drvdata, in, out, csdev, assoc_name); + if (err) + goto of_create_con_err; + + /* note any filter info */ + drvdata->config.trig_out_filter |= filter->used_mask; + kfree(filter); + return err; + +of_create_con_err: + of_cti_free_trig_grp(in); + of_cti_free_trig_grp(out); + of_cti_free_trig_grp(filter); + return err; +} + +static int of_cti_create_impdef_connections(struct device *dev, + struct device_node *np, + struct cti_drvdata *drvdata) +{ + int rc = 0; + struct device_node *nc = NULL; + + for_each_child_of_node(np, nc) { + if (of_node_cmp(nc->name, CTI_DT_CONNS) != 0) + continue; + rc = of_cti_create_connection(dev, nc, drvdata); + if (rc != 0) + goto of_cti_impdef_create_err; + } + return rc; + +of_cti_impdef_create_err: + /* last create should have freed its mem - unpick previous */ + cti_free_conn_info(drvdata); + return rc; +} + /* get the hardware configuration & connection data. */ int of_cti_get_hw_data(struct device *dev, struct device_node *np, @@ -221,9 +405,14 @@ int of_cti_get_hw_data(struct device *dev, /* get any CTM ID - defaults to 0 */ of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
- /* check for a v8 architectural CTI device */ + /* + * Check for a v8 architectural CTI device, + * otherwise implementation defined. + */ if (of_property_read_bool(np, CTI_DT_V8ARCH)) rc = of_cti_create_v8_connections(drvdata, np); + else + rc = of_cti_create_impdef_connections(dev, np, drvdata);
/* if no connections, just add a single default based on max IN-OUT */ if (!rc && (cti_dev->nr_trig_con == 0))
For the patch subject line in this set you can drop "drivers".
On Wed, May 01, 2019 at 09:49:36AM +0100, Mike Leach wrote:
Adds support for CTIs that are implementation defined at design time, and not constrained by v8 architecture.
Changelog descriptions needs to be line wrapped at 75 characters.
These CTIs have no standard connection setup, all the settings have to be defined in the device tree files. The patch creates a set of connections and trigger signals based on the information provided,
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.h | 1 + .../hwtracing/coresight/of_coresight-cti.c | 191 +++++++++++++++++- 2 files changed, 191 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index df40ca2d9548..7342f998f4c5 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -239,6 +239,7 @@ int cti_add_connection_entry(struct cti_drvdata *drvdata, struct cti_trig_grp *out_info, struct coresight_device *csdev, const char *assoc_dev_name); +void cti_free_conn_info(struct cti_drvdata *drvdata); int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); void cti_write_all_hw_regs(struct cti_drvdata *drvdata); diff --git a/drivers/hwtracing/coresight/of_coresight-cti.c b/drivers/hwtracing/coresight/of_coresight-cti.c index ee9a5906dc37..486fa99813ae 100644 --- a/drivers/hwtracing/coresight/of_coresight-cti.c +++ b/drivers/hwtracing/coresight/of_coresight-cti.c @@ -23,10 +23,19 @@ #define NR_V8PE_OUT_SIGS 3 #define NR_V8ETM_INOUT_SIGS 4 +/* CTI device tree trigger connection node keyword */ +#define CTI_DT_CONNS "trig-conns"
/* CTI device tree connection property keywords */ #define CTI_DT_V8ARCH "arm,cti-v8-arch" #define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" #define CTI_DT_CTM_ID "arm,cti-ctm-id" +#define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs" +#define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs" +#define CTI_DT_TRIGIN_TYPES "arm,trig-in-types" +#define CTI_DT_TRIGOUT_TYPES "arm,trig-out-types" +#define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters" +#define CTI_DT_CONN_NAME "arm,trig-conn-name"
I will skip this patch while we don't have a firm idea of how we will represent CTI connections. It may be that we expand the graph binding for data component or come up with a whole new set, like this set does. There are pros and cons with both approach and I want the group to have a dicussion before moving forward.
#ifdef CONFIG_OF @@ -210,6 +219,181 @@ static int of_cti_create_v8_connections(struct cti_drvdata *drvdata, return -ENOMEM; } +static int of_cti_read_trig_group(struct cti_trig_grp **trig_grp_ptr,
struct device_node *np,
const char *grp_name,
int max_trigs)
+{
- int items, err = 0;
- u32 value, pidx;
- struct cti_trig_grp *grp;
- grp = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!grp)
return -ENOMEM;
- *trig_grp_ptr = grp;
- items = of_property_count_elems_of_size(np, grp_name, 4);
- /* if the property doesn't exist or has no values, then return
* an empty connection group
*/
- if (items < 0)
return 0;
- if (items > max_trigs)
return -EINVAL;
- /* set the number of signals and usage mask */
- for (pidx = 0; pidx < items; pidx++) {
err = of_property_read_u32_index(np, grp_name, pidx, &value);
if (err)
return err;
grp->nr_sigs++;
grp->used_mask |= (0x1 << value);
- }
- return 0;
+}
+static int of_cti_read_trig_types(struct cti_trig_grp *trig_grp_ptr,
struct device_node *np,
const char *type_name)
+{
- int items, used = 0, err = 0, nr_sigs;
- u32 value, i;
- /* allocate an array according to number of signals in connection */
- nr_sigs = trig_grp_ptr->nr_sigs;
- if (!nr_sigs)
return 0;
- trig_grp_ptr->sig_types = kzalloc(sizeof(int) * nr_sigs, GFP_KERNEL);
- if (!trig_grp_ptr->sig_types)
return -ENOMEM;
- /* see if any types have been included in the device description */
- items = of_property_count_elems_of_size(np, type_name, 4);
- /*
* Match type id to signal index,
* too few - default to genio,
* too many - ignore excess.
*/
- for (i = 0; i < nr_sigs; i++) {
if (used < items) {
err = of_property_read_u32_index(np, type_name,
i, &value);
if (!err)
trig_grp_ptr->sig_types[i] = value;
else
trig_grp_ptr->sig_types[i] = CTITRIG_GEN_IO;
used++;
}
else
trig_grp_ptr->sig_types[i] = CTITRIG_GEN_IO;
- }
- return 0;
+}
+static void of_cti_free_trig_grp(struct cti_trig_grp *grp) +{
- if (grp) {
kfree(grp->sig_types);
kfree(grp);
- }
+}
+static int of_cti_create_connection(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
- struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
- int cpuid = -1, err = 0, trig_max = drvdata->config.nr_trig_max;
- struct device_node *cs_np;
- struct coresight_device *csdev = NULL;
- const char *assoc_name = "unknown";
- char cpu_name_str[16];
- /* look for the signals properties. */
- err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max);
- if (err)
goto of_create_con_err;
- err = of_cti_read_trig_types(in, np, CTI_DT_TRIGIN_TYPES);
- if (err)
goto of_create_con_err;
- err = of_cti_read_trig_group(&out, np, CTI_DT_TRIGOUT_SIGS, trig_max);
- if (err)
goto of_create_con_err;
- err = of_cti_read_trig_types(out, np, CTI_DT_TRIGOUT_TYPES);
- if (err)
goto of_create_con_err;
- err = of_cti_read_trig_group(&filter, np, CTI_DT_FILTER_OUT_SIGS,
trig_max);
- if (err)
goto of_create_con_err;
- /* read the connection name if set - may be overridden by later */
- of_property_read_string(np, CTI_DT_CONN_NAME, &assoc_name);
- /* associated cpu ? */
- cpuid = of_cti_get_cpu(np);
- if (cpuid >= 0) {
drvdata->ctidev.cpu = cpuid;
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
assoc_name = cpu_name_str;
- } else {
/* associated device ? */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
csdev = of_cti_get_assoc_cs_dev_by_node(cs_np);
if (csdev) /* use device name if csdev found */
assoc_name = dev_name(&csdev->dev);
else /* otherwise node name for later association */
assoc_name = cs_np->full_name;
of_node_put(cs_np);
}
- }
- /* set up a connection */
- err = cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
- if (err)
goto of_create_con_err;
- /* note any filter info */
- drvdata->config.trig_out_filter |= filter->used_mask;
- kfree(filter);
- return err;
+of_create_con_err:
- of_cti_free_trig_grp(in);
- of_cti_free_trig_grp(out);
- of_cti_free_trig_grp(filter);
- return err;
+}
+static int of_cti_create_impdef_connections(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
- int rc = 0;
- struct device_node *nc = NULL;
- for_each_child_of_node(np, nc) {
if (of_node_cmp(nc->name, CTI_DT_CONNS) != 0)
continue;
rc = of_cti_create_connection(dev, nc, drvdata);
if (rc != 0)
goto of_cti_impdef_create_err;
- }
- return rc;
+of_cti_impdef_create_err:
- /* last create should have freed its mem - unpick previous */
- cti_free_conn_info(drvdata);
- return rc;
+}
/* get the hardware configuration & connection data. */ int of_cti_get_hw_data(struct device *dev, struct device_node *np, @@ -221,9 +405,14 @@ int of_cti_get_hw_data(struct device *dev, /* get any CTM ID - defaults to 0 */ of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
- /* check for a v8 architectural CTI device */
- /*
* Check for a v8 architectural CTI device,
* otherwise implementation defined.
if (of_property_read_bool(np, CTI_DT_V8ARCH)) rc = of_cti_create_v8_connections(drvdata, np);*/
- else
rc = of_cti_create_impdef_connections(dev, np, drvdata);
/* if no connections, just add a single default based on max IN-OUT */ if (!rc && (cti_dev->nr_trig_con == 0)) -- 2.20.1
Hi Mathieu,
On Fri, 10 May 2019 at 18:26, Mathieu Poirier mathieu.poirier@linaro.org wrote:
For the patch subject line in this set you can drop "drivers".
On Wed, May 01, 2019 at 09:49:36AM +0100, Mike Leach wrote:
Adds support for CTIs that are implementation defined at design time, and not constrained by v8 architecture.
Changelog descriptions needs to be line wrapped at 75 characters.
These CTIs have no standard connection setup, all the settings have to be defined in the device tree files. The patch creates a set of connections and trigger signals based on the information provided,
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.h | 1 + .../hwtracing/coresight/of_coresight-cti.c | 191 +++++++++++++++++- 2 files changed, 191 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index df40ca2d9548..7342f998f4c5 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -239,6 +239,7 @@ int cti_add_connection_entry(struct cti_drvdata *drvdata, struct cti_trig_grp *out_info, struct coresight_device *csdev, const char *assoc_dev_name); +void cti_free_conn_info(struct cti_drvdata *drvdata); int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); void cti_write_all_hw_regs(struct cti_drvdata *drvdata); diff --git a/drivers/hwtracing/coresight/of_coresight-cti.c b/drivers/hwtracing/coresight/of_coresight-cti.c index ee9a5906dc37..486fa99813ae 100644 --- a/drivers/hwtracing/coresight/of_coresight-cti.c +++ b/drivers/hwtracing/coresight/of_coresight-cti.c @@ -23,10 +23,19 @@ #define NR_V8PE_OUT_SIGS 3 #define NR_V8ETM_INOUT_SIGS 4
+/* CTI device tree trigger connection node keyword */ +#define CTI_DT_CONNS "trig-conns"
/* CTI device tree connection property keywords */ #define CTI_DT_V8ARCH "arm,cti-v8-arch" #define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" #define CTI_DT_CTM_ID "arm,cti-ctm-id" +#define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs" +#define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs" +#define CTI_DT_TRIGIN_TYPES "arm,trig-in-types" +#define CTI_DT_TRIGOUT_TYPES "arm,trig-out-types" +#define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters" +#define CTI_DT_CONN_NAME "arm,trig-conn-name"
I will skip this patch while we don't have a firm idea of how we will represent CTI connections. It may be that we expand the graph binding for data component or come up with a whole new set, like this set does. There are pros and cons with both approach and I want the group to have a dicussion before moving forward.
Ok. But as I have mentioned before, the existing graph bindings have a built in assumption that all connections are coresight devices, and all the data is trace data. Graph bindings are not used between CPU and ETM - in fact there is no indication other than the cpu id that the connection exists at all. The information supplied here is that which is required to know how to program up the CTI device. Signal connection information alongside the device association information is a vital part of that. CPU=>ETM data flow is implied, CPU<=>CTI data flow needs to be explicit.
Any extension to the existing data path binding infrastructure needs so account for:- a) CoreSight devices - these are easy - we can put in / out nodes on both the device and CTI (given signal is bi-directional) and write coresight driver code to handle this/ b) non-Coresight devices that have an entry in the device tree (including CPUs) - can't really re-write arbitrary drivers to handle in / out nodes from coresight CTI, so this would have to be single ended. c) Any other device / IO pins that can connect but my not have a current entry in the device tree. Again this needs to be single ended. (for reference Juno has examples in all the above categories - so these are not hypothetical cases).
Using self contained connection information in the CTI device avoids having to add multiple in / out ports to lots of existing CoreSight definitions. For example, CTI0 connects to STM, TPIU, ETR and ETF0 - all bi-directionally. so thats 4 x in, 4 x out nodes on the CTI, plus (1 x in, 1 x out) x 4 devices. Then we have a choice as to where to put the signal information - at source, at sink, all on the associated device - all at the CTI as now.
I am happy to consider suggestions as to a better way to do this, but I have considered and did try to force the existing data graph bindings to work. I simply wasn't happy with the direction it was going in.
Thanks
Mike
#ifdef CONFIG_OF
@@ -210,6 +219,181 @@ static int of_cti_create_v8_connections(struct cti_drvdata *drvdata, return -ENOMEM; }
+static int of_cti_read_trig_group(struct cti_trig_grp **trig_grp_ptr,
struct device_node *np,
const char *grp_name,
int max_trigs)
+{
int items, err = 0;
u32 value, pidx;
struct cti_trig_grp *grp;
grp = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!grp)
return -ENOMEM;
*trig_grp_ptr = grp;
items = of_property_count_elems_of_size(np, grp_name, 4);
/* if the property doesn't exist or has no values, then return
* an empty connection group
*/
if (items < 0)
return 0;
if (items > max_trigs)
return -EINVAL;
/* set the number of signals and usage mask */
for (pidx = 0; pidx < items; pidx++) {
err = of_property_read_u32_index(np, grp_name, pidx, &value);
if (err)
return err;
grp->nr_sigs++;
grp->used_mask |= (0x1 << value);
}
return 0;
+}
+static int of_cti_read_trig_types(struct cti_trig_grp *trig_grp_ptr,
struct device_node *np,
const char *type_name)
+{
int items, used = 0, err = 0, nr_sigs;
u32 value, i;
/* allocate an array according to number of signals in connection */
nr_sigs = trig_grp_ptr->nr_sigs;
if (!nr_sigs)
return 0;
trig_grp_ptr->sig_types = kzalloc(sizeof(int) * nr_sigs, GFP_KERNEL);
if (!trig_grp_ptr->sig_types)
return -ENOMEM;
/* see if any types have been included in the device description */
items = of_property_count_elems_of_size(np, type_name, 4);
/*
* Match type id to signal index,
* too few - default to genio,
* too many - ignore excess.
*/
for (i = 0; i < nr_sigs; i++) {
if (used < items) {
err = of_property_read_u32_index(np, type_name,
i, &value);
if (!err)
trig_grp_ptr->sig_types[i] = value;
else
trig_grp_ptr->sig_types[i] = CTITRIG_GEN_IO;
used++;
}
else
trig_grp_ptr->sig_types[i] = CTITRIG_GEN_IO;
}
return 0;
+}
+static void of_cti_free_trig_grp(struct cti_trig_grp *grp) +{
if (grp) {
kfree(grp->sig_types);
kfree(grp);
}
+}
+static int of_cti_create_connection(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
int cpuid = -1, err = 0, trig_max = drvdata->config.nr_trig_max;
struct device_node *cs_np;
struct coresight_device *csdev = NULL;
const char *assoc_name = "unknown";
char cpu_name_str[16];
/* look for the signals properties. */
err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max);
if (err)
goto of_create_con_err;
err = of_cti_read_trig_types(in, np, CTI_DT_TRIGIN_TYPES);
if (err)
goto of_create_con_err;
err = of_cti_read_trig_group(&out, np, CTI_DT_TRIGOUT_SIGS, trig_max);
if (err)
goto of_create_con_err;
err = of_cti_read_trig_types(out, np, CTI_DT_TRIGOUT_TYPES);
if (err)
goto of_create_con_err;
err = of_cti_read_trig_group(&filter, np, CTI_DT_FILTER_OUT_SIGS,
trig_max);
if (err)
goto of_create_con_err;
/* read the connection name if set - may be overridden by later */
of_property_read_string(np, CTI_DT_CONN_NAME, &assoc_name);
/* associated cpu ? */
cpuid = of_cti_get_cpu(np);
if (cpuid >= 0) {
drvdata->ctidev.cpu = cpuid;
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
assoc_name = cpu_name_str;
} else {
/* associated device ? */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
csdev = of_cti_get_assoc_cs_dev_by_node(cs_np);
if (csdev) /* use device name if csdev found */
assoc_name = dev_name(&csdev->dev);
else /* otherwise node name for later association */
assoc_name = cs_np->full_name;
of_node_put(cs_np);
}
}
/* set up a connection */
err = cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
if (err)
goto of_create_con_err;
/* note any filter info */
drvdata->config.trig_out_filter |= filter->used_mask;
kfree(filter);
return err;
+of_create_con_err:
of_cti_free_trig_grp(in);
of_cti_free_trig_grp(out);
of_cti_free_trig_grp(filter);
return err;
+}
+static int of_cti_create_impdef_connections(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
int rc = 0;
struct device_node *nc = NULL;
for_each_child_of_node(np, nc) {
if (of_node_cmp(nc->name, CTI_DT_CONNS) != 0)
continue;
rc = of_cti_create_connection(dev, nc, drvdata);
if (rc != 0)
goto of_cti_impdef_create_err;
}
return rc;
+of_cti_impdef_create_err:
/* last create should have freed its mem - unpick previous */
cti_free_conn_info(drvdata);
return rc;
+}
/* get the hardware configuration & connection data. */ int of_cti_get_hw_data(struct device *dev, struct device_node *np, @@ -221,9 +405,14 @@ int of_cti_get_hw_data(struct device *dev, /* get any CTM ID - defaults to 0 */ of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
/* check for a v8 architectural CTI device */
/*
* Check for a v8 architectural CTI device,
* otherwise implementation defined.
*/ if (of_property_read_bool(np, CTI_DT_V8ARCH)) rc = of_cti_create_v8_connections(drvdata, np);
else
rc = of_cti_create_impdef_connections(dev, np, drvdata); /* if no connections, just add a single default based on max IN-OUT */ if (!rc && (cti_dev->nr_trig_con == 0))
-- 2.20.1
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
Hi Mike,
Apologies on the late reply - I'm running out of cycles.
On Wed, 29 May 2019 at 06:17, Mike Leach mike.leach@linaro.org wrote:
Hi Mathieu,
On Fri, 10 May 2019 at 18:26, Mathieu Poirier mathieu.poirier@linaro.org wrote:
For the patch subject line in this set you can drop "drivers".
On Wed, May 01, 2019 at 09:49:36AM +0100, Mike Leach wrote:
Adds support for CTIs that are implementation defined at design time, and not constrained by v8 architecture.
Changelog descriptions needs to be line wrapped at 75 characters.
These CTIs have no standard connection setup, all the settings have to be defined in the device tree files. The patch creates a set of connections and trigger signals based on the information provided,
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.h | 1 + .../hwtracing/coresight/of_coresight-cti.c | 191 +++++++++++++++++- 2 files changed, 191 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index df40ca2d9548..7342f998f4c5 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -239,6 +239,7 @@ int cti_add_connection_entry(struct cti_drvdata *drvdata, struct cti_trig_grp *out_info, struct coresight_device *csdev, const char *assoc_dev_name); +void cti_free_conn_info(struct cti_drvdata *drvdata); int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); void cti_write_all_hw_regs(struct cti_drvdata *drvdata); diff --git a/drivers/hwtracing/coresight/of_coresight-cti.c b/drivers/hwtracing/coresight/of_coresight-cti.c index ee9a5906dc37..486fa99813ae 100644 --- a/drivers/hwtracing/coresight/of_coresight-cti.c +++ b/drivers/hwtracing/coresight/of_coresight-cti.c @@ -23,10 +23,19 @@ #define NR_V8PE_OUT_SIGS 3 #define NR_V8ETM_INOUT_SIGS 4
+/* CTI device tree trigger connection node keyword */ +#define CTI_DT_CONNS "trig-conns"
/* CTI device tree connection property keywords */ #define CTI_DT_V8ARCH "arm,cti-v8-arch" #define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" #define CTI_DT_CTM_ID "arm,cti-ctm-id" +#define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs" +#define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs" +#define CTI_DT_TRIGIN_TYPES "arm,trig-in-types" +#define CTI_DT_TRIGOUT_TYPES "arm,trig-out-types" +#define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters" +#define CTI_DT_CONN_NAME "arm,trig-conn-name"
I will skip this patch while we don't have a firm idea of how we will represent CTI connections. It may be that we expand the graph binding for data component or come up with a whole new set, like this set does. There are pros and cons with both approach and I want the group to have a dicussion before moving forward.
Ok. But as I have mentioned before, the existing graph bindings have a built in assumption that all connections are coresight devices, and all the data is trace data. Graph bindings are not used between CPU and ETM - in fact there is no indication other than the cpu id that the connection exists at all. The information supplied here is that which is required to know how to program up the CTI device. Signal connection information alongside the device association information is a vital part of that. CPU=>ETM data flow is implied, CPU<=>CTI data flow needs to be explicit.
Any extension to the existing data path binding infrastructure needs so account for:- a) CoreSight devices - these are easy - we can put in / out nodes on both the device and CTI (given signal is bi-directional) and write coresight driver code to handle this/ b) non-Coresight devices that have an entry in the device tree (including CPUs) - can't really re-write arbitrary drivers to handle in / out nodes from coresight CTI, so this would have to be single ended. c) Any other device / IO pins that can connect but my not have a current entry in the device tree. Again this needs to be single ended. (for reference Juno has examples in all the above categories - so these are not hypothetical cases).
Using self contained connection information in the CTI device avoids having to add multiple in / out ports to lots of existing CoreSight definitions. For example, CTI0 connects to STM, TPIU, ETR and ETF0 - all bi-directionally. so thats 4 x in, 4 x out nodes on the CTI, plus (1 x in, 1 x out) x 4 devices. Then we have a choice as to where to put the signal information - at source, at sink, all on the associated device
- all at the CTI as now.
I am happy to consider suggestions as to a better way to do this, but I have considered and did try to force the existing data graph bindings to work. I simply wasn't happy with the direction it was going in.
This is a difficult problem - just like you did I tried expressing a CTI topology using our current graph bindings and things quickly became unmanageable. I am pretty sure that we will have to ask Rob Herring for guidance here. For now I suggest to concentrate on sysfs representation and deal with bindings in the next steps. As such when working on your next iteration please try to keep the code that reads bindings and the rest of the CTI driver as disjoint as possible so that one can be switched without affecting the other. If I remember correctly you're already doing well in that regard.
Also, just like Suzuki did with his ACPI patchset see if you can split your work in two - one part that adds the CTI specific mechanic to probe, parse the DT and setup CTI devices and another to represent connections between signal components. We may simply end up extending the latter to represent connections between data components.
Thanks, Mathieu
Thanks
Mike
#ifdef CONFIG_OF
@@ -210,6 +219,181 @@ static int of_cti_create_v8_connections(struct cti_drvdata *drvdata, return -ENOMEM; }
+static int of_cti_read_trig_group(struct cti_trig_grp **trig_grp_ptr,
struct device_node *np,
const char *grp_name,
int max_trigs)
+{
int items, err = 0;
u32 value, pidx;
struct cti_trig_grp *grp;
grp = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!grp)
return -ENOMEM;
*trig_grp_ptr = grp;
items = of_property_count_elems_of_size(np, grp_name, 4);
/* if the property doesn't exist or has no values, then return
* an empty connection group
*/
if (items < 0)
return 0;
if (items > max_trigs)
return -EINVAL;
/* set the number of signals and usage mask */
for (pidx = 0; pidx < items; pidx++) {
err = of_property_read_u32_index(np, grp_name, pidx, &value);
if (err)
return err;
grp->nr_sigs++;
grp->used_mask |= (0x1 << value);
}
return 0;
+}
+static int of_cti_read_trig_types(struct cti_trig_grp *trig_grp_ptr,
struct device_node *np,
const char *type_name)
+{
int items, used = 0, err = 0, nr_sigs;
u32 value, i;
/* allocate an array according to number of signals in connection */
nr_sigs = trig_grp_ptr->nr_sigs;
if (!nr_sigs)
return 0;
trig_grp_ptr->sig_types = kzalloc(sizeof(int) * nr_sigs, GFP_KERNEL);
if (!trig_grp_ptr->sig_types)
return -ENOMEM;
/* see if any types have been included in the device description */
items = of_property_count_elems_of_size(np, type_name, 4);
/*
* Match type id to signal index,
* too few - default to genio,
* too many - ignore excess.
*/
for (i = 0; i < nr_sigs; i++) {
if (used < items) {
err = of_property_read_u32_index(np, type_name,
i, &value);
if (!err)
trig_grp_ptr->sig_types[i] = value;
else
trig_grp_ptr->sig_types[i] = CTITRIG_GEN_IO;
used++;
}
else
trig_grp_ptr->sig_types[i] = CTITRIG_GEN_IO;
}
return 0;
+}
+static void of_cti_free_trig_grp(struct cti_trig_grp *grp) +{
if (grp) {
kfree(grp->sig_types);
kfree(grp);
}
+}
+static int of_cti_create_connection(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
int cpuid = -1, err = 0, trig_max = drvdata->config.nr_trig_max;
struct device_node *cs_np;
struct coresight_device *csdev = NULL;
const char *assoc_name = "unknown";
char cpu_name_str[16];
/* look for the signals properties. */
err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max);
if (err)
goto of_create_con_err;
err = of_cti_read_trig_types(in, np, CTI_DT_TRIGIN_TYPES);
if (err)
goto of_create_con_err;
err = of_cti_read_trig_group(&out, np, CTI_DT_TRIGOUT_SIGS, trig_max);
if (err)
goto of_create_con_err;
err = of_cti_read_trig_types(out, np, CTI_DT_TRIGOUT_TYPES);
if (err)
goto of_create_con_err;
err = of_cti_read_trig_group(&filter, np, CTI_DT_FILTER_OUT_SIGS,
trig_max);
if (err)
goto of_create_con_err;
/* read the connection name if set - may be overridden by later */
of_property_read_string(np, CTI_DT_CONN_NAME, &assoc_name);
/* associated cpu ? */
cpuid = of_cti_get_cpu(np);
if (cpuid >= 0) {
drvdata->ctidev.cpu = cpuid;
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
assoc_name = cpu_name_str;
} else {
/* associated device ? */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
csdev = of_cti_get_assoc_cs_dev_by_node(cs_np);
if (csdev) /* use device name if csdev found */
assoc_name = dev_name(&csdev->dev);
else /* otherwise node name for later association */
assoc_name = cs_np->full_name;
of_node_put(cs_np);
}
}
/* set up a connection */
err = cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
if (err)
goto of_create_con_err;
/* note any filter info */
drvdata->config.trig_out_filter |= filter->used_mask;
kfree(filter);
return err;
+of_create_con_err:
of_cti_free_trig_grp(in);
of_cti_free_trig_grp(out);
of_cti_free_trig_grp(filter);
return err;
+}
+static int of_cti_create_impdef_connections(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
int rc = 0;
struct device_node *nc = NULL;
for_each_child_of_node(np, nc) {
if (of_node_cmp(nc->name, CTI_DT_CONNS) != 0)
continue;
rc = of_cti_create_connection(dev, nc, drvdata);
if (rc != 0)
goto of_cti_impdef_create_err;
}
return rc;
+of_cti_impdef_create_err:
/* last create should have freed its mem - unpick previous */
cti_free_conn_info(drvdata);
return rc;
+}
/* get the hardware configuration & connection data. */ int of_cti_get_hw_data(struct device *dev, struct device_node *np, @@ -221,9 +405,14 @@ int of_cti_get_hw_data(struct device *dev, /* get any CTM ID - defaults to 0 */ of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
/* check for a v8 architectural CTI device */
/*
* Check for a v8 architectural CTI device,
* otherwise implementation defined.
*/ if (of_property_read_bool(np, CTI_DT_V8ARCH)) rc = of_cti_create_v8_connections(drvdata, np);
else
rc = of_cti_create_impdef_connections(dev, np, drvdata); /* if no connections, just add a single default based on max IN-OUT */ if (!rc && (cti_dev->nr_trig_con == 0))
-- 2.20.1
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
HI Mathieu,
Spotted this just after I pressed the go button on the next revision. However the conversation doesn't change....
On Wed, 5 Jun 2019 at 18:05, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Hi Mike,
Apologies on the late reply - I'm running out of cycles.
On Wed, 29 May 2019 at 06:17, Mike Leach mike.leach@linaro.org wrote:
Hi Mathieu,
On Fri, 10 May 2019 at 18:26, Mathieu Poirier mathieu.poirier@linaro.org wrote:
For the patch subject line in this set you can drop "drivers".
On Wed, May 01, 2019 at 09:49:36AM +0100, Mike Leach wrote:
Adds support for CTIs that are implementation defined at design time, and not constrained by v8 architecture.
Changelog descriptions needs to be line wrapped at 75 characters.
These CTIs have no standard connection setup, all the settings have to be defined in the device tree files. The patch creates a set of connections and trigger signals based on the information provided,
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.h | 1 + .../hwtracing/coresight/of_coresight-cti.c | 191 +++++++++++++++++- 2 files changed, 191 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index df40ca2d9548..7342f998f4c5 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -239,6 +239,7 @@ int cti_add_connection_entry(struct cti_drvdata *drvdata, struct cti_trig_grp *out_info, struct coresight_device *csdev, const char *assoc_dev_name); +void cti_free_conn_info(struct cti_drvdata *drvdata); int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); void cti_write_all_hw_regs(struct cti_drvdata *drvdata); diff --git a/drivers/hwtracing/coresight/of_coresight-cti.c b/drivers/hwtracing/coresight/of_coresight-cti.c index ee9a5906dc37..486fa99813ae 100644 --- a/drivers/hwtracing/coresight/of_coresight-cti.c +++ b/drivers/hwtracing/coresight/of_coresight-cti.c @@ -23,10 +23,19 @@ #define NR_V8PE_OUT_SIGS 3 #define NR_V8ETM_INOUT_SIGS 4
+/* CTI device tree trigger connection node keyword */ +#define CTI_DT_CONNS "trig-conns"
/* CTI device tree connection property keywords */ #define CTI_DT_V8ARCH "arm,cti-v8-arch" #define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" #define CTI_DT_CTM_ID "arm,cti-ctm-id" +#define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs" +#define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs" +#define CTI_DT_TRIGIN_TYPES "arm,trig-in-types" +#define CTI_DT_TRIGOUT_TYPES "arm,trig-out-types" +#define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters" +#define CTI_DT_CONN_NAME "arm,trig-conn-name"
I will skip this patch while we don't have a firm idea of how we will represent CTI connections. It may be that we expand the graph binding for data component or come up with a whole new set, like this set does. There are pros and cons with both approach and I want the group to have a dicussion before moving forward.
Ok. But as I have mentioned before, the existing graph bindings have a built in assumption that all connections are coresight devices, and all the data is trace data. Graph bindings are not used between CPU and ETM - in fact there is no indication other than the cpu id that the connection exists at all. The information supplied here is that which is required to know how to program up the CTI device. Signal connection information alongside the device association information is a vital part of that. CPU=>ETM data flow is implied, CPU<=>CTI data flow needs to be explicit.
Any extension to the existing data path binding infrastructure needs so account for:- a) CoreSight devices - these are easy - we can put in / out nodes on both the device and CTI (given signal is bi-directional) and write coresight driver code to handle this/ b) non-Coresight devices that have an entry in the device tree (including CPUs) - can't really re-write arbitrary drivers to handle in / out nodes from coresight CTI, so this would have to be single ended. c) Any other device / IO pins that can connect but my not have a current entry in the device tree. Again this needs to be single ended. (for reference Juno has examples in all the above categories - so these are not hypothetical cases).
Using self contained connection information in the CTI device avoids having to add multiple in / out ports to lots of existing CoreSight definitions. For example, CTI0 connects to STM, TPIU, ETR and ETF0 - all bi-directionally. so thats 4 x in, 4 x out nodes on the CTI, plus (1 x in, 1 x out) x 4 devices. Then we have a choice as to where to put the signal information - at source, at sink, all on the associated device
- all at the CTI as now.
I am happy to consider suggestions as to a better way to do this, but I have considered and did try to force the existing data graph bindings to work. I simply wasn't happy with the direction it was going in.
This is a difficult problem - just like you did I tried expressing a CTI topology using our current graph bindings and things quickly became unmanageable. I am pretty sure that we will have to ask Rob Herring for guidance here. For now I suggest to concentrate on sysfs representation and deal with bindings in the next steps. As such when working on your next iteration please try to keep the code that reads bindings and the rest of the CTI driver as disjoint as possible so that one can be switched without affecting the other. If I remember correctly you're already doing well in that regard.
Indeed - the next set is platform agnostic & uses fwnodes, so it plays well with the APCI set on coresight/next. All the device tree stuff is now in the renamed coresight-cti-platform file, with a neutral API and room to add ACPI later.
Also, just like Suzuki did with his ACPI patchset see if you can split your work in two - one part that adds the CTI specific mechanic to probe, parse the DT and setup CTI devices and another to represent connections between signal components. We may simply end up extending the latter to represent connections between data components.
The key issue here is that it is very easy to program an ETM without knowing anything about what it is connected to. This programming task is self contained and independent of anything that might be in the connection information.
The CTI is complelely different in this regard. The connection information - particularly the trigger signal definitions are essential for the correct programming of the device. These affect both how the driver itself operates, and how any user will gain the insight required to program it. I am not sure there is such a clean distinction between CTI specifics and connections - programming the CTI directly interconnects the signals defined in these connections.
Regards
Mike
Thanks, Mathieu
Thanks
Mike
#ifdef CONFIG_OF
@@ -210,6 +219,181 @@ static int of_cti_create_v8_connections(struct cti_drvdata *drvdata, return -ENOMEM; }
+static int of_cti_read_trig_group(struct cti_trig_grp **trig_grp_ptr,
struct device_node *np,
const char *grp_name,
int max_trigs)
+{
int items, err = 0;
u32 value, pidx;
struct cti_trig_grp *grp;
grp = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!grp)
return -ENOMEM;
*trig_grp_ptr = grp;
items = of_property_count_elems_of_size(np, grp_name, 4);
/* if the property doesn't exist or has no values, then return
* an empty connection group
*/
if (items < 0)
return 0;
if (items > max_trigs)
return -EINVAL;
/* set the number of signals and usage mask */
for (pidx = 0; pidx < items; pidx++) {
err = of_property_read_u32_index(np, grp_name, pidx, &value);
if (err)
return err;
grp->nr_sigs++;
grp->used_mask |= (0x1 << value);
}
return 0;
+}
+static int of_cti_read_trig_types(struct cti_trig_grp *trig_grp_ptr,
struct device_node *np,
const char *type_name)
+{
int items, used = 0, err = 0, nr_sigs;
u32 value, i;
/* allocate an array according to number of signals in connection */
nr_sigs = trig_grp_ptr->nr_sigs;
if (!nr_sigs)
return 0;
trig_grp_ptr->sig_types = kzalloc(sizeof(int) * nr_sigs, GFP_KERNEL);
if (!trig_grp_ptr->sig_types)
return -ENOMEM;
/* see if any types have been included in the device description */
items = of_property_count_elems_of_size(np, type_name, 4);
/*
* Match type id to signal index,
* too few - default to genio,
* too many - ignore excess.
*/
for (i = 0; i < nr_sigs; i++) {
if (used < items) {
err = of_property_read_u32_index(np, type_name,
i, &value);
if (!err)
trig_grp_ptr->sig_types[i] = value;
else
trig_grp_ptr->sig_types[i] = CTITRIG_GEN_IO;
used++;
}
else
trig_grp_ptr->sig_types[i] = CTITRIG_GEN_IO;
}
return 0;
+}
+static void of_cti_free_trig_grp(struct cti_trig_grp *grp) +{
if (grp) {
kfree(grp->sig_types);
kfree(grp);
}
+}
+static int of_cti_create_connection(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
int cpuid = -1, err = 0, trig_max = drvdata->config.nr_trig_max;
struct device_node *cs_np;
struct coresight_device *csdev = NULL;
const char *assoc_name = "unknown";
char cpu_name_str[16];
/* look for the signals properties. */
err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max);
if (err)
goto of_create_con_err;
err = of_cti_read_trig_types(in, np, CTI_DT_TRIGIN_TYPES);
if (err)
goto of_create_con_err;
err = of_cti_read_trig_group(&out, np, CTI_DT_TRIGOUT_SIGS, trig_max);
if (err)
goto of_create_con_err;
err = of_cti_read_trig_types(out, np, CTI_DT_TRIGOUT_TYPES);
if (err)
goto of_create_con_err;
err = of_cti_read_trig_group(&filter, np, CTI_DT_FILTER_OUT_SIGS,
trig_max);
if (err)
goto of_create_con_err;
/* read the connection name if set - may be overridden by later */
of_property_read_string(np, CTI_DT_CONN_NAME, &assoc_name);
/* associated cpu ? */
cpuid = of_cti_get_cpu(np);
if (cpuid >= 0) {
drvdata->ctidev.cpu = cpuid;
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
assoc_name = cpu_name_str;
} else {
/* associated device ? */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
csdev = of_cti_get_assoc_cs_dev_by_node(cs_np);
if (csdev) /* use device name if csdev found */
assoc_name = dev_name(&csdev->dev);
else /* otherwise node name for later association */
assoc_name = cs_np->full_name;
of_node_put(cs_np);
}
}
/* set up a connection */
err = cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
if (err)
goto of_create_con_err;
/* note any filter info */
drvdata->config.trig_out_filter |= filter->used_mask;
kfree(filter);
return err;
+of_create_con_err:
of_cti_free_trig_grp(in);
of_cti_free_trig_grp(out);
of_cti_free_trig_grp(filter);
return err;
+}
+static int of_cti_create_impdef_connections(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
int rc = 0;
struct device_node *nc = NULL;
for_each_child_of_node(np, nc) {
if (of_node_cmp(nc->name, CTI_DT_CONNS) != 0)
continue;
rc = of_cti_create_connection(dev, nc, drvdata);
if (rc != 0)
goto of_cti_impdef_create_err;
}
return rc;
+of_cti_impdef_create_err:
/* last create should have freed its mem - unpick previous */
cti_free_conn_info(drvdata);
return rc;
+}
/* get the hardware configuration & connection data. */ int of_cti_get_hw_data(struct device *dev, struct device_node *np, @@ -221,9 +405,14 @@ int of_cti_get_hw_data(struct device *dev, /* get any CTM ID - defaults to 0 */ of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
/* check for a v8 architectural CTI device */
/*
* Check for a v8 architectural CTI device,
* otherwise implementation defined.
*/ if (of_property_read_bool(np, CTI_DT_V8ARCH)) rc = of_cti_create_v8_connections(drvdata, np);
else
rc = of_cti_create_impdef_connections(dev, np, drvdata); /* if no connections, just add a single default based on max IN-OUT */ if (!rc && (cti_dev->nr_trig_con == 0))
-- 2.20.1
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
On Wed, 5 Jun 2019 at 13:34, Mike Leach mike.leach@linaro.org wrote:
HI Mathieu,
Spotted this just after I pressed the go button on the next revision.
That's what I thought when I saw your patches rolling in hours after my email.
However the conversation doesn't change....
On Wed, 5 Jun 2019 at 18:05, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Hi Mike,
Apologies on the late reply - I'm running out of cycles.
On Wed, 29 May 2019 at 06:17, Mike Leach mike.leach@linaro.org wrote:
Hi Mathieu,
On Fri, 10 May 2019 at 18:26, Mathieu Poirier mathieu.poirier@linaro.org wrote:
For the patch subject line in this set you can drop "drivers".
On Wed, May 01, 2019 at 09:49:36AM +0100, Mike Leach wrote:
Adds support for CTIs that are implementation defined at design time, and not constrained by v8 architecture.
Changelog descriptions needs to be line wrapped at 75 characters.
These CTIs have no standard connection setup, all the settings have to be defined in the device tree files. The patch creates a set of connections and trigger signals based on the information provided,
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.h | 1 + .../hwtracing/coresight/of_coresight-cti.c | 191 +++++++++++++++++- 2 files changed, 191 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index df40ca2d9548..7342f998f4c5 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -239,6 +239,7 @@ int cti_add_connection_entry(struct cti_drvdata *drvdata, struct cti_trig_grp *out_info, struct coresight_device *csdev, const char *assoc_dev_name); +void cti_free_conn_info(struct cti_drvdata *drvdata); int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); void cti_write_all_hw_regs(struct cti_drvdata *drvdata); diff --git a/drivers/hwtracing/coresight/of_coresight-cti.c b/drivers/hwtracing/coresight/of_coresight-cti.c index ee9a5906dc37..486fa99813ae 100644 --- a/drivers/hwtracing/coresight/of_coresight-cti.c +++ b/drivers/hwtracing/coresight/of_coresight-cti.c @@ -23,10 +23,19 @@ #define NR_V8PE_OUT_SIGS 3 #define NR_V8ETM_INOUT_SIGS 4
+/* CTI device tree trigger connection node keyword */ +#define CTI_DT_CONNS "trig-conns"
/* CTI device tree connection property keywords */ #define CTI_DT_V8ARCH "arm,cti-v8-arch" #define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" #define CTI_DT_CTM_ID "arm,cti-ctm-id" +#define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs" +#define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs" +#define CTI_DT_TRIGIN_TYPES "arm,trig-in-types" +#define CTI_DT_TRIGOUT_TYPES "arm,trig-out-types" +#define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters" +#define CTI_DT_CONN_NAME "arm,trig-conn-name"
I will skip this patch while we don't have a firm idea of how we will represent CTI connections. It may be that we expand the graph binding for data component or come up with a whole new set, like this set does. There are pros and cons with both approach and I want the group to have a dicussion before moving forward.
Ok. But as I have mentioned before, the existing graph bindings have a built in assumption that all connections are coresight devices, and all the data is trace data. Graph bindings are not used between CPU and ETM - in fact there is no indication other than the cpu id that the connection exists at all. The information supplied here is that which is required to know how to program up the CTI device. Signal connection information alongside the device association information is a vital part of that. CPU=>ETM data flow is implied, CPU<=>CTI data flow needs to be explicit.
Any extension to the existing data path binding infrastructure needs so account for:- a) CoreSight devices - these are easy - we can put in / out nodes on both the device and CTI (given signal is bi-directional) and write coresight driver code to handle this/ b) non-Coresight devices that have an entry in the device tree (including CPUs) - can't really re-write arbitrary drivers to handle in / out nodes from coresight CTI, so this would have to be single ended. c) Any other device / IO pins that can connect but my not have a current entry in the device tree. Again this needs to be single ended. (for reference Juno has examples in all the above categories - so these are not hypothetical cases).
Using self contained connection information in the CTI device avoids having to add multiple in / out ports to lots of existing CoreSight definitions. For example, CTI0 connects to STM, TPIU, ETR and ETF0 - all bi-directionally. so thats 4 x in, 4 x out nodes on the CTI, plus (1 x in, 1 x out) x 4 devices. Then we have a choice as to where to put the signal information - at source, at sink, all on the associated device
- all at the CTI as now.
I am happy to consider suggestions as to a better way to do this, but I have considered and did try to force the existing data graph bindings to work. I simply wasn't happy with the direction it was going in.
This is a difficult problem - just like you did I tried expressing a CTI topology using our current graph bindings and things quickly became unmanageable. I am pretty sure that we will have to ask Rob Herring for guidance here. For now I suggest to concentrate on sysfs representation and deal with bindings in the next steps. As such when working on your next iteration please try to keep the code that reads bindings and the rest of the CTI driver as disjoint as possible so that one can be switched without affecting the other. If I remember correctly you're already doing well in that regard.
Indeed - the next set is platform agnostic & uses fwnodes, so it plays well with the APCI set on coresight/next. All the device tree stuff is now in the renamed coresight-cti-platform file, with a neutral API and room to add ACPI later.
Excellent
Also, just like Suzuki did with his ACPI patchset see if you can split your work in two - one part that adds the CTI specific mechanic to probe, parse the DT and setup CTI devices and another to represent connections between signal components. We may simply end up extending the latter to represent connections between data components.
The key issue here is that it is very easy to program an ETM without knowing anything about what it is connected to. This programming task is self contained and independent of anything that might be in the connection information.
The CTI is complelely different in this regard. The connection information - particularly the trigger signal definitions are essential for the correct programming of the device. These affect both how the driver itself operates, and how any user will gain the insight required to program it. I am not sure there is such a clean distinction between CTI specifics and connections - programming the CTI directly interconnects the signals defined in these connections.
It is indeed a lot more difficult to program the CTIs without connection information. My goal was to separate the driver and the connection representation, even if it meant that without the second patchset (connection) the first one (CTI driver) is useless.
I will review your set. In the mean time, and if you have time, you could try to build on top of your patchset and add connection representation for the data components. You could start with Suzuki's patchet and substitute calls to his API for yours. Let me know what you decide to do.
Thanks, Mathieu
Regards
Mike
Thanks, Mathieu
Thanks
Mike
#ifdef CONFIG_OF
@@ -210,6 +219,181 @@ static int of_cti_create_v8_connections(struct cti_drvdata *drvdata, return -ENOMEM; }
+static int of_cti_read_trig_group(struct cti_trig_grp **trig_grp_ptr,
struct device_node *np,
const char *grp_name,
int max_trigs)
+{
int items, err = 0;
u32 value, pidx;
struct cti_trig_grp *grp;
grp = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!grp)
return -ENOMEM;
*trig_grp_ptr = grp;
items = of_property_count_elems_of_size(np, grp_name, 4);
/* if the property doesn't exist or has no values, then return
* an empty connection group
*/
if (items < 0)
return 0;
if (items > max_trigs)
return -EINVAL;
/* set the number of signals and usage mask */
for (pidx = 0; pidx < items; pidx++) {
err = of_property_read_u32_index(np, grp_name, pidx, &value);
if (err)
return err;
grp->nr_sigs++;
grp->used_mask |= (0x1 << value);
}
return 0;
+}
+static int of_cti_read_trig_types(struct cti_trig_grp *trig_grp_ptr,
struct device_node *np,
const char *type_name)
+{
int items, used = 0, err = 0, nr_sigs;
u32 value, i;
/* allocate an array according to number of signals in connection */
nr_sigs = trig_grp_ptr->nr_sigs;
if (!nr_sigs)
return 0;
trig_grp_ptr->sig_types = kzalloc(sizeof(int) * nr_sigs, GFP_KERNEL);
if (!trig_grp_ptr->sig_types)
return -ENOMEM;
/* see if any types have been included in the device description */
items = of_property_count_elems_of_size(np, type_name, 4);
/*
* Match type id to signal index,
* too few - default to genio,
* too many - ignore excess.
*/
for (i = 0; i < nr_sigs; i++) {
if (used < items) {
err = of_property_read_u32_index(np, type_name,
i, &value);
if (!err)
trig_grp_ptr->sig_types[i] = value;
else
trig_grp_ptr->sig_types[i] = CTITRIG_GEN_IO;
used++;
}
else
trig_grp_ptr->sig_types[i] = CTITRIG_GEN_IO;
}
return 0;
+}
+static void of_cti_free_trig_grp(struct cti_trig_grp *grp) +{
if (grp) {
kfree(grp->sig_types);
kfree(grp);
}
+}
+static int of_cti_create_connection(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
int cpuid = -1, err = 0, trig_max = drvdata->config.nr_trig_max;
struct device_node *cs_np;
struct coresight_device *csdev = NULL;
const char *assoc_name = "unknown";
char cpu_name_str[16];
/* look for the signals properties. */
err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max);
if (err)
goto of_create_con_err;
err = of_cti_read_trig_types(in, np, CTI_DT_TRIGIN_TYPES);
if (err)
goto of_create_con_err;
err = of_cti_read_trig_group(&out, np, CTI_DT_TRIGOUT_SIGS, trig_max);
if (err)
goto of_create_con_err;
err = of_cti_read_trig_types(out, np, CTI_DT_TRIGOUT_TYPES);
if (err)
goto of_create_con_err;
err = of_cti_read_trig_group(&filter, np, CTI_DT_FILTER_OUT_SIGS,
trig_max);
if (err)
goto of_create_con_err;
/* read the connection name if set - may be overridden by later */
of_property_read_string(np, CTI_DT_CONN_NAME, &assoc_name);
/* associated cpu ? */
cpuid = of_cti_get_cpu(np);
if (cpuid >= 0) {
drvdata->ctidev.cpu = cpuid;
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
assoc_name = cpu_name_str;
} else {
/* associated device ? */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
csdev = of_cti_get_assoc_cs_dev_by_node(cs_np);
if (csdev) /* use device name if csdev found */
assoc_name = dev_name(&csdev->dev);
else /* otherwise node name for later association */
assoc_name = cs_np->full_name;
of_node_put(cs_np);
}
}
/* set up a connection */
err = cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
if (err)
goto of_create_con_err;
/* note any filter info */
drvdata->config.trig_out_filter |= filter->used_mask;
kfree(filter);
return err;
+of_create_con_err:
of_cti_free_trig_grp(in);
of_cti_free_trig_grp(out);
of_cti_free_trig_grp(filter);
return err;
+}
+static int of_cti_create_impdef_connections(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
int rc = 0;
struct device_node *nc = NULL;
for_each_child_of_node(np, nc) {
if (of_node_cmp(nc->name, CTI_DT_CONNS) != 0)
continue;
rc = of_cti_create_connection(dev, nc, drvdata);
if (rc != 0)
goto of_cti_impdef_create_err;
}
return rc;
+of_cti_impdef_create_err:
/* last create should have freed its mem - unpick previous */
cti_free_conn_info(drvdata);
return rc;
+}
/* get the hardware configuration & connection data. */ int of_cti_get_hw_data(struct device *dev, struct device_node *np, @@ -221,9 +405,14 @@ int of_cti_get_hw_data(struct device *dev, /* get any CTM ID - defaults to 0 */ of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
/* check for a v8 architectural CTI device */
/*
* Check for a v8 architectural CTI device,
* otherwise implementation defined.
*/ if (of_property_read_bool(np, CTI_DT_V8ARCH)) rc = of_cti_create_v8_connections(drvdata, np);
else
rc = of_cti_create_impdef_connections(dev, np, drvdata); /* if no connections, just add a single default based on max IN-OUT */ if (!rc && (cti_dev->nr_trig_con == 0))
-- 2.20.1
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
The CoreSight subsystem enables a path of devices from source to sink. Any CTI devices associated with the path devices must be enabled at the same time.
This patch adds an associated device element to the main coresight device structure, and uses this to create associations between the CTI and other devices based on the device tree data. The assoc dev element is used to enable CTI in conjunction with the path elements. CTI are reference counted so where a single CTI is associated with multiple elements on the path, it will be enabled on the first associated device enable, and disabled with the last associated device disable.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/coresight-cti.c | 90 ++++++++++++++++++++ drivers/hwtracing/coresight/coresight-priv.h | 3 + drivers/hwtracing/coresight/coresight.c | 39 ++++++++- include/linux/coresight.h | 4 + 4 files changed, 135 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 32d9bf0bf0b9..6d452ca3725c 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -464,6 +464,93 @@ void cti_write_intack(struct device *dev, u32 ackval) spin_unlock(&drvdata->spinlock); }
+/* + * Look for a matching connection device name in the list of + * connections. If found then swap in the csdev name and return + * found. + */ +static int +cti_match_con_name(struct cti_device *ctidev, const char *node_name, + const char *csdev_name) +{ + int found = 0; + struct cti_trig_con *trig_con; + + list_for_each_entry(trig_con, &ctidev->trig_cons, node) { + if (trig_con->con_dev_name) { + if (!strcmp(node_name, trig_con->con_dev_name)) { + found = 1; + /* match: so swap in csdev name */ + kfree(trig_con->con_dev_name); + trig_con->con_dev_name = + kstrdup(csdev_name, GFP_KERNEL); + goto cti_con_name_match; + } + } + } +cti_con_name_match: + return found; +} + +/* + * Search the cti list to add an associated CTI into the supplied CS device + * This will set the association if CTI declared before the CS device + */ +int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{ + struct ect_node *ect_item; + struct cti_device *ctidev; + const char *node_name = NULL, *csdev_name; + + /* exit early for no CTIs or current is an ECT device.*/ + if (!cti_count || (csdev->type == CORESIGHT_DEV_TYPE_ECT)) + return 0; + + if (!csdev->dev.parent->of_node) + return 0; + + node_name = csdev->dev.parent->of_node->full_name; + + if (!node_name) + return 0; + + csdev_name = dev_name(&csdev->dev); + + /* for each CTI in list... */ + mutex_lock(&ect_mutex); + list_for_each_entry(ect_item, &ect_net, next) { + ctidev = &ect_item->cti_drv->ctidev; + if (cti_match_con_name(ctidev, node_name, csdev_name)) { + /* + * if we found a matching name then update the + * association pointers. + */ + csdev->ect_dev = ect_item->cti_drv->csdev; + goto cti_found; + } + } +cti_found: + mutex_unlock(&ect_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(cti_add_assoc_to_csdev); + +/* + * Update the cross references where the associated device was found + * while we were building the connection info. This will occur if the + * assoc device was registered before the CTI. + */ +static void cti_update_conn_xrefs(struct cti_drvdata *drvdata) +{ + struct cti_trig_con *tc; + struct cti_device *ctidev = &drvdata->ctidev; + + list_for_each_entry(tc, &ctidev->trig_cons, node) { + if (tc->con_dev) + tc->con_dev->ect_dev = drvdata->csdev; + } +} + /** cti ect operations **/ int cti_enable(struct coresight_device *csdev, void *__unused) { @@ -609,6 +696,9 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) } mutex_unlock(&ect_mutex);
+ /* set any cross references */ + cti_update_conn_xrefs(drvdata); + /* all done - dec pm refcount */ pm_runtime_put(&adev->dev); if (drvdata->ctidev.cpu >= 0) { diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 9b497e2f8725..b1fca5c4bdb2 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -165,8 +165,11 @@ static inline int etm_writel_cp14(u32 off, u32 val) { return 0; }
#ifdef CONFIG_CORESIGHT_CTI extern void cti_device_release(struct device *dev); +extern int cti_add_assoc_to_csdev(struct coresight_device *csdev); #else static inline void cti_device_release(struct device *dev) {}; +static inline int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{ return 0; } #endif
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 8db3d1a5314f..9d56b3b32172 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -216,6 +216,31 @@ void coresight_disclaim_device(void __iomem *base) CS_LOCK(base); }
+/* enable or disable an associated CTI device of the supplied CS device */ +static int +coresight_control_assoc_ectdev(struct coresight_device *csdev, int enable) +{ + int ect_ret = 0; + struct coresight_device *ect_csdev = csdev->ect_dev; + + if (!ect_csdev) + return 0; + + if (enable) { + if (ect_ops(ect_csdev)->enable) + ect_ret = ect_ops(ect_csdev)->enable(ect_csdev, NULL); + } else { + if (ect_ops(ect_csdev)->disable) + ect_ret = ect_ops(ect_csdev)->disable(ect_csdev, NULL); + } + + /* output warning if ECT enable is preventing trace operation */ + if (ect_ret) + dev_info(&csdev->dev, "Associated ECT device (%s) %s failed\n", + dev_name(&ect_csdev->dev), enable ? "enable" : "disable"); + return ect_ret; +} + static int coresight_enable_sink(struct coresight_device *csdev, u32 mode, void *data) { @@ -227,6 +252,9 @@ static int coresight_enable_sink(struct coresight_device *csdev, */ if (sink_ops(csdev)->enable) { ret = sink_ops(csdev)->enable(csdev, mode, data); + if (ret) + return ret; + ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret; csdev->enable = true; @@ -242,6 +270,7 @@ static void coresight_disable_sink(struct coresight_device *csdev) if (atomic_dec_return(csdev->refcnt) == 0) { if (sink_ops(csdev)->disable) { sink_ops(csdev)->disable(csdev); + coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false; } } @@ -275,6 +304,8 @@ static int coresight_enable_link(struct coresight_device *csdev, if (atomic_inc_return(&csdev->refcnt[refport]) == 1) { if (link_ops(csdev)->enable) { ret = link_ops(csdev)->enable(csdev, inport, outport); + if (!ret) + ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) { atomic_dec(&csdev->refcnt[refport]); return ret; @@ -314,8 +345,10 @@ static void coresight_disable_link(struct coresight_device *csdev, }
if (atomic_dec_return(&csdev->refcnt[refport]) == 0) { - if (link_ops(csdev)->disable) + if (link_ops(csdev)->disable) { link_ops(csdev)->disable(csdev, inport, outport); + coresight_control_assoc_ectdev(csdev, 0); + } }
for (i = 0; i < nr_conns; i++) @@ -338,6 +371,8 @@ static int coresight_enable_source(struct coresight_device *csdev, u32 mode) if (!csdev->enable) { if (source_ops(csdev)->enable) { ret = source_ops(csdev)->enable(csdev, NULL, mode); + if (!ret) + ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret; } @@ -362,6 +397,7 @@ static bool coresight_disable_source(struct coresight_device *csdev) if (atomic_dec_return(csdev->refcnt) == 0) { if (source_ops(csdev)->disable) source_ops(csdev)->disable(csdev, NULL); + coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false; } return !csdev->enable; @@ -1238,6 +1274,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
coresight_fixup_device_conns(csdev); coresight_fixup_orphan_conns(csdev); + cti_add_assoc_to_csdev(csdev);
mutex_unlock(&coresight_mutex);
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index d1c1ed17d2ca..50be479c35d1 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -167,6 +167,8 @@ struct coresight_connection { * activated but not yet enabled. Enabling for a _sink_ * appens when a source has been selected for that it. * @ea: Device attribute for sink representation under PMU directory. + * @ect_dev: Associated cross trigger device. Not part of the trace data + * path or connections. */ struct coresight_device { struct coresight_connection *conns; @@ -182,6 +184,8 @@ struct coresight_device { /* sink specific fields */ bool activated; /* true only if a sink is part of a path */ struct dev_ext_attribute *ea; + /* cross trigger handling */ + struct coresight_device *ect_dev; };
#define to_coresight_device(d) container_of(d, struct coresight_device, dev)
On Wed, May 01, 2019 at 09:49:37AM +0100, Mike Leach wrote:
The CoreSight subsystem enables a path of devices from source to sink. Any CTI devices associated with the path devices must be enabled at the same time.
This patch adds an associated device element to the main coresight device structure, and uses this to create associations between the CTI and other devices based on the device tree data. The assoc dev element is used to enable CTI in conjunction with the path elements. CTI are reference counted so where a single CTI is associated with multiple elements on the path, it will be enabled on the first associated device enable, and disabled with the last associated device disable.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 90 ++++++++++++++++++++ drivers/hwtracing/coresight/coresight-priv.h | 3 + drivers/hwtracing/coresight/coresight.c | 39 ++++++++- include/linux/coresight.h | 4 + 4 files changed, 135 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 32d9bf0bf0b9..6d452ca3725c 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -464,6 +464,93 @@ void cti_write_intack(struct device *dev, u32 ackval) spin_unlock(&drvdata->spinlock); } +/*
- Look for a matching connection device name in the list of
- connections. If found then swap in the csdev name and return
- found.
- */
+static int +cti_match_con_name(struct cti_device *ctidev, const char *node_name,
const char *csdev_name)
+{
- int found = 0;
- struct cti_trig_con *trig_con;
- list_for_each_entry(trig_con, &ctidev->trig_cons, node) {
if (trig_con->con_dev_name) {
if (!strcmp(node_name, trig_con->con_dev_name)) {
found = 1;
/* match: so swap in csdev name */
kfree(trig_con->con_dev_name);
trig_con->con_dev_name =
kstrdup(csdev_name, GFP_KERNEL);
I'm not sure I understand why we have to swap here but I suspect it is related to device ordering and probing. I won't pay too much attention to it for now since it will likely have to be modified once you've rebased on Suzuki's ACPI V3 patchset.
goto cti_con_name_match;
}
}
- }
+cti_con_name_match:
- return found;
+}
+/*
- Search the cti list to add an associated CTI into the supplied CS device
- This will set the association if CTI declared before the CS device
- */
+int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{
- struct ect_node *ect_item;
- struct cti_device *ctidev;
- const char *node_name = NULL, *csdev_name;
- /* exit early for no CTIs or current is an ECT device.*/
- if (!cti_count || (csdev->type == CORESIGHT_DEV_TYPE_ECT))
return 0;
- if (!csdev->dev.parent->of_node)
return 0;
- node_name = csdev->dev.parent->of_node->full_name;
- if (!node_name)
return 0;
- csdev_name = dev_name(&csdev->dev);
- /* for each CTI in list... */
- mutex_lock(&ect_mutex);
- list_for_each_entry(ect_item, &ect_net, next) {
ctidev = &ect_item->cti_drv->ctidev;
if (cti_match_con_name(ctidev, node_name, csdev_name)) {
/*
* if we found a matching name then update the
* association pointers.
*/
csdev->ect_dev = ect_item->cti_drv->csdev;
goto cti_found;
}
- }
+cti_found:
- mutex_unlock(&ect_mutex);
- return 0;
+} +EXPORT_SYMBOL_GPL(cti_add_assoc_to_csdev);
+/*
- Update the cross references where the associated device was found
- while we were building the connection info. This will occur if the
- assoc device was registered before the CTI.
- */
+static void cti_update_conn_xrefs(struct cti_drvdata *drvdata) +{
- struct cti_trig_con *tc;
- struct cti_device *ctidev = &drvdata->ctidev;
- list_for_each_entry(tc, &ctidev->trig_cons, node) {
if (tc->con_dev)
tc->con_dev->ect_dev = drvdata->csdev;
- }
+}
/** cti ect operations **/ int cti_enable(struct coresight_device *csdev, void *__unused) { @@ -609,6 +696,9 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) } mutex_unlock(&ect_mutex);
- /* set any cross references */
- cti_update_conn_xrefs(drvdata);
- /* all done - dec pm refcount */ pm_runtime_put(&adev->dev); if (drvdata->ctidev.cpu >= 0) {
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 9b497e2f8725..b1fca5c4bdb2 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -165,8 +165,11 @@ static inline int etm_writel_cp14(u32 off, u32 val) { return 0; } #ifdef CONFIG_CORESIGHT_CTI extern void cti_device_release(struct device *dev); +extern int cti_add_assoc_to_csdev(struct coresight_device *csdev); #else static inline void cti_device_release(struct device *dev) {}; +static inline int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{ return 0; } #endif diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 8db3d1a5314f..9d56b3b32172 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -216,6 +216,31 @@ void coresight_disclaim_device(void __iomem *base) CS_LOCK(base); } +/* enable or disable an associated CTI device of the supplied CS device */ +static int +coresight_control_assoc_ectdev(struct coresight_device *csdev, int enable) +{
- int ect_ret = 0;
- struct coresight_device *ect_csdev = csdev->ect_dev;
- if (!ect_csdev)
return 0;
- if (enable) {
if (ect_ops(ect_csdev)->enable)
ect_ret = ect_ops(ect_csdev)->enable(ect_csdev, NULL);
- } else {
if (ect_ops(ect_csdev)->disable)
ect_ret = ect_ops(ect_csdev)->disable(ect_csdev, NULL);
- }
- /* output warning if ECT enable is preventing trace operation */
- if (ect_ret)
dev_info(&csdev->dev, "Associated ECT device (%s) %s failed\n",
dev_name(&ect_csdev->dev), enable ? "enable" : "disable");
Yes, this could be useful.
- return ect_ret;
+}
static int coresight_enable_sink(struct coresight_device *csdev, u32 mode, void *data) { @@ -227,6 +252,9 @@ static int coresight_enable_sink(struct coresight_device *csdev, */ if (sink_ops(csdev)->enable) { ret = sink_ops(csdev)->enable(csdev, mode, data);
if (ret)
return ret;
if (ret) return ret;ret = coresight_control_assoc_ectdev(csdev, 1);
In case of error the sink is left enabled.
Also, wouldn't it be best to enable the ect device first and then the sink? Otherwise if the sink wrongly emits ECT signals when enabled we'll catch the condition all the time. In the case where the ECT is enabled after the sink we will never know if the wrongly emitted signals will be caught, leading to intermittent behavior.
csdev->enable = true;
@@ -242,6 +270,7 @@ static void coresight_disable_sink(struct coresight_device *csdev) if (atomic_dec_return(csdev->refcnt) == 0) { if (sink_ops(csdev)->disable) { sink_ops(csdev)->disable(csdev);
} }coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false;
@@ -275,6 +304,8 @@ static int coresight_enable_link(struct coresight_device *csdev, if (atomic_inc_return(&csdev->refcnt[refport]) == 1) { if (link_ops(csdev)->enable) { ret = link_ops(csdev)->enable(csdev, inport, outport);
if (!ret)
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) { atomic_dec(&csdev->refcnt[refport]); return ret;
Same comment as above.
@@ -314,8 +345,10 @@ static void coresight_disable_link(struct coresight_device *csdev, } if (atomic_dec_return(&csdev->refcnt[refport]) == 0) {
if (link_ops(csdev)->disable)
if (link_ops(csdev)->disable) { link_ops(csdev)->disable(csdev, inport, outport);
coresight_control_assoc_ectdev(csdev, 0);
}}
for (i = 0; i < nr_conns; i++) @@ -338,6 +371,8 @@ static int coresight_enable_source(struct coresight_device *csdev, u32 mode) if (!csdev->enable) { if (source_ops(csdev)->enable) { ret = source_ops(csdev)->enable(csdev, NULL, mode);
if (!ret)
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret;
Same comment as above.
}
@@ -362,6 +397,7 @@ static bool coresight_disable_source(struct coresight_device *csdev) if (atomic_dec_return(csdev->refcnt) == 0) { if (source_ops(csdev)->disable) source_ops(csdev)->disable(csdev, NULL);
csdev->enable = false; } return !csdev->enable;coresight_control_assoc_ectdev(csdev, 0);
@@ -1238,6 +1274,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) coresight_fixup_device_conns(csdev); coresight_fixup_orphan_conns(csdev);
- cti_add_assoc_to_csdev(csdev);
mutex_unlock(&coresight_mutex); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index d1c1ed17d2ca..50be479c35d1 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -167,6 +167,8 @@ struct coresight_connection {
activated but not yet enabled. Enabling for a _sink_
appens when a source has been selected for that it.
- @ea: Device attribute for sink representation under PMU directory.
- @ect_dev: Associated cross trigger device. Not part of the trace data
*/
path or connections.
struct coresight_device { struct coresight_connection *conns; @@ -182,6 +184,8 @@ struct coresight_device { /* sink specific fields */ bool activated; /* true only if a sink is part of a path */ struct dev_ext_attribute *ea;
- /* cross trigger handling */
- struct coresight_device *ect_dev;
I am still not sure how to treat ECT devices, i.e should they become a special helper device or be considered a separate class of device. They are a separate class since they deal with signals rather than data, but they could easily re-use the infrastructure put in place for data devices. I haven't taken a position on this yet so just keep going with the current scheme. I expect the decision will get easier to take as this set is refined over iterations.
};
#define to_coresight_device(d) container_of(d, struct coresight_device, dev)
2.20.1
Hi Mathieu,
On Tue, 14 May 2019 at 17:41, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, May 01, 2019 at 09:49:37AM +0100, Mike Leach wrote:
The CoreSight subsystem enables a path of devices from source to sink. Any CTI devices associated with the path devices must be enabled at the same time.
This patch adds an associated device element to the main coresight device structure, and uses this to create associations between the CTI and other devices based on the device tree data. The assoc dev element is used to enable CTI in conjunction with the path elements. CTI are reference counted so where a single CTI is associated with multiple elements on the path, it will be enabled on the first associated device enable, and disabled with the last associated device disable.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 90 ++++++++++++++++++++ drivers/hwtracing/coresight/coresight-priv.h | 3 + drivers/hwtracing/coresight/coresight.c | 39 ++++++++- include/linux/coresight.h | 4 + 4 files changed, 135 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 32d9bf0bf0b9..6d452ca3725c 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -464,6 +464,93 @@ void cti_write_intack(struct device *dev, u32 ackval) spin_unlock(&drvdata->spinlock); }
+/*
- Look for a matching connection device name in the list of
- connections. If found then swap in the csdev name and return
- found.
- */
+static int +cti_match_con_name(struct cti_device *ctidev, const char *node_name,
const char *csdev_name)
+{
int found = 0;
struct cti_trig_con *trig_con;
list_for_each_entry(trig_con, &ctidev->trig_cons, node) {
if (trig_con->con_dev_name) {
if (!strcmp(node_name, trig_con->con_dev_name)) {
found = 1;
/* match: so swap in csdev name */
kfree(trig_con->con_dev_name);
trig_con->con_dev_name =
kstrdup(csdev_name, GFP_KERNEL);
I'm not sure I understand why we have to swap here but I suspect it is related to device ordering and probing. I won't pay too much attention to it for now since it will likely have to be modified once you've rebased on Suzuki's ACPI V3 patchset.
Correct - it is to do with probing and matching ordering. This reflects the current "devicetree only" state of the art and will have to be revisited once the standardised naming schema for CS is introduced with ACPI.
goto cti_con_name_match;
}
}
}
+cti_con_name_match:
return found;
+}
+/*
- Search the cti list to add an associated CTI into the supplied CS device
- This will set the association if CTI declared before the CS device
- */
+int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{
struct ect_node *ect_item;
struct cti_device *ctidev;
const char *node_name = NULL, *csdev_name;
/* exit early for no CTIs or current is an ECT device.*/
if (!cti_count || (csdev->type == CORESIGHT_DEV_TYPE_ECT))
return 0;
if (!csdev->dev.parent->of_node)
return 0;
node_name = csdev->dev.parent->of_node->full_name;
if (!node_name)
return 0;
csdev_name = dev_name(&csdev->dev);
/* for each CTI in list... */
mutex_lock(&ect_mutex);
list_for_each_entry(ect_item, &ect_net, next) {
ctidev = &ect_item->cti_drv->ctidev;
if (cti_match_con_name(ctidev, node_name, csdev_name)) {
/*
* if we found a matching name then update the
* association pointers.
*/
csdev->ect_dev = ect_item->cti_drv->csdev;
goto cti_found;
}
}
+cti_found:
mutex_unlock(&ect_mutex);
return 0;
+} +EXPORT_SYMBOL_GPL(cti_add_assoc_to_csdev);
+/*
- Update the cross references where the associated device was found
- while we were building the connection info. This will occur if the
- assoc device was registered before the CTI.
- */
+static void cti_update_conn_xrefs(struct cti_drvdata *drvdata) +{
struct cti_trig_con *tc;
struct cti_device *ctidev = &drvdata->ctidev;
list_for_each_entry(tc, &ctidev->trig_cons, node) {
if (tc->con_dev)
tc->con_dev->ect_dev = drvdata->csdev;
}
+}
/** cti ect operations **/ int cti_enable(struct coresight_device *csdev, void *__unused) { @@ -609,6 +696,9 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) } mutex_unlock(&ect_mutex);
/* set any cross references */
cti_update_conn_xrefs(drvdata);
/* all done - dec pm refcount */ pm_runtime_put(&adev->dev); if (drvdata->ctidev.cpu >= 0) {
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 9b497e2f8725..b1fca5c4bdb2 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -165,8 +165,11 @@ static inline int etm_writel_cp14(u32 off, u32 val) { return 0; }
#ifdef CONFIG_CORESIGHT_CTI extern void cti_device_release(struct device *dev); +extern int cti_add_assoc_to_csdev(struct coresight_device *csdev); #else static inline void cti_device_release(struct device *dev) {}; +static inline int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{ return 0; } #endif
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 8db3d1a5314f..9d56b3b32172 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -216,6 +216,31 @@ void coresight_disclaim_device(void __iomem *base) CS_LOCK(base); }
+/* enable or disable an associated CTI device of the supplied CS device */ +static int +coresight_control_assoc_ectdev(struct coresight_device *csdev, int enable) +{
int ect_ret = 0;
struct coresight_device *ect_csdev = csdev->ect_dev;
if (!ect_csdev)
return 0;
if (enable) {
if (ect_ops(ect_csdev)->enable)
ect_ret = ect_ops(ect_csdev)->enable(ect_csdev, NULL);
} else {
if (ect_ops(ect_csdev)->disable)
ect_ret = ect_ops(ect_csdev)->disable(ect_csdev, NULL);
}
/* output warning if ECT enable is preventing trace operation */
if (ect_ret)
dev_info(&csdev->dev, "Associated ECT device (%s) %s failed\n",
dev_name(&ect_csdev->dev), enable ? "enable" : "disable");
Yes, this could be useful.
return ect_ret;
+}
static int coresight_enable_sink(struct coresight_device *csdev, u32 mode, void *data) { @@ -227,6 +252,9 @@ static int coresight_enable_sink(struct coresight_device *csdev, */ if (sink_ops(csdev)->enable) { ret = sink_ops(csdev)->enable(csdev, mode, data);
if (ret)
return ret;
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret;
In case of error the sink is left enabled.
good point - I'll have to recheck this - though if an error is seen in the "enable path" code in the coresight enable path process, does the "disable path" not unwind everything anyway?
Also, wouldn't it be best to enable the ect device first and then the sink? Otherwise if the sink wrongly emits ECT signals when enabled we'll catch the condition all the time. In the case where the ECT is enabled after the sink we will never know if the wrongly emitted signals will be caught, leading to intermittent behavior.
May not have a choice - CTIs can be connected to multiple devices, so any given CTI - especially ones not bound to cores can be enabled by association with any of these devices. The CTI enable process has a refcount - enable on first enable request, disable on last disable request.
I would tend to favour a sink / associated device first - CTI second model as programming / enable of the associated device should in general be done in such a way as to not have triggers active on enable. If enabling a sink triggers some output signals - that are typically FULL or ACQCOMP, then that is either a sink programming error, or we may have not seen a valid signal the last time we were enabled.
csdev->enable = true;
@@ -242,6 +270,7 @@ static void coresight_disable_sink(struct coresight_device *csdev) if (atomic_dec_return(csdev->refcnt) == 0) { if (sink_ops(csdev)->disable) { sink_ops(csdev)->disable(csdev);
coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false; } }
@@ -275,6 +304,8 @@ static int coresight_enable_link(struct coresight_device *csdev, if (atomic_inc_return(&csdev->refcnt[refport]) == 1) { if (link_ops(csdev)->enable) { ret = link_ops(csdev)->enable(csdev, inport, outport);
if (!ret)
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) { atomic_dec(&csdev->refcnt[refport]); return ret;
Same comment as above.
@@ -314,8 +345,10 @@ static void coresight_disable_link(struct coresight_device *csdev, }
if (atomic_dec_return(&csdev->refcnt[refport]) == 0) {
if (link_ops(csdev)->disable)
if (link_ops(csdev)->disable) { link_ops(csdev)->disable(csdev, inport, outport);
coresight_control_assoc_ectdev(csdev, 0);
} } for (i = 0; i < nr_conns; i++)
@@ -338,6 +371,8 @@ static int coresight_enable_source(struct coresight_device *csdev, u32 mode) if (!csdev->enable) { if (source_ops(csdev)->enable) { ret = source_ops(csdev)->enable(csdev, NULL, mode);
if (!ret)
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret;
Same comment as above.
}
@@ -362,6 +397,7 @@ static bool coresight_disable_source(struct coresight_device *csdev) if (atomic_dec_return(csdev->refcnt) == 0) { if (source_ops(csdev)->disable) source_ops(csdev)->disable(csdev, NULL);
coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false; } return !csdev->enable;
@@ -1238,6 +1274,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
coresight_fixup_device_conns(csdev); coresight_fixup_orphan_conns(csdev);
cti_add_assoc_to_csdev(csdev); mutex_unlock(&coresight_mutex);
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index d1c1ed17d2ca..50be479c35d1 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -167,6 +167,8 @@ struct coresight_connection {
activated but not yet enabled. Enabling for a _sink_
appens when a source has been selected for that it.
- @ea: Device attribute for sink representation under PMU directory.
- @ect_dev: Associated cross trigger device. Not part of the trace data
*/
path or connections.
struct coresight_device { struct coresight_connection *conns; @@ -182,6 +184,8 @@ struct coresight_device { /* sink specific fields */ bool activated; /* true only if a sink is part of a path */ struct dev_ext_attribute *ea;
/* cross trigger handling */
struct coresight_device *ect_dev;
I am still not sure how to treat ECT devices, i.e should they become a special helper device or be considered a separate class of device. They are a separate class since they deal with signals rather than data, but they could easily re-use the infrastructure put in place for data devices. I haven't taken a position on this yet so just keep going with the current scheme. I expect the decision will get easier to take as this set is refined over iterations.
I considered them separate from the data devices as the topology is completely different. The trace data devices have an end to end, one way flow of data. This has limit programmability in the way that data flows - i.e. you can choose your sink, but not much else, and downstream components cannot affect upstream components. ECT is a star topology, with highly programmable flexibility - with the correct configuration an ETM trace event can trigger a sink to stop collecting trace, or a sink full event can stop generation. When you add that to the possibility of the hooks into STM hardware signal triggers, and none CS hardware such as ELA, system profilers & clocks (see the Juno .dtsi for connections such as these) it becomes clear that the functionality does not fit easily into the existing data flow infrastructure.
My initial development tried to re-use more of the existing infrastructure but it just got increasingly complex & difficult.
};
#define to_coresight_device(d) container_of(d, struct coresight_device, dev)
2.20.1
Regards
Mike
On Wed, 15 May 2019 at 09:30, Mike Leach mike.leach@linaro.org wrote:
Hi Mathieu,
On Tue, 14 May 2019 at 17:41, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, May 01, 2019 at 09:49:37AM +0100, Mike Leach wrote:
The CoreSight subsystem enables a path of devices from source to sink. Any CTI devices associated with the path devices must be enabled at the same time.
This patch adds an associated device element to the main coresight device structure, and uses this to create associations between the CTI and other devices based on the device tree data. The assoc dev element is used to enable CTI in conjunction with the path elements. CTI are reference counted so where a single CTI is associated with multiple elements on the path, it will be enabled on the first associated device enable, and disabled with the last associated device disable.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 90 ++++++++++++++++++++ drivers/hwtracing/coresight/coresight-priv.h | 3 + drivers/hwtracing/coresight/coresight.c | 39 ++++++++- include/linux/coresight.h | 4 + 4 files changed, 135 insertions(+), 1 deletion(-)
[snip]
static int coresight_enable_sink(struct coresight_device *csdev, u32 mode, void *data) { @@ -227,6 +252,9 @@ static int coresight_enable_sink(struct coresight_device *csdev, */ if (sink_ops(csdev)->enable) { ret = sink_ops(csdev)->enable(csdev, mode, data);
if (ret)
return ret;
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret;
In case of error the sink is left enabled.
good point - I'll have to recheck this - though if an error is seen in the "enable path" code in the coresight enable path process, does the "disable path" not unwind everything anyway?
The sink is not - see comment here [1]. Sources are also special since they aren't handled by the core [2]. I think handling of CTI devices should look atomic to the framework, i.e both the device and CTI companion are enabled or both have failed.
[1]. https://elixir.bootlin.com/linux/latest/source/drivers/hwtracing/coresight/c... [2]. https://elixir.bootlin.com/linux/latest/source/drivers/hwtracing/coresight/c...
Also, wouldn't it be best to enable the ect device first and then the sink? Otherwise if the sink wrongly emits ECT signals when enabled we'll catch the condition all the time. In the case where the ECT is enabled after the sink we will never know if the wrongly emitted signals will be caught, leading to intermittent behavior.
May not have a choice - CTIs can be connected to multiple devices, so any given CTI - especially ones not bound to cores can be enabled by association with any of these devices. The CTI enable process has a refcount - enable on first enable request, disable on last disable request.
When enabling a CTI that is connected to a data device, is it possible to enable only the in/out ports for that device and enable the ports for other data devices when those are enabled? If the HW supports it function cti_write_all_hw_regs() would have to be redesigned.
I would tend to favour a sink / associated device first - CTI second model as programming / enable of the associated device should in general be done in such a way as to not have triggers active on enable. If enabling a sink triggers some output signals - that are typically FULL or ACQCOMP, then that is either a sink programming error, or we may have not seen a valid signal the last time we were enabled.
Exactly - and we won't know about these conditions if the associated device is enabled first and the CTI second. Let me know if I'm really missing the point.
Thanks, Mathieu
csdev->enable = true;
@@ -242,6 +270,7 @@ static void coresight_disable_sink(struct coresight_device *csdev) if (atomic_dec_return(csdev->refcnt) == 0) { if (sink_ops(csdev)->disable) { sink_ops(csdev)->disable(csdev);
coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false; } }
@@ -275,6 +304,8 @@ static int coresight_enable_link(struct coresight_device *csdev, if (atomic_inc_return(&csdev->refcnt[refport]) == 1) { if (link_ops(csdev)->enable) { ret = link_ops(csdev)->enable(csdev, inport, outport);
if (!ret)
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) { atomic_dec(&csdev->refcnt[refport]); return ret;
Same comment as above.
@@ -314,8 +345,10 @@ static void coresight_disable_link(struct coresight_device *csdev, }
if (atomic_dec_return(&csdev->refcnt[refport]) == 0) {
if (link_ops(csdev)->disable)
if (link_ops(csdev)->disable) { link_ops(csdev)->disable(csdev, inport, outport);
coresight_control_assoc_ectdev(csdev, 0);
} } for (i = 0; i < nr_conns; i++)
@@ -338,6 +371,8 @@ static int coresight_enable_source(struct coresight_device *csdev, u32 mode) if (!csdev->enable) { if (source_ops(csdev)->enable) { ret = source_ops(csdev)->enable(csdev, NULL, mode);
if (!ret)
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret;
Same comment as above.
}
@@ -362,6 +397,7 @@ static bool coresight_disable_source(struct coresight_device *csdev) if (atomic_dec_return(csdev->refcnt) == 0) { if (source_ops(csdev)->disable) source_ops(csdev)->disable(csdev, NULL);
coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false; } return !csdev->enable;
@@ -1238,6 +1274,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
coresight_fixup_device_conns(csdev); coresight_fixup_orphan_conns(csdev);
cti_add_assoc_to_csdev(csdev); mutex_unlock(&coresight_mutex);
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index d1c1ed17d2ca..50be479c35d1 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -167,6 +167,8 @@ struct coresight_connection {
activated but not yet enabled. Enabling for a _sink_
appens when a source has been selected for that it.
- @ea: Device attribute for sink representation under PMU directory.
- @ect_dev: Associated cross trigger device. Not part of the trace data
*/
path or connections.
struct coresight_device { struct coresight_connection *conns; @@ -182,6 +184,8 @@ struct coresight_device { /* sink specific fields */ bool activated; /* true only if a sink is part of a path */ struct dev_ext_attribute *ea;
/* cross trigger handling */
struct coresight_device *ect_dev;
I am still not sure how to treat ECT devices, i.e should they become a special helper device or be considered a separate class of device. They are a separate class since they deal with signals rather than data, but they could easily re-use the infrastructure put in place for data devices. I haven't taken a position on this yet so just keep going with the current scheme. I expect the decision will get easier to take as this set is refined over iterations.
I considered them separate from the data devices as the topology is completely different. The trace data devices have an end to end, one way flow of data. This has limit programmability in the way that data flows - i.e. you can choose your sink, but not much else, and downstream components cannot affect upstream components. ECT is a star topology, with highly programmable flexibility - with the correct configuration an ETM trace event can trigger a sink to stop collecting trace, or a sink full event can stop generation. When you add that to the possibility of the hooks into STM hardware signal triggers, and none CS hardware such as ELA, system profilers & clocks (see the Juno .dtsi for connections such as these) it becomes clear that the functionality does not fit easily into the existing data flow infrastructure.
My initial development tried to re-use more of the existing infrastructure but it just got increasingly complex & difficult.
};
#define to_coresight_device(d) container_of(d, struct coresight_device, dev)
2.20.1
Regards
Mike
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
Dynamically adds sysfs attributes for all connections defined in the CTI.
Each connection has _name, _trgin_type, _trgout_type, _trgin_sig and _trgout_sig, preceded by a connection index (e.g. 0_name is the name of connection 0).
Additionally each device has a nr_cons and trigout_filtered parameter. This allows clients to explore the connection and trigger signal details without needing to refer to device tree or specification of the device.
Standardised type information is provided for certain common functions - e.g. snk_full for a trigger from a sink indicating full. Otherwise type defaults to genio.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../hwtracing/coresight/coresight-cti-sysfs.c | 338 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.c | 11 + drivers/hwtracing/coresight/coresight-cti.h | 4 + 3 files changed, 353 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index ff8dad02a779..e4b23e291114 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -883,6 +883,343 @@ static struct attribute *coresight_cti_channel_attrs[] = { NULL, };
+/* Create the connections group attrs dynamically */ + +/* group is static - .attrs will be built dynamically */ +static struct attribute_group coresight_cti_conns_group = { + .name = "connections", +}; + +/* + *Each connection has dynamic name, trigin/out sigs/types attrs, + * + each device has static nr conns + filter attr + */ + +static ssize_t trigout_filtered_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *cfg = &drvdata->config; + int b_sz = PAGE_SIZE; + u32 sig_mask; + int sig_idx, used = 0, nr_trig_max = cfg->nr_trig_max; + + if (cfg->trig_out_filter) { + sig_mask = 0x1; + for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) { + if (sig_mask & cfg->trig_out_filter) { + used += scnprintf(buf+used, b_sz-used, "%d ", + sig_idx); + } + sig_mask <<= 1; + } + used += scnprintf(buf+used, b_sz-used, "\n"); + } else + used += scnprintf(buf, b_sz, "none\n"); + return used; +} +static DEVICE_ATTR_RO(trigout_filtered); + +static ssize_t nr_cons_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return scnprintf(buf, PAGE_SIZE, "%d\n", drvdata->ctidev.nr_trig_con); +} +static DEVICE_ATTR_RO(nr_cons); + +/* static attrs - 2 + null terminator */ +#define CTI_SYSFS_CONS_ST_ATTR 3 +/* dynamic attr - per connection */ +#define CTI_SYSFS_CONS_DYN_ATTR 5 + +static ssize_t con_name_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr; + struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var; + + return scnprintf(buf, PAGE_SIZE, "%s\n", con->con_dev_name); +} + +static ssize_t trigin_sig_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr; + struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *cfg = &drvdata->config; + + u32 sig_mask; + int sig_idx, used = 0, b_sz = PAGE_SIZE; + + sig_mask = 0x1; + for (sig_idx = 0; sig_idx < cfg->nr_trig_max; sig_idx++) { + if (sig_mask & con->con_in->used_mask) { + used += scnprintf(buf+used, b_sz-used, "%d ", + sig_idx); + } + sig_mask <<= 1; + } + used += scnprintf(buf+used, b_sz-used, "\n"); + return used; +} + +static ssize_t trigout_sig_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr; + struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *cfg = &drvdata->config; + + u32 sig_mask; + int sig_idx, used = 0, b_sz = PAGE_SIZE; + + sig_mask = 0x1; + for (sig_idx = 0; sig_idx < cfg->nr_trig_max; sig_idx++) { + if (sig_mask & con->con_out->used_mask) { + used += scnprintf(buf+used, b_sz-used, "%d ", + sig_idx); + } + sig_mask <<= 1; + } + used += scnprintf(buf+used, b_sz-used, "\n"); + return used; +} + +/* convert a sig type id to a name */ +static const char * +cti_sig_type_name(struct cti_trig_con *con, int used_count, bool in) +{ + static const char * const sig_type_names[] = { + "genio", "intreq", "intack", "haltreq", "restartreq", + "pe_edbgreq", "pe_dbgrestart", "pe_ctiirq", "pe_pmuirq", + "pe_dbgtrigger", "etm_extout", "etm_extin", "snk_full", + "snk_acqcomp", "snk_flushcomp", "snk_flushin", "snk_trigin", + "stm_asyncout", "stm_tout_spte", "stm_tout_sw", "stm_tout_hete", + "stm_hwevent", "ela_tstart", "ela_tstop", "ela_dbgreq", + }; + int idx = 0; + struct cti_trig_grp *grp = in ? con->con_in : con->con_out; + + if (grp->sig_types) { + if (used_count < grp->nr_sigs) + idx = grp->sig_types[used_count]; + } + return sig_type_names[idx]; +} + +static ssize_t trigin_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr; + struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var; + + int sig_idx, used = 0, b_sz = PAGE_SIZE; + const char *name; + + for (sig_idx = 0; sig_idx < con->con_in->nr_sigs; sig_idx++) { + name = cti_sig_type_name(con, sig_idx, true); + used += scnprintf(buf+used, b_sz-used, "%s ", name); + } + used += scnprintf(buf+used, b_sz-used, "\n"); + return used; +} + +static ssize_t trigout_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr; + struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var; + + int sig_idx, used = 0, b_sz = PAGE_SIZE; + const char *name; + + for (sig_idx = 0; sig_idx < con->con_out->nr_sigs; sig_idx++) { + name = cti_sig_type_name(con, sig_idx, false); + used += scnprintf(buf+used, b_sz-used, "%s ", name); + } + used += scnprintf(buf+used, b_sz-used, "\n"); + return used; +} + +typedef ssize_t (*p_show_fn)(struct device *dev, struct device_attribute *attr, + char *buf); + +enum cti_conn_attr_type { + CTI_CON_ATTR_NAME, + CTI_CON_ATTR_TRIGIN_SIG, + CTI_CON_ATTR_TRIGOUT_SIG, + CTI_CON_ATTR_TRIGIN_TYPES, + CTI_CON_ATTR_TRIGOUT_TYPES +}; + +static p_show_fn show_fns[] = { + con_name_show, + trigin_sig_show, + trigout_sig_show, + trigin_type_show, + trigout_type_show, +}; + +static const char * const base_names[] = { + "_name", + "_trgin_sig", + "_trgout_sig", + "_trgin_type", + "_trgout_type", +}; + +struct dev_ext_attribute * +cti_create_con_sysfs_attr(int con_idx, enum cti_conn_attr_type attr_type) +{ + struct dev_ext_attribute *dev_ext_attr = 0; + char *name = 0; + + dev_ext_attr = kzalloc(sizeof(struct dev_ext_attribute), GFP_KERNEL); + if (dev_ext_attr) { + name = kzalloc(sizeof(char) * 16, GFP_KERNEL); + if (name) { + /* fill out the underlying attribute struct */ + sprintf(name, "%d%s", con_idx, base_names[attr_type]); + dev_ext_attr->attr.attr.name = name; + dev_ext_attr->attr.attr.mode = 0444; + + /* now the device_attribute struct */ + dev_ext_attr->attr.show = show_fns[attr_type]; + } else { + kfree(dev_ext_attr); + dev_ext_attr = 0; + } + } + return dev_ext_attr; +} + +int cti_create_con_attr_set(int con_idx, int *next_attr_idx, + struct cti_device *ctidev, + struct cti_trig_con *con) +{ + struct dev_ext_attribute *dev_ext_attr; + int attr_idx = *next_attr_idx; + + dev_ext_attr = cti_create_con_sysfs_attr(con_idx, CTI_CON_ATTR_NAME); + if (!dev_ext_attr) + goto create_con_attr_err; + dev_ext_attr->var = con; + ctidev->con_attrs[attr_idx++] = dev_ext_attr; + + if (con->con_in->nr_sigs > 0) { + dev_ext_attr = + cti_create_con_sysfs_attr(con_idx, + CTI_CON_ATTR_TRIGIN_SIG); + if (!dev_ext_attr) + goto create_con_attr_err; + dev_ext_attr->var = con; + ctidev->con_attrs[attr_idx++] = dev_ext_attr; + + dev_ext_attr = + cti_create_con_sysfs_attr(con_idx, + CTI_CON_ATTR_TRIGIN_TYPES); + if (!dev_ext_attr) + goto create_con_attr_err; + dev_ext_attr->var = con; + ctidev->con_attrs[attr_idx++] = dev_ext_attr; + + } + + if (con->con_in->nr_sigs > 0) { + + dev_ext_attr = + cti_create_con_sysfs_attr(con_idx, + CTI_CON_ATTR_TRIGOUT_SIG); + if (!dev_ext_attr) + goto create_con_attr_err; + dev_ext_attr->var = con; + ctidev->con_attrs[attr_idx++] = dev_ext_attr; + + dev_ext_attr = + cti_create_con_sysfs_attr(con_idx, + CTI_CON_ATTR_TRIGOUT_TYPES); + if (!dev_ext_attr) + goto create_con_attr_err; + dev_ext_attr->var = con; + ctidev->con_attrs[attr_idx++] = dev_ext_attr; + } + *next_attr_idx = attr_idx; + return 0; + +create_con_attr_err: + cti_destroy_cons_sysfs(ctidev); + return -ENOMEM; +} + +int cti_create_cons_sysfs(struct cti_drvdata *drvdata) +{ + struct cti_device *ctidev = &drvdata->ctidev; + int err = 0, n_attrs = 0, con_idx = 0; + int next_attr_idx = CTI_SYSFS_CONS_ST_ATTR - 1; + struct cti_trig_con *tc; + + n_attrs = CTI_SYSFS_CONS_ST_ATTR + + (CTI_SYSFS_CONS_DYN_ATTR * ctidev->nr_trig_con); + ctidev->con_attrs = + kcalloc(n_attrs, sizeof(struct dev_ext_attribute *), + GFP_KERNEL); + if (ctidev->con_attrs != 0) { + ctidev->con_attrs[0] = (struct dev_ext_attribute *) + &dev_attr_trigout_filtered.attr; + ctidev->con_attrs[1] = (struct dev_ext_attribute *) + &dev_attr_nr_cons.attr; + + /* add dynamic set for each connection */ + list_for_each_entry(tc, &ctidev->trig_cons, node) { + err = cti_create_con_attr_set(con_idx++, + &next_attr_idx, + ctidev, + tc); + /* if create con fails it will free all prior cons */ + if (err) + return err; + } + coresight_cti_conns_group.attrs = + (struct attribute **)ctidev->con_attrs; + } else + err = -ENOMEM; + return err; +} + +void cti_destroy_cons_sysfs(struct cti_device *ctidev) +{ + int n_attrs = CTI_SYSFS_CONS_ST_ATTR + + (CTI_SYSFS_CONS_DYN_ATTR * ctidev->nr_trig_con); + int dyn_idx = CTI_SYSFS_CONS_ST_ATTR - 1; + bool eol = false; + struct dev_ext_attribute *dev_ext_attr = 0; + + if (ctidev->con_attrs) { + while (!eol && (dyn_idx < n_attrs)) { + dev_ext_attr = ctidev->con_attrs[dyn_idx]; + if (dev_ext_attr) { + kfree(dev_ext_attr->attr.attr.name); + kfree(dev_ext_attr); + } else + eol = true; + dyn_idx++; + } + kfree(ctidev->con_attrs); + ctidev->con_attrs = 0; + } +} + /* attribute and group sysfs tables. */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, @@ -908,5 +1245,6 @@ const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_mgmt_group, &coresight_cti_regs_group, &coresight_cti_channels_group, + &coresight_cti_conns_group, NULL, }; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 6d452ca3725c..e0192fd50804 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -671,6 +671,14 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) /* setup cpu related CTI devices, otherwise assume powered */ drvdata->config.hw_powered = true;
+ /* create dynamic attributes for connections */ + ret = cti_create_cons_sysfs(drvdata); + if (ret) { + pr_err("%s: create dynamic sysfs entries failed\n", + pdata->name); + goto err_out; + } + /* set up coresight component description */ cti_desc.pdata = pdata; cti_desc.type = CORESIGHT_DEV_TYPE_ECT; @@ -746,6 +754,9 @@ void cti_device_release(struct device *dev) if (drvdata->ctidev.cpu >= 0) cti_cpu_drv[drvdata->ctidev.cpu] = 0;
+ /* clear the dynamic sysfs associate with connections */ + cti_destroy_cons_sysfs(&drvdata->ctidev); + /* clear the connection list items */ cti_free_conn_info(drvdata);
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index 7342f998f4c5..2a3557bfcd16 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -125,6 +125,7 @@ struct cti_trig_con { * assumed there is a single CTM per SoC, ID 0). * @trig_cons: list of connections to this device. * @cpu: CPU ID if associated with CPU, -1 otherwise. + * @con_attrs: Dymanic sysfs attributes created to display connection info. * */ struct cti_device { @@ -132,6 +133,7 @@ struct cti_device { u32 ctm_id; struct list_head trig_cons; int cpu; + struct dev_ext_attribute **con_attrs; };
/** @@ -252,6 +254,8 @@ int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op, int cti_channel_setop(struct device *dev, enum cti_chan_set_op op, u32 channel_idx); void cti_write_intack(struct device *dev, u32 ackval); +int cti_create_cons_sysfs(struct cti_drvdata *drvdata); +void cti_destroy_cons_sysfs(struct cti_device *ctidev);
/* cti powered and enabled */ #define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
On Wed, May 01, 2019 at 09:49:38AM +0100, Mike Leach wrote:
Dynamically adds sysfs attributes for all connections defined in the CTI.
Each connection has _name, _trgin_type, _trgout_type, _trgin_sig and _trgout_sig, preceded by a connection index (e.g. 0_name is the name of connection 0).
Additionally each device has a nr_cons and trigout_filtered parameter. This allows clients to explore the connection and trigger signal details without needing to refer to device tree or specification of the device.
Standardised type information is provided for certain common functions - e.g. snk_full for a trigger from a sink indicating full. Otherwise type defaults to genio.
I will skip this patch until we find a way to uniformally represent connection information for signal and data components.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 338 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.c | 11 + drivers/hwtracing/coresight/coresight-cti.h | 4 + 3 files changed, 353 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index ff8dad02a779..e4b23e291114 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -883,6 +883,343 @@ static struct attribute *coresight_cti_channel_attrs[] = { NULL, }; +/* Create the connections group attrs dynamically */
+/* group is static - .attrs will be built dynamically */ +static struct attribute_group coresight_cti_conns_group = {
- .name = "connections",
+};
+/*
- *Each connection has dynamic name, trigin/out sigs/types attrs,
- each device has static nr conns + filter attr
- */
+static ssize_t trigout_filtered_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- int b_sz = PAGE_SIZE;
- u32 sig_mask;
- int sig_idx, used = 0, nr_trig_max = cfg->nr_trig_max;
- if (cfg->trig_out_filter) {
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & cfg->trig_out_filter) {
used += scnprintf(buf+used, b_sz-used, "%d ",
sig_idx);
}
sig_mask <<= 1;
}
used += scnprintf(buf+used, b_sz-used, "\n");
- } else
used += scnprintf(buf, b_sz, "none\n");
- return used;
+} +static DEVICE_ATTR_RO(trigout_filtered);
+static ssize_t nr_cons_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return scnprintf(buf, PAGE_SIZE, "%d\n", drvdata->ctidev.nr_trig_con);
+} +static DEVICE_ATTR_RO(nr_cons);
+/* static attrs - 2 + null terminator */ +#define CTI_SYSFS_CONS_ST_ATTR 3 +/* dynamic attr - per connection */ +#define CTI_SYSFS_CONS_DYN_ATTR 5
+static ssize_t con_name_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
- struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
- return scnprintf(buf, PAGE_SIZE, "%s\n", con->con_dev_name);
+}
+static ssize_t trigin_sig_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
- struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- u32 sig_mask;
- int sig_idx, used = 0, b_sz = PAGE_SIZE;
- sig_mask = 0x1;
- for (sig_idx = 0; sig_idx < cfg->nr_trig_max; sig_idx++) {
if (sig_mask & con->con_in->used_mask) {
used += scnprintf(buf+used, b_sz-used, "%d ",
sig_idx);
}
sig_mask <<= 1;
- }
- used += scnprintf(buf+used, b_sz-used, "\n");
- return used;
+}
+static ssize_t trigout_sig_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
- struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- u32 sig_mask;
- int sig_idx, used = 0, b_sz = PAGE_SIZE;
- sig_mask = 0x1;
- for (sig_idx = 0; sig_idx < cfg->nr_trig_max; sig_idx++) {
if (sig_mask & con->con_out->used_mask) {
used += scnprintf(buf+used, b_sz-used, "%d ",
sig_idx);
}
sig_mask <<= 1;
- }
- used += scnprintf(buf+used, b_sz-used, "\n");
- return used;
+}
+/* convert a sig type id to a name */ +static const char * +cti_sig_type_name(struct cti_trig_con *con, int used_count, bool in) +{
- static const char * const sig_type_names[] = {
"genio", "intreq", "intack", "haltreq", "restartreq",
"pe_edbgreq", "pe_dbgrestart", "pe_ctiirq", "pe_pmuirq",
"pe_dbgtrigger", "etm_extout", "etm_extin", "snk_full",
"snk_acqcomp", "snk_flushcomp", "snk_flushin", "snk_trigin",
"stm_asyncout", "stm_tout_spte", "stm_tout_sw", "stm_tout_hete",
"stm_hwevent", "ela_tstart", "ela_tstop", "ela_dbgreq",
- };
- int idx = 0;
- struct cti_trig_grp *grp = in ? con->con_in : con->con_out;
- if (grp->sig_types) {
if (used_count < grp->nr_sigs)
idx = grp->sig_types[used_count];
- }
- return sig_type_names[idx];
+}
+static ssize_t trigin_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
- struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
- int sig_idx, used = 0, b_sz = PAGE_SIZE;
- const char *name;
- for (sig_idx = 0; sig_idx < con->con_in->nr_sigs; sig_idx++) {
name = cti_sig_type_name(con, sig_idx, true);
used += scnprintf(buf+used, b_sz-used, "%s ", name);
- }
- used += scnprintf(buf+used, b_sz-used, "\n");
- return used;
+}
+static ssize_t trigout_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
- struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
- int sig_idx, used = 0, b_sz = PAGE_SIZE;
- const char *name;
- for (sig_idx = 0; sig_idx < con->con_out->nr_sigs; sig_idx++) {
name = cti_sig_type_name(con, sig_idx, false);
used += scnprintf(buf+used, b_sz-used, "%s ", name);
- }
- used += scnprintf(buf+used, b_sz-used, "\n");
- return used;
+}
+typedef ssize_t (*p_show_fn)(struct device *dev, struct device_attribute *attr,
char *buf);
+enum cti_conn_attr_type {
- CTI_CON_ATTR_NAME,
- CTI_CON_ATTR_TRIGIN_SIG,
- CTI_CON_ATTR_TRIGOUT_SIG,
- CTI_CON_ATTR_TRIGIN_TYPES,
- CTI_CON_ATTR_TRIGOUT_TYPES
+};
+static p_show_fn show_fns[] = {
- con_name_show,
- trigin_sig_show,
- trigout_sig_show,
- trigin_type_show,
- trigout_type_show,
+};
+static const char * const base_names[] = {
- "_name",
- "_trgin_sig",
- "_trgout_sig",
- "_trgin_type",
- "_trgout_type",
+};
+struct dev_ext_attribute * +cti_create_con_sysfs_attr(int con_idx, enum cti_conn_attr_type attr_type) +{
- struct dev_ext_attribute *dev_ext_attr = 0;
- char *name = 0;
- dev_ext_attr = kzalloc(sizeof(struct dev_ext_attribute), GFP_KERNEL);
- if (dev_ext_attr) {
name = kzalloc(sizeof(char) * 16, GFP_KERNEL);
if (name) {
/* fill out the underlying attribute struct */
sprintf(name, "%d%s", con_idx, base_names[attr_type]);
dev_ext_attr->attr.attr.name = name;
dev_ext_attr->attr.attr.mode = 0444;
/* now the device_attribute struct */
dev_ext_attr->attr.show = show_fns[attr_type];
} else {
kfree(dev_ext_attr);
dev_ext_attr = 0;
}
- }
- return dev_ext_attr;
+}
+int cti_create_con_attr_set(int con_idx, int *next_attr_idx,
struct cti_device *ctidev,
struct cti_trig_con *con)
+{
- struct dev_ext_attribute *dev_ext_attr;
- int attr_idx = *next_attr_idx;
- dev_ext_attr = cti_create_con_sysfs_attr(con_idx, CTI_CON_ATTR_NAME);
- if (!dev_ext_attr)
goto create_con_attr_err;
- dev_ext_attr->var = con;
- ctidev->con_attrs[attr_idx++] = dev_ext_attr;
- if (con->con_in->nr_sigs > 0) {
dev_ext_attr =
cti_create_con_sysfs_attr(con_idx,
CTI_CON_ATTR_TRIGIN_SIG);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
dev_ext_attr =
cti_create_con_sysfs_attr(con_idx,
CTI_CON_ATTR_TRIGIN_TYPES);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
- }
- if (con->con_in->nr_sigs > 0) {
dev_ext_attr =
cti_create_con_sysfs_attr(con_idx,
CTI_CON_ATTR_TRIGOUT_SIG);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
dev_ext_attr =
cti_create_con_sysfs_attr(con_idx,
CTI_CON_ATTR_TRIGOUT_TYPES);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
- }
- *next_attr_idx = attr_idx;
- return 0;
+create_con_attr_err:
- cti_destroy_cons_sysfs(ctidev);
- return -ENOMEM;
+}
+int cti_create_cons_sysfs(struct cti_drvdata *drvdata) +{
- struct cti_device *ctidev = &drvdata->ctidev;
- int err = 0, n_attrs = 0, con_idx = 0;
- int next_attr_idx = CTI_SYSFS_CONS_ST_ATTR - 1;
- struct cti_trig_con *tc;
- n_attrs = CTI_SYSFS_CONS_ST_ATTR +
(CTI_SYSFS_CONS_DYN_ATTR * ctidev->nr_trig_con);
- ctidev->con_attrs =
kcalloc(n_attrs, sizeof(struct dev_ext_attribute *),
GFP_KERNEL);
- if (ctidev->con_attrs != 0) {
ctidev->con_attrs[0] = (struct dev_ext_attribute *)
&dev_attr_trigout_filtered.attr;
ctidev->con_attrs[1] = (struct dev_ext_attribute *)
&dev_attr_nr_cons.attr;
/* add dynamic set for each connection */
list_for_each_entry(tc, &ctidev->trig_cons, node) {
err = cti_create_con_attr_set(con_idx++,
&next_attr_idx,
ctidev,
tc);
/* if create con fails it will free all prior cons */
if (err)
return err;
}
coresight_cti_conns_group.attrs =
(struct attribute **)ctidev->con_attrs;
- } else
err = -ENOMEM;
- return err;
+}
+void cti_destroy_cons_sysfs(struct cti_device *ctidev) +{
- int n_attrs = CTI_SYSFS_CONS_ST_ATTR +
(CTI_SYSFS_CONS_DYN_ATTR * ctidev->nr_trig_con);
- int dyn_idx = CTI_SYSFS_CONS_ST_ATTR - 1;
- bool eol = false;
- struct dev_ext_attribute *dev_ext_attr = 0;
- if (ctidev->con_attrs) {
while (!eol && (dyn_idx < n_attrs)) {
dev_ext_attr = ctidev->con_attrs[dyn_idx];
if (dev_ext_attr) {
kfree(dev_ext_attr->attr.attr.name);
kfree(dev_ext_attr);
} else
eol = true;
dyn_idx++;
}
kfree(ctidev->con_attrs);
ctidev->con_attrs = 0;
- }
+}
/* attribute and group sysfs tables. */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, @@ -908,5 +1245,6 @@ const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_mgmt_group, &coresight_cti_regs_group, &coresight_cti_channels_group,
- &coresight_cti_conns_group, NULL,
}; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 6d452ca3725c..e0192fd50804 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -671,6 +671,14 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) /* setup cpu related CTI devices, otherwise assume powered */ drvdata->config.hw_powered = true;
- /* create dynamic attributes for connections */
- ret = cti_create_cons_sysfs(drvdata);
- if (ret) {
pr_err("%s: create dynamic sysfs entries failed\n",
pdata->name);
goto err_out;
- }
- /* set up coresight component description */ cti_desc.pdata = pdata; cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
@@ -746,6 +754,9 @@ void cti_device_release(struct device *dev) if (drvdata->ctidev.cpu >= 0) cti_cpu_drv[drvdata->ctidev.cpu] = 0;
- /* clear the dynamic sysfs associate with connections */
- cti_destroy_cons_sysfs(&drvdata->ctidev);
- /* clear the connection list items */ cti_free_conn_info(drvdata);
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index 7342f998f4c5..2a3557bfcd16 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -125,6 +125,7 @@ struct cti_trig_con {
assumed there is a single CTM per SoC, ID 0).
- @trig_cons: list of connections to this device.
- @cpu: CPU ID if associated with CPU, -1 otherwise.
*/
- @con_attrs: Dymanic sysfs attributes created to display connection info.
struct cti_device { @@ -132,6 +133,7 @@ struct cti_device { u32 ctm_id; struct list_head trig_cons; int cpu;
- struct dev_ext_attribute **con_attrs;
}; /** @@ -252,6 +254,8 @@ int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op, int cti_channel_setop(struct device *dev, enum cti_chan_set_op op, u32 channel_idx); void cti_write_intack(struct device *dev, u32 ackval); +int cti_create_cons_sysfs(struct cti_drvdata *drvdata); +void cti_destroy_cons_sysfs(struct cti_device *ctidev); /* cti powered and enabled */
#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
2.20.1
Notification mechanism for CPU power events such as CPU hotplug added as general notification mechnism in CoreSight common code to be used by all CPU bound CS devices.
Provides a per-device registration mechanism with callback fn and event type notifier. Core code now manages the CPU hotplug registrations and calls back any registered device when events occur.
Added registration call from CTI devices.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/coresight-cti.c | 54 +++++++- drivers/hwtracing/coresight/coresight-priv.h | 25 ++++ drivers/hwtracing/coresight/coresight.c | 138 +++++++++++++++++++ 3 files changed, 213 insertions(+), 4 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index e0192fd50804..faae7db44bd6 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -587,6 +587,38 @@ const struct coresight_ops cti_ops = { .ect_ops = &cti_ops_ect, };
+/* pm event notifiers */ +int cti_cs_cpupm_event(struct coresight_device *csdev, unsigned int cpuid, + enum cs_cpu_pm_events event) +{ + struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev); + + switch (event) { + /* called on the device cpu during registration */ + case CS_CPUPM_SMP_ON_REG_CPU: + drvdata->config.hw_powered = true; + break; + + /* cpu hotplug start */ + case CS_CPUPM_CPUHP_START: + spin_lock(&drvdata->spinlock); + drvdata->config.hw_powered = true; + spin_unlock(&drvdata->spinlock); + if (atomic_read(&drvdata->config.enable_req_count)) + cti_enable_hw(drvdata); + break; + + /* cpu hotplug stop */ + case CS_CPUPM_CPUHP_STOP: + cti_disable_hw(drvdata); + spin_lock(&drvdata->spinlock); + drvdata->config.hw_powered = false; + spin_unlock(&drvdata->spinlock); + break; + } + return 0; +} + static int cti_probe(struct amba_device *adev, const struct amba_id *id) { int ret = 0; @@ -668,9 +700,6 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) /* initialise default driver values */ cti_set_default_config(&drvdata->config);
- /* setup cpu related CTI devices, otherwise assume powered */ - drvdata->config.hw_powered = true; - /* create dynamic attributes for connections */ ret = cti_create_cons_sysfs(drvdata); if (ret) { @@ -693,6 +722,19 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) goto err_out; }
+ /* setup cpu related CTI PM, otherwise assume powered */ + if (drvdata->ctidev.cpu >= 0) { + drvdata->config.hw_powered = false; + ret = coresight_cpupm_register(drvdata->csdev, + drvdata->ctidev.cpu, + cti_cs_cpupm_event); + if (ret) { + pr_err("%s: CS cpu pm register failed\n", pdata->name); + goto err_out; + } + } else + drvdata->config.hw_powered = true; + /* add to list of CTI devices */ mutex_lock(&ect_mutex); ect_nd->cti_drv = drvdata; @@ -719,6 +761,8 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) return 0;
err_out: + if (drvdata->csdev && (drvdata->ctidev.cpu >= 0)) + coresight_cpupm_unregister(drvdata->csdev, drvdata->ctidev.cpu); kfree(drvdata); kfree(ect_nd); return ret; @@ -751,8 +795,10 @@ void cti_device_release(struct device *dev) struct ect_node *ect_item, *ect_tmp;
/* free up resources associated with the cti connections */ - if (drvdata->ctidev.cpu >= 0) + if (drvdata->ctidev.cpu >= 0) { cti_cpu_drv[drvdata->ctidev.cpu] = 0; + coresight_cpupm_unregister(drvdata->csdev, drvdata->ctidev.cpu); + }
/* clear the dynamic sysfs associate with connections */ cti_destroy_cons_sysfs(&drvdata->ctidev); diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index b1fca5c4bdb2..716669b2a40a 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -212,4 +212,29 @@ static inline void *coresight_get_uci_data(const struct amba_id *id) return 0; }
+/* + * cpu power management - allow any CPU bound Coresight device + * to use a common registration infrastructure for notifications. + */ + +/* cpu pm event types */ +enum cs_cpu_pm_events { + CS_CPUPM_SMP_ON_REG_CPU,/* smp call on cpu at registration */ + CS_CPUPM_CPUHP_START, /* hotplug starting CPU */ + CS_CPUPM_CPUHP_STOP, /* hotplug stopping CPU */ +}; + +/* callback function type */ +typedef int (*p_cs_cpu_pm_event)(struct coresight_device *csdev, + unsigned int cpuid, + enum cs_cpu_pm_events event); + +/* registration functions */ +int coresight_cpupm_register(struct coresight_device *csdev, + unsigned int cpuid, + p_cs_cpu_pm_event callback); +void coresight_cpupm_unregister(struct coresight_device *csdev, + unsigned int cpuid); + + #endif diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 9d56b3b32172..afcc330e61eb 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1295,3 +1295,141 @@ void coresight_unregister(struct coresight_device *csdev) device_unregister(&csdev->dev); } EXPORT_SYMBOL_GPL(coresight_unregister); + +/* cs general CPU bound device pm notification */ +struct coresight_cpupm_item { + struct coresight_device *csdev; + p_cs_cpu_pm_event callback; + struct list_head node; +}; + +/* CS cpu pm notifier lists per cpu */ +static DEFINE_PER_CPU(struct list_head *, cs_cpupm_notifier); + +static enum cpuhp_state cscpu_hp_online; +static int cscpu_dev_reg_cnt; + +static int coresight_iterate_cpu_event_list(unsigned int cpu, + enum cs_cpu_pm_events event) +{ + struct list_head *cpu_list; + struct coresight_cpupm_item *item; + + mutex_lock(&coresight_mutex); + cpu_list = per_cpu(cs_cpupm_notifier, cpu); + if (!list_empty(cpu_list)) { + list_for_each_entry(item, cpu_list, node) { + item->callback(item->csdev, cpu, event); + } + } + mutex_unlock(&coresight_mutex); + return 0; +} + +static int coresight_hp_starting_cpu(unsigned int cpu) +{ + return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_START); +} + +static int coresight_hp_stopping_cpu(unsigned int cpu) +{ + return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_STOP); +} + +struct cs_smp_reg_cb_info { + unsigned int cpu; + struct coresight_cpupm_item *item; +}; + +static void coresight_smp_onreg_callback(void *info) +{ + struct cs_smp_reg_cb_info *cb_info = info; + + cb_info->item->callback(cb_info->item->csdev, cb_info->cpu, + CS_CPUPM_SMP_ON_REG_CPU); +} + +int coresight_cpupm_register(struct coresight_device *csdev, + unsigned int cpuid, + p_cs_cpu_pm_event callback) +{ + struct list_head *cpu_list; + struct coresight_cpupm_item *item; + int err = 0; + struct cs_smp_reg_cb_info on_reg_cb_info; + + mutex_lock(&coresight_mutex); + + cpu_list = per_cpu(cs_cpupm_notifier, cpuid); + + /* init list if no head yet */ + if (!cpu_list) { + cpu_list = kzalloc(sizeof(struct list_head), GFP_KERNEL); + if (!cpu_list) + goto cpupm_reg_done; + else + INIT_LIST_HEAD(cpu_list); + } + + /* create a list item */ + item = kzalloc(sizeof(struct coresight_cpupm_item), GFP_KERNEL); + if (!item) { + err = -ENOMEM; + goto cpupm_reg_done; + } + + item->csdev = csdev; + item->callback = callback; + + /* init the cpu hotplug notification */ + cpus_read_lock(); + if (!cscpu_dev_reg_cnt) { + cscpu_hp_online = cpuhp_setup_state_nocalls_cpuslocked( + CPUHP_AP_ONLINE_DYN, "arm/coresightcpu:online", + coresight_hp_starting_cpu, + coresight_hp_stopping_cpu); + if (cscpu_hp_online < 0) { + err = cscpu_hp_online; + kfree(item); + goto cpupm_hp_done; + } + } + cscpu_dev_reg_cnt++; + list_add(&item->node, cpu_list); + + /* smp callback on dev cpu @ registration */ + on_reg_cb_info.item = item; + on_reg_cb_info.cpu = cpuid; + smp_call_function_single(cpuid, coresight_smp_onreg_callback, + &on_reg_cb_info, 1); + +cpupm_hp_done: + cpus_read_unlock(); + +cpupm_reg_done: + mutex_unlock(&coresight_mutex); + return err; +} +EXPORT_SYMBOL_GPL(coresight_cpupm_register); + +void coresight_cpupm_unregister(struct coresight_device *csdev, + unsigned int cpuid) +{ + struct list_head *cpu_list; + struct coresight_cpupm_item *item, *tmp; + + mutex_lock(&coresight_mutex); + cpu_list = per_cpu(cs_cpupm_notifier, cpuid); + if (!list_empty(cpu_list)) { + list_for_each_entry_safe(item, tmp, cpu_list, node) { + if (item->csdev == csdev) { + list_del(&item->node); + kfree(item); + goto cs_cpupm_item_removed; + } + } + } +cs_cpupm_item_removed: + mutex_unlock(&coresight_mutex); +} +EXPORT_SYMBOL_GPL(coresight_cpupm_unregister);
On Wed, May 01, 2019 at 09:49:39AM +0100, Mike Leach wrote:
Notification mechanism for CPU power events such as CPU hotplug added as general notification mechnism in CoreSight common code to be used by all CPU bound CS devices.
Provides a per-device registration mechanism with callback fn and event type notifier. Core code now manages the CPU hotplug registrations and calls back any registered device when events occur.
Added registration call from CTI devices.
From what I understand Andrew Murray is currently working on something like
that. Have you guys had a chance to talk about this and compare strategies?
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 54 +++++++- drivers/hwtracing/coresight/coresight-priv.h | 25 ++++ drivers/hwtracing/coresight/coresight.c | 138 +++++++++++++++++++ 3 files changed, 213 insertions(+), 4 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index e0192fd50804..faae7db44bd6 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -587,6 +587,38 @@ const struct coresight_ops cti_ops = { .ect_ops = &cti_ops_ect, }; +/* pm event notifiers */ +int cti_cs_cpupm_event(struct coresight_device *csdev, unsigned int cpuid,
enum cs_cpu_pm_events event)
+{
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- switch (event) {
/* called on the device cpu during registration */
- case CS_CPUPM_SMP_ON_REG_CPU:
drvdata->config.hw_powered = true;
break;
/* cpu hotplug start */
- case CS_CPUPM_CPUHP_START:
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = true;
spin_unlock(&drvdata->spinlock);
if (atomic_read(&drvdata->config.enable_req_count))
cti_enable_hw(drvdata);
break;
/* cpu hotplug stop */
- case CS_CPUPM_CPUHP_STOP:
cti_disable_hw(drvdata);
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = false;
spin_unlock(&drvdata->spinlock);
break;
- }
- return 0;
+}
static int cti_probe(struct amba_device *adev, const struct amba_id *id) { int ret = 0; @@ -668,9 +700,6 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) /* initialise default driver values */ cti_set_default_config(&drvdata->config);
- /* setup cpu related CTI devices, otherwise assume powered */
- drvdata->config.hw_powered = true;
- /* create dynamic attributes for connections */ ret = cti_create_cons_sysfs(drvdata); if (ret) {
@@ -693,6 +722,19 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) goto err_out; }
- /* setup cpu related CTI PM, otherwise assume powered */
- if (drvdata->ctidev.cpu >= 0) {
drvdata->config.hw_powered = false;
ret = coresight_cpupm_register(drvdata->csdev,
drvdata->ctidev.cpu,
cti_cs_cpupm_event);
if (ret) {
pr_err("%s: CS cpu pm register failed\n", pdata->name);
goto err_out;
}
- } else
drvdata->config.hw_powered = true;
- /* add to list of CTI devices */ mutex_lock(&ect_mutex); ect_nd->cti_drv = drvdata;
@@ -719,6 +761,8 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) return 0; err_out:
- if (drvdata->csdev && (drvdata->ctidev.cpu >= 0))
kfree(drvdata); kfree(ect_nd); return ret;coresight_cpupm_unregister(drvdata->csdev, drvdata->ctidev.cpu);
@@ -751,8 +795,10 @@ void cti_device_release(struct device *dev) struct ect_node *ect_item, *ect_tmp; /* free up resources associated with the cti connections */
- if (drvdata->ctidev.cpu >= 0)
- if (drvdata->ctidev.cpu >= 0) { cti_cpu_drv[drvdata->ctidev.cpu] = 0;
coresight_cpupm_unregister(drvdata->csdev, drvdata->ctidev.cpu);
- }
/* clear the dynamic sysfs associate with connections */ cti_destroy_cons_sysfs(&drvdata->ctidev); diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index b1fca5c4bdb2..716669b2a40a 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -212,4 +212,29 @@ static inline void *coresight_get_uci_data(const struct amba_id *id) return 0; } +/*
- cpu power management - allow any CPU bound Coresight device
- to use a common registration infrastructure for notifications.
- */
+/* cpu pm event types */ +enum cs_cpu_pm_events {
- CS_CPUPM_SMP_ON_REG_CPU,/* smp call on cpu at registration */
- CS_CPUPM_CPUHP_START, /* hotplug starting CPU */
- CS_CPUPM_CPUHP_STOP, /* hotplug stopping CPU */
+};
+/* callback function type */ +typedef int (*p_cs_cpu_pm_event)(struct coresight_device *csdev,
unsigned int cpuid,
enum cs_cpu_pm_events event);
+/* registration functions */ +int coresight_cpupm_register(struct coresight_device *csdev,
unsigned int cpuid,
p_cs_cpu_pm_event callback);
+void coresight_cpupm_unregister(struct coresight_device *csdev,
unsigned int cpuid);
#endif diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 9d56b3b32172..afcc330e61eb 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1295,3 +1295,141 @@ void coresight_unregister(struct coresight_device *csdev) device_unregister(&csdev->dev); } EXPORT_SYMBOL_GPL(coresight_unregister);
+/* cs general CPU bound device pm notification */ +struct coresight_cpupm_item {
- struct coresight_device *csdev;
- p_cs_cpu_pm_event callback;
- struct list_head node;
+};
+/* CS cpu pm notifier lists per cpu */ +static DEFINE_PER_CPU(struct list_head *, cs_cpupm_notifier);
+static enum cpuhp_state cscpu_hp_online; +static int cscpu_dev_reg_cnt;
+static int coresight_iterate_cpu_event_list(unsigned int cpu,
enum cs_cpu_pm_events event)
+{
- struct list_head *cpu_list;
- struct coresight_cpupm_item *item;
- mutex_lock(&coresight_mutex);
- cpu_list = per_cpu(cs_cpupm_notifier, cpu);
- if (!list_empty(cpu_list)) {
list_for_each_entry(item, cpu_list, node) {
item->callback(item->csdev, cpu, event);
}
- }
- mutex_unlock(&coresight_mutex);
- return 0;
+}
+static int coresight_hp_starting_cpu(unsigned int cpu) +{
- return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_START);
+}
+static int coresight_hp_stopping_cpu(unsigned int cpu) +{
- return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_STOP);
+}
+struct cs_smp_reg_cb_info {
- unsigned int cpu;
- struct coresight_cpupm_item *item;
+};
+static void coresight_smp_onreg_callback(void *info) +{
- struct cs_smp_reg_cb_info *cb_info = info;
- cb_info->item->callback(cb_info->item->csdev, cb_info->cpu,
CS_CPUPM_SMP_ON_REG_CPU);
+}
+int coresight_cpupm_register(struct coresight_device *csdev,
unsigned int cpuid,
p_cs_cpu_pm_event callback)
+{
- struct list_head *cpu_list;
- struct coresight_cpupm_item *item;
- int err = 0;
- struct cs_smp_reg_cb_info on_reg_cb_info;
- mutex_lock(&coresight_mutex);
- cpu_list = per_cpu(cs_cpupm_notifier, cpuid);
- /* init list if no head yet */
- if (!cpu_list) {
cpu_list = kzalloc(sizeof(struct list_head), GFP_KERNEL);
if (!cpu_list)
goto cpupm_reg_done;
else
INIT_LIST_HEAD(cpu_list);
- }
- /* create a list item */
- item = kzalloc(sizeof(struct coresight_cpupm_item), GFP_KERNEL);
- if (!item) {
err = -ENOMEM;
goto cpupm_reg_done;
- }
- item->csdev = csdev;
- item->callback = callback;
- /* init the cpu hotplug notification */
- cpus_read_lock();
- if (!cscpu_dev_reg_cnt) {
cscpu_hp_online = cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ONLINE_DYN, "arm/coresightcpu:online",
coresight_hp_starting_cpu,
coresight_hp_stopping_cpu);
if (cscpu_hp_online < 0) {
err = cscpu_hp_online;
kfree(item);
goto cpupm_hp_done;
}
- }
- cscpu_dev_reg_cnt++;
- list_add(&item->node, cpu_list);
- /* smp callback on dev cpu @ registration */
- on_reg_cb_info.item = item;
- on_reg_cb_info.cpu = cpuid;
- smp_call_function_single(cpuid, coresight_smp_onreg_callback,
&on_reg_cb_info, 1);
+cpupm_hp_done:
- cpus_read_unlock();
+cpupm_reg_done:
- mutex_unlock(&coresight_mutex);
- return err;
+} +EXPORT_SYMBOL_GPL(coresight_cpupm_register);
+void coresight_cpupm_unregister(struct coresight_device *csdev,
unsigned int cpuid)
+{
- struct list_head *cpu_list;
- struct coresight_cpupm_item *item, *tmp;
- mutex_lock(&coresight_mutex);
- cpu_list = per_cpu(cs_cpupm_notifier, cpuid);
- if (!list_empty(cpu_list)) {
list_for_each_entry_safe(item, tmp, cpu_list, node) {
if (item->csdev == csdev) {
list_del(&item->node);
kfree(item);
goto cs_cpupm_item_removed;
}
}
- }
+cs_cpupm_item_removed:
- mutex_unlock(&coresight_mutex);
+}
+EXPORT_SYMBOL_GPL(coresight_cpupm_unregister);
2.20.1
Hi Mathieu,
On Tue, 14 May 2019 at 18:01, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, May 01, 2019 at 09:49:39AM +0100, Mike Leach wrote:
Notification mechanism for CPU power events such as CPU hotplug added as general notification mechnism in CoreSight common code to be used by all CPU bound CS devices.
Provides a per-device registration mechanism with callback fn and event type notifier. Core code now manages the CPU hotplug registrations and calls back any registered device when events occur.
Added registration call from CTI devices.
From what I understand Andrew Murray is currently working on something like that. Have you guys had a chance to talk about this and compare strategies?
I'm not aware of any work in this area - not seen anything on this list, but possible I may have missed something on the linux-arm-kernel ML as I have not looked since before my holiday. Are you aware of any postings I should see?
I know we discussed your intentions re: gen-pd @ connect - but this patch is simply to move the CPUHP out of specific drivers and into the common core code.
Thereafter it should be possible to a) remove the CPU-HP out of the ETM driver. b) have ETM and CPU-debug drivers use the common code to register with the notifier - this would be a follow-up to the current CTI specific set.
Regards
Mike
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 54 +++++++- drivers/hwtracing/coresight/coresight-priv.h | 25 ++++ drivers/hwtracing/coresight/coresight.c | 138 +++++++++++++++++++ 3 files changed, 213 insertions(+), 4 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index e0192fd50804..faae7db44bd6 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -587,6 +587,38 @@ const struct coresight_ops cti_ops = { .ect_ops = &cti_ops_ect, };
+/* pm event notifiers */ +int cti_cs_cpupm_event(struct coresight_device *csdev, unsigned int cpuid,
enum cs_cpu_pm_events event)
+{
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
switch (event) {
/* called on the device cpu during registration */
case CS_CPUPM_SMP_ON_REG_CPU:
drvdata->config.hw_powered = true;
break;
/* cpu hotplug start */
case CS_CPUPM_CPUHP_START:
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = true;
spin_unlock(&drvdata->spinlock);
if (atomic_read(&drvdata->config.enable_req_count))
cti_enable_hw(drvdata);
break;
/* cpu hotplug stop */
case CS_CPUPM_CPUHP_STOP:
cti_disable_hw(drvdata);
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = false;
spin_unlock(&drvdata->spinlock);
break;
}
return 0;
+}
static int cti_probe(struct amba_device *adev, const struct amba_id *id) { int ret = 0; @@ -668,9 +700,6 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) /* initialise default driver values */ cti_set_default_config(&drvdata->config);
/* setup cpu related CTI devices, otherwise assume powered */
drvdata->config.hw_powered = true;
/* create dynamic attributes for connections */ ret = cti_create_cons_sysfs(drvdata); if (ret) {
@@ -693,6 +722,19 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) goto err_out; }
/* setup cpu related CTI PM, otherwise assume powered */
if (drvdata->ctidev.cpu >= 0) {
drvdata->config.hw_powered = false;
ret = coresight_cpupm_register(drvdata->csdev,
drvdata->ctidev.cpu,
cti_cs_cpupm_event);
if (ret) {
pr_err("%s: CS cpu pm register failed\n", pdata->name);
goto err_out;
}
} else
drvdata->config.hw_powered = true;
/* add to list of CTI devices */ mutex_lock(&ect_mutex); ect_nd->cti_drv = drvdata;
@@ -719,6 +761,8 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) return 0;
err_out:
if (drvdata->csdev && (drvdata->ctidev.cpu >= 0))
coresight_cpupm_unregister(drvdata->csdev, drvdata->ctidev.cpu); kfree(drvdata); kfree(ect_nd); return ret;
@@ -751,8 +795,10 @@ void cti_device_release(struct device *dev) struct ect_node *ect_item, *ect_tmp;
/* free up resources associated with the cti connections */
if (drvdata->ctidev.cpu >= 0)
if (drvdata->ctidev.cpu >= 0) { cti_cpu_drv[drvdata->ctidev.cpu] = 0;
coresight_cpupm_unregister(drvdata->csdev, drvdata->ctidev.cpu);
} /* clear the dynamic sysfs associate with connections */ cti_destroy_cons_sysfs(&drvdata->ctidev);
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index b1fca5c4bdb2..716669b2a40a 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -212,4 +212,29 @@ static inline void *coresight_get_uci_data(const struct amba_id *id) return 0; }
+/*
- cpu power management - allow any CPU bound Coresight device
- to use a common registration infrastructure for notifications.
- */
+/* cpu pm event types */ +enum cs_cpu_pm_events {
CS_CPUPM_SMP_ON_REG_CPU,/* smp call on cpu at registration */
CS_CPUPM_CPUHP_START, /* hotplug starting CPU */
CS_CPUPM_CPUHP_STOP, /* hotplug stopping CPU */
+};
+/* callback function type */ +typedef int (*p_cs_cpu_pm_event)(struct coresight_device *csdev,
unsigned int cpuid,
enum cs_cpu_pm_events event);
+/* registration functions */ +int coresight_cpupm_register(struct coresight_device *csdev,
unsigned int cpuid,
p_cs_cpu_pm_event callback);
+void coresight_cpupm_unregister(struct coresight_device *csdev,
unsigned int cpuid);
#endif diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 9d56b3b32172..afcc330e61eb 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1295,3 +1295,141 @@ void coresight_unregister(struct coresight_device *csdev) device_unregister(&csdev->dev); } EXPORT_SYMBOL_GPL(coresight_unregister);
+/* cs general CPU bound device pm notification */ +struct coresight_cpupm_item {
struct coresight_device *csdev;
p_cs_cpu_pm_event callback;
struct list_head node;
+};
+/* CS cpu pm notifier lists per cpu */ +static DEFINE_PER_CPU(struct list_head *, cs_cpupm_notifier);
+static enum cpuhp_state cscpu_hp_online; +static int cscpu_dev_reg_cnt;
+static int coresight_iterate_cpu_event_list(unsigned int cpu,
enum cs_cpu_pm_events event)
+{
struct list_head *cpu_list;
struct coresight_cpupm_item *item;
mutex_lock(&coresight_mutex);
cpu_list = per_cpu(cs_cpupm_notifier, cpu);
if (!list_empty(cpu_list)) {
list_for_each_entry(item, cpu_list, node) {
item->callback(item->csdev, cpu, event);
}
}
mutex_unlock(&coresight_mutex);
return 0;
+}
+static int coresight_hp_starting_cpu(unsigned int cpu) +{
return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_START);
+}
+static int coresight_hp_stopping_cpu(unsigned int cpu) +{
return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_STOP);
+}
+struct cs_smp_reg_cb_info {
unsigned int cpu;
struct coresight_cpupm_item *item;
+};
+static void coresight_smp_onreg_callback(void *info) +{
struct cs_smp_reg_cb_info *cb_info = info;
cb_info->item->callback(cb_info->item->csdev, cb_info->cpu,
CS_CPUPM_SMP_ON_REG_CPU);
+}
+int coresight_cpupm_register(struct coresight_device *csdev,
unsigned int cpuid,
p_cs_cpu_pm_event callback)
+{
struct list_head *cpu_list;
struct coresight_cpupm_item *item;
int err = 0;
struct cs_smp_reg_cb_info on_reg_cb_info;
mutex_lock(&coresight_mutex);
cpu_list = per_cpu(cs_cpupm_notifier, cpuid);
/* init list if no head yet */
if (!cpu_list) {
cpu_list = kzalloc(sizeof(struct list_head), GFP_KERNEL);
if (!cpu_list)
goto cpupm_reg_done;
else
INIT_LIST_HEAD(cpu_list);
}
/* create a list item */
item = kzalloc(sizeof(struct coresight_cpupm_item), GFP_KERNEL);
if (!item) {
err = -ENOMEM;
goto cpupm_reg_done;
}
item->csdev = csdev;
item->callback = callback;
/* init the cpu hotplug notification */
cpus_read_lock();
if (!cscpu_dev_reg_cnt) {
cscpu_hp_online = cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ONLINE_DYN, "arm/coresightcpu:online",
coresight_hp_starting_cpu,
coresight_hp_stopping_cpu);
if (cscpu_hp_online < 0) {
err = cscpu_hp_online;
kfree(item);
goto cpupm_hp_done;
}
}
cscpu_dev_reg_cnt++;
list_add(&item->node, cpu_list);
/* smp callback on dev cpu @ registration */
on_reg_cb_info.item = item;
on_reg_cb_info.cpu = cpuid;
smp_call_function_single(cpuid, coresight_smp_onreg_callback,
&on_reg_cb_info, 1);
+cpupm_hp_done:
cpus_read_unlock();
+cpupm_reg_done:
mutex_unlock(&coresight_mutex);
return err;
+} +EXPORT_SYMBOL_GPL(coresight_cpupm_register);
+void coresight_cpupm_unregister(struct coresight_device *csdev,
unsigned int cpuid)
+{
struct list_head *cpu_list;
struct coresight_cpupm_item *item, *tmp;
mutex_lock(&coresight_mutex);
cpu_list = per_cpu(cs_cpupm_notifier, cpuid);
if (!list_empty(cpu_list)) {
list_for_each_entry_safe(item, tmp, cpu_list, node) {
if (item->csdev == csdev) {
list_del(&item->node);
kfree(item);
goto cs_cpupm_item_removed;
}
}
}
+cs_cpupm_item_removed:
mutex_unlock(&coresight_mutex);
+}
+EXPORT_SYMBOL_GPL(coresight_cpupm_unregister);
2.20.1
Good day,
On Wed, 15 May 2019 at 09:01, Mike Leach mike.leach@linaro.org wrote:
Hi Mathieu,
On Tue, 14 May 2019 at 18:01, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, May 01, 2019 at 09:49:39AM +0100, Mike Leach wrote:
Notification mechanism for CPU power events such as CPU hotplug added as general notification mechnism in CoreSight common code to be used by all CPU bound CS devices.
Provides a per-device registration mechanism with callback fn and event type notifier. Core code now manages the CPU hotplug registrations and calls back any registered device when events occur.
Added registration call from CTI devices.
From what I understand Andrew Murray is currently working on something like that. Have you guys had a chance to talk about this and compare strategies?
I'm not aware of any work in this area - not seen anything on this list, but possible I may have missed something on the linux-arm-kernel ML as I have not looked since before my holiday. Are you aware of any postings I should see?
I haven't seen any patches yet - this came up during an IRC conversation between Suzuki and I.
I know we discussed your intentions re: gen-pd @ connect - but this patch is simply to move the CPUHP out of specific drivers and into the common core code.
Right.
Thereafter it should be possible to a) remove the CPU-HP out of the ETM driver. b) have ETM and CPU-debug drivers use the common code to register with the notifier - this would be a follow-up to the current CTI specific set.
The best way to proceed would be to fix CPU hotplug for ETM and CPU-debug and then use the same mechanism for CTI. Since Andrew is working on doing just that I thought it might be a good idea for you guys to compare strategies. On the flip side I understand you have plenty of work to do with CTI - keep going with the current approach and we'll see how things play out. In the worse case scenarios I'll step in and do the ETM and CPU HP parts.
Regards
Mike
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 54 +++++++- drivers/hwtracing/coresight/coresight-priv.h | 25 ++++ drivers/hwtracing/coresight/coresight.c | 138 +++++++++++++++++++ 3 files changed, 213 insertions(+), 4 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index e0192fd50804..faae7db44bd6 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -587,6 +587,38 @@ const struct coresight_ops cti_ops = { .ect_ops = &cti_ops_ect, };
+/* pm event notifiers */ +int cti_cs_cpupm_event(struct coresight_device *csdev, unsigned int cpuid,
enum cs_cpu_pm_events event)
+{
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
switch (event) {
/* called on the device cpu during registration */
case CS_CPUPM_SMP_ON_REG_CPU:
drvdata->config.hw_powered = true;
break;
/* cpu hotplug start */
case CS_CPUPM_CPUHP_START:
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = true;
spin_unlock(&drvdata->spinlock);
if (atomic_read(&drvdata->config.enable_req_count))
cti_enable_hw(drvdata);
break;
/* cpu hotplug stop */
case CS_CPUPM_CPUHP_STOP:
cti_disable_hw(drvdata);
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = false;
spin_unlock(&drvdata->spinlock);
break;
}
return 0;
+}
static int cti_probe(struct amba_device *adev, const struct amba_id *id) { int ret = 0; @@ -668,9 +700,6 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) /* initialise default driver values */ cti_set_default_config(&drvdata->config);
/* setup cpu related CTI devices, otherwise assume powered */
drvdata->config.hw_powered = true;
/* create dynamic attributes for connections */ ret = cti_create_cons_sysfs(drvdata); if (ret) {
@@ -693,6 +722,19 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) goto err_out; }
/* setup cpu related CTI PM, otherwise assume powered */
if (drvdata->ctidev.cpu >= 0) {
drvdata->config.hw_powered = false;
ret = coresight_cpupm_register(drvdata->csdev,
drvdata->ctidev.cpu,
cti_cs_cpupm_event);
if (ret) {
pr_err("%s: CS cpu pm register failed\n", pdata->name);
goto err_out;
}
} else
drvdata->config.hw_powered = true;
/* add to list of CTI devices */ mutex_lock(&ect_mutex); ect_nd->cti_drv = drvdata;
@@ -719,6 +761,8 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) return 0;
err_out:
if (drvdata->csdev && (drvdata->ctidev.cpu >= 0))
coresight_cpupm_unregister(drvdata->csdev, drvdata->ctidev.cpu); kfree(drvdata); kfree(ect_nd); return ret;
@@ -751,8 +795,10 @@ void cti_device_release(struct device *dev) struct ect_node *ect_item, *ect_tmp;
/* free up resources associated with the cti connections */
if (drvdata->ctidev.cpu >= 0)
if (drvdata->ctidev.cpu >= 0) { cti_cpu_drv[drvdata->ctidev.cpu] = 0;
coresight_cpupm_unregister(drvdata->csdev, drvdata->ctidev.cpu);
} /* clear the dynamic sysfs associate with connections */ cti_destroy_cons_sysfs(&drvdata->ctidev);
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index b1fca5c4bdb2..716669b2a40a 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -212,4 +212,29 @@ static inline void *coresight_get_uci_data(const struct amba_id *id) return 0; }
+/*
- cpu power management - allow any CPU bound Coresight device
- to use a common registration infrastructure for notifications.
- */
+/* cpu pm event types */ +enum cs_cpu_pm_events {
CS_CPUPM_SMP_ON_REG_CPU,/* smp call on cpu at registration */
CS_CPUPM_CPUHP_START, /* hotplug starting CPU */
CS_CPUPM_CPUHP_STOP, /* hotplug stopping CPU */
+};
+/* callback function type */ +typedef int (*p_cs_cpu_pm_event)(struct coresight_device *csdev,
unsigned int cpuid,
enum cs_cpu_pm_events event);
+/* registration functions */ +int coresight_cpupm_register(struct coresight_device *csdev,
unsigned int cpuid,
p_cs_cpu_pm_event callback);
+void coresight_cpupm_unregister(struct coresight_device *csdev,
unsigned int cpuid);
#endif diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 9d56b3b32172..afcc330e61eb 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1295,3 +1295,141 @@ void coresight_unregister(struct coresight_device *csdev) device_unregister(&csdev->dev); } EXPORT_SYMBOL_GPL(coresight_unregister);
+/* cs general CPU bound device pm notification */ +struct coresight_cpupm_item {
struct coresight_device *csdev;
p_cs_cpu_pm_event callback;
struct list_head node;
+};
+/* CS cpu pm notifier lists per cpu */ +static DEFINE_PER_CPU(struct list_head *, cs_cpupm_notifier);
+static enum cpuhp_state cscpu_hp_online; +static int cscpu_dev_reg_cnt;
+static int coresight_iterate_cpu_event_list(unsigned int cpu,
enum cs_cpu_pm_events event)
+{
struct list_head *cpu_list;
struct coresight_cpupm_item *item;
mutex_lock(&coresight_mutex);
cpu_list = per_cpu(cs_cpupm_notifier, cpu);
if (!list_empty(cpu_list)) {
list_for_each_entry(item, cpu_list, node) {
item->callback(item->csdev, cpu, event);
}
}
mutex_unlock(&coresight_mutex);
return 0;
+}
+static int coresight_hp_starting_cpu(unsigned int cpu) +{
return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_START);
+}
+static int coresight_hp_stopping_cpu(unsigned int cpu) +{
return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_STOP);
+}
+struct cs_smp_reg_cb_info {
unsigned int cpu;
struct coresight_cpupm_item *item;
+};
+static void coresight_smp_onreg_callback(void *info) +{
struct cs_smp_reg_cb_info *cb_info = info;
cb_info->item->callback(cb_info->item->csdev, cb_info->cpu,
CS_CPUPM_SMP_ON_REG_CPU);
+}
+int coresight_cpupm_register(struct coresight_device *csdev,
unsigned int cpuid,
p_cs_cpu_pm_event callback)
+{
struct list_head *cpu_list;
struct coresight_cpupm_item *item;
int err = 0;
struct cs_smp_reg_cb_info on_reg_cb_info;
mutex_lock(&coresight_mutex);
cpu_list = per_cpu(cs_cpupm_notifier, cpuid);
/* init list if no head yet */
if (!cpu_list) {
cpu_list = kzalloc(sizeof(struct list_head), GFP_KERNEL);
if (!cpu_list)
goto cpupm_reg_done;
else
INIT_LIST_HEAD(cpu_list);
}
/* create a list item */
item = kzalloc(sizeof(struct coresight_cpupm_item), GFP_KERNEL);
if (!item) {
err = -ENOMEM;
goto cpupm_reg_done;
}
item->csdev = csdev;
item->callback = callback;
/* init the cpu hotplug notification */
cpus_read_lock();
if (!cscpu_dev_reg_cnt) {
cscpu_hp_online = cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ONLINE_DYN, "arm/coresightcpu:online",
coresight_hp_starting_cpu,
coresight_hp_stopping_cpu);
if (cscpu_hp_online < 0) {
err = cscpu_hp_online;
kfree(item);
goto cpupm_hp_done;
}
}
cscpu_dev_reg_cnt++;
list_add(&item->node, cpu_list);
/* smp callback on dev cpu @ registration */
on_reg_cb_info.item = item;
on_reg_cb_info.cpu = cpuid;
smp_call_function_single(cpuid, coresight_smp_onreg_callback,
&on_reg_cb_info, 1);
+cpupm_hp_done:
cpus_read_unlock();
+cpupm_reg_done:
mutex_unlock(&coresight_mutex);
return err;
+} +EXPORT_SYMBOL_GPL(coresight_cpupm_register);
+void coresight_cpupm_unregister(struct coresight_device *csdev,
unsigned int cpuid)
+{
struct list_head *cpu_list;
struct coresight_cpupm_item *item, *tmp;
mutex_lock(&coresight_mutex);
cpu_list = per_cpu(cs_cpupm_notifier, cpuid);
if (!list_empty(cpu_list)) {
list_for_each_entry_safe(item, tmp, cpu_list, node) {
if (item->csdev == csdev) {
list_del(&item->node);
kfree(item);
goto cs_cpupm_item_removed;
}
}
}
+cs_cpupm_item_removed:
mutex_unlock(&coresight_mutex);
+}
+EXPORT_SYMBOL_GPL(coresight_cpupm_unregister);
2.20.1
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
Adds new .txt file describing the bindings required to define CTI in the device trees.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../bindings/arm/coresight-ect-cti.txt | 203 ++++++++++++++++++ .../devicetree/bindings/arm/coresight.txt | 7 + 2 files changed, 210 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/coresight-ect-cti.txt
diff --git a/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt b/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt new file mode 100644 index 000000000000..ba95c0002ccc --- /dev/null +++ b/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt @@ -0,0 +1,203 @@ +*CoreSight Embedded Cross Trigger Components + +The CoreSight Embedded Cross Trigger (ECT) consists of CTI devices connected +to one or more CoreSight components and/or a CPU, with CTIs interconnected in +a star topology via the CTM (which is not programmable). The ECT components +are not part of the trace generation data path and are thus not part of the +CoreSight graph described above. + +The CTI component properties define the connections between the individual CTI +and the components it is directly connected to, consisting of input and output +hardware trigger signals. CTIs can have a maximum number of input and output +hardware trigger signals (8 each for v1 CTI, 32 each for v2 CTI). The number is +defined at design time, the maximum of each defined in the DEVID register. +Note that some hardware trigger signals can be connected to non-CoreSight +components (e.g. UART etc) depending on hardware implementation. + +CTIs are interconnect in a star topology via the CTM, using a number of +programmable channels usually 4, but again implementation defined and described +in the DEVID register. The star topology is not required to be described in the +bindings as the actual connections are software programmable. + +In general the connections between CTI and components via the trigger signals +are implementation defined, other than when v8 core and ETM is present. The v8 +architecture then defines the required signal connections between core and CTI, +and ETM and CTI, if the ETM if present. + +Certain triggers between CoreSight devices and the CTI have specific types / +usages. These can be defined along with the signal indexes with the constants +defined in <dt-bindings/arm/coresight-cti-dt.h> + +For example a CTI connected to a core will usually have a DBGREQ signal. This +is defined in the binding as type PE_EDBGREQ. These types will appear in an +optional array alongside the signal indexes. Omitting types will default all +signals to GEN_IO. + +The minimum required binding for a CTI consist of the only the required +properties above. + +e.g. + + /* sys cti 0 - connects to STM, ETF0 (core trace) TPIU and ETR */ + cti0: cti@20020000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x20020000 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + }; + + +This will result in the driver using the DEVID register to set the +input and output triggers and channels in use. Any user / client +application will require additional information on the connections +between the CTI and other components for correct operation. + +Where information is immediately available for the component connections then +a series of trigger connection nodes can be defined (trig-conns). + +These connections explicitly define the input and output triggers between the +CTI and a connected component. Connections to an cpu use the standard cpu +property, connections to other CoreSight components use the arm,cs-dev-assoc +property. + +Where the signals are connected to a device that is not coresight device then +no association is registered. + +cti@858000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x858000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + + trig-conns@0 { + arm,trig-in-sigs = <4 5 6 7>; + arm,trig-in-types = <ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT>; + arm,trig-out-sigs = <4 5 6 7>; + arm,trig-out-types = <ETM_EXTIN ETM_EXTIN ETM_EXTIN ETM_EXTIN>; + arm,cs-dev-assoc = <&etm0>; + }; + + trig-conns@1 { + cpu = <&CPU0>; + arm,trig-in-sigs=<0 1>; + arm,trig-in-types=<PE_DBGTRIGGER PE_PMUIRQ>; + arm,trig-out-sigs=<0 1 2 >; + arm,trig-out-types=<PE_EDBGREQ PE_DBGRESTART PE_CTIIRQ>; + arm,trig-filters=<0>; + }; + +}; + +Note that where input and output triggers are defined as above, then the driver +will limit the channel connection sysfs API to using only the defined signals. + +The arm,trig-filters property blocks output signals that could cause system +issues is set - such as the dbgreq signal into a CPU. The filtering is +enabled by default, but can be disabled at runtime. + +Finally, a CTI that is an architecturally defined v8 CTI connected to a cpu +and optional ETM may be declared as: + +cti@859000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x859000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + + arm,cti-v8-arch; + cpu = <&CPU1>; + arm,cs-dev-assoc = <&etm1>; +}; + +The arm,cti-v8-arch property declares this as a v8 CTI, the cpu property must +be present, and a single arm,cs-dev-assoc may be present to define an attached +ETM. No additional trig-conns nodes are permitted. The driver will build a +connection model according to architectural requirements. This will include +a filter on the CPU debreq signal as described above. + +All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by + a CTM. On these systems a CTM index is declared to associate CTI devices that + are interconnected via the CTM. + + e.g. + arm,ctm-ctm-id=<2> + +CTI devices with the same CTM ID are considered connected by the CTM. If this +parameter is absent then all CTIs are considered interconnected by the same +CTM. + +*Summary of CTI required properties: + + * compatible : should be "arm,coresight-cti"; supplemented with + "arm,primecell" since this driver is using the AMBA bus + interface. + + * reg: physical base address and length of the register + set(s) of the component. + + * clocks: the clocks associated to this component. + + * clock-names: the name of the clocks referenced by the code. + Since we are using the AMBA framework, the name of the clock + providing the interconnect should be "apb_pclk", and some + coresight blocks also have an additional clock "atclk", which + clocks the core of that coresight component. The latter clock + is optional. + +*Summary of CTI optional properties: + + * trig-conns : defines a connection node between CTI and a component. + Component may be a CPU, CoreSight device, any other hardware device + or simple external IO lines. + + *arm,trig-in-sigs: List of CTI trigger in signals in use by a + trig-conns node. Only valid as property of trig-conns node. + + *arm,trig-in-types: List of types for the CTI trigger in signals. + Types in this array match to the corresponding signal in the + arm,trig-in-sigs array. If the -types array is smaller, or omitted + completely, then the types will default to GEN_IO. + + *arm,trig-out-sigs: List of CTI trigger out signals in use by a + trig-conns node. Only valid as property of trig-conns node. + + *arm,trig-out-types: List of types for the CTI trigger out signals. + Types in this array match to the corresponding signal in the + arm,trig-out-sigs array. If the -types array is smaller, or omitted + completely, then the types will default to GEN_IO. + + *arm-trig-filters: List of CTI trigger out signals that will be + blocked from becoming active, unless filtering is disabled on the + driver. Only valid as property of trig-conns node. + + *arm,cti-v8-arch: Declares this CTI device as a v8 architecturally + defined device. Use in CTI base node only, no additional trig-conns + nodes permitted if this is declared. + + *cpu : defines a phandle reference to an associated CPU. Use in + trig-conns node, or in CTI base node when arm,cti-v8-arch present. + + *arm,cs-dev-assoc: defines a phandle reference to an associated + CoreSight trace device. When the associated trace device is enabled, + then the respective CTI will be enabled. Use in a trig-conns node, + or in CTI base node when arm,cti-v8-arch present. If the associated + device has not been registered then the node name will be stored as + the connection name for later resolution. If the associated device is + not a CoreSight device or not registered then the node name will + remain the connection name and automatic enabling will not occur. + + *arm,trig-conn-name: defines a connection name that will be displayed, + if not overridden by the name of associated device from + arm,cs-dev-assoc or the CPU. Only valid as property of trig-conns node. + Principle use for CTI that are connected to non-coresight devices, + or external IO. + + *arm,ctm-ctm-id: Defines the interconnecting CTM for this device. + Use in CTI base node. diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index f8aff65ab921..2013757b1f93 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -42,6 +42,10 @@ its hardware characteristcs. - Coresight Address Translation Unit (CATU) "arm,coresight-catu", "arm,primecell";
+ - Coresight Cross Trigger Interface (CTI): + "arm,coresight-cti", "arm,primecell"; + See coresight-ect-cti.txt for full CTI definitions. + * reg: physical base address and length of the register set(s) of the component.
@@ -64,6 +68,9 @@ its hardware characteristcs. * reg-names: the only acceptable values are "stm-base" and "stm-stimulus-base", each corresponding to the areas defined in "reg".
+* Required properties for Coresight Cross Trigger Interface (CTI) + See coresight-ect-cti.txt for full CTI definitions. + * Required properties for devices that don't show up on the AMBA bus, such as non-configurable replicators:
Allows trigger in and out signals which have specific functions on Coresight devices to be described.
Signed-off-by: Mike Leach mike.leach@linaro.org --- include/dt-bindings/arm/coresight-cti-dt.h | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 include/dt-bindings/arm/coresight-cti-dt.h
diff --git a/include/dt-bindings/arm/coresight-cti-dt.h b/include/dt-bindings/arm/coresight-cti-dt.h new file mode 100644 index 000000000000..343737b7ae7f --- /dev/null +++ b/include/dt-bindings/arm/coresight-cti-dt.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * This header provides constants for the trigger signal + * types on CoreSight CTI. + */ + +#ifndef _DT_BINDINGS_ARM_CORESIGHT_CTI_DT_H +#define _DT_BINDINGS_ARM_CORESIGHT_CTI_DT_H + +#define GEN_IO 0 +#define GEN_INTREQ 1 +#define GEN_INTACK 2 +#define GEN_HALTREQ 3 +#define GEN_RESTARTREQ 4 +#define PE_EDBGREQ 5 +#define PE_DBGRESTART 6 +#define PE_CTIIRQ 7 +#define PE_PMUIRQ 8 +#define PE_DBGTRIGGER 9 +#define ETM_EXTOUT 10 +#define ETM_EXTIN 11 +#define SNK_FULL 12 +#define SNK_ACQCOMP 13 +#define SNK_FLUSHCOMP 14 +#define SNK_FLUSHIN 15 +#define SNK_TRIGIN 16 +#define STM_ASYNCOUT 17 +#define STM_TOUT_SPTE 18 +#define STM_TOUT_SW 19 +#define STM_TOUT_HETE 20 +#define STM_HWEVENT 21 +#define ELA_TSTART 22 +#define ELA_TSTOP 23 +#define ELA_DBGREQ 24 + +#endif /*_DT_BINDINGS_ARM_CORESIGHT_CTI_DT_H */
Signed-off-by: Mike Leach mike.leach@linaro.org --- arch/arm64/boot/dts/qcom/msm8916.dtsi | 102 +++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi index 0803ca8c02da..86d0b185a207 100644 --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi @@ -16,6 +16,7 @@ #include <dt-bindings/reset/qcom,gcc-msm8916.h> #include <dt-bindings/clock/qcom,rpmcc.h> #include <dt-bindings/thermal/thermal.h> +#include <dt-bindings/arm/coresight-cti-dt.h>
/ { interrupt-parent = <&intc>; @@ -1352,7 +1353,7 @@ cpu = <&CPU3>; };
- etm@85c000 { + etm0: etm@85c000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85c000 0x1000>;
@@ -1370,7 +1371,7 @@ }; };
- etm@85d000 { + etm1: etm@85d000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85d000 0x1000>;
@@ -1388,7 +1389,7 @@ }; };
- etm@85e000 { + etm2: etm@85e000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85e000 0x1000>;
@@ -1406,7 +1407,7 @@ }; };
- etm@85f000 { + etm3: etm@85f000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85f000 0x1000>;
@@ -1424,6 +1425,99 @@ }; };
+ /* System CTIs */ + /* CTI 0 - TMC connections */ + cti@810000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x810000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + /* CTI 1 - TPIU connections */ + cti@811000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x811000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + /* CTIs 2-11 - no information - not instantiated */ + + /* Core CTIs; CTIs 12-15 */ + /* CTI - CPU-0 */ + cti@858000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x858000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + + arm,cti-v8-arch; + cpu = <&CPU0>; + arm,cs-dev-assoc = <&etm0>; + + }; + + /* CTI - CPU-1 */ + cti@859000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x859000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + + arm,cti-v8-arch; + cpu = <&CPU1>; + arm,cs-dev-assoc = <&etm1>; + }; + + /* CTI - CPU-2 */ + cti@85a000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x85a000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + + /* v8 cpu the hard way... */ + trig-conns@0 { +/* #address-cells = <1>; + #size-cells = <0>; + reg = <0>;*/ + cpu = <&CPU2>; + arm,trig-in-sigs=<0 1>; + arm,trig-in-types=<PE_DBGTRIGGER PE_PMUIRQ>; + arm,trig-out-sigs=<0 1 2 >; + arm,trig-out-types=<PE_EDBGREQ PE_DBGRESTART PE_CTIIRQ>; + arm,trig-filters=<0>; + }; + trig-conns@1 { + /* reg = <1>; */ + arm,trig-in-sigs = <4 5 6 7>; + arm,trig-in-types = <ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT>; + arm,trig-out-sigs = <4 5 6 7>; + arm,trig-out-types = <ETM_EXTIN ETM_EXTIN ETM_EXTIN ETM_EXTIN>; + arm,cs-dev-assoc = <&etm2>; + }; + }; + + /* CTI - CPU-3 */ + cti@85b000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x85b000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + + arm,cti-v8-arch; + cpu = <&CPU3>; + arm,cs-dev-assoc = <&etm3>; + }; + + venus: video-codec@1d00000 { compatible = "qcom,msm8916-venus"; reg = <0x01d00000 0xff000>;
On Wed, May 01, 2019 at 09:49:42AM +0100, Mike Leach wrote:
Signed-off-by: Mike Leach mike.leach@linaro.org
arch/arm64/boot/dts/qcom/msm8916.dtsi | 102 +++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi index 0803ca8c02da..86d0b185a207 100644 --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi @@ -16,6 +16,7 @@ #include <dt-bindings/reset/qcom,gcc-msm8916.h> #include <dt-bindings/clock/qcom,rpmcc.h> #include <dt-bindings/thermal/thermal.h> +#include <dt-bindings/arm/coresight-cti-dt.h> / { interrupt-parent = <&intc>; @@ -1352,7 +1353,7 @@ cpu = <&CPU3>; };
etm@85c000 {
etm0: etm@85c000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85c000 0x1000>;
@@ -1370,7 +1371,7 @@ }; };
etm@85d000 {
etm1: etm@85d000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85d000 0x1000>;
@@ -1388,7 +1389,7 @@ }; };
etm@85e000 {
etm2: etm@85e000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85e000 0x1000>;
@@ -1406,7 +1407,7 @@ }; };
etm@85f000 {
etm3: etm@85f000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85f000 0x1000>;
@@ -1424,6 +1425,99 @@ }; };
/* System CTIs */
/* CTI 0 - TMC connections */
cti@810000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x810000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
};
/* CTI 1 - TPIU connections */
cti@811000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x811000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
};
/* CTIs 2-11 - no information - not instantiated */
/* Core CTIs; CTIs 12-15 */
/* CTI - CPU-0 */
cti@858000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x858000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
arm,cti-v8-arch;
cpu = <&CPU0>;
arm,cs-dev-assoc = <&etm0>;
};
/* CTI - CPU-1 */
cti@859000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x859000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
arm,cti-v8-arch;
cpu = <&CPU1>;
arm,cs-dev-assoc = <&etm1>;
};
/* CTI - CPU-2 */
cti@85a000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x85a000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
/* v8 cpu the hard way... */
trig-conns@0 {
+/* #address-cells = <1>;
#size-cells = <0>;
reg = <0>;*/
cpu = <&CPU2>;
arm,trig-in-sigs=<0 1>;
arm,trig-in-types=<PE_DBGTRIGGER PE_PMUIRQ>;
arm,trig-out-sigs=<0 1 2 >;
arm,trig-out-types=<PE_EDBGREQ PE_DBGRESTART PE_CTIIRQ>;
arm,trig-filters=<0>;
};
trig-conns@1 {
/* reg = <1>; */
arm,trig-in-sigs = <4 5 6 7>;
arm,trig-in-types = <ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT>;
arm,trig-out-sigs = <4 5 6 7>;
arm,trig-out-types = <ETM_EXTIN ETM_EXTIN ETM_EXTIN ETM_EXTIN>;
arm,cs-dev-assoc = <&etm2>;
};
};
Should apply the same binding for CPU2 with other CPUs?
/* CTI - CPU-3 */
cti@85b000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x85b000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
arm,cti-v8-arch;
cpu = <&CPU3>;
arm,cs-dev-assoc = <&etm3>;
};
- venus: video-codec@1d00000 { compatible = "qcom,msm8916-venus"; reg = <0x01d00000 0xff000>;
-- 2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Hi Leo,
On Sun, 26 May 2019 at 13:29, Leo Yan leo.yan@linaro.org wrote:
On Wed, May 01, 2019 at 09:49:42AM +0100, Mike Leach wrote:
Signed-off-by: Mike Leach mike.leach@linaro.org
arch/arm64/boot/dts/qcom/msm8916.dtsi | 102 +++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi index 0803ca8c02da..86d0b185a207 100644 --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi @@ -16,6 +16,7 @@ #include <dt-bindings/reset/qcom,gcc-msm8916.h> #include <dt-bindings/clock/qcom,rpmcc.h> #include <dt-bindings/thermal/thermal.h> +#include <dt-bindings/arm/coresight-cti-dt.h>
/ { interrupt-parent = <&intc>; @@ -1352,7 +1353,7 @@ cpu = <&CPU3>; };
etm@85c000 {
etm0: etm@85c000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85c000 0x1000>;
@@ -1370,7 +1371,7 @@ }; };
etm@85d000 {
etm1: etm@85d000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85d000 0x1000>;
@@ -1388,7 +1389,7 @@ }; };
etm@85e000 {
etm2: etm@85e000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85e000 0x1000>;
@@ -1406,7 +1407,7 @@ }; };
etm@85f000 {
etm3: etm@85f000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85f000 0x1000>;
@@ -1424,6 +1425,99 @@ }; };
/* System CTIs */
/* CTI 0 - TMC connections */
cti@810000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x810000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
};
/* CTI 1 - TPIU connections */
cti@811000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x811000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
};
/* CTIs 2-11 - no information - not instantiated */
/* Core CTIs; CTIs 12-15 */
/* CTI - CPU-0 */
cti@858000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x858000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
arm,cti-v8-arch;
cpu = <&CPU0>;
arm,cs-dev-assoc = <&etm0>;
};
/* CTI - CPU-1 */
cti@859000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x859000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
arm,cti-v8-arch;
cpu = <&CPU1>;
arm,cs-dev-assoc = <&etm1>;
};
/* CTI - CPU-2 */
cti@85a000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x85a000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
/* v8 cpu the hard way... */
trig-conns@0 {
+/* #address-cells = <1>;
#size-cells = <0>;
reg = <0>;*/
cpu = <&CPU2>;
arm,trig-in-sigs=<0 1>;
arm,trig-in-types=<PE_DBGTRIGGER PE_PMUIRQ>;
arm,trig-out-sigs=<0 1 2 >;
arm,trig-out-types=<PE_EDBGREQ PE_DBGRESTART PE_CTIIRQ>;
arm,trig-filters=<0>;
};
trig-conns@1 {
/* reg = <1>; */
arm,trig-in-sigs = <4 5 6 7>;
arm,trig-in-types = <ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT>;
arm,trig-out-sigs = <4 5 6 7>;
arm,trig-out-types = <ETM_EXTIN ETM_EXTIN ETM_EXTIN ETM_EXTIN>;
arm,cs-dev-assoc = <&etm2>;
};
};
Should apply the same binding for CPU2 with other CPUs?
Yes - this was here for testing purposes - I missed changing it back. I'll reset it to a standard v8 binding next set
Thanks
Mike
/* CTI - CPU-3 */
cti@85b000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x85b000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
arm,cti-v8-arch;
cpu = <&CPU3>;
arm,cs-dev-assoc = <&etm3>;
};
venus: video-codec@1d00000 { compatible = "qcom,msm8916-venus"; reg = <0x01d00000 0xff000>;
-- 2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Add in CTI entries for Juno r0, r1 and r2 to device tree entries.
Signed-off-by: Mike Leach mike.leach@linaro.org --- arch/arm64/boot/dts/arm/juno-base.dtsi | 149 +++++++++++++++++++++- arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi | 31 ++++- arch/arm64/boot/dts/arm/juno-r1.dts | 25 ++++ arch/arm64/boot/dts/arm/juno-r2.dts | 25 ++++ arch/arm64/boot/dts/arm/juno.dts | 25 ++++ 5 files changed, 250 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/boot/dts/arm/juno-base.dtsi b/arch/arm64/boot/dts/arm/juno-base.dtsi index 7446e0dc154d..9eed01149733 100644 --- a/arch/arm64/boot/dts/arm/juno-base.dtsi +++ b/arch/arm64/boot/dts/arm/juno-base.dtsi @@ -108,7 +108,7 @@ * The actual size is just 4K though 64K is reserved. Access to the * unmapped reserved region results in a DECERR response. */ - etf@20010000 { /* etf0 */ + etf_sys0: etf@20010000 { /* etf0 */ compatible = "arm,coresight-tmc", "arm,primecell"; reg = <0 0x20010000 0 0x1000>;
@@ -132,7 +132,7 @@ }; };
- tpiu@20030000 { + tpiu_sys: tpiu@20030000 { compatible = "arm,coresight-tpiu", "arm,primecell"; reg = <0 0x20030000 0 0x1000>;
@@ -185,7 +185,7 @@ }; };
- etr@20070000 { + etr_sys: etr@20070000 { compatible = "arm,coresight-tmc", "arm,primecell"; reg = <0 0x20070000 0 0x1000>; iommus = <&smmu_etr 0>; @@ -203,7 +203,7 @@ }; };
- stm@20100000 { + stm_sys: stm@20100000 { compatible = "arm,coresight-stm", "arm,primecell"; reg = <0 0x20100000 0 0x1000>, <0 0x28000000 0 0x1000000>; @@ -279,7 +279,19 @@ }; }; }; + + cti0: cti@22020000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x22020000 0 0x1000>;
+ clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + arm,cti-v8-arch; + arm,cs-dev-assoc = <&etm0>; + }; + funnel@220c0000 { /* cluster0 funnel */ compatible = "arm,coresight-funnel", "arm,primecell"; reg = <0 0x220c0000 0 0x1000>; @@ -339,7 +351,19 @@ }; }; }; + + cti1: cti@22120000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x22120000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>;
+ arm,cti-v8-arch; + arm,cs-dev-assoc = <&etm1>; + }; + cpu_debug2: cpu-debug@23010000 { compatible = "arm,coresight-cpu-debug", "arm,primecell"; reg = <0x0 0x23010000 0x0 0x1000>; @@ -365,6 +389,18 @@ }; };
+ cti2: cti@23020000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x23020000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + arm,cti-v8-arch; + arm,cs-dev-assoc = <&etm2>; + }; + funnel@230c0000 { /* cluster1 funnel */ compatible = "arm,coresight-funnel", "arm,primecell"; reg = <0 0x230c0000 0 0x1000>; @@ -437,6 +473,18 @@ }; };
+ cti3: cti@23120000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x23120000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + arm,cti-v8-arch; + arm,cs-dev-assoc = <&etm3>; + }; + cpu_debug4: cpu-debug@23210000 { compatible = "arm,coresight-cpu-debug", "arm,primecell"; reg = <0x0 0x23210000 0x0 0x1000>; @@ -462,6 +510,18 @@ }; };
+ cti4: cti@23220000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x23220000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + arm,cti-v8-arch; + arm,cs-dev-assoc = <&etm4>; + }; + cpu_debug5: cpu-debug@23310000 { compatible = "arm,coresight-cpu-debug", "arm,primecell"; reg = <0x0 0x23310000 0x0 0x1000>; @@ -487,6 +547,87 @@ }; };
+ cti5: cti@23320000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x23320000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + arm,cti-v8-arch; + arm,cs-dev-assoc = <&etm5>; + }; + + + cti@20020000 { /* sys_cti_0 */ + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x20020000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + trig-conns@0 { + arm,trig-in-sigs=<2 3>; + arm,trig-in-types=<SNK_FULL SNK_ACQCOMP>; + arm,trig-out-sigs=<0 1>; + arm,trig-out-types=<SNK_FLUSHIN SNK_TRIGIN>; + arm,cs-dev-assoc = <&etr_sys>; + }; + + trig-conns@1 { + arm,trig-in-sigs=<0 1>; + arm,trig-in-types=<SNK_FULL SNK_ACQCOMP>; + arm,trig-out-sigs=<7 6>; + arm,trig-out-types=<SNK_FLUSHIN SNK_TRIGIN>; + arm,cs-dev-assoc = <&etf_sys0>; + }; + + trig-conns@2 { + arm,trig-in-sigs=<4 5 6 7>; + arm,trig-in-types=<STM_TOUT_SPTE STM_TOUT_SW STM_TOUT_HETE STM_ASYNCOUT>; + arm,trig-out-sigs=<4 5>; + arm,trig-out-types=<STM_HWEVENT STM_HWEVENT>; + arm,cs-dev-assoc = <&stm_sys>; + }; + + trig-conns@3 { + arm,trig-out-sigs=<2 3>; + arm,trig-out-types=<SNK_FLUSHIN SNK_TRIGIN>; + arm,cs-dev-assoc = <&tpiu_sys>; + }; + }; + + cti@20110000 { /* sys_cti_1 */ + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x20110000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + trig-conns@0 { + arm,trig-in-sigs=<0>; + arm,trig-in-types=<GEN_INTREQ>; + arm,trig-out-sigs=<0>; + arm,trig-out-types=<GEN_HALTREQ>; + arm,trig-conn-name = "sys_profiler"; + }; + + trig-conns@1 { + arm,trig-out-sigs=<2 3>; + arm,trig-out-types=<GEN_HALTREQ GEN_RESTARTREQ>; + arm,trig-conn-name = "watchdog"; + }; + + trig-conns@2 { + arm,trig-out-sigs=<1 6>; + arm,trig-out-types=<GEN_HALTREQ GEN_RESTARTREQ>; + arm,trig-conn-name = "g_counter"; + }; + }; + sram: sram@2e000000 { compatible = "arm,juno-sram-ns", "mmio-sram"; reg = <0x0 0x2e000000 0x0 0x8000>; diff --git a/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi b/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi index cf285152deab..cc1f6874ffea 100644 --- a/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi +++ b/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi @@ -23,7 +23,7 @@ }; };
- etf@20140000 { /* etf1 */ + etf_sys1: etf@20140000 { /* etf1 */ compatible = "arm,coresight-tmc", "arm,primecell"; reg = <0 0x20140000 0 0x1000>;
@@ -82,4 +82,33 @@
}; }; + + cti@20160000 { /* sys_cti_2 */ + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x20160000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + trig-conns@0 { + arm,trig-in-sigs=<0 1>; + arm,trig-in-types=<SNK_FULL SNK_ACQCOMP>; + arm,trig-out-sigs=<0 1>; + arm,trig-out-types=<SNK_FLUSHIN SNK_TRIGIN>; + arm,cs-dev-assoc = <&etf_sys1>; + }; + + trig-conns@1 { + arm,trig-in-sigs=<2 3 4>; + arm,trig-in-types=<ELA_DBGREQ ELA_TSTART ELA_TSTOP>; + arm,trig-conn-name = "ela_clus_0"; + }; + + trig-conns@2 { + arm,trig-in-sigs=<5 6 7>; + arm,trig-in-types=<ELA_DBGREQ ELA_TSTART ELA_TSTOP>; + arm,trig-conn-name = "ela_clus_1"; + }; + }; }; diff --git a/arch/arm64/boot/dts/arm/juno-r1.dts b/arch/arm64/boot/dts/arm/juno-r1.dts index 5f290090b0cf..02aa51eb311d 100644 --- a/arch/arm64/boot/dts/arm/juno-r1.dts +++ b/arch/arm64/boot/dts/arm/juno-r1.dts @@ -9,6 +9,7 @@ /dts-v1/;
#include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/arm/coresight-cti-dt.h> #include "juno-base.dtsi" #include "juno-cs-r1r2.dtsi"
@@ -309,3 +310,27 @@ &cpu_debug5 { cpu = <&A53_3>; }; + +&cti0 { + cpu = <&A57_0>; +}; + +&cti1 { + cpu = <&A57_1>; +}; + +&cti2 { + cpu = <&A53_0>; +}; + +&cti3 { + cpu = <&A53_1>; +}; + +&cti4 { + cpu = <&A53_2>; +}; + +&cti5 { + cpu = <&A53_3>; +}; diff --git a/arch/arm64/boot/dts/arm/juno-r2.dts b/arch/arm64/boot/dts/arm/juno-r2.dts index 305300dd521c..75bb27c2d4dc 100644 --- a/arch/arm64/boot/dts/arm/juno-r2.dts +++ b/arch/arm64/boot/dts/arm/juno-r2.dts @@ -9,6 +9,7 @@ /dts-v1/;
#include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/arm/coresight-cti-dt.h> #include "juno-base.dtsi" #include "juno-cs-r1r2.dtsi"
@@ -315,3 +316,27 @@ &cpu_debug5 { cpu = <&A53_3>; }; + +&cti0 { + cpu = <&A72_0>; +}; + +&cti1 { + cpu = <&A72_1>; +}; + +&cti2 { + cpu = <&A53_0>; +}; + +&cti3 { + cpu = <&A53_1>; +}; + +&cti4 { + cpu = <&A53_2>; +}; + +&cti5 { + cpu = <&A53_3>; +}; diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts index f00cffbd032c..dbc22e70b62c 100644 --- a/arch/arm64/boot/dts/arm/juno.dts +++ b/arch/arm64/boot/dts/arm/juno.dts @@ -9,6 +9,7 @@ /dts-v1/;
#include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/arm/coresight-cti-dt.h> #include "juno-base.dtsi"
/ { @@ -295,3 +296,27 @@ &cpu_debug5 { cpu = <&A53_3>; }; + +&cti0 { + cpu = <&A57_0>; +}; + +&cti1 { + cpu = <&A57_1>; +}; + +&cti2 { + cpu = <&A53_0>; +}; + +&cti3 { + cpu = <&A53_1>; +}; + +&cti4 { + cpu = <&A53_2>; +}; + +&cti5 { + cpu = <&A53_3>; +};
Add section in coresight.txt explaining operation and use of CTI devices in CoreSight.
Signed-off-by: Mike Leach mike.leach@linaro.org --- Documentation/trace/coresight.txt | 139 ++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+)
diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt index efbc832146e7..d3e1147b5156 100644 --- a/Documentation/trace/coresight.txt +++ b/Documentation/trace/coresight.txt @@ -425,6 +425,145 @@ root@genericarmv8:~#
Details on how to use the generic STM API can be found here [2].
+CoreSight Embedded Cross Trigger (CTI & CTM). +--------------------------------------------- + +The CoreSight Cross Trigger Interface (CTI) is a hardware device that takes +individual input and output hardware signals from devices and interconnects +them via the Cross Trigger Matrix (CTM) to other devices, in order to propogate +events between devices. + +e.g. + + 0000000 in_trigs ::::::: + 0 C 0----------->: : +======>(other CTI channel IO) + 0 P 0<-----------: : v + 0 U 0 out_trigs : : Channels ***** ::::::: + 0000000 : CTI :<=========>*CTM*<====>: CTI :---+ + ####### in_trigs : : (id 0-3) ***** ::::::: v + # ETM #----------->: : ^ ####### + # #<-----------: : +---# ETR # + ####### out_trigs ::::::: ####### + +The CTI driver enables the programming of the CTI to attach triggers to +channels. When an input trigger becomes active, the attached channel will +become active. Any output trigger also attached to that channel will also +become active. The active channel is propogated to other CTIs via the CTM +affecting output triggers there, unless filtered by the CTI channel gate. + +It is also possible to activate a channel using system software. + +The CTIs are registered by the system to be associated with CPUs and/or other +CoreSight devices on the trace data path. When these devices are enabled then +the attached CTIs will also be enabled. By default/on power up the CTIs have +no programmed trigger/channel attachments, so will not affect the system +until explicitly programmed. + +The hardware trigger connections between CTIs and devices is implementation +defined, unless the CPU/ETM combination is a v8 architecture, in which case +the connections have an architecturally defined standard layout. + +The hardware trigger signals can also be connected to non-CoreSight devices +(e.g. UART), or be propogated off chip as hardware IO lines. + +All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by + a CTM. On these systems a CTM index is declared to associate CTI devices that + are interconnected via the CTM. + +The CTI devices appear on the existing coresight bus alongside the other +CoreSight devices. + +# ls /sys/bus/coresight/devices/858000.cti +channels connections ctmid enable mgmt power regs subsystem uevent + +Key items are: +* enable: enables the CTI. +* ctmid : associated CTM - only relevant if system has multiple CTI+CTM clusters +that are not interconnected. + + +*connections: sub-dir containing the interconnection information for this CTI. +*channels: sub-dir containing the channel API - CTI main programming interface. +*regs: sub-dir giving access to the raw programmable CTI regs. +*mgmt: sub-dir for the standard CoreSight management registers. + +** Connections: This contains a list of read only data describing the devices +connected to this CTI, and the individual trigger signals to and from the +devices: +./b5b000.cti/connections# ls +0_name 0_trgin_type 0_trgout_type 1_trgin_sig 1_trgout_sig nr_cons +0_trgin_sig 0_trgout_sig 1_name 1_trgin_type 1_trgout_type trigout_filtered + +* nr_cons - number of connections +* trigout_filtered - trigger out signals that are prevented from being set if +filtering is enabled. Prevents accidental edbgreq signals stopping a core. + +Each connection has a set of parameters prefixed with the connection index. +Thus 0_name will show the name of connection 0. +* N_name : name of connection +* N_trgin_sig : input trigger signal indexes used in this connection. +* N_trgin_type : functional types for in signals. +* N_trgout_sig : output trigger signals for this connection. +* N_trgout_type : functional types for out signals. + +e.g. +.../85b000.cti/connections# cat 0_name +cpu3 +.../85b000.cti/connections# cat 0_trgin_sig +0 1 +.../85b000.cti/connections# cat 0_trgin_type +pe_dbgtrigger pe_pmuirq + +If a connection has zero signals in either the in or out triggers then those +parameters will be omitted from the connections sub-dir. + + +** Channels API : This provides an easy way to attach triggers to channels, +without needing the multiple register operations that are required if +manipluating the 'regs' sub-dir elements directly. + +# ls /sys/bus/coresight/devices/859000.cti/channels/ +chan_clear gate_disable list_xtrigs trigin_attach trigout_detach +chan_pulse gate_enable reset_xtrigs trigin_detach +chan_set list_chan_inuse trig_filter_enable trigout_attach + +Most access to these elements take the form: +echo <chan> [<trigger>] > /<device_path>/<operation> +where the optional <trigger> is only needed for trigXX_at|detach operations. + +e.g. +echo 0 1 > /sys/bus/coresight/devices/859000.cti/channels/trigin_attach +attach trig_in(1) to channel(0). +echo 0 > /sys/bus/coresight/devices/859000.cti/channels/chan_set +activate channel(0) + +*gate_enable, gate_disable operations set the CTI gate to propogate +(enable) to other devices. These operation can take a channel number or 'all' +to operate on all channels. CTI gate is enabled by default at power up. + +*list_gate_enable: show the current channels enabled through the gate. +*list_inuse: show the current channels attached to any signal +*list_free: show channels with no attached signals +*show_chan_sel: select the channel for the show_chan_xtrigs operation. +*show_chan_xtrigs: show the cross triggers programmed for the selected channel. + +.../85b000.cti/channels# echo 0 1 > trigin_attach +.../85b000.cti/channels# cat list_free +1 2 3 +.../85b000.cti/channels# cat list_inuse +0 +.../85b000.cti/channels# echo 0 > show_chan_sel +.../85b000.cti/channels# cat show_chan_xtrigs +IN: 1 OUT: + +*trig_filter_enable: defaults to enabled, disable to allow potentially +dangerous output signals to be set. + +*reset_xtrigs: clear all channel / trigger programming - reset device hardware +to default state. + [1]. Documentation/ABI/testing/sysfs-bus-coresight-devices-stm [2]. Documentation/trace/stm.rst [3]. https://github.com/Linaro/perf-opencsd
Add API usage document for sysfs API in CTI driver.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../testing/sysfs-bus-coresight-devices-cti | 225 ++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-coresight-devices-cti
diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti b/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti new file mode 100644 index 000000000000..4dd3ba94d2b2 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti @@ -0,0 +1,225 @@ +What: /sys/bus/coresight/devices/<cti-name>/enable +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Enable/Disable the CTI hardware. + +What: /sys/bus/coresight/devices/<cti-name>/ctmid +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Display the associated CTM ID + +What: /sys/bus/coresight/devices/<cti-name>/connections/nr_cons +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Number of devices connected to this CTI + +What: /sys/bus/coresight/devices/<cti-name>/connections/<N>_name +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Name of connected device <N> + +What: /sys/bus/coresight/devices/<cti-name>/connections/<N>_trgin_sig +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Input trigger signals from connected device <N> + +What: /sys/bus/coresight/devices/<cti-name>/connections/<N>_trgin_type +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Functional types for the input trigger signals + from connected device <N> + +What: /sys/bus/coresight/devices/<cti-name>/connections/<N>_trgout_sig +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Output trigger signals to connected device <N> + +What: /sys/bus/coresight/devices/<cti-name>/connections/<N>_trgout_type +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Functional types for the output trigger signals + to connected device <N> + +What: /sys/bus/coresight/devices/<cti-name>/regs/inout_sel +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Select the index for inen and outen registers. + +What: /sys/bus/coresight/devices/<cti-name>/regs/inen +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Read or write the CTIINEN register selected by inout_sel. + +What: /sys/bus/coresight/devices/<cti-name>/regs/outen +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Read or write the CTIOUTEN register selected by inout_sel. + +What: /sys/bus/coresight/devices/<cti-name>/regs/gate +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Read or write CTIGATE register. + +What: /sys/bus/coresight/devices/<cti-name>/regs/asicctl +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Read or write ASICCTL register. + +What: /sys/bus/coresight/devices/<cti-name>/regs/intack +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Write the INTACK register. + +What: /sys/bus/coresight/devices/<cti-name>/regs/appset +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Set CTIAPPSET register to activate channel. Read back to + determine current value of register. + +What: /sys/bus/coresight/devices/<cti-name>/regs/appclear +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Write APPCLEAR register to deactivate channel. + +What: /sys/bus/coresight/devices/<cti-name>/regs/apppulse +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Write APPPULSE to pulse a channel active for one clock + cycle. + +What: /sys/bus/coresight/devices/<cti-name>/regs/chinstatus +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Read current status of channel inputs. + +What: /sys/bus/coresight/devices/<cti-name>/regs/choutstatus +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) read current status of channel outputs. + +What: /sys/bus/coresight/devices/<cti-name>/regs/triginstatus +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) read current status of input trigger signals + +What: /sys/bus/coresight/devices/<cti-name>/regs/trigoutstatus +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) read current status of output trigger signals. + +What: /sys/bus/coresight/devices/<cti-name>/channels/trigin_attach +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Attach a CTI input trigger to a CTM channel. + +What: /sys/bus/coresight/devices/<cti-name>/channels/trigin_detach +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Detach a CTI input trigger from a CTM channel. + +What: /sys/bus/coresight/devices/<cti-name>/channels/trigout_attach +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Attach a CTI output trigger to a CTM channel. + +What: /sys/bus/coresight/devices/<cti-name>/channels/trigout_detach +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Detach a CTI output trigger from a CTM channel. + +What: /sys/bus/coresight/devices/<cti-name>/channels/gate_enable +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Enable CTIGATE for single channel or all channels. + +What: /sys/bus/coresight/devices/<cti-name>/channels/gate_disable +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Disable CTIGATE for single channel or all channels. + +What: /sys/bus/coresight/devices/<cti-name>/channels/chan_set +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Activate a single channel. + +What: /sys/bus/coresight/devices/<cti-name>/channels/chan_clear +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Deactivate a single channel. + +What: /sys/bus/coresight/devices/<cti-name>/channels/chan_pulse +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Pulse a single channel - activate for a single clock cycle. + +What: /sys/bus/coresight/devices/<cti-name>/channels/trig_filter_enable +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Enable or disable trigger output signal filtering. + +What: /sys/bus/coresight/devices/<cti-name>/channels/list_gate_ena +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Channels enabled through the gate. + +What: /sys/bus/coresight/devices/<cti-name>/channels/list_inuse +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) show channels with at least one attached trigger signal. + +What: /sys/bus/coresight/devices/<cti-name>/channels/list_free +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) show channels with no attached trigger signals. + +What: /sys/bus/coresight/devices/<cti-name>/channels/show_chan_sel +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Select a channel for the show_chan_xtrigs operation + +What: /sys/bus/coresight/devices/<cti-name>/channels/show_chan_xtrigs +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Show triggers attached to selected channel on this CTI. + +What: /sys/bus/coresight/devices/<cti-name>/channels/reset_xtrigs +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Clear all channel / trigger programming.
Hi Mike,
On Wed, May 01, 2019 at 09:49:32AM +0100, Mike Leach wrote:
CTIs are defined in the device tree and associated with other CoreSight devices. The core CoreSight code has been modified to enable the registration of the CTI devices on the same bus as the other CoreSight components, but as these are not actually trace generation / capture devices, they are not part of the Coresight path when generating trace.
However, the definition of the standard CoreSight device has been extended to include a reference to an associated CTI device, and the enable / disable trace path operations will auto enable/disable any associated CTI devices at the same time.
Programming is at present via sysfs - a full API is provided to utilise the hardware capabilities. As CTI devices are unprogrammed by default, the auto enable describe above will have no effect until explicit programming takes place.
A set of device tree bindings specific to the CTI topology has been defined.
Documentation has been updated to describe both the CTI hardware, its use and programming in sysfs, and the new dts bindings required.
I'd like to reply you for sysfs nodes for CTI topology and avoid to disturb for Suzuki's APCI binding patch set threading, so I copy 'n paste your comments at here:
I fully agree that there is requirement to expose device connections as Suzuki's patches provided. As commented in the original patch, it removes the need for users to have knowledge of hardware specifics or access to device tree source.
For the trace datapath a simple link is sufficient to express this information. The nature of the data and connection is known - it is the trace data running from source to sink. The linked components are guaranteed to be registered coresight devices
However, the requirement for the CTI is different.
CTI is not limited to connecting to other coresight devices. Any device can be wired into a CTI trigger signal. These devices may or may not have drivers / entries in the device tree. For each connection a client needs to know the signals connected to the cti, the signal directions, the signal prupose if possible, and the device connected. For this reason we dynamically fill out a connections infomation sub-dir in sysfs containing _name, _trigin_sig, _trigout_sig, _trigin_type, _trigout_type - described in the patch [1].
This information is sufficient and necessary to enable a user to program a CTI in most cases.
As an example look at the Juno dtsi in [2]. CTI 0 is connected to ETR, ETF, STM and TPIU - all coresight devices. CTI 1 is connected to REF_CLK, system profiler and watchdog - no coresight devices at all. CTI 2 is connected to ETF, and two ELA devices - so 1 coresight device and 2 not coresight devices.
So my view is that for the case where CTI is connected to another CoreSight device the sysfs link could be used in addition to the information described above.
To meet this requirement, below are my some suggestions:
- After read this patch set, it gives me impression that it mixes two things: hardware topology and triggers/channels configurations.
So below suggestions try to distinguish these two things.
- For the hardware topology, we can reuse Suzuki's approach for 'in' and 'out' nodes for CTI devices:
Firstly, to reflect the whole matrix, we can create the 'static' device for CTM (same with static replicator and static funnel), thus under CTM sysfs node we can see all its bounded CTI devices;
For CTI device, its sysfs node can use 'in' / 'out' nodes to connect ETM/TPIU/STM/ETR/ETF, CPU, or any other devices;
For ETM device or other CoreSight components, its sysfs node can use 'in' / 'out' nodes to connect to CTI device.
For this part, we only focus on the hardware connection rather than the detailed configurations.
- For the second level, it's more related with triggers and channels configuration; so this level is only related with every CTI device regsiters accessing under CTI's sysfs node.
It can use DT binding to accomplish related configurations if these configurations are defined in SoC and cannot be changed for specific SoC.
On the other hand, if these configurations are very dynamic for one SoC, I think we even can write one small tool (or python script) to parse the related user's configuration and access sysfs nodes to write corresponding registers. Finally we can use OpenCSD to store related files.
For this part, it's only specific in CTI/CTM drivers.
You and Mathieu/Suzuki have more deeper understanding for CoreSight framework and CTI usages, I just want to contribute some ideas (sorry if introduce noise :)
Thanks, Leo Yan
Hi Leo,
On Sun, 26 May 2019 at 14:11, Leo Yan leo.yan@linaro.org wrote:
Hi Mike,
On Wed, May 01, 2019 at 09:49:32AM +0100, Mike Leach wrote:
CTIs are defined in the device tree and associated with other CoreSight devices. The core CoreSight code has been modified to enable the registration of the CTI devices on the same bus as the other CoreSight components, but as these are not actually trace generation / capture devices, they are not part of the Coresight path when generating trace.
However, the definition of the standard CoreSight device has been extended to include a reference to an associated CTI device, and the enable / disable trace path operations will auto enable/disable any associated CTI devices at the same time.
Programming is at present via sysfs - a full API is provided to utilise the hardware capabilities. As CTI devices are unprogrammed by default, the auto enable describe above will have no effect until explicit programming takes place.
A set of device tree bindings specific to the CTI topology has been defined.
Documentation has been updated to describe both the CTI hardware, its use and programming in sysfs, and the new dts bindings required.
I'd like to reply you for sysfs nodes for CTI topology and avoid to disturb for Suzuki's APCI binding patch set threading, so I copy 'n paste your comments at here:
I fully agree that there is requirement to expose device connections as Suzuki's patches provided. As commented in the original patch, it removes the need for users to have knowledge of hardware specifics or access to device tree source.
For the trace datapath a simple link is sufficient to express this information. The nature of the data and connection is known - it is the trace data running from source to sink. The linked components are guaranteed to be registered coresight devices
However, the requirement for the CTI is different.
CTI is not limited to connecting to other coresight devices. Any device can be wired into a CTI trigger signal. These devices may or may not have drivers / entries in the device tree. For each connection a client needs to know the signals connected to the cti, the signal directions, the signal prupose if possible, and the device connected. For this reason we dynamically fill out a connections infomation sub-dir in sysfs containing _name, _trigin_sig, _trigout_sig, _trigin_type, _trigout_type - described in the patch [1].
This information is sufficient and necessary to enable a user to program a CTI in most cases.
As an example look at the Juno dtsi in [2]. CTI 0 is connected to ETR, ETF, STM and TPIU - all coresight devices. CTI 1 is connected to REF_CLK, system profiler and watchdog - no coresight devices at all. CTI 2 is connected to ETF, and two ELA devices - so 1 coresight device and 2 not coresight devices.
So my view is that for the case where CTI is connected to another CoreSight device the sysfs link could be used in addition to the information described above.
To meet this requirement, below are my some suggestions:
- After read this patch set, it gives me impression that it mixes two things: hardware topology and triggers/channels configurations.
You are correct - there is the hardware topology and a software programming API to connect channels and triggers.
The hardware topology describes the association between the CTI and connected devices, along with the details of the hardware trigger signals between CTI and device. These are static details that must appear in a device tree. I consider the connection information just as much part of the hardware topology as the association between the CTI and connected device.
So below suggestions try to distinguish these two things.
For the hardware topology, we can reuse Suzuki's approach for 'in' and 'out' nodes for CTI devices:
Firstly, to reflect the whole matrix, we can create the 'static' device for CTM (same with static replicator and static funnel), thus under CTM sysfs node we can see all its bounded CTI devices;
I am not sure that this gives us any advantage over the current system - each CTI is tagged with a CTM id - thus establishing the association between CTIs.
For CTI device, its sysfs node can use 'in' / 'out' nodes to connect ETM/TPIU/STM/ETR/ETF, CPU, or any other devices;
The "connection" between the ETM and CPU does not currently use the in/out node mechanism it uses a phandle reference - why should the connection between CPU and CTI be different?
I looked into re-using this mechnism while developing this set. It had a number of disadvantages:- 1) it assumes everything is a coresight device - not true for CTI. We can have connections to non-coresight devices, or simple IO running off chip. 2) I wanted to avoid a flood of new in/out connections between devices in the device trees. There is no concept of a "path" that needs walking in the case of triggers, so no advantage to declaring in and out nodes to follow. In many cases (CPU, ETM, ETR, ETF) there would be both in and out nodes running between device and CTI,
My attempts to re-use the connection infrastructure resulted in adding a lot of parameters and information to the existing structures that was not relevant to non-CTI devices. I felt a clean break was better and makes the overall code easier to read and maintain.
The essential information is the connection details at the CTI end, and the direction and function of the trigger signals between the relevant devices. Encapsulating this information in the CTI driver is sufficient to allow the user to successfully program it. Association of CTI and devices using phadle references also solves the issue of non-coresight devices, and allowing the flexibility of having a connection named, but not otherwise defined in the device tree allows the
For ETM device or other CoreSight components, its sysfs node can use 'in' / 'out' nodes to connect to CTI device.
For this part, we only focus on the hardware connection rather than the detailed configurations.
Here the trigger connections between the CTI device and the associated device are hardware connections in the same way that the trace bus is a hardware connection between an ETM and a funnel. The only difference is that the trace bus is not explicitly mentioned in the description of the hardware as it is the _only_ possible connection an therefore is implied. The in/out nodes describe the assocation between two coresight devices and the hardware signals between then - an ATB bus running from out -> in.
With the CTI user needs to know the hardware connections, but these cannot be implied from the association so must be specifically defined in the device tree / (or other hardware definition) - as they are not software discoverable or software configurable, These are part of the hardware topology of the device.
- For the second level, it's more related with triggers and channels configuration; so this level is only related with every CTI device regsiters accessing under CTI's sysfs node.
If you are referring to the connection between triggers and channels, then these are 100% software configurable by programming the CTI. These can never be defined in advance in the device tree.
Thus programming a set of CTIs we can connect a trigger connection e.g. the full signal from an ETR can be connected to a channel in the CTI associated with the ETR. The same channel can then be connected to the CTIIRQ trigger into a CPU by programming the CTI attached to the CPU.
It can use DT binding to accomplish related configurations if these configurations are defined in SoC and cannot be changed for specific SoC.
On the other hand, if these configurations are very dynamic for one SoC, I think we even can write one small tool (or python script) to parse the related user's configuration and access sysfs nodes to write corresponding registers. Finally we can use OpenCSD to store related files.
Programming up CTIs is a complex operation and I agree that a tool could be useful to handle this. As part of some additional work I am doing for the overall complex configuration of coresight systems there may be an opportunity to develop something.
For the present - this set describes the hardware topology which must include details of the trigger signal connections between CTI and device, and provides the software API to interconnect triggers and channels for propogation over the CTM to other CTIs/ devices.
Thanks for the feedback.
Mike
For this part, it's only specific in CTI/CTM drivers.
You and Mathieu/Suzuki have more deeper understanding for CoreSight framework and CTI usages, I just want to contribute some ideas (sorry if introduce noise :)
Thanks, Leo Yan
Hi Mike,
On Tue, May 28, 2019 at 10:36:08AM +0100, Mike Leach wrote:
[...]
To meet this requirement, below are my some suggestions:
- After read this patch set, it gives me impression that it mixes two things: hardware topology and triggers/channels configurations.
You are correct - there is the hardware topology and a software programming API to connect channels and triggers.
The hardware topology describes the association between the CTI and connected devices, along with the details of the hardware trigger signals between CTI and device.
Thanks for clarification.
These are static details that must appear in a device tree. I consider the connection information just as much part of the hardware topology as the association between the CTI and connected device.
I want to ask a general question so that I go back to read the driver with more clear idea. Now the detailed signals are presented in DT bindings, and these signals will set corresponding variables in CTI driver; the question is these singals finally will impact CTI registers configuration or we merely use these DT bindings to express the hardware attributions?
So below suggestions try to distinguish these two things.
For the hardware topology, we can reuse Suzuki's approach for 'in' and 'out' nodes for CTI devices:
Firstly, to reflect the whole matrix, we can create the 'static' device for CTM (same with static replicator and static funnel), thus under CTM sysfs node we can see all its bounded CTI devices;
I am not sure that this gives us any advantage over the current system
- each CTI is tagged with a CTM id - thus establishing the association
between CTIs.
From the driver's implementation point of view, I agree CTM driver is not
very necessary.
But let's think we want to use sysfs nodes to retrive the whole CTI connections under the same CTM matrix, if we without CTM node under sysfs, then we need to find all CTI devices and assume all of them are using the same CTM (but this is not always the case). If we create sysfs node for CTM, then we can use the CTM node to easily hook all connected CTI nodes.
BTW, if we review the CTI driver, it uses 'static' array to maintain CTI devices and assume all of them belong to CTM (id = 0); For flexibility, actually we can dynamically create CTM instance as well. But I don't have strong opinion for this, especially if you prefer the initial driver should be keep simple as possible; my suggestion is from how to easily reflect the connections under sysfs.
Though I went though you other replys, will take more time to understand them and reply when I have solid comment in next 2~3 days.
[...]
Thanks, Leo Yan
Hi Leo,
On Tue, 28 May 2019 at 14:41, Leo Yan leo.yan@linaro.org wrote:
Hi Mike,
On Tue, May 28, 2019 at 10:36:08AM +0100, Mike Leach wrote:
[...]
To meet this requirement, below are my some suggestions:
- After read this patch set, it gives me impression that it mixes two things: hardware topology and triggers/channels configurations.
You are correct - there is the hardware topology and a software programming API to connect channels and triggers.
The hardware topology describes the association between the CTI and connected devices, along with the details of the hardware trigger signals between CTI and device.
Thanks for clarification.
These are static details that must appear in a device tree. I consider the connection information just as much part of the hardware topology as the association between the CTI and connected device.
I want to ask a general question so that I go back to read the driver with more clear idea. Now the detailed signals are presented in DT bindings, and these signals will set corresponding variables in CTI driver; the question is these singals finally will impact CTI registers configuration or we merely use these DT bindings to express the hardware attributions?
The trigger signal information will not directly affect any of the CTI programmable registers. It will be used by the trigger / channel API to ensure that only valid (defined) triggers are connected to channels. The information can be used to identify the signal connections to correctly program the device.
So below suggestions try to distinguish these two things.
For the hardware topology, we can reuse Suzuki's approach for 'in' and 'out' nodes for CTI devices:
Firstly, to reflect the whole matrix, we can create the 'static' device for CTM (same with static replicator and static funnel), thus under CTM sysfs node we can see all its bounded CTI devices;
I am not sure that this gives us any advantage over the current system
- each CTI is tagged with a CTM id - thus establishing the association
between CTIs.
From the driver's implementation point of view, I agree CTM driver is not very necessary.
But let's think we want to use sysfs nodes to retrive the whole CTI connections under the same CTM matrix, if we without CTM node under sysfs, then we need to find all CTI devices and assume all of them are using the same CTM (but this is not always the case). If we create sysfs node for CTM, then we can use the CTM node to easily hook all connected CTI nodes.
The ctmid - an optional CTI device tree parameter - can be used in systems to differentiate between different CTMs. If this is not present then we assume all CTIs are connected to the same CTM. This is true for all systems we currently support - but may change in future especially multi-socket systems.
At present I do not see any advantage in finding all the CTIs on a given CTM. What you really need to know is if the CTI for device A, is on the same CTM as CTI for device B. For example, if I want to route the ETR full trigger back to a CPU outputting trace I need to know if the ETR CTI is on the same CTM as the CPU CTI. If you do need to know all the CTIs on a given CTM for some reason, then simply search the CoreSight bus and check the ctmid for each CTI found.
BTW, if we review the CTI driver, it uses 'static' array to maintain CTI devices and assume all of them belong to CTM (id = 0);
Next patch set has dropped the static array - there is only the dynamic list. The assumption of CTM ID = 0 is the defauit but is controllable in the device tree as explained above.
For flexibility, actually we can dynamically create CTM instance as well. But I don't have strong opinion for this, especially if you prefer the initial driver should be keep simple as possible; my suggestion is from how to easily reflect the connections under sysfs.
Though I went though you other replys, will take more time to understand them and reply when I have solid comment in next 2~3 days.
[...]
Thanks, Leo Yan
Thanks
Mike
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
On Tue, 28 May 2019 at 03:36, Mike Leach mike.leach@linaro.org wrote:
Hi Leo,
On Sun, 26 May 2019 at 14:11, Leo Yan leo.yan@linaro.org wrote:
Hi Mike,
On Wed, May 01, 2019 at 09:49:32AM +0100, Mike Leach wrote:
CTIs are defined in the device tree and associated with other CoreSight devices. The core CoreSight code has been modified to enable the registration of the CTI devices on the same bus as the other CoreSight components, but as these are not actually trace generation / capture devices, they are not part of the Coresight path when generating trace.
However, the definition of the standard CoreSight device has been extended to include a reference to an associated CTI device, and the enable / disable trace path operations will auto enable/disable any associated CTI devices at the same time.
Programming is at present via sysfs - a full API is provided to utilise the hardware capabilities. As CTI devices are unprogrammed by default, the auto enable describe above will have no effect until explicit programming takes place.
A set of device tree bindings specific to the CTI topology has been defined.
Documentation has been updated to describe both the CTI hardware, its use and programming in sysfs, and the new dts bindings required.
I'd like to reply you for sysfs nodes for CTI topology and avoid to disturb for Suzuki's APCI binding patch set threading, so I copy 'n paste your comments at here:
I fully agree that there is requirement to expose device connections as Suzuki's patches provided. As commented in the original patch, it removes the need for users to have knowledge of hardware specifics or access to device tree source.
For the trace datapath a simple link is sufficient to express this information. The nature of the data and connection is known - it is the trace data running from source to sink. The linked components are guaranteed to be registered coresight devices
However, the requirement for the CTI is different.
CTI is not limited to connecting to other coresight devices. Any device can be wired into a CTI trigger signal. These devices may or may not have drivers / entries in the device tree. For each connection a client needs to know the signals connected to the cti, the signal directions, the signal prupose if possible, and the device connected. For this reason we dynamically fill out a connections infomation sub-dir in sysfs containing _name, _trigin_sig, _trigout_sig, _trigin_type, _trigout_type - described in the patch [1].
This information is sufficient and necessary to enable a user to program a CTI in most cases.
As an example look at the Juno dtsi in [2]. CTI 0 is connected to ETR, ETF, STM and TPIU - all coresight devices. CTI 1 is connected to REF_CLK, system profiler and watchdog - no coresight devices at all. CTI 2 is connected to ETF, and two ELA devices - so 1 coresight device and 2 not coresight devices.
So my view is that for the case where CTI is connected to another CoreSight device the sysfs link could be used in addition to the information described above.
To meet this requirement, below are my some suggestions:
- After read this patch set, it gives me impression that it mixes two things: hardware topology and triggers/channels configurations.
You are correct - there is the hardware topology and a software programming API to connect channels and triggers.
The hardware topology describes the association between the CTI and connected devices, along with the details of the hardware trigger signals between CTI and device. These are static details that must appear in a device tree. I consider the connection information just as much part of the hardware topology as the association between the CTI and connected device.
So below suggestions try to distinguish these two things.
For the hardware topology, we can reuse Suzuki's approach for 'in' and 'out' nodes for CTI devices:
Firstly, to reflect the whole matrix, we can create the 'static' device for CTM (same with static replicator and static funnel), thus under CTM sysfs node we can see all its bounded CTI devices;
I am not sure that this gives us any advantage over the current system
- each CTI is tagged with a CTM id - thus establishing the association
between CTIs.
I think Leo's idea of creating static CTM devices isn't bad at all. What we currently do for static funnel and replicators definitely helps to understand the topology enacted on a platform. CTIs do have a CTM id but it's introducing another way of representing an association. I think there would be value in at least trying that approach in the next patchset.
HI Mathieu,
On Tue, 28 May 2019 at 17:07, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Tue, 28 May 2019 at 03:36, Mike Leach mike.leach@linaro.org wrote:
Hi Leo,
On Sun, 26 May 2019 at 14:11, Leo Yan leo.yan@linaro.org wrote:
Hi Mike,
On Wed, May 01, 2019 at 09:49:32AM +0100, Mike Leach wrote:
CTIs are defined in the device tree and associated with other CoreSight devices. The core CoreSight code has been modified to enable the registration of the CTI devices on the same bus as the other CoreSight components, but as these are not actually trace generation / capture devices, they are not part of the Coresight path when generating trace.
However, the definition of the standard CoreSight device has been extended to include a reference to an associated CTI device, and the enable / disable trace path operations will auto enable/disable any associated CTI devices at the same time.
Programming is at present via sysfs - a full API is provided to utilise the hardware capabilities. As CTI devices are unprogrammed by default, the auto enable describe above will have no effect until explicit programming takes place.
A set of device tree bindings specific to the CTI topology has been defined.
Documentation has been updated to describe both the CTI hardware, its use and programming in sysfs, and the new dts bindings required.
I'd like to reply you for sysfs nodes for CTI topology and avoid to disturb for Suzuki's APCI binding patch set threading, so I copy 'n paste your comments at here:
I fully agree that there is requirement to expose device connections as Suzuki's patches provided. As commented in the original patch, it removes the need for users to have knowledge of hardware specifics or access to device tree source.
For the trace datapath a simple link is sufficient to express this information. The nature of the data and connection is known - it is the trace data running from source to sink. The linked components are guaranteed to be registered coresight devices
However, the requirement for the CTI is different.
CTI is not limited to connecting to other coresight devices. Any device can be wired into a CTI trigger signal. These devices may or may not have drivers / entries in the device tree. For each connection a client needs to know the signals connected to the cti, the signal directions, the signal prupose if possible, and the device connected. For this reason we dynamically fill out a connections infomation sub-dir in sysfs containing _name, _trigin_sig, _trigout_sig, _trigin_type, _trigout_type - described in the patch [1].
This information is sufficient and necessary to enable a user to program a CTI in most cases.
As an example look at the Juno dtsi in [2]. CTI 0 is connected to ETR, ETF, STM and TPIU - all coresight devices. CTI 1 is connected to REF_CLK, system profiler and watchdog - no coresight devices at all. CTI 2 is connected to ETF, and two ELA devices - so 1 coresight device and 2 not coresight devices.
So my view is that for the case where CTI is connected to another CoreSight device the sysfs link could be used in addition to the information described above.
To meet this requirement, below are my some suggestions:
- After read this patch set, it gives me impression that it mixes two things: hardware topology and triggers/channels configurations.
You are correct - there is the hardware topology and a software programming API to connect channels and triggers.
The hardware topology describes the association between the CTI and connected devices, along with the details of the hardware trigger signals between CTI and device. These are static details that must appear in a device tree. I consider the connection information just as much part of the hardware topology as the association between the CTI and connected device.
So below suggestions try to distinguish these two things.
For the hardware topology, we can reuse Suzuki's approach for 'in' and 'out' nodes for CTI devices:
Firstly, to reflect the whole matrix, we can create the 'static' device for CTM (same with static replicator and static funnel), thus under CTM sysfs node we can see all its bounded CTI devices;
I am not sure that this gives us any advantage over the current system
- each CTI is tagged with a CTM id - thus establishing the association
between CTIs.
I think Leo's idea of creating static CTM devices isn't bad at all. What we currently do for static funnel and replicators definitely helps to understand the topology enacted on a platform. CTIs do have a CTM id but it's introducing another way of representing an association. I think there would be value in at least trying that approach in the next patchset.
I did consider this as part of my original design (list of CTMs, each CTM having a list of CTIs) - then decided I was writing a whole load of code that was unnecessarily complex for the problem being solved.
The static funnel and replicators are necessary to make the directed graph of the trace path make sense - combining or splitting paths at various points between source and sink.
There is no directed graph for CTIs - just a star topology with a single hop to all other CTIs. We could add a CTM driver, add in the device tree entries and connections, and a some driver code to handle them (and repeat for ACPI later) - but what problem is being solved by this? What advantages over the (optional) CTM ID?
If there was advantage later in creating an additional API that automatically connected trig_x on device_y via cti_n -> trig_a on device_b via cti_m, then a search of CTIs on a CTM might be useful. In this case we could go back to CTMs with lists of CTIs but these could be built using the CTM ID when each CTI was registered - there is still no overriding need for a CTM driver.
Yes it is a different way of representing and association - but it is a different type of association than the trace data path covered by the funnel/replicators.
Thanks
Mike