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. The driver accesses these in a platform agnostic manner, so ACPI bindings can be added later, once they have been agreed and defined for the CTI device.
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, on coresight/next tree - 5.2-rc1 based.
Changes since v2: Updates to allow for new features on coresight/next and feedback from Mathieu and Leo.
1) Rebase and restructuring to apply on top of ACPI support patch set, currently on coresight/next. of_coresight_cti has been renamed to coresight-cti-platform and device tree bindings added to this but accessed in a platform agnostic manner using fwnode for later ACPI support to be added. 2) Split the sysfs patch info a series of functional patches. 3) Revised the refcount and enabling support. 4) Adopted the generic naming protocol - CTIs are either cti_cpuN or cti_sysM 5) Various minor presentation /checkpatch issues highlighted in feedback. 6) revised CPU hotplug to cover missing cases needed by ETM.
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 (16): coresight: cti: Initial CoreSight CTI Driver coresight: cti: Add sysfs coresight mgmt reg access. coresight: cti: Add sysfs access to program function regs coresight: cti: Add sysfs trigger / channel programming API devicetree: bindings: Documentation for CTI bindings. coresight: cti: Add device tree support for v8 arch CTI coresight: cti: Add device tree support for impdef CTI. coresight: cti: Enable CTI associated with devices. coresight: cti: Add connection information to sysfs devicetree: bindings: Add header 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 drivers: coresight: Add generic CoreSight cpu power notifications. drivers: coresight: cti: Add CPU power event handling.
.../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 ++ MAINTAINERS | 2 + 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 | 85 +- drivers/hwtracing/coresight/Kconfig | 13 + drivers/hwtracing/coresight/Makefile | 4 + .../coresight/coresight-cti-platform.c | 501 +++++++ .../hwtracing/coresight/coresight-cti-sysfs.c | 1240 +++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.c | 835 +++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 262 ++++ drivers/hwtracing/coresight/coresight-priv.h | 36 + drivers/hwtracing/coresight/coresight.c | 232 ++- include/dt-bindings/arm/coresight-cti-dt.h | 36 + include/linux/coresight.h | 30 + 21 files changed, 4090 insertions(+), 15 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-platform.c 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 include/dt-bindings/arm/coresight-cti-dt.h
This introduces a baseline CTI driver and associated configuration files.
Uses the platform agnostic naming standard for CoreSight devices, along with a generic platform probing method that currently supports device tree descriptions, but allows for the ACPI bindings to be added once these have been defined for the CTI devices.
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 + .../coresight/coresight-cti-platform.c | 100 ++++ .../hwtracing/coresight/coresight-cti-sysfs.c | 82 +++ drivers/hwtracing/coresight/coresight-cti.c | 499 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 211 ++++++++ drivers/hwtracing/coresight/coresight-priv.h | 6 + drivers/hwtracing/coresight/coresight.c | 9 + include/linux/coresight.h | 26 + 9 files changed, 950 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti-platform.c 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
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 18e8d03321d6..49b894241520 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -107,4 +107,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 the 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 3c0ac421e211..43d563705e98 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -17,3 +17,7 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.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 \ + coresight-cti-platform.o \ + coresight-cti-sysfs.o + diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c new file mode 100644 index 000000000000..068932ac9e90 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + */ + +#include <linux/of.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. + */ +int of_get_coresight_cti_platform_data(struct device *dev, + struct coresight_platform_data *pdata) +{ + int ret = 0; + struct device_node *node = dev->of_node; + struct cti_drvdata *drvdata = dev_get_drvdata(dev); + + /* check for cpu association on default / v8 CTI */ + drvdata->ctidev.cpu = of_cti_get_cpu(node); + + /* get some CTI specifics */ + ret = of_cti_get_hw_data(dev, node, drvdata); + + return ret; +} +#else +inline int +of_get_coresight_cti_platform_data(struct device *dev, + struct coresight_platform_data *pdata) +{ + return -ENOENT; +} + +#endif + +struct coresight_platform_data * +coresight_cti_get_platform_data(struct device *dev) +{ + int ret = -ENOENT; + struct coresight_platform_data *pdata = NULL; + struct fwnode_handle *fwnode = dev_fwnode(dev); + + if (IS_ERR_OR_NULL(fwnode)) + goto error; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + ret = -ENOMEM; + goto error; + } + + if (is_of_node(fwnode)) + ret = of_get_coresight_cti_platform_data(dev, pdata); + + if (!ret) + return pdata; +error: + return ERR_PTR(ret); +} + diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c new file mode 100644 index 000000000000..deb882a6a367 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -0,0 +1,82 @@ +// 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 "coresight-cti.h" + +/* basic attributes */ +static ssize_t enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int enable_req, cpuid; + bool enabled, powered; + 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 (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"); + } + 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..4b3a5a91c4ff --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,499 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Linaro Limited, All rights reserved. + * Author: Mike Leach mike.leach@linaro.org + */ + +#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 non-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); + +#define csdev_to_cti_drvdata(csdev) \ + dev_get_drvdata(csdev->dev.parent) + +/* + * CTI naming. CTI bound to cores will have the name cti_cpu<N> where + * N is the CPU ID. System CTIs will have the name cti_sys<I> where I + * is an index allocated by order of discovery. + * + * CTI device name list - for CTI not bound to cores. + */ +DEFINE_CORESIGHT_DEVLIST(cti_sys_devs, "cti_sys"); + +/* 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(struct cti_drvdata *drvdata, bool update_refcount) +{ + struct cti_config *config = &drvdata->config; + struct device *dev = &drvdata->csdev->dev; + int rc = 0; + + pm_runtime_get_sync(dev->parent); + 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_err_not_enabled; + + if (drvdata->ctidev.cpu >= 0) { + rc = smp_call_function_single(drvdata->ctidev.cpu, + cti_enable_hw_smp_call, + drvdata, 1); + if (rc) + goto cti_err_not_enabled; + } else + cti_write_all_hw_regs(drvdata); + + config->hw_enabled = true; + if (update_refcount) + atomic_inc(&drvdata->config.enable_req_count); + spin_unlock(&drvdata->spinlock); + return rc; + + /* not enabled in this call */ +cti_not_enabled: + if (update_refcount) + atomic_inc(&drvdata->config.enable_req_count); + + /* cannot enable due to error */ +cti_err_not_enabled: + spin_unlock(&drvdata->spinlock); + pm_runtime_put(dev->parent); + return rc; +} + +/* disable hardware */ +static int cti_disable_hw(struct cti_drvdata *drvdata, bool update_refcount) +{ + struct cti_config *config = &drvdata->config; + struct device *dev = &drvdata->csdev->dev; + + spin_lock(&drvdata->spinlock); + + /* check refcount - disable on 0 */ + if (update_refcount && + (atomic_dec_return(&drvdata->config.enable_req_count) > 0)) + goto cti_not_disabled; + + /* no need to do anything if disabled or cpu unpowered*/ + if (!config->hw_enabled || !config->hw_powered) + goto cti_not_disabled; + + 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(dev); + return 0; + + /* not disabled this call */ +cti_not_disabled: + spin_unlock(&drvdata->spinlock); + return 0; +} + +static void cti_set_default_config(struct device *dev, + struct cti_drvdata *drvdata) +{ + struct cti_config *config = &drvdata->config; + u32 devid; + + /* + * Look at the HW DEVID register for some of the HW settings. + * DEVID[15:8] - max number of in / out triggers. + */ + devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); + 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 (config->nr_trig_max > CTIINOUTEN_MAX) { + dev_warn_once(dev, + "Limiting HW MaxTrig value(%d) to driver max(%d)\n", + config->nr_trig_max, CTIINOUTEN_MAX); + config->nr_trig_max = CTIINOUTEN_MAX; + } + + /* DEVID[19:16] - number of CTM channels */ + config->nr_ctm_channels = (int)((devid & 0xF0000) >> 16); + + /* Most regs default to 0 as zalloc'ed except...*/ + config->trig_filter_enable = 1; + config->ctigate = GENMASK(config->nr_ctm_channels - 1, 0); + 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 = GENMASK(n_trigs - 1, 0); + 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) +{ + struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev); + + /* enable hardware with refcount */ + return cti_enable_hw(drvdata, true); +} + +int cti_disable(struct coresight_device *csdev, void *__unused) +{ + struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev); + + /* disable hardware with refcount */ + return cti_disable_hw(drvdata, true); +} + +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; + 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 ect_node *ect_nd = NULL; + + /* 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*/ + dev_set_drvdata(dev, drvdata); + + /* default CTI device info */ + drvdata->ctidev.cpu = -1; + drvdata->ctidev.nr_trig_con = 0; + drvdata->ctidev.ctm_id = 0; + INIT_LIST_HEAD(&drvdata->ctidev.trig_cons); + + spin_lock_init(&drvdata->spinlock); + + /* initialise CTI driver config values */ + cti_set_default_config(dev, drvdata); + + /* Parse the .dts for connections and signals */ + pdata = coresight_cti_get_platform_data(dev); + if (IS_ERR(pdata)) { + dev_info(dev, "coresight_cti_get_platform_data err\n"); + ret = PTR_ERR(pdata); + goto err_out; + } + + /* default to powered - could change on PM notifications */ + drvdata->config.hw_powered = true; + + /* set up device name - will depend if cpu bound or otherwise */ + if (drvdata->ctidev.cpu >= 0) + cti_desc.name = devm_kasprintf(dev, GFP_KERNEL, "cti_cpu%d", + drvdata->ctidev.cpu); + else + cti_desc.name = coresight_alloc_device_name(&cti_sys_devs, dev); + if (!cti_desc.name) { + ret = -ENOMEM; + goto err_out; + } + + /* 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); + 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); + mutex_unlock(&ect_mutex); + + /* all done - dec pm refcount */ + pm_runtime_put(&adev->dev); + if (drvdata->ctidev.cpu >= 0) { + dev_info(dev, "%s: cti-CPU%d initialized\n", cti_desc.name, + drvdata->ctidev.cpu); + } else { + dev_info(dev, "%s: cti-system initialized\n", cti_desc.name); + } + return 0; + +err_out: + 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; + + mutex_lock(&ect_mutex); + + /* clear the connection list items */ + cti_free_conn_info(drvdata); + + /* remove from the list */ + list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) { + if (ect_item->cti_drv == drvdata) { + list_del(&ect_item->next); + goto ect_list_item_removed; + } + } +ect_list_item_removed: + mutex_unlock(&ect_mutex); +} +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..bc4c9bdf745e --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,211 @@ +/* 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. + * @asicctl: asic control register. + */ +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; + u8 xtrig_rchan_sel; + /* 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.. + * @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 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); +struct coresight_platform_data * +coresight_cti_get_platform_data(struct device *dev); + +#endif /* _CORESIGHT_CORESIGHT_CTI_H */ diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 8b07fe55395a..dd2991dfbf4c 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -161,6 +161,12 @@ 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 86d1fc2c1bd4..1c9ba2fd6879 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -972,6 +972,9 @@ static struct device_type coresight_dev_type[] = { { .name = "helper", }, + { + .name = "ect", + }, };
static void coresight_device_release(struct device *dev) @@ -979,6 +982,12 @@ static void coresight_device_release(struct device *dev) struct coresight_device *csdev = to_coresight_device(dev);
fwnode_handle_put(csdev->dev.fwnode); + + /* 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->refcnt); kfree(csdev); } diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a2b68823717b..0a2a69b5ade8 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; };
/** @@ -196,6 +206,7 @@ static struct coresight_dev_list (var) = { \ #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 @@ -262,11 +273,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
Hi Mike,
This is really shaping up nicely and things are much easier to read. Just a few notes below.
On Wed, Jun 05, 2019 at 08:02:58PM +0100, Mike Leach wrote:
This introduces a baseline CTI driver and associated configuration files.
Uses the platform agnostic naming standard for CoreSight devices, along with a generic platform probing method that currently supports device tree descriptions, but allows for the ACPI bindings to be added once these have been defined for the CTI devices.
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 + .../coresight/coresight-cti-platform.c | 100 ++++ .../hwtracing/coresight/coresight-cti-sysfs.c | 82 +++ drivers/hwtracing/coresight/coresight-cti.c | 499 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 211 ++++++++ drivers/hwtracing/coresight/coresight-priv.h | 6 + drivers/hwtracing/coresight/coresight.c | 9 + include/linux/coresight.h | 26 + 9 files changed, 950 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti-platform.c 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
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 18e8d03321d6..49b894241520 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -107,4 +107,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 the 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 3c0ac421e211..43d563705e98 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -17,3 +17,7 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.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 \
coresight-cti-platform.o \
coresight-cti-sysfs.o
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c new file mode 100644 index 000000000000..068932ac9e90 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2019, The Linux Foundation. All rights reserved.
- */
+#include <linux/of.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.
- */
+int of_get_coresight_cti_platform_data(struct device *dev,
struct coresight_platform_data *pdata)
+{
- int ret = 0;
- struct device_node *node = dev->of_node;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev);
- /* check for cpu association on default / v8 CTI */
- drvdata->ctidev.cpu = of_cti_get_cpu(node);
- /* get some CTI specifics */
- ret = of_cti_get_hw_data(dev, node, drvdata);
- return ret;
+} +#else +inline int +of_get_coresight_cti_platform_data(struct device *dev,
struct coresight_platform_data *pdata)
+{
- return -ENOENT;
+}
+#endif
+struct coresight_platform_data * +coresight_cti_get_platform_data(struct device *dev) +{
- int ret = -ENOENT;
- struct coresight_platform_data *pdata = NULL;
- struct fwnode_handle *fwnode = dev_fwnode(dev);
- if (IS_ERR_OR_NULL(fwnode))
goto error;
- pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
ret = -ENOMEM;
goto error;
- }
- if (is_of_node(fwnode))
ret = of_get_coresight_cti_platform_data(dev, pdata);
- if (!ret)
return pdata;
+error:
- return ERR_PTR(ret);
+}
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c new file mode 100644 index 000000000000..deb882a6a367 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -0,0 +1,82 @@ +// 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 "coresight-cti.h"
+/* basic attributes */ +static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int enable_req, cpuid;
- bool enabled, powered;
- 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 (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");
- }
- 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..4b3a5a91c4ff --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,499 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2019 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#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 non-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);
+#define csdev_to_cti_drvdata(csdev) \
- dev_get_drvdata(csdev->dev.parent)
+/*
- CTI naming. CTI bound to cores will have the name cti_cpu<N> where
- N is the CPU ID. System CTIs will have the name cti_sys<I> where I
- is an index allocated by order of discovery.
- CTI device name list - for CTI not bound to cores.
- */
+DEFINE_CORESIGHT_DEVLIST(cti_sys_devs, "cti_sys");
+/* 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));
Indentation problem.
- }
- /* 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(struct cti_drvdata *drvdata, bool update_refcount)
Is the update_refcount used at some point? If not please remove.
+{
- struct cti_config *config = &drvdata->config;
- struct device *dev = &drvdata->csdev->dev;
- int rc = 0;
- pm_runtime_get_sync(dev->parent);
- 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_err_not_enabled;
- if (drvdata->ctidev.cpu >= 0) {
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
if (rc)
goto cti_err_not_enabled;
- } else
cti_write_all_hw_regs(drvdata);
- config->hw_enabled = true;
- if (update_refcount)
atomic_inc(&drvdata->config.enable_req_count);
- spin_unlock(&drvdata->spinlock);
- return rc;
- /* not enabled in this call */
+cti_not_enabled:
- if (update_refcount)
atomic_inc(&drvdata->config.enable_req_count);
- /* cannot enable due to error */
+cti_err_not_enabled:
- spin_unlock(&drvdata->spinlock);
- pm_runtime_put(dev->parent);
- return rc;
+}
+/* disable hardware */ +static int cti_disable_hw(struct cti_drvdata *drvdata, bool update_refcount) +{
- struct cti_config *config = &drvdata->config;
- struct device *dev = &drvdata->csdev->dev;
- spin_lock(&drvdata->spinlock);
- /* check refcount - disable on 0 */
- if (update_refcount &&
(atomic_dec_return(&drvdata->config.enable_req_count) > 0))
goto cti_not_disabled;
- /* no need to do anything if disabled or cpu unpowered*/
- if (!config->hw_enabled || !config->hw_powered)
goto cti_not_disabled;
- 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(dev);
- return 0;
- /* not disabled this call */
+cti_not_disabled:
- spin_unlock(&drvdata->spinlock);
- return 0;
+}
+static void cti_set_default_config(struct device *dev,
struct cti_drvdata *drvdata)
+{
- struct cti_config *config = &drvdata->config;
- u32 devid;
- /*
* Look at the HW DEVID register for some of the HW settings.
* DEVID[15:8] - max number of in / out triggers.
*/
- devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
- 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 (config->nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
config->nr_trig_max, CTIINOUTEN_MAX);
config->nr_trig_max = CTIINOUTEN_MAX;
- }
- /* DEVID[19:16] - number of CTM channels */
- config->nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
- /* Most regs default to 0 as zalloc'ed except...*/
- config->trig_filter_enable = 1;
- config->ctigate = GENMASK(config->nr_ctm_channels - 1, 0);
- 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)
if (!tc) or NULL as Suzuki already pointed out.
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;
NULL
- int n_trigs = drvdata->config.nr_trig_max;
- u32 n_trig_mask = GENMASK(n_trigs - 1, 0);
- int *in_sig_types = 0, *out_sig_types = 0;
NULL
- 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)
I forgot to mention this in the earlier revision - please remove the void *. We can introduce it when it is needed. Same for cti_disable().
+{
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- /* enable hardware with refcount */
- return cti_enable_hw(drvdata, true);
+}
+int cti_disable(struct coresight_device *csdev, void *__unused) +{
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- /* disable hardware with refcount */
- return cti_disable_hw(drvdata, true);
+}
+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;
- 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 ect_node *ect_nd = NULL;
- /* 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*/
- dev_set_drvdata(dev, drvdata);
- /* default CTI device info */
- drvdata->ctidev.cpu = -1;
- drvdata->ctidev.nr_trig_con = 0;
- drvdata->ctidev.ctm_id = 0;
- INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
- spin_lock_init(&drvdata->spinlock);
- /* initialise CTI driver config values */
- cti_set_default_config(dev, drvdata);
- /* Parse the .dts for connections and signals */
- pdata = coresight_cti_get_platform_data(dev);
- if (IS_ERR(pdata)) {
dev_info(dev, "coresight_cti_get_platform_data err\n");
ret = PTR_ERR(pdata);
goto err_out;
- }
- /* default to powered - could change on PM notifications */
- drvdata->config.hw_powered = true;
- /* set up device name - will depend if cpu bound or otherwise */
- if (drvdata->ctidev.cpu >= 0)
cti_desc.name = devm_kasprintf(dev, GFP_KERNEL, "cti_cpu%d",
drvdata->ctidev.cpu);
- else
cti_desc.name = coresight_alloc_device_name(&cti_sys_devs, dev);
- if (!cti_desc.name) {
ret = -ENOMEM;
goto err_out;
- }
- /* 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);
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);
- mutex_unlock(&ect_mutex);
- /* all done - dec pm refcount */
- pm_runtime_put(&adev->dev);
- if (drvdata->ctidev.cpu >= 0) {
dev_info(dev, "%s: cti-CPU%d initialized\n", cti_desc.name,
drvdata->ctidev.cpu);
- } else {
dev_info(dev, "%s: cti-system initialized\n", cti_desc.name);
- }
- return 0;
+err_out:
- 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;
- mutex_lock(&ect_mutex);
- /* clear the connection list items */
- cti_free_conn_info(drvdata);
- /* remove from the list */
- list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
goto ect_list_item_removed;
}
- }
+ect_list_item_removed:
- mutex_unlock(&ect_mutex);
+} +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..bc4c9bdf745e --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,211 @@ +/* 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.
- @asicctl: asic control register.
- */
+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;
- u8 xtrig_rchan_sel;
- /* 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..
- @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 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); +struct coresight_platform_data * +coresight_cti_get_platform_data(struct device *dev);
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */ diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 8b07fe55395a..dd2991dfbf4c 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -161,6 +161,12 @@ 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 86d1fc2c1bd4..1c9ba2fd6879 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -972,6 +972,9 @@ static struct device_type coresight_dev_type[] = { { .name = "helper", },
- {
.name = "ect",
- },
}; static void coresight_device_release(struct device *dev) @@ -979,6 +982,12 @@ static void coresight_device_release(struct device *dev) struct coresight_device *csdev = to_coresight_device(dev); fwnode_handle_put(csdev->dev.fwnode);
- /* 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->refcnt); kfree(csdev);
} diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a2b68823717b..0a2a69b5ade8 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;
}; /** @@ -196,6 +206,7 @@ static struct coresight_dev_list (var) = { \ #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
@@ -262,11 +273,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
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
HI Mathieu,
On Mon, 10 Jun 2019 at 23:18, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Hi Mike,
This is really shaping up nicely and things are much easier to read. Just a few notes below.
On Wed, Jun 05, 2019 at 08:02:58PM +0100, Mike Leach wrote:
This introduces a baseline CTI driver and associated configuration files.
Uses the platform agnostic naming standard for CoreSight devices, along with a generic platform probing method that currently supports device tree descriptions, but allows for the ACPI bindings to be added once these have been defined for the CTI devices.
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 + .../coresight/coresight-cti-platform.c | 100 ++++ .../hwtracing/coresight/coresight-cti-sysfs.c | 82 +++ drivers/hwtracing/coresight/coresight-cti.c | 499 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 211 ++++++++ drivers/hwtracing/coresight/coresight-priv.h | 6 + drivers/hwtracing/coresight/coresight.c | 9 + include/linux/coresight.h | 26 + 9 files changed, 950 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti-platform.c 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
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 18e8d03321d6..49b894241520 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -107,4 +107,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 the 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 3c0ac421e211..43d563705e98 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -17,3 +17,7 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.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 \
coresight-cti-platform.o \
coresight-cti-sysfs.o
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c new file mode 100644 index 000000000000..068932ac9e90 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2019, The Linux Foundation. All rights reserved.
- */
+#include <linux/of.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.
- */
+int of_get_coresight_cti_platform_data(struct device *dev,
struct coresight_platform_data *pdata)
+{
int ret = 0;
struct device_node *node = dev->of_node;
struct cti_drvdata *drvdata = dev_get_drvdata(dev);
/* check for cpu association on default / v8 CTI */
drvdata->ctidev.cpu = of_cti_get_cpu(node);
/* get some CTI specifics */
ret = of_cti_get_hw_data(dev, node, drvdata);
return ret;
+} +#else +inline int +of_get_coresight_cti_platform_data(struct device *dev,
struct coresight_platform_data *pdata)
+{
return -ENOENT;
+}
+#endif
+struct coresight_platform_data * +coresight_cti_get_platform_data(struct device *dev) +{
int ret = -ENOENT;
struct coresight_platform_data *pdata = NULL;
struct fwnode_handle *fwnode = dev_fwnode(dev);
if (IS_ERR_OR_NULL(fwnode))
goto error;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
ret = -ENOMEM;
goto error;
}
if (is_of_node(fwnode))
ret = of_get_coresight_cti_platform_data(dev, pdata);
if (!ret)
return pdata;
+error:
return ERR_PTR(ret);
+}
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c new file mode 100644 index 000000000000..deb882a6a367 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -0,0 +1,82 @@ +// 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 "coresight-cti.h"
+/* basic attributes */ +static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int enable_req, cpuid;
bool enabled, powered;
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 (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");
}
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..4b3a5a91c4ff --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,499 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (c) 2019 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#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 non-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);
+#define csdev_to_cti_drvdata(csdev) \
dev_get_drvdata(csdev->dev.parent)
+/*
- CTI naming. CTI bound to cores will have the name cti_cpu<N> where
- N is the CPU ID. System CTIs will have the name cti_sys<I> where I
- is an index allocated by order of discovery.
- CTI device name list - for CTI not bound to cores.
- */
+DEFINE_CORESIGHT_DEVLIST(cti_sys_devs, "cti_sys");
+/* 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));
Indentation problem.
fixed.
}
/* 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(struct cti_drvdata *drvdata, bool update_refcount)
Is the update_refcount used at some point? If not please remove.
Yes, later in the function.
+{
struct cti_config *config = &drvdata->config;
struct device *dev = &drvdata->csdev->dev;
int rc = 0;
pm_runtime_get_sync(dev->parent);
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_err_not_enabled;
if (drvdata->ctidev.cpu >= 0) {
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
if (rc)
goto cti_err_not_enabled;
} else
cti_write_all_hw_regs(drvdata);
config->hw_enabled = true;
if (update_refcount)
atomic_inc(&drvdata->config.enable_req_count);
spin_unlock(&drvdata->spinlock);
return rc;
/* not enabled in this call */
+cti_not_enabled:
if (update_refcount)
atomic_inc(&drvdata->config.enable_req_count);
/* cannot enable due to error */
+cti_err_not_enabled:
spin_unlock(&drvdata->spinlock);
pm_runtime_put(dev->parent);
return rc;
+}
+/* disable hardware */ +static int cti_disable_hw(struct cti_drvdata *drvdata, bool update_refcount) +{
struct cti_config *config = &drvdata->config;
struct device *dev = &drvdata->csdev->dev;
spin_lock(&drvdata->spinlock);
/* check refcount - disable on 0 */
if (update_refcount &&
(atomic_dec_return(&drvdata->config.enable_req_count) > 0))
goto cti_not_disabled;
/* no need to do anything if disabled or cpu unpowered*/
if (!config->hw_enabled || !config->hw_powered)
goto cti_not_disabled;
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(dev);
return 0;
/* not disabled this call */
+cti_not_disabled:
spin_unlock(&drvdata->spinlock);
return 0;
+}
+static void cti_set_default_config(struct device *dev,
struct cti_drvdata *drvdata)
+{
struct cti_config *config = &drvdata->config;
u32 devid;
/*
* Look at the HW DEVID register for some of the HW settings.
* DEVID[15:8] - max number of in / out triggers.
*/
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
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 (config->nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
config->nr_trig_max, CTIINOUTEN_MAX);
config->nr_trig_max = CTIINOUTEN_MAX;
}
/* DEVID[19:16] - number of CTM channels */
config->nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
/* Most regs default to 0 as zalloc'ed except...*/
config->trig_filter_enable = 1;
config->ctigate = GENMASK(config->nr_ctm_channels - 1, 0);
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)
if (!tc) or NULL as Suzuki already pointed out.
OK - these fns somewhat reworked per suzukis suggestions
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;
NULL
int n_trigs = drvdata->config.nr_trig_max;
u32 n_trig_mask = GENMASK(n_trigs - 1, 0);
int *in_sig_types = 0, *out_sig_types = 0;
NULL
both done/reworked
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)
I forgot to mention this in the earlier revision - please remove the void *. We can introduce it when it is needed. Same for cti_disable().
done
+{
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
/* enable hardware with refcount */
return cti_enable_hw(drvdata, true);
+}
+int cti_disable(struct coresight_device *csdev, void *__unused) +{
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
/* disable hardware with refcount */
return cti_disable_hw(drvdata, true);
+}
+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;
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 ect_node *ect_nd = NULL;
/* 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*/
dev_set_drvdata(dev, drvdata);
/* default CTI device info */
drvdata->ctidev.cpu = -1;
drvdata->ctidev.nr_trig_con = 0;
drvdata->ctidev.ctm_id = 0;
INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
spin_lock_init(&drvdata->spinlock);
/* initialise CTI driver config values */
cti_set_default_config(dev, drvdata);
/* Parse the .dts for connections and signals */
pdata = coresight_cti_get_platform_data(dev);
if (IS_ERR(pdata)) {
dev_info(dev, "coresight_cti_get_platform_data err\n");
ret = PTR_ERR(pdata);
goto err_out;
}
/* default to powered - could change on PM notifications */
drvdata->config.hw_powered = true;
/* set up device name - will depend if cpu bound or otherwise */
if (drvdata->ctidev.cpu >= 0)
cti_desc.name = devm_kasprintf(dev, GFP_KERNEL, "cti_cpu%d",
drvdata->ctidev.cpu);
else
cti_desc.name = coresight_alloc_device_name(&cti_sys_devs, dev);
if (!cti_desc.name) {
ret = -ENOMEM;
goto err_out;
}
/* 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);
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);
mutex_unlock(&ect_mutex);
/* all done - dec pm refcount */
pm_runtime_put(&adev->dev);
if (drvdata->ctidev.cpu >= 0) {
dev_info(dev, "%s: cti-CPU%d initialized\n", cti_desc.name,
drvdata->ctidev.cpu);
} else {
dev_info(dev, "%s: cti-system initialized\n", cti_desc.name);
}
return 0;
+err_out:
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;
mutex_lock(&ect_mutex);
/* clear the connection list items */
cti_free_conn_info(drvdata);
/* remove from the list */
list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
goto ect_list_item_removed;
}
}
+ect_list_item_removed:
mutex_unlock(&ect_mutex);
+} +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..bc4c9bdf745e --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,211 @@ +/* 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.
- @asicctl: asic control register.
- */
+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;
u8 xtrig_rchan_sel;
/* 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..
- @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 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); +struct coresight_platform_data * +coresight_cti_get_platform_data(struct device *dev);
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */ diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 8b07fe55395a..dd2991dfbf4c 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -161,6 +161,12 @@ 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 86d1fc2c1bd4..1c9ba2fd6879 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -972,6 +972,9 @@ static struct device_type coresight_dev_type[] = { { .name = "helper", },
{
.name = "ect",
},
};
static void coresight_device_release(struct device *dev) @@ -979,6 +982,12 @@ static void coresight_device_release(struct device *dev) struct coresight_device *csdev = to_coresight_device(dev);
fwnode_handle_put(csdev->dev.fwnode);
/* 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->refcnt); kfree(csdev);
} diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a2b68823717b..0a2a69b5ade8 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;
};
/** @@ -196,6 +206,7 @@ static struct coresight_dev_list (var) = { \ #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
@@ -262,11 +273,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
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Thanks for the feedback
Mike
On 05/06/2019 20:02, Mike Leach wrote:
This introduces a baseline CTI driver and associated configuration files.
Uses the platform agnostic naming standard for CoreSight devices, along with a generic platform probing method that currently supports device tree descriptions, but allows for the ACPI bindings to be added once these have been defined for the CTI devices.
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 + .../coresight/coresight-cti-platform.c | 100 ++++ .../hwtracing/coresight/coresight-cti-sysfs.c | 82 +++ drivers/hwtracing/coresight/coresight-cti.c | 499 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 211 ++++++++ drivers/hwtracing/coresight/coresight-priv.h | 6 + drivers/hwtracing/coresight/coresight.c | 9 + include/linux/coresight.h | 26 + 9 files changed, 950 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti-platform.c 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
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 18e8d03321d6..49b894241520 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -107,4 +107,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
Does it need this dependency, given that CTI could be linked to non CoreSight device and it is perfectly possible to use for them alone without SINKS ?
+/*
- 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 */
minor nit: extra space ^^
- return (cpu < 0) ? -1 : cpu;
nit: We could simply return cpu ?
..
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c new file mode 100644 index 000000000000..deb882a6a367 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -0,0 +1,82 @@ +// 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 "coresight-cti.h"
+/* basic attributes */ +static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int enable_req, cpuid;
- bool enabled, powered;
- 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;
This is confusing. "hw" above means the associated device to the CTI
- enabled = drvdata->config.hw_enabled;
And here it means the actual CTI hw. Could we rename the above hw_powered to cpu_powered explicitly to avoid the confusion ?
+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);
As mentioned in the previous version, pleas use 0 instead of 16.
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c
+#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
s/pwr/power
- PE power domain.
- At this point we assume that the non-CPU CTIs are always powered as
- we do with sinks etc.
s/At this point we/We/
- 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;
+};
You may not need this extra structure, instead you could add a list_head to struct cti_drvdata.
+/*
- CTI naming. CTI bound to cores will have the name cti_cpu<N> where
- N is the CPU ID. System CTIs will have the name cti_sys<I> where I
- is an index allocated by order of discovery.
- CTI device name list - for CTI not bound to cores.
- */
+DEFINE_CORESIGHT_DEVLIST(cti_sys_devs, "cti_sys"); +/* write regs to hardware and enable */ +static int cti_enable_hw(struct cti_drvdata *drvdata, bool update_refcount) +{
- struct cti_config *config = &drvdata->config;
- struct device *dev = &drvdata->csdev->dev;
- int rc = 0;
- pm_runtime_get_sync(dev->parent);
- 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_err_not_enabled;
- if (drvdata->ctidev.cpu >= 0) {
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
if (rc)
goto cti_err_not_enabled;
- } else
cti_write_all_hw_regs(drvdata);
- config->hw_enabled = true;
- if (update_refcount)
atomic_inc(&drvdata->config.enable_req_count);
- spin_unlock(&drvdata->spinlock);
- return rc;
- /* not enabled in this call */
+cti_not_enabled:
That label unfortunately means the opposite of what it could have been. Could we simply make it cti_already_enabled ? That way, we could get rid of the comment above.
- if (update_refcount)
atomic_inc(&drvdata->config.enable_req_count);
- /* cannot enable due to error */
+cti_err_not_enabled:
- spin_unlock(&drvdata->spinlock);
- pm_runtime_put(dev->parent);
- return rc;
+}
+/* disable hardware */ +static int cti_disable_hw(struct cti_drvdata *drvdata, bool update_refcount) +{
- struct cti_config *config = &drvdata->config;
- struct device *dev = &drvdata->csdev->dev;
- spin_lock(&drvdata->spinlock);
- /* check refcount - disable on 0 */ > + if (update_refcount &&
I don't like the idea of passing "update_refcount" which could override the refcounts on the device for enable/disable. I think we could refactor the enable/disable operations to __enable() and __disable(). i.e,
enable_hw() { if (atomic_dec_return(refcount) == 0) __enable_hw() }
disable_hw() { if (atomic_dec_return(refcount) == 0) __disable_hw() }
Or simply rename the "update_refcount" to "force" with inverted loging to imply that we want to enable/disable forcefully irrespective of the refcount. Btw, I don't see why we need this bypassing, may be it will be clear in the coming patches.
(atomic_dec_return(&drvdata->config.enable_req_count) > 0))
goto cti_not_disabled;
- /* no need to do anything if disabled or cpu unpowered*/
nit: missing space "unpowered*/"
- if (!config->hw_enabled || !config->hw_powered)
goto cti_not_disabled;
This may need to be moved up before dropping the refcount, otherwise we may end up in "negative" refcounts if someone accidentally disables the device more times than it was enabled.
- 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(dev);
- return 0;
- /* not disabled this call */
nit: the label is self explanatory, you may drop this comment.
+cti_not_disabled:
- spin_unlock(&drvdata->spinlock);
- return 0;
+}
+static void cti_set_default_config(struct device *dev,
struct cti_drvdata *drvdata)
+{
- struct cti_config *config = &drvdata->config;
- u32 devid;
- /*
* Look at the HW DEVID register for some of the HW settings.
* DEVID[15:8] - max number of in / out triggers.
*/
- devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
- config->nr_trig_max = (int)((devid & 0xFF00) >> 8);
As mentioned in the other thread, please hide it under a macro.
- /*
* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
- if (config->nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
config->nr_trig_max, CTIINOUTEN_MAX);
config->nr_trig_max = CTIINOUTEN_MAX;
- }
- /* DEVID[19:16] - number of CTM channels */
- config->nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
Same here.
- /* Most regs default to 0 as zalloc'ed except...*/
- config->trig_filter_enable = 1;
- config->ctigate = GENMASK(config->nr_ctm_channels - 1, 0);
- 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)
nit: You may as well pass a cti_trig_con* as the argument and let the caller fill in that appropriately as it has to anyway allocate and fill in most of the data.
+{
- 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);
We need to check the result here.
- 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 = GENMASK(n_trigs - 1, 0);
- 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;
- }
I think we may be able to consolidate the cti_trig_grp struct by emebedding the sig_types into it.
i.e, struct cti_trig_grp { int nr_sigs; ... int sig_types[0]; }; and then :
in = kzalloc(offsetof(struct cti_trig_grp, sig_types[nr_trig], GFP_KERNEL);
- /* 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");
As mentioned above it may be a good idea to pass in the trig_con to the helper. Also, given that we may need to do this for other connections, you may add a helper to allocate a trig_con for a given number of "n_trigs" which will allocate all the bits needed for a trig_con including the trig_groups.
- if (ret)
goto conn_add_err;
- return ret;
+conn_add_err:
- kfree(in);
- kfree(out);
- kfree(out_sig_types);
- kfree(in_sig_types);
And that will cleanu up all of the above individual free ups. May be even add a helper to free a trig_con.
- return ret;
+}
+/** cti ect operations **/ +int cti_enable(struct coresight_device *csdev, void *__unused) +{
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- /* enable hardware with refcount */
- return cti_enable_hw(drvdata, true);
+}
+int cti_disable(struct coresight_device *csdev, void *__unused) +{
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- /* disable hardware with refcount */
- return cti_disable_hw(drvdata, true);
+}
+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,
+};
That looks like the normal "helper" ops, we use for CATU. We could see if there is a way to consolidate this under Helper type.
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
- int ret = 0;
- 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 ect_node *ect_nd = NULL;
- /* 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;
- }
As discussed ect_nd can be removed.
- /* driver data*/
- drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
You don't need this message here, you may keep them for debugging and drop it from the final series.
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__);
same here.
goto err_out;
- }
- drvdata->base = base;
- /* links between dev and drvdata*/
nit: superfluous comment
- dev_set_drvdata(dev, drvdata);
- /* default CTI device info */
- drvdata->ctidev.cpu = -1;
- drvdata->ctidev.nr_trig_con = 0;
- drvdata->ctidev.ctm_id = 0;
- INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
- spin_lock_init(&drvdata->spinlock);
- /* initialise CTI driver config values */
- cti_set_default_config(dev, drvdata);
- /* Parse the .dts for connections and signals */
- pdata = coresight_cti_get_platform_data(dev);
- if (IS_ERR(pdata)) {
dev_info(dev, "coresight_cti_get_platform_data err\n");
ret = PTR_ERR(pdata);
goto err_out;
- }
- /* default to powered - could change on PM notifications */
- drvdata->config.hw_powered = true;
- /* set up device name - will depend if cpu bound or otherwise */
- if (drvdata->ctidev.cpu >= 0)
cti_desc.name = devm_kasprintf(dev, GFP_KERNEL, "cti_cpu%d",
drvdata->ctidev.cpu);
- else
cti_desc.name = coresight_alloc_device_name(&cti_sys_devs, dev);
- if (!cti_desc.name) {
ret = -ENOMEM;
goto err_out;
- }
- /* 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);
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);
- mutex_unlock(&ect_mutex);
- /* all done - dec pm refcount */
- pm_runtime_put(&adev->dev);
- if (drvdata->ctidev.cpu >= 0) {
dev_info(dev, "%s: cti-CPU%d initialized\n", cti_desc.name,
drvdata->ctidev.cpu);
- } else {
dev_info(dev, "%s: cti-system initialized\n", cti_desc.name);
- }
The names already give you a clue as to whether it is CPU bound or system component. So, you may unify it :
dev_info(dev, "%s: initialized\n", cti_desc.name);
+/* 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);
If you unify the allocation/free of a "cti_trig_con" this could look a lot better and cleaner.
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;
- mutex_lock(&ect_mutex);
- /* clear the connection list items */
- cti_free_conn_info(drvdata);
- /* remove from the list */
- list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
goto ect_list_item_removed;
}
- }
+ect_list_item_removed:
- mutex_unlock(&ect_mutex);
+} +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 */
nit: simply say /* 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..bc4c9bdf745e --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,211 @@ +/* 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.
Is this true ? Do we need this at all here ? It doesn't affect the MAX number anyways.
- */
+#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;
See above, we could turn this into : int sig_types[0];
+};
+/**
- 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.
- @asicctl: asic control register.
- */
+struct cti_config {
- /* hardware description */
- int nr_ctm_channels;
- int nr_trig_max;
You may leave a new line here, if you want to use a comment like this.
- /* 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;
- u8 xtrig_rchan_sel;
- /* cti cross trig programmable regs */
- u32 ctiappset;
- u8 ctiinout_sel;
- u32 ctiinen[CTIINOUTEN_MAX];
- u32 ctiouten[CTIINOUTEN_MAX];
- u32 ctigate;
- u32 asicctl;
+};
Those lines improves the readability a lot.
+/**
- struct cti_drvdata - specifics for the CTI device
- @base: Memory mapped base address for 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 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); +struct coresight_platform_data * +coresight_cti_get_platform_data(struct device *dev);
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */ diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 8b07fe55395a..dd2991dfbf4c 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -161,6 +161,12 @@ 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 86d1fc2c1bd4..1c9ba2fd6879 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -972,6 +972,9 @@ static struct device_type coresight_dev_type[] = { { .name = "helper", },
- {
.name = "ect",
- }, };
static void coresight_device_release(struct device *dev) @@ -979,6 +982,12 @@ static void coresight_device_release(struct device *dev) struct coresight_device *csdev = to_coresight_device(dev); fwnode_handle_put(csdev->dev.fwnode);
- /* 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);
As discussed in the other thread, please introduce a "release" method for csdev and use it here. i.e,
if (csdev->release) csdev->release(csdev);
instead of hardcoding the ECT check in generic code.
kfree(csdev->refcnt); kfree(csdev); } diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a2b68823717b..0a2a69b5ade8 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,
I am not convinced that we need a new type.
}; 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
*/ union coresight_dev_subtype { /* We have some devices which acts as LINK and SINK */
defined by @coresight_dev_subtype_ect
@@ -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; };
/** @@ -196,6 +206,7 @@ static struct coresight_dev_list (var) = { \ #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
@@ -262,11 +273,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);
+};
These look similar to the "helper" ops. Could we not make the ECT a subtype of helper instead ?
Suzuki
Hi Suzuki,
On Tue, 11 Jun 2019 at 10:31, Suzuki K Poulose suzuki.poulose@arm.com wrote:
On 05/06/2019 20:02, Mike Leach wrote:
This introduces a baseline CTI driver and associated configuration files.
Uses the platform agnostic naming standard for CoreSight devices, along with a generic platform probing method that currently supports device tree descriptions, but allows for the ACPI bindings to be added once these have been defined for the CTI devices.
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 + .../coresight/coresight-cti-platform.c | 100 ++++ .../hwtracing/coresight/coresight-cti-sysfs.c | 82 +++ drivers/hwtracing/coresight/coresight-cti.c | 499 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 211 ++++++++ drivers/hwtracing/coresight/coresight-priv.h | 6 + drivers/hwtracing/coresight/coresight.c | 9 + include/linux/coresight.h | 26 + 9 files changed, 950 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti-platform.c 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
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 18e8d03321d6..49b894241520 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -107,4 +107,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
Does it need this dependency, given that CTI could be linked to non CoreSight device and it is perfectly possible to use for them alone without SINKS ?
You are correct - have removed.
+/*
- 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 */
minor nit: extra space ^^
return (cpu < 0) ? -1 : cpu;
nit: We could simply return cpu ?
This entire function disappears as we are now using a common get CPU fn that covers OF/ ACPI variants.
..
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c new file mode 100644 index 000000000000..deb882a6a367 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -0,0 +1,82 @@ +// 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 "coresight-cti.h"
+/* basic attributes */ +static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int enable_req, cpuid;
bool enabled, powered;
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;
This is confusing. "hw" above means the associated device to the CTI
enabled = drvdata->config.hw_enabled;
And here it means the actual CTI hw. Could we rename the above hw_powered to cpu_powered explicitly to avoid the confusion ?
CTI are not always CPU bound - so the labels mean precisely what they say:- hw_powered == CTI hw is powered. hw_enabled == CTI hardware is enabled.
granted a CPU bound CTI will have power dependencies on a cpu, but this is not the same thing.
+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);
As mentioned in the previous version, pleas use 0 instead of 16.
OK
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c
+#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
s/pwr/power
- PE power domain.
- At this point we assume that the non-CPU CTIs are always powered as
- we do with sinks etc.
s/At this point we/We/
- 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;
+};
You may not need this extra structure, instead you could add a list_head to struct cti_drvdata.
Good point - fixed for next spin
+/*
- CTI naming. CTI bound to cores will have the name cti_cpu<N> where
- N is the CPU ID. System CTIs will have the name cti_sys<I> where I
- is an index allocated by order of discovery.
- CTI device name list - for CTI not bound to cores.
- */
+DEFINE_CORESIGHT_DEVLIST(cti_sys_devs, "cti_sys"); +/* write regs to hardware and enable */ +static int cti_enable_hw(struct cti_drvdata *drvdata, bool update_refcount) +{
struct cti_config *config = &drvdata->config;
struct device *dev = &drvdata->csdev->dev;
int rc = 0;
pm_runtime_get_sync(dev->parent);
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_err_not_enabled;
if (drvdata->ctidev.cpu >= 0) {
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
if (rc)
goto cti_err_not_enabled;
} else
cti_write_all_hw_regs(drvdata);
config->hw_enabled = true;
if (update_refcount)
atomic_inc(&drvdata->config.enable_req_count);
spin_unlock(&drvdata->spinlock);
return rc;
/* not enabled in this call */
+cti_not_enabled:
That label unfortunately means the opposite of what it could have been. Could we simply make it cti_already_enabled ? That way, we could get rid of the comment above.
changed for next version
if (update_refcount)
atomic_inc(&drvdata->config.enable_req_count);
/* cannot enable due to error */
+cti_err_not_enabled:
spin_unlock(&drvdata->spinlock);
pm_runtime_put(dev->parent);
return rc;
+}
+/* disable hardware */ +static int cti_disable_hw(struct cti_drvdata *drvdata, bool update_refcount) +{
struct cti_config *config = &drvdata->config;
struct device *dev = &drvdata->csdev->dev;
spin_lock(&drvdata->spinlock);
/* check refcount - disable on 0 */ > + if (update_refcount &&
I don't like the idea of passing "update_refcount" which could override the refcounts on the device for enable/disable. I think we could refactor the enable/disable operations to __enable() and __disable(). i.e,
enable_hw() { if (atomic_dec_return(refcount) == 0) __enable_hw() }
disable_hw() { if (atomic_dec_return(refcount) == 0) __disable_hw() }
Or simply rename the "update_refcount" to "force" with inverted loging to imply that we want to enable/disable forcefully irrespective of the refcount. Btw, I don't see why we need this bypassing, may be it will be clear in the coming patches.
The CTI driver is power aware - it can be programmed if unpowered - and will assume that programmed state once it is powered. However this means that enable / disable can be called from two contexts:- 1) an enable/disable request context - i.e. sysfs / perf according to whatever trace operation is being prepared. In this case we count the requests as a CTI can be associated with multiple CS devices - each of which will ask for enable / disable CTI as they are enabled in the trace path 2) a power management context - when we want to ensure it is in the programmed state on power up / down. Here we must enable / disable according to the current request count, but must no affect that count.
The request maniipulation is done inside the spinlock of each of these functions, so that there is a single critical section controlling the hardware. This avoids the a race if CTI on CPU3 being hw disabled at the same time as CPU 3 is being powered down. Power management comes later but I want to avoid re-writiing the function logic later in the patchset which would get flagged as an issue.
(atomic_dec_return(&drvdata->config.enable_req_count) > 0))
goto cti_not_disabled;
/* no need to do anything if disabled or cpu unpowered*/
nit: missing space "unpowered*/"
if (!config->hw_enabled || !config->hw_powered)
goto cti_not_disabled;
This may need to be moved up before dropping the refcount, otherwise we may end up in "negative" refcounts if someone accidentally disables the device more times than it was enabled.
Moving this breaks the programming model - we are allowed to alter the enable request counter, even if currently unpowered or physically disabled. There is a risk I guess of a sysfs user disabling this more than enabling - but largely associated devices will be controlled as part of the path. we could add an explicit atomic_write of 0 on disable to ensure no overrun.
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(dev);
return 0;
/* not disabled this call */
nit: the label is self explanatory, you may drop this comment.
+cti_not_disabled:
spin_unlock(&drvdata->spinlock);
return 0;
+}
+static void cti_set_default_config(struct device *dev,
struct cti_drvdata *drvdata)
+{
struct cti_config *config = &drvdata->config;
u32 devid;
/*
* Look at the HW DEVID register for some of the HW settings.
* DEVID[15:8] - max number of in / out triggers.
*/
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
config->nr_trig_max = (int)((devid & 0xFF00) >> 8);
As mentioned in the other thread, please hide it under a macro.
/*
* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
if (config->nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
config->nr_trig_max, CTIINOUTEN_MAX);
config->nr_trig_max = CTIINOUTEN_MAX;
}
/* DEVID[19:16] - number of CTM channels */
config->nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
Same here.
Done
/* Most regs default to 0 as zalloc'ed except...*/
config->trig_filter_enable = 1;
config->ctigate = GENMASK(config->nr_ctm_channels - 1, 0);
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)
nit: You may as well pass a cti_trig_con* as the argument and let the caller fill in that appropriately as it has to anyway allocate and fill in most of the data.
This is reused in later patches but could probably be reworked.
+{
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);
We need to check the result here.
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 = GENMASK(n_trigs - 1, 0);
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;
}
I think we may be able to consolidate the cti_trig_grp struct by emebedding the sig_types into it.
i.e, struct cti_trig_grp { int nr_sigs; ... int sig_types[0]; }; and then :
in = kzalloc(offsetof(struct cti_trig_grp, sig_types[nr_trig], GFP_KERNEL);
That's a useful optimisation.
/* 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");
As mentioned above it may be a good idea to pass in the trig_con to the helper. Also, given that we may need to do this for other connections, you may add a helper to allocate a trig_con for a given number of "n_trigs" which will allocate all the bits needed for a trig_con including the trig_groups.
For specified connections n_trigs may and frequently does differ for in an out triggers in range 0 - max_trigs but this still could work.
if (ret)
goto conn_add_err;
return ret;
+conn_add_err:
kfree(in);
kfree(out);
kfree(out_sig_types);
kfree(in_sig_types);
And that will cleanu up all of the above individual free ups. May be even add a helper to free a trig_con.
return ret;
+}
+/** cti ect operations **/ +int cti_enable(struct coresight_device *csdev, void *__unused) +{
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
/* enable hardware with refcount */
return cti_enable_hw(drvdata, true);
+}
+int cti_disable(struct coresight_device *csdev, void *__unused) +{
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
/* disable hardware with refcount */
return cti_disable_hw(drvdata, true);
+}
+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,
+};
That looks like the normal "helper" ops, we use for CATU. We could see if there is a way to consolidate this under Helper type.
These have changed per Mathieus suggestion - no void * which the CATU help ops require.
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
int ret = 0;
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 ect_node *ect_nd = NULL;
/* 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;
}
As discussed ect_nd can be removed.
Yes
/* driver data*/
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
You don't need this message here, you may keep them for debugging and drop it from the final series.
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__);
same here.
goto err_out;
}
drvdata->base = base;
/* links between dev and drvdata*/
nit: superfluous comment
dev_set_drvdata(dev, drvdata);
/* default CTI device info */
drvdata->ctidev.cpu = -1;
drvdata->ctidev.nr_trig_con = 0;
drvdata->ctidev.ctm_id = 0;
INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
spin_lock_init(&drvdata->spinlock);
/* initialise CTI driver config values */
cti_set_default_config(dev, drvdata);
/* Parse the .dts for connections and signals */
pdata = coresight_cti_get_platform_data(dev);
if (IS_ERR(pdata)) {
dev_info(dev, "coresight_cti_get_platform_data err\n");
ret = PTR_ERR(pdata);
goto err_out;
}
/* default to powered - could change on PM notifications */
drvdata->config.hw_powered = true;
/* set up device name - will depend if cpu bound or otherwise */
if (drvdata->ctidev.cpu >= 0)
cti_desc.name = devm_kasprintf(dev, GFP_KERNEL, "cti_cpu%d",
drvdata->ctidev.cpu);
else
cti_desc.name = coresight_alloc_device_name(&cti_sys_devs, dev);
if (!cti_desc.name) {
ret = -ENOMEM;
goto err_out;
}
/* 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);
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);
mutex_unlock(&ect_mutex);
/* all done - dec pm refcount */
pm_runtime_put(&adev->dev);
if (drvdata->ctidev.cpu >= 0) {
dev_info(dev, "%s: cti-CPU%d initialized\n", cti_desc.name,
drvdata->ctidev.cpu);
} else {
dev_info(dev, "%s: cti-system initialized\n", cti_desc.name);
}
The names already give you a clue as to whether it is CPU bound or system component. So, you may unify it :
dev_info(dev, "%s: initialized\n", cti_desc.name);
indeed.
+/* 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);
If you unify the allocation/free of a "cti_trig_con" this could look a lot better and cleaner.
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;
mutex_lock(&ect_mutex);
/* clear the connection list items */
cti_free_conn_info(drvdata);
/* remove from the list */
list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
goto ect_list_item_removed;
}
}
+ect_list_item_removed:
mutex_unlock(&ect_mutex);
+} +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 */
nit: simply say /* 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..bc4c9bdf745e --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,211 @@ +/* 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.
Is this true ? Do we need this at all here ? It doesn't affect the MAX number anyways.
This is the theoretical hardware maximum for CTI CSSoc 600, plus the driver maximum.
- */
+#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;
See above, we could turn this into : int sig_types[0];
+};
+/**
- 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.
- @asicctl: asic control register.
- */
+struct cti_config {
/* hardware description */
int nr_ctm_channels;
int nr_trig_max;
You may leave a new line here, if you want to use a comment like this.
/* 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;
u8 xtrig_rchan_sel;
/* cti cross trig programmable regs */
u32 ctiappset;
u8 ctiinout_sel;
u32 ctiinen[CTIINOUTEN_MAX];
u32 ctiouten[CTIINOUTEN_MAX];
u32 ctigate;
u32 asicctl;
+};
Those lines improves the readability a lot.
+/**
- struct cti_drvdata - specifics for the CTI device
- @base: Memory mapped base address for 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 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); +struct coresight_platform_data * +coresight_cti_get_platform_data(struct device *dev);
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */ diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 8b07fe55395a..dd2991dfbf4c 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -161,6 +161,12 @@ 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 86d1fc2c1bd4..1c9ba2fd6879 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -972,6 +972,9 @@ static struct device_type coresight_dev_type[] = { { .name = "helper", },
{
.name = "ect",
},
};
static void coresight_device_release(struct device *dev)
@@ -979,6 +982,12 @@ static void coresight_device_release(struct device *dev) struct coresight_device *csdev = to_coresight_device(dev);
fwnode_handle_put(csdev->dev.fwnode);
/* 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);
As discussed in the other thread, please introduce a "release" method for csdev and use it here. i.e,
if (csdev->release) csdev->release(csdev);
instead of hardcoding the ECT check in generic code.
Did think the point of csdev->type was to allow just this - seems to be elsewhere. As this is CTI specific release function, this is now done by hijacking csdev->dev.release, & calling back into here from cti_device_release - which is neater than adding a "common" function to csdev that only CTI needs.
kfree(csdev->refcnt); kfree(csdev);
} diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a2b68823717b..0a2a69b5ade8 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,
I am not convinced that we need a new type.
ECT is a distinct subsystem from the normal trace data path and can connect to non coresight devices.
};
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
*/ union coresight_dev_subtype { /* We have some devices which acts as LINK and SINK */
defined by @coresight_dev_subtype_ect
@@ -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;
};
/**
@@ -196,6 +206,7 @@ static struct coresight_dev_list (var) = { \ #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
@@ -262,11 +273,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);
+};
These look similar to the "helper" ops. Could we not make the ECT a subtype of helper instead ?
These have changed - no void * as it was not used.
Thanks for the feedback
Mike
Suzuki
Adds sysfs access to the coresight management registers.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../hwtracing/coresight/coresight-cti-sysfs.c | 49 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-priv.h | 1 + 2 files changed, 50 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index deb882a6a367..ad7c45390313 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -66,9 +66,52 @@ 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, "%d\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) + +/* 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, };
@@ -76,7 +119,13 @@ 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", +}; + const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_group, + &coresight_cti_mgmt_group, NULL, }; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index dd2991dfbf4c..64a888cc620f 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -22,6 +22,7 @@ #define CORESIGHT_CLAIMCLR 0xfa4 #define CORESIGHT_LAR 0xfb0 #define CORESIGHT_LSR 0xfb4 +#define CORESIGHT_DEVARCH 0xfbc #define CORESIGHT_AUTHSTATUS 0xfb8 #define CORESIGHT_DEVID 0xfc8 #define CORESIGHT_DEVTYPE 0xfcc
On Wed, Jun 05, 2019 at 08:02:59PM +0100, Mike Leach wrote:
Adds sysfs access to the coresight management registers.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 49 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-priv.h | 1 + 2 files changed, 50 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index deb882a6a367..ad7c45390313 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -66,9 +66,52 @@ 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, "%d\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)
+/* 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);
Do we need the peripheral ID registers? Same for DEVARCH - I don't see it being used anywhere after this patch.
With the above, Reviewed-by: Mathieu Poirier mathieu.poirier@linaro.org
+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,
}; @@ -76,7 +119,13 @@ 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",
+};
const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_group,
- &coresight_cti_mgmt_group, NULL,
}; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index dd2991dfbf4c..64a888cc620f 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -22,6 +22,7 @@ #define CORESIGHT_CLAIMCLR 0xfa4 #define CORESIGHT_LAR 0xfb0 #define CORESIGHT_LSR 0xfb4 +#define CORESIGHT_DEVARCH 0xfbc #define CORESIGHT_AUTHSTATUS 0xfb8 #define CORESIGHT_DEVID 0xfc8
#define CORESIGHT_DEVTYPE 0xfcc
2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
On Mon, 10 Jun 2019 at 23:31, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, Jun 05, 2019 at 08:02:59PM +0100, Mike Leach wrote:
Adds sysfs access to the coresight management registers.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 49 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-priv.h | 1 + 2 files changed, 50 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index deb882a6a367..ad7c45390313 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -66,9 +66,52 @@ 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, "%d\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)
+/* 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);
Do we need the peripheral ID registers? Same for DEVARCH - I don't see it being used anywhere after this patch.
I prefer completeness - whoever is using the raw register access is likely to have the TRM - I don't wish to presume to know what some user may require. (as an aside - these regs are used in the AMBA layer to ID the CTI)
Thanks
Mike
With the above, Reviewed-by: Mathieu Poirier mathieu.poirier@linaro.org
+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,
};
@@ -76,7 +119,13 @@ 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",
+};
const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_group,
&coresight_cti_mgmt_group, NULL,
}; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index dd2991dfbf4c..64a888cc620f 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -22,6 +22,7 @@ #define CORESIGHT_CLAIMCLR 0xfa4 #define CORESIGHT_LAR 0xfb0 #define CORESIGHT_LSR 0xfb4 +#define CORESIGHT_DEVARCH 0xfbc #define CORESIGHT_AUTHSTATUS 0xfb8 #define CORESIGHT_DEVID 0xfc8
#define CORESIGHT_DEVTYPE 0xfcc
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
Mike,
On 05/06/2019 20:02, Mike Leach wrote:
Adds sysfs access to the coresight management registers.
Signed-off-by: Mike Leach mike.leach@linaro.org
Thanks for splitting the patches and this looks much nicer to review.
.../hwtracing/coresight/coresight-cti-sysfs.c | 49 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-priv.h | 1 + 2 files changed, 50 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index deb882a6a367..ad7c45390313 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -66,9 +66,52 @@ 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, "%d\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)
+/* 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);
Same question as Mathieu, do we need pidrX ? If at all we do, I would prefer to expose the consolidated PID.
Otherwise looks good to me:
Suzuki
On Tue, 11 Jun 2019 at 10:36, Suzuki K Poulose suzuki.poulose@arm.com wrote:
Mike,
On 05/06/2019 20:02, Mike Leach wrote:
Adds sysfs access to the coresight management registers.
Signed-off-by: Mike Leach mike.leach@linaro.org
Thanks for splitting the patches and this looks much nicer to review.
.../hwtracing/coresight/coresight-cti-sysfs.c | 49 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-priv.h | 1 + 2 files changed, 50 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index deb882a6a367..ad7c45390313 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -66,9 +66,52 @@ 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, "%d\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)
+/* 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);
Same question as Mathieu, do we need pidrX ? If at all we do,
Same answer - consistency with TRM
Thanks
Mike
I would prefer to expose the consolidated PID.
Otherwise looks good to me:
Suzuki
Adds in sysfs programming support for the CTI function register sets. Allows direct manipulation of channel / trigger association registers.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../hwtracing/coresight/coresight-cti-sysfs.c | 402 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.c | 19 + drivers/hwtracing/coresight/coresight-cti.h | 6 + 3 files changed, 427 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index ad7c45390313..caecf011f6e0 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -115,6 +115,402 @@ static struct attribute *coresight_cti_mgmt_attrs[] = { NULL, };
+/* CTI low level programming registers */ + +/* + * 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) +{ + u32 val = 0; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + spin_lock(&drvdata->spinlock); + if (pval) { + val = *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, "%#x\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; +} + +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, "%d\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, 10, &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; + 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*/ + 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; + 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 + */ + config->ctiappset &= ~val; + + /* 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); + +/* + * #define CTI_DEBUG_INTEGRATION_CTRL to enable the access to the integration + * control registers. Normally only used to investigate connection data. + */ +// #define CTI_DEBUG_INTEGRATION_CTRL + +#ifdef CTI_DEBUG_INTEGRATION_CTRL + +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(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK); + +#endif /* CTI_DEBUG_INTEGRATION_CTRL */ + +coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); + +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, +#ifdef CTI_DEBUG_INTEGRATION_CTRL + &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, +#endif + NULL, +}; + +/* sysfs groups */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, }; @@ -124,8 +520,14 @@ static const struct attribute_group coresight_cti_mgmt_group = { .name = "mgmt", };
+static const struct attribute_group coresight_cti_regs_group = { + .attrs = coresight_cti_regs_attrs, + .name = "regs", +}; + const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_group, &coresight_cti_mgmt_group, + &coresight_cti_regs_group, NULL, }; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 4b3a5a91c4ff..cd804b29df07 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -158,6 +158,25 @@ static int cti_disable_hw(struct cti_drvdata *drvdata, bool update_refcount) 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); +} + +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); +} + static void cti_set_default_config(struct device *dev, struct cti_drvdata *drvdata) { diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index bc4c9bdf745e..215b4a4ac45d 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -205,7 +205,13 @@ 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_intack(struct device *dev, u32 ackval); +void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value); + struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev);
+/* cti powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered) + #endif /* _CORESIGHT_CORESIGHT_CTI_H */
On Wed, Jun 05, 2019 at 08:03:00PM +0100, Mike Leach wrote:
Adds in sysfs programming support for the CTI function register sets. Allows direct manipulation of channel / trigger association registers.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 402 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.c | 19 + drivers/hwtracing/coresight/coresight-cti.h | 6 + 3 files changed, 427 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index ad7c45390313..caecf011f6e0 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -115,6 +115,402 @@ static struct attribute *coresight_cti_mgmt_attrs[] = { NULL, }; +/* CTI low level programming registers */
+/*
- 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)
+{
- u32 val = 0;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- if (pval) {
val = *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, "%#x\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)
reg_offset could have been on the second line.
+{
- 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;
+}
+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, "%d\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, 10, &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);
Why the -1 here? Wouldn't you want to see the real HW value if the device is enabled? The only logic I can see here is that if the HW is enabled then the value of CTIGATE *must* be the same as ->config.ctigate. The same comment/question for those below.
+}
+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;
- 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*/
- 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;
- 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
*/
- config->ctiappset &= ~val;
- /* 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);
+/*
- #define CTI_DEBUG_INTEGRATION_CTRL to enable the access to the integration
- control registers. Normally only used to investigate connection data.
- */
+// #define CTI_DEBUG_INTEGRATION_CTRL
C++ style comment, please remove.
+#ifdef CTI_DEBUG_INTEGRATION_CTRL
+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(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+#endif /* CTI_DEBUG_INTEGRATION_CTRL */
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS);
+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,
+#ifdef CTI_DEBUG_INTEGRATION_CTRL
- &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,
+#endif
- NULL,
+};
+/* sysfs groups */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, }; @@ -124,8 +520,14 @@ static const struct attribute_group coresight_cti_mgmt_group = { .name = "mgmt", }; +static const struct attribute_group coresight_cti_regs_group = {
- .attrs = coresight_cti_regs_attrs,
- .name = "regs",
+};
const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_group, &coresight_cti_mgmt_group,
- &coresight_cti_regs_group, NULL,
}; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 4b3a5a91c4ff..cd804b29df07 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -158,6 +158,25 @@ static int cti_disable_hw(struct cti_drvdata *drvdata, bool update_refcount) 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);
+}
+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);
+}
static void cti_set_default_config(struct device *dev, struct cti_drvdata *drvdata) { diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index bc4c9bdf745e..215b4a4ac45d 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -205,7 +205,13 @@ 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_intack(struct device *dev, u32 ackval); +void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value);
struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev); +/* cti powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
#endif /* _CORESIGHT_CORESIGHT_CTI_H */
With the above,
Reviewed-by: Mathieu Poirier mathieu.poirier@linaro.org
-- 2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
On Tue, 11 Jun 2019 at 22:43, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, Jun 05, 2019 at 08:03:00PM +0100, Mike Leach wrote:
Adds in sysfs programming support for the CTI function register sets. Allows direct manipulation of channel / trigger association registers.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 402 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.c | 19 + drivers/hwtracing/coresight/coresight-cti.h | 6 + 3 files changed, 427 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index ad7c45390313..caecf011f6e0 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -115,6 +115,402 @@ static struct attribute *coresight_cti_mgmt_attrs[] = { NULL, };
+/* CTI low level programming registers */
+/*
- 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)
+{
u32 val = 0;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
if (pval) {
val = *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, "%#x\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)
reg_offset could have been on the second line.
agreed.
+{
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;
+}
+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, "%d\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, 10, &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);
Why the -1 here? Wouldn't you want to see the real HW value if the device is enabled? The only logic I can see here is that if the HW is enabled then the value of CTIGATE *must* be the same as ->config.ctigate. The same comment/question for those below.
Logic of the cti_reg32_show() function means that if we provide a pointer to the cached value, then we use that irrespective of the -1 / offset provided. This in theory is more efficient than always reading back a value (and these static registers will _always_ be the same as the cached value).
Saying that, perhaps there is merit in changing the logic of the function to only use the cached value if we can't get at the real hw value - given that the cost of the read is low.
This has now been re-written as part of the "macro" changes suggested by suzuki
+}
+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;
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*/
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;
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
*/
config->ctiappset &= ~val;
/* 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);
+/*
- #define CTI_DEBUG_INTEGRATION_CTRL to enable the access to the integration
- control registers. Normally only used to investigate connection data.
- */
+// #define CTI_DEBUG_INTEGRATION_CTRL
C++ style comment, please remove.
Done.
+#ifdef CTI_DEBUG_INTEGRATION_CTRL
+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(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+#endif /* CTI_DEBUG_INTEGRATION_CTRL */
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS);
+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,
+#ifdef CTI_DEBUG_INTEGRATION_CTRL
&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,
+#endif
NULL,
+};
+/* sysfs groups */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, }; @@ -124,8 +520,14 @@ static const struct attribute_group coresight_cti_mgmt_group = { .name = "mgmt", };
+static const struct attribute_group coresight_cti_regs_group = {
.attrs = coresight_cti_regs_attrs,
.name = "regs",
+};
const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_group, &coresight_cti_mgmt_group,
&coresight_cti_regs_group, NULL,
}; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 4b3a5a91c4ff..cd804b29df07 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -158,6 +158,25 @@ static int cti_disable_hw(struct cti_drvdata *drvdata, bool update_refcount) 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);
+}
+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);
+}
static void cti_set_default_config(struct device *dev, struct cti_drvdata *drvdata) { diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index bc4c9bdf745e..215b4a4ac45d 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -205,7 +205,13 @@ 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_intack(struct device *dev, u32 ackval); +void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value);
struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev);
+/* cti powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
#endif /* _CORESIGHT_CORESIGHT_CTI_H */
With the above,
Reviewed-by: Mathieu Poirier mathieu.poirier@linaro.org
Ok, thanks
Mike
-- 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
On 05/06/2019 20:03, Mike Leach wrote:
Adds in sysfs programming support for the CTI function register sets. Allows direct manipulation of channel / trigger association registers.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 402 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.c | 19 + drivers/hwtracing/coresight/coresight-cti.h | 6 + 3 files changed, 427 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index ad7c45390313..caecf011f6e0 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -115,6 +115,402 @@ static struct attribute *coresight_cti_mgmt_attrs[] = { NULL, }; +/* CTI low level programming registers */
+/*
- 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)
nit: Please could you rename pval to *pcached ?
+{
- u32 val = 0;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- if (pval) {
val = *pval;
- } else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
nit: s/CTI_PWR_ENA/cti_available(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, "%#x\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 do not hard code 16. Use 0 instead, everywhere.
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;
+}
+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, "%d\n", val);
+}
I think we may be able to use dev_ext_attribute to expose the individual inen/inout attributes rather than using the inout_sel. I am not too particular about this, as it may follow the existing scheme for ETMs.
+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, 10, &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);
-- CUT here ---
+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 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);
---- End here ---
You may be able to use a macro reduce the code a bit: something like:
#define cti_config_ reg32_rw(name, cfg, offset) \ static ssize_t #name_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.##cfg, -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.##cfg, offset); \ } \ static DEVICE_ATTR_RW(name)
cti_config_reg32_rw(appset, ctiappset, CTIAPPSET); cti_config_reg32_rw(asicctl, asicctl, ASICCTL); cti_config_reg32_rw(gate, ctigate, CTIGATE);
+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))
s/16/0
return -EINVAL;
- cti_write_intack(dev, val);
- return size;
+} +static DEVICE_ATTR_WO(intack);
+static ssize_t appclear_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);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
s/16/0
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* a 1'b1 in appclr clears down the same bit in appset*/
- config->ctiappset &= ~val;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
Should we return an error if the device wasn't turned on ?
- 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;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
s/16/0
- spin_lock(&drvdata->spinlock);
- /*
* a 1'b1 in apppulse sets then clears the bit,
* effectively clears down the same bit in appset
*/
- config->ctiappset &= ~val;
- /* 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);
+/*
- #define CTI_DEBUG_INTEGRATION_CTRL to enable the access to the integration
- control registers. Normally only used to investigate connection data.
- */
+// #define CTI_DEBUG_INTEGRATION_CTRL
+#ifdef CTI_DEBUG_INTEGRATION_CTRL
+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);
Please use a macro to reduce the code, similar to above. Say: cti_reg32_rw(name, offset)
+coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+#endif /* CTI_DEBUG_INTEGRATION_CTRL */
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS);
+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,
+#ifdef CTI_DEBUG_INTEGRATION_CTRL
- &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,
+#endif
- NULL,
+};
+/* sysfs groups */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, }; @@ -124,8 +520,14 @@ static const struct attribute_group coresight_cti_mgmt_group = { .name = "mgmt", }; +static const struct attribute_group coresight_cti_regs_group = {
- .attrs = coresight_cti_regs_attrs,
- .name = "regs",
+};
- const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_group, &coresight_cti_mgmt_group,
- &coresight_cti_regs_group, NULL, };
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 4b3a5a91c4ff..cd804b29df07 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -158,6 +158,25 @@ static int cti_disable_hw(struct cti_drvdata *drvdata, bool update_refcount) return 0; } +void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) +{
- CS_UNLOCK(drvdata->base);
Don't we need to check if we have claimed the device before wrting something here ? Or at least make sure that the caller has claimed it ?
- writel_relaxed(value, drvdata->base+offset);
nit: coding style.
- CS_LOCK(drvdata->base);
+}
+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);
+}
- static void cti_set_default_config(struct device *dev, struct cti_drvdata *drvdata) {
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index bc4c9bdf745e..215b4a4ac45d 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -205,7 +205,13 @@ 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_intack(struct device *dev, u32 ackval); +void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value);
- struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev);
+/* cti powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
Please could we be more explicit with the name, say cti_available() ?
Cheers Suzuki
Hi Suzuki
On Wed, 12 Jun 2019 at 11:52, Suzuki K Poulose suzuki.poulose@arm.com wrote:
On 05/06/2019 20:03, Mike Leach wrote:
Adds in sysfs programming support for the CTI function register sets. Allows direct manipulation of channel / trigger association registers.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 402 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.c | 19 + drivers/hwtracing/coresight/coresight-cti.h | 6 + 3 files changed, 427 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index ad7c45390313..caecf011f6e0 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -115,6 +115,402 @@ static struct attribute *coresight_cti_mgmt_attrs[] = { NULL, };
+/* CTI low level programming registers */
+/*
- 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)
nit: Please could you rename pval to *pcached ?
done.
+{
u32 val = 0;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
if (pval) {
val = *pval;
} else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
nit: s/CTI_PWR_ENA/cti_available(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, "%#x\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 do not hard code 16. Use 0 instead, everywhere.
Fixed in all places
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;
+}
+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, "%d\n", val);
+}
I think we may be able to use dev_ext_attribute to expose the individual inen/inout attributes rather than using the inout_sel. I am not too particular about this, as it may follow the existing scheme for ETMs.
+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, 10, &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);
-- CUT here ---
+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 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);
---- End here ---
You may be able to use a macro reduce the code a bit: something like:
#define cti_config_ reg32_rw(name, cfg, offset) \ static ssize_t #name_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.##cfg, -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.##cfg, offset); \ } \ static DEVICE_ATTR_RW(name)
cti_config_reg32_rw(appset, ctiappset, CTIAPPSET); cti_config_reg32_rw(asicctl, asicctl, ASICCTL); cti_config_reg32_rw(gate, ctigate, CTIGATE);
Macros implemented here and a couple of other places.
+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))
s/16/0
return -EINVAL;
cti_write_intack(dev, val);
return size;
+} +static DEVICE_ATTR_WO(intack);
+static ssize_t appclear_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);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
s/16/0
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* a 1'b1 in appclr clears down the same bit in appset*/
config->ctiappset &= ~val;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
Should we return an error if the device wasn't turned on ?
No - if the device is not on then just the cached values are updated. Once the device is turned on then whatever the current value is will be applied to the system. This allows programming either before enable, while powered down, or while enabled. Unlike the ETM CTIs can be altered while live (appset / appclear are designed for just that).
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;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
s/16/0
spin_lock(&drvdata->spinlock);
/*
* a 1'b1 in apppulse sets then clears the bit,
* effectively clears down the same bit in appset
*/
config->ctiappset &= ~val;
/* 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);
+/*
- #define CTI_DEBUG_INTEGRATION_CTRL to enable the access to the integration
- control registers. Normally only used to investigate connection data.
- */
+// #define CTI_DEBUG_INTEGRATION_CTRL
+#ifdef CTI_DEBUG_INTEGRATION_CTRL
+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);
Please use a macro to reduce the code, similar to above. Say: cti_reg32_rw(name, offset)
+coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+#endif /* CTI_DEBUG_INTEGRATION_CTRL */
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS);
+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,
+#ifdef CTI_DEBUG_INTEGRATION_CTRL
&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,
+#endif
NULL,
+};
+/* sysfs groups */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, }; @@ -124,8 +520,14 @@ static const struct attribute_group coresight_cti_mgmt_group = { .name = "mgmt", };
+static const struct attribute_group coresight_cti_regs_group = {
.attrs = coresight_cti_regs_attrs,
.name = "regs",
+};
- const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_group, &coresight_cti_mgmt_group,
};&coresight_cti_regs_group, NULL,
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 4b3a5a91c4ff..cd804b29df07 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -158,6 +158,25 @@ static int cti_disable_hw(struct cti_drvdata *drvdata, bool update_refcount) return 0; }
+void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) +{
CS_UNLOCK(drvdata->base);
Don't we need to check if we have claimed the device before wrting something here ? Or at least make sure that the caller has claimed it ?
This is always called from a context we know the device is enabled and powered. Claim tag regs are checked on enable, and enable will fail if external debugger has the device.
writel_relaxed(value, drvdata->base+offset);
nit: coding style.
CS_LOCK(drvdata->base);
+}
+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);
+}
- static void cti_set_default_config(struct device *dev, struct cti_drvdata *drvdata) {
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index bc4c9bdf745e..215b4a4ac45d 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -205,7 +205,13 @@ 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_intack(struct device *dev, u32 ackval); +void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value);
- struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev);
+/* cti powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
Please could we be more explicit with the name, say cti_available() ?
'available' is less explicit & coding style requires CAPS for macros. There are some cases - e.g. the management registers - where we need to read before the device is enabled - which just use the powered test.
Cheers Suzuki
Thanks for the feedback.
Mike
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
Adds a user API to allow programming of CTI by trigger ID and channel number. This will take the channel and trigger ID supplied by the user and program the appropriate register values.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../hwtracing/coresight/coresight-cti-sysfs.c | 371 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.c | 159 +++++++- drivers/hwtracing/coresight/coresight-cti.h | 34 ++ 3 files changed, 563 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index caecf011f6e0..e1eacd2b0f6e 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -510,6 +510,371 @@ static struct attribute *coresight_cti_regs_attrs[] = { 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; + config->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, 10, &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, +}; + /* sysfs groups */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, @@ -525,9 +890,15 @@ static const struct attribute_group coresight_cti_regs_group = { .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 cd804b29df07..817b6f70ee23 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -42,7 +42,7 @@ static DEFINE_MUTEX(ect_mutex); DEFINE_CORESIGHT_DEVLIST(cti_sys_devs, "cti_sys");
/* 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; @@ -314,6 +314,163 @@ 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; + + /* 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; +} + /** cti ect operations **/ int cti_enable(struct coresight_device *csdev, void *__unused) { diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index 215b4a4ac45d..33516bf378a8 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -200,13 +200,47 @@ 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_intack(struct device *dev, u32 ackval); 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);
struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev);
Hi Mike,
On Wed, Jun 05, 2019 at 08:03:01PM +0100, Mike Leach wrote:
Adds a user API to allow programming of CTI by trigger ID and channel number. This will take the channel and trigger ID supplied by the user and program the appropriate register values.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 371 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.c | 159 +++++++- drivers/hwtracing/coresight/coresight-cti.h | 34 ++ 3 files changed, 563 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index caecf011f6e0..e1eacd2b0f6e 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -510,6 +510,371 @@ static struct attribute *coresight_cti_regs_attrs[] = { 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;
In patch 1/16 I was wondering whey config.trig_filter_enable was an 'int' and I still do.
- 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;
GENMASK()
- config->asicctl = 0;
- config->ctiappset = 0;
- config->ctiinout_sel = 0;
- config->xtrig_rchan_sel = 0;
- /* if enabled then write through */
- if (CTI_PWR_ENA(config))
cti_write_all_hw_regs(drvdata);
I am debating whether it is worth checking the claim tags before accessing the HW. CTI_PWR_ENA() is already checking ->hw_enabled, which can only happen if the claim tags have been acquired. The only thing it would do is interfere with an external agent that would have connected after we've claimed the tags, which would lead to all sort of failures anyway. The same applies to functions cti_channel_gate_op(), cti_channel_trig_op() and cti_channel_setop().
- 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;
xtrig_rchan_sel is not documented in struct cti_config.
- 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, 10, &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,
+};
/* sysfs groups */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, @@ -525,9 +890,15 @@ static const struct attribute_group coresight_cti_regs_group = { .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 cd804b29df07..817b6f70ee23 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -42,7 +42,7 @@ static DEFINE_MUTEX(ect_mutex); DEFINE_CORESIGHT_DEVLIST(cti_sys_devs, "cti_sys"); /* 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; @@ -314,6 +314,163 @@ 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)
Please rework the above parameter list to if as many in 80 characters. I know it doesn't make a different it will be flagged on the public mailing list.
+{
- 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;
- /* 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)
Here too.
+{
- 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)
And here.
+{
- 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;
+}
/** cti ect operations **/ int cti_enable(struct coresight_device *csdev, void *__unused) { diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index 215b4a4ac45d..33516bf378a8 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -200,13 +200,47 @@ 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_intack(struct device *dev, u32 ackval); 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);
struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev); -- 2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Hi,
On Wed, 12 Jun 2019 at 20:53, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Hi Mike,
On Wed, Jun 05, 2019 at 08:03:01PM +0100, Mike Leach wrote:
Adds a user API to allow programming of CTI by trigger ID and channel number. This will take the channel and trigger ID supplied by the user and program the appropriate register values.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 371 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.c | 159 +++++++- drivers/hwtracing/coresight/coresight-cti.h | 34 ++ 3 files changed, 563 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index caecf011f6e0..e1eacd2b0f6e 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -510,6 +510,371 @@ static struct attribute *coresight_cti_regs_attrs[] = { 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;
In patch 1/16 I was wondering whey config.trig_filter_enable was an 'int' and I still do.
switched to bool
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;
GENMASK()
config->asicctl = 0;
config->ctiappset = 0;
config->ctiinout_sel = 0;
config->xtrig_rchan_sel = 0;
/* if enabled then write through */
if (CTI_PWR_ENA(config))
cti_write_all_hw_regs(drvdata);
I am debating whether it is worth checking the claim tags before accessing the HW. CTI_PWR_ENA() is already checking ->hw_enabled, which can only happen if the claim tags have been acquired. The only thing it would do is interfere with an external agent that would have connected after we've claimed the tags, which would lead to all sort of failures anyway. The same applies to functions cti_channel_gate_op(), cti_channel_trig_op() and cti_channel_setop().
You are correct - enable is gated by claim tags so this is safe without checking them. External agents should respect the tags if we have them.
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;
xtrig_rchan_sel is not documented in struct cti_config.
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, 10, &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,
+};
/* sysfs groups */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, @@ -525,9 +890,15 @@ static const struct attribute_group coresight_cti_regs_group = { .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 cd804b29df07..817b6f70ee23 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -42,7 +42,7 @@ static DEFINE_MUTEX(ect_mutex); DEFINE_CORESIGHT_DEVLIST(cti_sys_devs, "cti_sys");
/* 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; @@ -314,6 +314,163 @@ 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)
Please rework the above parameter list to if as many in 80 characters. I know it doesn't make a different it will be flagged on the public mailing list.
OK
Thanks
Mike
+{
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;
/* 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)
Here too.
+{
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)
And here.
+{
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;
+}
/** cti ect operations **/ int cti_enable(struct coresight_device *csdev, void *__unused) { diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index 215b4a4ac45d..33516bf378a8 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -200,13 +200,47 @@ 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_intack(struct device *dev, u32 ackval); 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);
struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev); -- 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
Hi Mike,
On 05/06/2019 20:03, Mike Leach wrote:
Adds a user API to allow programming of CTI by trigger ID and channel number. This will take the channel and trigger ID supplied by the user and program the appropriate register values.
This patch looks fine to me. Some minor comments/suggestsions below.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 371 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.c | 159 +++++++- drivers/hwtracing/coresight/coresight-cti.h | 34 ++ 3 files changed, 563 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index caecf011f6e0..e1eacd2b0f6e 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -510,6 +510,371 @@ static struct attribute *coresight_cti_regs_attrs[] = { NULL,
..
+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);
Should we be strict about specifying "all" precisely ? i.e, the above code now matches "all*", isn't it ? Given you have made sure the (size >= 3), we could use strcmp() instead of strncmp() to match it precisely.
- } 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);
same comment as above.
- } else {
if (kstrtoint(buf, 0, &channel))
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, channel);
- }
- return err ? err : size;
+}
+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");
minor nit: Since we only store 1 or 0, for filter_enable, may be you could skip the integer part.
+}
+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;
minor nit: You may also use : drvdata->config.trig_filter_enable = !!val;
- spin_unlock(&drvdata->spinlock);
- return size;
+}
+/* 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, 10, &val))
return -EINVAL;
- if (val > (drvdata->config.nr_ctm_channels-1))
nit : 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);
+/* 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: ");
nit : You may reuse bitmap_print_to_pagebuf() for printing bitmask to their indices list.
- 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);
nit: missing space.
}
- }
- used += scnprintf(buf+used, buf_sz-used, "OUT: ");
nit: Do we need a new line before 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);
...
- }
- 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;
GENMASK ?
- 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) {
Please reuse bitmap_to_pagebuf().
+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);
}
}
As above, use bitmap_to_pagebuf(), or open code scnprintf() using %pb[l] modifier.
used += scnprintf(buf+used, b_sz-used, "\n");
- }
- return used;
+} +static DEVICE_ATTR_RO(list_gate_ena);
nit: Please use clear/obvious names. does it stand for "enabled" ?
+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, };
+/** 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;
- /* ensure indexes in range */
- if ((channel_idx >= config->nr_ctm_channels) ||
(trigger_idx >= config->nr_trig_max))
return -EINVAL;
- trig_bitmask = 0x1 << trigger_idx;
Use BIT(trigger_idx) instead everywhere ?
- /* 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;
BIT(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]);
nit: you may drop the parantheses ^^
- reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask :
reg_value & (~chan_bitmask);
I think this statement would be better readable with an if..else. i.e if (op == CTI_CHAN_ATTACH) reg_value |= chan_bitmask; else reg_valu &= ~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;
nit: As above, if you are only tracking a single idx in a mask, you may use BIT(idx) everywhere to make it more obvious.
- 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;
GENMASK()
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)
+{
- chan_bitmask = 0x1 << channel_idx;
BIT()
- return err;
+}
Suzuki
HI Suzuki,
On Mon, 17 Jun 2019 at 12:26, Suzuki K Poulose suzuki.poulose@arm.com wrote:
Hi Mike,
On 05/06/2019 20:03, Mike Leach wrote:
Adds a user API to allow programming of CTI by trigger ID and channel number. This will take the channel and trigger ID supplied by the user and program the appropriate register values.
This patch looks fine to me. Some minor comments/suggestsions below.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 371 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.c | 159 +++++++- drivers/hwtracing/coresight/coresight-cti.h | 34 ++ 3 files changed, 563 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index caecf011f6e0..e1eacd2b0f6e 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -510,6 +510,371 @@ static struct attribute *coresight_cti_regs_attrs[] = { NULL,
..
+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);
Should we be strict about specifying "all" precisely ? i.e, the above code now matches "all*", isn't it ? Given you have made sure the (size >= 3), we could use strcmp() instead of strncmp() to match it precisely.
} 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);
same comment as above.
} else {
if (kstrtoint(buf, 0, &channel))
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, channel);
}
return err ? err : size;
+}
+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");
minor nit: Since we only store 1 or 0, for filter_enable, may be you could skip the integer part.
Agreed - this is now a bool
+}
+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;
minor nit: You may also use : drvdata->config.trig_filter_enable = !!val;
spin_unlock(&drvdata->spinlock);
return size;
+}
+/* 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, 10, &val))
return -EINVAL;
if (val > (drvdata->config.nr_ctm_channels-1))
nit : missing space.
not sure where - checkpatch.pl normally gets annoyed about these things and it didn't trigger.
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: ");
nit : You may reuse bitmap_print_to_pagebuf() for printing bitmask to their indices list.
No, this will not work. we are comparing the chan_mask bit with multiple enable register bitmaps and printing per match.
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);
nit: missing space.
}
}
used += scnprintf(buf+used, buf_sz-used, "OUT: ");
nit: Do we need a new line before OUT ?
No - previous patches were rejected for multi-line sysfs output./
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);
...
}
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;
GENMASK ?
OK
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) {
Please reuse bitmap_to_pagebuf().
+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);
}
}
As above, use bitmap_to_pagebuf(), or open code scnprintf() using %pb[l] modifier.
used += scnprintf(buf+used, b_sz-used, "\n");
}
return used;
+} +static DEVICE_ATTR_RO(list_gate_ena);
nit: Please use clear/obvious names. does it stand for "enabled" ?
OK
+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,
+/** 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;
/* ensure indexes in range */
if ((channel_idx >= config->nr_ctm_channels) ||
(trigger_idx >= config->nr_trig_max))
return -EINVAL;
trig_bitmask = 0x1 << trigger_idx;
Use BIT(trigger_idx) instead everywhere ?
/* 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;
BIT(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]);
nit: you may drop the parantheses ^^
reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask :
reg_value & (~chan_bitmask);
I think this statement would be better readable with an if..else. i.e if (op == CTI_CHAN_ATTACH) reg_value |= chan_bitmask; else reg_valu &= ~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;
nit: As above, if you are only tracking a single idx in a mask, you may use BIT(idx) everywhere to make it more obvious.
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;
GENMASK()
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)
+{
chan_bitmask = 0x1 << channel_idx;
BIT()
return err;
+}
Suzuki
BIT & GENMASK used as suggested
Thanks
Mike
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
Hi Mike,
On 24/07/2019 12:04, Mike Leach wrote:
HI Suzuki,
On Mon, 17 Jun 2019 at 12:26, Suzuki K Poulose suzuki.poulose@arm.com wrote:
Hi Mike,
On 05/06/2019 20:03, Mike Leach wrote:
Adds a user API to allow programming of CTI by trigger ID and channel number. This will take the channel and trigger ID supplied by the user and program the appropriate register values.
This patch looks fine to me. Some minor comments/suggestsions below.
+/* 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, 10, &val))
return -EINVAL;
if (val > (drvdata->config.nr_ctm_channels-1))
nit : missing space.
not sure where - checkpatch.pl normally gets annoyed about these things and it didn't trigger.
space before and after arithmetic operators, in this case '-'.
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);
nit: missing space.
here: "buf + used"
Cheers Suzuki
Adds new .txt file describing the bindings required to define CTI in the device trees.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../bindings/arm/coresight-ect-cti.txt | 203 ++++++++++++++++++ .../devicetree/bindings/arm/coresight.txt | 7 + MAINTAINERS | 1 + 3 files changed, 211 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/coresight-ect-cti.txt
diff --git a/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt b/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt new file mode 100644 index 000000000000..ba95c0002ccc --- /dev/null +++ b/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt @@ -0,0 +1,203 @@ +*CoreSight Embedded Cross Trigger Components + +The CoreSight Embedded Cross Trigger (ECT) consists of CTI devices connected +to one or more CoreSight components and/or a CPU, with CTIs interconnected in +a star topology via the CTM (which is not programmable). The ECT components +are not part of the trace generation data path and are thus not part of the +CoreSight graph described above. + +The CTI component properties define the connections between the individual CTI +and the components it is directly connected to, consisting of input and output +hardware trigger signals. CTIs can have a maximum number of input and output +hardware trigger signals (8 each for v1 CTI, 32 each for v2 CTI). The number is +defined at design time, the maximum of each defined in the DEVID register. +Note that some hardware trigger signals can be connected to non-CoreSight +components (e.g. UART etc) depending on hardware implementation. + +CTIs are interconnect in a star topology via the CTM, using a number of +programmable channels usually 4, but again implementation defined and described +in the DEVID register. The star topology is not required to be described in the +bindings as the actual connections are software programmable. + +In general the connections between CTI and components via the trigger signals +are implementation defined, other than when v8 core and ETM is present. The v8 +architecture then defines the required signal connections between core and CTI, +and ETM and CTI, if the ETM if present. + +Certain triggers between CoreSight devices and the CTI have specific types / +usages. These can be defined along with the signal indexes with the constants +defined in <dt-bindings/arm/coresight-cti-dt.h> + +For example a CTI connected to a core will usually have a DBGREQ signal. This +is defined in the binding as type PE_EDBGREQ. These types will appear in an +optional array alongside the signal indexes. Omitting types will default all +signals to GEN_IO. + +The minimum required binding for a CTI consist of the only the required +properties above. + +e.g. + + /* sys cti 0 - connects to STM, ETF0 (core trace) TPIU and ETR */ + cti0: cti@20020000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x20020000 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + }; + + +This will result in the driver using the DEVID register to set the +input and output triggers and channels in use. Any user / client +application will require additional information on the connections +between the CTI and other components for correct operation. + +Where information is immediately available for the component connections then +a series of trigger connection nodes can be defined (trig-conns). + +These connections explicitly define the input and output triggers between the +CTI and a connected component. Connections to an cpu use the standard cpu +property, connections to other CoreSight components use the arm,cs-dev-assoc +property. + +Where the signals are connected to a device that is not coresight device then +no association is registered. + +cti@858000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x858000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + + trig-conns@0 { + arm,trig-in-sigs = <4 5 6 7>; + arm,trig-in-types = <ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT>; + arm,trig-out-sigs = <4 5 6 7>; + arm,trig-out-types = <ETM_EXTIN ETM_EXTIN ETM_EXTIN ETM_EXTIN>; + arm,cs-dev-assoc = <&etm0>; + }; + + trig-conns@1 { + cpu = <&CPU0>; + arm,trig-in-sigs=<0 1>; + arm,trig-in-types=<PE_DBGTRIGGER PE_PMUIRQ>; + arm,trig-out-sigs=<0 1 2 >; + arm,trig-out-types=<PE_EDBGREQ PE_DBGRESTART PE_CTIIRQ>; + arm,trig-filters=<0>; + }; + +}; + +Note that where input and output triggers are defined as above, then the driver +will limit the channel connection sysfs API to using only the defined signals. + +The arm,trig-filters property blocks output signals that could cause system +issues is set - such as the dbgreq signal into a CPU. The filtering is +enabled by default, but can be disabled at runtime. + +Finally, a CTI that is an architecturally defined v8 CTI connected to a cpu +and optional ETM may be declared as: + +cti@859000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x859000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + + arm,cti-v8-arch; + cpu = <&CPU1>; + arm,cs-dev-assoc = <&etm1>; +}; + +The arm,cti-v8-arch property declares this as a v8 CTI, the cpu property must +be present, and a single arm,cs-dev-assoc may be present to define an attached +ETM. No additional trig-conns nodes are permitted. The driver will build a +connection model according to architectural requirements. This will include +a filter on the CPU debreq signal as described above. + +All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by + a CTM. On these systems a CTM index is declared to associate CTI devices that + are interconnected via the CTM. + + e.g. + arm,ctm-ctm-id=<2> + +CTI devices with the same CTM ID are considered connected by the CTM. If this +parameter is absent then all CTIs are considered interconnected by the same +CTM. + +*Summary of CTI required properties: + + * compatible : should be "arm,coresight-cti"; supplemented with + "arm,primecell" since this driver is using the AMBA bus + interface. + + * reg: physical base address and length of the register + set(s) of the component. + + * clocks: the clocks associated to this component. + + * clock-names: the name of the clocks referenced by the code. + Since we are using the AMBA framework, the name of the clock + providing the interconnect should be "apb_pclk", and some + coresight blocks also have an additional clock "atclk", which + clocks the core of that coresight component. The latter clock + is optional. + +*Summary of CTI optional properties: + + * trig-conns : defines a connection node between CTI and a component. + Component may be a CPU, CoreSight device, any other hardware device + or simple external IO lines. + + *arm,trig-in-sigs: List of CTI trigger in signals in use by a + trig-conns node. Only valid as property of trig-conns node. + + *arm,trig-in-types: List of types for the CTI trigger in signals. + Types in this array match to the corresponding signal in the + arm,trig-in-sigs array. If the -types array is smaller, or omitted + completely, then the types will default to GEN_IO. + + *arm,trig-out-sigs: List of CTI trigger out signals in use by a + trig-conns node. Only valid as property of trig-conns node. + + *arm,trig-out-types: List of types for the CTI trigger out signals. + Types in this array match to the corresponding signal in the + arm,trig-out-sigs array. If the -types array is smaller, or omitted + completely, then the types will default to GEN_IO. + + *arm-trig-filters: List of CTI trigger out signals that will be + blocked from becoming active, unless filtering is disabled on the + driver. Only valid as property of trig-conns node. + + *arm,cti-v8-arch: Declares this CTI device as a v8 architecturally + defined device. Use in CTI base node only, no additional trig-conns + nodes permitted if this is declared. + + *cpu : defines a phandle reference to an associated CPU. Use in + trig-conns node, or in CTI base node when arm,cti-v8-arch present. + + *arm,cs-dev-assoc: defines a phandle reference to an associated + CoreSight trace device. When the associated trace device is enabled, + then the respective CTI will be enabled. Use in a trig-conns node, + or in CTI base node when arm,cti-v8-arch present. If the associated + device has not been registered then the node name will be stored as + the connection name for later resolution. If the associated device is + not a CoreSight device or not registered then the node name will + remain the connection name and automatic enabling will not occur. + + *arm,trig-conn-name: defines a connection name that will be displayed, + if not overridden by the name of associated device from + arm,cs-dev-assoc or the CPU. Only valid as property of trig-conns node. + Principle use for CTI that are connected to non-coresight devices, + or external IO. + + *arm,ctm-ctm-id: Defines the interconnecting CTM for this device. + Use in CTI base node. diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index 8a88ddebc1a2..c00f2aae188a 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -45,6 +45,10 @@ its hardware characteristcs. - Coresight Address Translation Unit (CATU) "arm,coresight-catu", "arm,primecell";
+ - Coresight Cross Trigger Interface (CTI): + "arm,coresight-cti", "arm,primecell"; + See coresight-ect-cti.txt for full CTI definitions. + * reg: physical base address and length of the register set(s) of the component.
@@ -67,6 +71,9 @@ its hardware characteristcs. * reg-names: the only acceptable values are "stm-base" and "stm-stimulus-base", each corresponding to the areas defined in "reg".
+* Required properties for Coresight Cross Trigger Interface (CTI) + See coresight-ect-cti.txt for full CTI definitions. + * Required properties for devices that don't show up on the AMBA bus, such as non-configurable replicators and non-configurable funnels:
diff --git a/MAINTAINERS b/MAINTAINERS index 5cfbea4ce575..4e5811b59e53 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1545,6 +1545,7 @@ F: Documentation/trace/coresight.txt F: Documentation/trace/coresight-cpu-debug.txt F: Documentation/devicetree/bindings/arm/coresight.txt F: Documentation/devicetree/bindings/arm/coresight-cpu-debug.txt +F: Documentation/devicetree/bindings/arm/coresight-ect-cti.txt F: Documentation/ABI/testing/sysfs-bus-coresight-devices-* F: tools/perf/arch/arm/util/pmu.c F: tools/perf/arch/arm/util/auxtrace.c
On Wed, Jun 05, 2019 at 08:03:02PM +0100, Mike Leach wrote:
Adds new .txt file describing the bindings required to define CTI in the device trees.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../bindings/arm/coresight-ect-cti.txt | 203 ++++++++++++++++++
Yes, we need a new file.
.../devicetree/bindings/arm/coresight.txt | 7 + MAINTAINERS | 1 + 3 files changed, 211 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/coresight-ect-cti.txt
diff --git a/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt b/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt new file mode 100644 index 000000000000..ba95c0002ccc --- /dev/null +++ b/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt @@ -0,0 +1,203 @@ +*CoreSight Embedded Cross Trigger Components
+The CoreSight Embedded Cross Trigger (ECT) consists of CTI devices connected +to one or more CoreSight components and/or a CPU, with CTIs interconnected in +a star topology via the CTM (which is not programmable). The ECT components +are not part of the trace generation data path and are thus not part of the +CoreSight graph described above.
What "CoreSight graph described above" are you referring to? Could that be from an earlier version where CTI bindings were part of coresight.txt?
+The CTI component properties define the connections between the individual CTI +and the components it is directly connected to, consisting of input and output +hardware trigger signals. CTIs can have a maximum number of input and output +hardware trigger signals (8 each for v1 CTI, 32 each for v2 CTI). The number is +defined at design time, the maximum of each defined in the DEVID register. +Note that some hardware trigger signals can be connected to non-CoreSight +components (e.g. UART etc) depending on hardware implementation.
+CTIs are interconnect in a star topology via the CTM, using a number of +programmable channels usually 4, but again implementation defined and described +in the DEVID register. The star topology is not required to be described in the +bindings as the actual connections are software programmable.
+In general the connections between CTI and components via the trigger signals +are implementation defined, other than when v8 core and ETM is present. The v8 +architecture then defines the required signal connections between core and CTI, +and ETM and CTI, if the ETM if present.
+Certain triggers between CoreSight devices and the CTI have specific types / +usages. These can be defined along with the signal indexes with the constants +defined in <dt-bindings/arm/coresight-cti-dt.h>
+For example a CTI connected to a core will usually have a DBGREQ signal. This +is defined in the binding as type PE_EDBGREQ. These types will appear in an +optional array alongside the signal indexes. Omitting types will default all +signals to GEN_IO.
+The minimum required binding for a CTI consist of the only the required
s/"of the only the"/"of the only"
+properties above.
Not sure about the word "above" - I can't find any properties above.
+e.g.
- /* sys cti 0 - connects to STM, ETF0 (core trace) TPIU and ETR */
- cti0: cti@20020000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x20020000 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
One line too many.
- };
+This will result in the driver using the DEVID register to set the +input and output triggers and channels in use. Any user / client +application will require additional information on the connections +between the CTI and other components for correct operation.
+Where information is immediately available for the component connections then +a series of trigger connection nodes can be defined (trig-conns).
+These connections explicitly define the input and output triggers between the +CTI and a connected component. Connections to an cpu use the standard cpu +property, connections to other CoreSight components use the arm,cs-dev-assoc +property.
+Where the signals are connected to a device that is not coresight device then
s/coresight/CoreSight
+no association is registered.
+cti@858000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x858000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
trig-conns@0 {
arm,trig-in-sigs = <4 5 6 7>;
arm,trig-in-types = <ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT>;
arm,trig-out-sigs = <4 5 6 7>;
arm,trig-out-types = <ETM_EXTIN ETM_EXTIN ETM_EXTIN ETM_EXTIN>;
arm,cs-dev-assoc = <&etm0>;
};
trig-conns@1 {
cpu = <&CPU0>;
arm,trig-in-sigs=<0 1>;
arm,trig-in-types=<PE_DBGTRIGGER PE_PMUIRQ>;
arm,trig-out-sigs=<0 1 2 >;
arm,trig-out-types=<PE_EDBGREQ PE_DBGRESTART PE_CTIIRQ>;
arm,trig-filters=<0>;
};
+};
+Note that where input and output triggers are defined as above, then the driver +will limit the channel connection sysfs API to using only the defined signals.
+The arm,trig-filters property blocks output signals that could cause system +issues is set - such as the dbgreq signal into a CPU. The filtering is
s/is/if
+enabled by default, but can be disabled at runtime.
+Finally, a CTI that is an architecturally defined v8 CTI connected to a cpu +and optional ETM may be declared as:
+cti@859000 {
- compatible = "arm,coresight-cti", "arm,primecell";
- reg = <0x859000 0x1000>;
- clocks = <&rpmcc RPM_QDSS_CLK>;
- clock-names = "apb_pclk";
- arm,cti-v8-arch;
- cpu = <&CPU1>;
- arm,cs-dev-assoc = <&etm1>;
+};
+The arm,cti-v8-arch property declares this as a v8 CTI, the cpu property must +be present, and a single arm,cs-dev-assoc may be present to define an attached +ETM. No additional trig-conns nodes are permitted. The driver will build a +connection model according to architectural requirements. This will include +a filter on the CPU debreq signal as described above.
s/debreq/dbgreq
+All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by
- a CTM. On these systems a CTM index is declared to associate CTI devices that
- are interconnected via the CTM.
The above two lines have one space too many at the beginning.
- e.g.
- arm,ctm-ctm-id=<2>
+CTI devices with the same CTM ID are considered connected by the CTM. If this +parameter is absent then all CTIs are considered interconnected by the same +CTM.
+*Summary of CTI required properties:
- compatible : should be "arm,coresight-cti"; supplemented with
"arm,primecell" since this driver is using the AMBA bus
interface.
- reg: physical base address and length of the register
set(s) of the component.
- clocks: the clocks associated to this component.
- clock-names: the name of the clocks referenced by the code.
Since we are using the AMBA framework, the name of the clock
providing the interconnect should be "apb_pclk", and some
coresight blocks also have an additional clock "atclk", which
s/coresight/CoreSight
clocks the core of that coresight component. The latter clock
s/coresight/CoreSight
is optional.
+*Summary of CTI optional properties:
* trig-conns : defines a connection node between CTI and a component.
Component may be a CPU, CoreSight device, any other hardware device
or simple external IO lines.
*arm,trig-in-sigs: List of CTI trigger in signals in use by a
trig-conns node. Only valid as property of trig-conns node.
*arm,trig-in-types: List of types for the CTI trigger in signals.
Types in this array match to the corresponding signal in the
arm,trig-in-sigs array. If the -types array is smaller, or omitted
completely, then the types will default to GEN_IO.
*arm,trig-out-sigs: List of CTI trigger out signals in use by a
trig-conns node. Only valid as property of trig-conns node.
*arm,trig-out-types: List of types for the CTI trigger out signals.
Types in this array match to the corresponding signal in the
arm,trig-out-sigs array. If the -types array is smaller, or omitted
completely, then the types will default to GEN_IO.
*arm-trig-filters: List of CTI trigger out signals that will be
s/arm-trig-filters/arm,trig-filters
blocked from becoming active, unless filtering is disabled on the
driver. Only valid as property of trig-conns node.
*arm,cti-v8-arch: Declares this CTI device as a v8 architecturally
defined device. Use in CTI base node only, no additional trig-conns
nodes permitted if this is declared.
*cpu : defines a phandle reference to an associated CPU. Use in
s/"cpu :"/"cpu:" s/Use/Used
trig-conns node, or in CTI base node when arm,cti-v8-arch present.
*arm,cs-dev-assoc: defines a phandle reference to an associated
CoreSight trace device. When the associated trace device is enabled,
then the respective CTI will be enabled. Use in a trig-conns node,
or in CTI base node when arm,cti-v8-arch present. If the associated
device has not been registered then the node name will be stored as
the connection name for later resolution. If the associated device is
not a CoreSight device or not registered then the node name will
remain the connection name and automatic enabling will not occur.
*arm,trig-conn-name: defines a connection name that will be displayed,
if not overridden by the name of associated device from
arm,cs-dev-assoc or the CPU. Only valid as property of trig-conns node.
Principle use for CTI that are connected to non-coresight devices,
s/coresight/CoreSight
or external IO.
*arm,ctm-ctm-id: Defines the interconnecting CTM for this device.
Use in CTI base node.
diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index 8a88ddebc1a2..c00f2aae188a 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -45,6 +45,10 @@ its hardware characteristcs. - Coresight Address Translation Unit (CATU) "arm,coresight-catu", "arm,primecell";
- Coresight Cross Trigger Interface (CTI):
"arm,coresight-cti", "arm,primecell";
See coresight-ect-cti.txt for full CTI definitions.
- reg: physical base address and length of the register set(s) of the component.
@@ -67,6 +71,9 @@ its hardware characteristcs.
- reg-names: the only acceptable values are "stm-base" and "stm-stimulus-base", each corresponding to the areas defined in "reg".
+* Required properties for Coresight Cross Trigger Interface (CTI)
See coresight-ect-cti.txt for full CTI definitions.
- Required properties for devices that don't show up on the AMBA bus, such as non-configurable replicators and non-configurable funnels:
diff --git a/MAINTAINERS b/MAINTAINERS index 5cfbea4ce575..4e5811b59e53 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1545,6 +1545,7 @@ F: Documentation/trace/coresight.txt F: Documentation/trace/coresight-cpu-debug.txt F: Documentation/devicetree/bindings/arm/coresight.txt F: Documentation/devicetree/bindings/arm/coresight-cpu-debug.txt +F: Documentation/devicetree/bindings/arm/coresight-ect-cti.txt F: Documentation/ABI/testing/sysfs-bus-coresight-devices-* F: tools/perf/arch/arm/util/pmu.c F: tools/perf/arch/arm/util/auxtrace.c -- 2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
On Wed, Jun 05, 2019 at 08:03:02PM +0100, Mike Leach wrote:
Adds new .txt file describing the bindings required to define CTI in the device trees.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../bindings/arm/coresight-ect-cti.txt | 203 ++++++++++++++++++ .../devicetree/bindings/arm/coresight.txt | 7 + MAINTAINERS | 1 + 3 files changed, 211 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/coresight-ect-cti.txt
diff --git a/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt b/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt new file mode 100644 index 000000000000..ba95c0002ccc --- /dev/null +++ b/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt @@ -0,0 +1,203 @@ +*CoreSight Embedded Cross Trigger Components
+The CoreSight Embedded Cross Trigger (ECT) consists of CTI devices connected +to one or more CoreSight components and/or a CPU, with CTIs interconnected in +a star topology via the CTM (which is not programmable). The ECT components +are not part of the trace generation data path and are thus not part of the +CoreSight graph described above.
+The CTI component properties define the connections between the individual CTI +and the components it is directly connected to, consisting of input and output +hardware trigger signals. CTIs can have a maximum number of input and output +hardware trigger signals (8 each for v1 CTI, 32 each for v2 CTI). The number is +defined at design time, the maximum of each defined in the DEVID register. +Note that some hardware trigger signals can be connected to non-CoreSight +components (e.g. UART etc) depending on hardware implementation.
+CTIs are interconnect in a star topology via the CTM, using a number of +programmable channels usually 4, but again implementation defined and described +in the DEVID register. The star topology is not required to be described in the +bindings as the actual connections are software programmable.
+In general the connections between CTI and components via the trigger signals +are implementation defined, other than when v8 core and ETM is present. The v8 +architecture then defines the required signal connections between core and CTI, +and ETM and CTI, if the ETM if present.
+Certain triggers between CoreSight devices and the CTI have specific types / +usages. These can be defined along with the signal indexes with the constants +defined in <dt-bindings/arm/coresight-cti-dt.h>
+For example a CTI connected to a core will usually have a DBGREQ signal. This +is defined in the binding as type PE_EDBGREQ. These types will appear in an +optional array alongside the signal indexes. Omitting types will default all +signals to GEN_IO.
+The minimum required binding for a CTI consist of the only the required +properties above.
+e.g.
- /* sys cti 0 - connects to STM, ETF0 (core trace) TPIU and ETR */
- cti0: cti@20020000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x20020000 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
- };
+This will result in the driver using the DEVID register to set the +input and output triggers and channels in use. Any user / client +application will require additional information on the connections +between the CTI and other components for correct operation.
+Where information is immediately available for the component connections then +a series of trigger connection nodes can be defined (trig-conns).
+These connections explicitly define the input and output triggers between the +CTI and a connected component. Connections to an cpu use the standard cpu +property, connections to other CoreSight components use the arm,cs-dev-assoc +property.
+Where the signals are connected to a device that is not coresight device then +no association is registered.
+cti@858000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x858000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
trig-conns@0 {
arm,trig-in-sigs = <4 5 6 7>;
arm,trig-in-types = <ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT>;
arm,trig-out-sigs = <4 5 6 7>;
arm,trig-out-types = <ETM_EXTIN ETM_EXTIN ETM_EXTIN ETM_EXTIN>;
arm,cs-dev-assoc = <&etm0>;
};
trig-conns@1 {
cpu = <&CPU0>;
arm,trig-in-sigs=<0 1>;
arm,trig-in-types=<PE_DBGTRIGGER PE_PMUIRQ>;
arm,trig-out-sigs=<0 1 2 >;
arm,trig-out-types=<PE_EDBGREQ PE_DBGRESTART PE_CTIIRQ>;
arm,trig-filters=<0>;
};
+};
+Note that where input and output triggers are defined as above, then the driver +will limit the channel connection sysfs API to using only the defined signals.
+The arm,trig-filters property blocks output signals that could cause system +issues is set - such as the dbgreq signal into a CPU. The filtering is +enabled by default, but can be disabled at runtime.
+Finally, a CTI that is an architecturally defined v8 CTI connected to a cpu +and optional ETM may be declared as:
+cti@859000 {
- compatible = "arm,coresight-cti", "arm,primecell";
- reg = <0x859000 0x1000>;
- clocks = <&rpmcc RPM_QDSS_CLK>;
- clock-names = "apb_pclk";
- arm,cti-v8-arch;
- cpu = <&CPU1>;
- arm,cs-dev-assoc = <&etm1>;
+};
+The arm,cti-v8-arch property declares this as a v8 CTI, the cpu property must +be present, and a single arm,cs-dev-assoc may be present to define an attached +ETM. No additional trig-conns nodes are permitted. The driver will build a +connection model according to architectural requirements. This will include +a filter on the CPU debreq signal as described above.
+All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by
- a CTM. On these systems a CTM index is declared to associate CTI devices that
- are interconnected via the CTM.
- e.g.
- arm,ctm-ctm-id=<2>
I'm pretty sure it should be "arm,cti-ctm-id" here.
+CTI devices with the same CTM ID are considered connected by the CTM. If this +parameter is absent then all CTIs are considered interconnected by the same +CTM.
+*Summary of CTI required properties:
- compatible : should be "arm,coresight-cti"; supplemented with
"arm,primecell" since this driver is using the AMBA bus
interface.
- reg: physical base address and length of the register
set(s) of the component.
- clocks: the clocks associated to this component.
- clock-names: the name of the clocks referenced by the code.
Since we are using the AMBA framework, the name of the clock
providing the interconnect should be "apb_pclk", and some
coresight blocks also have an additional clock "atclk", which
clocks the core of that coresight component. The latter clock
is optional.
+*Summary of CTI optional properties:
* trig-conns : defines a connection node between CTI and a component.
Component may be a CPU, CoreSight device, any other hardware device
or simple external IO lines.
*arm,trig-in-sigs: List of CTI trigger in signals in use by a
trig-conns node. Only valid as property of trig-conns node.
*arm,trig-in-types: List of types for the CTI trigger in signals.
Types in this array match to the corresponding signal in the
arm,trig-in-sigs array. If the -types array is smaller, or omitted
completely, then the types will default to GEN_IO.
*arm,trig-out-sigs: List of CTI trigger out signals in use by a
trig-conns node. Only valid as property of trig-conns node.
*arm,trig-out-types: List of types for the CTI trigger out signals.
Types in this array match to the corresponding signal in the
arm,trig-out-sigs array. If the -types array is smaller, or omitted
completely, then the types will default to GEN_IO.
*arm-trig-filters: List of CTI trigger out signals that will be
blocked from becoming active, unless filtering is disabled on the
driver. Only valid as property of trig-conns node.
*arm,cti-v8-arch: Declares this CTI device as a v8 architecturally
defined device. Use in CTI base node only, no additional trig-conns
nodes permitted if this is declared.
*cpu : defines a phandle reference to an associated CPU. Use in
trig-conns node, or in CTI base node when arm,cti-v8-arch present.
*arm,cs-dev-assoc: defines a phandle reference to an associated
CoreSight trace device. When the associated trace device is enabled,
then the respective CTI will be enabled. Use in a trig-conns node,
or in CTI base node when arm,cti-v8-arch present. If the associated
device has not been registered then the node name will be stored as
the connection name for later resolution. If the associated device is
not a CoreSight device or not registered then the node name will
remain the connection name and automatic enabling will not occur.
*arm,trig-conn-name: defines a connection name that will be displayed,
if not overridden by the name of associated device from
arm,cs-dev-assoc or the CPU. Only valid as property of trig-conns node.
Principle use for CTI that are connected to non-coresight devices,
or external IO.
*arm,ctm-ctm-id: Defines the interconnecting CTM for this device.
Here too.
Use in CTI base node.
diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index 8a88ddebc1a2..c00f2aae188a 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -45,6 +45,10 @@ its hardware characteristcs. - Coresight Address Translation Unit (CATU) "arm,coresight-catu", "arm,primecell";
- Coresight Cross Trigger Interface (CTI):
"arm,coresight-cti", "arm,primecell";
See coresight-ect-cti.txt for full CTI definitions.
- reg: physical base address and length of the register set(s) of the component.
@@ -67,6 +71,9 @@ its hardware characteristcs.
- reg-names: the only acceptable values are "stm-base" and "stm-stimulus-base", each corresponding to the areas defined in "reg".
+* Required properties for Coresight Cross Trigger Interface (CTI)
See coresight-ect-cti.txt for full CTI definitions.
- Required properties for devices that don't show up on the AMBA bus, such as non-configurable replicators and non-configurable funnels:
diff --git a/MAINTAINERS b/MAINTAINERS index 5cfbea4ce575..4e5811b59e53 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1545,6 +1545,7 @@ F: Documentation/trace/coresight.txt F: Documentation/trace/coresight-cpu-debug.txt F: Documentation/devicetree/bindings/arm/coresight.txt F: Documentation/devicetree/bindings/arm/coresight-cpu-debug.txt +F: Documentation/devicetree/bindings/arm/coresight-ect-cti.txt F: Documentation/ABI/testing/sysfs-bus-coresight-devices-* F: tools/perf/arch/arm/util/pmu.c F: tools/perf/arch/arm/util/auxtrace.c -- 2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Hi Mike
On 05/06/2019 20:03, Mike Leach wrote:
Adds new .txt file describing the bindings required to define CTI in the device trees.
This looks god to me. Minor comments inline.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../bindings/arm/coresight-ect-cti.txt | 203 ++++++++++++++++++ .../devicetree/bindings/arm/coresight.txt | 7 + MAINTAINERS | 1 + 3 files changed, 211 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/coresight-ect-cti.txt
diff --git a/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt b/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt new file mode 100644 index 000000000000..ba95c0002ccc --- /dev/null +++ b/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt @@ -0,0 +1,203 @@ +*CoreSight Embedded Cross Trigger Components
+The CoreSight Embedded Cross Trigger (ECT) consists of CTI devices connected +to one or more CoreSight components and/or a CPU, with CTIs interconnected in
s/ a CPU,/ non-CoreSight devices (e.g, CPU, UART)/ ?
+a star topology via the CTM (which is not programmable). The ECT components +are not part of the trace generation data path and are thus not part of the
s/trace generation data path/trace path/
+CoreSight graph described above.
"above" - Left over from previous version ?
+The CTI component properties define the connections between the individual CTI +and the components it is directly connected to, consisting of input and output +hardware trigger signals. CTIs can have a maximum number of input and output +hardware trigger signals (8 each for v1 CTI, 32 each for v2 CTI). The number is +defined at design time, the maximum of each defined in the DEVID register. +Note that some hardware trigger signals can be connected to non-CoreSight +components (e.g. UART etc) depending on hardware implementation.
+CTIs are interconnect in a star topology via the CTM, using a number of
s/interconnect/interconnected/ ?
+programmable channels usually 4, but again implementation defined and described +in the DEVID register. The star topology is not required to be described in the +bindings as the actual connections are software programmable.
+In general the connections between CTI and components via the trigger signals +are implementation defined, other than when v8 core and ETM is present. The v8 +architecture then defines the required signal connections between core and CTI,
s/architecture then/architecture/ , s/core/CPU core/
+and ETM and CTI, if the ETM if present.
+Certain triggers between CoreSight devices and the CTI have specific types / +usages. These can be defined along with the signal indexes with the constants +defined in <dt-bindings/arm/coresight-cti-dt.h>
+For example a CTI connected to a core will usually have a DBGREQ signal. This +is defined in the binding as type PE_EDBGREQ. These types will appear in an +optional array alongside the signal indexes. Omitting types will default all +signals to GEN_IO.
It may be a good idea to expand what GEN_IO is. e.g, GEN_IO (generic i/o).
+The minimum required binding for a CTI consist of the only the required +properties above.
This is a bit vague statement, unless you list the "properties" in separate subsection.
+e.g.
- /* sys cti 0 - connects to STM, ETF0 (core trace) TPIU and ETR */
- cti0: cti@20020000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x20020000 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
- };
And here you don't list anything other than the base address. So the above statement leaves the reader confused.
+This will result in the driver using the DEVID register to set the +input and output triggers and channels in use. Any user / client +application will require additional information on the connections +between the CTI and other components for correct operation.
+Where information is immediately available for the component connections then +a series of trigger connection nodes can be defined (trig-conns).
+These connections explicitly define the input and output triggers between the +CTI and a connected component. Connections to an cpu use the standard cpu +property,
"Connections to a CPU uses the coresight standard "cpu" property, " ?
connections to other CoreSight components use the arm,cs-dev-assoc +property.
How do we specify another device (say, UART) ? nit: I prefer a bit more clear name. "arm,coresight-connected-device" or "arm,coresight-assoicated-device"
But if this has been discussed over and agreed upon, I am fine.
+Where the signals are connected to a device that is not coresight device then +no association is registered.
+cti@858000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x858000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
trig-conns@0 {
arm,trig-in-sigs = <4 5 6 7>;
arm,trig-in-types = <ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT>;
nit: arm,trig-in-types = <ETM_EXTOUT, ETM_EXTOUT, ETM_EXTOUT, ETM_EXTOUT>
arm,trig-out-sigs = <4 5 6 7>;
arm,trig-out-types = <ETM_EXTIN ETM_EXTIN ETM_EXTIN ETM_EXTIN>;
same as above.
arm,cs-dev-assoc = <&etm0>;
};
trig-conns@1 {
cpu = <&CPU0>;
arm,trig-in-sigs=<0 1>;
arm,trig-in-types=<PE_DBGTRIGGER PE_PMUIRQ>;
arm,trig-out-sigs=<0 1 2 >;
nit: spurious space at the end.
arm,trig-out-types=<PE_EDBGREQ PE_DBGRESTART PE_CTIIRQ>;
nit: Please could we split into multiple lines.
arm,trig-filters=<0>;
};
+};
+Note that where input and output triggers are defined as above, then the driver +will limit the channel connection sysfs API to using only the defined signals.
+The arm,trig-filters property blocks output signals that could cause system +issues is set - such as the dbgreq signal into a CPU. The filtering is +enabled by default, but can be disabled at runtime.
+Finally, a CTI that is an architecturally defined v8 CTI connected to a cpu +and optional ETM may be declared as:
+cti@859000 {
- compatible = "arm,coresight-cti", "arm,primecell";
- reg = <0x859000 0x1000>;
- clocks = <&rpmcc RPM_QDSS_CLK>;
- clock-names = "apb_pclk";
- arm,cti-v8-arch;
- cpu = <&CPU1>;
- arm,cs-dev-assoc = <&etm1>;
+};
+The arm,cti-v8-arch property declares this as a v8 CTI, the cpu property must
nit: The "arm,cti-v8-arch" property ?
+be present, and a single arm,cs-dev-assoc may be present to define an attached +ETM. No additional trig-conns nodes are permitted. The driver will build a +connection model according to architectural requirements. This will include +a filter on the CPU debreq signal as described above.
s/debreq/PE_EDBGREQ ?
+All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by
- a CTM. On these systems a CTM index is declared to associate CTI devices that
- are interconnected via the CTM.
- e.g.
- arm,ctm-ctm-id=<2>
+CTI devices with the same CTM ID are considered connected by the CTM. If this +parameter is absent then all CTIs are considered interconnected by the same +CTM.
+*Summary of CTI required properties:
- compatible : should be "arm,coresight-cti"; supplemented with
"arm,primecell" since this driver is using the AMBA bus
interface.
- reg: physical base address and length of the register
set(s) of the component.
nit: * reg: physical base address and the size of the memory mapped region ?
- clocks: the clocks associated to this component.
- clock-names: the name of the clocks referenced by the code.
Since we are using the AMBA framework, the name of the clock
providing the interconnect should be "apb_pclk", and some
coresight blocks also have an additional clock "atclk", which
clocks the core of that coresight component. The latter clock
is optional.
+*Summary of CTI optional properties:
* trig-conns : defines a connection node between CTI and a component.
Component may be a CPU, CoreSight device, any other hardware device
or simple external IO lines.
--- CUT Here ---
*arm,trig-in-sigs: List of CTI trigger in signals in use by a
trig-conns node. Only valid as property of trig-conns node.
*arm,trig-in-types: List of types for the CTI trigger in signals.
Types in this array match to the corresponding signal in the
arm,trig-in-sigs array. If the -types array is smaller, or omitted
completely, then the types will default to GEN_IO.
*arm,trig-out-sigs: List of CTI trigger out signals in use by a
trig-conns node. Only valid as property of trig-conns node.
*arm,trig-out-types: List of types for the CTI trigger out signals.
Types in this array match to the corresponding signal in the
arm,trig-out-sigs array. If the -types array is smaller, or omitted
completely, then the types will default to GEN_IO.
*arm-trig-filters: List of CTI trigger out signals that will be
blocked from becoming active, unless filtering is disabled on the
driver. Only valid as property of trig-conns node.
--- End --- Should we intend the above section by an additional TAB to indicate, they belong to the trig-conns node explicitly (visible at one shot) ?
*arm,cti-v8-arch: Declares this CTI device as a v8 architecturally
defined device. Use in CTI base node only, no additional trig-conns
nodes permitted if this is declared.
*cpu : defines a phandle reference to an associated CPU. Use in
trig-conns node, or in CTI base node when arm,cti-v8-arch present.
*arm,cs-dev-assoc: defines a phandle reference to an associated
CoreSight trace device. When the associated trace device is enabled,
then the respective CTI will be enabled. Use in a trig-conns node,
or in CTI base node when arm,cti-v8-arch present. If the associated
device has not been registered then the node name will be stored as
the connection name for later resolution. If the associated device is
not a CoreSight device or not registered then the node name will
remain the connection name and automatic enabling will not occur.
*arm,trig-conn-name: defines a connection name that will be displayed,
if not overridden by the name of associated device from
arm,cs-dev-assoc or the CPU. Only valid as property of trig-conns node.
Principle use for CTI that are connected to non-coresight devices,
or external IO.
Please could you provide an example ? Also, even though you discuss small sections of example above, it would be a good idea to have an example for each type below at the end, for a quick reference for someone who is coming back to refer.
*arm,ctm-ctm-id: Defines the interconnecting CTM for this device.
Use in CTI base node.
Suzuki
Hi Mathieu, Suzuki.
Updated per your suggestions.
On Mon, 17 Jun 2019 at 14:17, Suzuki K Poulose suzuki.poulose@arm.com wrote:
Hi Mike
On 05/06/2019 20:03, Mike Leach wrote:
Adds new .txt file describing the bindings required to define CTI in the device trees.
This looks god to me. Minor comments inline.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../bindings/arm/coresight-ect-cti.txt | 203 ++++++++++++++++++ .../devicetree/bindings/arm/coresight.txt | 7 + MAINTAINERS | 1 + 3 files changed, 211 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/coresight-ect-cti.txt
diff --git a/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt b/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt new file mode 100644 index 000000000000..ba95c0002ccc --- /dev/null +++ b/Documentation/devicetree/bindings/arm/coresight-ect-cti.txt @@ -0,0 +1,203 @@ +*CoreSight Embedded Cross Trigger Components
+The CoreSight Embedded Cross Trigger (ECT) consists of CTI devices connected +to one or more CoreSight components and/or a CPU, with CTIs interconnected in
s/ a CPU,/ non-CoreSight devices (e.g, CPU, UART)/ ?
+a star topology via the CTM (which is not programmable). The ECT components +are not part of the trace generation data path and are thus not part of the
s/trace generation data path/trace path/
+CoreSight graph described above.
"above" - Left over from previous version ?
Indeed and re-written accordingly.
+The CTI component properties define the connections between the individual CTI +and the components it is directly connected to, consisting of input and output +hardware trigger signals. CTIs can have a maximum number of input and output +hardware trigger signals (8 each for v1 CTI, 32 each for v2 CTI). The number is +defined at design time, the maximum of each defined in the DEVID register. +Note that some hardware trigger signals can be connected to non-CoreSight +components (e.g. UART etc) depending on hardware implementation.
+CTIs are interconnect in a star topology via the CTM, using a number of
s/interconnect/interconnected/ ?
+programmable channels usually 4, but again implementation defined and described +in the DEVID register. The star topology is not required to be described in the +bindings as the actual connections are software programmable.
+In general the connections between CTI and components via the trigger signals +are implementation defined, other than when v8 core and ETM is present. The v8 +architecture then defines the required signal connections between core and CTI,
s/architecture then/architecture/ , s/core/CPU core/
+and ETM and CTI, if the ETM if present.
+Certain triggers between CoreSight devices and the CTI have specific types / +usages. These can be defined along with the signal indexes with the constants +defined in <dt-bindings/arm/coresight-cti-dt.h>
+For example a CTI connected to a core will usually have a DBGREQ signal. This +is defined in the binding as type PE_EDBGREQ. These types will appear in an +optional array alongside the signal indexes. Omitting types will default all +signals to GEN_IO.
It may be a good idea to expand what GEN_IO is. e.g, GEN_IO (generic i/o).
+The minimum required binding for a CTI consist of the only the required +properties above.
This is a bit vague statement, unless you list the "properties" in separate subsection.
+e.g.
/* sys cti 0 - connects to STM, ETF0 (core trace) TPIU and ETR */
cti0: cti@20020000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x20020000 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
};
And here you don't list anything other than the base address. So the above statement leaves the reader confused.
+This will result in the driver using the DEVID register to set the +input and output triggers and channels in use. Any user / client +application will require additional information on the connections +between the CTI and other components for correct operation.
+Where information is immediately available for the component connections then +a series of trigger connection nodes can be defined (trig-conns).
+These connections explicitly define the input and output triggers between the +CTI and a connected component. Connections to an cpu use the standard cpu +property,
"Connections to a CPU uses the coresight standard "cpu" property, " ?
connections to other CoreSight components use the arm,cs-dev-assoc +property.
How do we specify another device (say, UART) ? nit: I prefer a bit more clear name. "arm,coresight-connected-device" or "arm,coresight-assoicated-device"
But if this has been discussed over and agreed upon, I am fine.
+Where the signals are connected to a device that is not coresight device then +no association is registered.
+cti@858000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x858000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
trig-conns@0 {
arm,trig-in-sigs = <4 5 6 7>;
arm,trig-in-types = <ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT ETM_EXTOUT>;
nit: arm,trig-in-types = <ETM_EXTOUT, ETM_EXTOUT, ETM_EXTOUT, ETM_EXTOUT>
arm,trig-out-sigs = <4 5 6 7>;
arm,trig-out-types = <ETM_EXTIN ETM_EXTIN ETM_EXTIN ETM_EXTIN>;
same as above.
arm,cs-dev-assoc = <&etm0>;
};
trig-conns@1 {
cpu = <&CPU0>;
arm,trig-in-sigs=<0 1>;
arm,trig-in-types=<PE_DBGTRIGGER PE_PMUIRQ>;
arm,trig-out-sigs=<0 1 2 >;
nit: spurious space at the end.
arm,trig-out-types=<PE_EDBGREQ PE_DBGRESTART PE_CTIIRQ>;
nit: Please could we split into multiple lines.
arm,trig-filters=<0>;
};
+};
+Note that where input and output triggers are defined as above, then the driver +will limit the channel connection sysfs API to using only the defined signals.
+The arm,trig-filters property blocks output signals that could cause system +issues is set - such as the dbgreq signal into a CPU. The filtering is +enabled by default, but can be disabled at runtime.
+Finally, a CTI that is an architecturally defined v8 CTI connected to a cpu +and optional ETM may be declared as:
+cti@859000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x859000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
arm,cti-v8-arch;
cpu = <&CPU1>;
arm,cs-dev-assoc = <&etm1>;
+};
+The arm,cti-v8-arch property declares this as a v8 CTI, the cpu property must
nit: The "arm,cti-v8-arch" property ?
+be present, and a single arm,cs-dev-assoc may be present to define an attached +ETM. No additional trig-conns nodes are permitted. The driver will build a +connection model according to architectural requirements. This will include +a filter on the CPU debreq signal as described above.
s/debreq/PE_EDBGREQ ?
+All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by
- a CTM. On these systems a CTM index is declared to associate CTI devices that
- are interconnected via the CTM.
- e.g.
arm,ctm-ctm-id=<2>
+CTI devices with the same CTM ID are considered connected by the CTM. If this +parameter is absent then all CTIs are considered interconnected by the same +CTM.
+*Summary of CTI required properties:
* compatible : should be "arm,coresight-cti"; supplemented with
"arm,primecell" since this driver is using the AMBA bus
interface.
* reg: physical base address and length of the register
set(s) of the component.
nit: * reg: physical base address and the size of the memory mapped region ?
This is the same description as coresight.txt - it is a memory mapped register set so I am happy this is accurate.
* clocks: the clocks associated to this component.
* clock-names: the name of the clocks referenced by the code.
Since we are using the AMBA framework, the name of the clock
providing the interconnect should be "apb_pclk", and some
coresight blocks also have an additional clock "atclk", which
clocks the core of that coresight component. The latter clock
is optional.
+*Summary of CTI optional properties:
* trig-conns : defines a connection node between CTI and a component.
Component may be a CPU, CoreSight device, any other hardware device
or simple external IO lines.
--- CUT Here ---
*arm,trig-in-sigs: List of CTI trigger in signals in use by a
trig-conns node. Only valid as property of trig-conns node.
*arm,trig-in-types: List of types for the CTI trigger in signals.
Types in this array match to the corresponding signal in the
arm,trig-in-sigs array. If the -types array is smaller, or omitted
completely, then the types will default to GEN_IO.
*arm,trig-out-sigs: List of CTI trigger out signals in use by a
trig-conns node. Only valid as property of trig-conns node.
*arm,trig-out-types: List of types for the CTI trigger out signals.
Types in this array match to the corresponding signal in the
arm,trig-out-sigs array. If the -types array is smaller, or omitted
completely, then the types will default to GEN_IO.
*arm-trig-filters: List of CTI trigger out signals that will be
blocked from becoming active, unless filtering is disabled on the
driver. Only valid as property of trig-conns node.
--- End --- Should we intend the above section by an additional TAB to indicate, they belong to the trig-conns node explicitly (visible at one shot) ?
agreed and done.
Thanks
Mike
*arm,cti-v8-arch: Declares this CTI device as a v8 architecturally
defined device. Use in CTI base node only, no additional trig-conns
nodes permitted if this is declared.
*cpu : defines a phandle reference to an associated CPU. Use in
trig-conns node, or in CTI base node when arm,cti-v8-arch present.
*arm,cs-dev-assoc: defines a phandle reference to an associated
CoreSight trace device. When the associated trace device is enabled,
then the respective CTI will be enabled. Use in a trig-conns node,
or in CTI base node when arm,cti-v8-arch present. If the associated
device has not been registered then the node name will be stored as
the connection name for later resolution. If the associated device is
not a CoreSight device or not registered then the node name will
remain the connection name and automatic enabling will not occur.
*arm,trig-conn-name: defines a connection name that will be displayed,
if not overridden by the name of associated device from
arm,cs-dev-assoc or the CPU. Only valid as property of trig-conns node.
Principle use for CTI that are connected to non-coresight devices,
or external IO.
Please could you provide an example ? Also, even though you discuss small sections of example above, it would be a good idea to have an example for each type below at the end, for a quick reference for someone who is coming back to refer.
*arm,ctm-ctm-id: Defines the interconnecting CTM for this device.
Use in CTI base node.
Suzuki
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
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 --- .../coresight/coresight-cti-platform.c | 191 +++++++++++++++++- drivers/hwtracing/coresight/coresight-cti.c | 10 +- drivers/hwtracing/coresight/coresight-cti.h | 6 + 3 files changed, 200 insertions(+), 7 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index 068932ac9e90..2fb7d356e9c9 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -7,8 +7,38 @@
#include "coresight-cti.h"
-#ifdef CONFIG_OF +/* 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" + +/* + * Find a registered coresight device from a device fwnode. + * The node info is associated with the AMBA parent, but the + * csdev keeps a copy so iterate round the coresight bus to + * find the device. + */ +static struct coresight_device * +cti_get_assoc_csdev_by_fwnode(struct fwnode_handle *r_fwnode) +{ + struct device *dev; + struct coresight_device *csdev = NULL;
+ dev = bus_find_device(&coresight_bustype, NULL, r_fwnode, + coresight_device_fwnode_match); + if (dev) { + csdev = to_coresight_device(dev); + put_device(dev); + } + return csdev; +} + +#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 @@ -29,6 +59,157 @@ static int of_cti_get_cpu(const struct device_node *node) return (cpu < 0) ? -1 : cpu; }
+static int of_cti_create_v8_etm_connection(struct cti_drvdata *drvdata, + struct device_node *np) +{ + struct cti_trig_grp *etm_in = NULL, *etm_out = NULL; + int *etm_sig_in_types = NULL, *etm_sig_out_types = NULL; + int ret = 0, i; + struct device_node *cs_np; + const char *assoc_name = NULL; + struct coresight_device *csdev; + struct fwnode_handle *r_fwnode; + + /* Can optionally have an etm node - return if not */ + cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0); + if (!cs_np) + return 0; + + /* allocate memory */ + ret = -ENOMEM; + etm_in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!etm_in) + goto of_create_v8_etm_out; + + etm_out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!etm_out) + goto of_create_v8_etm_out; + + etm_sig_in_types = + kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL); + if (!etm_sig_in_types) + goto of_create_v8_etm_out; + + etm_sig_out_types = + kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL); + if (!etm_sig_out_types) + goto of_create_v8_etm_out; + + /* build connection data */ + etm_in->nr_sigs = NR_V8ETM_INOUT_SIGS; + etm_in->used_mask = 0xF0; /* sigs <4,5,6,7> */ + etm_in->sig_types = etm_sig_in_types; + etm_out->nr_sigs = NR_V8ETM_INOUT_SIGS; + etm_out->used_mask = 0xF0; /* sigs <4,5,6,7> */ + 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; + } + + r_fwnode = of_fwnode_handle(cs_np); + csdev = cti_get_assoc_csdev_by_fwnode(r_fwnode); + if (csdev) + assoc_name = dev_name(&csdev->dev); + else + assoc_name = cs_np->full_name; + ret = cti_add_connection_entry(drvdata, etm_in, etm_out, + csdev, assoc_name); + +of_create_v8_etm_out: + of_node_put(cs_np); + if (ret) { + kfree(etm_in); + kfree(etm_out); + kfree(etm_sig_in_types); + kfree(etm_sig_out_types); + } + return ret; +} + +/* + * Create an architecturally defined v8 connection + * must have a cpu, can have an ETM. + */ +static int of_cti_create_v8_connections(struct device *dev, + struct cti_drvdata *drvdata, + struct device_node *np) +{ + struct cti_device *cti_dev = &drvdata->ctidev; + struct cti_trig_grp *in = NULL, *out = NULL; + int cpuid = 0; + char cpu_name_str[16]; + int *sig_in_types = NULL, *sig_out_types = NULL; + int ret = -ENOMEM; + + /* Must have a cpu node */ + cpuid = of_cti_get_cpu(np); + if (cpuid < 0) { + dev_warn(dev, "CTI v8 DT binding no cpu\n"); + return -EINVAL; + } + cti_dev->cpu = cpuid; + + /* Allocate the v8 cpu memory */ + in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!in) + goto of_create_v8_out; + + out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!out) + goto of_create_v8_out; + + sig_in_types = kzalloc(sizeof(int) * NR_V8PE_IN_SIGS, GFP_KERNEL); + if (!sig_in_types) + goto of_create_v8_out; + + sig_out_types = kzalloc(sizeof(int) * NR_V8PE_OUT_SIGS, GFP_KERNEL); + if (!sig_out_types) + goto of_create_v8_out; + + /* 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); + + ret = cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str); + if (ret) + goto of_create_v8_out; + + /* Create the v8 ETM associated connection */ + ret = of_cti_create_v8_etm_connection(drvdata, np); + if (ret) { + /* + * Need to remove PE connection just created which + * frees all associated memory. + */ + cti_free_conn_info(drvdata); + return ret; + } + + /* filter pe_edbgreq - PE trigout sig <0> */ + drvdata->config.trig_out_filter |= 0x1; + +of_create_v8_out: + if (ret) { + kfree(in); + kfree(out); + kfree(sig_in_types); + kfree(sig_out_types); + } + return ret; +} + /* get the hardware configuration & connection data. */ int of_cti_get_hw_data(struct device *dev, struct device_node *np, @@ -37,6 +218,13 @@ int of_cti_get_hw_data(struct device *dev, int rc = 0; struct cti_device *cti_dev = &drvdata->ctidev;
+ /* check for a v8 architectural CTI device */ + if (of_property_read_bool(np, CTI_DT_V8ARCH)) { + rc = of_cti_create_v8_connections(dev, drvdata, np); + if (rc) + return rc; + } + /* 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); @@ -97,4 +285,3 @@ coresight_cti_get_platform_data(struct device *dev) error: return ERR_PTR(ret); } - diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 817b6f70ee23..a4e349984ab8 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -214,11 +214,11 @@ static void cti_set_default_config(struct device *dev, * 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 33516bf378a8..b87cc67543e0 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -229,6 +229,12 @@ enum cti_chan_set_op { /* private cti driver fns & vars */ 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); +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);
On Wed, Jun 05, 2019 at 08:03:03PM +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
.../coresight/coresight-cti-platform.c | 191 +++++++++++++++++- drivers/hwtracing/coresight/coresight-cti.c | 10 +- drivers/hwtracing/coresight/coresight-cti.h | 6 + 3 files changed, 200 insertions(+), 7 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index 068932ac9e90..2fb7d356e9c9 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -7,8 +7,38 @@ #include "coresight-cti.h" -#ifdef CONFIG_OF +/* 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"
+/*
- Find a registered coresight device from a device fwnode.
- The node info is associated with the AMBA parent, but the
- csdev keeps a copy so iterate round the coresight bus to
- find the device.
- */
+static struct coresight_device * +cti_get_assoc_csdev_by_fwnode(struct fwnode_handle *r_fwnode) +{
- struct device *dev;
- struct coresight_device *csdev = NULL;
- dev = bus_find_device(&coresight_bustype, NULL, r_fwnode,
coresight_device_fwnode_match);
Why not just use coresight_find_device_by_fwnode()?
- if (dev) {
csdev = to_coresight_device(dev);
put_device(dev);
- }
- return csdev;
+}
+#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
@@ -29,6 +59,157 @@ static int of_cti_get_cpu(const struct device_node *node) return (cpu < 0) ? -1 : cpu; } +static int of_cti_create_v8_etm_connection(struct cti_drvdata *drvdata,
struct device_node *np)
+{
- struct cti_trig_grp *etm_in = NULL, *etm_out = NULL;
- int *etm_sig_in_types = NULL, *etm_sig_out_types = NULL;
- int ret = 0, i;
- struct device_node *cs_np;
- const char *assoc_name = NULL;
- struct coresight_device *csdev;
- struct fwnode_handle *r_fwnode;
- /* Can optionally have an etm node - return if not */
- cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
- if (!cs_np)
return 0;
- /* allocate memory */
- ret = -ENOMEM;
- etm_in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!etm_in)
goto of_create_v8_etm_out;
- etm_out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!etm_out)
goto of_create_v8_etm_out;
- etm_sig_in_types =
kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL);
- if (!etm_sig_in_types)
goto of_create_v8_etm_out;
- etm_sig_out_types =
kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL);
- if (!etm_sig_out_types)
goto of_create_v8_etm_out;
- /* build connection data */
- etm_in->nr_sigs = NR_V8ETM_INOUT_SIGS;
- etm_in->used_mask = 0xF0; /* sigs <4,5,6,7> */
- etm_in->sig_types = etm_sig_in_types;
- etm_out->nr_sigs = NR_V8ETM_INOUT_SIGS;
- etm_out->used_mask = 0xF0; /* sigs <4,5,6,7> */
- 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;
- }
- r_fwnode = of_fwnode_handle(cs_np);
- csdev = cti_get_assoc_csdev_by_fwnode(r_fwnode);
- if (csdev)
assoc_name = dev_name(&csdev->dev);
- else
assoc_name = cs_np->full_name;
- ret = cti_add_connection_entry(drvdata, etm_in, etm_out,
csdev, assoc_name);
+of_create_v8_etm_out:
- of_node_put(cs_np);
- if (ret) {
kfree(etm_in);
kfree(etm_out);
kfree(etm_sig_in_types);
kfree(etm_sig_out_types);
- }
- return ret;
+}
+/*
- Create an architecturally defined v8 connection
- must have a cpu, can have an ETM.
- */
+static int of_cti_create_v8_connections(struct device *dev,
struct cti_drvdata *drvdata,
struct device_node *np)
+{
- struct cti_device *cti_dev = &drvdata->ctidev;
- struct cti_trig_grp *in = NULL, *out = NULL;
- int cpuid = 0;
- char cpu_name_str[16];
- int *sig_in_types = NULL, *sig_out_types = NULL;
- int ret = -ENOMEM;
- /* Must have a cpu node */
- cpuid = of_cti_get_cpu(np);
- if (cpuid < 0) {
dev_warn(dev, "CTI v8 DT binding no cpu\n");
return -EINVAL;
- }
- cti_dev->cpu = cpuid;
- /* Allocate the v8 cpu memory */
- in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!in)
goto of_create_v8_out;
- out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!out)
goto of_create_v8_out;
- sig_in_types = kzalloc(sizeof(int) * NR_V8PE_IN_SIGS, GFP_KERNEL);
- if (!sig_in_types)
goto of_create_v8_out;
- sig_out_types = kzalloc(sizeof(int) * NR_V8PE_OUT_SIGS, GFP_KERNEL);
- if (!sig_out_types)
goto of_create_v8_out;
Here 'dev' is available, if devm_kzalloc() was to be used, it would make error handling much easier. The same applies to function of_cti_create_v8_etm_connection() and cti_add_default_connection().
- /* 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);
- ret = cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);
- if (ret)
goto of_create_v8_out;
- /* Create the v8 ETM associated connection */
- ret = of_cti_create_v8_etm_connection(drvdata, np);
- if (ret) {
/*
* Need to remove PE connection just created which
* frees all associated memory.
*/
cti_free_conn_info(drvdata);
And if cti_add_connection_entry() was also using devm_kzalloc() and devm_kstrdup() you probably wouldn't need the above either, at least here.
return ret;
- }
- /* filter pe_edbgreq - PE trigout sig <0> */
- drvdata->config.trig_out_filter |= 0x1;
+of_create_v8_out:
- if (ret) {
kfree(in);
kfree(out);
kfree(sig_in_types);
kfree(sig_out_types);
- }
- return ret;
+}
/* get the hardware configuration & connection data. */ int of_cti_get_hw_data(struct device *dev, struct device_node *np, @@ -37,6 +218,13 @@ int of_cti_get_hw_data(struct device *dev, int rc = 0; struct cti_device *cti_dev = &drvdata->ctidev;
- /* check for a v8 architectural CTI device */
- if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
rc = of_cti_create_v8_connections(dev, drvdata, np);
if (rc)
return rc;
- }
- /* 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);
@@ -97,4 +285,3 @@ coresight_cti_get_platform_data(struct device *dev) error: return ERR_PTR(ret); }
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 817b6f70ee23..a4e349984ab8 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -214,11 +214,11 @@ static void cti_set_default_config(struct device *dev,
- 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)
Nobody will call you out for not making the function static in 1/16 and adding the prototype to coresight-cti.h. If this was spread out over several patchset then making it static would be the way to go. But in this case it is work you've introduced in a previous patch that is done again, which we want to avoid.
Just make the function public in 1/16 along with the prototype in the header file. The same applies to cti_free_conn_info(), if based on the above devm_xyx() comments, it is still required..
{ 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 33516bf378a8..b87cc67543e0 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -229,6 +229,12 @@ enum cti_chan_set_op { /* private cti driver fns & vars */ 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);
+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); -- 2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Hi Mathieu,
On Thu, 13 Jun 2019 at 21:34, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, Jun 05, 2019 at 08:03:03PM +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
.../coresight/coresight-cti-platform.c | 191 +++++++++++++++++- drivers/hwtracing/coresight/coresight-cti.c | 10 +- drivers/hwtracing/coresight/coresight-cti.h | 6 + 3 files changed, 200 insertions(+), 7 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index 068932ac9e90..2fb7d356e9c9 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -7,8 +7,38 @@
#include "coresight-cti.h"
-#ifdef CONFIG_OF +/* 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"
+/*
- Find a registered coresight device from a device fwnode.
- The node info is associated with the AMBA parent, but the
- csdev keeps a copy so iterate round the coresight bus to
- find the device.
- */
+static struct coresight_device * +cti_get_assoc_csdev_by_fwnode(struct fwnode_handle *r_fwnode) +{
struct device *dev;
struct coresight_device *csdev = NULL;
dev = bus_find_device(&coresight_bustype, NULL, r_fwnode,
coresight_device_fwnode_match);
Why not just use coresight_find_device_by_fwnode()?
Because that searches the AMBA bus, this function searches the CoreSight bus - so correctly locates coresight devices and therefore can get the related coresight_device structure
if (dev) {
csdev = to_coresight_device(dev);
put_device(dev);
}
return csdev;
+}
+#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
@@ -29,6 +59,157 @@ static int of_cti_get_cpu(const struct device_node *node) return (cpu < 0) ? -1 : cpu; }
+static int of_cti_create_v8_etm_connection(struct cti_drvdata *drvdata,
struct device_node *np)
+{
struct cti_trig_grp *etm_in = NULL, *etm_out = NULL;
int *etm_sig_in_types = NULL, *etm_sig_out_types = NULL;
int ret = 0, i;
struct device_node *cs_np;
const char *assoc_name = NULL;
struct coresight_device *csdev;
struct fwnode_handle *r_fwnode;
/* Can optionally have an etm node - return if not */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (!cs_np)
return 0;
/* allocate memory */
ret = -ENOMEM;
etm_in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!etm_in)
goto of_create_v8_etm_out;
etm_out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!etm_out)
goto of_create_v8_etm_out;
etm_sig_in_types =
kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL);
if (!etm_sig_in_types)
goto of_create_v8_etm_out;
etm_sig_out_types =
kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL);
if (!etm_sig_out_types)
goto of_create_v8_etm_out;
/* build connection data */
etm_in->nr_sigs = NR_V8ETM_INOUT_SIGS;
etm_in->used_mask = 0xF0; /* sigs <4,5,6,7> */
etm_in->sig_types = etm_sig_in_types;
etm_out->nr_sigs = NR_V8ETM_INOUT_SIGS;
etm_out->used_mask = 0xF0; /* sigs <4,5,6,7> */
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;
}
r_fwnode = of_fwnode_handle(cs_np);
csdev = cti_get_assoc_csdev_by_fwnode(r_fwnode);
if (csdev)
assoc_name = dev_name(&csdev->dev);
else
assoc_name = cs_np->full_name;
ret = cti_add_connection_entry(drvdata, etm_in, etm_out,
csdev, assoc_name);
+of_create_v8_etm_out:
of_node_put(cs_np);
if (ret) {
kfree(etm_in);
kfree(etm_out);
kfree(etm_sig_in_types);
kfree(etm_sig_out_types);
}
return ret;
+}
+/*
- Create an architecturally defined v8 connection
- must have a cpu, can have an ETM.
- */
+static int of_cti_create_v8_connections(struct device *dev,
struct cti_drvdata *drvdata,
struct device_node *np)
+{
struct cti_device *cti_dev = &drvdata->ctidev;
struct cti_trig_grp *in = NULL, *out = NULL;
int cpuid = 0;
char cpu_name_str[16];
int *sig_in_types = NULL, *sig_out_types = NULL;
int ret = -ENOMEM;
/* Must have a cpu node */
cpuid = of_cti_get_cpu(np);
if (cpuid < 0) {
dev_warn(dev, "CTI v8 DT binding no cpu\n");
return -EINVAL;
}
cti_dev->cpu = cpuid;
/* Allocate the v8 cpu memory */
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
goto of_create_v8_out;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
goto of_create_v8_out;
sig_in_types = kzalloc(sizeof(int) * NR_V8PE_IN_SIGS, GFP_KERNEL);
if (!sig_in_types)
goto of_create_v8_out;
sig_out_types = kzalloc(sizeof(int) * NR_V8PE_OUT_SIGS, GFP_KERNEL);
if (!sig_out_types)
goto of_create_v8_out;
Here 'dev' is available, if devm_kzalloc() was to be used, it would make error handling much easier. The same applies to function of_cti_create_v8_etm_connection() and cti_add_default_connection().
prev set moved this to common code to create the trig_conn struct so re-written
/* 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);
ret = cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);
if (ret)
goto of_create_v8_out;
/* Create the v8 ETM associated connection */
ret = of_cti_create_v8_etm_connection(drvdata, np);
if (ret) {
/*
* Need to remove PE connection just created which
* frees all associated memory.
*/
cti_free_conn_info(drvdata);
And if cti_add_connection_entry() was also using devm_kzalloc() and devm_kstrdup() you probably wouldn't need the above either, at least here.
mem access updated to use devm for these items
return ret;
}
/* filter pe_edbgreq - PE trigout sig <0> */
drvdata->config.trig_out_filter |= 0x1;
+of_create_v8_out:
if (ret) {
kfree(in);
kfree(out);
kfree(sig_in_types);
kfree(sig_out_types);
}
return ret;
+}
/* get the hardware configuration & connection data. */ int of_cti_get_hw_data(struct device *dev, struct device_node *np, @@ -37,6 +218,13 @@ int of_cti_get_hw_data(struct device *dev, int rc = 0; struct cti_device *cti_dev = &drvdata->ctidev;
/* check for a v8 architectural CTI device */
if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
rc = of_cti_create_v8_connections(dev, drvdata, np);
if (rc)
return rc;
}
/* 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);
@@ -97,4 +285,3 @@ coresight_cti_get_platform_data(struct device *dev) error: return ERR_PTR(ret); }
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 817b6f70ee23..a4e349984ab8 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -214,11 +214,11 @@ static void cti_set_default_config(struct device *dev,
- 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)
Nobody will call you out for not making the function static in 1/16 and adding the prototype to coresight-cti.h. If this was spread out over several patchset then making it static would be the way to go. But in this case it is work you've introduced in a previous patch that is done again, which we want to avoid.
Just make the function public in 1/16 along with the prototype in the header file. The same applies to cti_free_conn_info(), if based on the above devm_xyx() comments, it is still required..
done
{ 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 33516bf378a8..b87cc67543e0 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -229,6 +229,12 @@ enum cti_chan_set_op { /* private cti driver fns & vars */ 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);
+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); -- 2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Thanks
Mike
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
On 05/06/2019 20:03, 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
.../coresight/coresight-cti-platform.c | 191 +++++++++++++++++- drivers/hwtracing/coresight/coresight-cti.c | 10 +- drivers/hwtracing/coresight/coresight-cti.h | 6 + 3 files changed, 200 insertions(+), 7 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index 068932ac9e90..2fb7d356e9c9 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -7,8 +7,38 @@ #include "coresight-cti.h" -#ifdef CONFIG_OF +/* 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"
+/*
- Find a registered coresight device from a device fwnode.
- The node info is associated with the AMBA parent, but the
- csdev keeps a copy so iterate round the coresight bus to
- find the device.
- */
+static struct coresight_device * +cti_get_assoc_csdev_by_fwnode(struct fwnode_handle *r_fwnode) +{
- struct device *dev;
- struct coresight_device *csdev = NULL;
- dev = bus_find_device(&coresight_bustype, NULL, r_fwnode,
coresight_device_fwnode_match);
- if (dev) {
csdev = to_coresight_device(dev);
put_device(dev);
- }
- return csdev;
+}
+#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
@@ -29,6 +59,157 @@ static int of_cti_get_cpu(const struct device_node *node) return (cpu < 0) ? -1 : cpu; } +static int of_cti_create_v8_etm_connection(struct cti_drvdata *drvdata,
struct device_node *np)
+{
- struct cti_trig_grp *etm_in = NULL, *etm_out = NULL;
- int *etm_sig_in_types = NULL, *etm_sig_out_types = NULL;
- int ret = 0, i;
- struct device_node *cs_np;
- const char *assoc_name = NULL;
- struct coresight_device *csdev;
- struct fwnode_handle *r_fwnode;
- /* Can optionally have an etm node - return if not */
- cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
- if (!cs_np)
return 0;
- /* allocate memory */
- ret = -ENOMEM;
- etm_in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!etm_in)
goto of_create_v8_etm_out;
- etm_out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!etm_out)
goto of_create_v8_etm_out;
- etm_sig_in_types =
kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL);
- if (!etm_sig_in_types)
goto of_create_v8_etm_out;
- etm_sig_out_types =
kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL);
Could we not use devm_kzalloc() everywhere above ? ^
Also, I assume this would change if you re-shuffle the struct definitions.
- if (!etm_sig_out_types)
goto of_create_v8_etm_out;
- /* build connection data */
- etm_in->nr_sigs = NR_V8ETM_INOUT_SIGS;
- etm_in->used_mask = 0xF0; /* sigs <4,5,6,7> */
The used bits are 7-4, while the number of SIGS = 4. Does the nr_sigs need to be 8 and indicate that sigs 7-4 are only connected ?
- etm_in->sig_types = etm_sig_in_types;
- etm_out->nr_sigs = NR_V8ETM_INOUT_SIGS;
- etm_out->used_mask = 0xF0; /* sigs <4,5,6,7> */
- 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;
- }
- r_fwnode = of_fwnode_handle(cs_np);
- csdev = cti_get_assoc_csdev_by_fwnode(r_fwnode);
- if (csdev)
assoc_name = dev_name(&csdev->dev);
- else
assoc_name = cs_np->full_name;
- ret = cti_add_connection_entry(drvdata, etm_in, etm_out,
csdev, assoc_name);
+of_create_v8_etm_out:
- of_node_put(cs_np);
- if (ret) {
kfree(etm_in);
kfree(etm_out);
kfree(etm_sig_in_types);
kfree(etm_sig_out_types);
- }
If you use devm_kzalloc(), you could ignore freeing them out in the error path.
- return ret;
+}
+/*
- Create an architecturally defined v8 connection
- must have a cpu, can have an ETM.
- */
+static int of_cti_create_v8_connections(struct device *dev,
struct cti_drvdata *drvdata,
struct device_node *np)
+{
- struct cti_device *cti_dev = &drvdata->ctidev;
- struct cti_trig_grp *in = NULL, *out = NULL;
- int cpuid = 0;
- char cpu_name_str[16];
- int *sig_in_types = NULL, *sig_out_types = NULL;
- int ret = -ENOMEM;
- /* Must have a cpu node */
- cpuid = of_cti_get_cpu(np);
- if (cpuid < 0) {
dev_warn(dev, "CTI v8 DT binding no cpu\n");
return -EINVAL;
- }
- cti_dev->cpu = cpuid;
- /* Allocate the v8 cpu memory */
- in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!in)
goto of_create_v8_out;
- out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!out)
goto of_create_v8_out;
- sig_in_types = kzalloc(sizeof(int) * NR_V8PE_IN_SIGS, GFP_KERNEL);
- if (!sig_in_types)
goto of_create_v8_out;
- sig_out_types = kzalloc(sizeof(int) * NR_V8PE_OUT_SIGS, GFP_KERNEL);
- if (!sig_out_types)
goto of_create_v8_out;
- /* 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);
s/16/sizeof(cpu_name_str) ?
- ret = cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);
- if (ret)
goto of_create_v8_out;
- /* Create the v8 ETM associated connection */
- ret = of_cti_create_v8_etm_connection(drvdata, np);
- if (ret) {
/*
* Need to remove PE connection just created which
* frees all associated memory.
*/
cti_free_conn_info(drvdata);
return ret;
- }
- /* filter pe_edbgreq - PE trigout sig <0> */
- drvdata->config.trig_out_filter |= 0x1;
+of_create_v8_out:
- if (ret) {
kfree(in);
kfree(out);
kfree(sig_in_types);
kfree(sig_out_types);
- }
- return ret;
+}
- /* get the hardware configuration & connection data. */ int of_cti_get_hw_data(struct device *dev, struct device_node *np,
@@ -37,6 +218,13 @@ int of_cti_get_hw_data(struct device *dev, int rc = 0; struct cti_device *cti_dev = &drvdata->ctidev;
- /* check for a v8 architectural CTI device */
- if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
I would prefer to use fwnode_property_* variants of the property accessors, wherever possible, thinking about the "ACPI" support picture. Of course, I understand that, we have not discussed about the bindings for the connections and stuff. But if we reuse the DT property names, we are all set for it, rather than a rework and we don't loose much here.
rc = of_cti_create_v8_connections(dev, drvdata, np);
if (rc)
return rc;
- }
Suzuki
Hi Suzuki
On Mon, 17 Jun 2019 at 14:39, Suzuki K Poulose suzuki.poulose@arm.com wrote:
On 05/06/2019 20:03, 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
.../coresight/coresight-cti-platform.c | 191 +++++++++++++++++- drivers/hwtracing/coresight/coresight-cti.c | 10 +- drivers/hwtracing/coresight/coresight-cti.h | 6 + 3 files changed, 200 insertions(+), 7 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index 068932ac9e90..2fb7d356e9c9 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -7,8 +7,38 @@
#include "coresight-cti.h"
-#ifdef CONFIG_OF +/* 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"
+/*
- Find a registered coresight device from a device fwnode.
- The node info is associated with the AMBA parent, but the
- csdev keeps a copy so iterate round the coresight bus to
- find the device.
- */
+static struct coresight_device * +cti_get_assoc_csdev_by_fwnode(struct fwnode_handle *r_fwnode) +{
struct device *dev;
struct coresight_device *csdev = NULL;
dev = bus_find_device(&coresight_bustype, NULL, r_fwnode,
coresight_device_fwnode_match);
if (dev) {
csdev = to_coresight_device(dev);
put_device(dev);
}
return csdev;
+}
+#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
@@ -29,6 +59,157 @@ static int of_cti_get_cpu(const struct device_node *node) return (cpu < 0) ? -1 : cpu; }
+static int of_cti_create_v8_etm_connection(struct cti_drvdata *drvdata,
struct device_node *np)
+{
struct cti_trig_grp *etm_in = NULL, *etm_out = NULL;
int *etm_sig_in_types = NULL, *etm_sig_out_types = NULL;
int ret = 0, i;
struct device_node *cs_np;
const char *assoc_name = NULL;
struct coresight_device *csdev;
struct fwnode_handle *r_fwnode;
/* Can optionally have an etm node - return if not */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (!cs_np)
return 0;
/* allocate memory */
ret = -ENOMEM;
etm_in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!etm_in)
goto of_create_v8_etm_out;
etm_out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!etm_out)
goto of_create_v8_etm_out;
etm_sig_in_types =
kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL);
if (!etm_sig_in_types)
goto of_create_v8_etm_out;
etm_sig_out_types =
kzalloc(sizeof(int) * NR_V8ETM_INOUT_SIGS, GFP_KERNEL);
Could we not use devm_kzalloc() everywhere above ? ^
Also, I assume this would change if you re-shuffle the struct definitions.
re-structured in earlier patch to use devm and now calls a common create fn.
if (!etm_sig_out_types)
goto of_create_v8_etm_out;
/* build connection data */
etm_in->nr_sigs = NR_V8ETM_INOUT_SIGS;
etm_in->used_mask = 0xF0; /* sigs <4,5,6,7> */
The used bits are 7-4, while the number of SIGS = 4. Does the nr_sigs need to be 8 and indicate that sigs 7-4 are only connected ?
nr_sigs is for _this_ etm connection, the mask shows which they are, not the total number in the cti. Some of sigs 0-3 are connected to the CPU.
etm_in->sig_types = etm_sig_in_types;
etm_out->nr_sigs = NR_V8ETM_INOUT_SIGS;
etm_out->used_mask = 0xF0; /* sigs <4,5,6,7> */
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;
}
r_fwnode = of_fwnode_handle(cs_np);
csdev = cti_get_assoc_csdev_by_fwnode(r_fwnode);
if (csdev)
assoc_name = dev_name(&csdev->dev);
else
assoc_name = cs_np->full_name;
ret = cti_add_connection_entry(drvdata, etm_in, etm_out,
csdev, assoc_name);
+of_create_v8_etm_out:
of_node_put(cs_np);
if (ret) {
kfree(etm_in);
kfree(etm_out);
kfree(etm_sig_in_types);
kfree(etm_sig_out_types);
}
If you use devm_kzalloc(), you could ignore freeing them out in the error path.
return ret;
+}
+/*
- Create an architecturally defined v8 connection
- must have a cpu, can have an ETM.
- */
+static int of_cti_create_v8_connections(struct device *dev,
struct cti_drvdata *drvdata,
struct device_node *np)
+{
struct cti_device *cti_dev = &drvdata->ctidev;
struct cti_trig_grp *in = NULL, *out = NULL;
int cpuid = 0;
char cpu_name_str[16];
int *sig_in_types = NULL, *sig_out_types = NULL;
int ret = -ENOMEM;
/* Must have a cpu node */
cpuid = of_cti_get_cpu(np);
if (cpuid < 0) {
dev_warn(dev, "CTI v8 DT binding no cpu\n");
return -EINVAL;
}
cti_dev->cpu = cpuid;
/* Allocate the v8 cpu memory */
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
goto of_create_v8_out;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
goto of_create_v8_out;
sig_in_types = kzalloc(sizeof(int) * NR_V8PE_IN_SIGS, GFP_KERNEL);
if (!sig_in_types)
goto of_create_v8_out;
sig_out_types = kzalloc(sizeof(int) * NR_V8PE_OUT_SIGS, GFP_KERNEL);
if (!sig_out_types)
goto of_create_v8_out;
/* 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);
s/16/sizeof(cpu_name_str) ?
OK
ret = cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);
if (ret)
goto of_create_v8_out;
/* Create the v8 ETM associated connection */
ret = of_cti_create_v8_etm_connection(drvdata, np);
if (ret) {
/*
* Need to remove PE connection just created which
* frees all associated memory.
*/
cti_free_conn_info(drvdata);
return ret;
}
/* filter pe_edbgreq - PE trigout sig <0> */
drvdata->config.trig_out_filter |= 0x1;
+of_create_v8_out:
if (ret) {
kfree(in);
kfree(out);
kfree(sig_in_types);
kfree(sig_out_types);
}
return ret;
+}
- /* get the hardware configuration & connection data. */ int of_cti_get_hw_data(struct device *dev, struct device_node *np,
@@ -37,6 +218,13 @@ int of_cti_get_hw_data(struct device *dev, int rc = 0; struct cti_device *cti_dev = &drvdata->ctidev;
/* check for a v8 architectural CTI device */
if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
I would prefer to use fwnode_property_* variants of the property accessors, wherever possible, thinking about the "ACPI" support picture. Of course, I understand that, we have not discussed about the bindings for the connections and stuff. But if we reuse the DT property names, we are all set for it, rather than a rework and we don't loose much here.
The Of/ACPI split is curently in the coresight_cti_get_platform_data() fn, and the of fns here are generally passed in an of_node. Looking at the differences in the main coresight platform between OF/ACPI I wasn't confident that I could predict what structures were likely for ACPI, and at what point they would necessarily diverge, and common code becomes unsupportabel / maintainable. For now I prefer to write code for what is defined and re-work later once the complete problem is understood (by me at least!).
Thanks
Mike
rc = of_cti_create_v8_connections(dev, drvdata, np);
if (rc)
return rc;
}
Suzuki
-- 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 --- .../coresight/coresight-cti-platform.c | 202 +++++++++++++++++- 1 file changed, 197 insertions(+), 5 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index 2fb7d356e9c9..806941e1011e 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -12,10 +12,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"
/* * Find a registered coresight device from a device fwnode. @@ -210,6 +219,182 @@ static int of_cti_create_v8_connections(struct device *dev, return ret; }
+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 = kcalloc(nr_sigs, sizeof(int), 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 cti_drvdata *drvdata, + struct device_node *np) +{ + 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]; + struct fwnode_handle *r_fwnode; + + /* 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) { + r_fwnode = of_fwnode_handle(cs_np); + csdev = cti_get_assoc_csdev_by_fwnode(r_fwnode); + 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 cti_drvdata *drvdata, + struct device_node *np) +{ + 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, drvdata, nc); + 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, @@ -218,12 +403,19 @@ int of_cti_get_hw_data(struct device *dev, int rc = 0; struct cti_device *cti_dev = &drvdata->ctidev;
- /* check for a v8 architectural CTI device */ - if (of_property_read_bool(np, CTI_DT_V8ARCH)) { + /* 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, + * otherwise implementation defined. + */ + if (of_property_read_bool(np, CTI_DT_V8ARCH)) rc = of_cti_create_v8_connections(dev, drvdata, np); - if (rc) - return rc; - } + else + rc = of_cti_create_impdef_connections(dev, drvdata, np); + if (rc) + return rc;
/* if no connections, just add a single default based on max IN-OUT */ if (cti_dev->nr_trig_con == 0)
On Wed, Jun 05, 2019 at 08:03:04PM +0100, Mike Leach wrote:
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
.../coresight/coresight-cti-platform.c | 202 +++++++++++++++++- 1 file changed, 197 insertions(+), 5 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index 2fb7d356e9c9..806941e1011e 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -12,10 +12,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"
This is declared in the previous file but used here. Please move the declaration here.
+#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" /*
- Find a registered coresight device from a device fwnode.
@@ -210,6 +219,182 @@ static int of_cti_create_v8_connections(struct device *dev, return ret; } +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);
Once again I think we can use devm_kzalloc() to simplify the error path.
- if (!grp)
return -ENOMEM;
- *trig_grp_ptr = grp;
Memory allocation needs to be decoupled from reading of the signals. Otherwise of_cti_read_trig_group() _has_ to be called before of_cti_read_trig_types().
- 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;
Memory leak.
- if (items > max_trigs)
return -EINVAL;
Here too.
- /* 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 = kcalloc(nr_sigs, sizeof(int), 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);
Is there a use case for describing the signals but not their type? If not nr_sigs != items is an error.
- /*
* Match type id to signal index,
* too few - default to genio,
* too many - ignore excess.
*/
I think we should return an error in such cases.
- 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 cti_drvdata *drvdata,
struct device_node *np)
+{
- struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
NULL
- 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];
- struct fwnode_handle *r_fwnode;
- /* look for the signals properties. */
- err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max);
Function of_cti_read_trig_group() is dealing with signals, I would call it of_cti_read_trig_sig().
- 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) {
r_fwnode = of_fwnode_handle(cs_np);
csdev = cti_get_assoc_csdev_by_fwnode(r_fwnode);
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 cti_drvdata *drvdata,
struct device_node *np)
+{
- 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, drvdata, nc);
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 */
As much as I try, I don't understand the comment - please revise.
- 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, @@ -218,12 +403,19 @@ int of_cti_get_hw_data(struct device *dev, int rc = 0; struct cti_device *cti_dev = &drvdata->ctidev;
- /* check for a v8 architectural CTI device */
- if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
- /* 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,
* otherwise implementation defined.
*/
- if (of_property_read_bool(np, CTI_DT_V8ARCH)) rc = of_cti_create_v8_connections(dev, drvdata, np);
if (rc)
return rc;
- }
- else
rc = of_cti_create_impdef_connections(dev, drvdata, np);
- if (rc)
return rc;
/* if no connections, just add a single default based on max IN-OUT */ if (cti_dev->nr_trig_con == 0)
Now that I have a clearer picture of how things work, wouldn't it be simpler to mandate that all connections be specified in the DT? Even if we are to add default connections via cti_add_default_connection(), there is no telling the HW lines are there. As such we could simply return an error if rc == 0. Otherwise I'm missing a use case.
-- 2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Hi Mathieu,
On Thu, 13 Jun 2019 at 23:06, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, Jun 05, 2019 at 08:03:04PM +0100, Mike Leach wrote:
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
.../coresight/coresight-cti-platform.c | 202 +++++++++++++++++- 1 file changed, 197 insertions(+), 5 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index 2fb7d356e9c9..806941e1011e 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -12,10 +12,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"
This is declared in the previous file but used here. Please move the declaration here.
Done
+#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"
/*
- Find a registered coresight device from a device fwnode.
@@ -210,6 +219,182 @@ static int of_cti_create_v8_connections(struct device *dev, return ret; }
+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);
Once again I think we can use devm_kzalloc() to simplify the error path.
if (!grp)
return -ENOMEM;
*trig_grp_ptr = grp;
Memory allocation needs to be decoupled from reading of the signals. Otherwise of_cti_read_trig_group() _has_ to be called before of_cti_read_trig_types().
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;
Memory leak.
if (items > max_trigs)
return -EINVAL;
Here too.
Considerable re-write to use devm + some common allocation fns from previous patches. This should clear up issues above.
/* 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 = kcalloc(nr_sigs, sizeof(int), 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);
Is there a use case for describing the signals but not their type? If not nr_sigs != items is an error.
Yes - the types we are specifying relate to frequently used CoreSight component signals, with a couple of generic values (IO, INT, INTACK), CTI triggers can be connected to literally anything, so we define those that may be useful to coresight programming and have generic class for any others. Signals whose types are not described default to a generic IO. This means that in the DT we only need to allocate type to those we know are a specific type, allowing a cleaner DT.
/*
* Match type id to signal index,
* too few - default to genio,
* too many - ignore excess.
*/
I think we should return an error in such cases.
Do for too many, too few will still default to GEN_IO
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 cti_drvdata *drvdata,
struct device_node *np)
+{
struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
NULL
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];
struct fwnode_handle *r_fwnode;
/* look for the signals properties. */
err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max);
Function of_cti_read_trig_group() is dealing with signals, I would call it of_cti_read_trig_sig().
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) {
r_fwnode = of_fwnode_handle(cs_np);
csdev = cti_get_assoc_csdev_by_fwnode(r_fwnode);
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 cti_drvdata *drvdata,
struct device_node *np)
+{
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, drvdata, nc);
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 */
As much as I try, I don't understand the comment - please revise.
N/A now mem handling changed.
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, @@ -218,12 +403,19 @@ int of_cti_get_hw_data(struct device *dev, int rc = 0; struct cti_device *cti_dev = &drvdata->ctidev;
/* check for a v8 architectural CTI device */
if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
/* 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,
* otherwise implementation defined.
*/
if (of_property_read_bool(np, CTI_DT_V8ARCH)) rc = of_cti_create_v8_connections(dev, drvdata, np);
if (rc)
return rc;
}
else
rc = of_cti_create_impdef_connections(dev, drvdata, np);
if (rc)
return rc; /* if no connections, just add a single default based on max IN-OUT */ if (cti_dev->nr_trig_con == 0)
Now that I have a clearer picture of how things work, wouldn't it be simpler to mandate that all connections be specified in the DT? Even if we are to add default connections via cti_add_default_connection(), there is no telling the HW lines are there. As such we could simply return an error if rc == 0. Otherwise I'm missing a use case.
It is possible to compile in the intergration control registers and directly explore trigger signal connections. (or map the device memory into userspace and do the same if you do not want to re-compile the driver). Either way allowing a default CTI to be programmed after this process is beneficial as CTI connections tend to be the worst documented features.
-- 2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Thanks
Mike
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
Hi Mike,
On 05/06/2019 20:03, Mike Leach wrote:
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
.../coresight/coresight-cti-platform.c | 202 +++++++++++++++++- 1 file changed, 197 insertions(+), 5 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index 2fb7d356e9c9..806941e1011e 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -12,10 +12,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" /*
- Find a registered coresight device from a device fwnode.
@@ -210,6 +219,182 @@ static int of_cti_create_v8_connections(struct device *dev, return ret; } +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);
devm_ ?
- if (!grp)
return -ENOMEM;
- *trig_grp_ptr = grp;
nit: You may delay the assignment until we know that we are successful.
- 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);
Similar to the comment in the previous patch, please us fwnode_property_*
if (err)
return err;
grp->nr_sigs++;
grp->used_mask |= (0x1 << value);
BIT(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 = kcalloc(nr_sigs, sizeof(int), GFP_KERNEL);
devm_ ?
- 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 cti_drvdata *drvdata,
struct device_node *np)
+{
- struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
NULL ?
- 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];
nit: cpu_name ?
- struct fwnode_handle *r_fwnode;
- /* 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;
Could these not be clubbed into one ? i.e,
in = of_cti_read_trig_group(np, CTI_DT_TRIGIN_SIGS, trig_max); which could also invoke the read_trig_type() appropriately based on the type of the SIGS.
- 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 */
s/by//
- 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);
s/16/sizeof(cpu_name_str)
assoc_name = cpu_name_str;
- } else {
/* associated device ? */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
r_fwnode = of_fwnode_handle(cs_np);
csdev = cti_get_assoc_csdev_by_fwnode(r_fwnode);
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);
of_cti_free_tri_grp(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 cti_drvdata *drvdata,
struct device_node *np)
+{
- 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, drvdata, nc);
if (rc != 0)
nit: if (rc) ?
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,
@@ -218,12 +403,19 @@ int of_cti_get_hw_data(struct device *dev, int rc = 0; struct cti_device *cti_dev = &drvdata->ctidev;
- /* check for a v8 architectural CTI device */
- if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
- /* 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,
* otherwise implementation defined.
*/
- if (of_property_read_bool(np, CTI_DT_V8ARCH)) rc = of_cti_create_v8_connections(dev, drvdata, np);
if (rc)
return rc;
- }
- else
rc = of_cti_create_impdef_connections(dev, drvdata, np);
Do we report the error if someone specifies v8 connections *and* tirgger-connections manually ?
Cheers Suzuki
Hi Suzuki,
On Mon, 17 Jun 2019 at 17:26, Suzuki K Poulose suzuki.poulose@arm.com wrote:
Hi Mike,
On 05/06/2019 20:03, Mike Leach wrote:
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
.../coresight/coresight-cti-platform.c | 202 +++++++++++++++++- 1 file changed, 197 insertions(+), 5 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index 2fb7d356e9c9..806941e1011e 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -12,10 +12,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"
/*
- Find a registered coresight device from a device fwnode.
@@ -210,6 +219,182 @@ static int of_cti_create_v8_connections(struct device *dev, return ret; }
+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);
devm_ ?
if (!grp)
return -ENOMEM;
*trig_grp_ptr = grp;
nit: You may delay the assignment until we know that we are successful.
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);
Similar to the comment in the previous patch, please us fwnode_property_*
No fwnode equivalent to this - more importantly, there is no fwnode function that can count the number of elements in a property array - we allow the number of signals conected to be defined by the size of the array in the .dts file
if (err)
return err;
grp->nr_sigs++;
grp->used_mask |= (0x1 << value);
BIT(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 = kcalloc(nr_sigs, sizeof(int), GFP_KERNEL);
devm_ ?
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 cti_drvdata *drvdata,
struct device_node *np)
+{
struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
NULL ?
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];
nit: cpu_name ?
struct fwnode_handle *r_fwnode;
/* 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;
Could these not be clubbed into one ? i.e,
in = of_cti_read_trig_group(np, CTI_DT_TRIGIN_SIGS, trig_max);
which could also invoke the read_trig_type() appropriately based on the type of the SIGS.
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 */
s/by//
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);
s/16/sizeof(cpu_name_str)
assoc_name = cpu_name_str;
} else {
/* associated device ? */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
r_fwnode = of_fwnode_handle(cs_np);
csdev = cti_get_assoc_csdev_by_fwnode(r_fwnode);
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);
of_cti_free_tri_grp(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 cti_drvdata *drvdata,
struct device_node *np)
+{
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, drvdata, nc);
if (rc != 0)
nit: if (rc) ?
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,
@@ -218,12 +403,19 @@ int of_cti_get_hw_data(struct device *dev, int rc = 0; struct cti_device *cti_dev = &drvdata->ctidev;
/* check for a v8 architectural CTI device */
if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
/* 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,
* otherwise implementation defined.
*/
if (of_property_read_bool(np, CTI_DT_V8ARCH)) rc = of_cti_create_v8_connections(dev, drvdata, np);
if (rc)
return rc;
}
else
rc = of_cti_create_impdef_connections(dev, drvdata, np);
Do we report the error if someone specifies v8 connections *and* tirgger-connections manually ?
No - don't think it is necessary - it is explicitly stated in the bindings docs that this is not allowed, and should be obvious from examining the sysfs strucure created if things are missing.
Cheers Suzuki
Mem alloc points above should be covered by devm usage & rewrite of this/prior patches.
Thanks
Mike
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
Abbreviation or shorthands like "impdef" are sure to attrack criticism. To me using the work "custom" in the subject would work just as well. Using "implementation defined" elsewhere in the code to match the documentation is a good idea.
On Wed, Jun 05, 2019 at 08:03:04PM +0100, Mike Leach wrote:
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
.../coresight/coresight-cti-platform.c | 202 +++++++++++++++++- 1 file changed, 197 insertions(+), 5 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index 2fb7d356e9c9..806941e1011e 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -12,10 +12,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" /*
- Find a registered coresight device from a device fwnode.
@@ -210,6 +219,182 @@ static int of_cti_create_v8_connections(struct device *dev, return ret; } +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 = kcalloc(nr_sigs, sizeof(int), 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 cti_drvdata *drvdata,
struct device_node *np)
+{
- 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];
- struct fwnode_handle *r_fwnode;
- /* 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) {
r_fwnode = of_fwnode_handle(cs_np);
csdev = cti_get_assoc_csdev_by_fwnode(r_fwnode);
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 cti_drvdata *drvdata,
struct device_node *np)
+{
- 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, drvdata, nc);
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, @@ -218,12 +403,19 @@ int of_cti_get_hw_data(struct device *dev, int rc = 0; struct cti_device *cti_dev = &drvdata->ctidev;
- /* check for a v8 architectural CTI device */
- if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
- /* 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,
* otherwise implementation defined.
*/
- if (of_property_read_bool(np, CTI_DT_V8ARCH)) rc = of_cti_create_v8_connections(dev, drvdata, np);
if (rc)
return rc;
- }
- else
rc = of_cti_create_impdef_connections(dev, drvdata, np);
- if (rc)
return rc;
/* if no connections, just add a single default based on max IN-OUT */ if (cti_dev->nr_trig_con == 0) -- 2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
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 --- .../coresight/coresight-cti-platform.c | 22 +++++ drivers/hwtracing/coresight/coresight-cti.c | 90 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 1 + drivers/hwtracing/coresight/coresight-priv.h | 3 + drivers/hwtracing/coresight/coresight.c | 56 ++++++++++-- include/linux/coresight.h | 4 + 6 files changed, 171 insertions(+), 5 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index 806941e1011e..215e9f2f6d48 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -443,6 +443,12 @@ int of_get_coresight_cti_platform_data(struct device *dev,
return ret; } + +inline const char *of_cti_get_node_name(struct device *dev) +{ + return dev->of_node->full_name; +} + #else inline int of_get_coresight_cti_platform_data(struct device *dev, @@ -451,6 +457,11 @@ of_get_coresight_cti_platform_data(struct device *dev, return -ENOENT; }
+inline const char *of_get_node_name(struct device *dev) +{ + return NULL; +} + #endif
struct coresight_platform_data * @@ -477,3 +488,14 @@ coresight_cti_get_platform_data(struct device *dev) error: return ERR_PTR(ret); } + +const char *coresight_cti_get_node_name(struct device *dev) +{ + const char *node_name = NULL; + struct fwnode_handle *fwnode = dev_fwnode(dev); + + if (is_of_node(fwnode)) + node_name = of_cti_get_node_name(dev); + + return node_name; +} diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index a4e349984ab8..6fb924f70af2 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -471,6 +471,93 @@ int cti_channel_setop(struct device *dev, return err; }
+/* + * 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 + */ +void 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; + + /* protect the list */ + mutex_lock(&ect_mutex); + + /* exit if current is an ECT device.*/ + if ((csdev->type == CORESIGHT_DEV_TYPE_ECT) || list_empty(&ect_net)) + goto cti_add_done; + + /* if we didn't find the csdev previously we used the fwnode name */ + node_name = coresight_cti_get_node_name(csdev->dev.parent); + + if (!node_name) + goto cti_add_done; + + /* this is the name we want to use for the association */ + csdev_name = dev_name(&csdev->dev); + + /* for each CTI in list... */ + 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_add_done; + } + } +cti_add_done: + mutex_unlock(&ect_mutex); +} +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) { @@ -587,6 +674,9 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) list_add(&ect_nd->next, &ect_net); 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-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index b87cc67543e0..ab1be67eb704 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -250,6 +250,7 @@ int cti_channel_setop(struct device *dev, enum cti_chan_set_op op,
struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev); +const char *coresight_cti_get_node_name(struct device *dev);
/* cti powered and enabled */ #define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered) diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 64a888cc620f..9c4fd7eb56eb 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -164,8 +164,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 void 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 1c9ba2fd6879..74661fabe098 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) { @@ -228,11 +253,15 @@ static int coresight_enable_sink(struct coresight_device *csdev, if (!sink_ops(csdev)->enable) return -EINVAL;
- ret = sink_ops(csdev)->enable(csdev, mode, data); + ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret; + ret = sink_ops(csdev)->enable(csdev, mode, data); + if (ret) { + coresight_control_assoc_ectdev(csdev, 0); + return ret; + } csdev->enable = true; - return 0; }
@@ -246,6 +275,7 @@ static void coresight_disable_sink(struct coresight_device *csdev) ret = sink_ops(csdev)->disable(csdev); if (ret) return; + coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false; }
@@ -276,7 +306,14 @@ 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); + ret = coresight_control_assoc_ectdev(csdev, 1); + if (!ret) { + ret = link_ops(csdev)->enable(csdev, inport, + outport); + if (ret) + coresight_control_assoc_ectdev(csdev, + 0); + } if (ret) { atomic_dec(&csdev->refcnt[refport]); return ret; @@ -316,8 +353,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++) @@ -339,9 +378,14 @@ 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); + ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret; + ret = source_ops(csdev)->enable(csdev, NULL, mode); + if (ret) { + coresight_control_assoc_ectdev(csdev, 0); + return ret; + }; } csdev->enable = true; } @@ -364,6 +408,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; @@ -1277,6 +1322,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 0a2a69b5ade8..936dc138dbbb 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -163,6 +163,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_platform_data *pdata; @@ -176,6 +178,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; };
/*
On Wed, Jun 05, 2019 at 08:03:05PM +0100, Mike Leach wrote:
The CoreSight subsystem enables a path of devices from source to sink. Any CTI devices associated with the path devices must be enabled at the same time.
This patch adds an associated device element to the main coresight device structure, and uses this to create associations between the CTI and other devices based on the device tree data. The assoc dev element is used to
s/assoc/associated/
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
.../coresight/coresight-cti-platform.c | 22 +++++ drivers/hwtracing/coresight/coresight-cti.c | 90 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 1 + drivers/hwtracing/coresight/coresight-priv.h | 3 + drivers/hwtracing/coresight/coresight.c | 56 ++++++++++-- include/linux/coresight.h | 4 + 6 files changed, 171 insertions(+), 5 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index 806941e1011e..215e9f2f6d48 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -443,6 +443,12 @@ int of_get_coresight_cti_platform_data(struct device *dev, return ret; }
+inline const char *of_cti_get_node_name(struct device *dev) +{
- return dev->of_node->full_name;
+}
#else inline int of_get_coresight_cti_platform_data(struct device *dev, @@ -451,6 +457,11 @@ of_get_coresight_cti_platform_data(struct device *dev, return -ENOENT; } +inline const char *of_get_node_name(struct device *dev) +{
- return NULL;
+}
#endif struct coresight_platform_data * @@ -477,3 +488,14 @@ coresight_cti_get_platform_data(struct device *dev) error: return ERR_PTR(ret); }
+const char *coresight_cti_get_node_name(struct device *dev) +{
- const char *node_name = NULL;
- struct fwnode_handle *fwnode = dev_fwnode(dev);
- if (is_of_node(fwnode))
node_name = of_cti_get_node_name(dev);
- return node_name;
+} diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index a4e349984ab8..6fb924f70af2 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -471,6 +471,93 @@ int cti_channel_setop(struct device *dev, return err; } +/*
- 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
- */
+void 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;
- /* protect the list */
- mutex_lock(&ect_mutex);
- /* exit if current is an ECT device.*/
- if ((csdev->type == CORESIGHT_DEV_TYPE_ECT) || list_empty(&ect_net))
goto cti_add_done;
- /* if we didn't find the csdev previously we used the fwnode name */
- node_name = coresight_cti_get_node_name(csdev->dev.parent);
- if (!node_name)
goto cti_add_done;
- /* this is the name we want to use for the association */
- csdev_name = dev_name(&csdev->dev);
- /* for each CTI in list... */
- 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_add_done;
}
- }
+cti_add_done:
- mutex_unlock(&ect_mutex);
+} +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) { @@ -587,6 +674,9 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) list_add(&ect_nd->next, &ect_net); 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-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index b87cc67543e0..ab1be67eb704 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -250,6 +250,7 @@ int cti_channel_setop(struct device *dev, enum cti_chan_set_op op, struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev); +const char *coresight_cti_get_node_name(struct device *dev); /* cti powered and enabled */ #define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered) diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 64a888cc620f..9c4fd7eb56eb 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -164,8 +164,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 void 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 1c9ba2fd6879..74661fabe098 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)
I would make enable a bool type.
+{
- 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);
If CTIs have been probed at boot time but not configured from sysfs, calling ->enable() will have no effect on the trace session. Am I correct?
- } 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) { @@ -228,11 +253,15 @@ static int coresight_enable_sink(struct coresight_device *csdev, if (!sink_ops(csdev)->enable) return -EINVAL;
- ret = sink_ops(csdev)->enable(csdev, mode, data);
- ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret;
- ret = sink_ops(csdev)->enable(csdev, mode, data);
- if (ret) {
coresight_control_assoc_ectdev(csdev, 0);
return ret;
- } csdev->enable = true;
- return 0;
} @@ -246,6 +275,7 @@ static void coresight_disable_sink(struct coresight_device *csdev) ret = sink_ops(csdev)->disable(csdev); if (ret) return;
- coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false;
} @@ -276,7 +306,14 @@ 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);
ret = coresight_control_assoc_ectdev(csdev, 1);
if (!ret) {
ret = link_ops(csdev)->enable(csdev, inport,
outport);
if (ret)
coresight_control_assoc_ectdev(csdev,
0);
} if (ret) { atomic_dec(&csdev->refcnt[refport]); return ret;
@@ -316,8 +353,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++) @@ -339,9 +378,14 @@ 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);
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret;
ret = source_ops(csdev)->enable(csdev, NULL, mode);
if (ret) {
coresight_control_assoc_ectdev(csdev, 0);
return ret;
} csdev->enable = true; }};
@@ -364,6 +408,7 @@ static bool coresight_disable_source(struct coresight_device *csdev) if (atomic_dec_return(csdev->refcnt) == 0) { if (source_ops(csdev)->disable) source_ops(csdev)->disable(csdev, NULL);
csdev->enable = false; } return !csdev->enable;coresight_control_assoc_ectdev(csdev, 0);
@@ -1277,6 +1322,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 0a2a69b5ade8..936dc138dbbb 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -163,6 +163,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_platform_data *pdata; @@ -176,6 +178,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;
}; /* -- 2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Hi Mathieu,
On Mon, 17 Jun 2019 at 22:50, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, Jun 05, 2019 at 08:03:05PM +0100, Mike Leach wrote:
The CoreSight subsystem enables a path of devices from source to sink. Any CTI devices associated with the path devices must be enabled at the same time.
This patch adds an associated device element to the main coresight device structure, and uses this to create associations between the CTI and other devices based on the device tree data. The assoc dev element is used to
s/assoc/associated/
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
.../coresight/coresight-cti-platform.c | 22 +++++ drivers/hwtracing/coresight/coresight-cti.c | 90 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 1 + drivers/hwtracing/coresight/coresight-priv.h | 3 + drivers/hwtracing/coresight/coresight.c | 56 ++++++++++-- include/linux/coresight.h | 4 + 6 files changed, 171 insertions(+), 5 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c index 806941e1011e..215e9f2f6d48 100644 --- a/drivers/hwtracing/coresight/coresight-cti-platform.c +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c @@ -443,6 +443,12 @@ int of_get_coresight_cti_platform_data(struct device *dev,
return ret;
}
+inline const char *of_cti_get_node_name(struct device *dev) +{
return dev->of_node->full_name;
+}
#else inline int of_get_coresight_cti_platform_data(struct device *dev, @@ -451,6 +457,11 @@ of_get_coresight_cti_platform_data(struct device *dev, return -ENOENT; }
+inline const char *of_get_node_name(struct device *dev) +{
return NULL;
+}
#endif
struct coresight_platform_data * @@ -477,3 +488,14 @@ coresight_cti_get_platform_data(struct device *dev) error: return ERR_PTR(ret); }
+const char *coresight_cti_get_node_name(struct device *dev) +{
const char *node_name = NULL;
struct fwnode_handle *fwnode = dev_fwnode(dev);
if (is_of_node(fwnode))
node_name = of_cti_get_node_name(dev);
return node_name;
+} diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index a4e349984ab8..6fb924f70af2 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -471,6 +471,93 @@ int cti_channel_setop(struct device *dev, return err; }
+/*
- 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
- */
+void 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;
/* protect the list */
mutex_lock(&ect_mutex);
/* exit if current is an ECT device.*/
if ((csdev->type == CORESIGHT_DEV_TYPE_ECT) || list_empty(&ect_net))
goto cti_add_done;
/* if we didn't find the csdev previously we used the fwnode name */
node_name = coresight_cti_get_node_name(csdev->dev.parent);
if (!node_name)
goto cti_add_done;
/* this is the name we want to use for the association */
csdev_name = dev_name(&csdev->dev);
/* for each CTI in list... */
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_add_done;
}
}
+cti_add_done:
mutex_unlock(&ect_mutex);
+} +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) { @@ -587,6 +674,9 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) list_add(&ect_nd->next, &ect_net); 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-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index b87cc67543e0..ab1be67eb704 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -250,6 +250,7 @@ int cti_channel_setop(struct device *dev, enum cti_chan_set_op op,
struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev); +const char *coresight_cti_get_node_name(struct device *dev);
/* cti powered and enabled */ #define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered) diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 64a888cc620f..9c4fd7eb56eb 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -164,8 +164,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 void 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 1c9ba2fd6879..74661fabe098 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)
I would make enable a bool type.
OK
+{
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);
If CTIs have been probed at boot time but not configured from sysfs, calling ->enable() will have no effect on the trace session. Am I correct?
Correct - the default state is to have no triggers / channels programmed, so the trace will be unaffected.
Regards
Mike
} 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) { @@ -228,11 +253,15 @@ static int coresight_enable_sink(struct coresight_device *csdev, if (!sink_ops(csdev)->enable) return -EINVAL;
ret = sink_ops(csdev)->enable(csdev, mode, data);
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret;
ret = sink_ops(csdev)->enable(csdev, mode, data);
if (ret) {
coresight_control_assoc_ectdev(csdev, 0);
return ret;
} csdev->enable = true;
return 0;
}
@@ -246,6 +275,7 @@ static void coresight_disable_sink(struct coresight_device *csdev) ret = sink_ops(csdev)->disable(csdev); if (ret) return;
coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false;
}
@@ -276,7 +306,14 @@ 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);
ret = coresight_control_assoc_ectdev(csdev, 1);
if (!ret) {
ret = link_ops(csdev)->enable(csdev, inport,
outport);
if (ret)
coresight_control_assoc_ectdev(csdev,
0);
} if (ret) { atomic_dec(&csdev->refcnt[refport]); return ret;
@@ -316,8 +353,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++)
@@ -339,9 +378,14 @@ 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);
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret;
ret = source_ops(csdev)->enable(csdev, NULL, mode);
if (ret) {
coresight_control_assoc_ectdev(csdev, 0);
return ret;
}; } csdev->enable = true; }
@@ -364,6 +408,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;
@@ -1277,6 +1322,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 0a2a69b5ade8..936dc138dbbb 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -163,6 +163,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_platform_data *pdata; @@ -176,6 +178,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;
};
/*
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
On 05/06/2019 20:03, Mike Leach wrote:
The CoreSight subsystem enables a path of devices from source to sink. Any CTI devices associated with the path devices must be enabled at the same time.
This patch adds an associated device element to the main coresight device structure, and uses this to create associations between the CTI and other devices based on the device tree data. The assoc dev element is used to enable CTI in conjunction with the path elements. CTI are reference counted so where a single CTI is associated with multiple elements on the path, it will be enabled on the first associated device enable, and disabled with the last associated device disable.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../coresight/coresight-cti-platform.c | 22 +++++ drivers/hwtracing/coresight/coresight-cti.c | 90 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 1 + drivers/hwtracing/coresight/coresight-priv.h | 3 + drivers/hwtracing/coresight/coresight.c | 56 ++++++++++-- include/linux/coresight.h | 4 + 6 files changed, 171 insertions(+), 5 deletions(-)
struct coresight_platform_data * @@ -477,3 +488,14 @@ coresight_cti_get_platform_data(struct device *dev) error: return ERR_PTR(ret); }
+const char *coresight_cti_get_node_name(struct device *dev) +{
- const char *node_name = NULL;
- struct fwnode_handle *fwnode = dev_fwnode(dev);
- if (is_of_node(fwnode))
node_name = of_cti_get_node_name(dev);
- return node_name;
+}
nit: This is not specific to CTI and could be simple made coresight_get_fwnode_name() or something similar and moved to coresight-platform.c
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index a4e349984ab8..6fb924f70af2 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -471,6 +471,93 @@ int cti_channel_setop(struct device *dev, return err; } +/*
- 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;
nit: Instead, return 1; ?
}
}
- }
+cti_con_name_match:
- return found;
and return 0; here ?
+}
+/*
- 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
- */
+void 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;
- /* protect the list */
- mutex_lock(&ect_mutex);
- /* exit if current is an ECT device.*/
- if ((csdev->type == CORESIGHT_DEV_TYPE_ECT) || list_empty(&ect_net))
goto cti_add_done;
- /* if we didn't find the csdev previously we used the fwnode name */
- node_name = coresight_cti_get_node_name(csdev->dev.parent);
- if (!node_name)
goto cti_add_done;
- /* this is the name we want to use for the association */
- csdev_name = dev_name(&csdev->dev);
- /* for each CTI in list... */
- 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_add_done;
}
- }
+cti_add_done:
- mutex_unlock(&ect_mutex);
+} +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) {
@@ -587,6 +674,9 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) list_add(&ect_nd->next, &ect_net); 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-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index b87cc67543e0..ab1be67eb704 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -250,6 +250,7 @@ int cti_channel_setop(struct device *dev, enum cti_chan_set_op op, struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev); +const char *coresight_cti_get_node_name(struct device *dev); /* cti powered and enabled */ #define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered) diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 64a888cc620f..9c4fd7eb56eb 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -164,8 +164,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 void 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 1c9ba2fd6879..74661fabe098 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) {
@@ -228,11 +253,15 @@ static int coresight_enable_sink(struct coresight_device *csdev, if (!sink_ops(csdev)->enable) return -EINVAL;
- ret = sink_ops(csdev)->enable(csdev, mode, data);
- ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret;
- ret = sink_ops(csdev)->enable(csdev, mode, data);
- if (ret) {
coresight_control_assoc_ectdev(csdev, 0);
return ret;
- } csdev->enable = true;
- return 0; }
@@ -246,6 +275,7 @@ static void coresight_disable_sink(struct coresight_device *csdev) ret = sink_ops(csdev)->disable(csdev); if (ret) return;
- coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false; }
@@ -276,7 +306,14 @@ 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);
ret = coresight_control_assoc_ectdev(csdev, 1);
if (!ret) {
ret = link_ops(csdev)->enable(csdev, inport,
outport);
if (ret)
coresight_control_assoc_ectdev(csdev,
0);
} if (ret) { atomic_dec(&csdev->refcnt[refport]); return ret;
@@ -316,8 +353,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++) @@ -339,9 +378,14 @@ 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);
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret;
ret = source_ops(csdev)->enable(csdev, NULL, mode);
if (ret) {
coresight_control_assoc_ectdev(csdev, 0);
return ret;
} csdev->enable = true; }};
Instead of spilling the call for sink/link/source call backs, could we not add this to the coresight_enable/disable_path() where these gets called from ? For each device we could add "enable" associated device. This could be done for the source even. Otherwise looks good to me.
Suzuki
Hi Suzuki,
On Fri, 21 Jun 2019 at 09:26, Suzuki K Poulose suzuki.poulose@arm.com wrote:
On 05/06/2019 20:03, Mike Leach wrote:
The CoreSight subsystem enables a path of devices from source to sink. Any CTI devices associated with the path devices must be enabled at the same time.
This patch adds an associated device element to the main coresight device structure, and uses this to create associations between the CTI and other devices based on the device tree data. The assoc dev element is used to enable CTI in conjunction with the path elements. CTI are reference counted so where a single CTI is associated with multiple elements on the path, it will be enabled on the first associated device enable, and disabled with the last associated device disable.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../coresight/coresight-cti-platform.c | 22 +++++ drivers/hwtracing/coresight/coresight-cti.c | 90 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 1 + drivers/hwtracing/coresight/coresight-priv.h | 3 + drivers/hwtracing/coresight/coresight.c | 56 ++++++++++-- include/linux/coresight.h | 4 + 6 files changed, 171 insertions(+), 5 deletions(-)
struct coresight_platform_data * @@ -477,3 +488,14 @@ coresight_cti_get_platform_data(struct device *dev) error: return ERR_PTR(ret); }
+const char *coresight_cti_get_node_name(struct device *dev) +{
const char *node_name = NULL;
struct fwnode_handle *fwnode = dev_fwnode(dev);
if (is_of_node(fwnode))
node_name = of_cti_get_node_name(dev);
return node_name;
+}
nit: This is not specific to CTI and could be simple made coresight_get_fwnode_name() or something similar and moved to coresight-platform.c
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index a4e349984ab8..6fb924f70af2 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -471,6 +471,93 @@ int cti_channel_setop(struct device *dev, return err; }
+/*
- 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;
nit: Instead, return 1; ?
}
}
}
+cti_con_name_match:
return found;
and return 0; here ?
+}
+/*
- 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
- */
+void 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;
/* protect the list */
mutex_lock(&ect_mutex);
/* exit if current is an ECT device.*/
if ((csdev->type == CORESIGHT_DEV_TYPE_ECT) || list_empty(&ect_net))
goto cti_add_done;
/* if we didn't find the csdev previously we used the fwnode name */
node_name = coresight_cti_get_node_name(csdev->dev.parent);
if (!node_name)
goto cti_add_done;
/* this is the name we want to use for the association */
csdev_name = dev_name(&csdev->dev);
/* for each CTI in list... */
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_add_done;
}
}
+cti_add_done:
mutex_unlock(&ect_mutex);
+} +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) {
@@ -587,6 +674,9 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) list_add(&ect_nd->next, &ect_net); 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-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index b87cc67543e0..ab1be67eb704 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -250,6 +250,7 @@ int cti_channel_setop(struct device *dev, enum cti_chan_set_op op,
struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev); +const char *coresight_cti_get_node_name(struct device *dev);
/* cti powered and enabled */ #define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered) diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 64a888cc620f..9c4fd7eb56eb 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -164,8 +164,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 void 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 1c9ba2fd6879..74661fabe098 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) {
@@ -228,11 +253,15 @@ static int coresight_enable_sink(struct coresight_device *csdev, if (!sink_ops(csdev)->enable) return -EINVAL;
ret = sink_ops(csdev)->enable(csdev, mode, data);
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret;
ret = sink_ops(csdev)->enable(csdev, mode, data);
if (ret) {
coresight_control_assoc_ectdev(csdev, 0);
return ret;
} csdev->enable = true;
}return 0;
@@ -246,6 +275,7 @@ static void coresight_disable_sink(struct coresight_device *csdev) ret = sink_ops(csdev)->disable(csdev); if (ret) return;
}coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false;
@@ -276,7 +306,14 @@ 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);
ret = coresight_control_assoc_ectdev(csdev, 1);
if (!ret) {
ret = link_ops(csdev)->enable(csdev, inport,
outport);
if (ret)
coresight_control_assoc_ectdev(csdev,
0);
} if (ret) { atomic_dec(&csdev->refcnt[refport]); return ret;
@@ -316,8 +353,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++)
@@ -339,9 +378,14 @@ 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);
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret;
ret = source_ops(csdev)->enable(csdev, NULL, mode);
if (ret) {
coresight_control_assoc_ectdev(csdev, 0);
return ret;
}; } csdev->enable = true; }
Instead of spilling the call for sink/link/source call backs, could we not add this to the coresight_enable/disable_path() where these gets called from ? For each device we could add "enable" associated device. This could be done for the source even. Otherwise looks good to me.
If I remember correctly that is how Mike proceeded in his first patchset and doing error handling got really messy. The advantage in handling associated devices in callbacks is that operations look atomic to the core, i.e they either succeed or fail regardless of whether the component itself or the associated device caused the fault. It is also consistent with what was done for CATU devices where handling is done locally.
Mathieu
Suzuki _______________________________________________ CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Hi Mathieu,
On 21/06/2019 16:53, Mathieu Poirier wrote:
Hi Suzuki,
On Fri, 21 Jun 2019 at 09:26, Suzuki K Poulose suzuki.poulose@arm.com wrote:
On 05/06/2019 20:03, Mike Leach wrote:
The CoreSight subsystem enables a path of devices from source to sink. Any CTI devices associated with the path devices must be enabled at the same time.
This patch adds an associated device element to the main coresight device structure, and uses this to create associations between the CTI and other devices based on the device tree data. The assoc dev element is used to enable CTI in conjunction with the path elements. CTI are reference counted so where a single CTI is associated with multiple elements on the path, it will be enabled on the first associated device enable, and disabled with the last associated device disable.
...
@@ -339,9 +378,14 @@ 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);
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret;
ret = source_ops(csdev)->enable(csdev, NULL, mode);
if (ret) {
coresight_control_assoc_ectdev(csdev, 0);
return ret;
}; } csdev->enable = true; }
Instead of spilling the call for sink/link/source call backs, could we not add this to the coresight_enable/disable_path() where these gets called from ? For each device we could add "enable" associated device. This could be done for the source even. Otherwise looks good to me.
If I remember correctly that is how Mike proceeded in his first patchset and doing error handling got really messy. The advantage in handling associated devices in callbacks is that operations look atomic to the core, i.e they either succeed or fail regardless of whether the component itself or the associated device caused the fault. It is also consistent with what was done for CATU devices where handling is done locally.
Ok. Thanks for the explanation. Lets leave it as it is now then.
Cheers Suzuki
Hi Suzuki,
On Fri, 21 Jun 2019 at 16:25, Suzuki K Poulose suzuki.poulose@arm.com wrote:
On 05/06/2019 20:03, Mike Leach wrote:
The CoreSight subsystem enables a path of devices from source to sink. Any CTI devices associated with the path devices must be enabled at the same time.
This patch adds an associated device element to the main coresight device structure, and uses this to create associations between the CTI and other devices based on the device tree data. The assoc dev element is used to enable CTI in conjunction with the path elements. CTI are reference counted so where a single CTI is associated with multiple elements on the path, it will be enabled on the first associated device enable, and disabled with the last associated device disable.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../coresight/coresight-cti-platform.c | 22 +++++ drivers/hwtracing/coresight/coresight-cti.c | 90 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 1 + drivers/hwtracing/coresight/coresight-priv.h | 3 + drivers/hwtracing/coresight/coresight.c | 56 ++++++++++-- include/linux/coresight.h | 4 + 6 files changed, 171 insertions(+), 5 deletions(-)
struct coresight_platform_data * @@ -477,3 +488,14 @@ coresight_cti_get_platform_data(struct device *dev) error: return ERR_PTR(ret); }
+const char *coresight_cti_get_node_name(struct device *dev) +{
const char *node_name = NULL;
struct fwnode_handle *fwnode = dev_fwnode(dev);
if (is_of_node(fwnode))
node_name = of_cti_get_node_name(dev);
return node_name;
+}
nit: This is not specific to CTI and could be simple made coresight_get_fwnode_name() or something similar and moved to coresight-platform.c
Done.
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index a4e349984ab8..6fb924f70af2 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -471,6 +471,93 @@ int cti_channel_setop(struct device *dev, return err; }
+/*
- 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;
nit: Instead, return 1; ?
}
}
}
+cti_con_name_match:
return found;
and return 0; here ?
Agreed - somewaht neater.
Thanks
Mike
+}
+/*
- 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
- */
+void 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;
/* protect the list */
mutex_lock(&ect_mutex);
/* exit if current is an ECT device.*/
if ((csdev->type == CORESIGHT_DEV_TYPE_ECT) || list_empty(&ect_net))
goto cti_add_done;
/* if we didn't find the csdev previously we used the fwnode name */
node_name = coresight_cti_get_node_name(csdev->dev.parent);
if (!node_name)
goto cti_add_done;
/* this is the name we want to use for the association */
csdev_name = dev_name(&csdev->dev);
/* for each CTI in list... */
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_add_done;
}
}
+cti_add_done:
mutex_unlock(&ect_mutex);
+} +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) {
@@ -587,6 +674,9 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) list_add(&ect_nd->next, &ect_net); 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-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index b87cc67543e0..ab1be67eb704 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -250,6 +250,7 @@ int cti_channel_setop(struct device *dev, enum cti_chan_set_op op,
struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev); +const char *coresight_cti_get_node_name(struct device *dev);
/* cti powered and enabled */ #define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered) diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 64a888cc620f..9c4fd7eb56eb 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -164,8 +164,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 void 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 1c9ba2fd6879..74661fabe098 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) {
@@ -228,11 +253,15 @@ static int coresight_enable_sink(struct coresight_device *csdev, if (!sink_ops(csdev)->enable) return -EINVAL;
ret = sink_ops(csdev)->enable(csdev, mode, data);
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret;
ret = sink_ops(csdev)->enable(csdev, mode, data);
if (ret) {
coresight_control_assoc_ectdev(csdev, 0);
return ret;
} csdev->enable = true;
}return 0;
@@ -246,6 +275,7 @@ static void coresight_disable_sink(struct coresight_device *csdev) ret = sink_ops(csdev)->disable(csdev); if (ret) return;
}coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false;
@@ -276,7 +306,14 @@ 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);
ret = coresight_control_assoc_ectdev(csdev, 1);
if (!ret) {
ret = link_ops(csdev)->enable(csdev, inport,
outport);
if (ret)
coresight_control_assoc_ectdev(csdev,
0);
} if (ret) { atomic_dec(&csdev->refcnt[refport]); return ret;
@@ -316,8 +353,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++)
@@ -339,9 +378,14 @@ 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);
ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret;
ret = source_ops(csdev)->enable(csdev, NULL, mode);
if (ret) {
coresight_control_assoc_ectdev(csdev, 0);
return ret;
}; } csdev->enable = true; }
Instead of spilling the call for sink/link/source call backs, could we not add this to the coresight_enable/disable_path() where these gets called from ? For each device we could add "enable" associated device. This could be done for the source even. Otherwise looks good to me.
Suzuki
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
Dynamically adds sysfs attributes for all connections defined in the CTI.
Each connection has _name, _trgin_type, _trgout_type, _trgin_sig and _trgout_sig, preceded by a connection index (e.g. 0_name is the name of connection 0).
Additionally each device has a nr_cons and trigout_filtered parameter. This allows clients to explore the connection and trigger signal details without needing to refer to device tree or specification of the device.
Standardised type information is provided for certain common functions - e.g. snk_full for a trigger from a sink indicating full. Otherwise type defaults to genio.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../hwtracing/coresight/coresight-cti-sysfs.c | 338 +++++++++++++++++- drivers/hwtracing/coresight/coresight-cti.c | 11 + drivers/hwtracing/coresight/coresight-cti.h | 4 + 3 files changed, 352 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index e1eacd2b0f6e..a083b2f4306e 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -875,7 +875,342 @@ static struct attribute *coresight_cti_channel_attrs[] = { NULL, };
-/* sysfs groups */ +/* Create the connections group attrs dynamically */ + +/* group is static - .attrs will be built dynamically */ +static struct attribute_group coresight_cti_conns_group = { + .name = "connections", +}; + +/* + *Each connection has dynamic name, trigin/out sigs/types attrs, + * + each device has static nr conns + filter attr + */ +static ssize_t trigout_filtered_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *cfg = &drvdata->config; + int b_sz = PAGE_SIZE; + u32 sig_mask; + int sig_idx, used = 0, nr_trig_max = cfg->nr_trig_max; + + if (cfg->trig_out_filter) { + sig_mask = 0x1; + for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) { + if (sig_mask & cfg->trig_out_filter) { + used += scnprintf(buf+used, b_sz-used, "%d ", + sig_idx); + } + sig_mask <<= 1; + } + used += scnprintf(buf+used, b_sz-used, "\n"); + } else + used += scnprintf(buf, b_sz, "none\n"); + return used; +} +static DEVICE_ATTR_RO(trigout_filtered); + +static ssize_t nr_cons_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return scnprintf(buf, PAGE_SIZE, "%d\n", drvdata->ctidev.nr_trig_con); +} +static DEVICE_ATTR_RO(nr_cons); + +/* static attrs - 2 + null terminator */ +#define CTI_SYSFS_CONS_ST_ATTR 3 +/* dynamic attr - per connection */ +#define CTI_SYSFS_CONS_DYN_ATTR 5 + +static ssize_t con_name_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr; + struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var; + + return scnprintf(buf, PAGE_SIZE, "%s\n", con->con_dev_name); +} + +static ssize_t trigin_sig_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr; + struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *cfg = &drvdata->config; + + u32 sig_mask; + int sig_idx, used = 0, b_sz = PAGE_SIZE; + + sig_mask = 0x1; + for (sig_idx = 0; sig_idx < cfg->nr_trig_max; sig_idx++) { + if (sig_mask & con->con_in->used_mask) { + used += scnprintf(buf+used, b_sz-used, "%d ", + sig_idx); + } + sig_mask <<= 1; + } + used += scnprintf(buf+used, b_sz-used, "\n"); + return used; +} + +static ssize_t trigout_sig_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr; + struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *cfg = &drvdata->config; + + u32 sig_mask; + int sig_idx, used = 0, b_sz = PAGE_SIZE; + + sig_mask = 0x1; + for (sig_idx = 0; sig_idx < cfg->nr_trig_max; sig_idx++) { + if (sig_mask & con->con_out->used_mask) { + used += scnprintf(buf+used, b_sz-used, "%d ", + sig_idx); + } + sig_mask <<= 1; + } + used += scnprintf(buf+used, b_sz-used, "\n"); + return used; +} + +/* convert a sig type id to a name */ +static const char * +cti_sig_type_name(struct cti_trig_con *con, int used_count, bool in) +{ + static const char * const sig_type_names[] = { + "genio", "intreq", "intack", "haltreq", "restartreq", + "pe_edbgreq", "pe_dbgrestart", "pe_ctiirq", "pe_pmuirq", + "pe_dbgtrigger", "etm_extout", "etm_extin", "snk_full", + "snk_acqcomp", "snk_flushcomp", "snk_flushin", "snk_trigin", + "stm_asyncout", "stm_tout_spte", "stm_tout_sw", "stm_tout_hete", + "stm_hwevent", "ela_tstart", "ela_tstop", "ela_dbgreq", + }; + int idx = 0; + struct cti_trig_grp *grp = in ? con->con_in : con->con_out; + + if (grp->sig_types) { + if (used_count < grp->nr_sigs) + idx = grp->sig_types[used_count]; + } + return sig_type_names[idx]; +} + +static ssize_t trigin_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr; + struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var; + + int sig_idx, used = 0, b_sz = PAGE_SIZE; + const char *name; + + for (sig_idx = 0; sig_idx < con->con_in->nr_sigs; sig_idx++) { + name = cti_sig_type_name(con, sig_idx, true); + used += scnprintf(buf+used, b_sz-used, "%s ", name); + } + used += scnprintf(buf+used, b_sz-used, "\n"); + return used; +} + +static ssize_t trigout_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr; + struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var; + + int sig_idx, used = 0, b_sz = PAGE_SIZE; + const char *name; + + for (sig_idx = 0; sig_idx < con->con_out->nr_sigs; sig_idx++) { + name = cti_sig_type_name(con, sig_idx, false); + used += scnprintf(buf+used, b_sz-used, "%s ", name); + } + used += scnprintf(buf+used, b_sz-used, "\n"); + return used; +} + +typedef ssize_t (*p_show_fn)(struct device *dev, struct device_attribute *attr, + char *buf); + +enum cti_conn_attr_type { + CTI_CON_ATTR_NAME, + CTI_CON_ATTR_TRIGIN_SIG, + CTI_CON_ATTR_TRIGOUT_SIG, + CTI_CON_ATTR_TRIGIN_TYPES, + CTI_CON_ATTR_TRIGOUT_TYPES +}; + +static p_show_fn show_fns[] = { + con_name_show, + trigin_sig_show, + trigout_sig_show, + trigin_type_show, + trigout_type_show, +}; + +static const char * const base_names[] = { + "_name", + "_trgin_sig", + "_trgout_sig", + "_trgin_type", + "_trgout_type", +}; + +struct dev_ext_attribute * +cti_create_con_sysfs_attr(int con_idx, enum cti_conn_attr_type attr_type) +{ + struct dev_ext_attribute *dev_ext_attr = 0; + char *name = 0; + + dev_ext_attr = kzalloc(sizeof(struct dev_ext_attribute), GFP_KERNEL); + if (dev_ext_attr) { + name = kzalloc(sizeof(char) * 16, GFP_KERNEL); + if (name) { + /* fill out the underlying attribute struct */ + sprintf(name, "%d%s", con_idx, base_names[attr_type]); + dev_ext_attr->attr.attr.name = name; + dev_ext_attr->attr.attr.mode = 0444; + + /* now the device_attribute struct */ + dev_ext_attr->attr.show = show_fns[attr_type]; + } else { + kfree(dev_ext_attr); + dev_ext_attr = 0; + } + } + return dev_ext_attr; +} + +int cti_create_con_attr_set(int con_idx, int *next_attr_idx, + struct cti_device *ctidev, + struct cti_trig_con *con) +{ + struct dev_ext_attribute *dev_ext_attr; + int attr_idx = *next_attr_idx; + + dev_ext_attr = cti_create_con_sysfs_attr(con_idx, CTI_CON_ATTR_NAME); + if (!dev_ext_attr) + goto create_con_attr_err; + dev_ext_attr->var = con; + ctidev->con_attrs[attr_idx++] = dev_ext_attr; + + if (con->con_in->nr_sigs > 0) { + dev_ext_attr = + cti_create_con_sysfs_attr(con_idx, + CTI_CON_ATTR_TRIGIN_SIG); + if (!dev_ext_attr) + goto create_con_attr_err; + dev_ext_attr->var = con; + ctidev->con_attrs[attr_idx++] = dev_ext_attr; + + dev_ext_attr = + cti_create_con_sysfs_attr(con_idx, + CTI_CON_ATTR_TRIGIN_TYPES); + if (!dev_ext_attr) + goto create_con_attr_err; + dev_ext_attr->var = con; + ctidev->con_attrs[attr_idx++] = dev_ext_attr; + } + + if (con->con_in->nr_sigs > 0) { + + dev_ext_attr = + cti_create_con_sysfs_attr(con_idx, + CTI_CON_ATTR_TRIGOUT_SIG); + if (!dev_ext_attr) + goto create_con_attr_err; + dev_ext_attr->var = con; + ctidev->con_attrs[attr_idx++] = dev_ext_attr; + + dev_ext_attr = + cti_create_con_sysfs_attr(con_idx, + CTI_CON_ATTR_TRIGOUT_TYPES); + if (!dev_ext_attr) + goto create_con_attr_err; + dev_ext_attr->var = con; + ctidev->con_attrs[attr_idx++] = dev_ext_attr; + } + *next_attr_idx = attr_idx; + return 0; + +create_con_attr_err: + cti_destroy_cons_sysfs(ctidev); + return -ENOMEM; +} + +int cti_create_cons_sysfs(struct cti_drvdata *drvdata) +{ + struct cti_device *ctidev = &drvdata->ctidev; + int err = 0, n_attrs = 0, con_idx = 0; + int next_attr_idx = CTI_SYSFS_CONS_ST_ATTR - 1; + struct cti_trig_con *tc; + + n_attrs = CTI_SYSFS_CONS_ST_ATTR + + (CTI_SYSFS_CONS_DYN_ATTR * ctidev->nr_trig_con); + ctidev->con_attrs = + kcalloc(n_attrs, sizeof(struct dev_ext_attribute *), + GFP_KERNEL); + if (ctidev->con_attrs != 0) { + ctidev->con_attrs[0] = (struct dev_ext_attribute *) + &dev_attr_trigout_filtered.attr; + ctidev->con_attrs[1] = (struct dev_ext_attribute *) + &dev_attr_nr_cons.attr; + + /* add dynamic set for each connection */ + list_for_each_entry(tc, &ctidev->trig_cons, node) { + err = cti_create_con_attr_set(con_idx++, + &next_attr_idx, + ctidev, + tc); + /* if create con fails it will free all prior cons */ + if (err) + return err; + } + coresight_cti_conns_group.attrs = + (struct attribute **)ctidev->con_attrs; + } else + err = -ENOMEM; + return err; +} + +void cti_destroy_cons_sysfs(struct cti_device *ctidev) +{ + int n_attrs = CTI_SYSFS_CONS_ST_ATTR + + (CTI_SYSFS_CONS_DYN_ATTR * ctidev->nr_trig_con); + int dyn_idx = CTI_SYSFS_CONS_ST_ATTR - 1; + bool eol = false; + struct dev_ext_attribute *dev_ext_attr = 0; + + if (ctidev->con_attrs) { + while (!eol && (dyn_idx < n_attrs)) { + dev_ext_attr = ctidev->con_attrs[dyn_idx]; + if (dev_ext_attr) { + kfree(dev_ext_attr->attr.attr.name); + kfree(dev_ext_attr); + } else + eol = true; + dyn_idx++; + } + kfree(ctidev->con_attrs); + ctidev->con_attrs = 0; + } +} + +/* attribute and group sysfs tables. */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, }; @@ -900,5 +1235,6 @@ const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_mgmt_group, &coresight_cti_regs_group, &coresight_cti_channels_group, + &coresight_cti_conns_group, NULL, }; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 6fb924f70af2..6996b70f99e8 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -655,6 +655,14 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) goto err_out; }
+ /* create dynamic attributes for connections */ + ret = cti_create_cons_sysfs(drvdata); + if (ret) { + pr_err("%s: create dynamic sysfs entries failed\n", + cti_desc.name); + goto err_out; + } + /* set up coresight component description */ cti_desc.pdata = pdata; cti_desc.type = CORESIGHT_DEV_TYPE_ECT; @@ -719,6 +727,9 @@ void cti_device_release(struct device *dev)
mutex_lock(&ect_mutex);
+ /* clear the dynamic sysfs associate with connections */ + cti_destroy_cons_sysfs(&drvdata->ctidev); + /* clear the connection list items */ cti_free_conn_info(drvdata);
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index ab1be67eb704..99281c8fa126 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -124,12 +124,14 @@ struct cti_trig_con { * assumed there is a single CTM per SoC, ID 0). * @trig_cons: list of connections to this device. * @cpu: CPU ID if associated with CPU, -1 otherwise. + * @con_attrs: Dymanic sysfs attributes created to display connection info. */ struct cti_device { int nr_trig_con; u32 ctm_id; struct list_head trig_cons; int cpu; + struct dev_ext_attribute **con_attrs; };
/** @@ -247,6 +249,8 @@ 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); +int cti_create_cons_sysfs(struct cti_drvdata *drvdata); +void cti_destroy_cons_sysfs(struct cti_device *ctidev);
struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev);
On Wed, Jun 05, 2019 at 08:03:06PM +0100, Mike Leach wrote:
Dynamically adds sysfs attributes for all connections defined in the CTI.
Each connection has _name, _trgin_type, _trgout_type, _trgin_sig and _trgout_sig, preceded by a connection index (e.g. 0_name is the name of connection 0).
Additionally each device has a nr_cons and trigout_filtered parameter. This allows clients to explore the connection and trigger signal details without needing to refer to device tree or specification of the device.
Standardised type information is provided for certain common functions - e.g. snk_full for a trigger from a sink indicating full. Otherwise type defaults to genio.
I will skip this one and wait for V4.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 338 +++++++++++++++++- drivers/hwtracing/coresight/coresight-cti.c | 11 + drivers/hwtracing/coresight/coresight-cti.h | 4 + 3 files changed, 352 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index e1eacd2b0f6e..a083b2f4306e 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -875,7 +875,342 @@ static struct attribute *coresight_cti_channel_attrs[] = { NULL, }; -/* sysfs groups */ +/* Create the connections group attrs dynamically */
+/* group is static - .attrs will be built dynamically */ +static struct attribute_group coresight_cti_conns_group = {
- .name = "connections",
+};
+/*
- *Each connection has dynamic name, trigin/out sigs/types attrs,
- each device has static nr conns + filter attr
- */
+static ssize_t trigout_filtered_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- int b_sz = PAGE_SIZE;
- u32 sig_mask;
- int sig_idx, used = 0, nr_trig_max = cfg->nr_trig_max;
- if (cfg->trig_out_filter) {
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & cfg->trig_out_filter) {
used += scnprintf(buf+used, b_sz-used, "%d ",
sig_idx);
}
sig_mask <<= 1;
}
used += scnprintf(buf+used, b_sz-used, "\n");
- } else
used += scnprintf(buf, b_sz, "none\n");
- return used;
+} +static DEVICE_ATTR_RO(trigout_filtered);
+static ssize_t nr_cons_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return scnprintf(buf, PAGE_SIZE, "%d\n", drvdata->ctidev.nr_trig_con);
+} +static DEVICE_ATTR_RO(nr_cons);
+/* static attrs - 2 + null terminator */ +#define CTI_SYSFS_CONS_ST_ATTR 3 +/* dynamic attr - per connection */ +#define CTI_SYSFS_CONS_DYN_ATTR 5
+static ssize_t con_name_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
- struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
- return scnprintf(buf, PAGE_SIZE, "%s\n", con->con_dev_name);
+}
+static ssize_t trigin_sig_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
- struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- u32 sig_mask;
- int sig_idx, used = 0, b_sz = PAGE_SIZE;
- sig_mask = 0x1;
- for (sig_idx = 0; sig_idx < cfg->nr_trig_max; sig_idx++) {
if (sig_mask & con->con_in->used_mask) {
used += scnprintf(buf+used, b_sz-used, "%d ",
sig_idx);
}
sig_mask <<= 1;
- }
- used += scnprintf(buf+used, b_sz-used, "\n");
- return used;
+}
+static ssize_t trigout_sig_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
- struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- u32 sig_mask;
- int sig_idx, used = 0, b_sz = PAGE_SIZE;
- sig_mask = 0x1;
- for (sig_idx = 0; sig_idx < cfg->nr_trig_max; sig_idx++) {
if (sig_mask & con->con_out->used_mask) {
used += scnprintf(buf+used, b_sz-used, "%d ",
sig_idx);
}
sig_mask <<= 1;
- }
- used += scnprintf(buf+used, b_sz-used, "\n");
- return used;
+}
+/* convert a sig type id to a name */ +static const char * +cti_sig_type_name(struct cti_trig_con *con, int used_count, bool in) +{
- static const char * const sig_type_names[] = {
"genio", "intreq", "intack", "haltreq", "restartreq",
"pe_edbgreq", "pe_dbgrestart", "pe_ctiirq", "pe_pmuirq",
"pe_dbgtrigger", "etm_extout", "etm_extin", "snk_full",
"snk_acqcomp", "snk_flushcomp", "snk_flushin", "snk_trigin",
"stm_asyncout", "stm_tout_spte", "stm_tout_sw", "stm_tout_hete",
"stm_hwevent", "ela_tstart", "ela_tstop", "ela_dbgreq",
- };
- int idx = 0;
- struct cti_trig_grp *grp = in ? con->con_in : con->con_out;
- if (grp->sig_types) {
if (used_count < grp->nr_sigs)
idx = grp->sig_types[used_count];
- }
- return sig_type_names[idx];
+}
+static ssize_t trigin_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
- struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
- int sig_idx, used = 0, b_sz = PAGE_SIZE;
- const char *name;
- for (sig_idx = 0; sig_idx < con->con_in->nr_sigs; sig_idx++) {
name = cti_sig_type_name(con, sig_idx, true);
used += scnprintf(buf+used, b_sz-used, "%s ", name);
- }
- used += scnprintf(buf+used, b_sz-used, "\n");
- return used;
+}
+static ssize_t trigout_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
- struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
- int sig_idx, used = 0, b_sz = PAGE_SIZE;
- const char *name;
- for (sig_idx = 0; sig_idx < con->con_out->nr_sigs; sig_idx++) {
name = cti_sig_type_name(con, sig_idx, false);
used += scnprintf(buf+used, b_sz-used, "%s ", name);
- }
- used += scnprintf(buf+used, b_sz-used, "\n");
- return used;
+}
+typedef ssize_t (*p_show_fn)(struct device *dev, struct device_attribute *attr,
char *buf);
+enum cti_conn_attr_type {
- CTI_CON_ATTR_NAME,
- CTI_CON_ATTR_TRIGIN_SIG,
- CTI_CON_ATTR_TRIGOUT_SIG,
- CTI_CON_ATTR_TRIGIN_TYPES,
- CTI_CON_ATTR_TRIGOUT_TYPES
+};
+static p_show_fn show_fns[] = {
- con_name_show,
- trigin_sig_show,
- trigout_sig_show,
- trigin_type_show,
- trigout_type_show,
+};
+static const char * const base_names[] = {
- "_name",
- "_trgin_sig",
- "_trgout_sig",
- "_trgin_type",
- "_trgout_type",
+};
+struct dev_ext_attribute * +cti_create_con_sysfs_attr(int con_idx, enum cti_conn_attr_type attr_type) +{
- struct dev_ext_attribute *dev_ext_attr = 0;
- char *name = 0;
- dev_ext_attr = kzalloc(sizeof(struct dev_ext_attribute), GFP_KERNEL);
- if (dev_ext_attr) {
name = kzalloc(sizeof(char) * 16, GFP_KERNEL);
if (name) {
/* fill out the underlying attribute struct */
sprintf(name, "%d%s", con_idx, base_names[attr_type]);
dev_ext_attr->attr.attr.name = name;
dev_ext_attr->attr.attr.mode = 0444;
/* now the device_attribute struct */
dev_ext_attr->attr.show = show_fns[attr_type];
} else {
kfree(dev_ext_attr);
dev_ext_attr = 0;
}
- }
- return dev_ext_attr;
+}
+int cti_create_con_attr_set(int con_idx, int *next_attr_idx,
struct cti_device *ctidev,
struct cti_trig_con *con)
+{
- struct dev_ext_attribute *dev_ext_attr;
- int attr_idx = *next_attr_idx;
- dev_ext_attr = cti_create_con_sysfs_attr(con_idx, CTI_CON_ATTR_NAME);
- if (!dev_ext_attr)
goto create_con_attr_err;
- dev_ext_attr->var = con;
- ctidev->con_attrs[attr_idx++] = dev_ext_attr;
- if (con->con_in->nr_sigs > 0) {
dev_ext_attr =
cti_create_con_sysfs_attr(con_idx,
CTI_CON_ATTR_TRIGIN_SIG);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
dev_ext_attr =
cti_create_con_sysfs_attr(con_idx,
CTI_CON_ATTR_TRIGIN_TYPES);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
- }
- if (con->con_in->nr_sigs > 0) {
dev_ext_attr =
cti_create_con_sysfs_attr(con_idx,
CTI_CON_ATTR_TRIGOUT_SIG);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
dev_ext_attr =
cti_create_con_sysfs_attr(con_idx,
CTI_CON_ATTR_TRIGOUT_TYPES);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
- }
- *next_attr_idx = attr_idx;
- return 0;
+create_con_attr_err:
- cti_destroy_cons_sysfs(ctidev);
- return -ENOMEM;
+}
+int cti_create_cons_sysfs(struct cti_drvdata *drvdata) +{
- struct cti_device *ctidev = &drvdata->ctidev;
- int err = 0, n_attrs = 0, con_idx = 0;
- int next_attr_idx = CTI_SYSFS_CONS_ST_ATTR - 1;
- struct cti_trig_con *tc;
- n_attrs = CTI_SYSFS_CONS_ST_ATTR +
(CTI_SYSFS_CONS_DYN_ATTR * ctidev->nr_trig_con);
- ctidev->con_attrs =
kcalloc(n_attrs, sizeof(struct dev_ext_attribute *),
GFP_KERNEL);
- if (ctidev->con_attrs != 0) {
ctidev->con_attrs[0] = (struct dev_ext_attribute *)
&dev_attr_trigout_filtered.attr;
ctidev->con_attrs[1] = (struct dev_ext_attribute *)
&dev_attr_nr_cons.attr;
/* add dynamic set for each connection */
list_for_each_entry(tc, &ctidev->trig_cons, node) {
err = cti_create_con_attr_set(con_idx++,
&next_attr_idx,
ctidev,
tc);
/* if create con fails it will free all prior cons */
if (err)
return err;
}
coresight_cti_conns_group.attrs =
(struct attribute **)ctidev->con_attrs;
- } else
err = -ENOMEM;
- return err;
+}
+void cti_destroy_cons_sysfs(struct cti_device *ctidev) +{
- int n_attrs = CTI_SYSFS_CONS_ST_ATTR +
(CTI_SYSFS_CONS_DYN_ATTR * ctidev->nr_trig_con);
- int dyn_idx = CTI_SYSFS_CONS_ST_ATTR - 1;
- bool eol = false;
- struct dev_ext_attribute *dev_ext_attr = 0;
- if (ctidev->con_attrs) {
while (!eol && (dyn_idx < n_attrs)) {
dev_ext_attr = ctidev->con_attrs[dyn_idx];
if (dev_ext_attr) {
kfree(dev_ext_attr->attr.attr.name);
kfree(dev_ext_attr);
} else
eol = true;
dyn_idx++;
}
kfree(ctidev->con_attrs);
ctidev->con_attrs = 0;
- }
+}
+/* attribute and group sysfs tables. */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, }; @@ -900,5 +1235,6 @@ const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_mgmt_group, &coresight_cti_regs_group, &coresight_cti_channels_group,
- &coresight_cti_conns_group, NULL,
}; diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 6fb924f70af2..6996b70f99e8 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -655,6 +655,14 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) goto err_out; }
- /* create dynamic attributes for connections */
- ret = cti_create_cons_sysfs(drvdata);
- if (ret) {
pr_err("%s: create dynamic sysfs entries failed\n",
cti_desc.name);
goto err_out;
- }
- /* set up coresight component description */ cti_desc.pdata = pdata; cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
@@ -719,6 +727,9 @@ void cti_device_release(struct device *dev) mutex_lock(&ect_mutex);
- /* clear the dynamic sysfs associate with connections */
- cti_destroy_cons_sysfs(&drvdata->ctidev);
- /* clear the connection list items */ cti_free_conn_info(drvdata);
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index ab1be67eb704..99281c8fa126 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -124,12 +124,14 @@ struct cti_trig_con {
assumed there is a single CTM per SoC, ID 0).
- @trig_cons: list of connections to this device.
- @cpu: CPU ID if associated with CPU, -1 otherwise.
*/
- @con_attrs: Dymanic sysfs attributes created to display connection info.
struct cti_device { int nr_trig_con; u32 ctm_id; struct list_head trig_cons; int cpu;
- struct dev_ext_attribute **con_attrs;
}; /** @@ -247,6 +249,8 @@ 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); +int cti_create_cons_sysfs(struct cti_drvdata *drvdata); +void cti_destroy_cons_sysfs(struct cti_device *ctidev); struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev); -- 2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Hi Mike,
I understand that the connection layout will change based on the discussion. Some minor comments below.
On 05/06/2019 20:03, Mike Leach wrote:
Dynamically adds sysfs attributes for all connections defined in the CTI.
Each connection has _name, _trgin_type, _trgout_type, _trgin_sig and _trgout_sig, preceded by a connection index (e.g. 0_name is the name of connection 0).
Additionally each device has a nr_cons and trigout_filtered parameter. This allows clients to explore the connection and trigger signal details without needing to refer to device tree or specification of the device.
Standardised type information is provided for certain common functions - e.g. snk_full for a trigger from a sink indicating full. Otherwise type defaults to genio.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 338 +++++++++++++++++- drivers/hwtracing/coresight/coresight-cti.c | 11 + drivers/hwtracing/coresight/coresight-cti.h | 4 + 3 files changed, 352 insertions(+), 1 deletion(-)
+/*
- *Each connection has dynamic name, trigin/out sigs/types attrs,
- each device has static nr conns + filter attr
- */
+static ssize_t trigout_filtered_show(struct device *dev,
struct device_attribute *attr,
char *buf)
- if (cfg->trig_out_filter) {
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & cfg->trig_out_filter) {
used += scnprintf(buf+used, b_sz-used, "%d ",
sig_idx);
}
sig_mask <<= 1;
}
used += scnprintf(buf+used, b_sz-used, "\n");
- } else
used += scnprintf(buf, b_sz, "none\n");
As mentioned in the other patch, please use %pb[l] or bitmap_print_to_pagebuf().
+/* static attrs - 2 + null terminator */ +#define CTI_SYSFS_CONS_ST_ATTR 3
I would prefer you leave this 2 here and then while computing the n_attrs, say:
+/* dynamic attr - per connection */ +#define CTI_SYSFS_CONS_DYN_ATTR 5
n_attrs = static_attrs + nr_trig_cons * dynamic_per_trig_con_attrs + 1 (for NULL);
+static ssize_t con_name_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
- struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
- return scnprintf(buf, PAGE_SIZE, "%s\n", con->con_dev_name);
+}
+static ssize_t trigin_sig_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
- struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- u32 sig_mask;
- int sig_idx, used = 0, b_sz = PAGE_SIZE;
- sig_mask = 0x1;
- for (sig_idx = 0; sig_idx < cfg->nr_trig_max; sig_idx++) {
if (sig_mask & con->con_in->used_mask) {
used += scnprintf(buf+used, b_sz-used, "%d ",
sig_idx);
}
sig_mask <<= 1;
- }
- used += scnprintf(buf+used, b_sz-used, "\n");
- return used;
+}
+static ssize_t trigout_sig_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
- struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- u32 sig_mask;
- int sig_idx, used = 0, b_sz = PAGE_SIZE;
- sig_mask = 0x1;
- for (sig_idx = 0; sig_idx < cfg->nr_trig_max; sig_idx++) {
if (sig_mask & con->con_out->used_mask) {
used += scnprintf(buf+used, b_sz-used, "%d ",
sig_idx);
}
sig_mask <<= 1;
- }
- used += scnprintf(buf+used, b_sz-used, "\n");
- return used;
+}
+/* convert a sig type id to a name */ +static const char * +cti_sig_type_name(struct cti_trig_con *con, int used_count, bool in)
+{
- static const char * const sig_type_names[] = {
"genio", "intreq", "intack", "haltreq", "restartreq",
"pe_edbgreq", "pe_dbgrestart", "pe_ctiirq", "pe_pmuirq",
"pe_dbgtrigger", "etm_extout", "etm_extin", "snk_full",
"snk_acqcomp", "snk_flushcomp", "snk_flushin", "snk_trigin",
"stm_asyncout", "stm_tout_spte", "stm_tout_sw", "stm_tout_hete",
"stm_hwevent", "ela_tstart", "ela_tstop", "ela_dbgreq",
- };
- int idx = 0;
nit: Please could we s/used_count/sig_idx/ and s/idx/sig_type/ ?
- struct cti_trig_grp *grp = in ? con->con_in : con->con_out;
- if (grp->sig_types) {
if (used_count < grp->nr_sigs)
idx = grp->sig_types[used_count];
- }
- return sig_type_names[idx];
+}
+static ssize_t trigin_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
- struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
- int sig_idx, used = 0, b_sz = PAGE_SIZE;
- const char *name;
- for (sig_idx = 0; sig_idx < con->con_in->nr_sigs; sig_idx++) {
name = cti_sig_type_name(con, sig_idx, true);
used += scnprintf(buf+used, b_sz-used, "%s ", name);
- }
- used += scnprintf(buf+used, b_sz-used, "\n");
- return used;
+}
+static ssize_t trigout_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
- struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
- int sig_idx, used = 0, b_sz = PAGE_SIZE;
- const char *name;
- for (sig_idx = 0; sig_idx < con->con_out->nr_sigs; sig_idx++) {
name = cti_sig_type_name(con, sig_idx, false);
used += scnprintf(buf+used, b_sz-used, "%s ", name);
- }
- used += scnprintf(buf+used, b_sz-used, "\n");
- return used;
+}
+typedef ssize_t (*p_show_fn)(struct device *dev, struct device_attribute *attr,
char *buf);
+enum cti_conn_attr_type {
- CTI_CON_ATTR_NAME,
- CTI_CON_ATTR_TRIGIN_SIG,
- CTI_CON_ATTR_TRIGOUT_SIG,
- CTI_CON_ATTR_TRIGIN_TYPES,
- CTI_CON_ATTR_TRIGOUT_TYPES
+};
+static p_show_fn show_fns[] = {
- con_name_show,
- trigin_sig_show,
- trigout_sig_show,
- trigin_type_show,
- trigout_type_show,
+};
+static const char * const base_names[] = {
- "_name",
- "_trgin_sig",
- "_trgout_sig",
- "_trgin_type",
- "_trgout_type",
+};
+struct dev_ext_attribute * +cti_create_con_sysfs_attr(int con_idx, enum cti_conn_attr_type attr_type) +{
- struct dev_ext_attribute *dev_ext_attr = 0;
- char *name = 0;
- dev_ext_attr = kzalloc(sizeof(struct dev_ext_attribute), GFP_KERNEL);
- if (dev_ext_attr) {
name = kzalloc(sizeof(char) * 16, GFP_KERNEL);
if (name) {
/* fill out the underlying attribute struct */
sprintf(name, "%d%s", con_idx, base_names[attr_type]);
--->>---
dev_ext_attr->attr.attr.name = name;
dev_ext_attr->attr.attr.mode = 0444;
/* now the device_attribute struct */
dev_ext_attr->attr.show = show_fns[attr_type];
-----<<---
nit: You may do : dev_ext_attr->attr = __ATTR(name, 0444, show_fns[attr_type], NULL);
} else {
kfree(dev_ext_attr);
dev_ext_attr = 0;
}
- }
- return dev_ext_attr;
+}
+int cti_create_con_attr_set(int con_idx, int *next_attr_idx,
struct cti_device *ctidev,
struct cti_trig_con *con)
+{
- struct dev_ext_attribute *dev_ext_attr;
- int attr_idx = *next_attr_idx;
- dev_ext_attr = cti_create_con_sysfs_attr(con_idx, CTI_CON_ATTR_NAME);
- if (!dev_ext_attr)
goto create_con_attr_err;
- dev_ext_attr->var = con;
- ctidev->con_attrs[attr_idx++] = dev_ext_attr;
- if (con->con_in->nr_sigs > 0) {
dev_ext_attr =
cti_create_con_sysfs_attr(con_idx,
CTI_CON_ATTR_TRIGIN_SIG);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
dev_ext_attr =
cti_create_con_sysfs_attr(con_idx,
CTI_CON_ATTR_TRIGIN_TYPES);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
- }
- if (con->con_in->nr_sigs > 0) {
dev_ext_attr =
cti_create_con_sysfs_attr(con_idx,
CTI_CON_ATTR_TRIGOUT_SIG);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
dev_ext_attr =
cti_create_con_sysfs_attr(con_idx,
CTI_CON_ATTR_TRIGOUT_TYPES);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
- }
- *next_attr_idx = attr_idx;
- return 0;
+create_con_attr_err:
- cti_destroy_cons_sysfs(ctidev);
- return -ENOMEM;
+}
+int cti_create_cons_sysfs(struct cti_drvdata *drvdata) +{
- struct cti_device *ctidev = &drvdata->ctidev;
- int err = 0, n_attrs = 0, con_idx = 0;
- int next_attr_idx = CTI_SYSFS_CONS_ST_ATTR - 1;
- struct cti_trig_con *tc;
- n_attrs = CTI_SYSFS_CONS_ST_ATTR +
(CTI_SYSFS_CONS_DYN_ATTR * ctidev->nr_trig_con);
nit: Please could we add a macro (after the change to ST_ATTR);
/* Number of attributes to represent nr_trig_con connections */ #define cti_sysfs_connection_n_attrs(nr_trig_con) \ (CTI_SYSFS_CONS_ST_ATTR + (CTI_SYSFS_CONS_DYN_ATTR * (nr_trig_con)) + 1)
and then :
n_attrs = cti_sysfs_connection_n_attrs(ctidev->nr_trig_con);
- ctidev->con_attrs =
kcalloc(n_attrs, sizeof(struct dev_ext_attribute *),
GFP_KERNEL);
- if (ctidev->con_attrs != 0) {
ctidev->con_attrs[0] = (struct dev_ext_attribute *)
&dev_attr_trigout_filtered.attr;
ctidev->con_attrs[1] = (struct dev_ext_attribute *)
&dev_attr_nr_cons.attr;
nit:
Also if you keep the static attrs in an array then you may simply do :
for (attr_idx = 0; attr_idx < ST_ATTR; attr_idx ++) ctidev->con_attrs[attr_idx] = static_attrs[attr_idx]; and then go on to populate the dynamic ones. Thus you may skip initializing the attr_idx to ST_ATTRS - 1 and manualy copying the bits above. This might be cleaner and future proof, if we wanted to add more static attrs.
/* add dynamic set for each connection */
list_for_each_entry(tc, &ctidev->trig_cons, node) {
err = cti_create_con_attr_set(con_idx++,
&next_attr_idx,
ctidev,
tc);
/* if create con fails it will free all prior cons */
if (err)
return err;
}
coresight_cti_conns_group.attrs =
(struct attribute **)ctidev->con_attrs;
- } else
err = -ENOMEM;
- return err;
+}
+void cti_destroy_cons_sysfs(struct cti_device *ctidev) +{
- int n_attrs = CTI_SYSFS_CONS_ST_ATTR +
(CTI_SYSFS_CONS_DYN_ATTR * ctidev->nr_trig_con);
nit: You could reuse the macro from above.
- int dyn_idx = CTI_SYSFS_CONS_ST_ATTR - 1;
- bool eol = false;
- struct dev_ext_attribute *dev_ext_attr = 0;
- if (ctidev->con_attrs) {
while (!eol && (dyn_idx < n_attrs)) {
dev_ext_attr = ctidev->con_attrs[dyn_idx];
if (dev_ext_attr) {
kfree(dev_ext_attr->attr.attr.name);
kfree(dev_ext_attr);
} else
eol = true;
break ? and drop eol variable ?
Rest looks fine.
Suzuki
Hi,
On Fri, 21 Jun 2019 at 18:10, Suzuki K Poulose suzuki.poulose@arm.com wrote:
Hi Mike,
I understand that the connection layout will change based on the discussion. Some minor comments below.
On 05/06/2019 20:03, Mike Leach wrote:
Dynamically adds sysfs attributes for all connections defined in the CTI.
Each connection has _name, _trgin_type, _trgout_type, _trgin_sig and _trgout_sig, preceded by a connection index (e.g. 0_name is the name of connection 0).
Additionally each device has a nr_cons and trigout_filtered parameter. This allows clients to explore the connection and trigger signal details without needing to refer to device tree or specification of the device.
Standardised type information is provided for certain common functions - e.g. snk_full for a trigger from a sink indicating full. Otherwise type defaults to genio.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 338 +++++++++++++++++- drivers/hwtracing/coresight/coresight-cti.c | 11 + drivers/hwtracing/coresight/coresight-cti.h | 4 + 3 files changed, 352 insertions(+), 1 deletion(-)
+/*
- *Each connection has dynamic name, trigin/out sigs/types attrs,
- each device has static nr conns + filter attr
- */
+static ssize_t trigout_filtered_show(struct device *dev,
struct device_attribute *attr,
char *buf)
if (cfg->trig_out_filter) {
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & cfg->trig_out_filter) {
used += scnprintf(buf+used, b_sz-used, "%d ",
sig_idx);
}
sig_mask <<= 1;
}
used += scnprintf(buf+used, b_sz-used, "\n");
} else
used += scnprintf(buf, b_sz, "none\n");
As mentioned in the other patch, please use %pb[l] or bitmap_print_to_pagebuf().
OK done.
+/* static attrs - 2 + null terminator */ +#define CTI_SYSFS_CONS_ST_ATTR 3
I would prefer you leave this 2 here and then while computing the n_attrs, say:
+/* dynamic attr - per connection */ +#define CTI_SYSFS_CONS_DYN_ATTR 5
n_attrs = static_attrs + nr_trig_cons * dynamic_per_trig_con_attrs + 1 (for NULL);
+static ssize_t con_name_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
return scnprintf(buf, PAGE_SIZE, "%s\n", con->con_dev_name);
+}
+static ssize_t trigin_sig_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config;
u32 sig_mask;
int sig_idx, used = 0, b_sz = PAGE_SIZE;
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < cfg->nr_trig_max; sig_idx++) {
if (sig_mask & con->con_in->used_mask) {
used += scnprintf(buf+used, b_sz-used, "%d ",
sig_idx);
}
sig_mask <<= 1;
}
used += scnprintf(buf+used, b_sz-used, "\n");
return used;
+}
+static ssize_t trigout_sig_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config;
u32 sig_mask;
int sig_idx, used = 0, b_sz = PAGE_SIZE;
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < cfg->nr_trig_max; sig_idx++) {
if (sig_mask & con->con_out->used_mask) {
used += scnprintf(buf+used, b_sz-used, "%d ",
sig_idx);
}
sig_mask <<= 1;
}
used += scnprintf(buf+used, b_sz-used, "\n");
return used;
+}
+/* convert a sig type id to a name */ +static const char * +cti_sig_type_name(struct cti_trig_con *con, int used_count, bool in)
+{
static const char * const sig_type_names[] = {
"genio", "intreq", "intack", "haltreq", "restartreq",
"pe_edbgreq", "pe_dbgrestart", "pe_ctiirq", "pe_pmuirq",
"pe_dbgtrigger", "etm_extout", "etm_extin", "snk_full",
"snk_acqcomp", "snk_flushcomp", "snk_flushin", "snk_trigin",
"stm_asyncout", "stm_tout_spte", "stm_tout_sw", "stm_tout_hete",
"stm_hwevent", "ela_tstart", "ela_tstop", "ela_dbgreq",
};
int idx = 0;
nit: Please could we s/used_count/sig_idx/ and s/idx/sig_type/ ?
struct cti_trig_grp *grp = in ? con->con_in : con->con_out;
if (grp->sig_types) {
if (used_count < grp->nr_sigs)
idx = grp->sig_types[used_count];
}
return sig_type_names[idx];
+}
+static ssize_t trigin_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
int sig_idx, used = 0, b_sz = PAGE_SIZE;
const char *name;
for (sig_idx = 0; sig_idx < con->con_in->nr_sigs; sig_idx++) {
name = cti_sig_type_name(con, sig_idx, true);
used += scnprintf(buf+used, b_sz-used, "%s ", name);
}
used += scnprintf(buf+used, b_sz-used, "\n");
return used;
+}
+static ssize_t trigout_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
int sig_idx, used = 0, b_sz = PAGE_SIZE;
const char *name;
for (sig_idx = 0; sig_idx < con->con_out->nr_sigs; sig_idx++) {
name = cti_sig_type_name(con, sig_idx, false);
used += scnprintf(buf+used, b_sz-used, "%s ", name);
}
used += scnprintf(buf+used, b_sz-used, "\n");
return used;
+}
+typedef ssize_t (*p_show_fn)(struct device *dev, struct device_attribute *attr,
char *buf);
+enum cti_conn_attr_type {
CTI_CON_ATTR_NAME,
CTI_CON_ATTR_TRIGIN_SIG,
CTI_CON_ATTR_TRIGOUT_SIG,
CTI_CON_ATTR_TRIGIN_TYPES,
CTI_CON_ATTR_TRIGOUT_TYPES
+};
+static p_show_fn show_fns[] = {
con_name_show,
trigin_sig_show,
trigout_sig_show,
trigin_type_show,
trigout_type_show,
+};
+static const char * const base_names[] = {
"_name",
"_trgin_sig",
"_trgout_sig",
"_trgin_type",
"_trgout_type",
+};
+struct dev_ext_attribute * +cti_create_con_sysfs_attr(int con_idx, enum cti_conn_attr_type attr_type) +{
struct dev_ext_attribute *dev_ext_attr = 0;
char *name = 0;
dev_ext_attr = kzalloc(sizeof(struct dev_ext_attribute), GFP_KERNEL);
if (dev_ext_attr) {
name = kzalloc(sizeof(char) * 16, GFP_KERNEL);
if (name) {
/* fill out the underlying attribute struct */
sprintf(name, "%d%s", con_idx, base_names[attr_type]);
--->>---
dev_ext_attr->attr.attr.name = name;
dev_ext_attr->attr.attr.mode = 0444;
/* now the device_attribute struct */
dev_ext_attr->attr.show = show_fns[attr_type];
-----<<---
nit: You may do : dev_ext_attr->attr = __ATTR(name, 0444, show_fns[attr_type], NULL);
Compile error using this on dynamic allocation - macro static only?
} else {
kfree(dev_ext_attr);
dev_ext_attr = 0;
}
}
return dev_ext_attr;
+}
+int cti_create_con_attr_set(int con_idx, int *next_attr_idx,
struct cti_device *ctidev,
struct cti_trig_con *con)
+{
struct dev_ext_attribute *dev_ext_attr;
int attr_idx = *next_attr_idx;
dev_ext_attr = cti_create_con_sysfs_attr(con_idx, CTI_CON_ATTR_NAME);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
if (con->con_in->nr_sigs > 0) {
dev_ext_attr =
cti_create_con_sysfs_attr(con_idx,
CTI_CON_ATTR_TRIGIN_SIG);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
dev_ext_attr =
cti_create_con_sysfs_attr(con_idx,
CTI_CON_ATTR_TRIGIN_TYPES);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
}
if (con->con_in->nr_sigs > 0) {
dev_ext_attr =
cti_create_con_sysfs_attr(con_idx,
CTI_CON_ATTR_TRIGOUT_SIG);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
dev_ext_attr =
cti_create_con_sysfs_attr(con_idx,
CTI_CON_ATTR_TRIGOUT_TYPES);
if (!dev_ext_attr)
goto create_con_attr_err;
dev_ext_attr->var = con;
ctidev->con_attrs[attr_idx++] = dev_ext_attr;
}
*next_attr_idx = attr_idx;
return 0;
+create_con_attr_err:
cti_destroy_cons_sysfs(ctidev);
return -ENOMEM;
+}
+int cti_create_cons_sysfs(struct cti_drvdata *drvdata) +{
struct cti_device *ctidev = &drvdata->ctidev;
int err = 0, n_attrs = 0, con_idx = 0;
int next_attr_idx = CTI_SYSFS_CONS_ST_ATTR - 1;
struct cti_trig_con *tc;
n_attrs = CTI_SYSFS_CONS_ST_ATTR +
(CTI_SYSFS_CONS_DYN_ATTR * ctidev->nr_trig_con);
nit: Please could we add a macro (after the change to ST_ATTR);
/* Number of attributes to represent nr_trig_con connections */ #define cti_sysfs_connection_n_attrs(nr_trig_con) \ (CTI_SYSFS_CONS_ST_ATTR + (CTI_SYSFS_CONS_DYN_ATTR * (nr_trig_con)) + 1)
and then :
n_attrs = cti_sysfs_connection_n_attrs(ctidev->nr_trig_con);
Changed to group per connection changes much of the above code.
ctidev->con_attrs =
kcalloc(n_attrs, sizeof(struct dev_ext_attribute *),
GFP_KERNEL);
if (ctidev->con_attrs != 0) {
ctidev->con_attrs[0] = (struct dev_ext_attribute *)
&dev_attr_trigout_filtered.attr;
ctidev->con_attrs[1] = (struct dev_ext_attribute *)
&dev_attr_nr_cons.attr;
nit:
Also if you keep the static attrs in an array then you may simply do :
for (attr_idx = 0; attr_idx < ST_ATTR; attr_idx ++) ctidev->con_attrs[attr_idx] = static_attrs[attr_idx];
and then go on to populate the dynamic ones. Thus you may skip initializing the attr_idx to ST_ATTRS - 1 and manualy copying the bits above. This might be cleaner and future proof, if we wanted to add more static attrs.
/* add dynamic set for each connection */
list_for_each_entry(tc, &ctidev->trig_cons, node) {
err = cti_create_con_attr_set(con_idx++,
&next_attr_idx,
ctidev,
tc);
/* if create con fails it will free all prior cons */
if (err)
return err;
}
coresight_cti_conns_group.attrs =
(struct attribute **)ctidev->con_attrs;
} else
err = -ENOMEM;
return err;
+}
+void cti_destroy_cons_sysfs(struct cti_device *ctidev) +{
int n_attrs = CTI_SYSFS_CONS_ST_ATTR +
(CTI_SYSFS_CONS_DYN_ATTR * ctidev->nr_trig_con);
nit: You could reuse the macro from above.
int dyn_idx = CTI_SYSFS_CONS_ST_ATTR - 1;
bool eol = false;
struct dev_ext_attribute *dev_ext_attr = 0;
if (ctidev->con_attrs) {
while (!eol && (dyn_idx < n_attrs)) {
dev_ext_attr = ctidev->con_attrs[dyn_idx];
if (dev_ext_attr) {
kfree(dev_ext_attr->attr.attr.name);
kfree(dev_ext_attr);
} else
eol = true;
break ? and drop eol variable ?
Rest looks fine.
Suzuki
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
Allows trigger in and out signals which have specific functions on Coresight devices to be described.
Signed-off-by: Mike Leach mike.leach@linaro.org --- MAINTAINERS | 1 + include/dt-bindings/arm/coresight-cti-dt.h | 36 ++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 include/dt-bindings/arm/coresight-cti-dt.h
diff --git a/MAINTAINERS b/MAINTAINERS index 4e5811b59e53..d5f579ca1f2d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1541,6 +1541,7 @@ R: Suzuki K Poulose suzuki.poulose@arm.com L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: drivers/hwtracing/coresight/* +F: include/dt-bindings/arm/coresight-cti-dt.h F: Documentation/trace/coresight.txt F: Documentation/trace/coresight-cpu-debug.txt F: Documentation/devicetree/bindings/arm/coresight.txt diff --git a/include/dt-bindings/arm/coresight-cti-dt.h b/include/dt-bindings/arm/coresight-cti-dt.h new file mode 100644 index 000000000000..343737b7ae7f --- /dev/null +++ b/include/dt-bindings/arm/coresight-cti-dt.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * This header provides constants for the trigger signal + * types on CoreSight CTI. + */ + +#ifndef _DT_BINDINGS_ARM_CORESIGHT_CTI_DT_H +#define _DT_BINDINGS_ARM_CORESIGHT_CTI_DT_H + +#define GEN_IO 0 +#define GEN_INTREQ 1 +#define GEN_INTACK 2 +#define GEN_HALTREQ 3 +#define GEN_RESTARTREQ 4 +#define PE_EDBGREQ 5 +#define PE_DBGRESTART 6 +#define PE_CTIIRQ 7 +#define PE_PMUIRQ 8 +#define PE_DBGTRIGGER 9 +#define ETM_EXTOUT 10 +#define ETM_EXTIN 11 +#define SNK_FULL 12 +#define SNK_ACQCOMP 13 +#define SNK_FLUSHCOMP 14 +#define SNK_FLUSHIN 15 +#define SNK_TRIGIN 16 +#define STM_ASYNCOUT 17 +#define STM_TOUT_SPTE 18 +#define STM_TOUT_SW 19 +#define STM_TOUT_HETE 20 +#define STM_HWEVENT 21 +#define ELA_TSTART 22 +#define ELA_TSTOP 23 +#define ELA_DBGREQ 24 + +#endif /*_DT_BINDINGS_ARM_CORESIGHT_CTI_DT_H */
On Wed, Jun 05, 2019 at 08:03:07PM +0100, Mike Leach wrote:
Allows trigger in and out signals which have specific functions on Coresight devices to be described.
Signed-off-by: Mike Leach mike.leach@linaro.org
MAINTAINERS | 1 + include/dt-bindings/arm/coresight-cti-dt.h | 36 ++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 include/dt-bindings/arm/coresight-cti-dt.h
diff --git a/MAINTAINERS b/MAINTAINERS index 4e5811b59e53..d5f579ca1f2d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1541,6 +1541,7 @@ R: Suzuki K Poulose suzuki.poulose@arm.com L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: drivers/hwtracing/coresight/* +F: include/dt-bindings/arm/coresight-cti-dt.h F: Documentation/trace/coresight.txt F: Documentation/trace/coresight-cpu-debug.txt F: Documentation/devicetree/bindings/arm/coresight.txt diff --git a/include/dt-bindings/arm/coresight-cti-dt.h b/include/dt-bindings/arm/coresight-cti-dt.h new file mode 100644 index 000000000000..343737b7ae7f --- /dev/null +++ b/include/dt-bindings/arm/coresight-cti-dt.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */
Any reason for making this GPL-2.0 OR MIT while all the rest of the work in this set is GPL-2.0 only?
+/*
- This header provides constants for the trigger signal
- types on CoreSight CTI.
- */
+#ifndef _DT_BINDINGS_ARM_CORESIGHT_CTI_DT_H +#define _DT_BINDINGS_ARM_CORESIGHT_CTI_DT_H
+#define GEN_IO 0 +#define GEN_INTREQ 1 +#define GEN_INTACK 2 +#define GEN_HALTREQ 3 +#define GEN_RESTARTREQ 4 +#define PE_EDBGREQ 5 +#define PE_DBGRESTART 6 +#define PE_CTIIRQ 7 +#define PE_PMUIRQ 8 +#define PE_DBGTRIGGER 9 +#define ETM_EXTOUT 10 +#define ETM_EXTIN 11 +#define SNK_FULL 12 +#define SNK_ACQCOMP 13 +#define SNK_FLUSHCOMP 14 +#define SNK_FLUSHIN 15 +#define SNK_TRIGIN 16 +#define STM_ASYNCOUT 17 +#define STM_TOUT_SPTE 18 +#define STM_TOUT_SW 19 +#define STM_TOUT_HETE 20 +#define STM_HWEVENT 21 +#define ELA_TSTART 22 +#define ELA_TSTOP 23 +#define ELA_DBGREQ 24
+#endif /*_DT_BINDINGS_ARM_CORESIGHT_CTI_DT_H */
2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Adds system and CPU bound CTI definitions for Qualcom msm8916 platform (Dragonboard DB410C). System CTIs 2-11 are omitted as no information available at present.
Signed-off-by: Mike Leach mike.leach@linaro.org --- arch/arm64/boot/dts/qcom/msm8916.dtsi | 85 +++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi index 423dda996b5d..4886ebb3f3d2 100644 --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi @@ -16,6 +16,7 @@ #include <dt-bindings/reset/qcom,gcc-msm8916.h> #include <dt-bindings/clock/qcom,rpmcc.h> #include <dt-bindings/thermal/thermal.h> +#include <dt-bindings/arm/coresight-cti-dt.h>
/ { interrupt-parent = <&intc>; @@ -1362,7 +1363,7 @@ cpu = <&CPU3>; };
- etm@85c000 { + etm0: etm@85c000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85c000 0x1000>;
@@ -1380,7 +1381,7 @@ }; };
- etm@85d000 { + etm1: etm@85d000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85d000 0x1000>;
@@ -1398,7 +1399,7 @@ }; };
- etm@85e000 { + etm2: etm@85e000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85e000 0x1000>;
@@ -1416,7 +1417,7 @@ }; };
- etm@85f000 { + etm3: etm@85f000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85f000 0x1000>;
@@ -1434,6 +1435,82 @@ }; };
+ /* System CTIs */ + /* CTI 0 - TMC connections */ + cti@810000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x810000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + /* CTI 1 - TPIU connections */ + cti@811000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x811000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + /* CTIs 2-11 - no information - not instantiated */ + + /* Core CTIs; CTIs 12-15 */ + /* CTI - CPU-0 */ + cti@858000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x858000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + + arm,cti-v8-arch; + cpu = <&CPU0>; + arm,cs-dev-assoc = <&etm0>; + + }; + + /* CTI - CPU-1 */ + cti@859000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x859000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + + arm,cti-v8-arch; + cpu = <&CPU1>; + arm,cs-dev-assoc = <&etm1>; + }; + + /* CTI - CPU-2 */ + cti@85a000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x85a000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + + arm,cti-v8-arch; + cpu = <&CPU2>; + arm,cs-dev-assoc = <&etm2>; + }; + + /* CTI - CPU-3 */ + cti@85b000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x85b000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + + arm,cti-v8-arch; + cpu = <&CPU3>; + arm,cs-dev-assoc = <&etm3>; + }; + + venus: video-codec@1d00000 { compatible = "qcom,msm8916-venus"; reg = <0x01d00000 0xff000>;
Mike,
nit: The subject doesn't follow the standard for DTS patches. You may drop "drivers" for better filtering by the maintainers.
You could use :
"dts: msm8916: Add CTI nodes"
Similarly for the Juno.
On 05/06/2019 20:03, Mike Leach wrote:
Adds system and CPU bound CTI definitions for Qualcom msm8916 platform (Dragonboard DB410C). System CTIs 2-11 are omitted as no information available at present. > Signed-off-by: Mike Leach mike.leach@linaro.org
arch/arm64/boot/dts/qcom/msm8916.dtsi | 85 +++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi index 423dda996b5d..4886ebb3f3d2 100644 --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi
..
/* System CTIs */
/* CTI 0 - TMC connections */
cti@810000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x810000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
};
/* CTI 1 - TPIU connections */
cti@811000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x811000 0x1000>;
oh oh. The CTI devices are in the same 64K page ? This could be tricky with a 64K kernel.
Cheers Suzuki
Add in CTI entries for Juno r0, r1 and r2 to device tree entries.
Signed-off-by: Mike Leach mike.leach@linaro.org --- arch/arm64/boot/dts/arm/juno-base.dtsi | 149 +++++++++++++++++++++- arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi | 31 ++++- arch/arm64/boot/dts/arm/juno-r1.dts | 25 ++++ arch/arm64/boot/dts/arm/juno-r2.dts | 25 ++++ arch/arm64/boot/dts/arm/juno.dts | 25 ++++ 5 files changed, 250 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/boot/dts/arm/juno-base.dtsi b/arch/arm64/boot/dts/arm/juno-base.dtsi index 7446e0dc154d..0ceb90c31199 100644 --- a/arch/arm64/boot/dts/arm/juno-base.dtsi +++ b/arch/arm64/boot/dts/arm/juno-base.dtsi @@ -108,7 +108,7 @@ * The actual size is just 4K though 64K is reserved. Access to the * unmapped reserved region results in a DECERR response. */ - etf@20010000 { /* etf0 */ + etf_sys0: etf@20010000 { /* etf0 */ compatible = "arm,coresight-tmc", "arm,primecell"; reg = <0 0x20010000 0 0x1000>;
@@ -132,7 +132,7 @@ }; };
- tpiu@20030000 { + tpiu_sys: tpiu@20030000 { compatible = "arm,coresight-tpiu", "arm,primecell"; reg = <0 0x20030000 0 0x1000>;
@@ -185,7 +185,7 @@ }; };
- etr@20070000 { + etr_sys: etr@20070000 { compatible = "arm,coresight-tmc", "arm,primecell"; reg = <0 0x20070000 0 0x1000>; iommus = <&smmu_etr 0>; @@ -203,7 +203,7 @@ }; };
- stm@20100000 { + stm_sys: stm@20100000 { compatible = "arm,coresight-stm", "arm,primecell"; reg = <0 0x20100000 0 0x1000>, <0 0x28000000 0 0x1000000>; @@ -280,6 +280,18 @@ }; };
+ cti0: cti@22020000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x22020000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + arm,cti-v8-arch; + arm,cs-dev-assoc = <&etm0>; + }; + funnel@220c0000 { /* cluster0 funnel */ compatible = "arm,coresight-funnel", "arm,primecell"; reg = <0 0x220c0000 0 0x1000>; @@ -340,6 +352,18 @@ }; };
+ cti1: cti@22120000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x22120000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + arm,cti-v8-arch; + arm,cs-dev-assoc = <&etm1>; + }; + cpu_debug2: cpu-debug@23010000 { compatible = "arm,coresight-cpu-debug", "arm,primecell"; reg = <0x0 0x23010000 0x0 0x1000>; @@ -365,6 +389,18 @@ }; };
+ cti2: cti@23020000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x23020000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + arm,cti-v8-arch; + arm,cs-dev-assoc = <&etm2>; + }; + funnel@230c0000 { /* cluster1 funnel */ compatible = "arm,coresight-funnel", "arm,primecell"; reg = <0 0x230c0000 0 0x1000>; @@ -437,6 +473,18 @@ }; };
+ cti3: cti@23120000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x23120000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + arm,cti-v8-arch; + arm,cs-dev-assoc = <&etm3>; + }; + cpu_debug4: cpu-debug@23210000 { compatible = "arm,coresight-cpu-debug", "arm,primecell"; reg = <0x0 0x23210000 0x0 0x1000>; @@ -462,6 +510,18 @@ }; };
+ cti4: cti@23220000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x23220000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + arm,cti-v8-arch; + arm,cs-dev-assoc = <&etm4>; + }; + cpu_debug5: cpu-debug@23310000 { compatible = "arm,coresight-cpu-debug", "arm,primecell"; reg = <0x0 0x23310000 0x0 0x1000>; @@ -487,6 +547,87 @@ }; };
+ cti5: cti@23320000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x23320000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + arm,cti-v8-arch; + arm,cs-dev-assoc = <&etm5>; + }; + + + cti@20020000 { /* sys_cti_0 */ + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x20020000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + trig-conns@0 { + arm,trig-in-sigs=<2 3>; + arm,trig-in-types=<SNK_FULL SNK_ACQCOMP>; + arm,trig-out-sigs=<0 1>; + arm,trig-out-types=<SNK_FLUSHIN SNK_TRIGIN>; + arm,cs-dev-assoc = <&etr_sys>; + }; + + trig-conns@1 { + arm,trig-in-sigs=<0 1>; + arm,trig-in-types=<SNK_FULL SNK_ACQCOMP>; + arm,trig-out-sigs=<7 6>; + arm,trig-out-types=<SNK_FLUSHIN SNK_TRIGIN>; + arm,cs-dev-assoc = <&etf_sys0>; + }; + + trig-conns@2 { + arm,trig-in-sigs=<4 5 6 7>; + arm,trig-in-types=<STM_TOUT_SPTE STM_TOUT_SW STM_TOUT_HETE STM_ASYNCOUT>; + arm,trig-out-sigs=<4 5>; + arm,trig-out-types=<STM_HWEVENT STM_HWEVENT>; + arm,cs-dev-assoc = <&stm_sys>; + }; + + trig-conns@3 { + arm,trig-out-sigs=<2 3>; + arm,trig-out-types=<SNK_FLUSHIN SNK_TRIGIN>; + arm,cs-dev-assoc = <&tpiu_sys>; + }; + }; + + cti@20110000 { /* sys_cti_1 */ + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x20110000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + trig-conns@0 { + arm,trig-in-sigs=<0>; + arm,trig-in-types=<GEN_INTREQ>; + arm,trig-out-sigs=<0>; + arm,trig-out-types=<GEN_HALTREQ>; + arm,trig-conn-name = "sys_profiler"; + }; + + trig-conns@1 { + arm,trig-out-sigs=<2 3>; + arm,trig-out-types=<GEN_HALTREQ GEN_RESTARTREQ>; + arm,trig-conn-name = "watchdog"; + }; + + trig-conns@2 { + arm,trig-out-sigs=<1 6>; + arm,trig-out-types=<GEN_HALTREQ GEN_RESTARTREQ>; + arm,trig-conn-name = "g_counter"; + }; + }; + sram: sram@2e000000 { compatible = "arm,juno-sram-ns", "mmio-sram"; reg = <0x0 0x2e000000 0x0 0x8000>; diff --git a/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi b/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi index cf285152deab..ccade2e56ced 100644 --- a/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi +++ b/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi @@ -23,7 +23,7 @@ }; };
- etf@20140000 { /* etf1 */ + etf_sys1: etf@20140000 { /* etf1 */ compatible = "arm,coresight-tmc", "arm,primecell"; reg = <0 0x20140000 0 0x1000>;
@@ -82,4 +82,33 @@
}; }; + + cti@20160000 { /* sys_cti_2 */ + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x20160000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + trig-conns@0 { + arm,trig-in-sigs=<0 1>; + arm,trig-in-types=<SNK_FULL SNK_ACQCOMP>; + arm,trig-out-sigs=<0 1>; + arm,trig-out-types=<SNK_FLUSHIN SNK_TRIGIN>; + arm,cs-dev-assoc = <&etf_sys1>; + }; + + trig-conns@1 { + arm,trig-in-sigs=<2 3 4>; + arm,trig-in-types=<ELA_DBGREQ ELA_TSTART ELA_TSTOP>; + arm,trig-conn-name = "ela_clus_0"; + }; + + trig-conns@2 { + arm,trig-in-sigs=<5 6 7>; + arm,trig-in-types=<ELA_DBGREQ ELA_TSTART ELA_TSTOP>; + arm,trig-conn-name = "ela_clus_1"; + }; + }; }; diff --git a/arch/arm64/boot/dts/arm/juno-r1.dts b/arch/arm64/boot/dts/arm/juno-r1.dts index 5f290090b0cf..02aa51eb311d 100644 --- a/arch/arm64/boot/dts/arm/juno-r1.dts +++ b/arch/arm64/boot/dts/arm/juno-r1.dts @@ -9,6 +9,7 @@ /dts-v1/;
#include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/arm/coresight-cti-dt.h> #include "juno-base.dtsi" #include "juno-cs-r1r2.dtsi"
@@ -309,3 +310,27 @@ &cpu_debug5 { cpu = <&A53_3>; }; + +&cti0 { + cpu = <&A57_0>; +}; + +&cti1 { + cpu = <&A57_1>; +}; + +&cti2 { + cpu = <&A53_0>; +}; + +&cti3 { + cpu = <&A53_1>; +}; + +&cti4 { + cpu = <&A53_2>; +}; + +&cti5 { + cpu = <&A53_3>; +}; diff --git a/arch/arm64/boot/dts/arm/juno-r2.dts b/arch/arm64/boot/dts/arm/juno-r2.dts index 305300dd521c..75bb27c2d4dc 100644 --- a/arch/arm64/boot/dts/arm/juno-r2.dts +++ b/arch/arm64/boot/dts/arm/juno-r2.dts @@ -9,6 +9,7 @@ /dts-v1/;
#include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/arm/coresight-cti-dt.h> #include "juno-base.dtsi" #include "juno-cs-r1r2.dtsi"
@@ -315,3 +316,27 @@ &cpu_debug5 { cpu = <&A53_3>; }; + +&cti0 { + cpu = <&A72_0>; +}; + +&cti1 { + cpu = <&A72_1>; +}; + +&cti2 { + cpu = <&A53_0>; +}; + +&cti3 { + cpu = <&A53_1>; +}; + +&cti4 { + cpu = <&A53_2>; +}; + +&cti5 { + cpu = <&A53_3>; +}; diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts index f00cffbd032c..dbc22e70b62c 100644 --- a/arch/arm64/boot/dts/arm/juno.dts +++ b/arch/arm64/boot/dts/arm/juno.dts @@ -9,6 +9,7 @@ /dts-v1/;
#include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/arm/coresight-cti-dt.h> #include "juno-base.dtsi"
/ { @@ -295,3 +296,27 @@ &cpu_debug5 { cpu = <&A53_3>; }; + +&cti0 { + cpu = <&A57_0>; +}; + +&cti1 { + cpu = <&A57_1>; +}; + +&cti2 { + cpu = <&A53_0>; +}; + +&cti3 { + cpu = <&A53_1>; +}; + +&cti4 { + cpu = <&A53_2>; +}; + +&cti5 { + cpu = <&A53_3>; +};
Add section in coresight.txt explaining operation and use of CTI devices in CoreSight.
Signed-off-by: Mike Leach mike.leach@linaro.org --- Documentation/trace/coresight.txt | 139 ++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+)
diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt index efbc832146e7..bf8125f3e39c 100644 --- a/Documentation/trace/coresight.txt +++ b/Documentation/trace/coresight.txt @@ -425,6 +425,145 @@ root@genericarmv8:~#
Details on how to use the generic STM API can be found here [2].
+CoreSight Embedded Cross Trigger (CTI & CTM). +--------------------------------------------- + +The CoreSight Cross Trigger Interface (CTI) is a hardware device that takes +individual input and output hardware signals from devices and interconnects +them via the Cross Trigger Matrix (CTM) to other devices, in order to propagate +events between devices. + +e.g. + + 0000000 in_trigs ::::::: + 0 C 0----------->: : +======>(other CTI channel IO) + 0 P 0<-----------: : v + 0 U 0 out_trigs : : Channels ***** ::::::: + 0000000 : CTI :<=========>*CTM*<====>: CTI :---+ + ####### in_trigs : : (id 0-3) ***** ::::::: v + # ETM #----------->: : ^ ####### + # #<-----------: : +---# ETR # + ####### out_trigs ::::::: ####### + +The CTI driver enables the programming of the CTI to attach triggers to +channels. When an input trigger becomes active, the attached channel will +become active. Any output trigger also attached to that channel will also +become active. The active channel is propagated to other CTIs via the CTM +affecting output triggers there, unless filtered by the CTI channel gate. + +It is also possible to activate a channel using system software. + +The CTIs are registered by the system to be associated with CPUs and/or other +CoreSight devices on the trace data path. When these devices are enabled then +the attached CTIs will also be enabled. By default/on power up the CTIs have +no programmed trigger/channel attachments, so will not affect the system +until explicitly programmed. + +The hardware trigger connections between CTIs and devices is implementation +defined, unless the CPU/ETM combination is a v8 architecture, in which case +the connections have an architecturally defined standard layout. + +The hardware trigger signals can also be connected to non-CoreSight devices +(e.g. UART), or be propagated off chip as hardware IO lines. + +All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by + a CTM. On these systems a CTM index is declared to associate CTI devices that + are interconnected via the CTM. + +The CTI devices appear on the existing coresight bus alongside the other +CoreSight devices. + +# ls /sys/bus/coresight/devices/858000.cti +channels connections ctmid enable mgmt power regs subsystem uevent + +Key items are: +* enable: enables the CTI. +* ctmid : associated CTM - only relevant if system has multiple CTI+CTM clusters +that are not interconnected. + + +*connections: sub-dir containing the interconnection information for this CTI. +*channels: sub-dir containing the channel API - CTI main programming interface. +*regs: sub-dir giving access to the raw programmable CTI regs. +*mgmt: sub-dir for the standard CoreSight management registers. + +** Connections: This contains a list of read only data describing the devices +connected to this CTI, and the individual trigger signals to and from the +devices: +./b5b000.cti/connections# ls +0_name 0_trgin_type 0_trgout_type 1_trgin_sig 1_trgout_sig nr_cons +0_trgin_sig 0_trgout_sig 1_name 1_trgin_type 1_trgout_type trigout_filtered + +* nr_cons - number of connections +* trigout_filtered - trigger out signals that are prevented from being set if +filtering is enabled. Prevents accidental edbgreq signals stopping a core. + +Each connection has a set of parameters prefixed with the connection index. +Thus 0_name will show the name of connection 0. +* N_name : name of connection +* N_trgin_sig : input trigger signal indexes used in this connection. +* N_trgin_type : functional types for in signals. +* N_trgout_sig : output trigger signals for this connection. +* N_trgout_type : functional types for out signals. + +e.g. +.../85b000.cti/connections# cat 0_name +cpu3 +.../85b000.cti/connections# cat 0_trgin_sig +0 1 +.../85b000.cti/connections# cat 0_trgin_type +pe_dbgtrigger pe_pmuirq + +If a connection has zero signals in either the in or out triggers then those +parameters will be omitted from the connections sub-dir. + + +** Channels API : This provides an easy way to attach triggers to channels, +without needing the multiple register operations that are required if +manipluating the 'regs' sub-dir elements directly. + +# ls /sys/bus/coresight/devices/859000.cti/channels/ +chan_clear gate_disable list_xtrigs trigin_attach trigout_detach +chan_pulse gate_enable reset_xtrigs trigin_detach +chan_set list_chan_inuse trig_filter_enable trigout_attach + +Most access to these elements take the form: +echo <chan> [<trigger>] > /<device_path>/<operation> +where the optional <trigger> is only needed for trigXX_at|detach operations. + +e.g. +echo 0 1 > /sys/bus/coresight/devices/859000.cti/channels/trigin_attach +attach trig_in(1) to channel(0). +echo 0 > /sys/bus/coresight/devices/859000.cti/channels/chan_set +activate channel(0) + +*gate_enable, gate_disable operations set the CTI gate to propagate +(enable) to other devices. These operation can take a channel number or 'all' +to operate on all channels. CTI gate is enabled by default at power up. + +*list_gate_enable: show the current channels enabled through the gate. +*list_inuse: show the current channels attached to any signal +*list_free: show channels with no attached signals +*show_chan_sel: select the channel for the show_chan_xtrigs operation. +*show_chan_xtrigs: show the cross triggers programmed for the selected channel. + +.../85b000.cti/channels# echo 0 1 > trigin_attach +.../85b000.cti/channels# cat list_free +1 2 3 +.../85b000.cti/channels# cat list_inuse +0 +.../85b000.cti/channels# echo 0 > show_chan_sel +.../85b000.cti/channels# cat show_chan_xtrigs +IN: 1 OUT: + +*trig_filter_enable: defaults to enabled, disable to allow potentially +dangerous output signals to be set. + +*reset_xtrigs: clear all channel / trigger programming - reset device hardware +to default state. + [1]. Documentation/ABI/testing/sysfs-bus-coresight-devices-stm [2]. Documentation/trace/stm.rst [3]. https://github.com/Linaro/perf-opencsd
Hi Mike,
On 05/06/2019 20:03, Mike Leach wrote:
Add section in coresight.txt explaining operation and use of CTI devices in CoreSight.
Thanks a lot for the detailed documentation. That really helps to get a better picture of the CTI.
PS: I am starting here, to have a better understanding before I look at the driver. :-)
Some minor comments below :
Signed-off-by: Mike Leach mike.leach@linaro.org
Documentation/trace/coresight.txt | 139 ++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+)
diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt index efbc832146e7..bf8125f3e39c 100644 --- a/Documentation/trace/coresight.txt +++ b/Documentation/trace/coresight.txt @@ -425,6 +425,145 @@ root@genericarmv8:~# Details on how to use the generic STM API can be found here [2]. +CoreSight Embedded Cross Trigger (CTI & CTM). +---------------------------------------------
+The CoreSight Cross Trigger Interface (CTI) is a hardware device that takes +individual input and output hardware signals from devices and interconnects
How about introducing the term "triggers" here ^^. i.e,
"input and output hardware signals from/to devices *(called "triggers")* and .."
and channels below.
+them via the Cross Trigger Matrix (CTM) to other devices, in order to propagate
"(CTM) to other devices *via channels* to other devices,...."
+events between devices.
+e.g.
- 0000000 in_trigs :::::::
- 0 C 0----------->: : +======>(other CTI channel IO)
- 0 P 0<-----------: : v
- 0 U 0 out_trigs : : Channels ***** :::::::
- 0000000 : CTI :<=========>*CTM*<====>: CTI :---+
- ####### in_trigs : : (id 0-3) ***** ::::::: v
- # ETM #----------->: : ^ #######
- # #<-----------: : +---# ETR #
- ####### out_trigs ::::::: #######
+The CTI driver enables the programming of the CTI to attach triggers to
for better clarity, "CTI to tie a given device trigger to a a single channel."
+channels. When an input trigger becomes active, the attached channel will +become active. Any output trigger also attached to that channel will also
s/output trigger also attached/output trigger attached/
+become active. The active channel is propagated to other CTIs via the CTM
s/via the CTM/via the CTM,/
+affecting output triggers there, unless filtered by the CTI channel gate.
s/affecting/activating/ s/the output triggers there/the output triggers connected to the channels/
+It is also possible to activate a channel using system software.
s/using system software/by directly programming the CTI/
+The CTIs are registered by the system to be associated with CPUs and/or other +CoreSight devices on the trace data path. When these devices are enabled then
s/then//
+the attached CTIs will also be enabled. By default/on power up the CTIs have +no programmed trigger/channel attachments, so will not affect the system +until explicitly programmed.
+The hardware trigger connections between CTIs and devices is implementation +defined, unless the CPU/ETM combination is a v8 architecture, in which case +the connections have an architecturally defined standard layout.
+The hardware trigger signals can also be connected to non-CoreSight devices +(e.g. UART), or be propagated off chip as hardware IO lines.
+All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by
- a CTM.
s/by a CTM/to each other/
On these systems a CTM index is declared to associate CTI devices that
- are interconnected via the CTM.
+The CTI devices appear on the existing coresight bus alongside the other +CoreSight devices.
+# ls /sys/bus/coresight/devices/858000.cti +channels connections ctmid enable mgmt power regs subsystem uevent
+Key items are: +* enable: enables the CTI.
Enable/disable the CTI ?
+* ctmid : associated CTM - only relevant if system has multiple CTI+CTM clusters +that are not interconnected.
+*connections: sub-dir containing the interconnection information for this CTI. +*channels: sub-dir containing the channel API - CTI main programming interface. +*regs: sub-dir giving access to the raw programmable CTI regs. +*mgmt: sub-dir for the standard CoreSight management registers.
minor nit: The above looks a bit inconsistent in terms of spacing. e.,g "* enable" vs "*connections:"
I think, having a TAB at the beginning for the key items might make them more visible (especially when you want to quickly look up the name to see what it implies, for e.g). So :
enable - Enable/Disable the CTI ctmid - Line-1 of this long comment Line-2 of this long comment.
Just my thought, as long as it is aligned and easily distinguishable, I am fine.
+** Connections: This contains a list of read only data describing the devices +connected to this CTI, and the individual trigger signals to and from the +devices: +./b5b000.cti/connections# ls
nit: Should this be named "triggers" to match what they imply ?
+0_name 0_trgin_type 0_trgout_type 1_trgin_sig 1_trgout_sig nr_cons +0_trgin_sig 0_trgout_sig 1_name 1_trgin_type 1_trgout_type trigout_filtered
I personally find these names a bit difficult to follow and I understand it is terribly painful to group them under a sub-directory. If we assume that the device connected to a given Trigger is always present, we may be able to create permanent attributes to list these triggers. I think this is a fair assumption as we don't care if the device is there or not. All we care is pass/attach the triggers to channels and vice versa. So, we may be able to create an "attribute_group" per trigger with the following attributes:
device in_type out_type in_sig out_sig
e.g,
ls $b5b000.cti/trigger_0/$ device in_type out_type in_sig out_sig
And add it to the coresight device.
We couldn't do this for "graph" connections as we cannot expose the connections unless the device is present and that becomes pain.
+* nr_cons - number of connections +* trigout_filtered - trigger out signals that are prevented from being set if +filtering is enabled. Prevents accidental edbgreq signals stopping a core.
+Each connection has a set of parameters prefixed with the connection index. +Thus 0_name will show the name of connection 0. +* N_name : name of connection +* N_trgin_sig : input trigger signal indexes used in this connection. +* N_trgin_type : functional types for in signals. +* N_trgout_sig : output trigger signals for this connection. +* N_trgout_type : functional types for out signals.
+e.g. +.../85b000.cti/connections# cat 0_name +cpu3 +.../85b000.cti/connections# cat 0_trgin_sig +0 1 +.../85b000.cti/connections# cat 0_trgin_type +pe_dbgtrigger pe_pmuirq
+If a connection has zero signals in either the in or out triggers then those +parameters will be omitted from the connections sub-dir.
+** Channels API : This provides an easy way to attach triggers to channels, +without needing the multiple register operations that are required if +manipluating the 'regs' sub-dir elements directly.
+# ls /sys/bus/coresight/devices/859000.cti/channels/ +chan_clear gate_disable list_xtrigs trigin_attach trigout_detach +chan_pulse gate_enable reset_xtrigs trigin_detach +chan_set list_chan_inuse trig_filter_enable trigout_attach
+Most access to these elements take the form: +echo <chan> [<trigger>] > /<device_path>/<operation>
You may drop the /<device_path>/
+where the optional <trigger> is only needed for trigXX_at|detach operations.
+e.g. +echo 0 1 > /sys/bus/coresight/devices/859000.cti/channels/trigin_attach +attach trig_in(1) to channel(0). +echo 0 > /sys/bus/coresight/devices/859000.cti/channels/chan_set +activate channel(0)
+*gate_enable, gate_disable operations set the CTI gate to propagate +(enable) to other devices. These operation can take a channel number or 'all' +to operate on all channels. CTI gate is enabled by default at power up.
+*list_gate_enable: show the current channels enabled through the gate. +*list_inuse: show the current channels attached to any signal +*list_free: show channels with no attached signals +*show_chan_sel: select the channel for the show_chan_xtrigs operation. +*show_chan_xtrigs: show the cross triggers programmed for the selected channel.
+.../85b000.cti/channels# echo 0 1 > trigin_attach +.../85b000.cti/channels# cat list_free +1 2 3 +.../85b000.cti/channels# cat list_inuse +0 +.../85b000.cti/channels# echo 0 > show_chan_sel +.../85b000.cti/channels# cat show_chan_xtrigs +IN: 1 OUT:
+*trig_filter_enable: defaults to enabled, disable to allow potentially +dangerous output signals to be set.
+*reset_xtrigs: clear all channel / trigger programming - reset device hardware +to default state.
I think all the attributes may/must be documented under ABI/testing/sysfs-bus-coresight-devices-cti/
Cheers Suzuki
On Wed, Jun 05, 2019 at 08:03:10PM +0100, Mike Leach wrote:
Add section in coresight.txt explaining operation and use of CTI devices in CoreSight.
Signed-off-by: Mike Leach mike.leach@linaro.org
Documentation/trace/coresight.txt | 139 ++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+)
Since CTIs are so different I think this should be in a file of its own, i.e coresight-ect.txt .
diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt index efbc832146e7..bf8125f3e39c 100644 --- a/Documentation/trace/coresight.txt +++ b/Documentation/trace/coresight.txt @@ -425,6 +425,145 @@ root@genericarmv8:~# Details on how to use the generic STM API can be found here [2]. +CoreSight Embedded Cross Trigger (CTI & CTM). +---------------------------------------------
+The CoreSight Cross Trigger Interface (CTI) is a hardware device that takes +individual input and output hardware signals from devices and interconnects +them via the Cross Trigger Matrix (CTM) to other devices, in order to propagate +events between devices.
+e.g.
- 0000000 in_trigs :::::::
- 0 C 0----------->: : +======>(other CTI channel IO)
- 0 P 0<-----------: : v
- 0 U 0 out_trigs : : Channels ***** :::::::
- 0000000 : CTI :<=========>*CTM*<====>: CTI :---+
- ####### in_trigs : : (id 0-3) ***** ::::::: v
- # ETM #----------->: : ^ #######
- # #<-----------: : +---# ETR #
- ####### out_trigs ::::::: #######
+The CTI driver enables the programming of the CTI to attach triggers to +channels. When an input trigger becomes active, the attached channel will +become active. Any output trigger also attached to that channel will also +become active. The active channel is propagated to other CTIs via the CTM +affecting output triggers there, unless filtered by the CTI channel gate.
+It is also possible to activate a channel using system software.
+The CTIs are registered by the system to be associated with CPUs and/or other +CoreSight devices on the trace data path. When these devices are enabled then +the attached CTIs will also be enabled. By default/on power up the CTIs have +no programmed trigger/channel attachments, so will not affect the system +until explicitly programmed.
+The hardware trigger connections between CTIs and devices is implementation +defined, unless the CPU/ETM combination is a v8 architecture, in which case +the connections have an architecturally defined standard layout.
+The hardware trigger signals can also be connected to non-CoreSight devices +(e.g. UART), or be propagated off chip as hardware IO lines.
+All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by
- a CTM. On these systems a CTM index is declared to associate CTI devices that
- are interconnected via the CTM.
+The CTI devices appear on the existing coresight bus alongside the other
s/coresight/CoreSight
+CoreSight devices.
+# ls /sys/bus/coresight/devices/858000.cti
Since Suzuki's work will be queued for 5.3, the new naming convention should be used.
+channels connections ctmid enable mgmt power regs subsystem uevent
+Key items are: +* enable: enables the CTI. +* ctmid : associated CTM - only relevant if system has multiple CTI+CTM clusters +that are not interconnected.
Why the above two blank lines?
+*connections: sub-dir containing the interconnection information for this CTI. +*channels: sub-dir containing the channel API - CTI main programming interface. +*regs: sub-dir giving access to the raw programmable CTI regs. +*mgmt: sub-dir for the standard CoreSight management registers.
+** Connections: This contains a list of read only data describing the devices +connected to this CTI, and the individual trigger signals to and from the +devices: +./b5b000.cti/connections# ls +0_name 0_trgin_type 0_trgout_type 1_trgin_sig 1_trgout_sig nr_cons +0_trgin_sig 0_trgout_sig 1_name 1_trgin_type 1_trgout_type trigout_filtered
That too will need fixing for V4.
+* nr_cons - number of connections +* trigout_filtered - trigger out signals that are prevented from being set if +filtering is enabled. Prevents accidental edbgreq signals stopping a core.
+Each connection has a set of parameters prefixed with the connection index. +Thus 0_name will show the name of connection 0. +* N_name : name of connection +* N_trgin_sig : input trigger signal indexes used in this connection. +* N_trgin_type : functional types for in signals. +* N_trgout_sig : output trigger signals for this connection. +* N_trgout_type : functional types for out signals.
+e.g. +.../85b000.cti/connections# cat 0_name +cpu3 +.../85b000.cti/connections# cat 0_trgin_sig +0 1 +.../85b000.cti/connections# cat 0_trgin_type +pe_dbgtrigger pe_pmuirq
+If a connection has zero signals in either the in or out triggers then those +parameters will be omitted from the connections sub-dir.
s/sub-dir/sub-directory
+** Channels API : This provides an easy way to attach triggers to channels, +without needing the multiple register operations that are required if +manipluating the 'regs' sub-dir elements directly.
s/sub-dir/sub-directory
+# ls /sys/bus/coresight/devices/859000.cti/channels/ +chan_clear gate_disable list_xtrigs trigin_attach trigout_detach +chan_pulse gate_enable reset_xtrigs trigin_detach +chan_set list_chan_inuse trig_filter_enable trigout_attach
+Most access to these elements take the form: +echo <chan> [<trigger>] > /<device_path>/<operation> +where the optional <trigger> is only needed for trigXX_at|detach operations.
+e.g. +echo 0 1 > /sys/bus/coresight/devices/859000.cti/channels/trigin_attach +attach trig_in(1) to channel(0). +echo 0 > /sys/bus/coresight/devices/859000.cti/channels/chan_set +activate channel(0)
+*gate_enable, gate_disable operations set the CTI gate to propagate +(enable) to other devices. These operation can take a channel number or 'all' +to operate on all channels. CTI gate is enabled by default at power up.
+*list_gate_enable: show the current channels enabled through the gate. +*list_inuse: show the current channels attached to any signal +*list_free: show channels with no attached signals +*show_chan_sel: select the channel for the show_chan_xtrigs operation. +*show_chan_xtrigs: show the cross triggers programmed for the selected channel.
+.../85b000.cti/channels# echo 0 1 > trigin_attach +.../85b000.cti/channels# cat list_free +1 2 3 +.../85b000.cti/channels# cat list_inuse +0 +.../85b000.cti/channels# echo 0 > show_chan_sel +.../85b000.cti/channels# cat show_chan_xtrigs +IN: 1 OUT:
+*trig_filter_enable: defaults to enabled, disable to allow potentially +dangerous output signals to be set.
+*reset_xtrigs: clear all channel / trigger programming - reset device hardware +to default state.
[1]. Documentation/ABI/testing/sysfs-bus-coresight-devices-stm [2]. Documentation/trace/stm.rst [3]. https://github.com/Linaro/perf-opencsd -- 2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Add API usage document for sysfs API in CTI driver.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../testing/sysfs-bus-coresight-devices-cti | 225 ++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-coresight-devices-cti
diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti b/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti new file mode 100644 index 000000000000..4dd3ba94d2b2 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti @@ -0,0 +1,225 @@ +What: /sys/bus/coresight/devices/<cti-name>/enable +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Enable/Disable the CTI hardware. + +What: /sys/bus/coresight/devices/<cti-name>/ctmid +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Display the associated CTM ID + +What: /sys/bus/coresight/devices/<cti-name>/connections/nr_cons +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Number of devices connected to this CTI + +What: /sys/bus/coresight/devices/<cti-name>/connections/<N>_name +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Name of connected device <N> + +What: /sys/bus/coresight/devices/<cti-name>/connections/<N>_trgin_sig +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Input trigger signals from connected device <N> + +What: /sys/bus/coresight/devices/<cti-name>/connections/<N>_trgin_type +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Functional types for the input trigger signals + from connected device <N> + +What: /sys/bus/coresight/devices/<cti-name>/connections/<N>_trgout_sig +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Output trigger signals to connected device <N> + +What: /sys/bus/coresight/devices/<cti-name>/connections/<N>_trgout_type +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Functional types for the output trigger signals + to connected device <N> + +What: /sys/bus/coresight/devices/<cti-name>/regs/inout_sel +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Select the index for inen and outen registers. + +What: /sys/bus/coresight/devices/<cti-name>/regs/inen +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Read or write the CTIINEN register selected by inout_sel. + +What: /sys/bus/coresight/devices/<cti-name>/regs/outen +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Read or write the CTIOUTEN register selected by inout_sel. + +What: /sys/bus/coresight/devices/<cti-name>/regs/gate +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Read or write CTIGATE register. + +What: /sys/bus/coresight/devices/<cti-name>/regs/asicctl +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Read or write ASICCTL register. + +What: /sys/bus/coresight/devices/<cti-name>/regs/intack +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Write the INTACK register. + +What: /sys/bus/coresight/devices/<cti-name>/regs/appset +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Set CTIAPPSET register to activate channel. Read back to + determine current value of register. + +What: /sys/bus/coresight/devices/<cti-name>/regs/appclear +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Write APPCLEAR register to deactivate channel. + +What: /sys/bus/coresight/devices/<cti-name>/regs/apppulse +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Write APPPULSE to pulse a channel active for one clock + cycle. + +What: /sys/bus/coresight/devices/<cti-name>/regs/chinstatus +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Read current status of channel inputs. + +What: /sys/bus/coresight/devices/<cti-name>/regs/choutstatus +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) read current status of channel outputs. + +What: /sys/bus/coresight/devices/<cti-name>/regs/triginstatus +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) read current status of input trigger signals + +What: /sys/bus/coresight/devices/<cti-name>/regs/trigoutstatus +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) read current status of output trigger signals. + +What: /sys/bus/coresight/devices/<cti-name>/channels/trigin_attach +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Attach a CTI input trigger to a CTM channel. + +What: /sys/bus/coresight/devices/<cti-name>/channels/trigin_detach +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Detach a CTI input trigger from a CTM channel. + +What: /sys/bus/coresight/devices/<cti-name>/channels/trigout_attach +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Attach a CTI output trigger to a CTM channel. + +What: /sys/bus/coresight/devices/<cti-name>/channels/trigout_detach +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Detach a CTI output trigger from a CTM channel. + +What: /sys/bus/coresight/devices/<cti-name>/channels/gate_enable +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Enable CTIGATE for single channel or all channels. + +What: /sys/bus/coresight/devices/<cti-name>/channels/gate_disable +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Disable CTIGATE for single channel or all channels. + +What: /sys/bus/coresight/devices/<cti-name>/channels/chan_set +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Activate a single channel. + +What: /sys/bus/coresight/devices/<cti-name>/channels/chan_clear +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Deactivate a single channel. + +What: /sys/bus/coresight/devices/<cti-name>/channels/chan_pulse +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Pulse a single channel - activate for a single clock cycle. + +What: /sys/bus/coresight/devices/<cti-name>/channels/trig_filter_enable +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Enable or disable trigger output signal filtering. + +What: /sys/bus/coresight/devices/<cti-name>/channels/list_gate_ena +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Channels enabled through the gate. + +What: /sys/bus/coresight/devices/<cti-name>/channels/list_inuse +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) show channels with at least one attached trigger signal. + +What: /sys/bus/coresight/devices/<cti-name>/channels/list_free +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) show channels with no attached trigger signals. + +What: /sys/bus/coresight/devices/<cti-name>/channels/show_chan_sel +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Select a channel for the show_chan_xtrigs operation + +What: /sys/bus/coresight/devices/<cti-name>/channels/show_chan_xtrigs +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Show triggers attached to selected channel on this CTI. + +What: /sys/bus/coresight/devices/<cti-name>/channels/reset_xtrigs +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Clear all channel / trigger programming.
On Wed, Jun 05, 2019 at 08:03:11PM +0100, Mike Leach wrote:
Add API usage document for sysfs API in CTI driver.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../testing/sysfs-bus-coresight-devices-cti | 225 ++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-coresight-devices-cti
diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti b/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti new file mode 100644 index 000000000000..4dd3ba94d2b2 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti @@ -0,0 +1,225 @@ +What: /sys/bus/coresight/devices/<cti-name>/enable +Date: Apr 2019 +KernelVersion 5.3
I would like your work to become public during the 5.3 cycle and hopfully queued for 5.4, which will likely show up in September 2019.
+Contact: Mike Leach or Mathieu Poirier +Description: (RW) Enable/Disable the CTI hardware.
+What: /sys/bus/coresight/devices/<cti-name>/ctmid +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Display the associated CTM ID
+What: /sys/bus/coresight/devices/<cti-name>/connections/nr_cons +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Number of devices connected to this CTI
+What: /sys/bus/coresight/devices/<cti-name>/connections/<N>_name +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Name of connected device <N>
+What: /sys/bus/coresight/devices/<cti-name>/connections/<N>_trgin_sig +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Input trigger signals from connected device <N>
+What: /sys/bus/coresight/devices/<cti-name>/connections/<N>_trgin_type +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Functional types for the input trigger signals
from connected device <N>
+What: /sys/bus/coresight/devices/<cti-name>/connections/<N>_trgout_sig +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Output trigger signals to connected device <N>
+What: /sys/bus/coresight/devices/<cti-name>/connections/<N>_trgout_type +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Functional types for the output trigger signals
to connected device <N>
+What: /sys/bus/coresight/devices/<cti-name>/regs/inout_sel +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Select the index for inen and outen registers.
+What: /sys/bus/coresight/devices/<cti-name>/regs/inen +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Read or write the CTIINEN register selected by inout_sel.
+What: /sys/bus/coresight/devices/<cti-name>/regs/outen +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Read or write the CTIOUTEN register selected by inout_sel.
+What: /sys/bus/coresight/devices/<cti-name>/regs/gate +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Read or write CTIGATE register.
+What: /sys/bus/coresight/devices/<cti-name>/regs/asicctl +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Read or write ASICCTL register.
+What: /sys/bus/coresight/devices/<cti-name>/regs/intack +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Write the INTACK register.
+What: /sys/bus/coresight/devices/<cti-name>/regs/appset +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Set CTIAPPSET register to activate channel. Read back to
determine current value of register.
+What: /sys/bus/coresight/devices/<cti-name>/regs/appclear +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Write APPCLEAR register to deactivate channel.
+What: /sys/bus/coresight/devices/<cti-name>/regs/apppulse +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Write APPPULSE to pulse a channel active for one clock
cycle.
+What: /sys/bus/coresight/devices/<cti-name>/regs/chinstatus +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Read current status of channel inputs.
+What: /sys/bus/coresight/devices/<cti-name>/regs/choutstatus +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) read current status of channel outputs.
s/read/Read
+What: /sys/bus/coresight/devices/<cti-name>/regs/triginstatus +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) read current status of input trigger signals
s/read/Read
+What: /sys/bus/coresight/devices/<cti-name>/regs/trigoutstatus +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) read current status of output trigger signals.
s/read/Read
+What: /sys/bus/coresight/devices/<cti-name>/channels/trigin_attach +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Attach a CTI input trigger to a CTM channel.
+What: /sys/bus/coresight/devices/<cti-name>/channels/trigin_detach +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Detach a CTI input trigger from a CTM channel.
+What: /sys/bus/coresight/devices/<cti-name>/channels/trigout_attach +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Attach a CTI output trigger to a CTM channel.
+What: /sys/bus/coresight/devices/<cti-name>/channels/trigout_detach +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Detach a CTI output trigger from a CTM channel.
+What: /sys/bus/coresight/devices/<cti-name>/channels/gate_enable +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Enable CTIGATE for single channel or all channels.
+What: /sys/bus/coresight/devices/<cti-name>/channels/gate_disable +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Disable CTIGATE for single channel or all channels.
+What: /sys/bus/coresight/devices/<cti-name>/channels/chan_set +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Activate a single channel.
+What: /sys/bus/coresight/devices/<cti-name>/channels/chan_clear +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Deactivate a single channel.
+What: /sys/bus/coresight/devices/<cti-name>/channels/chan_pulse +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (W) Pulse a single channel - activate for a single clock cycle.
+What: /sys/bus/coresight/devices/<cti-name>/channels/trig_filter_enable +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Enable or disable trigger output signal filtering.
+What: /sys/bus/coresight/devices/<cti-name>/channels/list_gate_ena +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Channels enabled through the gate.
+What: /sys/bus/coresight/devices/<cti-name>/channels/list_inuse +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) show channels with at least one attached trigger signal.
s/show/Show
+What: /sys/bus/coresight/devices/<cti-name>/channels/list_free +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) show channels with no attached trigger signals.
s/show/Show
+What: /sys/bus/coresight/devices/<cti-name>/channels/show_chan_sel +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (RW) Select a channel for the show_chan_xtrigs operation
+What: /sys/bus/coresight/devices/<cti-name>/channels/show_chan_xtrigs +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier +Description: (R) Show triggers attached to selected channel on this CTI.
+What: /sys/bus/coresight/devices/<cti-name>/channels/reset_xtrigs +Date: Apr 2019 +KernelVersion 5.3 +Contact: Mike Leach or Mathieu Poirier
+Description: (W) Clear all channel / trigger programming.
2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Currently each driver type sets up own CPU power notifications such as CPU hotplug etc. Need a common mechanism for all driver types to use.
Adds a notification mechanism for CPU power events such as CPU hotplug as a general notification mechnism in CoreSight common code to be used by all CPU bound CS devices.
Provides a per-device registration mechanism with callback fn and event type notifier. Core code now manages the CPU hotplug registrations and calls back any registered device when events occur.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/coresight-priv.h | 26 +++ drivers/hwtracing/coresight/coresight.c | 167 ++++++++++++++++++- 2 files changed, 192 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 9c4fd7eb56eb..5ea2f86f71e0 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -214,4 +214,30 @@ void coresight_release_platform_data(struct coresight_platform_data *pdata);
int coresight_device_fwnode_match(struct device *dev, void *fwnode);
+/* + * cpu power management - allow any CPU bound Coresight device + * to use a common registration infrastructure for notifications. + */ + +/* cpu pm event types */ +enum cs_cpu_pm_events { + CS_CPUPM_SMP_ON_REG_CPU, /* smp call on cpu at registration */ + CS_CPUPM_CPUHP_CS_START, /* hotplug CoreSight starting CPU */ + CS_CPUPM_CPUHP_CS_STOP, /* hotplug CoreSight stopping CPU */ + CS_CPUPM_CPUHP_AP_DYN_ONLINE, /* hotplug AP dynamic online CPU */ + CS_CPUPM_CPUHP_AP_DYN_OFFLINE, /* hotplug AP dynamic offline CPU */ +}; + +/* callback function type */ +typedef int (*p_cs_cpu_pm_event)(struct coresight_device *csdev, + unsigned int cpuid, + enum cs_cpu_pm_events event); + +/* registration functions */ +int coresight_cpupm_register(struct coresight_device *csdev, + unsigned int cpuid, + p_cs_cpu_pm_event callback); +void coresight_cpupm_unregister(struct coresight_device *csdev, + unsigned int cpuid); + #endif diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 74661fabe098..509fc0b8329d 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1347,7 +1347,6 @@ void coresight_unregister(struct coresight_device *csdev) } EXPORT_SYMBOL_GPL(coresight_unregister);
- /* * coresight_search_device_idx - Search the fwnode handle of a device * in the given dev_idx list. Must be called with the coresight_mutex held. @@ -1404,3 +1403,169 @@ char *coresight_alloc_device_name(struct coresight_dev_list *dict, return name; } EXPORT_SYMBOL_GPL(coresight_alloc_device_name); + +/* cs general CPU bound device pm notification */ +struct coresight_cpupm_item { + struct coresight_device *csdev; + p_cs_cpu_pm_event callback; + struct list_head node; +}; + +/* CS cpu pm notifier lists per cpu */ +static DEFINE_PER_CPU(struct list_head *, cs_cpupm_notifier); + +static enum cpuhp_state cscpu_hp_online; +static int cscpu_dev_reg_cnt; + +static int coresight_iterate_cpu_event_list(unsigned int cpu, + enum cs_cpu_pm_events event) +{ + struct list_head *cpu_list; + struct coresight_cpupm_item *item; + + mutex_lock(&coresight_mutex); + cpu_list = per_cpu(cs_cpupm_notifier, cpu); + if (!list_empty(cpu_list)) { + list_for_each_entry(item, cpu_list, node) { + item->callback(item->csdev, cpu, event); + } + } + mutex_unlock(&coresight_mutex); + return 0; +} + +static int coresight_hp_starting_cpu(unsigned int cpu) +{ + return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_CS_START); +} + +static int coresight_hp_stopping_cpu(unsigned int cpu) +{ + return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_CS_STOP); +} + +static int coresight_hp_dyn_online_cpu(unsigned int cpu) +{ + return coresight_iterate_cpu_event_list(cpu, + CS_CPUPM_CPUHP_AP_DYN_ONLINE); +} + +static int coresight_hp_dyn_offline_cpu(unsigned int cpu) +{ + return coresight_iterate_cpu_event_list(cpu, + CS_CPUPM_CPUHP_AP_DYN_OFFLINE); +} + +struct cs_smp_reg_cb_info { + unsigned int cpu; + struct coresight_cpupm_item *item; +}; + +static void coresight_smp_onreg_callback(void *info) +{ + struct cs_smp_reg_cb_info *cb_info = info; + + cb_info->item->callback(cb_info->item->csdev, cb_info->cpu, + CS_CPUPM_SMP_ON_REG_CPU); +} + +int coresight_cpupm_register(struct coresight_device *csdev, + unsigned int cpuid, + p_cs_cpu_pm_event callback) +{ + struct list_head *cpu_list; + struct coresight_cpupm_item *item; + int err = 0; + struct cs_smp_reg_cb_info on_reg_cb_info; + + mutex_lock(&coresight_mutex); + + cpu_list = per_cpu(cs_cpupm_notifier, cpuid); + + /* init list if no head yet */ + if (!cpu_list) { + cpu_list = kzalloc(sizeof(struct list_head), GFP_KERNEL); + if (!cpu_list) + goto cpupm_reg_done; + else + INIT_LIST_HEAD(cpu_list); + } + + /* create a list item */ + item = kzalloc(sizeof(struct coresight_cpupm_item), GFP_KERNEL); + if (!item) { + err = -ENOMEM; + goto cpupm_reg_done; + } + + item->csdev = csdev; + item->callback = callback; + + /* init the cpu hotplug notification */ + cpus_read_lock(); + if (!cscpu_dev_reg_cnt) { + cpuhp_setup_state_nocalls_cpuslocked( + CPUHP_AP_ARM_CORESIGHT_STARTING, + "arm/coresightcpu:starting", + coresight_hp_starting_cpu, + coresight_hp_stopping_cpu); + + cscpu_hp_online = cpuhp_setup_state_nocalls_cpuslocked( + CPUHP_AP_ONLINE_DYN, "arm/coresightcpu:online", + coresight_hp_dyn_online_cpu, + coresight_hp_dyn_offline_cpu); + + if (cscpu_hp_online < 0) { + err = cscpu_hp_online; + cpuhp_remove_state_nocalls( + CPUHP_AP_ARM_CORESIGHT_STARTING); + kfree(item); + cscpu_hp_online = 0; + goto cpupm_hp_done; + } + } + cscpu_dev_reg_cnt++; + list_add(&item->node, cpu_list); + + /* smp callback on dev cpu @ registration */ + on_reg_cb_info.item = item; + on_reg_cb_info.cpu = cpuid; + smp_call_function_single(cpuid, coresight_smp_onreg_callback, + &on_reg_cb_info, 1); + +cpupm_hp_done: + cpus_read_unlock(); + +cpupm_reg_done: + mutex_unlock(&coresight_mutex); + return err; +} +EXPORT_SYMBOL_GPL(coresight_cpupm_register); + +void coresight_cpupm_unregister(struct coresight_device *csdev, + unsigned int cpuid) +{ + struct list_head *cpu_list; + struct coresight_cpupm_item *item, *tmp; + + mutex_lock(&coresight_mutex); + cpu_list = per_cpu(cs_cpupm_notifier, cpuid); + if (!list_empty(cpu_list)) { + list_for_each_entry_safe(item, tmp, cpu_list, node) { + if (item->csdev == csdev) { + list_del(&item->node); + cscpu_dev_reg_cnt--; + kfree(item); + goto cs_cpupm_item_removed; + } + } + } +cs_cpupm_item_removed: + if (!cscpu_dev_reg_cnt) { + cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); + if (cscpu_hp_online) + cpuhp_remove_state_nocalls(cscpu_hp_online); + } + mutex_unlock(&coresight_mutex); +} +EXPORT_SYMBOL_GPL(coresight_cpupm_unregister);
Hi,
On Wed, Jun 05, 2019 at 08:03:12PM +0100, Mike Leach wrote:
Currently each driver type sets up own CPU power notifications such as CPU hotplug etc. Need a common mechanism for all driver types to use.
Adds a notification mechanism for CPU power events such as CPU hotplug as a general notification mechnism in CoreSight common code to be used by all CPU bound CS devices.
Provides a per-device registration mechanism with callback fn and event type notifier. Core code now manages the CPU hotplug registrations and calls back any registered device when events occur.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-priv.h | 26 +++ drivers/hwtracing/coresight/coresight.c | 167 ++++++++++++++++++- 2 files changed, 192 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 9c4fd7eb56eb..5ea2f86f71e0 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -214,4 +214,30 @@ void coresight_release_platform_data(struct coresight_platform_data *pdata); int coresight_device_fwnode_match(struct device *dev, void *fwnode); +/*
- cpu power management - allow any CPU bound Coresight device
- to use a common registration infrastructure for notifications.
- */
+/* cpu pm event types */ +enum cs_cpu_pm_events {
- CS_CPUPM_SMP_ON_REG_CPU, /* smp call on cpu at registration */
- CS_CPUPM_CPUHP_CS_START, /* hotplug CoreSight starting CPU */
- CS_CPUPM_CPUHP_CS_STOP, /* hotplug CoreSight stopping CPU */
- CS_CPUPM_CPUHP_AP_DYN_ONLINE, /* hotplug AP dynamic online CPU */
- CS_CPUPM_CPUHP_AP_DYN_OFFLINE, /* hotplug AP dynamic offline CPU */
+};
+/* callback function type */ +typedef int (*p_cs_cpu_pm_event)(struct coresight_device *csdev,
unsigned int cpuid,
enum cs_cpu_pm_events event);
+/* registration functions */ +int coresight_cpupm_register(struct coresight_device *csdev,
unsigned int cpuid,
p_cs_cpu_pm_event callback);
+void coresight_cpupm_unregister(struct coresight_device *csdev,
unsigned int cpuid);
#endif diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 74661fabe098..509fc0b8329d 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1347,7 +1347,6 @@ void coresight_unregister(struct coresight_device *csdev) } EXPORT_SYMBOL_GPL(coresight_unregister);
/*
- coresight_search_device_idx - Search the fwnode handle of a device
- in the given dev_idx list. Must be called with the coresight_mutex held.
@@ -1404,3 +1403,169 @@ char *coresight_alloc_device_name(struct coresight_dev_list *dict, return name; } EXPORT_SYMBOL_GPL(coresight_alloc_device_name);
+/* cs general CPU bound device pm notification */ +struct coresight_cpupm_item {
- struct coresight_device *csdev;
- p_cs_cpu_pm_event callback;
- struct list_head node;
+};
+/* CS cpu pm notifier lists per cpu */ +static DEFINE_PER_CPU(struct list_head *, cs_cpupm_notifier);
+static enum cpuhp_state cscpu_hp_online; +static int cscpu_dev_reg_cnt;
I do not really see the advantage of adding all this code. To me it is like shadowing an infrastructure the CPUhotplug subsystem is alreading giving us. And looking at function cti_cs_cpupm_event() in the next patch the code to handle 3 events is grouped into a single function. To me we are not gaining anything other than replicating code that already exists.
To me it is much easier to have a percpu instance of the cti_drvdata (I think Suzuki already hinted at that in his review) and access the right one using the CPU parameter provided by the individual callbacks.
Let me know if the above is not handling a scenario that this patch is.
+static int coresight_iterate_cpu_event_list(unsigned int cpu,
enum cs_cpu_pm_events event)
+{
- struct list_head *cpu_list;
- struct coresight_cpupm_item *item;
- mutex_lock(&coresight_mutex);
- cpu_list = per_cpu(cs_cpupm_notifier, cpu);
- if (!list_empty(cpu_list)) {
list_for_each_entry(item, cpu_list, node) {
item->callback(item->csdev, cpu, event);
}
- }
- mutex_unlock(&coresight_mutex);
- return 0;
+}
+static int coresight_hp_starting_cpu(unsigned int cpu) +{
- return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_CS_START);
+}
+static int coresight_hp_stopping_cpu(unsigned int cpu) +{
- return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_CS_STOP);
+}
+static int coresight_hp_dyn_online_cpu(unsigned int cpu) +{
- return coresight_iterate_cpu_event_list(cpu,
CS_CPUPM_CPUHP_AP_DYN_ONLINE);
+}
+static int coresight_hp_dyn_offline_cpu(unsigned int cpu) +{
- return coresight_iterate_cpu_event_list(cpu,
CS_CPUPM_CPUHP_AP_DYN_OFFLINE);
+}
+struct cs_smp_reg_cb_info {
- unsigned int cpu;
- struct coresight_cpupm_item *item;
+};
+static void coresight_smp_onreg_callback(void *info) +{
- struct cs_smp_reg_cb_info *cb_info = info;
- cb_info->item->callback(cb_info->item->csdev, cb_info->cpu,
CS_CPUPM_SMP_ON_REG_CPU);
+}
+int coresight_cpupm_register(struct coresight_device *csdev,
unsigned int cpuid,
p_cs_cpu_pm_event callback)
+{
- struct list_head *cpu_list;
- struct coresight_cpupm_item *item;
- int err = 0;
- struct cs_smp_reg_cb_info on_reg_cb_info;
- mutex_lock(&coresight_mutex);
- cpu_list = per_cpu(cs_cpupm_notifier, cpuid);
- /* init list if no head yet */
- if (!cpu_list) {
cpu_list = kzalloc(sizeof(struct list_head), GFP_KERNEL);
if (!cpu_list)
goto cpupm_reg_done;
else
INIT_LIST_HEAD(cpu_list);
- }
- /* create a list item */
- item = kzalloc(sizeof(struct coresight_cpupm_item), GFP_KERNEL);
- if (!item) {
err = -ENOMEM;
goto cpupm_reg_done;
- }
- item->csdev = csdev;
- item->callback = callback;
- /* init the cpu hotplug notification */
- cpus_read_lock();
- if (!cscpu_dev_reg_cnt) {
cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ARM_CORESIGHT_STARTING,
"arm/coresightcpu:starting",
coresight_hp_starting_cpu,
coresight_hp_stopping_cpu);
cscpu_hp_online = cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ONLINE_DYN, "arm/coresightcpu:online",
coresight_hp_dyn_online_cpu,
coresight_hp_dyn_offline_cpu);
if (cscpu_hp_online < 0) {
err = cscpu_hp_online;
cpuhp_remove_state_nocalls(
CPUHP_AP_ARM_CORESIGHT_STARTING);
kfree(item);
cscpu_hp_online = 0;
goto cpupm_hp_done;
}
- }
- cscpu_dev_reg_cnt++;
- list_add(&item->node, cpu_list);
- /* smp callback on dev cpu @ registration */
- on_reg_cb_info.item = item;
- on_reg_cb_info.cpu = cpuid;
- smp_call_function_single(cpuid, coresight_smp_onreg_callback,
&on_reg_cb_info, 1);
+cpupm_hp_done:
- cpus_read_unlock();
+cpupm_reg_done:
- mutex_unlock(&coresight_mutex);
- return err;
+} +EXPORT_SYMBOL_GPL(coresight_cpupm_register);
+void coresight_cpupm_unregister(struct coresight_device *csdev,
unsigned int cpuid)
+{
- struct list_head *cpu_list;
- struct coresight_cpupm_item *item, *tmp;
- mutex_lock(&coresight_mutex);
- cpu_list = per_cpu(cs_cpupm_notifier, cpuid);
- if (!list_empty(cpu_list)) {
list_for_each_entry_safe(item, tmp, cpu_list, node) {
if (item->csdev == csdev) {
list_del(&item->node);
cscpu_dev_reg_cnt--;
kfree(item);
goto cs_cpupm_item_removed;
}
}
- }
+cs_cpupm_item_removed:
- if (!cscpu_dev_reg_cnt) {
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
if (cscpu_hp_online)
cpuhp_remove_state_nocalls(cscpu_hp_online);
- }
- mutex_unlock(&coresight_mutex);
+}
+EXPORT_SYMBOL_GPL(coresight_cpupm_unregister);
2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
On 18/06/2019 21:16, Mathieu Poirier wrote:
Hi,
On Wed, Jun 05, 2019 at 08:03:12PM +0100, Mike Leach wrote:
Currently each driver type sets up own CPU power notifications such as CPU hotplug etc. Need a common mechanism for all driver types to use.
Adds a notification mechanism for CPU power events such as CPU hotplug as a general notification mechnism in CoreSight common code to be used by all CPU bound CS devices.
Provides a per-device registration mechanism with callback fn and event type notifier. Core code now manages the CPU hotplug registrations and calls back any registered device when events occur.
Signed-off-by: Mike Leach mike.leach@linaro.org
/*
- coresight_search_device_idx - Search the fwnode handle of a device
- in the given dev_idx list. Must be called with the coresight_mutex held.
@@ -1404,3 +1403,169 @@ char *coresight_alloc_device_name(struct coresight_dev_list *dict, return name; } EXPORT_SYMBOL_GPL(coresight_alloc_device_name);
+/* cs general CPU bound device pm notification */ +struct coresight_cpupm_item {
- struct coresight_device *csdev;
- p_cs_cpu_pm_event callback;
- struct list_head node;
+};
+/* CS cpu pm notifier lists per cpu */ +static DEFINE_PER_CPU(struct list_head *, cs_cpupm_notifier);
+static enum cpuhp_state cscpu_hp_online; +static int cscpu_dev_reg_cnt;
I do not really see the advantage of adding all this code. To me it is like shadowing an infrastructure the CPUhotplug subsystem is alreading giving us. And looking at function cti_cs_cpupm_event() in the next patch the code to handle 3 events is grouped into a single function. To me we are not gaining anything other than replicating code that already exists.
Exactly what I think. We are adding another layer behind already existing layers, with coresight specific event names, not worth the overhead.
To me it is much easier to have a percpu instance of the cti_drvdata (I think Suzuki already hinted at that in his review) and access the right one using the CPU parameter provided by the individual callbacks.
Cheers Suzuki
Hi Mathieu,
I'm working through the other responses from you and suzuki, but this seems worth taking out of order...
On Tue, 18 Jun 2019 at 21:16, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Hi,
On Wed, Jun 05, 2019 at 08:03:12PM +0100, Mike Leach wrote:
Currently each driver type sets up own CPU power notifications such as CPU hotplug etc. Need a common mechanism for all driver types to use.
Adds a notification mechanism for CPU power events such as CPU hotplug as a general notification mechnism in CoreSight common code to be used by all CPU bound CS devices.
Provides a per-device registration mechanism with callback fn and event type notifier. Core code now manages the CPU hotplug registrations and calls back any registered device when events occur.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-priv.h | 26 +++ drivers/hwtracing/coresight/coresight.c | 167 ++++++++++++++++++- 2 files changed, 192 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 9c4fd7eb56eb..5ea2f86f71e0 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -214,4 +214,30 @@ void coresight_release_platform_data(struct coresight_platform_data *pdata);
int coresight_device_fwnode_match(struct device *dev, void *fwnode);
+/*
- cpu power management - allow any CPU bound Coresight device
- to use a common registration infrastructure for notifications.
- */
+/* cpu pm event types */ +enum cs_cpu_pm_events {
CS_CPUPM_SMP_ON_REG_CPU, /* smp call on cpu at registration */
CS_CPUPM_CPUHP_CS_START, /* hotplug CoreSight starting CPU */
CS_CPUPM_CPUHP_CS_STOP, /* hotplug CoreSight stopping CPU */
CS_CPUPM_CPUHP_AP_DYN_ONLINE, /* hotplug AP dynamic online CPU */
CS_CPUPM_CPUHP_AP_DYN_OFFLINE, /* hotplug AP dynamic offline CPU */
+};
+/* callback function type */ +typedef int (*p_cs_cpu_pm_event)(struct coresight_device *csdev,
unsigned int cpuid,
enum cs_cpu_pm_events event);
+/* registration functions */ +int coresight_cpupm_register(struct coresight_device *csdev,
unsigned int cpuid,
p_cs_cpu_pm_event callback);
+void coresight_cpupm_unregister(struct coresight_device *csdev,
unsigned int cpuid);
#endif diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 74661fabe098..509fc0b8329d 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1347,7 +1347,6 @@ void coresight_unregister(struct coresight_device *csdev) } EXPORT_SYMBOL_GPL(coresight_unregister);
/*
- coresight_search_device_idx - Search the fwnode handle of a device
- in the given dev_idx list. Must be called with the coresight_mutex held.
@@ -1404,3 +1403,169 @@ char *coresight_alloc_device_name(struct coresight_dev_list *dict, return name; } EXPORT_SYMBOL_GPL(coresight_alloc_device_name);
+/* cs general CPU bound device pm notification */ +struct coresight_cpupm_item {
struct coresight_device *csdev;
p_cs_cpu_pm_event callback;
struct list_head node;
+};
+/* CS cpu pm notifier lists per cpu */ +static DEFINE_PER_CPU(struct list_head *, cs_cpupm_notifier);
+static enum cpuhp_state cscpu_hp_online; +static int cscpu_dev_reg_cnt;
I do not really see the advantage of adding all this code. To me it is like shadowing an infrastructure the CPUhotplug subsystem is alreading giving us. And looking at function cti_cs_cpupm_event() in the next patch the code to handle 3 events is grouped into a single function. To me we are not gaining anything other than replicating code that already exists.
To me it is much easier to have a percpu instance of the cti_drvdata (I think Suzuki already hinted at that in his review) and access the right one using the CPU parameter provided by the individual callbacks.
Let me know if the above is not handling a scenario that this patch is.
My initial thoughts were:- 1) this code appears both in the ETMv3 driver and ETMv4 driver. I could add it to the CTI driver but thats just more maintenance. Seems better design for common code to go into the coresight core.
2) this patches adds CPU hotplug. I can also add CPUidle to the same infrastructure code - with a small change in the core code and the addition of 3 lines in the CTI driver CPUIdle power states are covered too. I have a test branch that does just that. As the CTI driver is designed from the ground up to respect the power states while programming this is needed for testing.
Then I looked deeper into the issue... 3) As I looked further into this, the current ETM drivers use the cpuhp_setup_state_nocalls_cpuslocked() call - this sets a single instance callback state. So any attempt to register a second set of callbacks fails - either with -EBUSY if you try to register callbacks with a different name, -EINVAL if you use name as NULL. Now the reason for the -EINVAL is that it is forbidden to register a second instance on a single instance callback. Now looking at the CPU hotplug call, there is a multi-instance variant - but this only allows for the registration of one set of callback functions, which have a 'node' parameter, in addition to the CPU. The first instance to register can provide the callback functions, the remaining instances register using a different call - add_instance() which just provides a 'node' object (actually something with a hlist struct). So using the multi-instance variant, the callback functions have to be in the coresight core code anyway, and we need code in there to figure out which node and which driver to notify - there is no way that the ETM driver can find the correct callback into the CTI driver, or vice versa.
I will admit I discovered (3) only after writing this patch - in my view 1 & 2 were pretty strong reasons - but now it seems it is required to allow the CPU bound drivers to get notified. I was of the view that this common code could be added and additional patches could fix up the ETMv3/ETMv4 drivers.
At this point, I think that the best way to solve this is to split off the CPU hotplug updates to the CTI driver, and issue two patchsets. 1st - a baseline CTI driver without any of the hotplug handling. 2nd - a CPU hotplug fixup patchset, that adds common HP handling and fixes up ETMv3 and ETMv4 and adds to CTI in the same set. This is necessary as without removing the HP registration from the two ETM drivers, the common code will fail when invoked in the CTI driver. (interestingly, neither of the ETM drivers, nor my initial patch looked at the return code from cpuhp_setup_state_nocalls_cpuslocked() - when that is checked these issues become very apparent).
Thanks and Regards
Mike
+static int coresight_iterate_cpu_event_list(unsigned int cpu,
enum cs_cpu_pm_events event)
+{
struct list_head *cpu_list;
struct coresight_cpupm_item *item;
mutex_lock(&coresight_mutex);
cpu_list = per_cpu(cs_cpupm_notifier, cpu);
if (!list_empty(cpu_list)) {
list_for_each_entry(item, cpu_list, node) {
item->callback(item->csdev, cpu, event);
}
}
mutex_unlock(&coresight_mutex);
return 0;
+}
+static int coresight_hp_starting_cpu(unsigned int cpu) +{
return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_CS_START);
+}
+static int coresight_hp_stopping_cpu(unsigned int cpu) +{
return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_CS_STOP);
+}
+static int coresight_hp_dyn_online_cpu(unsigned int cpu) +{
return coresight_iterate_cpu_event_list(cpu,
CS_CPUPM_CPUHP_AP_DYN_ONLINE);
+}
+static int coresight_hp_dyn_offline_cpu(unsigned int cpu) +{
return coresight_iterate_cpu_event_list(cpu,
CS_CPUPM_CPUHP_AP_DYN_OFFLINE);
+}
+struct cs_smp_reg_cb_info {
unsigned int cpu;
struct coresight_cpupm_item *item;
+};
+static void coresight_smp_onreg_callback(void *info) +{
struct cs_smp_reg_cb_info *cb_info = info;
cb_info->item->callback(cb_info->item->csdev, cb_info->cpu,
CS_CPUPM_SMP_ON_REG_CPU);
+}
+int coresight_cpupm_register(struct coresight_device *csdev,
unsigned int cpuid,
p_cs_cpu_pm_event callback)
+{
struct list_head *cpu_list;
struct coresight_cpupm_item *item;
int err = 0;
struct cs_smp_reg_cb_info on_reg_cb_info;
mutex_lock(&coresight_mutex);
cpu_list = per_cpu(cs_cpupm_notifier, cpuid);
/* init list if no head yet */
if (!cpu_list) {
cpu_list = kzalloc(sizeof(struct list_head), GFP_KERNEL);
if (!cpu_list)
goto cpupm_reg_done;
else
INIT_LIST_HEAD(cpu_list);
}
/* create a list item */
item = kzalloc(sizeof(struct coresight_cpupm_item), GFP_KERNEL);
if (!item) {
err = -ENOMEM;
goto cpupm_reg_done;
}
item->csdev = csdev;
item->callback = callback;
/* init the cpu hotplug notification */
cpus_read_lock();
if (!cscpu_dev_reg_cnt) {
cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ARM_CORESIGHT_STARTING,
"arm/coresightcpu:starting",
coresight_hp_starting_cpu,
coresight_hp_stopping_cpu);
cscpu_hp_online = cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ONLINE_DYN, "arm/coresightcpu:online",
coresight_hp_dyn_online_cpu,
coresight_hp_dyn_offline_cpu);
if (cscpu_hp_online < 0) {
err = cscpu_hp_online;
cpuhp_remove_state_nocalls(
CPUHP_AP_ARM_CORESIGHT_STARTING);
kfree(item);
cscpu_hp_online = 0;
goto cpupm_hp_done;
}
}
cscpu_dev_reg_cnt++;
list_add(&item->node, cpu_list);
/* smp callback on dev cpu @ registration */
on_reg_cb_info.item = item;
on_reg_cb_info.cpu = cpuid;
smp_call_function_single(cpuid, coresight_smp_onreg_callback,
&on_reg_cb_info, 1);
+cpupm_hp_done:
cpus_read_unlock();
+cpupm_reg_done:
mutex_unlock(&coresight_mutex);
return err;
+} +EXPORT_SYMBOL_GPL(coresight_cpupm_register);
+void coresight_cpupm_unregister(struct coresight_device *csdev,
unsigned int cpuid)
+{
struct list_head *cpu_list;
struct coresight_cpupm_item *item, *tmp;
mutex_lock(&coresight_mutex);
cpu_list = per_cpu(cs_cpupm_notifier, cpuid);
if (!list_empty(cpu_list)) {
list_for_each_entry_safe(item, tmp, cpu_list, node) {
if (item->csdev == csdev) {
list_del(&item->node);
cscpu_dev_reg_cnt--;
kfree(item);
goto cs_cpupm_item_removed;
}
}
}
+cs_cpupm_item_removed:
if (!cscpu_dev_reg_cnt) {
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
if (cscpu_hp_online)
cpuhp_remove_state_nocalls(cscpu_hp_online);
}
mutex_unlock(&coresight_mutex);
+}
+EXPORT_SYMBOL_GPL(coresight_cpupm_unregister);
2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
On Mon, 24 Jun 2019 at 06:39, Mike Leach mike.leach@linaro.org wrote:
Hi Mathieu,
I'm working through the other responses from you and suzuki, but this seems worth taking out of order...
On Tue, 18 Jun 2019 at 21:16, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Hi,
On Wed, Jun 05, 2019 at 08:03:12PM +0100, Mike Leach wrote:
Currently each driver type sets up own CPU power notifications such as CPU hotplug etc. Need a common mechanism for all driver types to use.
Adds a notification mechanism for CPU power events such as CPU hotplug as a general notification mechnism in CoreSight common code to be used by all CPU bound CS devices.
Provides a per-device registration mechanism with callback fn and event type notifier. Core code now manages the CPU hotplug registrations and calls back any registered device when events occur.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-priv.h | 26 +++ drivers/hwtracing/coresight/coresight.c | 167 ++++++++++++++++++- 2 files changed, 192 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 9c4fd7eb56eb..5ea2f86f71e0 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -214,4 +214,30 @@ void coresight_release_platform_data(struct coresight_platform_data *pdata);
int coresight_device_fwnode_match(struct device *dev, void *fwnode);
+/*
- cpu power management - allow any CPU bound Coresight device
- to use a common registration infrastructure for notifications.
- */
+/* cpu pm event types */ +enum cs_cpu_pm_events {
CS_CPUPM_SMP_ON_REG_CPU, /* smp call on cpu at registration */
CS_CPUPM_CPUHP_CS_START, /* hotplug CoreSight starting CPU */
CS_CPUPM_CPUHP_CS_STOP, /* hotplug CoreSight stopping CPU */
CS_CPUPM_CPUHP_AP_DYN_ONLINE, /* hotplug AP dynamic online CPU */
CS_CPUPM_CPUHP_AP_DYN_OFFLINE, /* hotplug AP dynamic offline CPU */
+};
+/* callback function type */ +typedef int (*p_cs_cpu_pm_event)(struct coresight_device *csdev,
unsigned int cpuid,
enum cs_cpu_pm_events event);
+/* registration functions */ +int coresight_cpupm_register(struct coresight_device *csdev,
unsigned int cpuid,
p_cs_cpu_pm_event callback);
+void coresight_cpupm_unregister(struct coresight_device *csdev,
unsigned int cpuid);
#endif diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 74661fabe098..509fc0b8329d 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1347,7 +1347,6 @@ void coresight_unregister(struct coresight_device *csdev) } EXPORT_SYMBOL_GPL(coresight_unregister);
/*
- coresight_search_device_idx - Search the fwnode handle of a device
- in the given dev_idx list. Must be called with the coresight_mutex held.
@@ -1404,3 +1403,169 @@ char *coresight_alloc_device_name(struct coresight_dev_list *dict, return name; } EXPORT_SYMBOL_GPL(coresight_alloc_device_name);
+/* cs general CPU bound device pm notification */ +struct coresight_cpupm_item {
struct coresight_device *csdev;
p_cs_cpu_pm_event callback;
struct list_head node;
+};
+/* CS cpu pm notifier lists per cpu */ +static DEFINE_PER_CPU(struct list_head *, cs_cpupm_notifier);
+static enum cpuhp_state cscpu_hp_online; +static int cscpu_dev_reg_cnt;
I do not really see the advantage of adding all this code. To me it is like shadowing an infrastructure the CPUhotplug subsystem is alreading giving us. And looking at function cti_cs_cpupm_event() in the next patch the code to handle 3 events is grouped into a single function. To me we are not gaining anything other than replicating code that already exists.
To me it is much easier to have a percpu instance of the cti_drvdata (I think Suzuki already hinted at that in his review) and access the right one using the CPU parameter provided by the individual callbacks.
Let me know if the above is not handling a scenario that this patch is.
My initial thoughts were:-
- this code appears both in the ETMv3 driver and ETMv4 driver. I
could add it to the CTI driver but thats just more maintenance. Seems better design for common code to go into the coresight core.
- this patches adds CPU hotplug. I can also add CPUidle to the same
infrastructure code - with a small change in the core code and the addition of 3 lines in the CTI driver CPUIdle power states are covered too. I have a test branch that does just that. As the CTI driver is designed from the ground up to respect the power states while programming this is needed for testing.
Then I looked deeper into the issue... 3) As I looked further into this, the current ETM drivers use the cpuhp_setup_state_nocalls_cpuslocked() call - this sets a single instance callback state. So any attempt to register a second set of callbacks fails - either with -EBUSY if you try to register callbacks with a different name, -EINVAL if you use name as NULL. Now the reason for the -EINVAL is that it is forbidden to register a second instance on a single instance callback. Now looking at the CPU hotplug call, there is a multi-instance variant
- but this only allows for the registration of one set of callback
functions, which have a 'node' parameter, in addition to the CPU. The first instance to register can provide the callback functions, the remaining instances register using a different call - add_instance() which just provides a 'node' object (actually something with a hlist struct). So using the multi-instance variant, the callback functions have to be in the coresight core code anyway, and we need code in there to figure out which node and which driver to notify - there is no way that the ETM driver can find the correct callback into the CTI driver, or vice versa.
I will admit I discovered (3) only after writing this patch - in my view 1 & 2 were pretty strong reasons - but now it seems it is required to allow the CPU bound drivers to get notified. I was of the view that this common code could be added and additional patches could fix up the ETMv3/ETMv4 drivers.
At this point, I think that the best way to solve this is to split off the CPU hotplug updates to the CTI driver, and issue two patchsets. 1st - a baseline CTI driver without any of the hotplug handling. 2nd - a CPU hotplug fixup patchset, that adds common HP handling and fixes up ETMv3 and ETMv4 and adds to CTI in the same set. This is necessary as without removing the HP registration from the two ETM drivers, the common code will fail when invoked in the CTI driver. (interestingly, neither of the ETM drivers, nor my initial patch looked at the return code from cpuhp_setup_state_nocalls_cpuslocked()
- when that is checked these issues become very apparent).
Very interesting - I haven't had a chance to look into this today but will try tomorrow.
Thanks and Regards
Mike
+static int coresight_iterate_cpu_event_list(unsigned int cpu,
enum cs_cpu_pm_events event)
+{
struct list_head *cpu_list;
struct coresight_cpupm_item *item;
mutex_lock(&coresight_mutex);
cpu_list = per_cpu(cs_cpupm_notifier, cpu);
if (!list_empty(cpu_list)) {
list_for_each_entry(item, cpu_list, node) {
item->callback(item->csdev, cpu, event);
}
}
mutex_unlock(&coresight_mutex);
return 0;
+}
+static int coresight_hp_starting_cpu(unsigned int cpu) +{
return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_CS_START);
+}
+static int coresight_hp_stopping_cpu(unsigned int cpu) +{
return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_CS_STOP);
+}
+static int coresight_hp_dyn_online_cpu(unsigned int cpu) +{
return coresight_iterate_cpu_event_list(cpu,
CS_CPUPM_CPUHP_AP_DYN_ONLINE);
+}
+static int coresight_hp_dyn_offline_cpu(unsigned int cpu) +{
return coresight_iterate_cpu_event_list(cpu,
CS_CPUPM_CPUHP_AP_DYN_OFFLINE);
+}
+struct cs_smp_reg_cb_info {
unsigned int cpu;
struct coresight_cpupm_item *item;
+};
+static void coresight_smp_onreg_callback(void *info) +{
struct cs_smp_reg_cb_info *cb_info = info;
cb_info->item->callback(cb_info->item->csdev, cb_info->cpu,
CS_CPUPM_SMP_ON_REG_CPU);
+}
+int coresight_cpupm_register(struct coresight_device *csdev,
unsigned int cpuid,
p_cs_cpu_pm_event callback)
+{
struct list_head *cpu_list;
struct coresight_cpupm_item *item;
int err = 0;
struct cs_smp_reg_cb_info on_reg_cb_info;
mutex_lock(&coresight_mutex);
cpu_list = per_cpu(cs_cpupm_notifier, cpuid);
/* init list if no head yet */
if (!cpu_list) {
cpu_list = kzalloc(sizeof(struct list_head), GFP_KERNEL);
if (!cpu_list)
goto cpupm_reg_done;
else
INIT_LIST_HEAD(cpu_list);
}
/* create a list item */
item = kzalloc(sizeof(struct coresight_cpupm_item), GFP_KERNEL);
if (!item) {
err = -ENOMEM;
goto cpupm_reg_done;
}
item->csdev = csdev;
item->callback = callback;
/* init the cpu hotplug notification */
cpus_read_lock();
if (!cscpu_dev_reg_cnt) {
cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ARM_CORESIGHT_STARTING,
"arm/coresightcpu:starting",
coresight_hp_starting_cpu,
coresight_hp_stopping_cpu);
cscpu_hp_online = cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ONLINE_DYN, "arm/coresightcpu:online",
coresight_hp_dyn_online_cpu,
coresight_hp_dyn_offline_cpu);
if (cscpu_hp_online < 0) {
err = cscpu_hp_online;
cpuhp_remove_state_nocalls(
CPUHP_AP_ARM_CORESIGHT_STARTING);
kfree(item);
cscpu_hp_online = 0;
goto cpupm_hp_done;
}
}
cscpu_dev_reg_cnt++;
list_add(&item->node, cpu_list);
/* smp callback on dev cpu @ registration */
on_reg_cb_info.item = item;
on_reg_cb_info.cpu = cpuid;
smp_call_function_single(cpuid, coresight_smp_onreg_callback,
&on_reg_cb_info, 1);
+cpupm_hp_done:
cpus_read_unlock();
+cpupm_reg_done:
mutex_unlock(&coresight_mutex);
return err;
+} +EXPORT_SYMBOL_GPL(coresight_cpupm_register);
+void coresight_cpupm_unregister(struct coresight_device *csdev,
unsigned int cpuid)
+{
struct list_head *cpu_list;
struct coresight_cpupm_item *item, *tmp;
mutex_lock(&coresight_mutex);
cpu_list = per_cpu(cs_cpupm_notifier, cpuid);
if (!list_empty(cpu_list)) {
list_for_each_entry_safe(item, tmp, cpu_list, node) {
if (item->csdev == csdev) {
list_del(&item->node);
cscpu_dev_reg_cnt--;
kfree(item);
goto cs_cpupm_item_removed;
}
}
}
+cs_cpupm_item_removed:
if (!cscpu_dev_reg_cnt) {
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
if (cscpu_hp_online)
cpuhp_remove_state_nocalls(cscpu_hp_online);
}
mutex_unlock(&coresight_mutex);
+}
+EXPORT_SYMBOL_GPL(coresight_cpupm_unregister);
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
On Mon, 24 Jun 2019 at 06:39, Mike Leach mike.leach@linaro.org wrote:
Hi Mathieu,
I'm working through the other responses from you and suzuki, but this seems worth taking out of order...
On Tue, 18 Jun 2019 at 21:16, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Hi,
On Wed, Jun 05, 2019 at 08:03:12PM +0100, Mike Leach wrote:
Currently each driver type sets up own CPU power notifications such as CPU hotplug etc. Need a common mechanism for all driver types to use.
Adds a notification mechanism for CPU power events such as CPU hotplug as a general notification mechnism in CoreSight common code to be used by all CPU bound CS devices.
Provides a per-device registration mechanism with callback fn and event type notifier. Core code now manages the CPU hotplug registrations and calls back any registered device when events occur.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-priv.h | 26 +++ drivers/hwtracing/coresight/coresight.c | 167 ++++++++++++++++++- 2 files changed, 192 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 9c4fd7eb56eb..5ea2f86f71e0 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -214,4 +214,30 @@ void coresight_release_platform_data(struct coresight_platform_data *pdata);
int coresight_device_fwnode_match(struct device *dev, void *fwnode);
+/*
- cpu power management - allow any CPU bound Coresight device
- to use a common registration infrastructure for notifications.
- */
+/* cpu pm event types */ +enum cs_cpu_pm_events {
CS_CPUPM_SMP_ON_REG_CPU, /* smp call on cpu at registration */
CS_CPUPM_CPUHP_CS_START, /* hotplug CoreSight starting CPU */
CS_CPUPM_CPUHP_CS_STOP, /* hotplug CoreSight stopping CPU */
CS_CPUPM_CPUHP_AP_DYN_ONLINE, /* hotplug AP dynamic online CPU */
CS_CPUPM_CPUHP_AP_DYN_OFFLINE, /* hotplug AP dynamic offline CPU */
+};
+/* callback function type */ +typedef int (*p_cs_cpu_pm_event)(struct coresight_device *csdev,
unsigned int cpuid,
enum cs_cpu_pm_events event);
+/* registration functions */ +int coresight_cpupm_register(struct coresight_device *csdev,
unsigned int cpuid,
p_cs_cpu_pm_event callback);
+void coresight_cpupm_unregister(struct coresight_device *csdev,
unsigned int cpuid);
#endif diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 74661fabe098..509fc0b8329d 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1347,7 +1347,6 @@ void coresight_unregister(struct coresight_device *csdev) } EXPORT_SYMBOL_GPL(coresight_unregister);
/*
- coresight_search_device_idx - Search the fwnode handle of a device
- in the given dev_idx list. Must be called with the coresight_mutex held.
@@ -1404,3 +1403,169 @@ char *coresight_alloc_device_name(struct coresight_dev_list *dict, return name; } EXPORT_SYMBOL_GPL(coresight_alloc_device_name);
+/* cs general CPU bound device pm notification */ +struct coresight_cpupm_item {
struct coresight_device *csdev;
p_cs_cpu_pm_event callback;
struct list_head node;
+};
+/* CS cpu pm notifier lists per cpu */ +static DEFINE_PER_CPU(struct list_head *, cs_cpupm_notifier);
+static enum cpuhp_state cscpu_hp_online; +static int cscpu_dev_reg_cnt;
I do not really see the advantage of adding all this code. To me it is like shadowing an infrastructure the CPUhotplug subsystem is alreading giving us. And looking at function cti_cs_cpupm_event() in the next patch the code to handle 3 events is grouped into a single function. To me we are not gaining anything other than replicating code that already exists.
To me it is much easier to have a percpu instance of the cti_drvdata (I think Suzuki already hinted at that in his review) and access the right one using the CPU parameter provided by the individual callbacks.
Let me know if the above is not handling a scenario that this patch is.
My initial thoughts were:-
- this code appears both in the ETMv3 driver and ETMv4 driver. I
could add it to the CTI driver but thats just more maintenance. Seems better design for common code to go into the coresight core.
- this patches adds CPU hotplug. I can also add CPUidle to the same
infrastructure code - with a small change in the core code and the addition of 3 lines in the CTI driver CPUIdle power states are covered too. I have a test branch that does just that. As the CTI driver is designed from the ground up to respect the power states while programming this is needed for testing.
Then I looked deeper into the issue... 3) As I looked further into this, the current ETM drivers use the cpuhp_setup_state_nocalls_cpuslocked() call - this sets a single instance callback state. So any attempt to register a second set of callbacks fails - either with -EBUSY if you try to register callbacks with a different name, -EINVAL if you use name as NULL. Now the reason for the -EINVAL is that it is forbidden to register a second instance on a single instance callback. Now looking at the CPU hotplug call, there is a multi-instance variant
- but this only allows for the registration of one set of callback
functions, which have a 'node' parameter, in addition to the CPU. The first instance to register can provide the callback functions, the remaining instances register using a different call - add_instance() which just provides a 'node' object (actually something with a hlist struct). So using the multi-instance variant, the callback functions have to be in the coresight core code anyway, and we need code in there to figure out which node and which driver to notify - there is no way that the ETM driver can find the correct callback into the CTI driver, or vice versa.
I will admit I discovered (3) only after writing this patch - in my view 1 & 2 were pretty strong reasons - but now it seems it is required to allow the CPU bound drivers to get notified. I was of the view that this common code could be added and additional patches could fix up the ETMv3/ETMv4 drivers.
Now that Andrew is taking care of ETMv3/4 we can concentrate on CTIs. The ETM drivers are using the same CPUHP_AP_ARM_CORESIGHT_STARTING without problem because they are mutually exclusive. We also find the same CPUHP_AP_ONLINE_DYN in both drivers because it is one of only two defines along with (CPUHP_BP_PREPARE_DYN) that can be added multiple times.
That leaves us with 3 choices:
1) We use CPUHP_AP_ARM_CORESIGHT_STARTING as a multiplexer, like you did. 2) We make CPUHP_AP_ARM_CORESIGHT_STARTING part of the _DYN [1] API, something that has little chances of flying upstream. 3) We add a new define, something like CPUHP_AP_ARM_CORESIGHT_CTI_STARTING, just like others have done [2].
[1]. https://elixir.bootlin.com/linux/latest/source/kernel/cpu.c#L1561 [2]. git log --oneline -10 include/linux/cpuhotplug.h
At this point, I think that the best way to solve this is to split off the CPU hotplug updates to the CTI driver, and issue two patchsets. 1st - a baseline CTI driver without any of the hotplug handling. 2nd - a CPU hotplug fixup patchset, that adds common HP handling and fixes up ETMv3 and ETMv4 and adds to CTI in the same set. This is necessary as without removing the HP registration from the two ETM drivers, the common code will fail when invoked in the CTI driver. (interestingly, neither of the ETM drivers, nor my initial patch looked at the return code from cpuhp_setup_state_nocalls_cpuslocked()
I just noticed that as well. If I remember correctly Sebastian Siewior sent that code as part of a substantial patchset when the hotplug subsystem got a serious face lift a few years back. Since all the patches were the same I applied his code but looking back at it now, I can't see a reason to ignore the return code associated with the CPUHP_AP_ARM_CORESIGHT_STARTING registration.
- when that is checked these issues become very apparent).
Thanks and Regards
Mike
+static int coresight_iterate_cpu_event_list(unsigned int cpu,
enum cs_cpu_pm_events event)
+{
struct list_head *cpu_list;
struct coresight_cpupm_item *item;
mutex_lock(&coresight_mutex);
cpu_list = per_cpu(cs_cpupm_notifier, cpu);
if (!list_empty(cpu_list)) {
list_for_each_entry(item, cpu_list, node) {
item->callback(item->csdev, cpu, event);
}
}
mutex_unlock(&coresight_mutex);
return 0;
+}
+static int coresight_hp_starting_cpu(unsigned int cpu) +{
return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_CS_START);
+}
+static int coresight_hp_stopping_cpu(unsigned int cpu) +{
return coresight_iterate_cpu_event_list(cpu, CS_CPUPM_CPUHP_CS_STOP);
+}
+static int coresight_hp_dyn_online_cpu(unsigned int cpu) +{
return coresight_iterate_cpu_event_list(cpu,
CS_CPUPM_CPUHP_AP_DYN_ONLINE);
+}
+static int coresight_hp_dyn_offline_cpu(unsigned int cpu) +{
return coresight_iterate_cpu_event_list(cpu,
CS_CPUPM_CPUHP_AP_DYN_OFFLINE);
+}
+struct cs_smp_reg_cb_info {
unsigned int cpu;
struct coresight_cpupm_item *item;
+};
+static void coresight_smp_onreg_callback(void *info) +{
struct cs_smp_reg_cb_info *cb_info = info;
cb_info->item->callback(cb_info->item->csdev, cb_info->cpu,
CS_CPUPM_SMP_ON_REG_CPU);
+}
+int coresight_cpupm_register(struct coresight_device *csdev,
unsigned int cpuid,
p_cs_cpu_pm_event callback)
+{
struct list_head *cpu_list;
struct coresight_cpupm_item *item;
int err = 0;
struct cs_smp_reg_cb_info on_reg_cb_info;
mutex_lock(&coresight_mutex);
cpu_list = per_cpu(cs_cpupm_notifier, cpuid);
/* init list if no head yet */
if (!cpu_list) {
cpu_list = kzalloc(sizeof(struct list_head), GFP_KERNEL);
if (!cpu_list)
goto cpupm_reg_done;
else
INIT_LIST_HEAD(cpu_list);
}
/* create a list item */
item = kzalloc(sizeof(struct coresight_cpupm_item), GFP_KERNEL);
if (!item) {
err = -ENOMEM;
goto cpupm_reg_done;
}
item->csdev = csdev;
item->callback = callback;
/* init the cpu hotplug notification */
cpus_read_lock();
if (!cscpu_dev_reg_cnt) {
cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ARM_CORESIGHT_STARTING,
"arm/coresightcpu:starting",
coresight_hp_starting_cpu,
coresight_hp_stopping_cpu);
cscpu_hp_online = cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ONLINE_DYN, "arm/coresightcpu:online",
coresight_hp_dyn_online_cpu,
coresight_hp_dyn_offline_cpu);
if (cscpu_hp_online < 0) {
err = cscpu_hp_online;
cpuhp_remove_state_nocalls(
CPUHP_AP_ARM_CORESIGHT_STARTING);
kfree(item);
cscpu_hp_online = 0;
goto cpupm_hp_done;
}
}
cscpu_dev_reg_cnt++;
list_add(&item->node, cpu_list);
/* smp callback on dev cpu @ registration */
on_reg_cb_info.item = item;
on_reg_cb_info.cpu = cpuid;
smp_call_function_single(cpuid, coresight_smp_onreg_callback,
&on_reg_cb_info, 1);
+cpupm_hp_done:
cpus_read_unlock();
+cpupm_reg_done:
mutex_unlock(&coresight_mutex);
return err;
+} +EXPORT_SYMBOL_GPL(coresight_cpupm_register);
+void coresight_cpupm_unregister(struct coresight_device *csdev,
unsigned int cpuid)
+{
struct list_head *cpu_list;
struct coresight_cpupm_item *item, *tmp;
mutex_lock(&coresight_mutex);
cpu_list = per_cpu(cs_cpupm_notifier, cpuid);
if (!list_empty(cpu_list)) {
list_for_each_entry_safe(item, tmp, cpu_list, node) {
if (item->csdev == csdev) {
list_del(&item->node);
cscpu_dev_reg_cnt--;
kfree(item);
goto cs_cpupm_item_removed;
}
}
}
+cs_cpupm_item_removed:
if (!cscpu_dev_reg_cnt) {
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
if (cscpu_hp_online)
cpuhp_remove_state_nocalls(cscpu_hp_online);
}
mutex_unlock(&coresight_mutex);
+}
+EXPORT_SYMBOL_GPL(coresight_cpupm_unregister);
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
CPU bound CTI's require registration with CPU power event handling to ensure we know when device power state changes alongside associated CPU.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/coresight-cti.c | 59 +++++++++++++++++++++ 1 file changed, 59 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 6996b70f99e8..c58173d4b11b 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -91,6 +91,11 @@ static int cti_enable_hw(struct cti_drvdata *drvdata, bool update_refcount) if (config->hw_enabled || !config->hw_powered) goto cti_not_enabled;
+ /* if not updating refcount and it is 0 then nothing to do */ + if (!update_refcount && + (atomic_read(&drvdata->config.enable_req_count) == 0)) + goto cti_not_enabled; + /* claim the device */ rc = coresight_claim_device(drvdata->base); if (rc) @@ -584,6 +589,40 @@ const struct coresight_ops cti_ops = { .ect_ops = &cti_ops_ect, };
+/* pm event notifiers */ +int cti_cs_cpupm_event(struct coresight_device *csdev, unsigned int cpuid, + enum cs_cpu_pm_events event) +{ + struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev); + + switch (event) { + /* called on the device cpu during registration */ + case CS_CPUPM_SMP_ON_REG_CPU: + drvdata->config.hw_powered = true; + break; + + /* cpu hotplug start */ + case CS_CPUPM_CPUHP_CS_START: + spin_lock(&drvdata->spinlock); + drvdata->config.hw_powered = true; + spin_unlock(&drvdata->spinlock); + cti_enable_hw(drvdata, false); + break; + + /* cpu hotplug stop */ + case CS_CPUPM_CPUHP_CS_STOP: + cti_disable_hw(drvdata, false); + spin_lock(&drvdata->spinlock); + drvdata->config.hw_powered = false; + spin_unlock(&drvdata->spinlock); + break; + + default: + break; + } + return 0; +} + static int cti_probe(struct amba_device *adev, const struct amba_id *id) { int ret = 0; @@ -594,6 +633,7 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) struct coresight_platform_data *pdata = NULL; struct resource *res = &adev->res; struct ect_node *ect_nd = NULL; + bool cpupm_registered = false;
/* node to keep track of CTI net */ ect_nd = devm_kzalloc(dev, sizeof(struct ect_node), GFP_KERNEL); @@ -676,6 +716,20 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) goto err_out; }
+ /* setup cpu related CTI PM, otherwise assume powered */ + if (drvdata->ctidev.cpu >= 0) { + drvdata->config.hw_powered = false; + ret = coresight_cpupm_register(drvdata->csdev, + drvdata->ctidev.cpu, + cti_cs_cpupm_event); + if (ret) { + pr_err("%s:CS cpu pm register error\n", cti_desc.name); + goto err_out; + } + cpupm_registered = true; + } else + drvdata->config.hw_powered = true; + /* add to list of CTI devices */ mutex_lock(&ect_mutex); ect_nd->cti_drv = drvdata; @@ -696,6 +750,8 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) return 0;
err_out: + if (cpupm_registered && drvdata->csdev && (drvdata->ctidev.cpu >= 0)) + coresight_cpupm_unregister(drvdata->csdev, drvdata->ctidev.cpu); return ret; }
@@ -726,6 +782,9 @@ void cti_device_release(struct device *dev) struct ect_node *ect_item, *ect_tmp;
mutex_lock(&ect_mutex); + /* free up resources associated with the cti connections */ + if (drvdata->ctidev.cpu >= 0) + coresight_cpupm_unregister(drvdata->csdev, drvdata->ctidev.cpu);
/* clear the dynamic sysfs associate with connections */ cti_destroy_cons_sysfs(&drvdata->ctidev);
On Wed, Jun 05, 2019 at 08:03:13PM +0100, Mike Leach wrote:
CPU bound CTI's require registration with CPU power event handling to ensure we know when device power state changes alongside associated CPU.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 59 +++++++++++++++++++++ 1 file changed, 59 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 6996b70f99e8..c58173d4b11b 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -91,6 +91,11 @@ static int cti_enable_hw(struct cti_drvdata *drvdata, bool update_refcount) if (config->hw_enabled || !config->hw_powered) goto cti_not_enabled;
- /* if not updating refcount and it is 0 then nothing to do */
- if (!update_refcount &&
(atomic_read(&drvdata->config.enable_req_count) == 0))
goto cti_not_enabled;
What scenario is the above covering?
I am officially done reviewing this set.
Thanks, Mathieu
/* claim the device */ rc = coresight_claim_device(drvdata->base); if (rc) @@ -584,6 +589,40 @@ const struct coresight_ops cti_ops = { .ect_ops = &cti_ops_ect, }; +/* pm event notifiers */ +int cti_cs_cpupm_event(struct coresight_device *csdev, unsigned int cpuid,
enum cs_cpu_pm_events event)
+{
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- switch (event) {
/* called on the device cpu during registration */
- case CS_CPUPM_SMP_ON_REG_CPU:
drvdata->config.hw_powered = true;
break;
/* cpu hotplug start */
- case CS_CPUPM_CPUHP_CS_START:
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = true;
spin_unlock(&drvdata->spinlock);
cti_enable_hw(drvdata, false);
break;
/* cpu hotplug stop */
- case CS_CPUPM_CPUHP_CS_STOP:
cti_disable_hw(drvdata, false);
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = false;
spin_unlock(&drvdata->spinlock);
break;
- default:
break;
- }
- return 0;
+}
static int cti_probe(struct amba_device *adev, const struct amba_id *id) { int ret = 0; @@ -594,6 +633,7 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) struct coresight_platform_data *pdata = NULL; struct resource *res = &adev->res; struct ect_node *ect_nd = NULL;
- bool cpupm_registered = false;
/* node to keep track of CTI net */ ect_nd = devm_kzalloc(dev, sizeof(struct ect_node), GFP_KERNEL); @@ -676,6 +716,20 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) goto err_out; }
- /* setup cpu related CTI PM, otherwise assume powered */
- if (drvdata->ctidev.cpu >= 0) {
drvdata->config.hw_powered = false;
ret = coresight_cpupm_register(drvdata->csdev,
drvdata->ctidev.cpu,
cti_cs_cpupm_event);
if (ret) {
pr_err("%s:CS cpu pm register error\n", cti_desc.name);
goto err_out;
}
cpupm_registered = true;
- } else
drvdata->config.hw_powered = true;
- /* add to list of CTI devices */ mutex_lock(&ect_mutex); ect_nd->cti_drv = drvdata;
@@ -696,6 +750,8 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) return 0; err_out:
- if (cpupm_registered && drvdata->csdev && (drvdata->ctidev.cpu >= 0))
return ret;coresight_cpupm_unregister(drvdata->csdev, drvdata->ctidev.cpu);
} @@ -726,6 +782,9 @@ void cti_device_release(struct device *dev) struct ect_node *ect_item, *ect_tmp; mutex_lock(&ect_mutex);
- /* free up resources associated with the cti connections */
- if (drvdata->ctidev.cpu >= 0)
coresight_cpupm_unregister(drvdata->csdev, drvdata->ctidev.cpu);
/* clear the dynamic sysfs associate with connections */ cti_destroy_cons_sysfs(&drvdata->ctidev); -- 2.20.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight