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