This patchset introduces initial concepts in CoreSight complex configuration support - the device "feature", which is a method of programming the device to perform a specific function.
A built-in feature is provided - the ETM strobing function, which programs the ETM to switch trace on and off in a specific mark / space ratio to effectively sample the program being traced. This feature is essential for the Auto-FDO flow using CoreSight trace.
Features are declared as a data table, a set of register, resource and parameter requirements. A feature can then be loaded into a device, when the requirements are validated. Once loaded a feature can be enabled for a specific trace run.
A feature appears in the sysfs file for the device, as a directory of form 'name.feat', with parameters 'enable', 'description' and any input parameters that may be used to control the operation.
For example the ETM strobing feature provided has parameters of 'window' and 'period' to control the sampling mark / space ratio. The representation in sysfs for the ETMv4 is therefore:- etmX/strobing.feat/ /enable /window /period
Future developments will introduce resource management, and allow for the runtime loading of additional features, and the setting of features across an entire CoreSight system.
Mike Leach (3): coresight: etmv4: Fix resource selector constant. coresight: etmv4: Counter values not saved on disable. coresight: etmv4: Adds initial complex config with ETM4 strobe feature.
drivers/hwtracing/coresight/Makefile | 7 +- .../hwtracing/coresight/coresight-config.c | 380 ++++++++++++++++++ .../hwtracing/coresight/coresight-config.h | 156 +++++++ .../hwtracing/coresight/coresight-etm4x-cfg.c | 325 +++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 29 ++ .../coresight/coresight-etm4x-sysfs.c | 3 + drivers/hwtracing/coresight/coresight-etm4x.c | 24 +- drivers/hwtracing/coresight/coresight-etm4x.h | 4 +- drivers/hwtracing/coresight/coresight.c | 1 + include/linux/coresight.h | 2 + 10 files changed, 925 insertions(+), 6 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-config.c create mode 100644 drivers/hwtracing/coresight/coresight-config.h create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.c create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.h
ETMv4 max resource selector constant incorrectly set to 16. Updated to the correct 32 value, and adjustments made to limited code using it.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/coresight-etm4x.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 4a695bf90582..b0d633daf716 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -133,7 +133,7 @@ #define ETMv4_MAX_CTXID_CMP 8 #define ETM_MAX_VMID_CMP 8 #define ETM_MAX_PE_CMP 8 -#define ETM_MAX_RES_SEL 16 +#define ETM_MAX_RES_SEL 32 #define ETM_MAX_SS_CMP 8
#define ETM_ARCH_V4 0x40 @@ -325,7 +325,7 @@ struct etmv4_save_state { u32 trccntctlr[ETMv4_MAX_CNTR]; u32 trccntvr[ETMv4_MAX_CNTR];
- u32 trcrsctlr[ETM_MAX_RES_SEL * 2]; + u32 trcrsctlr[ETM_MAX_RES_SEL];
u32 trcssccr[ETM_MAX_SS_CMP]; u32 trcsscsr[ETM_MAX_SS_CMP];
The counter value registers change during operation, however this change is not reflected in the values seen by the user in sysfs.
This fixes the issue by reading back the values on disable.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/coresight-etm4x.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 747afc875f91..60a5133d801c 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -507,6 +507,12 @@ static void etm4_disable_hw(void *info) readl_relaxed(drvdata->base + TRCSSCSRn(i)); }
+ /* read back the current counter values */ + for (i = 0; i < drvdata->nr_cntr; i++) { + config->cntr_val[i] = + readl_relaxed(drvdata->base + TRCCNTVRn(i)); + } + coresight_disclaim_device_unlocked(drvdata->base);
CS_LOCK(drvdata->base);
On Wed, Jun 10, 2020 at 10:13:31AM +0100, Mike Leach wrote:
The counter value registers change during operation, however this change is not reflected in the values seen by the user in sysfs.
This fixes the issue by reading back the values on disable.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-etm4x.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 747afc875f91..60a5133d801c 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -507,6 +507,12 @@ static void etm4_disable_hw(void *info) readl_relaxed(drvdata->base + TRCSSCSRn(i)); }
- /* read back the current counter values */
- for (i = 0; i < drvdata->nr_cntr; i++) {
config->cntr_val[i] =
readl_relaxed(drvdata->base + TRCCNTVRn(i));
- }
I have applied patches 1 and 2 in this set - no need to send them again.
Thanks, Mathieu
coresight_disclaim_device_unlocked(drvdata->base); CS_LOCK(drvdata->base); -- 2.17.1
Hi Mathieu,
OK - unfortunately I too recognised that the first two patches were fixes that could be applied irrespective of the new work, so created a separate patch set that went to the main arm-kernel mailing list, with these two patches plus one other relating to the comment fix that was originally part of the default sink set. Feel free to ignore the repeats!
Regards
Mike
On Tue, 16 Jun 2020 at 20:19, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, Jun 10, 2020 at 10:13:31AM +0100, Mike Leach wrote:
The counter value registers change during operation, however this change is not reflected in the values seen by the user in sysfs.
This fixes the issue by reading back the values on disable.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-etm4x.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 747afc875f91..60a5133d801c 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -507,6 +507,12 @@ static void etm4_disable_hw(void *info) readl_relaxed(drvdata->base + TRCSSCSRn(i)); }
/* read back the current counter values */
for (i = 0; i < drvdata->nr_cntr; i++) {
config->cntr_val[i] =
readl_relaxed(drvdata->base + TRCCNTVRn(i));
}
I have applied patches 1 and 2 in this set - no need to send them again.
Thanks, Mathieu
coresight_disclaim_device_unlocked(drvdata->base); CS_LOCK(drvdata->base);
-- 2.17.1
This set introduces the concept of complex features for CoreSight devices, and adds a feature to ETMv4 to enable strobing of the etm capture.
1) Complex Features: These are programming settings for devices, especially ETM which have many programmable register that can be combined to create complex functionality. Features appear as sub-directories in sysfs, having an enable parameter to set the full program setting.
2) ETM strobing. This feature is introduced to ETMv4 to allow the trace capture to be periodically switched on and off - strobed - to a given mark / space ratio to generate statistical trace data for workflows such as auto-fdo.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/Makefile | 7 +- .../hwtracing/coresight/coresight-config.c | 380 ++++++++++++++++++ .../hwtracing/coresight/coresight-config.h | 156 +++++++ .../hwtracing/coresight/coresight-etm4x-cfg.c | 325 +++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 29 ++ .../coresight/coresight-etm4x-sysfs.c | 3 + drivers/hwtracing/coresight/coresight-etm4x.c | 18 +- drivers/hwtracing/coresight/coresight.c | 1 + include/linux/coresight.h | 2 + 9 files changed, 917 insertions(+), 4 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-config.c create mode 100644 drivers/hwtracing/coresight/coresight-config.h create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.c create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.h
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 19497d1d92bf..b6c28f4cfca7 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -3,7 +3,8 @@ # Makefile for CoreSight drivers. # obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o \ - coresight-platform.o coresight-sysfs.o + coresight-platform.o coresight-sysfs.o \ + coresight-config.o obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \ coresight-tmc-etf.o \ coresight-tmc-etr.o @@ -14,7 +15,9 @@ obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \ - coresight-etm4x-sysfs.o + coresight-etm4x-sysfs.o \ + coresight-etm4x-cfg.o + obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c new file mode 100644 index 000000000000..10c2d546ca82 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(C) 2020 Linaro Limited. All rights reserved. + * Author: Mike Leach mike.leach@linaro.org + */ + +#include <linux/sysfs.h> +#include "coresight-config.h" +#include "coresight-priv.h" + +static DEFINE_MUTEX(coresight_cfg_mutex); + +/* + * Handlers for creating and manipulating the feature representation + * in sysfs. + */ + +/* base feature attribute info */ +struct feat_attr_info { + const char *name; + ssize_t (*show)(struct device *dev, struct device_attribute *attr, + char *buf); + ssize_t (*store)(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); +}; + +static ssize_t cs_cfg_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *eattr = + container_of(attr, struct dev_ext_attribute, attr); + struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var; + int val; + + spin_lock(feat->dev_spinlock); + val = feat->enabled; + spin_unlock(feat->dev_spinlock); + + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t cs_cfg_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dev_ext_attribute *eattr = + container_of(attr, struct dev_ext_attribute, attr); + struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var; + + int val; + + spin_lock(feat->dev_spinlock); + if (!kstrtoint(buf, 0, &val)) + feat->enabled = val; + spin_unlock(feat->dev_spinlock); + + return size; +} + +static ssize_t cs_cfg_desc_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *eattr = + container_of(attr, struct dev_ext_attribute, attr); + struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var; + + return scnprintf(buf, PAGE_SIZE, "%s\n", feat->desc->brief); +} + +struct feat_attr_info feature_std_attr[] = { + { + .name = "enable", + .show = cs_cfg_enable_show, + .store = cs_cfg_enable_store, + }, + { + .name = "description", + .show = cs_cfg_desc_show, + }, + { 0 }, +}; + +static ssize_t cs_cfg_param_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *eattr = + container_of(attr, struct dev_ext_attribute, attr); + struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var; + u64 val; + + spin_lock(param->feat->dev_spinlock); + val = param->current_value; + spin_unlock(param->feat->dev_spinlock); + + return scnprintf(buf, PAGE_SIZE, "%lld\n", val); +} + +static ssize_t cs_cfg_param_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dev_ext_attribute *eattr = + container_of(attr, struct dev_ext_attribute, attr); + struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var; + u64 val; + + spin_lock(param->feat->dev_spinlock); + if (!kstrtoull(buf, 0, &val)) + param->current_value = val; + spin_unlock(param->feat->dev_spinlock); + + return size; +} + +static struct attribute * +cs_cfg_sysfs_create_feat_attr(struct device *dev, struct cs_cfg_feature *feat, + struct feat_attr_info *info) +{ + struct dev_ext_attribute *eattr; + + eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute), + GFP_KERNEL); + if (!eattr) + return NULL; + eattr->attr.attr.name = devm_kstrdup(dev, info->name, GFP_KERNEL); + eattr->attr.attr.mode = info->store ? 0644 : 0444; + eattr->attr.show = info->show; + eattr->attr.store = info->store; + eattr->var = feat; + return &eattr->attr.attr; +} + +static struct attribute * +cs_cfg_sysfs_create_param_attr(struct device *dev, const char *name, + struct cs_cfg_parameter *param) +{ + struct dev_ext_attribute *eattr; + + eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute), + GFP_KERNEL); + if (!eattr) + return NULL; + + eattr->attr.attr.name = devm_kstrdup(dev, name, GFP_KERNEL); + eattr->attr.attr.mode = 0644; + eattr->attr.show = cs_cfg_param_show; + eattr->attr.store = cs_cfg_param_store; + eattr->var = param; + return &eattr->attr.attr; +} + +int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat) +{ + int p, attr_idx = 0; + struct attribute **attrs; + const char *name; + struct feat_attr_info *info; + struct device *dev = feat->csdev->dev.parent; + + /* create sysfs group in csdev */ + feat->param_group = devm_kzalloc(dev, sizeof(struct attribute_group), + GFP_KERNEL); + if (!feat->param_group) + return -ENOMEM; + + feat->param_group->name = devm_kasprintf(dev, GFP_KERNEL, "%s.feat", + feat->desc->name); + if (!feat->param_group->name) + return -ENOMEM; + + /* attributes - nr_params + enable + desc */ + attrs = devm_kcalloc(dev, feat->nr_params + 2, + sizeof(struct attribute *), GFP_KERNEL); + if (!attrs) + return -ENOMEM; + + /* create base attributes in group */ + while (feature_std_attr[attr_idx].name) { + info = &feature_std_attr[attr_idx]; + attrs[attr_idx] = cs_cfg_sysfs_create_feat_attr(dev, feat, + info); + if (!attrs[attr_idx]) + return -ENOMEM; + attr_idx++; + } + + /* create params in group */ + for (p = 0; p < feat->nr_params; p++) { + name = feat->desc->params[p].name; + attrs[attr_idx] = + cs_cfg_sysfs_create_param_attr(dev, name, + &feat->params[p]); + if (!attrs[attr_idx]) + return -ENOMEM; + attr_idx++; + } + feat->param_group->attrs = attrs; + + return sysfs_create_group(&feat->csdev->dev.kobj, feat->param_group); +} + +/* + * standard routines to set/save/reset enabled features, and iterate + * groups of features on a device. + */ + +void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat) +{ + mutex_lock(&coresight_cfg_mutex); + list_add(&feat->node, &feat->csdev->feat_list); + mutex_unlock(&coresight_cfg_mutex); +} + +inline void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags) +{ + u32 *p_val32 = (u32 *)reg->drv_store; + u32 tmp32; + + if (!(flags & CS_CFG_REG_VAL_64BIT)) { + if (flags & CS_CFG_REG_VAL_MASK) { + tmp32 = *p_val32; + tmp32 &= ~reg->value.mask32; + tmp32 |= reg->value.val32 & reg->value.mask32; + *p_val32 = tmp32; + } else + *p_val32 = reg->value.val32; + } else + *((u64 *)reg->drv_store) = reg->value.val64; +} + +inline void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags) +{ + if (flags & CS_CFG_REG_VAL_64BIT) + reg->value.val64 = *(u64 *)(reg->drv_store); + else + reg->value.val32 = *(u32 *)(reg->drv_store); +} + +inline void coresight_cfg_init_reg(struct cs_cfg_reg *reg, + const struct cs_cfg_reg_desc *desc) +{ + reg->value.val64 = desc->value.val64; +} + +inline void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags, + struct cs_cfg_parameter *param) +{ + if (flags & CS_CFG_REG_VAL_64BIT) + reg->value.val64 = param->current_value; + else + reg->value.val32 = (u32)param->current_value; +} + +/* default set - will set values without resource checking */ +static int cs_cfg_set_on_enable(struct cs_cfg_feature *feat) +{ + int i; + u32 flags; + + spin_lock(feat->dev_spinlock); + if (feat->enabled) { + for (i = 0; i < feat->nr_regs; i++) { + flags = feat->desc->regs[i].flags; + coresight_cfg_set_reg(&feat->regs[i], flags); + } + } + dev_dbg(&feat->csdev->dev, "Feature %s: %s", feat->desc->name, + feat->enabled ? "Set enabled" : "Skip disabled"); + spin_unlock(feat->dev_spinlock); + return 0; +} + +static void cs_cfg_save_on_disable(struct cs_cfg_feature *feat) +{ + int i; + u32 flags; + + spin_lock(feat->dev_spinlock); + if (feat->enabled) { + for (i = 0; i < feat->nr_regs; i++) { + flags = feat->desc->regs[i].flags; + if (flags & CS_CFG_REG_VAL_SAVE) + coresight_cfg_save_reg(&feat->regs[i], flags); + } + } + spin_unlock(feat->dev_spinlock); +} + +/* default reset - restore default values, disable feature */ +static void cs_cfg_reset_feat(struct cs_cfg_feature *feat) +{ + const struct cs_cfg_feature_desc *feat_desc = feat->desc; + struct cs_cfg_reg_desc *reg_desc; + struct cs_cfg_parameter *param; + struct cs_cfg_reg *reg; + int i; + u32 flags; + + spin_lock(feat->dev_spinlock); + feat->enabled = false; + + /* + * set the default values for all parameters and regs from the + * relevant static descriptors. + */ + for (i = 0; i < feat->nr_params; i++) + feat->params[i].current_value = feat_desc->params[i].value; + + for (i = 0; i < feat->nr_regs; i++) { + reg_desc = &feat_desc->regs[i]; + flags = reg_desc->flags; + reg = &feat->regs[i]; + + /* check if reg set from a parameter otherwise desc default */ + if (flags & CS_CFG_REG_VAL_PARAM) { + param = &feat->params[reg_desc->value.val32]; + coresight_cfg_init_reg_param(reg, flags, param); + } else + coresight_cfg_init_reg(reg, reg_desc); + } + spin_unlock(feat->dev_spinlock); +} + +void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat) +{ + feat->ops.set_on_enable = cs_cfg_set_on_enable; + feat->ops.save_on_disable = cs_cfg_save_on_disable; + feat->ops.reset = cs_cfg_reset_feat; +} + +int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev) +{ + struct cs_cfg_feature *feat; + int err = 0; + + if (list_empty(&csdev->feat_list)) + return 0; + + list_for_each_entry(feat, &csdev->feat_list, node) { + dev_dbg(&csdev->dev, "Found feature:%s", feat->desc->name); + + if (feat->ops.set_on_enable) { + err = feat->ops.set_on_enable(feat); + dev_dbg(&csdev->dev, "Feature %s: %s", + feat->desc->name, err ? "Set failed" : "OK"); + if (!err) + break; + } + } + return err; +} + +void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev) +{ + struct cs_cfg_feature *feat; + + if (list_empty(&csdev->feat_list)) + return; + + list_for_each_entry(feat, &csdev->feat_list, node) { + if (feat->ops.save_on_disable) + feat->ops.save_on_disable(feat); + } +} + +void coresight_cfg_reset_feats(struct coresight_device *csdev) +{ + struct cs_cfg_feature *feat; + + if (list_empty(&csdev->feat_list)) + return; + + list_for_each_entry(feat, &csdev->feat_list, node) { + if (feat->ops.reset) + feat->ops.reset(feat); + } +} diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h new file mode 100644 index 000000000000..ce69ad5d2bbf --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020 Linaro Limited, All rights reserved. + * Author: Mike Leach mike.leach@linaro.org + */ + +#ifndef _CORESIGHT_CORESIGHT_CONFIG_H +#define _CORESIGHT_CORESIGHT_CONFIG_H + +/* CoreSight Configuration - complex component and system config handling */ + +/* generic register flags */ +#define CS_CFG_REG_STD 0x80000000 /* reg is standard reg */ +#define CS_CFG_REG_RESOURCE 0x40000000 /* reg is a resource */ +#define CS_CFG_REG_VAL_PARAM 0x08000000 /* reg value uses param */ +#define CS_CFG_REG_VAL_MASK 0x04000000 /* reg value bit masked */ +#define CS_CFG_REG_VAL_64BIT 0x02000000 /* reg value 64 bit */ +#define CS_CFG_REG_VAL_SAVE 0x01000000 /* reg value save on disable */ +#define CS_CFG_REG_ID_MASK 0x00000FFF /* reg offset / id in device */ +#define CS_CFG_REG_DEV_MASK 0x00FFF000 /* device specific flags */ + +/** + * Parameter descriptor for a device feature. + * + * @name: Name of parameter. + * @value: Initial or default value. + */ +struct cs_cfg_parameter_desc { + const char *name; + const u64 value; +}; + +/** + * Representation of register value. + * Supports full 64 bit register value, or 32 bit value with optional mask + * value. + */ +union cs_cfg_regval { + u64 val64; + struct { + u32 val32; + u32 mask32; + }; +}; + +/** + * Register descriptor for a device feature + * + * @flags: flags defining the handling of the register. + * @value: Initial value & optional mask. + */ +struct cs_cfg_reg_desc { + const u32 flags; + const union cs_cfg_regval value; +}; + +/** + * Device feature descriptor - combination of registers and parameters to + * program a device to implement a specific complex function. + * + * @name: feature name. + * @brief: brief description of the feature. + * @params: list of parameters used - 0 terminated. + * @reg: list of registers used. + */ +struct cs_cfg_feature_desc { + const char *name; + const char *brief; + struct cs_cfg_parameter_desc *params; + struct cs_cfg_reg_desc *regs; +}; + +/** + * config parameter instance - part of a loaded feature. + * + * @feat: parent feature + * @current_value: current value of parameter - may be set by user via + * sysfs, or modified during device operation. + */ +struct cs_cfg_parameter { + struct cs_cfg_feature *feat; + u64 current_value; +}; + +/** + * config register instance - part of a loaded feature. + * maps register values to driver structures + * + * @value: value to use when setting feature on device / store for + * readback of volatile values. + * @drv_store: pointer to internal driver element used to set the value + * in hardware. + */ +struct cs_cfg_reg { + union cs_cfg_regval value; + void *drv_store; +}; + +/** + * cs_cfg_ops - standard operations for features. + * + * CS config core provides basic defaults for these, which can be overridden by + * device specific versions. + * + * @set_on_enable: Set the feature on the driver before hw enable. + * @save_on_disable: Save any volatile register values after hw disable. + * @reset: Reset feature to default values. + */ +struct cs_cfg_ops { + int (*set_on_enable)(struct cs_cfg_feature *feat); + void (*save_on_disable)(struct cs_cfg_feature *feat); + void (*reset)(struct cs_cfg_feature *feat); +}; + +/** + * Feature instance loaded into a CoreSight device driver. + * + * @desc: pointer to the static descriptor for this feature. + * @csdev: parent CoreSight device instance. + * @node: list entry into feature list for this device. + * @dev_spinlock: device spinlock. + * @enabled: feature is enabled on this device. + * @nr_params: number of parameters. + * @params: current parameter values on this device + * @param_group: sysfs group for feature parameters. + * @ops: standard ops to enable and disable features on devices. + */ +struct cs_cfg_feature { + const struct cs_cfg_feature_desc *desc; + struct coresight_device *csdev; + struct list_head node; + spinlock_t *dev_spinlock; + bool enabled; + int nr_params; + struct cs_cfg_parameter *params; + struct attribute_group *param_group; + int nr_regs; + struct cs_cfg_reg *regs; + struct cs_cfg_ops ops; +}; + +/* coresight config API functions */ +int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat); +int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev); +void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev); +void coresight_cfg_reset_feats(struct coresight_device *csdev); +void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat); +void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat); +void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_init_reg(struct cs_cfg_reg *reg, + const struct cs_cfg_reg_desc *desc); +void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags, + struct cs_cfg_parameter *param); + +#endif /* _CORESIGHT_CORESIGHT_CONFIG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c new file mode 100644 index 000000000000..240e0235c130 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(C) 2020 Linaro Limited. All rights reserved. + * Author: Mike Leach mike.leach@linaro.org + */ + +#include "coresight-etm4x.h" +#include "coresight-etm4x-cfg.h" +#include "coresight-priv.h" + +/* built in features for ETMv4 */ + +/* strobe feature */ + +/* register defines */ +#define STRB_REG_CTR (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_CTR) +#define STRB_REG_CTR_RB (STRB_REG_CTR | CS_CFG_REG_VAL_SAVE) +#define STRB_REG_CTR_PRM (STRB_REG_CTR | CS_CFG_REG_VAL_PARAM) +#define STRB_REG_SEQ (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEQ) +#define STRB_REG_SEL (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEL) +#define STRB_REG_VI (CS_CFG_REG_STD | CS_CFG_REG_VAL_MASK) + +static struct cs_cfg_parameter_desc strobe_params[] = { + { + .name = "window", + .value = 1000, + }, + { + .name = "period", + .value = 10000, + }, + { 0 }, +}; + +static struct cs_cfg_reg_desc strob_regs[] = { + /* resource selectors */ + { + .flags = STRB_REG_SEL | TRCRSCTLRn(2), + .value = { + .val32 = 0x20001, + }, + }, + { + .flags = STRB_REG_SEQ | TRCRSCTLRn(3), + .value = { + .val32 = 0x20002, + } + }, + /* strobe window counter 0 - reload from param 0 */ + { + .flags = STRB_REG_CTR_RB | TRCCNTVRn(0), + }, + { + .flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(0), + .value = { + .val32 = 0, + }, + }, + { + .flags = STRB_REG_CTR | TRCCNTCTLRn(0), + .value = { + .val32 = 0x10001, + }, + }, + /* strobe period counter 1 - reload from param 1 */ + { + .flags = STRB_REG_CTR_RB | TRCCNTVRn(1), + }, + { + .flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(1), + .value = { + .val32 = 1, + }, + }, + { + .flags = STRB_REG_CTR | TRCCNTCTLRn(1), + .value = { + .val32 = 0x8102, + }, + }, + /* sequencer */ + { + .flags = STRB_REG_SEQ | TRCSEQEVRn(0), + .value = { + .val32 = 0x0081, + }, + }, + { + .flags = STRB_REG_SEQ | TRCSEQEVRn(1), + .value = { + .val32 = 0x0000, + }, + }, + /* view-inst */ + { + .flags = STRB_REG_VI | TRCVICTLR, + .value = { + .val32 = 0x0003, + .mask32 = 0x0003, + }, + }, + /* end of regs */ + { 0 }, +}; + +static struct cs_cfg_feature_desc strobe = { + .name = "strobing", + .brief = "Generate periodic trace capture windows.", + .params = strobe_params, + .regs = strob_regs, +}; + +static struct cs_cfg_feature_desc *built_in_feats[] = { + &strobe, + 0 +}; + +/** + * etm4_cfg_map_reg_offset - validate and map the register offset into a + * location in the driver config struct. + * + * Limits the number of registers that can be accessed and programmed in + * features, to those which are used to control the trace capture parameters. + * + * Omits or limits access to those which the driver must use exclusively. + * + * Invalid offsets will result in fail code return and feature load failure. + * + * @drvdata: driver data to map into. + * @reg: register to map. + * @offset: device offset for the register + */ +static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata, + struct cs_cfg_reg *reg, u32 offset) +{ + int err = 0, idx; + struct etmv4_config *drvcfg = &drvdata->config; + + #define MAPREG(cval, elem) \ + case cval: \ + reg->drv_store = &drvcfg->elem; \ + break; + + #define MAPREGIDX(cval, elem, off_idx) \ + case cval: \ + reg->drv_store = &drvcfg->elem[off_idx]; \ + break; + + if (((offset >= TRCEVENTCTL0R) && (offset <= TRCVIPCSSCTLR)) || + ((offset >= TRCSEQRSTEVR) && (offset <= TRCEXTINSELR)) || + ((offset >= TRCCIDCCTLR0) && (offset <= TRCVMIDCCTLR1))) { + switch (offset) { + /* 32 bit single control and filter registers */ + MAPREG(TRCEVENTCTL0R, eventctrl0); + MAPREG(TRCEVENTCTL1R, eventctrl1); + MAPREG(TRCSTALLCTLR, stall_ctrl); + MAPREG(TRCTSCTLR, ts_ctrl); + MAPREG(TRCSYNCPR, syncfreq); + MAPREG(TRCCCCTLR, ccctlr); + MAPREG(TRCBBCTLR, bb_ctrl); + MAPREG(TRCVICTLR, vinst_ctrl); + MAPREG(TRCVIIECTLR, viiectlr); + MAPREG(TRCVISSCTLR, vissctlr); + MAPREG(TRCVIPCSSCTLR, vipcssctlr); + MAPREG(TRCSEQRSTEVR, seq_rst); + MAPREG(TRCSEQSTR, seq_state); + MAPREG(TRCEXTINSELR, ext_inp); + MAPREG(TRCCIDCCTLR0, ctxid_mask0); + MAPREG(TRCCIDCCTLR1, ctxid_mask1); + MAPREG(TRCVMIDCCTLR0, vmid_mask0); + MAPREG(TRCVMIDCCTLR1, vmid_mask1); + default: + err = -EINVAL; + break; + } + } else if ((offset & GENMASK(11, 4)) == TRCSEQEVRn(0)) { + /* sequencer state control registers */ + idx = (offset & GENMASK(3, 0)) / 4; + if (idx < ETM_MAX_SEQ_STATES) + reg->drv_store = &drvcfg->seq_ctrl[idx]; + else + err = -EINVAL; + } else if ((offset >= TRCSSCCRn(0)) && (offset <= TRCSSPCICRn(7))) { + /* 32 bit, 8 off indexed register sets */ + idx = (offset & GENMASK(4, 0)) / 4; + switch (offset & GENMASK(11, 5)) { + MAPREGIDX(TRCSSCCRn(0), ss_ctrl, idx); + MAPREGIDX(TRCSSCSRn(0), ss_status, idx); + MAPREGIDX(TRCSSPCICRn(0), ss_pe_cmp, idx); + default: + err = -EINVAL; + break; + } + } else if ((offset >= TRCCIDCVRn(0)) && (offset <= TRCVMIDCVRn(7))) { + /* 64 bit, 8 off indexed register sets */ + idx = (offset & GENMASK(5, 0)) / 8; + switch (offset & GENMASK(11, 6)) { + MAPREGIDX(TRCCIDCVRn(0), ctxid_pid, idx); + MAPREGIDX(TRCVMIDCVRn(0), vmid_val, idx); + default: + err = -EINVAL; + break; + } + } else if ((offset >= TRCRSCTLRn(2)) && + (offset <= TRCRSCTLRn((ETM_MAX_RES_SEL - 1)))) { + /* 32 bit resource selection regs, 32 off, skip fixed 0,1 */ + idx = (offset & GENMASK(6, 0)) / 4; + if (idx < ETM_MAX_RES_SEL) + reg->drv_store = &drvcfg->res_ctrl[idx]; + else + err = -EINVAL; + } else if ((offset >= TRCACVRn(0)) && + (offset <= TRCACATRn((ETM_MAX_SINGLE_ADDR_CMP - 1)))) { + /* 64 bit addr cmp regs, 16 off */ + idx = (offset & GENMASK(6, 0)) / 8; + switch (offset & GENMASK(11, 7)) { + MAPREGIDX(TRCACVRn(0), addr_val, idx); + MAPREGIDX(TRCACATRn(0), addr_acc, idx); + default: + err = -EINVAL; + break; + } + + } else if ((offset >= TRCCNTRLDVRn(0)) && + (offset <= TRCCNTVRn((ETMv4_MAX_CNTR - 1)))) { + /* 32 bit counter regs, 4 off (ETMv4_MAX_CNTR - 1) */ + idx = (offset & GENMASK(3, 0)) / 4; + switch (offset & GENMASK(11, 4)) { + MAPREGIDX(TRCCNTRLDVRn(0), cntrldvr, idx); + MAPREGIDX(TRCCNTCTLRn(0), cntr_ctrl, idx); + MAPREGIDX(TRCCNTVRn(0), cntr_val, idx); + default: + err = -EINVAL; + break; + } + } else { + err = -EINVAL; + } + + return err; +} + +/** + * etm4_cfg_load_feature - load a feature into a device instance. + * + * @csdev: An ETMv4 CoreSight device. + * @feat_desc: The static feature descriptor to be loaded. + * + * The function will load a feature instance into the device, based on the + * supplied descriptor. The descriptor will be checked to ensure a valid + * feature can be loaded for this ETMv4 device. + * + * Parameter and register definitions will be converted into internal + * structures that are used to set the values in the driver when the + * feature is enabled for the device. + */ +int etm4_cfg_load_feature(struct coresight_device *csdev, + const struct cs_cfg_feature_desc *feat_desc) +{ + struct device *dev = csdev->dev.parent; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev); + struct cs_cfg_feature *feat; + u32 offset; + int i = 0, err = 0; + + feat = devm_kzalloc(dev, sizeof(struct cs_cfg_feature), GFP_KERNEL); + if (!feat) + return -ENOMEM; + + feat->desc = feat_desc; + feat->dev_spinlock = &drvdata->spinlock; + feat->csdev = csdev; + + /* count the params regs and allocate space. */ + while (feat_desc->params[i++].name) + feat->nr_params++; + + i = 0; + while (feat_desc->regs[i++].flags) + feat->nr_regs++; + + feat->params = devm_kcalloc(dev, feat->nr_params, + sizeof(struct cs_cfg_parameter), + GFP_KERNEL); + if (!feat->params) + return -ENOMEM; + + feat->regs = devm_kcalloc(dev, feat->nr_regs, + sizeof(struct cs_cfg_reg), GFP_KERNEL); + if (!feat->regs) + return -ENOMEM; + + /* load the parameters default values */ + for (i = 0; i < feat->nr_params; i++) + feat->params[i].feat = feat; + + /* process the register descriptions */ + for (i = 0; i < feat->nr_regs; i++) { + offset = feat_desc->regs[i].flags & CS_CFG_REG_ID_MASK; + err = etm4_cfg_map_reg_offset(drvdata, &feat->regs[i], offset); + if (err) + return err; + } + + /* set sysfs representation of the feature */ + err = coresight_cfg_sysfs_add_grp(feat); + + /* add the feature to the device list and init default values */ + if (!err) { + coresight_cfg_list_add_feat(feat); + coresight_cfg_set_def_ops(feat); + feat->ops.reset(feat); + } + return err; +} + +/* load up the list of builtin features */ +int etm4_cfg_load_builtins(struct coresight_device *csdev) +{ + int idx = 0, err = 0; + + while (built_in_feats[idx] && !err) + err = etm4_cfg_load_feature(csdev, built_in_feats[idx++]); + return err; +} diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h new file mode 100644 index 000000000000..703657cac2fc --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + */ + +#ifndef _CORESIGHT_CORESIGHT_ETM4X_CFG_H +#define _CORESIGHT_CORESIGHT_ETM4X_CFG_H + +#include "coresight-config.h" + +/* ETMv4 specific config defines */ + +/* resource IDs */ + +#define ETM4_CFG_RES_CTR 0x00001000 +#define ETM4_CFG_RES_CMP 0x00002000 +#define ETM4_CFG_RES_CMP_PAIR0 0x00003000 +#define ETM4_CFG_RES_CMP_PAIR1 0x00004000 +#define ETM4_CFG_RES_SEL 0x00005000 +#define ETM4_CFG_RES_SEL_PAIR0 0x00006000 +#define ETM4_CFG_RES_SEL_PAIR1 0x00007000 +#define ETM4_CFG_RES_SEQ 0x00008000 +#define ETM4_CFG_RES_TS 0x00009000 +#define ETM4_CFG_RES_MASK 0x0000F000 + +/* ETM4 configuration handlers */ +int etm4_cfg_load_builtins(struct coresight_device *csdev); + +#endif /* _CORESIGHT_CORESIGHT_ETM4X_CFG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index b673e738bc9a..ec182544a064 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -9,6 +9,7 @@ #include <linux/sysfs.h> #include "coresight-etm4x.h" #include "coresight-priv.h" +#include "coresight-config.h"
static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) { @@ -269,6 +270,8 @@ static ssize_t reset_store(struct device *dev,
spin_unlock(&drvdata->spinlock);
+ coresight_cfg_reset_feats(to_coresight_device(dev)); + return size; } static DEVICE_ATTR_WO(reset); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 60a5133d801c..e69dbcf5cd4d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -34,6 +34,7 @@
#include "coresight-etm4x.h" #include "coresight-etm-perf.h" +#include "coresight-etm4x-cfg.h"
static int boot_enable; module_param(boot_enable, int, 0444); @@ -320,10 +321,11 @@ static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata) return ret; }
-static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, +static int etm4_parse_event_config(struct coresight_device *csdev, struct perf_event *event) { int ret = 0; + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_config *config = &drvdata->config; struct perf_event_attr *attr = &event->attr;
@@ -383,6 +385,9 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, /* bit[12], Return stack enable bit */ config->cfg |= BIT(12);
+ /* set any enabled features */ + ret = coresight_cfg_set_feats_on_ena(csdev); + out: return ret; } @@ -399,7 +404,7 @@ static int etm4_enable_perf(struct coresight_device *csdev, }
/* Configure the tracer based on the session's specifics */ - ret = etm4_parse_event_config(drvdata, event); + ret = etm4_parse_event_config(csdev, event); if (ret) goto out; /* And enable it */ @@ -415,6 +420,8 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) struct etm4_enable_arg arg = { }; int ret;
+ coresight_cfg_set_feats_on_ena(csdev); + spin_lock(&drvdata->spinlock);
/* @@ -532,6 +539,7 @@ static int etm4_disable_perf(struct coresight_device *csdev, return -EINVAL;
etm4_disable_hw(drvdata); + coresight_cfg_save_feats_on_dis(csdev);
/* * Check if the start/stop logic was active when the unit was stopped. @@ -566,6 +574,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) smp_call_function_single(drvdata->cpu, etm4_disable_hw, drvdata, 1);
spin_unlock(&drvdata->spinlock); + coresight_cfg_save_feats_on_dis(csdev); cpus_read_unlock();
dev_dbg(&csdev->dev, "ETM tracing disabled\n"); @@ -1510,6 +1519,11 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) goto err_arch_supported; }
+ /* load builtin features */ + ret = etm4_cfg_load_builtins(drvdata->csdev); + if (ret) + goto err_arch_supported; + pm_runtime_put(&adev->dev); dev_info(&drvdata->csdev->dev, "CPU%d: ETM v%d.%d initialized\n", drvdata->cpu, drvdata->arch >> 4, drvdata->arch & 0xf); diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index f3efbb3b2b4d..ac16d96124f6 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1335,6 +1335,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) ret = coresight_fixup_orphan_conns(csdev); if (!ret) cti_add_assoc_to_csdev(csdev); + INIT_LIST_HEAD(&csdev->feat_list);
mutex_unlock(&coresight_mutex); if (ret) { diff --git a/include/linux/coresight.h b/include/linux/coresight.h index e3e9f0e3a878..0e8d529282dd 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -204,6 +204,8 @@ struct coresight_device { /* sysfs links between components */ int nr_links; bool has_conns_grp; + /* feature list */ + struct list_head feat_list; };
/*
On 10/06/2020 10:13, Mike Leach wrote:
This set introduces the concept of complex features for CoreSight devices, and adds a feature to ETMv4 to enable strobing of the etm capture.
- Complex Features: These are programming settings for devices, especially
ETM which have many programmable register that can be combined to create complex functionality. Features appear as sub-directories in sysfs, having an enable parameter to set the full program setting.
- ETM strobing. This feature is introduced to ETMv4 to allow the trace
capture to be periodically switched on and off - strobed - to a given mark / space ratio to generate statistical trace data for workflows such as auto-fdo.
Signed-off-by: Mike Leach mike.leach@linaro.org
Hi Mike,
Good to see this set posted. Some comments inline.
Regards
Rob
drivers/hwtracing/coresight/Makefile | 7 +- .../hwtracing/coresight/coresight-config.c | 380 ++++++++++++++++++ .../hwtracing/coresight/coresight-config.h | 156 +++++++ .../hwtracing/coresight/coresight-etm4x-cfg.c | 325 +++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 29 ++ .../coresight/coresight-etm4x-sysfs.c | 3 + drivers/hwtracing/coresight/coresight-etm4x.c | 18 +- drivers/hwtracing/coresight/coresight.c | 1 + include/linux/coresight.h | 2 + 9 files changed, 917 insertions(+), 4 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-config.c create mode 100644 drivers/hwtracing/coresight/coresight-config.h create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.c create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.h
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 19497d1d92bf..b6c28f4cfca7 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -3,7 +3,8 @@ # Makefile for CoreSight drivers. # obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o \
coresight-platform.o coresight-sysfs.o
coresight-platform.o coresight-sysfs.o \
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \ coresight-tmc-etf.o \ coresight-tmc-etr.ocoresight-config.o
@@ -14,7 +15,9 @@ obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \
coresight-etm4x-sysfs.o
coresight-etm4x-sysfs.o \
coresight-etm4x-cfg.o
- obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o
diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c new file mode 100644 index 000000000000..10c2d546ca82 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/sysfs.h> +#include "coresight-config.h" +#include "coresight-priv.h"
+static DEFINE_MUTEX(coresight_cfg_mutex);
+/*
- Handlers for creating and manipulating the feature representation
- in sysfs.
- */
+/* base feature attribute info */ +struct feat_attr_info {
- const char *name;
- ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
- ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
+};
+static ssize_t cs_cfg_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
- struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
- int val;
- spin_lock(feat->dev_spinlock);
- val = feat->enabled;
- spin_unlock(feat->dev_spinlock);
- return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+static ssize_t cs_cfg_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
- struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
- int val;
- spin_lock(feat->dev_spinlock);
- if (!kstrtoint(buf, 0, &val))
feat->enabled = val;
- spin_unlock(feat->dev_spinlock);
- return size;
+}
+static ssize_t cs_cfg_desc_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
- struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
- return scnprintf(buf, PAGE_SIZE, "%s\n", feat->desc->brief);
+}
+struct feat_attr_info feature_std_attr[] = {
- {
.name = "enable",
.show = cs_cfg_enable_show,
.store = cs_cfg_enable_store,
- },
- {
.name = "description",
.show = cs_cfg_desc_show,
- },
- { 0 },
+};
+static ssize_t cs_cfg_param_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
- struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
- u64 val;
- spin_lock(param->feat->dev_spinlock);
- val = param->current_value;
- spin_unlock(param->feat->dev_spinlock);
- return scnprintf(buf, PAGE_SIZE, "%lld\n", val);
+}
+static ssize_t cs_cfg_param_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
- struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
- u64 val;
- spin_lock(param->feat->dev_spinlock);
- if (!kstrtoull(buf, 0, &val))
param->current_value = val;
- spin_unlock(param->feat->dev_spinlock);
- return size;
+}
+static struct attribute * +cs_cfg_sysfs_create_feat_attr(struct device *dev, struct cs_cfg_feature *feat,
struct feat_attr_info *info)
+{
- struct dev_ext_attribute *eattr;
- eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
- if (!eattr)
return NULL;
- eattr->attr.attr.name = devm_kstrdup(dev, info->name, GFP_KERNEL);
- eattr->attr.attr.mode = info->store ? 0644 : 0444;
- eattr->attr.show = info->show;
- eattr->attr.store = info->store;
- eattr->var = feat;
- return &eattr->attr.attr;
+}
+static struct attribute * +cs_cfg_sysfs_create_param_attr(struct device *dev, const char *name,
struct cs_cfg_parameter *param)
+{
- struct dev_ext_attribute *eattr;
- eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
- if (!eattr)
return NULL;
- eattr->attr.attr.name = devm_kstrdup(dev, name, GFP_KERNEL);
- eattr->attr.attr.mode = 0644;
- eattr->attr.show = cs_cfg_param_show;
- eattr->attr.store = cs_cfg_param_store;
- eattr->var = param;
- return &eattr->attr.attr;
+}
+int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat) +{
- int p, attr_idx = 0;
- struct attribute **attrs;
- const char *name;
- struct feat_attr_info *info;
- struct device *dev = feat->csdev->dev.parent;
- /* create sysfs group in csdev */
- feat->param_group = devm_kzalloc(dev, sizeof(struct attribute_group),
GFP_KERNEL);
- if (!feat->param_group)
return -ENOMEM;
- feat->param_group->name = devm_kasprintf(dev, GFP_KERNEL, "%s.feat",
feat->desc->name);
- if (!feat->param_group->name)
return -ENOMEM;
- /* attributes - nr_params + enable + desc */
- attrs = devm_kcalloc(dev, feat->nr_params + 2,
sizeof(struct attribute *), GFP_KERNEL);
- if (!attrs)
return -ENOMEM;
- /* create base attributes in group */
- while (feature_std_attr[attr_idx].name) {
info = &feature_std_attr[attr_idx];
attrs[attr_idx] = cs_cfg_sysfs_create_feat_attr(dev, feat,
info);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
- }
- /* create params in group */
- for (p = 0; p < feat->nr_params; p++) {
name = feat->desc->params[p].name;
attrs[attr_idx] =
cs_cfg_sysfs_create_param_attr(dev, name,
&feat->params[p]);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
- }
- feat->param_group->attrs = attrs;
- return sysfs_create_group(&feat->csdev->dev.kobj, feat->param_group);
+}
+/*
- standard routines to set/save/reset enabled features, and iterate
- groups of features on a device.
- */
+void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat) +{
- mutex_lock(&coresight_cfg_mutex);
- list_add(&feat->node, &feat->csdev->feat_list);
- mutex_unlock(&coresight_cfg_mutex);
+}
+inline void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags) +{
- u32 *p_val32 = (u32 *)reg->drv_store;
- u32 tmp32;
- if (!(flags & CS_CFG_REG_VAL_64BIT)) {
if (flags & CS_CFG_REG_VAL_MASK) {
tmp32 = *p_val32;
tmp32 &= ~reg->value.mask32;
tmp32 |= reg->value.val32 & reg->value.mask32;
*p_val32 = tmp32;
} else
*p_val32 = reg->value.val32;
- } else
*((u64 *)reg->drv_store) = reg->value.val64;
+}
+inline void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags) +{
- if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = *(u64 *)(reg->drv_store);
- else
reg->value.val32 = *(u32 *)(reg->drv_store);
+}
+inline void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc)
+{
- reg->value.val64 = desc->value.val64;
+}
+inline void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param)
+{
- if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = param->current_value;
- else
reg->value.val32 = (u32)param->current_value;
+}
+/* default set - will set values without resource checking */ +static int cs_cfg_set_on_enable(struct cs_cfg_feature *feat) +{
- int i;
- u32 flags;
- spin_lock(feat->dev_spinlock);
- if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
coresight_cfg_set_reg(&feat->regs[i], flags);
}
- }
- dev_dbg(&feat->csdev->dev, "Feature %s: %s", feat->desc->name,
feat->enabled ? "Set enabled" : "Skip disabled");
- spin_unlock(feat->dev_spinlock);
- return 0;
+}
+static void cs_cfg_save_on_disable(struct cs_cfg_feature *feat) +{
- int i;
- u32 flags;
- spin_lock(feat->dev_spinlock);
- if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
if (flags & CS_CFG_REG_VAL_SAVE)
coresight_cfg_save_reg(&feat->regs[i], flags);
}
- }
- spin_unlock(feat->dev_spinlock);
+}
+/* default reset - restore default values, disable feature */ +static void cs_cfg_reset_feat(struct cs_cfg_feature *feat) +{
- const struct cs_cfg_feature_desc *feat_desc = feat->desc;
- struct cs_cfg_reg_desc *reg_desc;
- struct cs_cfg_parameter *param;
- struct cs_cfg_reg *reg;
- int i;
- u32 flags;
- spin_lock(feat->dev_spinlock);
- feat->enabled = false;
- /*
* set the default values for all parameters and regs from the
* relevant static descriptors.
*/
- for (i = 0; i < feat->nr_params; i++)
feat->params[i].current_value = feat_desc->params[i].value;
- for (i = 0; i < feat->nr_regs; i++) {
reg_desc = &feat_desc->regs[i];
flags = reg_desc->flags;
reg = &feat->regs[i];
/* check if reg set from a parameter otherwise desc default */
if (flags & CS_CFG_REG_VAL_PARAM) {
param = &feat->params[reg_desc->value.val32];
coresight_cfg_init_reg_param(reg, flags, param);
} else
coresight_cfg_init_reg(reg, reg_desc);
- }
- spin_unlock(feat->dev_spinlock);
+}
+void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat) +{
- feat->ops.set_on_enable = cs_cfg_set_on_enable;
- feat->ops.save_on_disable = cs_cfg_save_on_disable;
- feat->ops.reset = cs_cfg_reset_feat;
+}
+int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev) +{
- struct cs_cfg_feature *feat;
- int err = 0;
- if (list_empty(&csdev->feat_list))
return 0;
- list_for_each_entry(feat, &csdev->feat_list, node) {
dev_dbg(&csdev->dev, "Found feature:%s", feat->desc->name);
if (feat->ops.set_on_enable) {
err = feat->ops.set_on_enable(feat);
dev_dbg(&csdev->dev, "Feature %s: %s",
feat->desc->name, err ? "Set failed" : "OK");
if (!err)
break;
}
- }
- return err;
+}
+void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev) +{
- struct cs_cfg_feature *feat;
- if (list_empty(&csdev->feat_list))
return;
- list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.save_on_disable)
feat->ops.save_on_disable(feat);
- }
+}
+void coresight_cfg_reset_feats(struct coresight_device *csdev) +{
- struct cs_cfg_feature *feat;
- if (list_empty(&csdev->feat_list))
return;
- list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.reset)
feat->ops.reset(feat);
- }
+} diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h new file mode 100644 index 000000000000..ce69ad5d2bbf --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2020 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CONFIG_H +#define _CORESIGHT_CORESIGHT_CONFIG_H
+/* CoreSight Configuration - complex component and system config handling */
+/* generic register flags */ +#define CS_CFG_REG_STD 0x80000000 /* reg is standard reg */ +#define CS_CFG_REG_RESOURCE 0x40000000 /* reg is a resource */ +#define CS_CFG_REG_VAL_PARAM 0x08000000 /* reg value uses param */ +#define CS_CFG_REG_VAL_MASK 0x04000000 /* reg value bit masked */ +#define CS_CFG_REG_VAL_64BIT 0x02000000 /* reg value 64 bit */ +#define CS_CFG_REG_VAL_SAVE 0x01000000 /* reg value save on disable */ +#define CS_CFG_REG_ID_MASK 0x00000FFF /* reg offset / id in device */ +#define CS_CFG_REG_DEV_MASK 0x00FFF000 /* device specific flags */
+/**
- Parameter descriptor for a device feature.
- @name: Name of parameter.
- @value: Initial or default value.
- */
+struct cs_cfg_parameter_desc {
- const char *name;
- const u64 value;
+};
+/**
- Representation of register value.
- Supports full 64 bit register value, or 32 bit value with optional mask
- value.
- */
+union cs_cfg_regval {
- u64 val64;
- struct {
u32 val32;
u32 mask32;
- };
+};
+/**
- Register descriptor for a device feature
- @flags: flags defining the handling of the register.
- @value: Initial value & optional mask.
- */
+struct cs_cfg_reg_desc {
- const u32 flags;
- const union cs_cfg_regval value;
+};
+/**
- Device feature descriptor - combination of registers and parameters to
- program a device to implement a specific complex function.
- @name: feature name.
- @brief: brief description of the feature.
- @params: list of parameters used - 0 terminated.
- @reg: list of registers used.
- */
+struct cs_cfg_feature_desc {
- const char *name;
- const char *brief;
- struct cs_cfg_parameter_desc *params;
- struct cs_cfg_reg_desc *regs;
+};
+/**
- config parameter instance - part of a loaded feature.
- @feat: parent feature
- @current_value: current value of parameter - may be set by user via
sysfs, or modified during device operation.
- */
+struct cs_cfg_parameter {
- struct cs_cfg_feature *feat;
- u64 current_value;
+};
+/**
- config register instance - part of a loaded feature.
maps register values to driver structures
- @value: value to use when setting feature on device / store for
readback of volatile values.
- @drv_store: pointer to internal driver element used to set the value
in hardware.
- */
+struct cs_cfg_reg {
- union cs_cfg_regval value;
- void *drv_store;
+};
+/**
- cs_cfg_ops - standard operations for features.
- CS config core provides basic defaults for these, which can be overridden by
- device specific versions.
- @set_on_enable: Set the feature on the driver before hw enable.
- @save_on_disable: Save any volatile register values after hw disable.
- @reset: Reset feature to default values.
- */
+struct cs_cfg_ops {
- int (*set_on_enable)(struct cs_cfg_feature *feat);
- void (*save_on_disable)(struct cs_cfg_feature *feat);
- void (*reset)(struct cs_cfg_feature *feat);
+};
+/**
- Feature instance loaded into a CoreSight device driver.
- @desc: pointer to the static descriptor for this feature.
- @csdev: parent CoreSight device instance.
- @node: list entry into feature list for this device.
- @dev_spinlock: device spinlock.
- @enabled: feature is enabled on this device.
- @nr_params: number of parameters.
- @params: current parameter values on this device
- @param_group: sysfs group for feature parameters.
- @ops: standard ops to enable and disable features on devices.
- */
+struct cs_cfg_feature {
- const struct cs_cfg_feature_desc *desc;
- struct coresight_device *csdev;
- struct list_head node;
- spinlock_t *dev_spinlock;
- bool enabled;
- int nr_params;
- struct cs_cfg_parameter *params;
- struct attribute_group *param_group;
- int nr_regs;
- struct cs_cfg_reg *regs;
- struct cs_cfg_ops ops;
+};
+/* coresight config API functions */ +int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat); +int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev); +void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev); +void coresight_cfg_reset_feats(struct coresight_device *csdev); +void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat); +void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat); +void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc);
+void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param);
+#endif /* _CORESIGHT_CORESIGHT_CONFIG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c new file mode 100644 index 000000000000..240e0235c130 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include "coresight-etm4x.h" +#include "coresight-etm4x-cfg.h" +#include "coresight-priv.h"
+/* built in features for ETMv4 */
+/* strobe feature */
+/* register defines */ +#define STRB_REG_CTR (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_CTR) +#define STRB_REG_CTR_RB (STRB_REG_CTR | CS_CFG_REG_VAL_SAVE) +#define STRB_REG_CTR_PRM (STRB_REG_CTR | CS_CFG_REG_VAL_PARAM) +#define STRB_REG_SEQ (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEQ) +#define STRB_REG_SEL (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEL) +#define STRB_REG_VI (CS_CFG_REG_STD | CS_CFG_REG_VAL_MASK)
+static struct cs_cfg_parameter_desc strobe_params[] = {
- {
.name = "window",
.value = 1000,
- },
- {
.name = "period",
.value = 10000,
- },
- { 0 },
+};
+static struct cs_cfg_reg_desc strob_regs[] = {
- /* resource selectors */
- {
.flags = STRB_REG_SEL | TRCRSCTLRn(2),
.value = {
.val32 = 0x20001,
},
- },
- {
.flags = STRB_REG_SEQ | TRCRSCTLRn(3),
.value = {
.val32 = 0x20002,
}
- },
- /* strobe window counter 0 - reload from param 0 */
- {
.flags = STRB_REG_CTR_RB | TRCCNTVRn(0),
- },
- {
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(0),
.value = {
.val32 = 0,
},
- },
- {
.flags = STRB_REG_CTR | TRCCNTCTLRn(0),
.value = {
.val32 = 0x10001,
},
- },
- /* strobe period counter 1 - reload from param 1 */
- {
.flags = STRB_REG_CTR_RB | TRCCNTVRn(1),
- },
- {
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(1),
.value = {
.val32 = 1,
},
- },
- {
.flags = STRB_REG_CTR | TRCCNTCTLRn(1),
.value = {
.val32 = 0x8102,
},
- },
Using 2 counters for strobing conflicts with CPU wide trace - which also uses a counter to emit periodic timestamps (to align trace streams from each CPU). As current ETM implementations only have 2 counters, there aren't enough for both, so trace start would fail on the original proof-of-concept patch. How will this be handled with this patch set? When strobing is enabled, it probably doesn't make much sense to use a counter to generate the timestamps - you will get a timestamp at the start of each burst, which should be enough to order bursts from different cores (and it's unlikely that multiple cores will be emitting a burst at the same time). Maybe the counter configuration for CPU wide should be another built in feature?
- /* sequencer */
- {
.flags = STRB_REG_SEQ | TRCSEQEVRn(0),
.value = {
.val32 = 0x0081,
},
- },
- {
.flags = STRB_REG_SEQ | TRCSEQEVRn(1),
.value = {
.val32 = 0x0000,
},
- },
The sequencer isn't required for strobing - I think that was some debugging left over from my original proof-of-concept patch.
- /* view-inst */
- {
.flags = STRB_REG_VI | TRCVICTLR,
.value = {
.val32 = 0x0003,
.mask32 = 0x0003,
},
- },
- /* end of regs */
- { 0 },
+};
+static struct cs_cfg_feature_desc strobe = {
- .name = "strobing",
- .brief = "Generate periodic trace capture windows.",
- .params = strobe_params,
- .regs = strob_regs,
+};
+static struct cs_cfg_feature_desc *built_in_feats[] = {
- &strobe,
- 0
+};
+/**
- etm4_cfg_map_reg_offset - validate and map the register offset into a
location in the driver config struct.
- Limits the number of registers that can be accessed and programmed in
- features, to those which are used to control the trace capture parameters.
- Omits or limits access to those which the driver must use exclusively.
- Invalid offsets will result in fail code return and feature load failure.
- @drvdata: driver data to map into.
- @reg: register to map.
- @offset: device offset for the register
- */
+static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata,
struct cs_cfg_reg *reg, u32 offset)
+{
- int err = 0, idx;
- struct etmv4_config *drvcfg = &drvdata->config;
- #define MAPREG(cval, elem) \
case cval: \
reg->drv_store = &drvcfg->elem; \
break;
- #define MAPREGIDX(cval, elem, off_idx) \
case cval: \
reg->drv_store = &drvcfg->elem[off_idx]; \
break;
- if (((offset >= TRCEVENTCTL0R) && (offset <= TRCVIPCSSCTLR)) ||
((offset >= TRCSEQRSTEVR) && (offset <= TRCEXTINSELR)) ||
((offset >= TRCCIDCCTLR0) && (offset <= TRCVMIDCCTLR1))) {
switch (offset) {
/* 32 bit single control and filter registers */
MAPREG(TRCEVENTCTL0R, eventctrl0);
MAPREG(TRCEVENTCTL1R, eventctrl1);
MAPREG(TRCSTALLCTLR, stall_ctrl);
MAPREG(TRCTSCTLR, ts_ctrl);
MAPREG(TRCSYNCPR, syncfreq);
MAPREG(TRCCCCTLR, ccctlr);
MAPREG(TRCBBCTLR, bb_ctrl);
MAPREG(TRCVICTLR, vinst_ctrl);
MAPREG(TRCVIIECTLR, viiectlr);
MAPREG(TRCVISSCTLR, vissctlr);
MAPREG(TRCVIPCSSCTLR, vipcssctlr);
MAPREG(TRCSEQRSTEVR, seq_rst);
MAPREG(TRCSEQSTR, seq_state);
MAPREG(TRCEXTINSELR, ext_inp);
MAPREG(TRCCIDCCTLR0, ctxid_mask0);
MAPREG(TRCCIDCCTLR1, ctxid_mask1);
MAPREG(TRCVMIDCCTLR0, vmid_mask0);
MAPREG(TRCVMIDCCTLR1, vmid_mask1);
default:
err = -EINVAL;
break;
}
- } else if ((offset & GENMASK(11, 4)) == TRCSEQEVRn(0)) {
/* sequencer state control registers */
idx = (offset & GENMASK(3, 0)) / 4;
if (idx < ETM_MAX_SEQ_STATES)
reg->drv_store = &drvcfg->seq_ctrl[idx];
else
err = -EINVAL;
- } else if ((offset >= TRCSSCCRn(0)) && (offset <= TRCSSPCICRn(7))) {
/* 32 bit, 8 off indexed register sets */
idx = (offset & GENMASK(4, 0)) / 4;
switch (offset & GENMASK(11, 5)) {
MAPREGIDX(TRCSSCCRn(0), ss_ctrl, idx);
MAPREGIDX(TRCSSCSRn(0), ss_status, idx);
MAPREGIDX(TRCSSPCICRn(0), ss_pe_cmp, idx);
default:
err = -EINVAL;
break;
}
- } else if ((offset >= TRCCIDCVRn(0)) && (offset <= TRCVMIDCVRn(7))) {
/* 64 bit, 8 off indexed register sets */
idx = (offset & GENMASK(5, 0)) / 8;
switch (offset & GENMASK(11, 6)) {
MAPREGIDX(TRCCIDCVRn(0), ctxid_pid, idx);
MAPREGIDX(TRCVMIDCVRn(0), vmid_val, idx);
default:
err = -EINVAL;
break;
}
- } else if ((offset >= TRCRSCTLRn(2)) &&
(offset <= TRCRSCTLRn((ETM_MAX_RES_SEL - 1)))) {
/* 32 bit resource selection regs, 32 off, skip fixed 0,1 */
idx = (offset & GENMASK(6, 0)) / 4;
if (idx < ETM_MAX_RES_SEL)
reg->drv_store = &drvcfg->res_ctrl[idx];
else
err = -EINVAL;
- } else if ((offset >= TRCACVRn(0)) &&
(offset <= TRCACATRn((ETM_MAX_SINGLE_ADDR_CMP - 1)))) {
/* 64 bit addr cmp regs, 16 off */
idx = (offset & GENMASK(6, 0)) / 8;
switch (offset & GENMASK(11, 7)) {
MAPREGIDX(TRCACVRn(0), addr_val, idx);
MAPREGIDX(TRCACATRn(0), addr_acc, idx);
default:
err = -EINVAL;
break;
}
- } else if ((offset >= TRCCNTRLDVRn(0)) &&
(offset <= TRCCNTVRn((ETMv4_MAX_CNTR - 1)))) {
/* 32 bit counter regs, 4 off (ETMv4_MAX_CNTR - 1) */
idx = (offset & GENMASK(3, 0)) / 4;
switch (offset & GENMASK(11, 4)) {
MAPREGIDX(TRCCNTRLDVRn(0), cntrldvr, idx);
MAPREGIDX(TRCCNTCTLRn(0), cntr_ctrl, idx);
MAPREGIDX(TRCCNTVRn(0), cntr_val, idx);
default:
err = -EINVAL;
break;
}
- } else {
err = -EINVAL;
- }
- return err;
+}
+/**
- etm4_cfg_load_feature - load a feature into a device instance.
- @csdev: An ETMv4 CoreSight device.
- @feat_desc: The static feature descriptor to be loaded.
- The function will load a feature instance into the device, based on the
- supplied descriptor. The descriptor will be checked to ensure a valid
- feature can be loaded for this ETMv4 device.
- Parameter and register definitions will be converted into internal
- structures that are used to set the values in the driver when the
- feature is enabled for the device.
- */
+int etm4_cfg_load_feature(struct coresight_device *csdev,
const struct cs_cfg_feature_desc *feat_desc)
+{
- struct device *dev = csdev->dev.parent;
- struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
- struct cs_cfg_feature *feat;
- u32 offset;
- int i = 0, err = 0;
- feat = devm_kzalloc(dev, sizeof(struct cs_cfg_feature), GFP_KERNEL);
- if (!feat)
return -ENOMEM;
- feat->desc = feat_desc;
- feat->dev_spinlock = &drvdata->spinlock;
- feat->csdev = csdev;
- /* count the params regs and allocate space. */
- while (feat_desc->params[i++].name)
feat->nr_params++;
- i = 0;
- while (feat_desc->regs[i++].flags)
feat->nr_regs++;
- feat->params = devm_kcalloc(dev, feat->nr_params,
sizeof(struct cs_cfg_parameter),
GFP_KERNEL);
- if (!feat->params)
return -ENOMEM;
- feat->regs = devm_kcalloc(dev, feat->nr_regs,
sizeof(struct cs_cfg_reg), GFP_KERNEL);
- if (!feat->regs)
return -ENOMEM;
- /* load the parameters default values */
- for (i = 0; i < feat->nr_params; i++)
feat->params[i].feat = feat;
- /* process the register descriptions */
- for (i = 0; i < feat->nr_regs; i++) {
offset = feat_desc->regs[i].flags & CS_CFG_REG_ID_MASK;
err = etm4_cfg_map_reg_offset(drvdata, &feat->regs[i], offset);
if (err)
return err;
- }
- /* set sysfs representation of the feature */
- err = coresight_cfg_sysfs_add_grp(feat);
- /* add the feature to the device list and init default values */
- if (!err) {
coresight_cfg_list_add_feat(feat);
coresight_cfg_set_def_ops(feat);
feat->ops.reset(feat);
- }
- return err;
+}
+/* load up the list of builtin features */ +int etm4_cfg_load_builtins(struct coresight_device *csdev) +{
- int idx = 0, err = 0;
- while (built_in_feats[idx] && !err)
err = etm4_cfg_load_feature(csdev, built_in_feats[idx++]);
- return err;
+} diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h new file mode 100644 index 000000000000..703657cac2fc --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
- */
+#ifndef _CORESIGHT_CORESIGHT_ETM4X_CFG_H +#define _CORESIGHT_CORESIGHT_ETM4X_CFG_H
+#include "coresight-config.h"
+/* ETMv4 specific config defines */
+/* resource IDs */
+#define ETM4_CFG_RES_CTR 0x00001000 +#define ETM4_CFG_RES_CMP 0x00002000 +#define ETM4_CFG_RES_CMP_PAIR0 0x00003000 +#define ETM4_CFG_RES_CMP_PAIR1 0x00004000 +#define ETM4_CFG_RES_SEL 0x00005000 +#define ETM4_CFG_RES_SEL_PAIR0 0x00006000 +#define ETM4_CFG_RES_SEL_PAIR1 0x00007000 +#define ETM4_CFG_RES_SEQ 0x00008000 +#define ETM4_CFG_RES_TS 0x00009000 +#define ETM4_CFG_RES_MASK 0x0000F000
+/* ETM4 configuration handlers */ +int etm4_cfg_load_builtins(struct coresight_device *csdev);
+#endif /* _CORESIGHT_CORESIGHT_ETM4X_CFG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index b673e738bc9a..ec182544a064 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -9,6 +9,7 @@ #include <linux/sysfs.h> #include "coresight-etm4x.h" #include "coresight-priv.h" +#include "coresight-config.h" static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) { @@ -269,6 +270,8 @@ static ssize_t reset_store(struct device *dev, spin_unlock(&drvdata->spinlock);
- coresight_cfg_reset_feats(to_coresight_device(dev));
- return size; } static DEVICE_ATTR_WO(reset);
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 60a5133d801c..e69dbcf5cd4d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -34,6 +34,7 @@ #include "coresight-etm4x.h" #include "coresight-etm-perf.h" +#include "coresight-etm4x-cfg.h" static int boot_enable; module_param(boot_enable, int, 0444); @@ -320,10 +321,11 @@ static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata) return ret; } -static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, +static int etm4_parse_event_config(struct coresight_device *csdev, struct perf_event *event) { int ret = 0;
- struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_config *config = &drvdata->config; struct perf_event_attr *attr = &event->attr;
@@ -383,6 +385,9 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, /* bit[12], Return stack enable bit */ config->cfg |= BIT(12);
- /* set any enabled features */
- ret = coresight_cfg_set_feats_on_ena(csdev);
- out: return ret; }
@@ -399,7 +404,7 @@ static int etm4_enable_perf(struct coresight_device *csdev, } /* Configure the tracer based on the session's specifics */
- ret = etm4_parse_event_config(drvdata, event);
- ret = etm4_parse_event_config(csdev, event); if (ret) goto out; /* And enable it */
@@ -415,6 +420,8 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) struct etm4_enable_arg arg = { }; int ret;
- coresight_cfg_set_feats_on_ena(csdev);
- spin_lock(&drvdata->spinlock);
/* @@ -532,6 +539,7 @@ static int etm4_disable_perf(struct coresight_device *csdev, return -EINVAL; etm4_disable_hw(drvdata);
- coresight_cfg_save_feats_on_dis(csdev);
/* * Check if the start/stop logic was active when the unit was stopped. @@ -566,6 +574,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) smp_call_function_single(drvdata->cpu, etm4_disable_hw, drvdata, 1); spin_unlock(&drvdata->spinlock);
- coresight_cfg_save_feats_on_dis(csdev); cpus_read_unlock();
dev_dbg(&csdev->dev, "ETM tracing disabled\n"); @@ -1510,6 +1519,11 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) goto err_arch_supported; }
- /* load builtin features */
- ret = etm4_cfg_load_builtins(drvdata->csdev);
- if (ret)
goto err_arch_supported;
- pm_runtime_put(&adev->dev); dev_info(&drvdata->csdev->dev, "CPU%d: ETM v%d.%d initialized\n", drvdata->cpu, drvdata->arch >> 4, drvdata->arch & 0xf);
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index f3efbb3b2b4d..ac16d96124f6 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1335,6 +1335,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) ret = coresight_fixup_orphan_conns(csdev); if (!ret) cti_add_assoc_to_csdev(csdev);
- INIT_LIST_HEAD(&csdev->feat_list);
mutex_unlock(&coresight_mutex); if (ret) { diff --git a/include/linux/coresight.h b/include/linux/coresight.h index e3e9f0e3a878..0e8d529282dd 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -204,6 +204,8 @@ struct coresight_device { /* sysfs links between components */ int nr_links; bool has_conns_grp;
- /* feature list */
- struct list_head feat_list; };
/*
Hi Rob,
On Wed, 10 Jun 2020 at 16:25, Robert Walker robert.walker@arm.com wrote:
On 10/06/2020 10:13, Mike Leach wrote:
This set introduces the concept of complex features for CoreSight devices, and adds a feature to ETMv4 to enable strobing of the etm capture.
- Complex Features: These are programming settings for devices, especially
ETM which have many programmable register that can be combined to create complex functionality. Features appear as sub-directories in sysfs, having an enable parameter to set the full program setting.
- ETM strobing. This feature is introduced to ETMv4 to allow the trace
capture to be periodically switched on and off - strobed - to a given mark / space ratio to generate statistical trace data for workflows such as auto-fdo.
Signed-off-by: Mike Leach mike.leach@linaro.org
Hi Mike,
Good to see this set posted. Some comments inline.
Regards
Rob
drivers/hwtracing/coresight/Makefile | 7 +- .../hwtracing/coresight/coresight-config.c | 380 ++++++++++++++++++ .../hwtracing/coresight/coresight-config.h | 156 +++++++ .../hwtracing/coresight/coresight-etm4x-cfg.c | 325 +++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 29 ++ .../coresight/coresight-etm4x-sysfs.c | 3 + drivers/hwtracing/coresight/coresight-etm4x.c | 18 +- drivers/hwtracing/coresight/coresight.c | 1 + include/linux/coresight.h | 2 + 9 files changed, 917 insertions(+), 4 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-config.c create mode 100644 drivers/hwtracing/coresight/coresight-config.h create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.c create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.h
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 19497d1d92bf..b6c28f4cfca7 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -3,7 +3,8 @@ # Makefile for CoreSight drivers. # obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o \
coresight-platform.o coresight-sysfs.o
coresight-platform.o coresight-sysfs.o \
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \ coresight-tmc-etf.o \ coresight-tmc-etr.ocoresight-config.o
@@ -14,7 +15,9 @@ obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \
coresight-etm4x-sysfs.o
coresight-etm4x-sysfs.o \
coresight-etm4x-cfg.o
- obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o
diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c new file mode 100644 index 000000000000..10c2d546ca82 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/sysfs.h> +#include "coresight-config.h" +#include "coresight-priv.h"
+static DEFINE_MUTEX(coresight_cfg_mutex);
+/*
- Handlers for creating and manipulating the feature representation
- in sysfs.
- */
+/* base feature attribute info */ +struct feat_attr_info {
const char *name;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
+};
+static ssize_t cs_cfg_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
int val;
spin_lock(feat->dev_spinlock);
val = feat->enabled;
spin_unlock(feat->dev_spinlock);
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+static ssize_t cs_cfg_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
int val;
spin_lock(feat->dev_spinlock);
if (!kstrtoint(buf, 0, &val))
feat->enabled = val;
spin_unlock(feat->dev_spinlock);
return size;
+}
+static ssize_t cs_cfg_desc_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
return scnprintf(buf, PAGE_SIZE, "%s\n", feat->desc->brief);
+}
+struct feat_attr_info feature_std_attr[] = {
{
.name = "enable",
.show = cs_cfg_enable_show,
.store = cs_cfg_enable_store,
},
{
.name = "description",
.show = cs_cfg_desc_show,
},
{ 0 },
+};
+static ssize_t cs_cfg_param_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
u64 val;
spin_lock(param->feat->dev_spinlock);
val = param->current_value;
spin_unlock(param->feat->dev_spinlock);
return scnprintf(buf, PAGE_SIZE, "%lld\n", val);
+}
+static ssize_t cs_cfg_param_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
u64 val;
spin_lock(param->feat->dev_spinlock);
if (!kstrtoull(buf, 0, &val))
param->current_value = val;
spin_unlock(param->feat->dev_spinlock);
return size;
+}
+static struct attribute * +cs_cfg_sysfs_create_feat_attr(struct device *dev, struct cs_cfg_feature *feat,
struct feat_attr_info *info)
+{
struct dev_ext_attribute *eattr;
eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
if (!eattr)
return NULL;
eattr->attr.attr.name = devm_kstrdup(dev, info->name, GFP_KERNEL);
eattr->attr.attr.mode = info->store ? 0644 : 0444;
eattr->attr.show = info->show;
eattr->attr.store = info->store;
eattr->var = feat;
return &eattr->attr.attr;
+}
+static struct attribute * +cs_cfg_sysfs_create_param_attr(struct device *dev, const char *name,
struct cs_cfg_parameter *param)
+{
struct dev_ext_attribute *eattr;
eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
if (!eattr)
return NULL;
eattr->attr.attr.name = devm_kstrdup(dev, name, GFP_KERNEL);
eattr->attr.attr.mode = 0644;
eattr->attr.show = cs_cfg_param_show;
eattr->attr.store = cs_cfg_param_store;
eattr->var = param;
return &eattr->attr.attr;
+}
+int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat) +{
int p, attr_idx = 0;
struct attribute **attrs;
const char *name;
struct feat_attr_info *info;
struct device *dev = feat->csdev->dev.parent;
/* create sysfs group in csdev */
feat->param_group = devm_kzalloc(dev, sizeof(struct attribute_group),
GFP_KERNEL);
if (!feat->param_group)
return -ENOMEM;
feat->param_group->name = devm_kasprintf(dev, GFP_KERNEL, "%s.feat",
feat->desc->name);
if (!feat->param_group->name)
return -ENOMEM;
/* attributes - nr_params + enable + desc */
attrs = devm_kcalloc(dev, feat->nr_params + 2,
sizeof(struct attribute *), GFP_KERNEL);
if (!attrs)
return -ENOMEM;
/* create base attributes in group */
while (feature_std_attr[attr_idx].name) {
info = &feature_std_attr[attr_idx];
attrs[attr_idx] = cs_cfg_sysfs_create_feat_attr(dev, feat,
info);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
}
/* create params in group */
for (p = 0; p < feat->nr_params; p++) {
name = feat->desc->params[p].name;
attrs[attr_idx] =
cs_cfg_sysfs_create_param_attr(dev, name,
&feat->params[p]);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
}
feat->param_group->attrs = attrs;
return sysfs_create_group(&feat->csdev->dev.kobj, feat->param_group);
+}
+/*
- standard routines to set/save/reset enabled features, and iterate
- groups of features on a device.
- */
+void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat) +{
mutex_lock(&coresight_cfg_mutex);
list_add(&feat->node, &feat->csdev->feat_list);
mutex_unlock(&coresight_cfg_mutex);
+}
+inline void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags) +{
u32 *p_val32 = (u32 *)reg->drv_store;
u32 tmp32;
if (!(flags & CS_CFG_REG_VAL_64BIT)) {
if (flags & CS_CFG_REG_VAL_MASK) {
tmp32 = *p_val32;
tmp32 &= ~reg->value.mask32;
tmp32 |= reg->value.val32 & reg->value.mask32;
*p_val32 = tmp32;
} else
*p_val32 = reg->value.val32;
} else
*((u64 *)reg->drv_store) = reg->value.val64;
+}
+inline void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags) +{
if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = *(u64 *)(reg->drv_store);
else
reg->value.val32 = *(u32 *)(reg->drv_store);
+}
+inline void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc)
+{
reg->value.val64 = desc->value.val64;
+}
+inline void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param)
+{
if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = param->current_value;
else
reg->value.val32 = (u32)param->current_value;
+}
+/* default set - will set values without resource checking */ +static int cs_cfg_set_on_enable(struct cs_cfg_feature *feat) +{
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
coresight_cfg_set_reg(&feat->regs[i], flags);
}
}
dev_dbg(&feat->csdev->dev, "Feature %s: %s", feat->desc->name,
feat->enabled ? "Set enabled" : "Skip disabled");
spin_unlock(feat->dev_spinlock);
return 0;
+}
+static void cs_cfg_save_on_disable(struct cs_cfg_feature *feat) +{
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
if (flags & CS_CFG_REG_VAL_SAVE)
coresight_cfg_save_reg(&feat->regs[i], flags);
}
}
spin_unlock(feat->dev_spinlock);
+}
+/* default reset - restore default values, disable feature */ +static void cs_cfg_reset_feat(struct cs_cfg_feature *feat) +{
const struct cs_cfg_feature_desc *feat_desc = feat->desc;
struct cs_cfg_reg_desc *reg_desc;
struct cs_cfg_parameter *param;
struct cs_cfg_reg *reg;
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
feat->enabled = false;
/*
* set the default values for all parameters and regs from the
* relevant static descriptors.
*/
for (i = 0; i < feat->nr_params; i++)
feat->params[i].current_value = feat_desc->params[i].value;
for (i = 0; i < feat->nr_regs; i++) {
reg_desc = &feat_desc->regs[i];
flags = reg_desc->flags;
reg = &feat->regs[i];
/* check if reg set from a parameter otherwise desc default */
if (flags & CS_CFG_REG_VAL_PARAM) {
param = &feat->params[reg_desc->value.val32];
coresight_cfg_init_reg_param(reg, flags, param);
} else
coresight_cfg_init_reg(reg, reg_desc);
}
spin_unlock(feat->dev_spinlock);
+}
+void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat) +{
feat->ops.set_on_enable = cs_cfg_set_on_enable;
feat->ops.save_on_disable = cs_cfg_save_on_disable;
feat->ops.reset = cs_cfg_reset_feat;
+}
+int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
int err = 0;
if (list_empty(&csdev->feat_list))
return 0;
list_for_each_entry(feat, &csdev->feat_list, node) {
dev_dbg(&csdev->dev, "Found feature:%s", feat->desc->name);
if (feat->ops.set_on_enable) {
err = feat->ops.set_on_enable(feat);
dev_dbg(&csdev->dev, "Feature %s: %s",
feat->desc->name, err ? "Set failed" : "OK");
if (!err)
break;
}
}
return err;
+}
+void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
if (list_empty(&csdev->feat_list))
return;
list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.save_on_disable)
feat->ops.save_on_disable(feat);
}
+}
+void coresight_cfg_reset_feats(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
if (list_empty(&csdev->feat_list))
return;
list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.reset)
feat->ops.reset(feat);
}
+} diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h new file mode 100644 index 000000000000..ce69ad5d2bbf --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2020 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CONFIG_H +#define _CORESIGHT_CORESIGHT_CONFIG_H
+/* CoreSight Configuration - complex component and system config handling */
+/* generic register flags */ +#define CS_CFG_REG_STD 0x80000000 /* reg is standard reg */ +#define CS_CFG_REG_RESOURCE 0x40000000 /* reg is a resource */ +#define CS_CFG_REG_VAL_PARAM 0x08000000 /* reg value uses param */ +#define CS_CFG_REG_VAL_MASK 0x04000000 /* reg value bit masked */ +#define CS_CFG_REG_VAL_64BIT 0x02000000 /* reg value 64 bit */ +#define CS_CFG_REG_VAL_SAVE 0x01000000 /* reg value save on disable */ +#define CS_CFG_REG_ID_MASK 0x00000FFF /* reg offset / id in device */ +#define CS_CFG_REG_DEV_MASK 0x00FFF000 /* device specific flags */
+/**
- Parameter descriptor for a device feature.
- @name: Name of parameter.
- @value: Initial or default value.
- */
+struct cs_cfg_parameter_desc {
const char *name;
const u64 value;
+};
+/**
- Representation of register value.
- Supports full 64 bit register value, or 32 bit value with optional mask
- value.
- */
+union cs_cfg_regval {
u64 val64;
struct {
u32 val32;
u32 mask32;
};
+};
+/**
- Register descriptor for a device feature
- @flags: flags defining the handling of the register.
- @value: Initial value & optional mask.
- */
+struct cs_cfg_reg_desc {
const u32 flags;
const union cs_cfg_regval value;
+};
+/**
- Device feature descriptor - combination of registers and parameters to
- program a device to implement a specific complex function.
- @name: feature name.
- @brief: brief description of the feature.
- @params: list of parameters used - 0 terminated.
- @reg: list of registers used.
- */
+struct cs_cfg_feature_desc {
const char *name;
const char *brief;
struct cs_cfg_parameter_desc *params;
struct cs_cfg_reg_desc *regs;
+};
+/**
- config parameter instance - part of a loaded feature.
- @feat: parent feature
- @current_value: current value of parameter - may be set by user via
sysfs, or modified during device operation.
- */
+struct cs_cfg_parameter {
struct cs_cfg_feature *feat;
u64 current_value;
+};
+/**
- config register instance - part of a loaded feature.
maps register values to driver structures
- @value: value to use when setting feature on device / store for
readback of volatile values.
- @drv_store: pointer to internal driver element used to set the value
in hardware.
- */
+struct cs_cfg_reg {
union cs_cfg_regval value;
void *drv_store;
+};
+/**
- cs_cfg_ops - standard operations for features.
- CS config core provides basic defaults for these, which can be overridden by
- device specific versions.
- @set_on_enable: Set the feature on the driver before hw enable.
- @save_on_disable: Save any volatile register values after hw disable.
- @reset: Reset feature to default values.
- */
+struct cs_cfg_ops {
int (*set_on_enable)(struct cs_cfg_feature *feat);
void (*save_on_disable)(struct cs_cfg_feature *feat);
void (*reset)(struct cs_cfg_feature *feat);
+};
+/**
- Feature instance loaded into a CoreSight device driver.
- @desc: pointer to the static descriptor for this feature.
- @csdev: parent CoreSight device instance.
- @node: list entry into feature list for this device.
- @dev_spinlock: device spinlock.
- @enabled: feature is enabled on this device.
- @nr_params: number of parameters.
- @params: current parameter values on this device
- @param_group: sysfs group for feature parameters.
- @ops: standard ops to enable and disable features on devices.
- */
+struct cs_cfg_feature {
const struct cs_cfg_feature_desc *desc;
struct coresight_device *csdev;
struct list_head node;
spinlock_t *dev_spinlock;
bool enabled;
int nr_params;
struct cs_cfg_parameter *params;
struct attribute_group *param_group;
int nr_regs;
struct cs_cfg_reg *regs;
struct cs_cfg_ops ops;
+};
+/* coresight config API functions */ +int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat); +int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev); +void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev); +void coresight_cfg_reset_feats(struct coresight_device *csdev); +void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat); +void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat); +void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc);
+void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param);
+#endif /* _CORESIGHT_CORESIGHT_CONFIG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c new file mode 100644 index 000000000000..240e0235c130 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include "coresight-etm4x.h" +#include "coresight-etm4x-cfg.h" +#include "coresight-priv.h"
+/* built in features for ETMv4 */
+/* strobe feature */
+/* register defines */ +#define STRB_REG_CTR (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_CTR) +#define STRB_REG_CTR_RB (STRB_REG_CTR | CS_CFG_REG_VAL_SAVE) +#define STRB_REG_CTR_PRM (STRB_REG_CTR | CS_CFG_REG_VAL_PARAM) +#define STRB_REG_SEQ (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEQ) +#define STRB_REG_SEL (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEL) +#define STRB_REG_VI (CS_CFG_REG_STD | CS_CFG_REG_VAL_MASK)
+static struct cs_cfg_parameter_desc strobe_params[] = {
{
.name = "window",
.value = 1000,
},
{
.name = "period",
.value = 10000,
},
{ 0 },
+};
+static struct cs_cfg_reg_desc strob_regs[] = {
/* resource selectors */
{
.flags = STRB_REG_SEL | TRCRSCTLRn(2),
.value = {
.val32 = 0x20001,
},
},
{
.flags = STRB_REG_SEQ | TRCRSCTLRn(3),
.value = {
.val32 = 0x20002,
}
},
/* strobe window counter 0 - reload from param 0 */
{
.flags = STRB_REG_CTR_RB | TRCCNTVRn(0),
},
{
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(0),
.value = {
.val32 = 0,
},
},
{
.flags = STRB_REG_CTR | TRCCNTCTLRn(0),
.value = {
.val32 = 0x10001,
},
},
/* strobe period counter 1 - reload from param 1 */
{
.flags = STRB_REG_CTR_RB | TRCCNTVRn(1),
},
{
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(1),
.value = {
.val32 = 1,
},
},
{
.flags = STRB_REG_CTR | TRCCNTCTLRn(1),
.value = {
.val32 = 0x8102,
},
},
Using 2 counters for strobing conflicts with CPU wide trace - which also uses a counter to emit periodic timestamps (to align trace streams from each CPU). As current ETM implementations only have 2 counters, there aren't enough for both, so trace start would fail on the original proof-of-concept patch. How will this be handled with this patch set?
As it stands, TS will be enabled, but the counters used by strobing.
When strobing is enabled, it probably doesn't make much sense to use a counter to generate the timestamps - you will get a timestamp at the start of each burst, which should be enough to order bursts from different cores (and it's unlikely that multiple cores will be emitting a burst at the same time). Maybe the counter configuration for CPU wide should be another built in feature?
You are quite correct and TS with counter will indeed be included in a future patch as a builtin - as will ETM4 resource management. What we can do is to soften the requirement for CPU wide TS to have an exclusive counter - it can "borrow" one simply by setting a resource event selector. In the strobe case this will not make any difference as the TS period will be the same as the window period so as you say, the TS appears at the start of each burst.
/* sequencer */
{
.flags = STRB_REG_SEQ | TRCSEQEVRn(0),
.value = {
.val32 = 0x0081,
},
},
{
.flags = STRB_REG_SEQ | TRCSEQEVRn(1),
.value = {
.val32 = 0x0000,
},
},
The sequencer isn't required for strobing - I think that was some debugging left over from my original proof-of-concept patch.
Yes, I now remember us discussing this a while back when we were still actually allowed in the office.
Though having it in there did make this a nicer example to play with. I'll remove it though.
Mike
/* view-inst */
{
.flags = STRB_REG_VI | TRCVICTLR,
.value = {
.val32 = 0x0003,
.mask32 = 0x0003,
},
},
/* end of regs */
{ 0 },
+};
+static struct cs_cfg_feature_desc strobe = {
.name = "strobing",
.brief = "Generate periodic trace capture windows.",
.params = strobe_params,
.regs = strob_regs,
+};
+static struct cs_cfg_feature_desc *built_in_feats[] = {
&strobe,
0
+};
+/**
- etm4_cfg_map_reg_offset - validate and map the register offset into a
location in the driver config struct.
- Limits the number of registers that can be accessed and programmed in
- features, to those which are used to control the trace capture parameters.
- Omits or limits access to those which the driver must use exclusively.
- Invalid offsets will result in fail code return and feature load failure.
- @drvdata: driver data to map into.
- @reg: register to map.
- @offset: device offset for the register
- */
+static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata,
struct cs_cfg_reg *reg, u32 offset)
+{
int err = 0, idx;
struct etmv4_config *drvcfg = &drvdata->config;
#define MAPREG(cval, elem) \
case cval: \
reg->drv_store = &drvcfg->elem; \
break;
#define MAPREGIDX(cval, elem, off_idx) \
case cval: \
reg->drv_store = &drvcfg->elem[off_idx]; \
break;
if (((offset >= TRCEVENTCTL0R) && (offset <= TRCVIPCSSCTLR)) ||
((offset >= TRCSEQRSTEVR) && (offset <= TRCEXTINSELR)) ||
((offset >= TRCCIDCCTLR0) && (offset <= TRCVMIDCCTLR1))) {
switch (offset) {
/* 32 bit single control and filter registers */
MAPREG(TRCEVENTCTL0R, eventctrl0);
MAPREG(TRCEVENTCTL1R, eventctrl1);
MAPREG(TRCSTALLCTLR, stall_ctrl);
MAPREG(TRCTSCTLR, ts_ctrl);
MAPREG(TRCSYNCPR, syncfreq);
MAPREG(TRCCCCTLR, ccctlr);
MAPREG(TRCBBCTLR, bb_ctrl);
MAPREG(TRCVICTLR, vinst_ctrl);
MAPREG(TRCVIIECTLR, viiectlr);
MAPREG(TRCVISSCTLR, vissctlr);
MAPREG(TRCVIPCSSCTLR, vipcssctlr);
MAPREG(TRCSEQRSTEVR, seq_rst);
MAPREG(TRCSEQSTR, seq_state);
MAPREG(TRCEXTINSELR, ext_inp);
MAPREG(TRCCIDCCTLR0, ctxid_mask0);
MAPREG(TRCCIDCCTLR1, ctxid_mask1);
MAPREG(TRCVMIDCCTLR0, vmid_mask0);
MAPREG(TRCVMIDCCTLR1, vmid_mask1);
default:
err = -EINVAL;
break;
}
} else if ((offset & GENMASK(11, 4)) == TRCSEQEVRn(0)) {
/* sequencer state control registers */
idx = (offset & GENMASK(3, 0)) / 4;
if (idx < ETM_MAX_SEQ_STATES)
reg->drv_store = &drvcfg->seq_ctrl[idx];
else
err = -EINVAL;
} else if ((offset >= TRCSSCCRn(0)) && (offset <= TRCSSPCICRn(7))) {
/* 32 bit, 8 off indexed register sets */
idx = (offset & GENMASK(4, 0)) / 4;
switch (offset & GENMASK(11, 5)) {
MAPREGIDX(TRCSSCCRn(0), ss_ctrl, idx);
MAPREGIDX(TRCSSCSRn(0), ss_status, idx);
MAPREGIDX(TRCSSPCICRn(0), ss_pe_cmp, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCCIDCVRn(0)) && (offset <= TRCVMIDCVRn(7))) {
/* 64 bit, 8 off indexed register sets */
idx = (offset & GENMASK(5, 0)) / 8;
switch (offset & GENMASK(11, 6)) {
MAPREGIDX(TRCCIDCVRn(0), ctxid_pid, idx);
MAPREGIDX(TRCVMIDCVRn(0), vmid_val, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCRSCTLRn(2)) &&
(offset <= TRCRSCTLRn((ETM_MAX_RES_SEL - 1)))) {
/* 32 bit resource selection regs, 32 off, skip fixed 0,1 */
idx = (offset & GENMASK(6, 0)) / 4;
if (idx < ETM_MAX_RES_SEL)
reg->drv_store = &drvcfg->res_ctrl[idx];
else
err = -EINVAL;
} else if ((offset >= TRCACVRn(0)) &&
(offset <= TRCACATRn((ETM_MAX_SINGLE_ADDR_CMP - 1)))) {
/* 64 bit addr cmp regs, 16 off */
idx = (offset & GENMASK(6, 0)) / 8;
switch (offset & GENMASK(11, 7)) {
MAPREGIDX(TRCACVRn(0), addr_val, idx);
MAPREGIDX(TRCACATRn(0), addr_acc, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCCNTRLDVRn(0)) &&
(offset <= TRCCNTVRn((ETMv4_MAX_CNTR - 1)))) {
/* 32 bit counter regs, 4 off (ETMv4_MAX_CNTR - 1) */
idx = (offset & GENMASK(3, 0)) / 4;
switch (offset & GENMASK(11, 4)) {
MAPREGIDX(TRCCNTRLDVRn(0), cntrldvr, idx);
MAPREGIDX(TRCCNTCTLRn(0), cntr_ctrl, idx);
MAPREGIDX(TRCCNTVRn(0), cntr_val, idx);
default:
err = -EINVAL;
break;
}
} else {
err = -EINVAL;
}
return err;
+}
+/**
- etm4_cfg_load_feature - load a feature into a device instance.
- @csdev: An ETMv4 CoreSight device.
- @feat_desc: The static feature descriptor to be loaded.
- The function will load a feature instance into the device, based on the
- supplied descriptor. The descriptor will be checked to ensure a valid
- feature can be loaded for this ETMv4 device.
- Parameter and register definitions will be converted into internal
- structures that are used to set the values in the driver when the
- feature is enabled for the device.
- */
+int etm4_cfg_load_feature(struct coresight_device *csdev,
const struct cs_cfg_feature_desc *feat_desc)
+{
struct device *dev = csdev->dev.parent;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
struct cs_cfg_feature *feat;
u32 offset;
int i = 0, err = 0;
feat = devm_kzalloc(dev, sizeof(struct cs_cfg_feature), GFP_KERNEL);
if (!feat)
return -ENOMEM;
feat->desc = feat_desc;
feat->dev_spinlock = &drvdata->spinlock;
feat->csdev = csdev;
/* count the params regs and allocate space. */
while (feat_desc->params[i++].name)
feat->nr_params++;
i = 0;
while (feat_desc->regs[i++].flags)
feat->nr_regs++;
feat->params = devm_kcalloc(dev, feat->nr_params,
sizeof(struct cs_cfg_parameter),
GFP_KERNEL);
if (!feat->params)
return -ENOMEM;
feat->regs = devm_kcalloc(dev, feat->nr_regs,
sizeof(struct cs_cfg_reg), GFP_KERNEL);
if (!feat->regs)
return -ENOMEM;
/* load the parameters default values */
for (i = 0; i < feat->nr_params; i++)
feat->params[i].feat = feat;
/* process the register descriptions */
for (i = 0; i < feat->nr_regs; i++) {
offset = feat_desc->regs[i].flags & CS_CFG_REG_ID_MASK;
err = etm4_cfg_map_reg_offset(drvdata, &feat->regs[i], offset);
if (err)
return err;
}
/* set sysfs representation of the feature */
err = coresight_cfg_sysfs_add_grp(feat);
/* add the feature to the device list and init default values */
if (!err) {
coresight_cfg_list_add_feat(feat);
coresight_cfg_set_def_ops(feat);
feat->ops.reset(feat);
}
return err;
+}
+/* load up the list of builtin features */ +int etm4_cfg_load_builtins(struct coresight_device *csdev) +{
int idx = 0, err = 0;
while (built_in_feats[idx] && !err)
err = etm4_cfg_load_feature(csdev, built_in_feats[idx++]);
return err;
+} diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h new file mode 100644 index 000000000000..703657cac2fc --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
- */
+#ifndef _CORESIGHT_CORESIGHT_ETM4X_CFG_H +#define _CORESIGHT_CORESIGHT_ETM4X_CFG_H
+#include "coresight-config.h"
+/* ETMv4 specific config defines */
+/* resource IDs */
+#define ETM4_CFG_RES_CTR 0x00001000 +#define ETM4_CFG_RES_CMP 0x00002000 +#define ETM4_CFG_RES_CMP_PAIR0 0x00003000 +#define ETM4_CFG_RES_CMP_PAIR1 0x00004000 +#define ETM4_CFG_RES_SEL 0x00005000 +#define ETM4_CFG_RES_SEL_PAIR0 0x00006000 +#define ETM4_CFG_RES_SEL_PAIR1 0x00007000 +#define ETM4_CFG_RES_SEQ 0x00008000 +#define ETM4_CFG_RES_TS 0x00009000 +#define ETM4_CFG_RES_MASK 0x0000F000
+/* ETM4 configuration handlers */ +int etm4_cfg_load_builtins(struct coresight_device *csdev);
+#endif /* _CORESIGHT_CORESIGHT_ETM4X_CFG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index b673e738bc9a..ec182544a064 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -9,6 +9,7 @@ #include <linux/sysfs.h> #include "coresight-etm4x.h" #include "coresight-priv.h" +#include "coresight-config.h"
static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) { @@ -269,6 +270,8 @@ static ssize_t reset_store(struct device *dev,
spin_unlock(&drvdata->spinlock);
coresight_cfg_reset_feats(to_coresight_device(dev));
} static DEVICE_ATTR_WO(reset);return size;
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 60a5133d801c..e69dbcf5cd4d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -34,6 +34,7 @@
#include "coresight-etm4x.h" #include "coresight-etm-perf.h" +#include "coresight-etm4x-cfg.h"
static int boot_enable; module_param(boot_enable, int, 0444); @@ -320,10 +321,11 @@ static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata) return ret; }
-static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, +static int etm4_parse_event_config(struct coresight_device *csdev, struct perf_event *event) { int ret = 0;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_config *config = &drvdata->config; struct perf_event_attr *attr = &event->attr;
@@ -383,6 +385,9 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, /* bit[12], Return stack enable bit */ config->cfg |= BIT(12);
/* set any enabled features */
ret = coresight_cfg_set_feats_on_ena(csdev);
- out: return ret; }
@@ -399,7 +404,7 @@ static int etm4_enable_perf(struct coresight_device *csdev, }
/* Configure the tracer based on the session's specifics */
ret = etm4_parse_event_config(drvdata, event);
ret = etm4_parse_event_config(csdev, event); if (ret) goto out; /* And enable it */
@@ -415,6 +420,8 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) struct etm4_enable_arg arg = { }; int ret;
coresight_cfg_set_feats_on_ena(csdev);
spin_lock(&drvdata->spinlock); /*
@@ -532,6 +539,7 @@ static int etm4_disable_perf(struct coresight_device *csdev, return -EINVAL;
etm4_disable_hw(drvdata);
coresight_cfg_save_feats_on_dis(csdev); /* * Check if the start/stop logic was active when the unit was stopped.
@@ -566,6 +574,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) smp_call_function_single(drvdata->cpu, etm4_disable_hw, drvdata, 1);
spin_unlock(&drvdata->spinlock);
coresight_cfg_save_feats_on_dis(csdev); cpus_read_unlock(); dev_dbg(&csdev->dev, "ETM tracing disabled\n");
@@ -1510,6 +1519,11 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) goto err_arch_supported; }
/* load builtin features */
ret = etm4_cfg_load_builtins(drvdata->csdev);
if (ret)
goto err_arch_supported;
pm_runtime_put(&adev->dev); dev_info(&drvdata->csdev->dev, "CPU%d: ETM v%d.%d initialized\n", drvdata->cpu, drvdata->arch >> 4, drvdata->arch & 0xf);
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index f3efbb3b2b4d..ac16d96124f6 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1335,6 +1335,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) ret = coresight_fixup_orphan_conns(csdev); if (!ret) cti_add_assoc_to_csdev(csdev);
INIT_LIST_HEAD(&csdev->feat_list); mutex_unlock(&coresight_mutex); if (ret) {
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index e3e9f0e3a878..0e8d529282dd 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -204,6 +204,8 @@ struct coresight_device { /* sysfs links between components */ int nr_links; bool has_conns_grp;
/* feature list */
struct list_head feat_list;
};
/*
Hi Mike,
It will take me a while to digest all this so expect comments to be staggered over the next few days, which will probably overflow into next week. Some minor comments below to start...
On Wed, Jun 10, 2020 at 10:13:32AM +0100, Mike Leach wrote:
This set introduces the concept of complex features for CoreSight devices, and adds a feature to ETMv4 to enable strobing of the etm capture.
- Complex Features: These are programming settings for devices, especially
ETM which have many programmable register that can be combined to create complex functionality. Features appear as sub-directories in sysfs, having an enable parameter to set the full program setting.
- ETM strobing. This feature is introduced to ETMv4 to allow the trace
capture to be periodically switched on and off - strobed - to a given mark / space ratio to generate statistical trace data for workflows such as auto-fdo.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/Makefile | 7 +- .../hwtracing/coresight/coresight-config.c | 380 ++++++++++++++++++ .../hwtracing/coresight/coresight-config.h | 156 +++++++ .../hwtracing/coresight/coresight-etm4x-cfg.c | 325 +++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 29 ++ .../coresight/coresight-etm4x-sysfs.c | 3 + drivers/hwtracing/coresight/coresight-etm4x.c | 18 +- drivers/hwtracing/coresight/coresight.c | 1 + include/linux/coresight.h | 2 + 9 files changed, 917 insertions(+), 4 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-config.c create mode 100644 drivers/hwtracing/coresight/coresight-config.h create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.c create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.h
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 19497d1d92bf..b6c28f4cfca7 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -3,7 +3,8 @@ # Makefile for CoreSight drivers. # obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o \
coresight-platform.o coresight-sysfs.o
coresight-platform.o coresight-sysfs.o \
coresight-config.o
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \ coresight-tmc-etf.o \ coresight-tmc-etr.o @@ -14,7 +15,9 @@ obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \
coresight-etm4x-sysfs.o
coresight-etm4x-sysfs.o \
coresight-etm4x-cfg.o
obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c new file mode 100644 index 000000000000..10c2d546ca82 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/sysfs.h> +#include "coresight-config.h" +#include "coresight-priv.h"
+static DEFINE_MUTEX(coresight_cfg_mutex);
+/*
- Handlers for creating and manipulating the feature representation
- in sysfs.
- */
+/* base feature attribute info */ +struct feat_attr_info {
- const char *name;
- ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
- ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
+};
+static ssize_t cs_cfg_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
- struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
- int val;
- spin_lock(feat->dev_spinlock);
- val = feat->enabled;
- spin_unlock(feat->dev_spinlock);
- return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+static ssize_t cs_cfg_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
- struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
- int val;
- spin_lock(feat->dev_spinlock);
- if (!kstrtoint(buf, 0, &val))
feat->enabled = val;
- spin_unlock(feat->dev_spinlock);
- return size;
+}
+static ssize_t cs_cfg_desc_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
- struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
- return scnprintf(buf, PAGE_SIZE, "%s\n", feat->desc->brief);
+}
+struct feat_attr_info feature_std_attr[] = {
- {
.name = "enable",
.show = cs_cfg_enable_show,
.store = cs_cfg_enable_store,
- },
- {
.name = "description",
.show = cs_cfg_desc_show,
- },
- { 0 },
+};
+static ssize_t cs_cfg_param_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
- struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
- u64 val;
- spin_lock(param->feat->dev_spinlock);
- val = param->current_value;
- spin_unlock(param->feat->dev_spinlock);
- return scnprintf(buf, PAGE_SIZE, "%lld\n", val);
+}
+static ssize_t cs_cfg_param_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
- struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
- u64 val;
- spin_lock(param->feat->dev_spinlock);
- if (!kstrtoull(buf, 0, &val))
param->current_value = val;
- spin_unlock(param->feat->dev_spinlock);
- return size;
+}
+static struct attribute * +cs_cfg_sysfs_create_feat_attr(struct device *dev, struct cs_cfg_feature *feat,
struct feat_attr_info *info)
+{
- struct dev_ext_attribute *eattr;
- eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
- if (!eattr)
return NULL;
- eattr->attr.attr.name = devm_kstrdup(dev, info->name, GFP_KERNEL);
- eattr->attr.attr.mode = info->store ? 0644 : 0444;
- eattr->attr.show = info->show;
- eattr->attr.store = info->store;
- eattr->var = feat;
- return &eattr->attr.attr;
+}
+static struct attribute * +cs_cfg_sysfs_create_param_attr(struct device *dev, const char *name,
struct cs_cfg_parameter *param)
+{
- struct dev_ext_attribute *eattr;
- eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
- if (!eattr)
return NULL;
- eattr->attr.attr.name = devm_kstrdup(dev, name, GFP_KERNEL);
- eattr->attr.attr.mode = 0644;
- eattr->attr.show = cs_cfg_param_show;
- eattr->attr.store = cs_cfg_param_store;
- eattr->var = param;
- return &eattr->attr.attr;
+}
+int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat) +{
- int p, attr_idx = 0;
- struct attribute **attrs;
- const char *name;
- struct feat_attr_info *info;
- struct device *dev = feat->csdev->dev.parent;
- /* create sysfs group in csdev */
- feat->param_group = devm_kzalloc(dev, sizeof(struct attribute_group),
GFP_KERNEL);
- if (!feat->param_group)
return -ENOMEM;
- feat->param_group->name = devm_kasprintf(dev, GFP_KERNEL, "%s.feat",
feat->desc->name);
- if (!feat->param_group->name)
return -ENOMEM;
- /* attributes - nr_params + enable + desc */
- attrs = devm_kcalloc(dev, feat->nr_params + 2,
sizeof(struct attribute *), GFP_KERNEL);
- if (!attrs)
return -ENOMEM;
- /* create base attributes in group */
- while (feature_std_attr[attr_idx].name) {
info = &feature_std_attr[attr_idx];
attrs[attr_idx] = cs_cfg_sysfs_create_feat_attr(dev, feat,
info);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
- }
- /* create params in group */
- for (p = 0; p < feat->nr_params; p++) {
name = feat->desc->params[p].name;
attrs[attr_idx] =
cs_cfg_sysfs_create_param_attr(dev, name,
&feat->params[p]);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
- }
- feat->param_group->attrs = attrs;
- return sysfs_create_group(&feat->csdev->dev.kobj, feat->param_group);
+}
+/*
- standard routines to set/save/reset enabled features, and iterate
- groups of features on a device.
- */
+void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat) +{
- mutex_lock(&coresight_cfg_mutex);
- list_add(&feat->node, &feat->csdev->feat_list);
- mutex_unlock(&coresight_cfg_mutex);
+}
+inline void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags) +{
- u32 *p_val32 = (u32 *)reg->drv_store;
- u32 tmp32;
- if (!(flags & CS_CFG_REG_VAL_64BIT)) {
if (flags & CS_CFG_REG_VAL_MASK) {
tmp32 = *p_val32;
tmp32 &= ~reg->value.mask32;
tmp32 |= reg->value.val32 & reg->value.mask32;
*p_val32 = tmp32;
} else
*p_val32 = reg->value.val32;
- } else
*((u64 *)reg->drv_store) = reg->value.val64;
+}
+inline void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags) +{
- if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = *(u64 *)(reg->drv_store);
- else
reg->value.val32 = *(u32 *)(reg->drv_store);
+}
+inline void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc)
+{
- reg->value.val64 = desc->value.val64;
+}
+inline void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param)
+{
- if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = param->current_value;
- else
reg->value.val32 = (u32)param->current_value;
+}
+/* default set - will set values without resource checking */ +static int cs_cfg_set_on_enable(struct cs_cfg_feature *feat) +{
- int i;
- u32 flags;
- spin_lock(feat->dev_spinlock);
- if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
coresight_cfg_set_reg(&feat->regs[i], flags);
}
- }
- dev_dbg(&feat->csdev->dev, "Feature %s: %s", feat->desc->name,
feat->enabled ? "Set enabled" : "Skip disabled");
- spin_unlock(feat->dev_spinlock);
- return 0;
+}
+static void cs_cfg_save_on_disable(struct cs_cfg_feature *feat) +{
- int i;
- u32 flags;
- spin_lock(feat->dev_spinlock);
- if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
if (flags & CS_CFG_REG_VAL_SAVE)
coresight_cfg_save_reg(&feat->regs[i], flags);
}
- }
- spin_unlock(feat->dev_spinlock);
+}
+/* default reset - restore default values, disable feature */ +static void cs_cfg_reset_feat(struct cs_cfg_feature *feat) +{
- const struct cs_cfg_feature_desc *feat_desc = feat->desc;
- struct cs_cfg_reg_desc *reg_desc;
- struct cs_cfg_parameter *param;
- struct cs_cfg_reg *reg;
- int i;
- u32 flags;
- spin_lock(feat->dev_spinlock);
- feat->enabled = false;
- /*
* set the default values for all parameters and regs from the
* relevant static descriptors.
*/
- for (i = 0; i < feat->nr_params; i++)
feat->params[i].current_value = feat_desc->params[i].value;
- for (i = 0; i < feat->nr_regs; i++) {
reg_desc = &feat_desc->regs[i];
flags = reg_desc->flags;
reg = &feat->regs[i];
/* check if reg set from a parameter otherwise desc default */
if (flags & CS_CFG_REG_VAL_PARAM) {
param = &feat->params[reg_desc->value.val32];
coresight_cfg_init_reg_param(reg, flags, param);
} else
coresight_cfg_init_reg(reg, reg_desc);
- }
- spin_unlock(feat->dev_spinlock);
+}
+void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat) +{
- feat->ops.set_on_enable = cs_cfg_set_on_enable;
- feat->ops.save_on_disable = cs_cfg_save_on_disable;
- feat->ops.reset = cs_cfg_reset_feat;
+}
+int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev) +{
- struct cs_cfg_feature *feat;
- int err = 0;
- if (list_empty(&csdev->feat_list))
return 0;
- list_for_each_entry(feat, &csdev->feat_list, node) {
dev_dbg(&csdev->dev, "Found feature:%s", feat->desc->name);
if (feat->ops.set_on_enable) {
err = feat->ops.set_on_enable(feat);
dev_dbg(&csdev->dev, "Feature %s: %s",
feat->desc->name, err ? "Set failed" : "OK");
if (!err)
break;
}
- }
- return err;
+}
+void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev) +{
- struct cs_cfg_feature *feat;
- if (list_empty(&csdev->feat_list))
return;
- list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.save_on_disable)
feat->ops.save_on_disable(feat);
- }
+}
+void coresight_cfg_reset_feats(struct coresight_device *csdev) +{
- struct cs_cfg_feature *feat;
- if (list_empty(&csdev->feat_list))
return;
- list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.reset)
feat->ops.reset(feat);
- }
+} diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h new file mode 100644 index 000000000000..ce69ad5d2bbf --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2020 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CONFIG_H +#define _CORESIGHT_CORESIGHT_CONFIG_H
+/* CoreSight Configuration - complex component and system config handling */
+/* generic register flags */ +#define CS_CFG_REG_STD 0x80000000 /* reg is standard reg */ +#define CS_CFG_REG_RESOURCE 0x40000000 /* reg is a resource */ +#define CS_CFG_REG_VAL_PARAM 0x08000000 /* reg value uses param */ +#define CS_CFG_REG_VAL_MASK 0x04000000 /* reg value bit masked */ +#define CS_CFG_REG_VAL_64BIT 0x02000000 /* reg value 64 bit */ +#define CS_CFG_REG_VAL_SAVE 0x01000000 /* reg value save on disable */ +#define CS_CFG_REG_ID_MASK 0x00000FFF /* reg offset / id in device */ +#define CS_CFG_REG_DEV_MASK 0x00FFF000 /* device specific flags */
+/**
- Parameter descriptor for a device feature.
- @name: Name of parameter.
- @value: Initial or default value.
- */
+struct cs_cfg_parameter_desc {
- const char *name;
- const u64 value;
+};
+/**
- Representation of register value.
- Supports full 64 bit register value, or 32 bit value with optional mask
- value.
- */
+union cs_cfg_regval {
- u64 val64;
- struct {
u32 val32;
u32 mask32;
- };
+};
+/**
- Register descriptor for a device feature
- @flags: flags defining the handling of the register.
- @value: Initial value & optional mask.
- */
+struct cs_cfg_reg_desc {
- const u32 flags;
- const union cs_cfg_regval value;
+};
+/**
- Device feature descriptor - combination of registers and parameters to
- program a device to implement a specific complex function.
- @name: feature name.
- @brief: brief description of the feature.
- @params: list of parameters used - 0 terminated.
- @reg: list of registers used.
- */
+struct cs_cfg_feature_desc {
- const char *name;
- const char *brief;
- struct cs_cfg_parameter_desc *params;
- struct cs_cfg_reg_desc *regs;
+};
+/**
- config parameter instance - part of a loaded feature.
- @feat: parent feature
- @current_value: current value of parameter - may be set by user via
sysfs, or modified during device operation.
- */
+struct cs_cfg_parameter {
- struct cs_cfg_feature *feat;
- u64 current_value;
+};
+/**
- config register instance - part of a loaded feature.
maps register values to driver structures
- @value: value to use when setting feature on device / store for
readback of volatile values.
- @drv_store: pointer to internal driver element used to set the value
in hardware.
- */
+struct cs_cfg_reg {
- union cs_cfg_regval value;
- void *drv_store;
+};
+/**
- cs_cfg_ops - standard operations for features.
- CS config core provides basic defaults for these, which can be overridden by
- device specific versions.
- @set_on_enable: Set the feature on the driver before hw enable.
- @save_on_disable: Save any volatile register values after hw disable.
- @reset: Reset feature to default values.
- */
+struct cs_cfg_ops {
- int (*set_on_enable)(struct cs_cfg_feature *feat);
- void (*save_on_disable)(struct cs_cfg_feature *feat);
- void (*reset)(struct cs_cfg_feature *feat);
+};
+/**
- Feature instance loaded into a CoreSight device driver.
- @desc: pointer to the static descriptor for this feature.
- @csdev: parent CoreSight device instance.
- @node: list entry into feature list for this device.
- @dev_spinlock: device spinlock.
- @enabled: feature is enabled on this device.
- @nr_params: number of parameters.
- @params: current parameter values on this device
- @param_group: sysfs group for feature parameters.
- @ops: standard ops to enable and disable features on devices.
- */
+struct cs_cfg_feature {
- const struct cs_cfg_feature_desc *desc;
- struct coresight_device *csdev;
- struct list_head node;
- spinlock_t *dev_spinlock;
- bool enabled;
- int nr_params;
- struct cs_cfg_parameter *params;
- struct attribute_group *param_group;
- int nr_regs;
- struct cs_cfg_reg *regs;
- struct cs_cfg_ops ops;
+};
+/* coresight config API functions */ +int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat); +int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev); +void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev); +void coresight_cfg_reset_feats(struct coresight_device *csdev); +void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat); +void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat); +void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc);
+void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param);
+#endif /* _CORESIGHT_CORESIGHT_CONFIG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c new file mode 100644 index 000000000000..240e0235c130 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include "coresight-etm4x.h" +#include "coresight-etm4x-cfg.h" +#include "coresight-priv.h"
+/* built in features for ETMv4 */
+/* strobe feature */
+/* register defines */ +#define STRB_REG_CTR (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_CTR) +#define STRB_REG_CTR_RB (STRB_REG_CTR | CS_CFG_REG_VAL_SAVE) +#define STRB_REG_CTR_PRM (STRB_REG_CTR | CS_CFG_REG_VAL_PARAM) +#define STRB_REG_SEQ (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEQ) +#define STRB_REG_SEL (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEL) +#define STRB_REG_VI (CS_CFG_REG_STD | CS_CFG_REG_VAL_MASK)
+static struct cs_cfg_parameter_desc strobe_params[] = {
- {
.name = "window",
.value = 1000,
- },
- {
.name = "period",
.value = 10000,
- },
- { 0 },
+};
+static struct cs_cfg_reg_desc strob_regs[] = {
- /* resource selectors */
- {
.flags = STRB_REG_SEL | TRCRSCTLRn(2),
.value = {
.val32 = 0x20001,
},
- },
- {
.flags = STRB_REG_SEQ | TRCRSCTLRn(3),
.value = {
.val32 = 0x20002,
}
- },
- /* strobe window counter 0 - reload from param 0 */
- {
.flags = STRB_REG_CTR_RB | TRCCNTVRn(0),
- },
- {
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(0),
.value = {
.val32 = 0,
},
- },
- {
.flags = STRB_REG_CTR | TRCCNTCTLRn(0),
.value = {
.val32 = 0x10001,
},
- },
- /* strobe period counter 1 - reload from param 1 */
- {
.flags = STRB_REG_CTR_RB | TRCCNTVRn(1),
- },
- {
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(1),
.value = {
.val32 = 1,
},
- },
- {
.flags = STRB_REG_CTR | TRCCNTCTLRn(1),
.value = {
.val32 = 0x8102,
},
- },
- /* sequencer */
- {
.flags = STRB_REG_SEQ | TRCSEQEVRn(0),
.value = {
.val32 = 0x0081,
},
- },
- {
.flags = STRB_REG_SEQ | TRCSEQEVRn(1),
.value = {
.val32 = 0x0000,
},
- },
- /* view-inst */
- {
.flags = STRB_REG_VI | TRCVICTLR,
.value = {
.val32 = 0x0003,
.mask32 = 0x0003,
},
- },
- /* end of regs */
- { 0 },
+};
+static struct cs_cfg_feature_desc strobe = {
- .name = "strobing",
- .brief = "Generate periodic trace capture windows.",
- .params = strobe_params,
- .regs = strob_regs,
+};
+static struct cs_cfg_feature_desc *built_in_feats[] = {
- &strobe,
- 0
+};
+/**
- etm4_cfg_map_reg_offset - validate and map the register offset into a
location in the driver config struct.
- Limits the number of registers that can be accessed and programmed in
- features, to those which are used to control the trace capture parameters.
- Omits or limits access to those which the driver must use exclusively.
- Invalid offsets will result in fail code return and feature load failure.
- @drvdata: driver data to map into.
- @reg: register to map.
- @offset: device offset for the register
- */
+static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata,
struct cs_cfg_reg *reg, u32 offset)
+{
- int err = 0, idx;
- struct etmv4_config *drvcfg = &drvdata->config;
- #define MAPREG(cval, elem) \
case cval: \
reg->drv_store = &drvcfg->elem; \
break;
- #define MAPREGIDX(cval, elem, off_idx) \
case cval: \
reg->drv_store = &drvcfg->elem[off_idx]; \
break;
- if (((offset >= TRCEVENTCTL0R) && (offset <= TRCVIPCSSCTLR)) ||
((offset >= TRCSEQRSTEVR) && (offset <= TRCEXTINSELR)) ||
((offset >= TRCCIDCCTLR0) && (offset <= TRCVMIDCCTLR1))) {
switch (offset) {
/* 32 bit single control and filter registers */
MAPREG(TRCEVENTCTL0R, eventctrl0);
MAPREG(TRCEVENTCTL1R, eventctrl1);
MAPREG(TRCSTALLCTLR, stall_ctrl);
MAPREG(TRCTSCTLR, ts_ctrl);
MAPREG(TRCSYNCPR, syncfreq);
MAPREG(TRCCCCTLR, ccctlr);
MAPREG(TRCBBCTLR, bb_ctrl);
MAPREG(TRCVICTLR, vinst_ctrl);
MAPREG(TRCVIIECTLR, viiectlr);
MAPREG(TRCVISSCTLR, vissctlr);
MAPREG(TRCVIPCSSCTLR, vipcssctlr);
MAPREG(TRCSEQRSTEVR, seq_rst);
MAPREG(TRCSEQSTR, seq_state);
MAPREG(TRCEXTINSELR, ext_inp);
MAPREG(TRCCIDCCTLR0, ctxid_mask0);
MAPREG(TRCCIDCCTLR1, ctxid_mask1);
MAPREG(TRCVMIDCCTLR0, vmid_mask0);
MAPREG(TRCVMIDCCTLR1, vmid_mask1);
default:
err = -EINVAL;
break;
}
- } else if ((offset & GENMASK(11, 4)) == TRCSEQEVRn(0)) {
/* sequencer state control registers */
idx = (offset & GENMASK(3, 0)) / 4;
if (idx < ETM_MAX_SEQ_STATES)
reg->drv_store = &drvcfg->seq_ctrl[idx];
else
err = -EINVAL;
- } else if ((offset >= TRCSSCCRn(0)) && (offset <= TRCSSPCICRn(7))) {
/* 32 bit, 8 off indexed register sets */
idx = (offset & GENMASK(4, 0)) / 4;
switch (offset & GENMASK(11, 5)) {
MAPREGIDX(TRCSSCCRn(0), ss_ctrl, idx);
MAPREGIDX(TRCSSCSRn(0), ss_status, idx);
MAPREGIDX(TRCSSPCICRn(0), ss_pe_cmp, idx);
default:
err = -EINVAL;
break;
}
- } else if ((offset >= TRCCIDCVRn(0)) && (offset <= TRCVMIDCVRn(7))) {
/* 64 bit, 8 off indexed register sets */
idx = (offset & GENMASK(5, 0)) / 8;
switch (offset & GENMASK(11, 6)) {
MAPREGIDX(TRCCIDCVRn(0), ctxid_pid, idx);
MAPREGIDX(TRCVMIDCVRn(0), vmid_val, idx);
default:
err = -EINVAL;
break;
}
- } else if ((offset >= TRCRSCTLRn(2)) &&
(offset <= TRCRSCTLRn((ETM_MAX_RES_SEL - 1)))) {
/* 32 bit resource selection regs, 32 off, skip fixed 0,1 */
idx = (offset & GENMASK(6, 0)) / 4;
if (idx < ETM_MAX_RES_SEL)
reg->drv_store = &drvcfg->res_ctrl[idx];
else
err = -EINVAL;
- } else if ((offset >= TRCACVRn(0)) &&
(offset <= TRCACATRn((ETM_MAX_SINGLE_ADDR_CMP - 1)))) {
/* 64 bit addr cmp regs, 16 off */
idx = (offset & GENMASK(6, 0)) / 8;
switch (offset & GENMASK(11, 7)) {
MAPREGIDX(TRCACVRn(0), addr_val, idx);
MAPREGIDX(TRCACATRn(0), addr_acc, idx);
default:
err = -EINVAL;
break;
}
- } else if ((offset >= TRCCNTRLDVRn(0)) &&
(offset <= TRCCNTVRn((ETMv4_MAX_CNTR - 1)))) {
/* 32 bit counter regs, 4 off (ETMv4_MAX_CNTR - 1) */
idx = (offset & GENMASK(3, 0)) / 4;
switch (offset & GENMASK(11, 4)) {
MAPREGIDX(TRCCNTRLDVRn(0), cntrldvr, idx);
MAPREGIDX(TRCCNTCTLRn(0), cntr_ctrl, idx);
MAPREGIDX(TRCCNTVRn(0), cntr_val, idx);
default:
err = -EINVAL;
break;
}
- } else {
err = -EINVAL;
- }
- return err;
+}
+/**
- etm4_cfg_load_feature - load a feature into a device instance.
- @csdev: An ETMv4 CoreSight device.
- @feat_desc: The static feature descriptor to be loaded.
- The function will load a feature instance into the device, based on the
- supplied descriptor. The descriptor will be checked to ensure a valid
- feature can be loaded for this ETMv4 device.
- Parameter and register definitions will be converted into internal
- structures that are used to set the values in the driver when the
- feature is enabled for the device.
- */
+int etm4_cfg_load_feature(struct coresight_device *csdev,
const struct cs_cfg_feature_desc *feat_desc)
+{
- struct device *dev = csdev->dev.parent;
- struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
- struct cs_cfg_feature *feat;
- u32 offset;
- int i = 0, err = 0;
- feat = devm_kzalloc(dev, sizeof(struct cs_cfg_feature), GFP_KERNEL);
- if (!feat)
return -ENOMEM;
- feat->desc = feat_desc;
- feat->dev_spinlock = &drvdata->spinlock;
- feat->csdev = csdev;
- /* count the params regs and allocate space. */
- while (feat_desc->params[i++].name)
feat->nr_params++;
- i = 0;
- while (feat_desc->regs[i++].flags)
feat->nr_regs++;
- feat->params = devm_kcalloc(dev, feat->nr_params,
sizeof(struct cs_cfg_parameter),
GFP_KERNEL);
- if (!feat->params)
return -ENOMEM;
- feat->regs = devm_kcalloc(dev, feat->nr_regs,
sizeof(struct cs_cfg_reg), GFP_KERNEL);
- if (!feat->regs)
return -ENOMEM;
- /* load the parameters default values */
- for (i = 0; i < feat->nr_params; i++)
feat->params[i].feat = feat;
- /* process the register descriptions */
- for (i = 0; i < feat->nr_regs; i++) {
offset = feat_desc->regs[i].flags & CS_CFG_REG_ID_MASK;
err = etm4_cfg_map_reg_offset(drvdata, &feat->regs[i], offset);
if (err)
return err;
- }
- /* set sysfs representation of the feature */
- err = coresight_cfg_sysfs_add_grp(feat);
- /* add the feature to the device list and init default values */
- if (!err) {
coresight_cfg_list_add_feat(feat);
coresight_cfg_set_def_ops(feat);
feat->ops.reset(feat);
- }
- return err;
+}
+/* load up the list of builtin features */ +int etm4_cfg_load_builtins(struct coresight_device *csdev) +{
- int idx = 0, err = 0;
- while (built_in_feats[idx] && !err)
err = etm4_cfg_load_feature(csdev, built_in_feats[idx++]);
- return err;
+} diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h new file mode 100644 index 000000000000..703657cac2fc --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
- */
+#ifndef _CORESIGHT_CORESIGHT_ETM4X_CFG_H +#define _CORESIGHT_CORESIGHT_ETM4X_CFG_H
+#include "coresight-config.h"
+/* ETMv4 specific config defines */
+/* resource IDs */
+#define ETM4_CFG_RES_CTR 0x00001000 +#define ETM4_CFG_RES_CMP 0x00002000 +#define ETM4_CFG_RES_CMP_PAIR0 0x00003000 +#define ETM4_CFG_RES_CMP_PAIR1 0x00004000 +#define ETM4_CFG_RES_SEL 0x00005000 +#define ETM4_CFG_RES_SEL_PAIR0 0x00006000 +#define ETM4_CFG_RES_SEL_PAIR1 0x00007000 +#define ETM4_CFG_RES_SEQ 0x00008000 +#define ETM4_CFG_RES_TS 0x00009000 +#define ETM4_CFG_RES_MASK 0x0000F000
+/* ETM4 configuration handlers */ +int etm4_cfg_load_builtins(struct coresight_device *csdev);
+#endif /* _CORESIGHT_CORESIGHT_ETM4X_CFG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index b673e738bc9a..ec182544a064 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -9,6 +9,7 @@ #include <linux/sysfs.h> #include "coresight-etm4x.h" #include "coresight-priv.h" +#include "coresight-config.h" static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) { @@ -269,6 +270,8 @@ static ssize_t reset_store(struct device *dev, spin_unlock(&drvdata->spinlock);
- coresight_cfg_reset_feats(to_coresight_device(dev));
- return size;
} static DEVICE_ATTR_WO(reset); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 60a5133d801c..e69dbcf5cd4d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -34,6 +34,7 @@ #include "coresight-etm4x.h" #include "coresight-etm-perf.h" +#include "coresight-etm4x-cfg.h" static int boot_enable; module_param(boot_enable, int, 0444); @@ -320,10 +321,11 @@ static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata) return ret; } -static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, +static int etm4_parse_event_config(struct coresight_device *csdev, struct perf_event *event) { int ret = 0;
- struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_config *config = &drvdata->config; struct perf_event_attr *attr = &event->attr;
@@ -383,6 +385,9 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, /* bit[12], Return stack enable bit */ config->cfg |= BIT(12);
- /* set any enabled features */
- ret = coresight_cfg_set_feats_on_ena(csdev);
out: return ret; } @@ -399,7 +404,7 @@ static int etm4_enable_perf(struct coresight_device *csdev, } /* Configure the tracer based on the session's specifics */
- ret = etm4_parse_event_config(drvdata, event);
- ret = etm4_parse_event_config(csdev, event); if (ret) goto out; /* And enable it */
@@ -415,6 +420,8 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) struct etm4_enable_arg arg = { }; int ret;
- coresight_cfg_set_feats_on_ena(csdev);
- spin_lock(&drvdata->spinlock);
/* @@ -532,6 +539,7 @@ static int etm4_disable_perf(struct coresight_device *csdev, return -EINVAL; etm4_disable_hw(drvdata);
- coresight_cfg_save_feats_on_dis(csdev);
/* * Check if the start/stop logic was active when the unit was stopped. @@ -566,6 +574,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) smp_call_function_single(drvdata->cpu, etm4_disable_hw, drvdata, 1); spin_unlock(&drvdata->spinlock);
- coresight_cfg_save_feats_on_dis(csdev); cpus_read_unlock();
dev_dbg(&csdev->dev, "ETM tracing disabled\n"); @@ -1510,6 +1519,11 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) goto err_arch_supported; }
- /* load builtin features */
- ret = etm4_cfg_load_builtins(drvdata->csdev);
- if (ret)
goto err_arch_supported;
- pm_runtime_put(&adev->dev); dev_info(&drvdata->csdev->dev, "CPU%d: ETM v%d.%d initialized\n", drvdata->cpu, drvdata->arch >> 4, drvdata->arch & 0xf);
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index f3efbb3b2b4d..ac16d96124f6 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1335,6 +1335,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) ret = coresight_fixup_orphan_conns(csdev); if (!ret) cti_add_assoc_to_csdev(csdev);
- INIT_LIST_HEAD(&csdev->feat_list);
Please move out of the mutex area.
mutex_unlock(&coresight_mutex); if (ret) { diff --git a/include/linux/coresight.h b/include/linux/coresight.h index e3e9f0e3a878..0e8d529282dd 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -204,6 +204,8 @@ struct coresight_device { /* sysfs links between components */ int nr_links; bool has_conns_grp;
- /* feature list */
- struct list_head feat_list;
Please document the same way other fields are.
}; /* -- 2.17.1
Good day,
I like what you did in this set. It took me a while to come to terms with all the new structures but this is something we can address in subsequent revisions if need be. I also found a couple of minor problems but that too can be dealt with later. I would like to discuss 3 mains points:
1) Make the features configurable _and_ modular. Depending on how many features people come up with it may become tedious to carry all that extra code. For the modularity I suggest you take inspiration from the governor model used in the cpufreq subsystem. Governors, such as the performance governor[1], are all modules that get registered with the cpufreq core[2]. From there when a governor is selected[3] the core increases the governor's module count[4], preventing the module from being unloaded while a governor is used.
2) Please explore the possibility of using configfs rather than sysfs. That way users can create their own features without the need to add a new module.
3) Perf sessions need to be able to choose which feature they want to use rather than defaulting to a system wide configuration. If you don't want to deal with that at this stage - something I would certainly understand - simpy don't take features into account from the perf interface.
Thanks, Mathieu
[1]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq_perfo... [2]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L23... [3]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L76... [4]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L63...
On Wed, Jun 10, 2020 at 10:13:32AM +0100, Mike Leach wrote:
This set introduces the concept of complex features for CoreSight devices, and adds a feature to ETMv4 to enable strobing of the etm capture.
- Complex Features: These are programming settings for devices, especially
ETM which have many programmable register that can be combined to create complex functionality. Features appear as sub-directories in sysfs, having an enable parameter to set the full program setting.
- ETM strobing. This feature is introduced to ETMv4 to allow the trace
capture to be periodically switched on and off - strobed - to a given mark / space ratio to generate statistical trace data for workflows such as auto-fdo.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/Makefile | 7 +- .../hwtracing/coresight/coresight-config.c | 380 ++++++++++++++++++ .../hwtracing/coresight/coresight-config.h | 156 +++++++ .../hwtracing/coresight/coresight-etm4x-cfg.c | 325 +++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 29 ++ .../coresight/coresight-etm4x-sysfs.c | 3 + drivers/hwtracing/coresight/coresight-etm4x.c | 18 +- drivers/hwtracing/coresight/coresight.c | 1 + include/linux/coresight.h | 2 + 9 files changed, 917 insertions(+), 4 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-config.c create mode 100644 drivers/hwtracing/coresight/coresight-config.h create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.c create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.h
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 19497d1d92bf..b6c28f4cfca7 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -3,7 +3,8 @@ # Makefile for CoreSight drivers. # obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o \
coresight-platform.o coresight-sysfs.o
coresight-platform.o coresight-sysfs.o \
coresight-config.o
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \ coresight-tmc-etf.o \ coresight-tmc-etr.o @@ -14,7 +15,9 @@ obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \
coresight-etm4x-sysfs.o
coresight-etm4x-sysfs.o \
coresight-etm4x-cfg.o
obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c new file mode 100644 index 000000000000..10c2d546ca82 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/sysfs.h> +#include "coresight-config.h" +#include "coresight-priv.h"
+static DEFINE_MUTEX(coresight_cfg_mutex);
+/*
- Handlers for creating and manipulating the feature representation
- in sysfs.
- */
+/* base feature attribute info */ +struct feat_attr_info {
- const char *name;
- ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
- ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
+};
+static ssize_t cs_cfg_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
- struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
- int val;
- spin_lock(feat->dev_spinlock);
- val = feat->enabled;
- spin_unlock(feat->dev_spinlock);
- return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+static ssize_t cs_cfg_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
- struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
- int val;
- spin_lock(feat->dev_spinlock);
- if (!kstrtoint(buf, 0, &val))
feat->enabled = val;
- spin_unlock(feat->dev_spinlock);
- return size;
+}
+static ssize_t cs_cfg_desc_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
- struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
- return scnprintf(buf, PAGE_SIZE, "%s\n", feat->desc->brief);
+}
+struct feat_attr_info feature_std_attr[] = {
- {
.name = "enable",
.show = cs_cfg_enable_show,
.store = cs_cfg_enable_store,
- },
- {
.name = "description",
.show = cs_cfg_desc_show,
- },
- { 0 },
+};
+static ssize_t cs_cfg_param_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
- struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
- u64 val;
- spin_lock(param->feat->dev_spinlock);
- val = param->current_value;
- spin_unlock(param->feat->dev_spinlock);
- return scnprintf(buf, PAGE_SIZE, "%lld\n", val);
+}
+static ssize_t cs_cfg_param_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
- struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
- u64 val;
- spin_lock(param->feat->dev_spinlock);
- if (!kstrtoull(buf, 0, &val))
param->current_value = val;
- spin_unlock(param->feat->dev_spinlock);
- return size;
+}
+static struct attribute * +cs_cfg_sysfs_create_feat_attr(struct device *dev, struct cs_cfg_feature *feat,
struct feat_attr_info *info)
+{
- struct dev_ext_attribute *eattr;
- eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
- if (!eattr)
return NULL;
- eattr->attr.attr.name = devm_kstrdup(dev, info->name, GFP_KERNEL);
- eattr->attr.attr.mode = info->store ? 0644 : 0444;
- eattr->attr.show = info->show;
- eattr->attr.store = info->store;
- eattr->var = feat;
- return &eattr->attr.attr;
+}
+static struct attribute * +cs_cfg_sysfs_create_param_attr(struct device *dev, const char *name,
struct cs_cfg_parameter *param)
+{
- struct dev_ext_attribute *eattr;
- eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
- if (!eattr)
return NULL;
- eattr->attr.attr.name = devm_kstrdup(dev, name, GFP_KERNEL);
- eattr->attr.attr.mode = 0644;
- eattr->attr.show = cs_cfg_param_show;
- eattr->attr.store = cs_cfg_param_store;
- eattr->var = param;
- return &eattr->attr.attr;
+}
+int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat) +{
- int p, attr_idx = 0;
- struct attribute **attrs;
- const char *name;
- struct feat_attr_info *info;
- struct device *dev = feat->csdev->dev.parent;
- /* create sysfs group in csdev */
- feat->param_group = devm_kzalloc(dev, sizeof(struct attribute_group),
GFP_KERNEL);
- if (!feat->param_group)
return -ENOMEM;
- feat->param_group->name = devm_kasprintf(dev, GFP_KERNEL, "%s.feat",
feat->desc->name);
- if (!feat->param_group->name)
return -ENOMEM;
- /* attributes - nr_params + enable + desc */
- attrs = devm_kcalloc(dev, feat->nr_params + 2,
sizeof(struct attribute *), GFP_KERNEL);
- if (!attrs)
return -ENOMEM;
- /* create base attributes in group */
- while (feature_std_attr[attr_idx].name) {
info = &feature_std_attr[attr_idx];
attrs[attr_idx] = cs_cfg_sysfs_create_feat_attr(dev, feat,
info);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
- }
- /* create params in group */
- for (p = 0; p < feat->nr_params; p++) {
name = feat->desc->params[p].name;
attrs[attr_idx] =
cs_cfg_sysfs_create_param_attr(dev, name,
&feat->params[p]);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
- }
- feat->param_group->attrs = attrs;
- return sysfs_create_group(&feat->csdev->dev.kobj, feat->param_group);
+}
+/*
- standard routines to set/save/reset enabled features, and iterate
- groups of features on a device.
- */
+void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat) +{
- mutex_lock(&coresight_cfg_mutex);
- list_add(&feat->node, &feat->csdev->feat_list);
- mutex_unlock(&coresight_cfg_mutex);
+}
+inline void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags) +{
- u32 *p_val32 = (u32 *)reg->drv_store;
- u32 tmp32;
- if (!(flags & CS_CFG_REG_VAL_64BIT)) {
if (flags & CS_CFG_REG_VAL_MASK) {
tmp32 = *p_val32;
tmp32 &= ~reg->value.mask32;
tmp32 |= reg->value.val32 & reg->value.mask32;
*p_val32 = tmp32;
} else
*p_val32 = reg->value.val32;
- } else
*((u64 *)reg->drv_store) = reg->value.val64;
+}
+inline void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags) +{
- if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = *(u64 *)(reg->drv_store);
- else
reg->value.val32 = *(u32 *)(reg->drv_store);
+}
+inline void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc)
+{
- reg->value.val64 = desc->value.val64;
+}
+inline void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param)
+{
- if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = param->current_value;
- else
reg->value.val32 = (u32)param->current_value;
+}
+/* default set - will set values without resource checking */ +static int cs_cfg_set_on_enable(struct cs_cfg_feature *feat) +{
- int i;
- u32 flags;
- spin_lock(feat->dev_spinlock);
- if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
coresight_cfg_set_reg(&feat->regs[i], flags);
}
- }
- dev_dbg(&feat->csdev->dev, "Feature %s: %s", feat->desc->name,
feat->enabled ? "Set enabled" : "Skip disabled");
- spin_unlock(feat->dev_spinlock);
- return 0;
+}
+static void cs_cfg_save_on_disable(struct cs_cfg_feature *feat) +{
- int i;
- u32 flags;
- spin_lock(feat->dev_spinlock);
- if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
if (flags & CS_CFG_REG_VAL_SAVE)
coresight_cfg_save_reg(&feat->regs[i], flags);
}
- }
- spin_unlock(feat->dev_spinlock);
+}
+/* default reset - restore default values, disable feature */ +static void cs_cfg_reset_feat(struct cs_cfg_feature *feat) +{
- const struct cs_cfg_feature_desc *feat_desc = feat->desc;
- struct cs_cfg_reg_desc *reg_desc;
- struct cs_cfg_parameter *param;
- struct cs_cfg_reg *reg;
- int i;
- u32 flags;
- spin_lock(feat->dev_spinlock);
- feat->enabled = false;
- /*
* set the default values for all parameters and regs from the
* relevant static descriptors.
*/
- for (i = 0; i < feat->nr_params; i++)
feat->params[i].current_value = feat_desc->params[i].value;
- for (i = 0; i < feat->nr_regs; i++) {
reg_desc = &feat_desc->regs[i];
flags = reg_desc->flags;
reg = &feat->regs[i];
/* check if reg set from a parameter otherwise desc default */
if (flags & CS_CFG_REG_VAL_PARAM) {
param = &feat->params[reg_desc->value.val32];
coresight_cfg_init_reg_param(reg, flags, param);
} else
coresight_cfg_init_reg(reg, reg_desc);
- }
- spin_unlock(feat->dev_spinlock);
+}
+void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat) +{
- feat->ops.set_on_enable = cs_cfg_set_on_enable;
- feat->ops.save_on_disable = cs_cfg_save_on_disable;
- feat->ops.reset = cs_cfg_reset_feat;
+}
+int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev) +{
- struct cs_cfg_feature *feat;
- int err = 0;
- if (list_empty(&csdev->feat_list))
return 0;
- list_for_each_entry(feat, &csdev->feat_list, node) {
dev_dbg(&csdev->dev, "Found feature:%s", feat->desc->name);
if (feat->ops.set_on_enable) {
err = feat->ops.set_on_enable(feat);
dev_dbg(&csdev->dev, "Feature %s: %s",
feat->desc->name, err ? "Set failed" : "OK");
if (!err)
break;
}
- }
- return err;
+}
+void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev) +{
- struct cs_cfg_feature *feat;
- if (list_empty(&csdev->feat_list))
return;
- list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.save_on_disable)
feat->ops.save_on_disable(feat);
- }
+}
+void coresight_cfg_reset_feats(struct coresight_device *csdev) +{
- struct cs_cfg_feature *feat;
- if (list_empty(&csdev->feat_list))
return;
- list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.reset)
feat->ops.reset(feat);
- }
+} diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h new file mode 100644 index 000000000000..ce69ad5d2bbf --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2020 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CONFIG_H +#define _CORESIGHT_CORESIGHT_CONFIG_H
+/* CoreSight Configuration - complex component and system config handling */
+/* generic register flags */ +#define CS_CFG_REG_STD 0x80000000 /* reg is standard reg */ +#define CS_CFG_REG_RESOURCE 0x40000000 /* reg is a resource */ +#define CS_CFG_REG_VAL_PARAM 0x08000000 /* reg value uses param */ +#define CS_CFG_REG_VAL_MASK 0x04000000 /* reg value bit masked */ +#define CS_CFG_REG_VAL_64BIT 0x02000000 /* reg value 64 bit */ +#define CS_CFG_REG_VAL_SAVE 0x01000000 /* reg value save on disable */ +#define CS_CFG_REG_ID_MASK 0x00000FFF /* reg offset / id in device */ +#define CS_CFG_REG_DEV_MASK 0x00FFF000 /* device specific flags */
+/**
- Parameter descriptor for a device feature.
- @name: Name of parameter.
- @value: Initial or default value.
- */
+struct cs_cfg_parameter_desc {
- const char *name;
- const u64 value;
+};
+/**
- Representation of register value.
- Supports full 64 bit register value, or 32 bit value with optional mask
- value.
- */
+union cs_cfg_regval {
- u64 val64;
- struct {
u32 val32;
u32 mask32;
- };
+};
+/**
- Register descriptor for a device feature
- @flags: flags defining the handling of the register.
- @value: Initial value & optional mask.
- */
+struct cs_cfg_reg_desc {
- const u32 flags;
- const union cs_cfg_regval value;
+};
+/**
- Device feature descriptor - combination of registers and parameters to
- program a device to implement a specific complex function.
- @name: feature name.
- @brief: brief description of the feature.
- @params: list of parameters used - 0 terminated.
- @reg: list of registers used.
- */
+struct cs_cfg_feature_desc {
- const char *name;
- const char *brief;
- struct cs_cfg_parameter_desc *params;
- struct cs_cfg_reg_desc *regs;
+};
+/**
- config parameter instance - part of a loaded feature.
- @feat: parent feature
- @current_value: current value of parameter - may be set by user via
sysfs, or modified during device operation.
- */
+struct cs_cfg_parameter {
- struct cs_cfg_feature *feat;
- u64 current_value;
+};
+/**
- config register instance - part of a loaded feature.
maps register values to driver structures
- @value: value to use when setting feature on device / store for
readback of volatile values.
- @drv_store: pointer to internal driver element used to set the value
in hardware.
- */
+struct cs_cfg_reg {
- union cs_cfg_regval value;
- void *drv_store;
+};
+/**
- cs_cfg_ops - standard operations for features.
- CS config core provides basic defaults for these, which can be overridden by
- device specific versions.
- @set_on_enable: Set the feature on the driver before hw enable.
- @save_on_disable: Save any volatile register values after hw disable.
- @reset: Reset feature to default values.
- */
+struct cs_cfg_ops {
- int (*set_on_enable)(struct cs_cfg_feature *feat);
- void (*save_on_disable)(struct cs_cfg_feature *feat);
- void (*reset)(struct cs_cfg_feature *feat);
+};
+/**
- Feature instance loaded into a CoreSight device driver.
- @desc: pointer to the static descriptor for this feature.
- @csdev: parent CoreSight device instance.
- @node: list entry into feature list for this device.
- @dev_spinlock: device spinlock.
- @enabled: feature is enabled on this device.
- @nr_params: number of parameters.
- @params: current parameter values on this device
- @param_group: sysfs group for feature parameters.
- @ops: standard ops to enable and disable features on devices.
- */
+struct cs_cfg_feature {
- const struct cs_cfg_feature_desc *desc;
- struct coresight_device *csdev;
- struct list_head node;
- spinlock_t *dev_spinlock;
- bool enabled;
- int nr_params;
- struct cs_cfg_parameter *params;
- struct attribute_group *param_group;
- int nr_regs;
- struct cs_cfg_reg *regs;
- struct cs_cfg_ops ops;
+};
+/* coresight config API functions */ +int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat); +int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev); +void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev); +void coresight_cfg_reset_feats(struct coresight_device *csdev); +void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat); +void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat); +void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc);
+void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param);
+#endif /* _CORESIGHT_CORESIGHT_CONFIG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c new file mode 100644 index 000000000000..240e0235c130 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include "coresight-etm4x.h" +#include "coresight-etm4x-cfg.h" +#include "coresight-priv.h"
+/* built in features for ETMv4 */
+/* strobe feature */
+/* register defines */ +#define STRB_REG_CTR (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_CTR) +#define STRB_REG_CTR_RB (STRB_REG_CTR | CS_CFG_REG_VAL_SAVE) +#define STRB_REG_CTR_PRM (STRB_REG_CTR | CS_CFG_REG_VAL_PARAM) +#define STRB_REG_SEQ (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEQ) +#define STRB_REG_SEL (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEL) +#define STRB_REG_VI (CS_CFG_REG_STD | CS_CFG_REG_VAL_MASK)
+static struct cs_cfg_parameter_desc strobe_params[] = {
- {
.name = "window",
.value = 1000,
- },
- {
.name = "period",
.value = 10000,
- },
- { 0 },
+};
+static struct cs_cfg_reg_desc strob_regs[] = {
- /* resource selectors */
- {
.flags = STRB_REG_SEL | TRCRSCTLRn(2),
.value = {
.val32 = 0x20001,
},
- },
- {
.flags = STRB_REG_SEQ | TRCRSCTLRn(3),
.value = {
.val32 = 0x20002,
}
- },
- /* strobe window counter 0 - reload from param 0 */
- {
.flags = STRB_REG_CTR_RB | TRCCNTVRn(0),
- },
- {
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(0),
.value = {
.val32 = 0,
},
- },
- {
.flags = STRB_REG_CTR | TRCCNTCTLRn(0),
.value = {
.val32 = 0x10001,
},
- },
- /* strobe period counter 1 - reload from param 1 */
- {
.flags = STRB_REG_CTR_RB | TRCCNTVRn(1),
- },
- {
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(1),
.value = {
.val32 = 1,
},
- },
- {
.flags = STRB_REG_CTR | TRCCNTCTLRn(1),
.value = {
.val32 = 0x8102,
},
- },
- /* sequencer */
- {
.flags = STRB_REG_SEQ | TRCSEQEVRn(0),
.value = {
.val32 = 0x0081,
},
- },
- {
.flags = STRB_REG_SEQ | TRCSEQEVRn(1),
.value = {
.val32 = 0x0000,
},
- },
- /* view-inst */
- {
.flags = STRB_REG_VI | TRCVICTLR,
.value = {
.val32 = 0x0003,
.mask32 = 0x0003,
},
- },
- /* end of regs */
- { 0 },
+};
+static struct cs_cfg_feature_desc strobe = {
- .name = "strobing",
- .brief = "Generate periodic trace capture windows.",
- .params = strobe_params,
- .regs = strob_regs,
+};
+static struct cs_cfg_feature_desc *built_in_feats[] = {
- &strobe,
- 0
+};
+/**
- etm4_cfg_map_reg_offset - validate and map the register offset into a
location in the driver config struct.
- Limits the number of registers that can be accessed and programmed in
- features, to those which are used to control the trace capture parameters.
- Omits or limits access to those which the driver must use exclusively.
- Invalid offsets will result in fail code return and feature load failure.
- @drvdata: driver data to map into.
- @reg: register to map.
- @offset: device offset for the register
- */
+static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata,
struct cs_cfg_reg *reg, u32 offset)
+{
- int err = 0, idx;
- struct etmv4_config *drvcfg = &drvdata->config;
- #define MAPREG(cval, elem) \
case cval: \
reg->drv_store = &drvcfg->elem; \
break;
- #define MAPREGIDX(cval, elem, off_idx) \
case cval: \
reg->drv_store = &drvcfg->elem[off_idx]; \
break;
- if (((offset >= TRCEVENTCTL0R) && (offset <= TRCVIPCSSCTLR)) ||
((offset >= TRCSEQRSTEVR) && (offset <= TRCEXTINSELR)) ||
((offset >= TRCCIDCCTLR0) && (offset <= TRCVMIDCCTLR1))) {
switch (offset) {
/* 32 bit single control and filter registers */
MAPREG(TRCEVENTCTL0R, eventctrl0);
MAPREG(TRCEVENTCTL1R, eventctrl1);
MAPREG(TRCSTALLCTLR, stall_ctrl);
MAPREG(TRCTSCTLR, ts_ctrl);
MAPREG(TRCSYNCPR, syncfreq);
MAPREG(TRCCCCTLR, ccctlr);
MAPREG(TRCBBCTLR, bb_ctrl);
MAPREG(TRCVICTLR, vinst_ctrl);
MAPREG(TRCVIIECTLR, viiectlr);
MAPREG(TRCVISSCTLR, vissctlr);
MAPREG(TRCVIPCSSCTLR, vipcssctlr);
MAPREG(TRCSEQRSTEVR, seq_rst);
MAPREG(TRCSEQSTR, seq_state);
MAPREG(TRCEXTINSELR, ext_inp);
MAPREG(TRCCIDCCTLR0, ctxid_mask0);
MAPREG(TRCCIDCCTLR1, ctxid_mask1);
MAPREG(TRCVMIDCCTLR0, vmid_mask0);
MAPREG(TRCVMIDCCTLR1, vmid_mask1);
default:
err = -EINVAL;
break;
}
- } else if ((offset & GENMASK(11, 4)) == TRCSEQEVRn(0)) {
/* sequencer state control registers */
idx = (offset & GENMASK(3, 0)) / 4;
if (idx < ETM_MAX_SEQ_STATES)
reg->drv_store = &drvcfg->seq_ctrl[idx];
else
err = -EINVAL;
- } else if ((offset >= TRCSSCCRn(0)) && (offset <= TRCSSPCICRn(7))) {
/* 32 bit, 8 off indexed register sets */
idx = (offset & GENMASK(4, 0)) / 4;
switch (offset & GENMASK(11, 5)) {
MAPREGIDX(TRCSSCCRn(0), ss_ctrl, idx);
MAPREGIDX(TRCSSCSRn(0), ss_status, idx);
MAPREGIDX(TRCSSPCICRn(0), ss_pe_cmp, idx);
default:
err = -EINVAL;
break;
}
- } else if ((offset >= TRCCIDCVRn(0)) && (offset <= TRCVMIDCVRn(7))) {
/* 64 bit, 8 off indexed register sets */
idx = (offset & GENMASK(5, 0)) / 8;
switch (offset & GENMASK(11, 6)) {
MAPREGIDX(TRCCIDCVRn(0), ctxid_pid, idx);
MAPREGIDX(TRCVMIDCVRn(0), vmid_val, idx);
default:
err = -EINVAL;
break;
}
- } else if ((offset >= TRCRSCTLRn(2)) &&
(offset <= TRCRSCTLRn((ETM_MAX_RES_SEL - 1)))) {
/* 32 bit resource selection regs, 32 off, skip fixed 0,1 */
idx = (offset & GENMASK(6, 0)) / 4;
if (idx < ETM_MAX_RES_SEL)
reg->drv_store = &drvcfg->res_ctrl[idx];
else
err = -EINVAL;
- } else if ((offset >= TRCACVRn(0)) &&
(offset <= TRCACATRn((ETM_MAX_SINGLE_ADDR_CMP - 1)))) {
/* 64 bit addr cmp regs, 16 off */
idx = (offset & GENMASK(6, 0)) / 8;
switch (offset & GENMASK(11, 7)) {
MAPREGIDX(TRCACVRn(0), addr_val, idx);
MAPREGIDX(TRCACATRn(0), addr_acc, idx);
default:
err = -EINVAL;
break;
}
- } else if ((offset >= TRCCNTRLDVRn(0)) &&
(offset <= TRCCNTVRn((ETMv4_MAX_CNTR - 1)))) {
/* 32 bit counter regs, 4 off (ETMv4_MAX_CNTR - 1) */
idx = (offset & GENMASK(3, 0)) / 4;
switch (offset & GENMASK(11, 4)) {
MAPREGIDX(TRCCNTRLDVRn(0), cntrldvr, idx);
MAPREGIDX(TRCCNTCTLRn(0), cntr_ctrl, idx);
MAPREGIDX(TRCCNTVRn(0), cntr_val, idx);
default:
err = -EINVAL;
break;
}
- } else {
err = -EINVAL;
- }
- return err;
+}
+/**
- etm4_cfg_load_feature - load a feature into a device instance.
- @csdev: An ETMv4 CoreSight device.
- @feat_desc: The static feature descriptor to be loaded.
- The function will load a feature instance into the device, based on the
- supplied descriptor. The descriptor will be checked to ensure a valid
- feature can be loaded for this ETMv4 device.
- Parameter and register definitions will be converted into internal
- structures that are used to set the values in the driver when the
- feature is enabled for the device.
- */
+int etm4_cfg_load_feature(struct coresight_device *csdev,
const struct cs_cfg_feature_desc *feat_desc)
+{
- struct device *dev = csdev->dev.parent;
- struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
- struct cs_cfg_feature *feat;
- u32 offset;
- int i = 0, err = 0;
- feat = devm_kzalloc(dev, sizeof(struct cs_cfg_feature), GFP_KERNEL);
- if (!feat)
return -ENOMEM;
- feat->desc = feat_desc;
- feat->dev_spinlock = &drvdata->spinlock;
- feat->csdev = csdev;
- /* count the params regs and allocate space. */
- while (feat_desc->params[i++].name)
feat->nr_params++;
- i = 0;
- while (feat_desc->regs[i++].flags)
feat->nr_regs++;
- feat->params = devm_kcalloc(dev, feat->nr_params,
sizeof(struct cs_cfg_parameter),
GFP_KERNEL);
- if (!feat->params)
return -ENOMEM;
- feat->regs = devm_kcalloc(dev, feat->nr_regs,
sizeof(struct cs_cfg_reg), GFP_KERNEL);
- if (!feat->regs)
return -ENOMEM;
- /* load the parameters default values */
- for (i = 0; i < feat->nr_params; i++)
feat->params[i].feat = feat;
- /* process the register descriptions */
- for (i = 0; i < feat->nr_regs; i++) {
offset = feat_desc->regs[i].flags & CS_CFG_REG_ID_MASK;
err = etm4_cfg_map_reg_offset(drvdata, &feat->regs[i], offset);
if (err)
return err;
- }
- /* set sysfs representation of the feature */
- err = coresight_cfg_sysfs_add_grp(feat);
- /* add the feature to the device list and init default values */
- if (!err) {
coresight_cfg_list_add_feat(feat);
coresight_cfg_set_def_ops(feat);
feat->ops.reset(feat);
- }
- return err;
+}
+/* load up the list of builtin features */ +int etm4_cfg_load_builtins(struct coresight_device *csdev) +{
- int idx = 0, err = 0;
- while (built_in_feats[idx] && !err)
err = etm4_cfg_load_feature(csdev, built_in_feats[idx++]);
- return err;
+} diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h new file mode 100644 index 000000000000..703657cac2fc --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
- */
+#ifndef _CORESIGHT_CORESIGHT_ETM4X_CFG_H +#define _CORESIGHT_CORESIGHT_ETM4X_CFG_H
+#include "coresight-config.h"
+/* ETMv4 specific config defines */
+/* resource IDs */
+#define ETM4_CFG_RES_CTR 0x00001000 +#define ETM4_CFG_RES_CMP 0x00002000 +#define ETM4_CFG_RES_CMP_PAIR0 0x00003000 +#define ETM4_CFG_RES_CMP_PAIR1 0x00004000 +#define ETM4_CFG_RES_SEL 0x00005000 +#define ETM4_CFG_RES_SEL_PAIR0 0x00006000 +#define ETM4_CFG_RES_SEL_PAIR1 0x00007000 +#define ETM4_CFG_RES_SEQ 0x00008000 +#define ETM4_CFG_RES_TS 0x00009000 +#define ETM4_CFG_RES_MASK 0x0000F000
+/* ETM4 configuration handlers */ +int etm4_cfg_load_builtins(struct coresight_device *csdev);
+#endif /* _CORESIGHT_CORESIGHT_ETM4X_CFG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index b673e738bc9a..ec182544a064 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -9,6 +9,7 @@ #include <linux/sysfs.h> #include "coresight-etm4x.h" #include "coresight-priv.h" +#include "coresight-config.h" static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) { @@ -269,6 +270,8 @@ static ssize_t reset_store(struct device *dev, spin_unlock(&drvdata->spinlock);
- coresight_cfg_reset_feats(to_coresight_device(dev));
- return size;
} static DEVICE_ATTR_WO(reset); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 60a5133d801c..e69dbcf5cd4d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -34,6 +34,7 @@ #include "coresight-etm4x.h" #include "coresight-etm-perf.h" +#include "coresight-etm4x-cfg.h" static int boot_enable; module_param(boot_enable, int, 0444); @@ -320,10 +321,11 @@ static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata) return ret; } -static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, +static int etm4_parse_event_config(struct coresight_device *csdev, struct perf_event *event) { int ret = 0;
- struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_config *config = &drvdata->config; struct perf_event_attr *attr = &event->attr;
@@ -383,6 +385,9 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, /* bit[12], Return stack enable bit */ config->cfg |= BIT(12);
- /* set any enabled features */
- ret = coresight_cfg_set_feats_on_ena(csdev);
out: return ret; } @@ -399,7 +404,7 @@ static int etm4_enable_perf(struct coresight_device *csdev, } /* Configure the tracer based on the session's specifics */
- ret = etm4_parse_event_config(drvdata, event);
- ret = etm4_parse_event_config(csdev, event); if (ret) goto out; /* And enable it */
@@ -415,6 +420,8 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) struct etm4_enable_arg arg = { }; int ret;
- coresight_cfg_set_feats_on_ena(csdev);
- spin_lock(&drvdata->spinlock);
/* @@ -532,6 +539,7 @@ static int etm4_disable_perf(struct coresight_device *csdev, return -EINVAL; etm4_disable_hw(drvdata);
- coresight_cfg_save_feats_on_dis(csdev);
/* * Check if the start/stop logic was active when the unit was stopped. @@ -566,6 +574,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) smp_call_function_single(drvdata->cpu, etm4_disable_hw, drvdata, 1); spin_unlock(&drvdata->spinlock);
- coresight_cfg_save_feats_on_dis(csdev); cpus_read_unlock();
dev_dbg(&csdev->dev, "ETM tracing disabled\n"); @@ -1510,6 +1519,11 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) goto err_arch_supported; }
- /* load builtin features */
- ret = etm4_cfg_load_builtins(drvdata->csdev);
- if (ret)
goto err_arch_supported;
- pm_runtime_put(&adev->dev); dev_info(&drvdata->csdev->dev, "CPU%d: ETM v%d.%d initialized\n", drvdata->cpu, drvdata->arch >> 4, drvdata->arch & 0xf);
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index f3efbb3b2b4d..ac16d96124f6 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1335,6 +1335,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) ret = coresight_fixup_orphan_conns(csdev); if (!ret) cti_add_assoc_to_csdev(csdev);
- INIT_LIST_HEAD(&csdev->feat_list);
mutex_unlock(&coresight_mutex); if (ret) { diff --git a/include/linux/coresight.h b/include/linux/coresight.h index e3e9f0e3a878..0e8d529282dd 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -204,6 +204,8 @@ struct coresight_device { /* sysfs links between components */ int nr_links; bool has_conns_grp;
- /* feature list */
- struct list_head feat_list;
}; /* -- 2.17.1
Hi Mathieu,
On Wed, 24 Jun 2020 at 18:59, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Good day,
I like what you did in this set. It took me a while to come to terms with all the new structures but this is something we can address in subsequent revisions if need be. I also found a couple of minor problems but that too can be dealt with later. I would like to discuss 3 mains points:
- Make the features configurable _and_ modular. Depending on how many features
people come up with it may become tedious to carry all that extra code. For the modularity I suggest you take inspiration from the governor model used in the cpufreq subsystem. Governors, such as the performance governor[1], are all modules that get registered with the cpufreq core[2]. From there when a governor is selected[3] the core increases the governor's module count[4], preventing the module from being unloaded while a governor is used.
That is certainly the intention. But rather than compilable C modules, new features will be binary data , loadable at runtime, using the same format as the builtin example in this patch. The intention is that these data structures can be generated as a binary file using some scripted tool interpreting a simple text file description. This is then loaded as described below.
- Please explore the possibility of using configfs rather than sysfs. That way
users can create their own features without the need to add a new module.
This is also underway. A coresight system config driver will have a configfs interface that will allow the loading of new system configurations and features - where a configuration is a set of new or existing features to use as part of a system wide setup. The driver will interpret the incoming binary file, and distribute features to the correct drivers - e.g. a new ETMv4 feature will be loaded into all the ETMv4 instances. New features can be loaded in this to supplement any "builtin" that exist such as the one in the current patch.
Sysfs is retained to control features as in this example - the features do the same job as the other registers / files in sysfs in programming up the device.
So the current infrastructure that may seem more than is necessary for just strobing in ETMv4, is there to prepare for the modularity that is required.
- Perf sessions need to be able to choose which feature they want to use rather
than defaulting to a system wide configuration. If you don't want to deal with that at this stage - something I would certainly understand - simpy don't take features into account from the perf interface.
Ultimately this too is the goal. If "auto-fdo" is the name of a configuration, that selects the "strobing" feature on all etms used in the perf session then the command:- perf record -e cs_etm/@auto-fdo/ <other perf options> will allow the selection of the feature. A later patch set will introduce this concept. Here we are replacing the sink ID with a configuration ID (and selecting a default sink instead) - which is why default sink was important for this work.
The current patch is actually a functionally identical patch to one that used to be on perf-opencsd that enabled etm strobing for autoFDO and perf. That patch also added etm strobing without selection by perf - but just did it in a way that was less obvious. It is being used by customers as an out of tree patch, so there is a requirement from them to get it up upstreamed so it can be used in production environments.
The patch here introduces a limited amount of the complex config API, sufficient to support etm strobing for those who are actively using it.
The intention being to introduce the rest of complex config in separate stages to make is easier for reviewers to understand what is being done.
Regards
Mike
Thanks, Mathieu
[1]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq_perfo... [2]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L23... [3]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L76... [4]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L63...
On Wed, Jun 10, 2020 at 10:13:32AM +0100, Mike Leach wrote:
This set introduces the concept of complex features for CoreSight devices, and adds a feature to ETMv4 to enable strobing of the etm capture.
- Complex Features: These are programming settings for devices, especially
ETM which have many programmable register that can be combined to create complex functionality. Features appear as sub-directories in sysfs, having an enable parameter to set the full program setting.
- ETM strobing. This feature is introduced to ETMv4 to allow the trace
capture to be periodically switched on and off - strobed - to a given mark / space ratio to generate statistical trace data for workflows such as auto-fdo.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/Makefile | 7 +- .../hwtracing/coresight/coresight-config.c | 380 ++++++++++++++++++ .../hwtracing/coresight/coresight-config.h | 156 +++++++ .../hwtracing/coresight/coresight-etm4x-cfg.c | 325 +++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 29 ++ .../coresight/coresight-etm4x-sysfs.c | 3 + drivers/hwtracing/coresight/coresight-etm4x.c | 18 +- drivers/hwtracing/coresight/coresight.c | 1 + include/linux/coresight.h | 2 + 9 files changed, 917 insertions(+), 4 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-config.c create mode 100644 drivers/hwtracing/coresight/coresight-config.h create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.c create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.h
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 19497d1d92bf..b6c28f4cfca7 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -3,7 +3,8 @@ # Makefile for CoreSight drivers. # obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o \
coresight-platform.o coresight-sysfs.o
coresight-platform.o coresight-sysfs.o \
coresight-config.o
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \ coresight-tmc-etf.o \ coresight-tmc-etr.o @@ -14,7 +15,9 @@ obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \
coresight-etm4x-sysfs.o
coresight-etm4x-sysfs.o \
coresight-etm4x-cfg.o
obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c new file mode 100644 index 000000000000..10c2d546ca82 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/sysfs.h> +#include "coresight-config.h" +#include "coresight-priv.h"
+static DEFINE_MUTEX(coresight_cfg_mutex);
+/*
- Handlers for creating and manipulating the feature representation
- in sysfs.
- */
+/* base feature attribute info */ +struct feat_attr_info {
const char *name;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
+};
+static ssize_t cs_cfg_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
int val;
spin_lock(feat->dev_spinlock);
val = feat->enabled;
spin_unlock(feat->dev_spinlock);
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+static ssize_t cs_cfg_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
int val;
spin_lock(feat->dev_spinlock);
if (!kstrtoint(buf, 0, &val))
feat->enabled = val;
spin_unlock(feat->dev_spinlock);
return size;
+}
+static ssize_t cs_cfg_desc_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
return scnprintf(buf, PAGE_SIZE, "%s\n", feat->desc->brief);
+}
+struct feat_attr_info feature_std_attr[] = {
{
.name = "enable",
.show = cs_cfg_enable_show,
.store = cs_cfg_enable_store,
},
{
.name = "description",
.show = cs_cfg_desc_show,
},
{ 0 },
+};
+static ssize_t cs_cfg_param_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
u64 val;
spin_lock(param->feat->dev_spinlock);
val = param->current_value;
spin_unlock(param->feat->dev_spinlock);
return scnprintf(buf, PAGE_SIZE, "%lld\n", val);
+}
+static ssize_t cs_cfg_param_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
u64 val;
spin_lock(param->feat->dev_spinlock);
if (!kstrtoull(buf, 0, &val))
param->current_value = val;
spin_unlock(param->feat->dev_spinlock);
return size;
+}
+static struct attribute * +cs_cfg_sysfs_create_feat_attr(struct device *dev, struct cs_cfg_feature *feat,
struct feat_attr_info *info)
+{
struct dev_ext_attribute *eattr;
eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
if (!eattr)
return NULL;
eattr->attr.attr.name = devm_kstrdup(dev, info->name, GFP_KERNEL);
eattr->attr.attr.mode = info->store ? 0644 : 0444;
eattr->attr.show = info->show;
eattr->attr.store = info->store;
eattr->var = feat;
return &eattr->attr.attr;
+}
+static struct attribute * +cs_cfg_sysfs_create_param_attr(struct device *dev, const char *name,
struct cs_cfg_parameter *param)
+{
struct dev_ext_attribute *eattr;
eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
if (!eattr)
return NULL;
eattr->attr.attr.name = devm_kstrdup(dev, name, GFP_KERNEL);
eattr->attr.attr.mode = 0644;
eattr->attr.show = cs_cfg_param_show;
eattr->attr.store = cs_cfg_param_store;
eattr->var = param;
return &eattr->attr.attr;
+}
+int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat) +{
int p, attr_idx = 0;
struct attribute **attrs;
const char *name;
struct feat_attr_info *info;
struct device *dev = feat->csdev->dev.parent;
/* create sysfs group in csdev */
feat->param_group = devm_kzalloc(dev, sizeof(struct attribute_group),
GFP_KERNEL);
if (!feat->param_group)
return -ENOMEM;
feat->param_group->name = devm_kasprintf(dev, GFP_KERNEL, "%s.feat",
feat->desc->name);
if (!feat->param_group->name)
return -ENOMEM;
/* attributes - nr_params + enable + desc */
attrs = devm_kcalloc(dev, feat->nr_params + 2,
sizeof(struct attribute *), GFP_KERNEL);
if (!attrs)
return -ENOMEM;
/* create base attributes in group */
while (feature_std_attr[attr_idx].name) {
info = &feature_std_attr[attr_idx];
attrs[attr_idx] = cs_cfg_sysfs_create_feat_attr(dev, feat,
info);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
}
/* create params in group */
for (p = 0; p < feat->nr_params; p++) {
name = feat->desc->params[p].name;
attrs[attr_idx] =
cs_cfg_sysfs_create_param_attr(dev, name,
&feat->params[p]);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
}
feat->param_group->attrs = attrs;
return sysfs_create_group(&feat->csdev->dev.kobj, feat->param_group);
+}
+/*
- standard routines to set/save/reset enabled features, and iterate
- groups of features on a device.
- */
+void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat) +{
mutex_lock(&coresight_cfg_mutex);
list_add(&feat->node, &feat->csdev->feat_list);
mutex_unlock(&coresight_cfg_mutex);
+}
+inline void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags) +{
u32 *p_val32 = (u32 *)reg->drv_store;
u32 tmp32;
if (!(flags & CS_CFG_REG_VAL_64BIT)) {
if (flags & CS_CFG_REG_VAL_MASK) {
tmp32 = *p_val32;
tmp32 &= ~reg->value.mask32;
tmp32 |= reg->value.val32 & reg->value.mask32;
*p_val32 = tmp32;
} else
*p_val32 = reg->value.val32;
} else
*((u64 *)reg->drv_store) = reg->value.val64;
+}
+inline void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags) +{
if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = *(u64 *)(reg->drv_store);
else
reg->value.val32 = *(u32 *)(reg->drv_store);
+}
+inline void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc)
+{
reg->value.val64 = desc->value.val64;
+}
+inline void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param)
+{
if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = param->current_value;
else
reg->value.val32 = (u32)param->current_value;
+}
+/* default set - will set values without resource checking */ +static int cs_cfg_set_on_enable(struct cs_cfg_feature *feat) +{
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
coresight_cfg_set_reg(&feat->regs[i], flags);
}
}
dev_dbg(&feat->csdev->dev, "Feature %s: %s", feat->desc->name,
feat->enabled ? "Set enabled" : "Skip disabled");
spin_unlock(feat->dev_spinlock);
return 0;
+}
+static void cs_cfg_save_on_disable(struct cs_cfg_feature *feat) +{
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
if (flags & CS_CFG_REG_VAL_SAVE)
coresight_cfg_save_reg(&feat->regs[i], flags);
}
}
spin_unlock(feat->dev_spinlock);
+}
+/* default reset - restore default values, disable feature */ +static void cs_cfg_reset_feat(struct cs_cfg_feature *feat) +{
const struct cs_cfg_feature_desc *feat_desc = feat->desc;
struct cs_cfg_reg_desc *reg_desc;
struct cs_cfg_parameter *param;
struct cs_cfg_reg *reg;
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
feat->enabled = false;
/*
* set the default values for all parameters and regs from the
* relevant static descriptors.
*/
for (i = 0; i < feat->nr_params; i++)
feat->params[i].current_value = feat_desc->params[i].value;
for (i = 0; i < feat->nr_regs; i++) {
reg_desc = &feat_desc->regs[i];
flags = reg_desc->flags;
reg = &feat->regs[i];
/* check if reg set from a parameter otherwise desc default */
if (flags & CS_CFG_REG_VAL_PARAM) {
param = &feat->params[reg_desc->value.val32];
coresight_cfg_init_reg_param(reg, flags, param);
} else
coresight_cfg_init_reg(reg, reg_desc);
}
spin_unlock(feat->dev_spinlock);
+}
+void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat) +{
feat->ops.set_on_enable = cs_cfg_set_on_enable;
feat->ops.save_on_disable = cs_cfg_save_on_disable;
feat->ops.reset = cs_cfg_reset_feat;
+}
+int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
int err = 0;
if (list_empty(&csdev->feat_list))
return 0;
list_for_each_entry(feat, &csdev->feat_list, node) {
dev_dbg(&csdev->dev, "Found feature:%s", feat->desc->name);
if (feat->ops.set_on_enable) {
err = feat->ops.set_on_enable(feat);
dev_dbg(&csdev->dev, "Feature %s: %s",
feat->desc->name, err ? "Set failed" : "OK");
if (!err)
break;
}
}
return err;
+}
+void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
if (list_empty(&csdev->feat_list))
return;
list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.save_on_disable)
feat->ops.save_on_disable(feat);
}
+}
+void coresight_cfg_reset_feats(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
if (list_empty(&csdev->feat_list))
return;
list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.reset)
feat->ops.reset(feat);
}
+} diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h new file mode 100644 index 000000000000..ce69ad5d2bbf --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2020 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CONFIG_H +#define _CORESIGHT_CORESIGHT_CONFIG_H
+/* CoreSight Configuration - complex component and system config handling */
+/* generic register flags */ +#define CS_CFG_REG_STD 0x80000000 /* reg is standard reg */ +#define CS_CFG_REG_RESOURCE 0x40000000 /* reg is a resource */ +#define CS_CFG_REG_VAL_PARAM 0x08000000 /* reg value uses param */ +#define CS_CFG_REG_VAL_MASK 0x04000000 /* reg value bit masked */ +#define CS_CFG_REG_VAL_64BIT 0x02000000 /* reg value 64 bit */ +#define CS_CFG_REG_VAL_SAVE 0x01000000 /* reg value save on disable */ +#define CS_CFG_REG_ID_MASK 0x00000FFF /* reg offset / id in device */ +#define CS_CFG_REG_DEV_MASK 0x00FFF000 /* device specific flags */
+/**
- Parameter descriptor for a device feature.
- @name: Name of parameter.
- @value: Initial or default value.
- */
+struct cs_cfg_parameter_desc {
const char *name;
const u64 value;
+};
+/**
- Representation of register value.
- Supports full 64 bit register value, or 32 bit value with optional mask
- value.
- */
+union cs_cfg_regval {
u64 val64;
struct {
u32 val32;
u32 mask32;
};
+};
+/**
- Register descriptor for a device feature
- @flags: flags defining the handling of the register.
- @value: Initial value & optional mask.
- */
+struct cs_cfg_reg_desc {
const u32 flags;
const union cs_cfg_regval value;
+};
+/**
- Device feature descriptor - combination of registers and parameters to
- program a device to implement a specific complex function.
- @name: feature name.
- @brief: brief description of the feature.
- @params: list of parameters used - 0 terminated.
- @reg: list of registers used.
- */
+struct cs_cfg_feature_desc {
const char *name;
const char *brief;
struct cs_cfg_parameter_desc *params;
struct cs_cfg_reg_desc *regs;
+};
+/**
- config parameter instance - part of a loaded feature.
- @feat: parent feature
- @current_value: current value of parameter - may be set by user via
sysfs, or modified during device operation.
- */
+struct cs_cfg_parameter {
struct cs_cfg_feature *feat;
u64 current_value;
+};
+/**
- config register instance - part of a loaded feature.
maps register values to driver structures
- @value: value to use when setting feature on device / store for
readback of volatile values.
- @drv_store: pointer to internal driver element used to set the value
in hardware.
- */
+struct cs_cfg_reg {
union cs_cfg_regval value;
void *drv_store;
+};
+/**
- cs_cfg_ops - standard operations for features.
- CS config core provides basic defaults for these, which can be overridden by
- device specific versions.
- @set_on_enable: Set the feature on the driver before hw enable.
- @save_on_disable: Save any volatile register values after hw disable.
- @reset: Reset feature to default values.
- */
+struct cs_cfg_ops {
int (*set_on_enable)(struct cs_cfg_feature *feat);
void (*save_on_disable)(struct cs_cfg_feature *feat);
void (*reset)(struct cs_cfg_feature *feat);
+};
+/**
- Feature instance loaded into a CoreSight device driver.
- @desc: pointer to the static descriptor for this feature.
- @csdev: parent CoreSight device instance.
- @node: list entry into feature list for this device.
- @dev_spinlock: device spinlock.
- @enabled: feature is enabled on this device.
- @nr_params: number of parameters.
- @params: current parameter values on this device
- @param_group: sysfs group for feature parameters.
- @ops: standard ops to enable and disable features on devices.
- */
+struct cs_cfg_feature {
const struct cs_cfg_feature_desc *desc;
struct coresight_device *csdev;
struct list_head node;
spinlock_t *dev_spinlock;
bool enabled;
int nr_params;
struct cs_cfg_parameter *params;
struct attribute_group *param_group;
int nr_regs;
struct cs_cfg_reg *regs;
struct cs_cfg_ops ops;
+};
+/* coresight config API functions */ +int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat); +int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev); +void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev); +void coresight_cfg_reset_feats(struct coresight_device *csdev); +void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat); +void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat); +void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc);
+void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param);
+#endif /* _CORESIGHT_CORESIGHT_CONFIG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c new file mode 100644 index 000000000000..240e0235c130 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include "coresight-etm4x.h" +#include "coresight-etm4x-cfg.h" +#include "coresight-priv.h"
+/* built in features for ETMv4 */
+/* strobe feature */
+/* register defines */ +#define STRB_REG_CTR (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_CTR) +#define STRB_REG_CTR_RB (STRB_REG_CTR | CS_CFG_REG_VAL_SAVE) +#define STRB_REG_CTR_PRM (STRB_REG_CTR | CS_CFG_REG_VAL_PARAM) +#define STRB_REG_SEQ (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEQ) +#define STRB_REG_SEL (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEL) +#define STRB_REG_VI (CS_CFG_REG_STD | CS_CFG_REG_VAL_MASK)
+static struct cs_cfg_parameter_desc strobe_params[] = {
{
.name = "window",
.value = 1000,
},
{
.name = "period",
.value = 10000,
},
{ 0 },
+};
+static struct cs_cfg_reg_desc strob_regs[] = {
/* resource selectors */
{
.flags = STRB_REG_SEL | TRCRSCTLRn(2),
.value = {
.val32 = 0x20001,
},
},
{
.flags = STRB_REG_SEQ | TRCRSCTLRn(3),
.value = {
.val32 = 0x20002,
}
},
/* strobe window counter 0 - reload from param 0 */
{
.flags = STRB_REG_CTR_RB | TRCCNTVRn(0),
},
{
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(0),
.value = {
.val32 = 0,
},
},
{
.flags = STRB_REG_CTR | TRCCNTCTLRn(0),
.value = {
.val32 = 0x10001,
},
},
/* strobe period counter 1 - reload from param 1 */
{
.flags = STRB_REG_CTR_RB | TRCCNTVRn(1),
},
{
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(1),
.value = {
.val32 = 1,
},
},
{
.flags = STRB_REG_CTR | TRCCNTCTLRn(1),
.value = {
.val32 = 0x8102,
},
},
/* sequencer */
{
.flags = STRB_REG_SEQ | TRCSEQEVRn(0),
.value = {
.val32 = 0x0081,
},
},
{
.flags = STRB_REG_SEQ | TRCSEQEVRn(1),
.value = {
.val32 = 0x0000,
},
},
/* view-inst */
{
.flags = STRB_REG_VI | TRCVICTLR,
.value = {
.val32 = 0x0003,
.mask32 = 0x0003,
},
},
/* end of regs */
{ 0 },
+};
+static struct cs_cfg_feature_desc strobe = {
.name = "strobing",
.brief = "Generate periodic trace capture windows.",
.params = strobe_params,
.regs = strob_regs,
+};
+static struct cs_cfg_feature_desc *built_in_feats[] = {
&strobe,
0
+};
+/**
- etm4_cfg_map_reg_offset - validate and map the register offset into a
location in the driver config struct.
- Limits the number of registers that can be accessed and programmed in
- features, to those which are used to control the trace capture parameters.
- Omits or limits access to those which the driver must use exclusively.
- Invalid offsets will result in fail code return and feature load failure.
- @drvdata: driver data to map into.
- @reg: register to map.
- @offset: device offset for the register
- */
+static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata,
struct cs_cfg_reg *reg, u32 offset)
+{
int err = 0, idx;
struct etmv4_config *drvcfg = &drvdata->config;
#define MAPREG(cval, elem) \
case cval: \
reg->drv_store = &drvcfg->elem; \
break;
#define MAPREGIDX(cval, elem, off_idx) \
case cval: \
reg->drv_store = &drvcfg->elem[off_idx]; \
break;
if (((offset >= TRCEVENTCTL0R) && (offset <= TRCVIPCSSCTLR)) ||
((offset >= TRCSEQRSTEVR) && (offset <= TRCEXTINSELR)) ||
((offset >= TRCCIDCCTLR0) && (offset <= TRCVMIDCCTLR1))) {
switch (offset) {
/* 32 bit single control and filter registers */
MAPREG(TRCEVENTCTL0R, eventctrl0);
MAPREG(TRCEVENTCTL1R, eventctrl1);
MAPREG(TRCSTALLCTLR, stall_ctrl);
MAPREG(TRCTSCTLR, ts_ctrl);
MAPREG(TRCSYNCPR, syncfreq);
MAPREG(TRCCCCTLR, ccctlr);
MAPREG(TRCBBCTLR, bb_ctrl);
MAPREG(TRCVICTLR, vinst_ctrl);
MAPREG(TRCVIIECTLR, viiectlr);
MAPREG(TRCVISSCTLR, vissctlr);
MAPREG(TRCVIPCSSCTLR, vipcssctlr);
MAPREG(TRCSEQRSTEVR, seq_rst);
MAPREG(TRCSEQSTR, seq_state);
MAPREG(TRCEXTINSELR, ext_inp);
MAPREG(TRCCIDCCTLR0, ctxid_mask0);
MAPREG(TRCCIDCCTLR1, ctxid_mask1);
MAPREG(TRCVMIDCCTLR0, vmid_mask0);
MAPREG(TRCVMIDCCTLR1, vmid_mask1);
default:
err = -EINVAL;
break;
}
} else if ((offset & GENMASK(11, 4)) == TRCSEQEVRn(0)) {
/* sequencer state control registers */
idx = (offset & GENMASK(3, 0)) / 4;
if (idx < ETM_MAX_SEQ_STATES)
reg->drv_store = &drvcfg->seq_ctrl[idx];
else
err = -EINVAL;
} else if ((offset >= TRCSSCCRn(0)) && (offset <= TRCSSPCICRn(7))) {
/* 32 bit, 8 off indexed register sets */
idx = (offset & GENMASK(4, 0)) / 4;
switch (offset & GENMASK(11, 5)) {
MAPREGIDX(TRCSSCCRn(0), ss_ctrl, idx);
MAPREGIDX(TRCSSCSRn(0), ss_status, idx);
MAPREGIDX(TRCSSPCICRn(0), ss_pe_cmp, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCCIDCVRn(0)) && (offset <= TRCVMIDCVRn(7))) {
/* 64 bit, 8 off indexed register sets */
idx = (offset & GENMASK(5, 0)) / 8;
switch (offset & GENMASK(11, 6)) {
MAPREGIDX(TRCCIDCVRn(0), ctxid_pid, idx);
MAPREGIDX(TRCVMIDCVRn(0), vmid_val, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCRSCTLRn(2)) &&
(offset <= TRCRSCTLRn((ETM_MAX_RES_SEL - 1)))) {
/* 32 bit resource selection regs, 32 off, skip fixed 0,1 */
idx = (offset & GENMASK(6, 0)) / 4;
if (idx < ETM_MAX_RES_SEL)
reg->drv_store = &drvcfg->res_ctrl[idx];
else
err = -EINVAL;
} else if ((offset >= TRCACVRn(0)) &&
(offset <= TRCACATRn((ETM_MAX_SINGLE_ADDR_CMP - 1)))) {
/* 64 bit addr cmp regs, 16 off */
idx = (offset & GENMASK(6, 0)) / 8;
switch (offset & GENMASK(11, 7)) {
MAPREGIDX(TRCACVRn(0), addr_val, idx);
MAPREGIDX(TRCACATRn(0), addr_acc, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCCNTRLDVRn(0)) &&
(offset <= TRCCNTVRn((ETMv4_MAX_CNTR - 1)))) {
/* 32 bit counter regs, 4 off (ETMv4_MAX_CNTR - 1) */
idx = (offset & GENMASK(3, 0)) / 4;
switch (offset & GENMASK(11, 4)) {
MAPREGIDX(TRCCNTRLDVRn(0), cntrldvr, idx);
MAPREGIDX(TRCCNTCTLRn(0), cntr_ctrl, idx);
MAPREGIDX(TRCCNTVRn(0), cntr_val, idx);
default:
err = -EINVAL;
break;
}
} else {
err = -EINVAL;
}
return err;
+}
+/**
- etm4_cfg_load_feature - load a feature into a device instance.
- @csdev: An ETMv4 CoreSight device.
- @feat_desc: The static feature descriptor to be loaded.
- The function will load a feature instance into the device, based on the
- supplied descriptor. The descriptor will be checked to ensure a valid
- feature can be loaded for this ETMv4 device.
- Parameter and register definitions will be converted into internal
- structures that are used to set the values in the driver when the
- feature is enabled for the device.
- */
+int etm4_cfg_load_feature(struct coresight_device *csdev,
const struct cs_cfg_feature_desc *feat_desc)
+{
struct device *dev = csdev->dev.parent;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
struct cs_cfg_feature *feat;
u32 offset;
int i = 0, err = 0;
feat = devm_kzalloc(dev, sizeof(struct cs_cfg_feature), GFP_KERNEL);
if (!feat)
return -ENOMEM;
feat->desc = feat_desc;
feat->dev_spinlock = &drvdata->spinlock;
feat->csdev = csdev;
/* count the params regs and allocate space. */
while (feat_desc->params[i++].name)
feat->nr_params++;
i = 0;
while (feat_desc->regs[i++].flags)
feat->nr_regs++;
feat->params = devm_kcalloc(dev, feat->nr_params,
sizeof(struct cs_cfg_parameter),
GFP_KERNEL);
if (!feat->params)
return -ENOMEM;
feat->regs = devm_kcalloc(dev, feat->nr_regs,
sizeof(struct cs_cfg_reg), GFP_KERNEL);
if (!feat->regs)
return -ENOMEM;
/* load the parameters default values */
for (i = 0; i < feat->nr_params; i++)
feat->params[i].feat = feat;
/* process the register descriptions */
for (i = 0; i < feat->nr_regs; i++) {
offset = feat_desc->regs[i].flags & CS_CFG_REG_ID_MASK;
err = etm4_cfg_map_reg_offset(drvdata, &feat->regs[i], offset);
if (err)
return err;
}
/* set sysfs representation of the feature */
err = coresight_cfg_sysfs_add_grp(feat);
/* add the feature to the device list and init default values */
if (!err) {
coresight_cfg_list_add_feat(feat);
coresight_cfg_set_def_ops(feat);
feat->ops.reset(feat);
}
return err;
+}
+/* load up the list of builtin features */ +int etm4_cfg_load_builtins(struct coresight_device *csdev) +{
int idx = 0, err = 0;
while (built_in_feats[idx] && !err)
err = etm4_cfg_load_feature(csdev, built_in_feats[idx++]);
return err;
+} diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h new file mode 100644 index 000000000000..703657cac2fc --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
- */
+#ifndef _CORESIGHT_CORESIGHT_ETM4X_CFG_H +#define _CORESIGHT_CORESIGHT_ETM4X_CFG_H
+#include "coresight-config.h"
+/* ETMv4 specific config defines */
+/* resource IDs */
+#define ETM4_CFG_RES_CTR 0x00001000 +#define ETM4_CFG_RES_CMP 0x00002000 +#define ETM4_CFG_RES_CMP_PAIR0 0x00003000 +#define ETM4_CFG_RES_CMP_PAIR1 0x00004000 +#define ETM4_CFG_RES_SEL 0x00005000 +#define ETM4_CFG_RES_SEL_PAIR0 0x00006000 +#define ETM4_CFG_RES_SEL_PAIR1 0x00007000 +#define ETM4_CFG_RES_SEQ 0x00008000 +#define ETM4_CFG_RES_TS 0x00009000 +#define ETM4_CFG_RES_MASK 0x0000F000
+/* ETM4 configuration handlers */ +int etm4_cfg_load_builtins(struct coresight_device *csdev);
+#endif /* _CORESIGHT_CORESIGHT_ETM4X_CFG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index b673e738bc9a..ec182544a064 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -9,6 +9,7 @@ #include <linux/sysfs.h> #include "coresight-etm4x.h" #include "coresight-priv.h" +#include "coresight-config.h"
static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) { @@ -269,6 +270,8 @@ static ssize_t reset_store(struct device *dev,
spin_unlock(&drvdata->spinlock);
coresight_cfg_reset_feats(to_coresight_device(dev));
return size;
} static DEVICE_ATTR_WO(reset); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 60a5133d801c..e69dbcf5cd4d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -34,6 +34,7 @@
#include "coresight-etm4x.h" #include "coresight-etm-perf.h" +#include "coresight-etm4x-cfg.h"
static int boot_enable; module_param(boot_enable, int, 0444); @@ -320,10 +321,11 @@ static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata) return ret; }
-static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, +static int etm4_parse_event_config(struct coresight_device *csdev, struct perf_event *event) { int ret = 0;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_config *config = &drvdata->config; struct perf_event_attr *attr = &event->attr;
@@ -383,6 +385,9 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, /* bit[12], Return stack enable bit */ config->cfg |= BIT(12);
/* set any enabled features */
ret = coresight_cfg_set_feats_on_ena(csdev);
out: return ret; } @@ -399,7 +404,7 @@ static int etm4_enable_perf(struct coresight_device *csdev, }
/* Configure the tracer based on the session's specifics */
ret = etm4_parse_event_config(drvdata, event);
ret = etm4_parse_event_config(csdev, event); if (ret) goto out; /* And enable it */
@@ -415,6 +420,8 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) struct etm4_enable_arg arg = { }; int ret;
coresight_cfg_set_feats_on_ena(csdev);
spin_lock(&drvdata->spinlock); /*
@@ -532,6 +539,7 @@ static int etm4_disable_perf(struct coresight_device *csdev, return -EINVAL;
etm4_disable_hw(drvdata);
coresight_cfg_save_feats_on_dis(csdev); /* * Check if the start/stop logic was active when the unit was stopped.
@@ -566,6 +574,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) smp_call_function_single(drvdata->cpu, etm4_disable_hw, drvdata, 1);
spin_unlock(&drvdata->spinlock);
coresight_cfg_save_feats_on_dis(csdev); cpus_read_unlock(); dev_dbg(&csdev->dev, "ETM tracing disabled\n");
@@ -1510,6 +1519,11 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) goto err_arch_supported; }
/* load builtin features */
ret = etm4_cfg_load_builtins(drvdata->csdev);
if (ret)
goto err_arch_supported;
pm_runtime_put(&adev->dev); dev_info(&drvdata->csdev->dev, "CPU%d: ETM v%d.%d initialized\n", drvdata->cpu, drvdata->arch >> 4, drvdata->arch & 0xf);
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index f3efbb3b2b4d..ac16d96124f6 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1335,6 +1335,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) ret = coresight_fixup_orphan_conns(csdev); if (!ret) cti_add_assoc_to_csdev(csdev);
INIT_LIST_HEAD(&csdev->feat_list); mutex_unlock(&coresight_mutex); if (ret) {
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index e3e9f0e3a878..0e8d529282dd 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -204,6 +204,8 @@ struct coresight_device { /* sysfs links between components */ int nr_links; bool has_conns_grp;
/* feature list */
struct list_head feat_list;
};
/*
2.17.1
Hi
On Wed, 1 Jul 2020 at 13:26, Mike Leach mike.leach@linaro.org wrote:
Hi Mathieu,
On Wed, 24 Jun 2020 at 18:59, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Good day,
I like what you did in this set. It took me a while to come to terms with all the new structures but this is something we can address in subsequent revisions if need be. I also found a couple of minor problems but that too can be dealt with later. I would like to discuss 3 mains points:
- Make the features configurable _and_ modular. Depending on how many features
people come up with it may become tedious to carry all that extra code. For the modularity I suggest you take inspiration from the governor model used in the cpufreq subsystem. Governors, such as the performance governor[1], are all modules that get registered with the cpufreq core[2]. From there when a governor is selected[3] the core increases the governor's module count[4], preventing the module from being unloaded while a governor is used.
That is certainly the intention. But rather than compilable C modules, new features will be binary data , loadable at runtime, using the same format as the builtin example in this patch.
What command do you envision using to do the loading at runtime?
The intention is that these data structures can be generated as a binary file using some scripted tool interpreting a simple text file description. This is then loaded as described below.
- Please explore the possibility of using configfs rather than sysfs. That way
users can create their own features without the need to add a new module.
This is also underway. A coresight system config driver will have a configfs interface that will allow the loading of new system configurations and features - where a configuration is a set of new or existing features to use as part of a system wide setup. The driver will interpret the incoming binary file, and distribute features to the correct drivers - e.g. a new ETMv4 feature will be loaded into all the ETMv4 instances. New features can be loaded in this to supplement any "builtin" that exist such as the one in the current patch.
Sysfs is retained to control features as in this example - the features do the same job as the other registers / files in sysfs in programming up the device.
Does that mean we'd have two different places for configuring features? I'm fine with feature names showing up in sysfs but all configuration specifics should be in configfs.
So the current infrastructure that may seem more than is necessary for just strobing in ETMv4, is there to prepare for the modularity that is required.
- Perf sessions need to be able to choose which feature they want to use rather
than defaulting to a system wide configuration. If you don't want to deal with that at this stage - something I would certainly understand - simpy don't take features into account from the perf interface.
Ultimately this too is the goal. If "auto-fdo" is the name of a configuration, that selects the "strobing" feature on all etms used in the perf session then the command:- perf record -e cs_etm/@auto-fdo/ <other perf options> will allow the selection of the feature. A later patch set will introduce this concept.
Right, that's the optimal solution.
Here we are replacing the sink ID with a configuration ID (and selecting a default sink instead) - which is why default sink was important for this work.
The current patch is actually a functionally identical patch to one that used to be on perf-opencsd that enabled etm strobing for autoFDO and perf. That patch also added etm strobing without selection by perf
- but just did it in a way that was less obvious. It is being used by
customers as an out of tree patch, so there is a requirement from them to get it up upstreamed so it can be used in production environments.
The patch here introduces a limited amount of the complex config API, sufficient to support etm strobing for those who are actively using it.
The intention being to introduce the rest of complex config in separate stages to make is easier for reviewers to understand what is being done.
Right, I'm all in favour of that. On the flip side I want to make sure not to introduce anything we'll have to maintain in the long run. Right now the global nature of the feature selection in sysfs can't be mixed with perf.
Regards
Mike
Thanks, Mathieu
[1]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq_perfo... [2]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L23... [3]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L76... [4]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L63...
On Wed, Jun 10, 2020 at 10:13:32AM +0100, Mike Leach wrote:
This set introduces the concept of complex features for CoreSight devices, and adds a feature to ETMv4 to enable strobing of the etm capture.
- Complex Features: These are programming settings for devices, especially
ETM which have many programmable register that can be combined to create complex functionality. Features appear as sub-directories in sysfs, having an enable parameter to set the full program setting.
- ETM strobing. This feature is introduced to ETMv4 to allow the trace
capture to be periodically switched on and off - strobed - to a given mark / space ratio to generate statistical trace data for workflows such as auto-fdo.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/Makefile | 7 +- .../hwtracing/coresight/coresight-config.c | 380 ++++++++++++++++++ .../hwtracing/coresight/coresight-config.h | 156 +++++++ .../hwtracing/coresight/coresight-etm4x-cfg.c | 325 +++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 29 ++ .../coresight/coresight-etm4x-sysfs.c | 3 + drivers/hwtracing/coresight/coresight-etm4x.c | 18 +- drivers/hwtracing/coresight/coresight.c | 1 + include/linux/coresight.h | 2 + 9 files changed, 917 insertions(+), 4 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-config.c create mode 100644 drivers/hwtracing/coresight/coresight-config.h create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.c create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.h
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 19497d1d92bf..b6c28f4cfca7 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -3,7 +3,8 @@ # Makefile for CoreSight drivers. # obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o \
coresight-platform.o coresight-sysfs.o
coresight-platform.o coresight-sysfs.o \
coresight-config.o
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \ coresight-tmc-etf.o \ coresight-tmc-etr.o @@ -14,7 +15,9 @@ obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \
coresight-etm4x-sysfs.o
coresight-etm4x-sysfs.o \
coresight-etm4x-cfg.o
obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c new file mode 100644 index 000000000000..10c2d546ca82 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/sysfs.h> +#include "coresight-config.h" +#include "coresight-priv.h"
+static DEFINE_MUTEX(coresight_cfg_mutex);
+/*
- Handlers for creating and manipulating the feature representation
- in sysfs.
- */
+/* base feature attribute info */ +struct feat_attr_info {
const char *name;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
+};
+static ssize_t cs_cfg_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
int val;
spin_lock(feat->dev_spinlock);
val = feat->enabled;
spin_unlock(feat->dev_spinlock);
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+static ssize_t cs_cfg_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
int val;
spin_lock(feat->dev_spinlock);
if (!kstrtoint(buf, 0, &val))
feat->enabled = val;
spin_unlock(feat->dev_spinlock);
return size;
+}
+static ssize_t cs_cfg_desc_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
return scnprintf(buf, PAGE_SIZE, "%s\n", feat->desc->brief);
+}
+struct feat_attr_info feature_std_attr[] = {
{
.name = "enable",
.show = cs_cfg_enable_show,
.store = cs_cfg_enable_store,
},
{
.name = "description",
.show = cs_cfg_desc_show,
},
{ 0 },
+};
+static ssize_t cs_cfg_param_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
u64 val;
spin_lock(param->feat->dev_spinlock);
val = param->current_value;
spin_unlock(param->feat->dev_spinlock);
return scnprintf(buf, PAGE_SIZE, "%lld\n", val);
+}
+static ssize_t cs_cfg_param_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
u64 val;
spin_lock(param->feat->dev_spinlock);
if (!kstrtoull(buf, 0, &val))
param->current_value = val;
spin_unlock(param->feat->dev_spinlock);
return size;
+}
+static struct attribute * +cs_cfg_sysfs_create_feat_attr(struct device *dev, struct cs_cfg_feature *feat,
struct feat_attr_info *info)
+{
struct dev_ext_attribute *eattr;
eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
if (!eattr)
return NULL;
eattr->attr.attr.name = devm_kstrdup(dev, info->name, GFP_KERNEL);
eattr->attr.attr.mode = info->store ? 0644 : 0444;
eattr->attr.show = info->show;
eattr->attr.store = info->store;
eattr->var = feat;
return &eattr->attr.attr;
+}
+static struct attribute * +cs_cfg_sysfs_create_param_attr(struct device *dev, const char *name,
struct cs_cfg_parameter *param)
+{
struct dev_ext_attribute *eattr;
eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
if (!eattr)
return NULL;
eattr->attr.attr.name = devm_kstrdup(dev, name, GFP_KERNEL);
eattr->attr.attr.mode = 0644;
eattr->attr.show = cs_cfg_param_show;
eattr->attr.store = cs_cfg_param_store;
eattr->var = param;
return &eattr->attr.attr;
+}
+int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat) +{
int p, attr_idx = 0;
struct attribute **attrs;
const char *name;
struct feat_attr_info *info;
struct device *dev = feat->csdev->dev.parent;
/* create sysfs group in csdev */
feat->param_group = devm_kzalloc(dev, sizeof(struct attribute_group),
GFP_KERNEL);
if (!feat->param_group)
return -ENOMEM;
feat->param_group->name = devm_kasprintf(dev, GFP_KERNEL, "%s.feat",
feat->desc->name);
if (!feat->param_group->name)
return -ENOMEM;
/* attributes - nr_params + enable + desc */
attrs = devm_kcalloc(dev, feat->nr_params + 2,
sizeof(struct attribute *), GFP_KERNEL);
if (!attrs)
return -ENOMEM;
/* create base attributes in group */
while (feature_std_attr[attr_idx].name) {
info = &feature_std_attr[attr_idx];
attrs[attr_idx] = cs_cfg_sysfs_create_feat_attr(dev, feat,
info);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
}
/* create params in group */
for (p = 0; p < feat->nr_params; p++) {
name = feat->desc->params[p].name;
attrs[attr_idx] =
cs_cfg_sysfs_create_param_attr(dev, name,
&feat->params[p]);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
}
feat->param_group->attrs = attrs;
return sysfs_create_group(&feat->csdev->dev.kobj, feat->param_group);
+}
+/*
- standard routines to set/save/reset enabled features, and iterate
- groups of features on a device.
- */
+void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat) +{
mutex_lock(&coresight_cfg_mutex);
list_add(&feat->node, &feat->csdev->feat_list);
mutex_unlock(&coresight_cfg_mutex);
+}
+inline void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags) +{
u32 *p_val32 = (u32 *)reg->drv_store;
u32 tmp32;
if (!(flags & CS_CFG_REG_VAL_64BIT)) {
if (flags & CS_CFG_REG_VAL_MASK) {
tmp32 = *p_val32;
tmp32 &= ~reg->value.mask32;
tmp32 |= reg->value.val32 & reg->value.mask32;
*p_val32 = tmp32;
} else
*p_val32 = reg->value.val32;
} else
*((u64 *)reg->drv_store) = reg->value.val64;
+}
+inline void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags) +{
if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = *(u64 *)(reg->drv_store);
else
reg->value.val32 = *(u32 *)(reg->drv_store);
+}
+inline void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc)
+{
reg->value.val64 = desc->value.val64;
+}
+inline void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param)
+{
if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = param->current_value;
else
reg->value.val32 = (u32)param->current_value;
+}
+/* default set - will set values without resource checking */ +static int cs_cfg_set_on_enable(struct cs_cfg_feature *feat) +{
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
coresight_cfg_set_reg(&feat->regs[i], flags);
}
}
dev_dbg(&feat->csdev->dev, "Feature %s: %s", feat->desc->name,
feat->enabled ? "Set enabled" : "Skip disabled");
spin_unlock(feat->dev_spinlock);
return 0;
+}
+static void cs_cfg_save_on_disable(struct cs_cfg_feature *feat) +{
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
if (flags & CS_CFG_REG_VAL_SAVE)
coresight_cfg_save_reg(&feat->regs[i], flags);
}
}
spin_unlock(feat->dev_spinlock);
+}
+/* default reset - restore default values, disable feature */ +static void cs_cfg_reset_feat(struct cs_cfg_feature *feat) +{
const struct cs_cfg_feature_desc *feat_desc = feat->desc;
struct cs_cfg_reg_desc *reg_desc;
struct cs_cfg_parameter *param;
struct cs_cfg_reg *reg;
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
feat->enabled = false;
/*
* set the default values for all parameters and regs from the
* relevant static descriptors.
*/
for (i = 0; i < feat->nr_params; i++)
feat->params[i].current_value = feat_desc->params[i].value;
for (i = 0; i < feat->nr_regs; i++) {
reg_desc = &feat_desc->regs[i];
flags = reg_desc->flags;
reg = &feat->regs[i];
/* check if reg set from a parameter otherwise desc default */
if (flags & CS_CFG_REG_VAL_PARAM) {
param = &feat->params[reg_desc->value.val32];
coresight_cfg_init_reg_param(reg, flags, param);
} else
coresight_cfg_init_reg(reg, reg_desc);
}
spin_unlock(feat->dev_spinlock);
+}
+void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat) +{
feat->ops.set_on_enable = cs_cfg_set_on_enable;
feat->ops.save_on_disable = cs_cfg_save_on_disable;
feat->ops.reset = cs_cfg_reset_feat;
+}
+int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
int err = 0;
if (list_empty(&csdev->feat_list))
return 0;
list_for_each_entry(feat, &csdev->feat_list, node) {
dev_dbg(&csdev->dev, "Found feature:%s", feat->desc->name);
if (feat->ops.set_on_enable) {
err = feat->ops.set_on_enable(feat);
dev_dbg(&csdev->dev, "Feature %s: %s",
feat->desc->name, err ? "Set failed" : "OK");
if (!err)
break;
}
}
return err;
+}
+void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
if (list_empty(&csdev->feat_list))
return;
list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.save_on_disable)
feat->ops.save_on_disable(feat);
}
+}
+void coresight_cfg_reset_feats(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
if (list_empty(&csdev->feat_list))
return;
list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.reset)
feat->ops.reset(feat);
}
+} diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h new file mode 100644 index 000000000000..ce69ad5d2bbf --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2020 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CONFIG_H +#define _CORESIGHT_CORESIGHT_CONFIG_H
+/* CoreSight Configuration - complex component and system config handling */
+/* generic register flags */ +#define CS_CFG_REG_STD 0x80000000 /* reg is standard reg */ +#define CS_CFG_REG_RESOURCE 0x40000000 /* reg is a resource */ +#define CS_CFG_REG_VAL_PARAM 0x08000000 /* reg value uses param */ +#define CS_CFG_REG_VAL_MASK 0x04000000 /* reg value bit masked */ +#define CS_CFG_REG_VAL_64BIT 0x02000000 /* reg value 64 bit */ +#define CS_CFG_REG_VAL_SAVE 0x01000000 /* reg value save on disable */ +#define CS_CFG_REG_ID_MASK 0x00000FFF /* reg offset / id in device */ +#define CS_CFG_REG_DEV_MASK 0x00FFF000 /* device specific flags */
+/**
- Parameter descriptor for a device feature.
- @name: Name of parameter.
- @value: Initial or default value.
- */
+struct cs_cfg_parameter_desc {
const char *name;
const u64 value;
+};
+/**
- Representation of register value.
- Supports full 64 bit register value, or 32 bit value with optional mask
- value.
- */
+union cs_cfg_regval {
u64 val64;
struct {
u32 val32;
u32 mask32;
};
+};
+/**
- Register descriptor for a device feature
- @flags: flags defining the handling of the register.
- @value: Initial value & optional mask.
- */
+struct cs_cfg_reg_desc {
const u32 flags;
const union cs_cfg_regval value;
+};
+/**
- Device feature descriptor - combination of registers and parameters to
- program a device to implement a specific complex function.
- @name: feature name.
- @brief: brief description of the feature.
- @params: list of parameters used - 0 terminated.
- @reg: list of registers used.
- */
+struct cs_cfg_feature_desc {
const char *name;
const char *brief;
struct cs_cfg_parameter_desc *params;
struct cs_cfg_reg_desc *regs;
+};
+/**
- config parameter instance - part of a loaded feature.
- @feat: parent feature
- @current_value: current value of parameter - may be set by user via
sysfs, or modified during device operation.
- */
+struct cs_cfg_parameter {
struct cs_cfg_feature *feat;
u64 current_value;
+};
+/**
- config register instance - part of a loaded feature.
maps register values to driver structures
- @value: value to use when setting feature on device / store for
readback of volatile values.
- @drv_store: pointer to internal driver element used to set the value
in hardware.
- */
+struct cs_cfg_reg {
union cs_cfg_regval value;
void *drv_store;
+};
+/**
- cs_cfg_ops - standard operations for features.
- CS config core provides basic defaults for these, which can be overridden by
- device specific versions.
- @set_on_enable: Set the feature on the driver before hw enable.
- @save_on_disable: Save any volatile register values after hw disable.
- @reset: Reset feature to default values.
- */
+struct cs_cfg_ops {
int (*set_on_enable)(struct cs_cfg_feature *feat);
void (*save_on_disable)(struct cs_cfg_feature *feat);
void (*reset)(struct cs_cfg_feature *feat);
+};
+/**
- Feature instance loaded into a CoreSight device driver.
- @desc: pointer to the static descriptor for this feature.
- @csdev: parent CoreSight device instance.
- @node: list entry into feature list for this device.
- @dev_spinlock: device spinlock.
- @enabled: feature is enabled on this device.
- @nr_params: number of parameters.
- @params: current parameter values on this device
- @param_group: sysfs group for feature parameters.
- @ops: standard ops to enable and disable features on devices.
- */
+struct cs_cfg_feature {
const struct cs_cfg_feature_desc *desc;
struct coresight_device *csdev;
struct list_head node;
spinlock_t *dev_spinlock;
bool enabled;
int nr_params;
struct cs_cfg_parameter *params;
struct attribute_group *param_group;
int nr_regs;
struct cs_cfg_reg *regs;
struct cs_cfg_ops ops;
+};
+/* coresight config API functions */ +int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat); +int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev); +void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev); +void coresight_cfg_reset_feats(struct coresight_device *csdev); +void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat); +void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat); +void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc);
+void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param);
+#endif /* _CORESIGHT_CORESIGHT_CONFIG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c new file mode 100644 index 000000000000..240e0235c130 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include "coresight-etm4x.h" +#include "coresight-etm4x-cfg.h" +#include "coresight-priv.h"
+/* built in features for ETMv4 */
+/* strobe feature */
+/* register defines */ +#define STRB_REG_CTR (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_CTR) +#define STRB_REG_CTR_RB (STRB_REG_CTR | CS_CFG_REG_VAL_SAVE) +#define STRB_REG_CTR_PRM (STRB_REG_CTR | CS_CFG_REG_VAL_PARAM) +#define STRB_REG_SEQ (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEQ) +#define STRB_REG_SEL (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEL) +#define STRB_REG_VI (CS_CFG_REG_STD | CS_CFG_REG_VAL_MASK)
+static struct cs_cfg_parameter_desc strobe_params[] = {
{
.name = "window",
.value = 1000,
},
{
.name = "period",
.value = 10000,
},
{ 0 },
+};
+static struct cs_cfg_reg_desc strob_regs[] = {
/* resource selectors */
{
.flags = STRB_REG_SEL | TRCRSCTLRn(2),
.value = {
.val32 = 0x20001,
},
},
{
.flags = STRB_REG_SEQ | TRCRSCTLRn(3),
.value = {
.val32 = 0x20002,
}
},
/* strobe window counter 0 - reload from param 0 */
{
.flags = STRB_REG_CTR_RB | TRCCNTVRn(0),
},
{
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(0),
.value = {
.val32 = 0,
},
},
{
.flags = STRB_REG_CTR | TRCCNTCTLRn(0),
.value = {
.val32 = 0x10001,
},
},
/* strobe period counter 1 - reload from param 1 */
{
.flags = STRB_REG_CTR_RB | TRCCNTVRn(1),
},
{
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(1),
.value = {
.val32 = 1,
},
},
{
.flags = STRB_REG_CTR | TRCCNTCTLRn(1),
.value = {
.val32 = 0x8102,
},
},
/* sequencer */
{
.flags = STRB_REG_SEQ | TRCSEQEVRn(0),
.value = {
.val32 = 0x0081,
},
},
{
.flags = STRB_REG_SEQ | TRCSEQEVRn(1),
.value = {
.val32 = 0x0000,
},
},
/* view-inst */
{
.flags = STRB_REG_VI | TRCVICTLR,
.value = {
.val32 = 0x0003,
.mask32 = 0x0003,
},
},
/* end of regs */
{ 0 },
+};
+static struct cs_cfg_feature_desc strobe = {
.name = "strobing",
.brief = "Generate periodic trace capture windows.",
.params = strobe_params,
.regs = strob_regs,
+};
+static struct cs_cfg_feature_desc *built_in_feats[] = {
&strobe,
0
+};
+/**
- etm4_cfg_map_reg_offset - validate and map the register offset into a
location in the driver config struct.
- Limits the number of registers that can be accessed and programmed in
- features, to those which are used to control the trace capture parameters.
- Omits or limits access to those which the driver must use exclusively.
- Invalid offsets will result in fail code return and feature load failure.
- @drvdata: driver data to map into.
- @reg: register to map.
- @offset: device offset for the register
- */
+static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata,
struct cs_cfg_reg *reg, u32 offset)
+{
int err = 0, idx;
struct etmv4_config *drvcfg = &drvdata->config;
#define MAPREG(cval, elem) \
case cval: \
reg->drv_store = &drvcfg->elem; \
break;
#define MAPREGIDX(cval, elem, off_idx) \
case cval: \
reg->drv_store = &drvcfg->elem[off_idx]; \
break;
if (((offset >= TRCEVENTCTL0R) && (offset <= TRCVIPCSSCTLR)) ||
((offset >= TRCSEQRSTEVR) && (offset <= TRCEXTINSELR)) ||
((offset >= TRCCIDCCTLR0) && (offset <= TRCVMIDCCTLR1))) {
switch (offset) {
/* 32 bit single control and filter registers */
MAPREG(TRCEVENTCTL0R, eventctrl0);
MAPREG(TRCEVENTCTL1R, eventctrl1);
MAPREG(TRCSTALLCTLR, stall_ctrl);
MAPREG(TRCTSCTLR, ts_ctrl);
MAPREG(TRCSYNCPR, syncfreq);
MAPREG(TRCCCCTLR, ccctlr);
MAPREG(TRCBBCTLR, bb_ctrl);
MAPREG(TRCVICTLR, vinst_ctrl);
MAPREG(TRCVIIECTLR, viiectlr);
MAPREG(TRCVISSCTLR, vissctlr);
MAPREG(TRCVIPCSSCTLR, vipcssctlr);
MAPREG(TRCSEQRSTEVR, seq_rst);
MAPREG(TRCSEQSTR, seq_state);
MAPREG(TRCEXTINSELR, ext_inp);
MAPREG(TRCCIDCCTLR0, ctxid_mask0);
MAPREG(TRCCIDCCTLR1, ctxid_mask1);
MAPREG(TRCVMIDCCTLR0, vmid_mask0);
MAPREG(TRCVMIDCCTLR1, vmid_mask1);
default:
err = -EINVAL;
break;
}
} else if ((offset & GENMASK(11, 4)) == TRCSEQEVRn(0)) {
/* sequencer state control registers */
idx = (offset & GENMASK(3, 0)) / 4;
if (idx < ETM_MAX_SEQ_STATES)
reg->drv_store = &drvcfg->seq_ctrl[idx];
else
err = -EINVAL;
} else if ((offset >= TRCSSCCRn(0)) && (offset <= TRCSSPCICRn(7))) {
/* 32 bit, 8 off indexed register sets */
idx = (offset & GENMASK(4, 0)) / 4;
switch (offset & GENMASK(11, 5)) {
MAPREGIDX(TRCSSCCRn(0), ss_ctrl, idx);
MAPREGIDX(TRCSSCSRn(0), ss_status, idx);
MAPREGIDX(TRCSSPCICRn(0), ss_pe_cmp, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCCIDCVRn(0)) && (offset <= TRCVMIDCVRn(7))) {
/* 64 bit, 8 off indexed register sets */
idx = (offset & GENMASK(5, 0)) / 8;
switch (offset & GENMASK(11, 6)) {
MAPREGIDX(TRCCIDCVRn(0), ctxid_pid, idx);
MAPREGIDX(TRCVMIDCVRn(0), vmid_val, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCRSCTLRn(2)) &&
(offset <= TRCRSCTLRn((ETM_MAX_RES_SEL - 1)))) {
/* 32 bit resource selection regs, 32 off, skip fixed 0,1 */
idx = (offset & GENMASK(6, 0)) / 4;
if (idx < ETM_MAX_RES_SEL)
reg->drv_store = &drvcfg->res_ctrl[idx];
else
err = -EINVAL;
} else if ((offset >= TRCACVRn(0)) &&
(offset <= TRCACATRn((ETM_MAX_SINGLE_ADDR_CMP - 1)))) {
/* 64 bit addr cmp regs, 16 off */
idx = (offset & GENMASK(6, 0)) / 8;
switch (offset & GENMASK(11, 7)) {
MAPREGIDX(TRCACVRn(0), addr_val, idx);
MAPREGIDX(TRCACATRn(0), addr_acc, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCCNTRLDVRn(0)) &&
(offset <= TRCCNTVRn((ETMv4_MAX_CNTR - 1)))) {
/* 32 bit counter regs, 4 off (ETMv4_MAX_CNTR - 1) */
idx = (offset & GENMASK(3, 0)) / 4;
switch (offset & GENMASK(11, 4)) {
MAPREGIDX(TRCCNTRLDVRn(0), cntrldvr, idx);
MAPREGIDX(TRCCNTCTLRn(0), cntr_ctrl, idx);
MAPREGIDX(TRCCNTVRn(0), cntr_val, idx);
default:
err = -EINVAL;
break;
}
} else {
err = -EINVAL;
}
return err;
+}
+/**
- etm4_cfg_load_feature - load a feature into a device instance.
- @csdev: An ETMv4 CoreSight device.
- @feat_desc: The static feature descriptor to be loaded.
- The function will load a feature instance into the device, based on the
- supplied descriptor. The descriptor will be checked to ensure a valid
- feature can be loaded for this ETMv4 device.
- Parameter and register definitions will be converted into internal
- structures that are used to set the values in the driver when the
- feature is enabled for the device.
- */
+int etm4_cfg_load_feature(struct coresight_device *csdev,
const struct cs_cfg_feature_desc *feat_desc)
+{
struct device *dev = csdev->dev.parent;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
struct cs_cfg_feature *feat;
u32 offset;
int i = 0, err = 0;
feat = devm_kzalloc(dev, sizeof(struct cs_cfg_feature), GFP_KERNEL);
if (!feat)
return -ENOMEM;
feat->desc = feat_desc;
feat->dev_spinlock = &drvdata->spinlock;
feat->csdev = csdev;
/* count the params regs and allocate space. */
while (feat_desc->params[i++].name)
feat->nr_params++;
i = 0;
while (feat_desc->regs[i++].flags)
feat->nr_regs++;
feat->params = devm_kcalloc(dev, feat->nr_params,
sizeof(struct cs_cfg_parameter),
GFP_KERNEL);
if (!feat->params)
return -ENOMEM;
feat->regs = devm_kcalloc(dev, feat->nr_regs,
sizeof(struct cs_cfg_reg), GFP_KERNEL);
if (!feat->regs)
return -ENOMEM;
/* load the parameters default values */
for (i = 0; i < feat->nr_params; i++)
feat->params[i].feat = feat;
/* process the register descriptions */
for (i = 0; i < feat->nr_regs; i++) {
offset = feat_desc->regs[i].flags & CS_CFG_REG_ID_MASK;
err = etm4_cfg_map_reg_offset(drvdata, &feat->regs[i], offset);
if (err)
return err;
}
/* set sysfs representation of the feature */
err = coresight_cfg_sysfs_add_grp(feat);
/* add the feature to the device list and init default values */
if (!err) {
coresight_cfg_list_add_feat(feat);
coresight_cfg_set_def_ops(feat);
feat->ops.reset(feat);
}
return err;
+}
+/* load up the list of builtin features */ +int etm4_cfg_load_builtins(struct coresight_device *csdev) +{
int idx = 0, err = 0;
while (built_in_feats[idx] && !err)
err = etm4_cfg_load_feature(csdev, built_in_feats[idx++]);
return err;
+} diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h new file mode 100644 index 000000000000..703657cac2fc --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
- */
+#ifndef _CORESIGHT_CORESIGHT_ETM4X_CFG_H +#define _CORESIGHT_CORESIGHT_ETM4X_CFG_H
+#include "coresight-config.h"
+/* ETMv4 specific config defines */
+/* resource IDs */
+#define ETM4_CFG_RES_CTR 0x00001000 +#define ETM4_CFG_RES_CMP 0x00002000 +#define ETM4_CFG_RES_CMP_PAIR0 0x00003000 +#define ETM4_CFG_RES_CMP_PAIR1 0x00004000 +#define ETM4_CFG_RES_SEL 0x00005000 +#define ETM4_CFG_RES_SEL_PAIR0 0x00006000 +#define ETM4_CFG_RES_SEL_PAIR1 0x00007000 +#define ETM4_CFG_RES_SEQ 0x00008000 +#define ETM4_CFG_RES_TS 0x00009000 +#define ETM4_CFG_RES_MASK 0x0000F000
+/* ETM4 configuration handlers */ +int etm4_cfg_load_builtins(struct coresight_device *csdev);
+#endif /* _CORESIGHT_CORESIGHT_ETM4X_CFG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index b673e738bc9a..ec182544a064 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -9,6 +9,7 @@ #include <linux/sysfs.h> #include "coresight-etm4x.h" #include "coresight-priv.h" +#include "coresight-config.h"
static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) { @@ -269,6 +270,8 @@ static ssize_t reset_store(struct device *dev,
spin_unlock(&drvdata->spinlock);
coresight_cfg_reset_feats(to_coresight_device(dev));
return size;
} static DEVICE_ATTR_WO(reset); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 60a5133d801c..e69dbcf5cd4d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -34,6 +34,7 @@
#include "coresight-etm4x.h" #include "coresight-etm-perf.h" +#include "coresight-etm4x-cfg.h"
static int boot_enable; module_param(boot_enable, int, 0444); @@ -320,10 +321,11 @@ static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata) return ret; }
-static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, +static int etm4_parse_event_config(struct coresight_device *csdev, struct perf_event *event) { int ret = 0;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_config *config = &drvdata->config; struct perf_event_attr *attr = &event->attr;
@@ -383,6 +385,9 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, /* bit[12], Return stack enable bit */ config->cfg |= BIT(12);
/* set any enabled features */
ret = coresight_cfg_set_feats_on_ena(csdev);
out: return ret; } @@ -399,7 +404,7 @@ static int etm4_enable_perf(struct coresight_device *csdev, }
/* Configure the tracer based on the session's specifics */
ret = etm4_parse_event_config(drvdata, event);
ret = etm4_parse_event_config(csdev, event); if (ret) goto out; /* And enable it */
@@ -415,6 +420,8 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) struct etm4_enable_arg arg = { }; int ret;
coresight_cfg_set_feats_on_ena(csdev);
spin_lock(&drvdata->spinlock); /*
@@ -532,6 +539,7 @@ static int etm4_disable_perf(struct coresight_device *csdev, return -EINVAL;
etm4_disable_hw(drvdata);
coresight_cfg_save_feats_on_dis(csdev); /* * Check if the start/stop logic was active when the unit was stopped.
@@ -566,6 +574,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) smp_call_function_single(drvdata->cpu, etm4_disable_hw, drvdata, 1);
spin_unlock(&drvdata->spinlock);
coresight_cfg_save_feats_on_dis(csdev); cpus_read_unlock(); dev_dbg(&csdev->dev, "ETM tracing disabled\n");
@@ -1510,6 +1519,11 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) goto err_arch_supported; }
/* load builtin features */
ret = etm4_cfg_load_builtins(drvdata->csdev);
if (ret)
goto err_arch_supported;
pm_runtime_put(&adev->dev); dev_info(&drvdata->csdev->dev, "CPU%d: ETM v%d.%d initialized\n", drvdata->cpu, drvdata->arch >> 4, drvdata->arch & 0xf);
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index f3efbb3b2b4d..ac16d96124f6 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1335,6 +1335,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) ret = coresight_fixup_orphan_conns(csdev); if (!ret) cti_add_assoc_to_csdev(csdev);
INIT_LIST_HEAD(&csdev->feat_list); mutex_unlock(&coresight_mutex); if (ret) {
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index e3e9f0e3a878..0e8d529282dd 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -204,6 +204,8 @@ struct coresight_device { /* sysfs links between components */ int nr_links; bool has_conns_grp;
/* feature list */
struct list_head feat_list;
};
/*
2.17.1
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
Hi Mathieu,
On Thu, 2 Jul 2020 at 18:53, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Hi
On Wed, 1 Jul 2020 at 13:26, Mike Leach mike.leach@linaro.org wrote:
Hi Mathieu,
On Wed, 24 Jun 2020 at 18:59, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Good day,
I like what you did in this set. It took me a while to come to terms with all the new structures but this is something we can address in subsequent revisions if need be. I also found a couple of minor problems but that too can be dealt with later. I would like to discuss 3 mains points:
- Make the features configurable _and_ modular. Depending on how many features
people come up with it may become tedious to carry all that extra code. For the modularity I suggest you take inspiration from the governor model used in the cpufreq subsystem. Governors, such as the performance governor[1], are all modules that get registered with the cpufreq core[2]. From there when a governor is selected[3] the core increases the governor's module count[4], preventing the module from being unloaded while a governor is used.
That is certainly the intention. But rather than compilable C modules, new features will be binary data , loadable at runtime, using the same format as the builtin example in this patch.
What command do you envision using to do the loading at runtime?
Copy of a file into the configfs directory managed by the coregisht system driver (this is added by the complex config work, though not in this set). The system driver will then load specific component drivers as required.
The intention is that these data structures can be generated as a binary file using some scripted tool interpreting a simple text file description. This is then loaded as described below.
- Please explore the possibility of using configfs rather than sysfs. That way
users can create their own features without the need to add a new module.
This is also underway. A coresight system config driver will have a configfs interface that will allow the loading of new system configurations and features - where a configuration is a set of new or existing features to use as part of a system wide setup. The driver will interpret the incoming binary file, and distribute features to the correct drivers - e.g. a new ETMv4 feature will be loaded into all the ETMv4 instances. New features can be loaded in this to supplement any "builtin" that exist such as the one in the current patch.
Sysfs is retained to control features as in this example - the features do the same job as the other registers / files in sysfs in programming up the device.
Does that mean we'd have two different places for configuring features? I'm fine with feature names showing up in sysfs but all configuration specifics should be in configfs.
Features and configurations are loaded using configfs as described above. Configurations may consist of new complex features - such as strobing, but can also include any of the intrinsic features such as address filtering, timestamps, etc.
So perf users will select a configuration by name, which will load the indicated features on any ETM it uses.
sysfs users can control any loaded complex features in the same way as they would intrinsic features, maintaining a logical programming structure.
Isolation between perf and sysfs is therefore maintained as it was previously.
So the current infrastructure that may seem more than is necessary for just strobing in ETMv4, is there to prepare for the modularity that is required.
- Perf sessions need to be able to choose which feature they want to use rather
than defaulting to a system wide configuration. If you don't want to deal with that at this stage - something I would certainly understand - simpy don't take features into account from the perf interface.
Ultimately this too is the goal. If "auto-fdo" is the name of a configuration, that selects the "strobing" feature on all etms used in the perf session then the command:- perf record -e cs_etm/@auto-fdo/ <other perf options> will allow the selection of the feature. A later patch set will introduce this concept.
Right, that's the optimal solution.
Here we are replacing the sink ID with a configuration ID (and selecting a default sink instead) - which is why default sink was important for this work.
The current patch is actually a functionally identical patch to one that used to be on perf-opencsd that enabled etm strobing for autoFDO and perf. That patch also added etm strobing without selection by perf
- but just did it in a way that was less obvious. It is being used by
customers as an out of tree patch, so there is a requirement from them to get it up upstreamed so it can be used in production environments.
The patch here introduces a limited amount of the complex config API, sufficient to support etm strobing for those who are actively using it.
The intention being to introduce the rest of complex config in separate stages to make is easier for reviewers to understand what is being done.
Right, I'm all in favour of that. On the flip side I want to make sure not to introduce anything we'll have to maintain in the long run. Right now the global nature of the feature selection in sysfs can't be mixed with perf.
Good point.
OK - then this set really doesn't work in terms of getting strobing upstream to those key customers who need it. I will have to add more to make this viable - i.e. some of the pref selection code.
Regards
Mike
Regards
Mike
Thanks, Mathieu
[1]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq_perfo... [2]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L23... [3]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L76... [4]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L63...
On Wed, Jun 10, 2020 at 10:13:32AM +0100, Mike Leach wrote:
This set introduces the concept of complex features for CoreSight devices, and adds a feature to ETMv4 to enable strobing of the etm capture.
- Complex Features: These are programming settings for devices, especially
ETM which have many programmable register that can be combined to create complex functionality. Features appear as sub-directories in sysfs, having an enable parameter to set the full program setting.
- ETM strobing. This feature is introduced to ETMv4 to allow the trace
capture to be periodically switched on and off - strobed - to a given mark / space ratio to generate statistical trace data for workflows such as auto-fdo.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/Makefile | 7 +- .../hwtracing/coresight/coresight-config.c | 380 ++++++++++++++++++ .../hwtracing/coresight/coresight-config.h | 156 +++++++ .../hwtracing/coresight/coresight-etm4x-cfg.c | 325 +++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 29 ++ .../coresight/coresight-etm4x-sysfs.c | 3 + drivers/hwtracing/coresight/coresight-etm4x.c | 18 +- drivers/hwtracing/coresight/coresight.c | 1 + include/linux/coresight.h | 2 + 9 files changed, 917 insertions(+), 4 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-config.c create mode 100644 drivers/hwtracing/coresight/coresight-config.h create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.c create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.h
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 19497d1d92bf..b6c28f4cfca7 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -3,7 +3,8 @@ # Makefile for CoreSight drivers. # obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o \
coresight-platform.o coresight-sysfs.o
coresight-platform.o coresight-sysfs.o \
coresight-config.o
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \ coresight-tmc-etf.o \ coresight-tmc-etr.o @@ -14,7 +15,9 @@ obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \
coresight-etm4x-sysfs.o
coresight-etm4x-sysfs.o \
coresight-etm4x-cfg.o
obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c new file mode 100644 index 000000000000..10c2d546ca82 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/sysfs.h> +#include "coresight-config.h" +#include "coresight-priv.h"
+static DEFINE_MUTEX(coresight_cfg_mutex);
+/*
- Handlers for creating and manipulating the feature representation
- in sysfs.
- */
+/* base feature attribute info */ +struct feat_attr_info {
const char *name;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
+};
+static ssize_t cs_cfg_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
int val;
spin_lock(feat->dev_spinlock);
val = feat->enabled;
spin_unlock(feat->dev_spinlock);
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+static ssize_t cs_cfg_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
int val;
spin_lock(feat->dev_spinlock);
if (!kstrtoint(buf, 0, &val))
feat->enabled = val;
spin_unlock(feat->dev_spinlock);
return size;
+}
+static ssize_t cs_cfg_desc_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
return scnprintf(buf, PAGE_SIZE, "%s\n", feat->desc->brief);
+}
+struct feat_attr_info feature_std_attr[] = {
{
.name = "enable",
.show = cs_cfg_enable_show,
.store = cs_cfg_enable_store,
},
{
.name = "description",
.show = cs_cfg_desc_show,
},
{ 0 },
+};
+static ssize_t cs_cfg_param_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
u64 val;
spin_lock(param->feat->dev_spinlock);
val = param->current_value;
spin_unlock(param->feat->dev_spinlock);
return scnprintf(buf, PAGE_SIZE, "%lld\n", val);
+}
+static ssize_t cs_cfg_param_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
u64 val;
spin_lock(param->feat->dev_spinlock);
if (!kstrtoull(buf, 0, &val))
param->current_value = val;
spin_unlock(param->feat->dev_spinlock);
return size;
+}
+static struct attribute * +cs_cfg_sysfs_create_feat_attr(struct device *dev, struct cs_cfg_feature *feat,
struct feat_attr_info *info)
+{
struct dev_ext_attribute *eattr;
eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
if (!eattr)
return NULL;
eattr->attr.attr.name = devm_kstrdup(dev, info->name, GFP_KERNEL);
eattr->attr.attr.mode = info->store ? 0644 : 0444;
eattr->attr.show = info->show;
eattr->attr.store = info->store;
eattr->var = feat;
return &eattr->attr.attr;
+}
+static struct attribute * +cs_cfg_sysfs_create_param_attr(struct device *dev, const char *name,
struct cs_cfg_parameter *param)
+{
struct dev_ext_attribute *eattr;
eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
if (!eattr)
return NULL;
eattr->attr.attr.name = devm_kstrdup(dev, name, GFP_KERNEL);
eattr->attr.attr.mode = 0644;
eattr->attr.show = cs_cfg_param_show;
eattr->attr.store = cs_cfg_param_store;
eattr->var = param;
return &eattr->attr.attr;
+}
+int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat) +{
int p, attr_idx = 0;
struct attribute **attrs;
const char *name;
struct feat_attr_info *info;
struct device *dev = feat->csdev->dev.parent;
/* create sysfs group in csdev */
feat->param_group = devm_kzalloc(dev, sizeof(struct attribute_group),
GFP_KERNEL);
if (!feat->param_group)
return -ENOMEM;
feat->param_group->name = devm_kasprintf(dev, GFP_KERNEL, "%s.feat",
feat->desc->name);
if (!feat->param_group->name)
return -ENOMEM;
/* attributes - nr_params + enable + desc */
attrs = devm_kcalloc(dev, feat->nr_params + 2,
sizeof(struct attribute *), GFP_KERNEL);
if (!attrs)
return -ENOMEM;
/* create base attributes in group */
while (feature_std_attr[attr_idx].name) {
info = &feature_std_attr[attr_idx];
attrs[attr_idx] = cs_cfg_sysfs_create_feat_attr(dev, feat,
info);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
}
/* create params in group */
for (p = 0; p < feat->nr_params; p++) {
name = feat->desc->params[p].name;
attrs[attr_idx] =
cs_cfg_sysfs_create_param_attr(dev, name,
&feat->params[p]);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
}
feat->param_group->attrs = attrs;
return sysfs_create_group(&feat->csdev->dev.kobj, feat->param_group);
+}
+/*
- standard routines to set/save/reset enabled features, and iterate
- groups of features on a device.
- */
+void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat) +{
mutex_lock(&coresight_cfg_mutex);
list_add(&feat->node, &feat->csdev->feat_list);
mutex_unlock(&coresight_cfg_mutex);
+}
+inline void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags) +{
u32 *p_val32 = (u32 *)reg->drv_store;
u32 tmp32;
if (!(flags & CS_CFG_REG_VAL_64BIT)) {
if (flags & CS_CFG_REG_VAL_MASK) {
tmp32 = *p_val32;
tmp32 &= ~reg->value.mask32;
tmp32 |= reg->value.val32 & reg->value.mask32;
*p_val32 = tmp32;
} else
*p_val32 = reg->value.val32;
} else
*((u64 *)reg->drv_store) = reg->value.val64;
+}
+inline void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags) +{
if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = *(u64 *)(reg->drv_store);
else
reg->value.val32 = *(u32 *)(reg->drv_store);
+}
+inline void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc)
+{
reg->value.val64 = desc->value.val64;
+}
+inline void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param)
+{
if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = param->current_value;
else
reg->value.val32 = (u32)param->current_value;
+}
+/* default set - will set values without resource checking */ +static int cs_cfg_set_on_enable(struct cs_cfg_feature *feat) +{
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
coresight_cfg_set_reg(&feat->regs[i], flags);
}
}
dev_dbg(&feat->csdev->dev, "Feature %s: %s", feat->desc->name,
feat->enabled ? "Set enabled" : "Skip disabled");
spin_unlock(feat->dev_spinlock);
return 0;
+}
+static void cs_cfg_save_on_disable(struct cs_cfg_feature *feat) +{
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
if (flags & CS_CFG_REG_VAL_SAVE)
coresight_cfg_save_reg(&feat->regs[i], flags);
}
}
spin_unlock(feat->dev_spinlock);
+}
+/* default reset - restore default values, disable feature */ +static void cs_cfg_reset_feat(struct cs_cfg_feature *feat) +{
const struct cs_cfg_feature_desc *feat_desc = feat->desc;
struct cs_cfg_reg_desc *reg_desc;
struct cs_cfg_parameter *param;
struct cs_cfg_reg *reg;
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
feat->enabled = false;
/*
* set the default values for all parameters and regs from the
* relevant static descriptors.
*/
for (i = 0; i < feat->nr_params; i++)
feat->params[i].current_value = feat_desc->params[i].value;
for (i = 0; i < feat->nr_regs; i++) {
reg_desc = &feat_desc->regs[i];
flags = reg_desc->flags;
reg = &feat->regs[i];
/* check if reg set from a parameter otherwise desc default */
if (flags & CS_CFG_REG_VAL_PARAM) {
param = &feat->params[reg_desc->value.val32];
coresight_cfg_init_reg_param(reg, flags, param);
} else
coresight_cfg_init_reg(reg, reg_desc);
}
spin_unlock(feat->dev_spinlock);
+}
+void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat) +{
feat->ops.set_on_enable = cs_cfg_set_on_enable;
feat->ops.save_on_disable = cs_cfg_save_on_disable;
feat->ops.reset = cs_cfg_reset_feat;
+}
+int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
int err = 0;
if (list_empty(&csdev->feat_list))
return 0;
list_for_each_entry(feat, &csdev->feat_list, node) {
dev_dbg(&csdev->dev, "Found feature:%s", feat->desc->name);
if (feat->ops.set_on_enable) {
err = feat->ops.set_on_enable(feat);
dev_dbg(&csdev->dev, "Feature %s: %s",
feat->desc->name, err ? "Set failed" : "OK");
if (!err)
break;
}
}
return err;
+}
+void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
if (list_empty(&csdev->feat_list))
return;
list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.save_on_disable)
feat->ops.save_on_disable(feat);
}
+}
+void coresight_cfg_reset_feats(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
if (list_empty(&csdev->feat_list))
return;
list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.reset)
feat->ops.reset(feat);
}
+} diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h new file mode 100644 index 000000000000..ce69ad5d2bbf --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2020 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CONFIG_H +#define _CORESIGHT_CORESIGHT_CONFIG_H
+/* CoreSight Configuration - complex component and system config handling */
+/* generic register flags */ +#define CS_CFG_REG_STD 0x80000000 /* reg is standard reg */ +#define CS_CFG_REG_RESOURCE 0x40000000 /* reg is a resource */ +#define CS_CFG_REG_VAL_PARAM 0x08000000 /* reg value uses param */ +#define CS_CFG_REG_VAL_MASK 0x04000000 /* reg value bit masked */ +#define CS_CFG_REG_VAL_64BIT 0x02000000 /* reg value 64 bit */ +#define CS_CFG_REG_VAL_SAVE 0x01000000 /* reg value save on disable */ +#define CS_CFG_REG_ID_MASK 0x00000FFF /* reg offset / id in device */ +#define CS_CFG_REG_DEV_MASK 0x00FFF000 /* device specific flags */
+/**
- Parameter descriptor for a device feature.
- @name: Name of parameter.
- @value: Initial or default value.
- */
+struct cs_cfg_parameter_desc {
const char *name;
const u64 value;
+};
+/**
- Representation of register value.
- Supports full 64 bit register value, or 32 bit value with optional mask
- value.
- */
+union cs_cfg_regval {
u64 val64;
struct {
u32 val32;
u32 mask32;
};
+};
+/**
- Register descriptor for a device feature
- @flags: flags defining the handling of the register.
- @value: Initial value & optional mask.
- */
+struct cs_cfg_reg_desc {
const u32 flags;
const union cs_cfg_regval value;
+};
+/**
- Device feature descriptor - combination of registers and parameters to
- program a device to implement a specific complex function.
- @name: feature name.
- @brief: brief description of the feature.
- @params: list of parameters used - 0 terminated.
- @reg: list of registers used.
- */
+struct cs_cfg_feature_desc {
const char *name;
const char *brief;
struct cs_cfg_parameter_desc *params;
struct cs_cfg_reg_desc *regs;
+};
+/**
- config parameter instance - part of a loaded feature.
- @feat: parent feature
- @current_value: current value of parameter - may be set by user via
sysfs, or modified during device operation.
- */
+struct cs_cfg_parameter {
struct cs_cfg_feature *feat;
u64 current_value;
+};
+/**
- config register instance - part of a loaded feature.
maps register values to driver structures
- @value: value to use when setting feature on device / store for
readback of volatile values.
- @drv_store: pointer to internal driver element used to set the value
in hardware.
- */
+struct cs_cfg_reg {
union cs_cfg_regval value;
void *drv_store;
+};
+/**
- cs_cfg_ops - standard operations for features.
- CS config core provides basic defaults for these, which can be overridden by
- device specific versions.
- @set_on_enable: Set the feature on the driver before hw enable.
- @save_on_disable: Save any volatile register values after hw disable.
- @reset: Reset feature to default values.
- */
+struct cs_cfg_ops {
int (*set_on_enable)(struct cs_cfg_feature *feat);
void (*save_on_disable)(struct cs_cfg_feature *feat);
void (*reset)(struct cs_cfg_feature *feat);
+};
+/**
- Feature instance loaded into a CoreSight device driver.
- @desc: pointer to the static descriptor for this feature.
- @csdev: parent CoreSight device instance.
- @node: list entry into feature list for this device.
- @dev_spinlock: device spinlock.
- @enabled: feature is enabled on this device.
- @nr_params: number of parameters.
- @params: current parameter values on this device
- @param_group: sysfs group for feature parameters.
- @ops: standard ops to enable and disable features on devices.
- */
+struct cs_cfg_feature {
const struct cs_cfg_feature_desc *desc;
struct coresight_device *csdev;
struct list_head node;
spinlock_t *dev_spinlock;
bool enabled;
int nr_params;
struct cs_cfg_parameter *params;
struct attribute_group *param_group;
int nr_regs;
struct cs_cfg_reg *regs;
struct cs_cfg_ops ops;
+};
+/* coresight config API functions */ +int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat); +int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev); +void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev); +void coresight_cfg_reset_feats(struct coresight_device *csdev); +void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat); +void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat); +void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc);
+void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param);
+#endif /* _CORESIGHT_CORESIGHT_CONFIG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c new file mode 100644 index 000000000000..240e0235c130 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include "coresight-etm4x.h" +#include "coresight-etm4x-cfg.h" +#include "coresight-priv.h"
+/* built in features for ETMv4 */
+/* strobe feature */
+/* register defines */ +#define STRB_REG_CTR (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_CTR) +#define STRB_REG_CTR_RB (STRB_REG_CTR | CS_CFG_REG_VAL_SAVE) +#define STRB_REG_CTR_PRM (STRB_REG_CTR | CS_CFG_REG_VAL_PARAM) +#define STRB_REG_SEQ (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEQ) +#define STRB_REG_SEL (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEL) +#define STRB_REG_VI (CS_CFG_REG_STD | CS_CFG_REG_VAL_MASK)
+static struct cs_cfg_parameter_desc strobe_params[] = {
{
.name = "window",
.value = 1000,
},
{
.name = "period",
.value = 10000,
},
{ 0 },
+};
+static struct cs_cfg_reg_desc strob_regs[] = {
/* resource selectors */
{
.flags = STRB_REG_SEL | TRCRSCTLRn(2),
.value = {
.val32 = 0x20001,
},
},
{
.flags = STRB_REG_SEQ | TRCRSCTLRn(3),
.value = {
.val32 = 0x20002,
}
},
/* strobe window counter 0 - reload from param 0 */
{
.flags = STRB_REG_CTR_RB | TRCCNTVRn(0),
},
{
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(0),
.value = {
.val32 = 0,
},
},
{
.flags = STRB_REG_CTR | TRCCNTCTLRn(0),
.value = {
.val32 = 0x10001,
},
},
/* strobe period counter 1 - reload from param 1 */
{
.flags = STRB_REG_CTR_RB | TRCCNTVRn(1),
},
{
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(1),
.value = {
.val32 = 1,
},
},
{
.flags = STRB_REG_CTR | TRCCNTCTLRn(1),
.value = {
.val32 = 0x8102,
},
},
/* sequencer */
{
.flags = STRB_REG_SEQ | TRCSEQEVRn(0),
.value = {
.val32 = 0x0081,
},
},
{
.flags = STRB_REG_SEQ | TRCSEQEVRn(1),
.value = {
.val32 = 0x0000,
},
},
/* view-inst */
{
.flags = STRB_REG_VI | TRCVICTLR,
.value = {
.val32 = 0x0003,
.mask32 = 0x0003,
},
},
/* end of regs */
{ 0 },
+};
+static struct cs_cfg_feature_desc strobe = {
.name = "strobing",
.brief = "Generate periodic trace capture windows.",
.params = strobe_params,
.regs = strob_regs,
+};
+static struct cs_cfg_feature_desc *built_in_feats[] = {
&strobe,
0
+};
+/**
- etm4_cfg_map_reg_offset - validate and map the register offset into a
location in the driver config struct.
- Limits the number of registers that can be accessed and programmed in
- features, to those which are used to control the trace capture parameters.
- Omits or limits access to those which the driver must use exclusively.
- Invalid offsets will result in fail code return and feature load failure.
- @drvdata: driver data to map into.
- @reg: register to map.
- @offset: device offset for the register
- */
+static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata,
struct cs_cfg_reg *reg, u32 offset)
+{
int err = 0, idx;
struct etmv4_config *drvcfg = &drvdata->config;
#define MAPREG(cval, elem) \
case cval: \
reg->drv_store = &drvcfg->elem; \
break;
#define MAPREGIDX(cval, elem, off_idx) \
case cval: \
reg->drv_store = &drvcfg->elem[off_idx]; \
break;
if (((offset >= TRCEVENTCTL0R) && (offset <= TRCVIPCSSCTLR)) ||
((offset >= TRCSEQRSTEVR) && (offset <= TRCEXTINSELR)) ||
((offset >= TRCCIDCCTLR0) && (offset <= TRCVMIDCCTLR1))) {
switch (offset) {
/* 32 bit single control and filter registers */
MAPREG(TRCEVENTCTL0R, eventctrl0);
MAPREG(TRCEVENTCTL1R, eventctrl1);
MAPREG(TRCSTALLCTLR, stall_ctrl);
MAPREG(TRCTSCTLR, ts_ctrl);
MAPREG(TRCSYNCPR, syncfreq);
MAPREG(TRCCCCTLR, ccctlr);
MAPREG(TRCBBCTLR, bb_ctrl);
MAPREG(TRCVICTLR, vinst_ctrl);
MAPREG(TRCVIIECTLR, viiectlr);
MAPREG(TRCVISSCTLR, vissctlr);
MAPREG(TRCVIPCSSCTLR, vipcssctlr);
MAPREG(TRCSEQRSTEVR, seq_rst);
MAPREG(TRCSEQSTR, seq_state);
MAPREG(TRCEXTINSELR, ext_inp);
MAPREG(TRCCIDCCTLR0, ctxid_mask0);
MAPREG(TRCCIDCCTLR1, ctxid_mask1);
MAPREG(TRCVMIDCCTLR0, vmid_mask0);
MAPREG(TRCVMIDCCTLR1, vmid_mask1);
default:
err = -EINVAL;
break;
}
} else if ((offset & GENMASK(11, 4)) == TRCSEQEVRn(0)) {
/* sequencer state control registers */
idx = (offset & GENMASK(3, 0)) / 4;
if (idx < ETM_MAX_SEQ_STATES)
reg->drv_store = &drvcfg->seq_ctrl[idx];
else
err = -EINVAL;
} else if ((offset >= TRCSSCCRn(0)) && (offset <= TRCSSPCICRn(7))) {
/* 32 bit, 8 off indexed register sets */
idx = (offset & GENMASK(4, 0)) / 4;
switch (offset & GENMASK(11, 5)) {
MAPREGIDX(TRCSSCCRn(0), ss_ctrl, idx);
MAPREGIDX(TRCSSCSRn(0), ss_status, idx);
MAPREGIDX(TRCSSPCICRn(0), ss_pe_cmp, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCCIDCVRn(0)) && (offset <= TRCVMIDCVRn(7))) {
/* 64 bit, 8 off indexed register sets */
idx = (offset & GENMASK(5, 0)) / 8;
switch (offset & GENMASK(11, 6)) {
MAPREGIDX(TRCCIDCVRn(0), ctxid_pid, idx);
MAPREGIDX(TRCVMIDCVRn(0), vmid_val, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCRSCTLRn(2)) &&
(offset <= TRCRSCTLRn((ETM_MAX_RES_SEL - 1)))) {
/* 32 bit resource selection regs, 32 off, skip fixed 0,1 */
idx = (offset & GENMASK(6, 0)) / 4;
if (idx < ETM_MAX_RES_SEL)
reg->drv_store = &drvcfg->res_ctrl[idx];
else
err = -EINVAL;
} else if ((offset >= TRCACVRn(0)) &&
(offset <= TRCACATRn((ETM_MAX_SINGLE_ADDR_CMP - 1)))) {
/* 64 bit addr cmp regs, 16 off */
idx = (offset & GENMASK(6, 0)) / 8;
switch (offset & GENMASK(11, 7)) {
MAPREGIDX(TRCACVRn(0), addr_val, idx);
MAPREGIDX(TRCACATRn(0), addr_acc, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCCNTRLDVRn(0)) &&
(offset <= TRCCNTVRn((ETMv4_MAX_CNTR - 1)))) {
/* 32 bit counter regs, 4 off (ETMv4_MAX_CNTR - 1) */
idx = (offset & GENMASK(3, 0)) / 4;
switch (offset & GENMASK(11, 4)) {
MAPREGIDX(TRCCNTRLDVRn(0), cntrldvr, idx);
MAPREGIDX(TRCCNTCTLRn(0), cntr_ctrl, idx);
MAPREGIDX(TRCCNTVRn(0), cntr_val, idx);
default:
err = -EINVAL;
break;
}
} else {
err = -EINVAL;
}
return err;
+}
+/**
- etm4_cfg_load_feature - load a feature into a device instance.
- @csdev: An ETMv4 CoreSight device.
- @feat_desc: The static feature descriptor to be loaded.
- The function will load a feature instance into the device, based on the
- supplied descriptor. The descriptor will be checked to ensure a valid
- feature can be loaded for this ETMv4 device.
- Parameter and register definitions will be converted into internal
- structures that are used to set the values in the driver when the
- feature is enabled for the device.
- */
+int etm4_cfg_load_feature(struct coresight_device *csdev,
const struct cs_cfg_feature_desc *feat_desc)
+{
struct device *dev = csdev->dev.parent;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
struct cs_cfg_feature *feat;
u32 offset;
int i = 0, err = 0;
feat = devm_kzalloc(dev, sizeof(struct cs_cfg_feature), GFP_KERNEL);
if (!feat)
return -ENOMEM;
feat->desc = feat_desc;
feat->dev_spinlock = &drvdata->spinlock;
feat->csdev = csdev;
/* count the params regs and allocate space. */
while (feat_desc->params[i++].name)
feat->nr_params++;
i = 0;
while (feat_desc->regs[i++].flags)
feat->nr_regs++;
feat->params = devm_kcalloc(dev, feat->nr_params,
sizeof(struct cs_cfg_parameter),
GFP_KERNEL);
if (!feat->params)
return -ENOMEM;
feat->regs = devm_kcalloc(dev, feat->nr_regs,
sizeof(struct cs_cfg_reg), GFP_KERNEL);
if (!feat->regs)
return -ENOMEM;
/* load the parameters default values */
for (i = 0; i < feat->nr_params; i++)
feat->params[i].feat = feat;
/* process the register descriptions */
for (i = 0; i < feat->nr_regs; i++) {
offset = feat_desc->regs[i].flags & CS_CFG_REG_ID_MASK;
err = etm4_cfg_map_reg_offset(drvdata, &feat->regs[i], offset);
if (err)
return err;
}
/* set sysfs representation of the feature */
err = coresight_cfg_sysfs_add_grp(feat);
/* add the feature to the device list and init default values */
if (!err) {
coresight_cfg_list_add_feat(feat);
coresight_cfg_set_def_ops(feat);
feat->ops.reset(feat);
}
return err;
+}
+/* load up the list of builtin features */ +int etm4_cfg_load_builtins(struct coresight_device *csdev) +{
int idx = 0, err = 0;
while (built_in_feats[idx] && !err)
err = etm4_cfg_load_feature(csdev, built_in_feats[idx++]);
return err;
+} diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h new file mode 100644 index 000000000000..703657cac2fc --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
- */
+#ifndef _CORESIGHT_CORESIGHT_ETM4X_CFG_H +#define _CORESIGHT_CORESIGHT_ETM4X_CFG_H
+#include "coresight-config.h"
+/* ETMv4 specific config defines */
+/* resource IDs */
+#define ETM4_CFG_RES_CTR 0x00001000 +#define ETM4_CFG_RES_CMP 0x00002000 +#define ETM4_CFG_RES_CMP_PAIR0 0x00003000 +#define ETM4_CFG_RES_CMP_PAIR1 0x00004000 +#define ETM4_CFG_RES_SEL 0x00005000 +#define ETM4_CFG_RES_SEL_PAIR0 0x00006000 +#define ETM4_CFG_RES_SEL_PAIR1 0x00007000 +#define ETM4_CFG_RES_SEQ 0x00008000 +#define ETM4_CFG_RES_TS 0x00009000 +#define ETM4_CFG_RES_MASK 0x0000F000
+/* ETM4 configuration handlers */ +int etm4_cfg_load_builtins(struct coresight_device *csdev);
+#endif /* _CORESIGHT_CORESIGHT_ETM4X_CFG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index b673e738bc9a..ec182544a064 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -9,6 +9,7 @@ #include <linux/sysfs.h> #include "coresight-etm4x.h" #include "coresight-priv.h" +#include "coresight-config.h"
static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) { @@ -269,6 +270,8 @@ static ssize_t reset_store(struct device *dev,
spin_unlock(&drvdata->spinlock);
coresight_cfg_reset_feats(to_coresight_device(dev));
return size;
} static DEVICE_ATTR_WO(reset); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 60a5133d801c..e69dbcf5cd4d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -34,6 +34,7 @@
#include "coresight-etm4x.h" #include "coresight-etm-perf.h" +#include "coresight-etm4x-cfg.h"
static int boot_enable; module_param(boot_enable, int, 0444); @@ -320,10 +321,11 @@ static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata) return ret; }
-static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, +static int etm4_parse_event_config(struct coresight_device *csdev, struct perf_event *event) { int ret = 0;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_config *config = &drvdata->config; struct perf_event_attr *attr = &event->attr;
@@ -383,6 +385,9 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, /* bit[12], Return stack enable bit */ config->cfg |= BIT(12);
/* set any enabled features */
ret = coresight_cfg_set_feats_on_ena(csdev);
out: return ret; } @@ -399,7 +404,7 @@ static int etm4_enable_perf(struct coresight_device *csdev, }
/* Configure the tracer based on the session's specifics */
ret = etm4_parse_event_config(drvdata, event);
ret = etm4_parse_event_config(csdev, event); if (ret) goto out; /* And enable it */
@@ -415,6 +420,8 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) struct etm4_enable_arg arg = { }; int ret;
coresight_cfg_set_feats_on_ena(csdev);
spin_lock(&drvdata->spinlock); /*
@@ -532,6 +539,7 @@ static int etm4_disable_perf(struct coresight_device *csdev, return -EINVAL;
etm4_disable_hw(drvdata);
coresight_cfg_save_feats_on_dis(csdev); /* * Check if the start/stop logic was active when the unit was stopped.
@@ -566,6 +574,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) smp_call_function_single(drvdata->cpu, etm4_disable_hw, drvdata, 1);
spin_unlock(&drvdata->spinlock);
coresight_cfg_save_feats_on_dis(csdev); cpus_read_unlock(); dev_dbg(&csdev->dev, "ETM tracing disabled\n");
@@ -1510,6 +1519,11 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) goto err_arch_supported; }
/* load builtin features */
ret = etm4_cfg_load_builtins(drvdata->csdev);
if (ret)
goto err_arch_supported;
pm_runtime_put(&adev->dev); dev_info(&drvdata->csdev->dev, "CPU%d: ETM v%d.%d initialized\n", drvdata->cpu, drvdata->arch >> 4, drvdata->arch & 0xf);
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index f3efbb3b2b4d..ac16d96124f6 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1335,6 +1335,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) ret = coresight_fixup_orphan_conns(csdev); if (!ret) cti_add_assoc_to_csdev(csdev);
INIT_LIST_HEAD(&csdev->feat_list); mutex_unlock(&coresight_mutex); if (ret) {
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index e3e9f0e3a878..0e8d529282dd 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -204,6 +204,8 @@ struct coresight_device { /* sysfs links between components */ int nr_links; bool has_conns_grp;
/* feature list */
struct list_head feat_list;
};
/*
2.17.1
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
On Mon, 6 Jul 2020 at 03:08, Mike Leach mike.leach@linaro.org wrote:
Hi Mathieu,
On Thu, 2 Jul 2020 at 18:53, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Hi
On Wed, 1 Jul 2020 at 13:26, Mike Leach mike.leach@linaro.org wrote:
Hi Mathieu,
On Wed, 24 Jun 2020 at 18:59, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Good day,
I like what you did in this set. It took me a while to come to terms with all the new structures but this is something we can address in subsequent revisions if need be. I also found a couple of minor problems but that too can be dealt with later. I would like to discuss 3 mains points:
- Make the features configurable _and_ modular. Depending on how many features
people come up with it may become tedious to carry all that extra code. For the modularity I suggest you take inspiration from the governor model used in the cpufreq subsystem. Governors, such as the performance governor[1], are all modules that get registered with the cpufreq core[2]. From there when a governor is selected[3] the core increases the governor's module count[4], preventing the module from being unloaded while a governor is used.
That is certainly the intention. But rather than compilable C modules, new features will be binary data , loadable at runtime, using the same format as the builtin example in this patch.
What command do you envision using to do the loading at runtime?
Copy of a file into the configfs directory managed by the coregisht system driver (this is added by the complex config work, though not in this set). The system driver will then load specific component drivers as required.
The conversion between text and binary is essentially compiling a module, hence suggesting that all features be a loadable module. We don't want to introduce a new tool in the mix to do the binary conversion. Moreover, as part of Google's GKI effort, there is a giant push to make CS drivers dynamically loadable.
The intention is that these data structures can be generated as a binary file using some scripted tool interpreting a simple text file description. This is then loaded as described below.
- Please explore the possibility of using configfs rather than sysfs. That way
users can create their own features without the need to add a new module.
This is also underway. A coresight system config driver will have a configfs interface that will allow the loading of new system configurations and features - where a configuration is a set of new or existing features to use as part of a system wide setup. The driver will interpret the incoming binary file, and distribute features to the correct drivers - e.g. a new ETMv4 feature will be loaded into all the ETMv4 instances. New features can be loaded in this to supplement any "builtin" that exist such as the one in the current patch.
Sysfs is retained to control features as in this example - the features do the same job as the other registers / files in sysfs in programming up the device.
Does that mean we'd have two different places for configuring features? I'm fine with feature names showing up in sysfs but all configuration specifics should be in configfs.
Features and configurations are loaded using configfs as described above. Configurations may consist of new complex features - such as strobing, but can also include any of the intrinsic features such as address filtering, timestamps, etc.
Right now it is possible to modify feature attributes and parameters from sysfs - that should all be moved to configfs.
So perf users will select a configuration by name, which will load the indicated features on any ETM it uses.
sysfs users can control any loaded complex features in the same way as they would intrinsic features, maintaining a logical programming structure.
Isolation between perf and sysfs is therefore maintained as it was previously.
So the current infrastructure that may seem more than is necessary for just strobing in ETMv4, is there to prepare for the modularity that is required.
- Perf sessions need to be able to choose which feature they want to use rather
than defaulting to a system wide configuration. If you don't want to deal with that at this stage - something I would certainly understand - simpy don't take features into account from the perf interface.
Ultimately this too is the goal. If "auto-fdo" is the name of a configuration, that selects the "strobing" feature on all etms used in the perf session then the command:- perf record -e cs_etm/@auto-fdo/ <other perf options> will allow the selection of the feature. A later patch set will introduce this concept.
Right, that's the optimal solution.
Here we are replacing the sink ID with a configuration ID (and selecting a default sink instead) - which is why default sink was important for this work.
The current patch is actually a functionally identical patch to one that used to be on perf-opencsd that enabled etm strobing for autoFDO and perf. That patch also added etm strobing without selection by perf
- but just did it in a way that was less obvious. It is being used by
customers as an out of tree patch, so there is a requirement from them to get it up upstreamed so it can be used in production environments.
The patch here introduces a limited amount of the complex config API, sufficient to support etm strobing for those who are actively using it.
The intention being to introduce the rest of complex config in separate stages to make is easier for reviewers to understand what is being done.
Right, I'm all in favour of that. On the flip side I want to make sure not to introduce anything we'll have to maintain in the long run. Right now the global nature of the feature selection in sysfs can't be mixed with perf.
Good point.
OK - then this set really doesn't work in terms of getting strobing upstream to those key customers who need it. I will have to add more to make this viable - i.e. some of the pref selection code.
Regards
Mike
Regards
Mike
Thanks, Mathieu
[1]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq_perfo... [2]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L23... [3]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L76... [4]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L63...
On Wed, Jun 10, 2020 at 10:13:32AM +0100, Mike Leach wrote:
This set introduces the concept of complex features for CoreSight devices, and adds a feature to ETMv4 to enable strobing of the etm capture.
- Complex Features: These are programming settings for devices, especially
ETM which have many programmable register that can be combined to create complex functionality. Features appear as sub-directories in sysfs, having an enable parameter to set the full program setting.
- ETM strobing. This feature is introduced to ETMv4 to allow the trace
capture to be periodically switched on and off - strobed - to a given mark / space ratio to generate statistical trace data for workflows such as auto-fdo.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/Makefile | 7 +- .../hwtracing/coresight/coresight-config.c | 380 ++++++++++++++++++ .../hwtracing/coresight/coresight-config.h | 156 +++++++ .../hwtracing/coresight/coresight-etm4x-cfg.c | 325 +++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 29 ++ .../coresight/coresight-etm4x-sysfs.c | 3 + drivers/hwtracing/coresight/coresight-etm4x.c | 18 +- drivers/hwtracing/coresight/coresight.c | 1 + include/linux/coresight.h | 2 + 9 files changed, 917 insertions(+), 4 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-config.c create mode 100644 drivers/hwtracing/coresight/coresight-config.h create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.c create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.h
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 19497d1d92bf..b6c28f4cfca7 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -3,7 +3,8 @@ # Makefile for CoreSight drivers. # obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o \
coresight-platform.o coresight-sysfs.o
coresight-platform.o coresight-sysfs.o \
coresight-config.o
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \ coresight-tmc-etf.o \ coresight-tmc-etr.o @@ -14,7 +15,9 @@ obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \
coresight-etm4x-sysfs.o
coresight-etm4x-sysfs.o \
coresight-etm4x-cfg.o
obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c new file mode 100644 index 000000000000..10c2d546ca82 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/sysfs.h> +#include "coresight-config.h" +#include "coresight-priv.h"
+static DEFINE_MUTEX(coresight_cfg_mutex);
+/*
- Handlers for creating and manipulating the feature representation
- in sysfs.
- */
+/* base feature attribute info */ +struct feat_attr_info {
const char *name;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
+};
+static ssize_t cs_cfg_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
int val;
spin_lock(feat->dev_spinlock);
val = feat->enabled;
spin_unlock(feat->dev_spinlock);
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+static ssize_t cs_cfg_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
int val;
spin_lock(feat->dev_spinlock);
if (!kstrtoint(buf, 0, &val))
feat->enabled = val;
spin_unlock(feat->dev_spinlock);
return size;
+}
+static ssize_t cs_cfg_desc_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
return scnprintf(buf, PAGE_SIZE, "%s\n", feat->desc->brief);
+}
+struct feat_attr_info feature_std_attr[] = {
{
.name = "enable",
.show = cs_cfg_enable_show,
.store = cs_cfg_enable_store,
},
{
.name = "description",
.show = cs_cfg_desc_show,
},
{ 0 },
+};
+static ssize_t cs_cfg_param_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
u64 val;
spin_lock(param->feat->dev_spinlock);
val = param->current_value;
spin_unlock(param->feat->dev_spinlock);
return scnprintf(buf, PAGE_SIZE, "%lld\n", val);
+}
+static ssize_t cs_cfg_param_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
u64 val;
spin_lock(param->feat->dev_spinlock);
if (!kstrtoull(buf, 0, &val))
param->current_value = val;
spin_unlock(param->feat->dev_spinlock);
return size;
+}
+static struct attribute * +cs_cfg_sysfs_create_feat_attr(struct device *dev, struct cs_cfg_feature *feat,
struct feat_attr_info *info)
+{
struct dev_ext_attribute *eattr;
eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
if (!eattr)
return NULL;
eattr->attr.attr.name = devm_kstrdup(dev, info->name, GFP_KERNEL);
eattr->attr.attr.mode = info->store ? 0644 : 0444;
eattr->attr.show = info->show;
eattr->attr.store = info->store;
eattr->var = feat;
return &eattr->attr.attr;
+}
+static struct attribute * +cs_cfg_sysfs_create_param_attr(struct device *dev, const char *name,
struct cs_cfg_parameter *param)
+{
struct dev_ext_attribute *eattr;
eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
if (!eattr)
return NULL;
eattr->attr.attr.name = devm_kstrdup(dev, name, GFP_KERNEL);
eattr->attr.attr.mode = 0644;
eattr->attr.show = cs_cfg_param_show;
eattr->attr.store = cs_cfg_param_store;
eattr->var = param;
return &eattr->attr.attr;
+}
+int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat) +{
int p, attr_idx = 0;
struct attribute **attrs;
const char *name;
struct feat_attr_info *info;
struct device *dev = feat->csdev->dev.parent;
/* create sysfs group in csdev */
feat->param_group = devm_kzalloc(dev, sizeof(struct attribute_group),
GFP_KERNEL);
if (!feat->param_group)
return -ENOMEM;
feat->param_group->name = devm_kasprintf(dev, GFP_KERNEL, "%s.feat",
feat->desc->name);
if (!feat->param_group->name)
return -ENOMEM;
/* attributes - nr_params + enable + desc */
attrs = devm_kcalloc(dev, feat->nr_params + 2,
sizeof(struct attribute *), GFP_KERNEL);
if (!attrs)
return -ENOMEM;
/* create base attributes in group */
while (feature_std_attr[attr_idx].name) {
info = &feature_std_attr[attr_idx];
attrs[attr_idx] = cs_cfg_sysfs_create_feat_attr(dev, feat,
info);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
}
/* create params in group */
for (p = 0; p < feat->nr_params; p++) {
name = feat->desc->params[p].name;
attrs[attr_idx] =
cs_cfg_sysfs_create_param_attr(dev, name,
&feat->params[p]);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
}
feat->param_group->attrs = attrs;
return sysfs_create_group(&feat->csdev->dev.kobj, feat->param_group);
+}
+/*
- standard routines to set/save/reset enabled features, and iterate
- groups of features on a device.
- */
+void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat) +{
mutex_lock(&coresight_cfg_mutex);
list_add(&feat->node, &feat->csdev->feat_list);
mutex_unlock(&coresight_cfg_mutex);
+}
+inline void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags) +{
u32 *p_val32 = (u32 *)reg->drv_store;
u32 tmp32;
if (!(flags & CS_CFG_REG_VAL_64BIT)) {
if (flags & CS_CFG_REG_VAL_MASK) {
tmp32 = *p_val32;
tmp32 &= ~reg->value.mask32;
tmp32 |= reg->value.val32 & reg->value.mask32;
*p_val32 = tmp32;
} else
*p_val32 = reg->value.val32;
} else
*((u64 *)reg->drv_store) = reg->value.val64;
+}
+inline void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags) +{
if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = *(u64 *)(reg->drv_store);
else
reg->value.val32 = *(u32 *)(reg->drv_store);
+}
+inline void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc)
+{
reg->value.val64 = desc->value.val64;
+}
+inline void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param)
+{
if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = param->current_value;
else
reg->value.val32 = (u32)param->current_value;
+}
+/* default set - will set values without resource checking */ +static int cs_cfg_set_on_enable(struct cs_cfg_feature *feat) +{
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
coresight_cfg_set_reg(&feat->regs[i], flags);
}
}
dev_dbg(&feat->csdev->dev, "Feature %s: %s", feat->desc->name,
feat->enabled ? "Set enabled" : "Skip disabled");
spin_unlock(feat->dev_spinlock);
return 0;
+}
+static void cs_cfg_save_on_disable(struct cs_cfg_feature *feat) +{
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
if (flags & CS_CFG_REG_VAL_SAVE)
coresight_cfg_save_reg(&feat->regs[i], flags);
}
}
spin_unlock(feat->dev_spinlock);
+}
+/* default reset - restore default values, disable feature */ +static void cs_cfg_reset_feat(struct cs_cfg_feature *feat) +{
const struct cs_cfg_feature_desc *feat_desc = feat->desc;
struct cs_cfg_reg_desc *reg_desc;
struct cs_cfg_parameter *param;
struct cs_cfg_reg *reg;
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
feat->enabled = false;
/*
* set the default values for all parameters and regs from the
* relevant static descriptors.
*/
for (i = 0; i < feat->nr_params; i++)
feat->params[i].current_value = feat_desc->params[i].value;
for (i = 0; i < feat->nr_regs; i++) {
reg_desc = &feat_desc->regs[i];
flags = reg_desc->flags;
reg = &feat->regs[i];
/* check if reg set from a parameter otherwise desc default */
if (flags & CS_CFG_REG_VAL_PARAM) {
param = &feat->params[reg_desc->value.val32];
coresight_cfg_init_reg_param(reg, flags, param);
} else
coresight_cfg_init_reg(reg, reg_desc);
}
spin_unlock(feat->dev_spinlock);
+}
+void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat) +{
feat->ops.set_on_enable = cs_cfg_set_on_enable;
feat->ops.save_on_disable = cs_cfg_save_on_disable;
feat->ops.reset = cs_cfg_reset_feat;
+}
+int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
int err = 0;
if (list_empty(&csdev->feat_list))
return 0;
list_for_each_entry(feat, &csdev->feat_list, node) {
dev_dbg(&csdev->dev, "Found feature:%s", feat->desc->name);
if (feat->ops.set_on_enable) {
err = feat->ops.set_on_enable(feat);
dev_dbg(&csdev->dev, "Feature %s: %s",
feat->desc->name, err ? "Set failed" : "OK");
if (!err)
break;
}
}
return err;
+}
+void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
if (list_empty(&csdev->feat_list))
return;
list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.save_on_disable)
feat->ops.save_on_disable(feat);
}
+}
+void coresight_cfg_reset_feats(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
if (list_empty(&csdev->feat_list))
return;
list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.reset)
feat->ops.reset(feat);
}
+} diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h new file mode 100644 index 000000000000..ce69ad5d2bbf --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2020 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CONFIG_H +#define _CORESIGHT_CORESIGHT_CONFIG_H
+/* CoreSight Configuration - complex component and system config handling */
+/* generic register flags */ +#define CS_CFG_REG_STD 0x80000000 /* reg is standard reg */ +#define CS_CFG_REG_RESOURCE 0x40000000 /* reg is a resource */ +#define CS_CFG_REG_VAL_PARAM 0x08000000 /* reg value uses param */ +#define CS_CFG_REG_VAL_MASK 0x04000000 /* reg value bit masked */ +#define CS_CFG_REG_VAL_64BIT 0x02000000 /* reg value 64 bit */ +#define CS_CFG_REG_VAL_SAVE 0x01000000 /* reg value save on disable */ +#define CS_CFG_REG_ID_MASK 0x00000FFF /* reg offset / id in device */ +#define CS_CFG_REG_DEV_MASK 0x00FFF000 /* device specific flags */
+/**
- Parameter descriptor for a device feature.
- @name: Name of parameter.
- @value: Initial or default value.
- */
+struct cs_cfg_parameter_desc {
const char *name;
const u64 value;
+};
+/**
- Representation of register value.
- Supports full 64 bit register value, or 32 bit value with optional mask
- value.
- */
+union cs_cfg_regval {
u64 val64;
struct {
u32 val32;
u32 mask32;
};
+};
+/**
- Register descriptor for a device feature
- @flags: flags defining the handling of the register.
- @value: Initial value & optional mask.
- */
+struct cs_cfg_reg_desc {
const u32 flags;
const union cs_cfg_regval value;
+};
+/**
- Device feature descriptor - combination of registers and parameters to
- program a device to implement a specific complex function.
- @name: feature name.
- @brief: brief description of the feature.
- @params: list of parameters used - 0 terminated.
- @reg: list of registers used.
- */
+struct cs_cfg_feature_desc {
const char *name;
const char *brief;
struct cs_cfg_parameter_desc *params;
struct cs_cfg_reg_desc *regs;
+};
+/**
- config parameter instance - part of a loaded feature.
- @feat: parent feature
- @current_value: current value of parameter - may be set by user via
sysfs, or modified during device operation.
- */
+struct cs_cfg_parameter {
struct cs_cfg_feature *feat;
u64 current_value;
+};
+/**
- config register instance - part of a loaded feature.
maps register values to driver structures
- @value: value to use when setting feature on device / store for
readback of volatile values.
- @drv_store: pointer to internal driver element used to set the value
in hardware.
- */
+struct cs_cfg_reg {
union cs_cfg_regval value;
void *drv_store;
+};
+/**
- cs_cfg_ops - standard operations for features.
- CS config core provides basic defaults for these, which can be overridden by
- device specific versions.
- @set_on_enable: Set the feature on the driver before hw enable.
- @save_on_disable: Save any volatile register values after hw disable.
- @reset: Reset feature to default values.
- */
+struct cs_cfg_ops {
int (*set_on_enable)(struct cs_cfg_feature *feat);
void (*save_on_disable)(struct cs_cfg_feature *feat);
void (*reset)(struct cs_cfg_feature *feat);
+};
+/**
- Feature instance loaded into a CoreSight device driver.
- @desc: pointer to the static descriptor for this feature.
- @csdev: parent CoreSight device instance.
- @node: list entry into feature list for this device.
- @dev_spinlock: device spinlock.
- @enabled: feature is enabled on this device.
- @nr_params: number of parameters.
- @params: current parameter values on this device
- @param_group: sysfs group for feature parameters.
- @ops: standard ops to enable and disable features on devices.
- */
+struct cs_cfg_feature {
const struct cs_cfg_feature_desc *desc;
struct coresight_device *csdev;
struct list_head node;
spinlock_t *dev_spinlock;
bool enabled;
int nr_params;
struct cs_cfg_parameter *params;
struct attribute_group *param_group;
int nr_regs;
struct cs_cfg_reg *regs;
struct cs_cfg_ops ops;
+};
+/* coresight config API functions */ +int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat); +int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev); +void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev); +void coresight_cfg_reset_feats(struct coresight_device *csdev); +void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat); +void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat); +void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc);
+void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param);
+#endif /* _CORESIGHT_CORESIGHT_CONFIG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c new file mode 100644 index 000000000000..240e0235c130 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include "coresight-etm4x.h" +#include "coresight-etm4x-cfg.h" +#include "coresight-priv.h"
+/* built in features for ETMv4 */
+/* strobe feature */
+/* register defines */ +#define STRB_REG_CTR (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_CTR) +#define STRB_REG_CTR_RB (STRB_REG_CTR | CS_CFG_REG_VAL_SAVE) +#define STRB_REG_CTR_PRM (STRB_REG_CTR | CS_CFG_REG_VAL_PARAM) +#define STRB_REG_SEQ (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEQ) +#define STRB_REG_SEL (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEL) +#define STRB_REG_VI (CS_CFG_REG_STD | CS_CFG_REG_VAL_MASK)
+static struct cs_cfg_parameter_desc strobe_params[] = {
{
.name = "window",
.value = 1000,
},
{
.name = "period",
.value = 10000,
},
{ 0 },
+};
+static struct cs_cfg_reg_desc strob_regs[] = {
/* resource selectors */
{
.flags = STRB_REG_SEL | TRCRSCTLRn(2),
.value = {
.val32 = 0x20001,
},
},
{
.flags = STRB_REG_SEQ | TRCRSCTLRn(3),
.value = {
.val32 = 0x20002,
}
},
/* strobe window counter 0 - reload from param 0 */
{
.flags = STRB_REG_CTR_RB | TRCCNTVRn(0),
},
{
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(0),
.value = {
.val32 = 0,
},
},
{
.flags = STRB_REG_CTR | TRCCNTCTLRn(0),
.value = {
.val32 = 0x10001,
},
},
/* strobe period counter 1 - reload from param 1 */
{
.flags = STRB_REG_CTR_RB | TRCCNTVRn(1),
},
{
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(1),
.value = {
.val32 = 1,
},
},
{
.flags = STRB_REG_CTR | TRCCNTCTLRn(1),
.value = {
.val32 = 0x8102,
},
},
/* sequencer */
{
.flags = STRB_REG_SEQ | TRCSEQEVRn(0),
.value = {
.val32 = 0x0081,
},
},
{
.flags = STRB_REG_SEQ | TRCSEQEVRn(1),
.value = {
.val32 = 0x0000,
},
},
/* view-inst */
{
.flags = STRB_REG_VI | TRCVICTLR,
.value = {
.val32 = 0x0003,
.mask32 = 0x0003,
},
},
/* end of regs */
{ 0 },
+};
+static struct cs_cfg_feature_desc strobe = {
.name = "strobing",
.brief = "Generate periodic trace capture windows.",
.params = strobe_params,
.regs = strob_regs,
+};
+static struct cs_cfg_feature_desc *built_in_feats[] = {
&strobe,
0
+};
+/**
- etm4_cfg_map_reg_offset - validate and map the register offset into a
location in the driver config struct.
- Limits the number of registers that can be accessed and programmed in
- features, to those which are used to control the trace capture parameters.
- Omits or limits access to those which the driver must use exclusively.
- Invalid offsets will result in fail code return and feature load failure.
- @drvdata: driver data to map into.
- @reg: register to map.
- @offset: device offset for the register
- */
+static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata,
struct cs_cfg_reg *reg, u32 offset)
+{
int err = 0, idx;
struct etmv4_config *drvcfg = &drvdata->config;
#define MAPREG(cval, elem) \
case cval: \
reg->drv_store = &drvcfg->elem; \
break;
#define MAPREGIDX(cval, elem, off_idx) \
case cval: \
reg->drv_store = &drvcfg->elem[off_idx]; \
break;
if (((offset >= TRCEVENTCTL0R) && (offset <= TRCVIPCSSCTLR)) ||
((offset >= TRCSEQRSTEVR) && (offset <= TRCEXTINSELR)) ||
((offset >= TRCCIDCCTLR0) && (offset <= TRCVMIDCCTLR1))) {
switch (offset) {
/* 32 bit single control and filter registers */
MAPREG(TRCEVENTCTL0R, eventctrl0);
MAPREG(TRCEVENTCTL1R, eventctrl1);
MAPREG(TRCSTALLCTLR, stall_ctrl);
MAPREG(TRCTSCTLR, ts_ctrl);
MAPREG(TRCSYNCPR, syncfreq);
MAPREG(TRCCCCTLR, ccctlr);
MAPREG(TRCBBCTLR, bb_ctrl);
MAPREG(TRCVICTLR, vinst_ctrl);
MAPREG(TRCVIIECTLR, viiectlr);
MAPREG(TRCVISSCTLR, vissctlr);
MAPREG(TRCVIPCSSCTLR, vipcssctlr);
MAPREG(TRCSEQRSTEVR, seq_rst);
MAPREG(TRCSEQSTR, seq_state);
MAPREG(TRCEXTINSELR, ext_inp);
MAPREG(TRCCIDCCTLR0, ctxid_mask0);
MAPREG(TRCCIDCCTLR1, ctxid_mask1);
MAPREG(TRCVMIDCCTLR0, vmid_mask0);
MAPREG(TRCVMIDCCTLR1, vmid_mask1);
default:
err = -EINVAL;
break;
}
} else if ((offset & GENMASK(11, 4)) == TRCSEQEVRn(0)) {
/* sequencer state control registers */
idx = (offset & GENMASK(3, 0)) / 4;
if (idx < ETM_MAX_SEQ_STATES)
reg->drv_store = &drvcfg->seq_ctrl[idx];
else
err = -EINVAL;
} else if ((offset >= TRCSSCCRn(0)) && (offset <= TRCSSPCICRn(7))) {
/* 32 bit, 8 off indexed register sets */
idx = (offset & GENMASK(4, 0)) / 4;
switch (offset & GENMASK(11, 5)) {
MAPREGIDX(TRCSSCCRn(0), ss_ctrl, idx);
MAPREGIDX(TRCSSCSRn(0), ss_status, idx);
MAPREGIDX(TRCSSPCICRn(0), ss_pe_cmp, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCCIDCVRn(0)) && (offset <= TRCVMIDCVRn(7))) {
/* 64 bit, 8 off indexed register sets */
idx = (offset & GENMASK(5, 0)) / 8;
switch (offset & GENMASK(11, 6)) {
MAPREGIDX(TRCCIDCVRn(0), ctxid_pid, idx);
MAPREGIDX(TRCVMIDCVRn(0), vmid_val, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCRSCTLRn(2)) &&
(offset <= TRCRSCTLRn((ETM_MAX_RES_SEL - 1)))) {
/* 32 bit resource selection regs, 32 off, skip fixed 0,1 */
idx = (offset & GENMASK(6, 0)) / 4;
if (idx < ETM_MAX_RES_SEL)
reg->drv_store = &drvcfg->res_ctrl[idx];
else
err = -EINVAL;
} else if ((offset >= TRCACVRn(0)) &&
(offset <= TRCACATRn((ETM_MAX_SINGLE_ADDR_CMP - 1)))) {
/* 64 bit addr cmp regs, 16 off */
idx = (offset & GENMASK(6, 0)) / 8;
switch (offset & GENMASK(11, 7)) {
MAPREGIDX(TRCACVRn(0), addr_val, idx);
MAPREGIDX(TRCACATRn(0), addr_acc, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCCNTRLDVRn(0)) &&
(offset <= TRCCNTVRn((ETMv4_MAX_CNTR - 1)))) {
/* 32 bit counter regs, 4 off (ETMv4_MAX_CNTR - 1) */
idx = (offset & GENMASK(3, 0)) / 4;
switch (offset & GENMASK(11, 4)) {
MAPREGIDX(TRCCNTRLDVRn(0), cntrldvr, idx);
MAPREGIDX(TRCCNTCTLRn(0), cntr_ctrl, idx);
MAPREGIDX(TRCCNTVRn(0), cntr_val, idx);
default:
err = -EINVAL;
break;
}
} else {
err = -EINVAL;
}
return err;
+}
+/**
- etm4_cfg_load_feature - load a feature into a device instance.
- @csdev: An ETMv4 CoreSight device.
- @feat_desc: The static feature descriptor to be loaded.
- The function will load a feature instance into the device, based on the
- supplied descriptor. The descriptor will be checked to ensure a valid
- feature can be loaded for this ETMv4 device.
- Parameter and register definitions will be converted into internal
- structures that are used to set the values in the driver when the
- feature is enabled for the device.
- */
+int etm4_cfg_load_feature(struct coresight_device *csdev,
const struct cs_cfg_feature_desc *feat_desc)
+{
struct device *dev = csdev->dev.parent;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
struct cs_cfg_feature *feat;
u32 offset;
int i = 0, err = 0;
feat = devm_kzalloc(dev, sizeof(struct cs_cfg_feature), GFP_KERNEL);
if (!feat)
return -ENOMEM;
feat->desc = feat_desc;
feat->dev_spinlock = &drvdata->spinlock;
feat->csdev = csdev;
/* count the params regs and allocate space. */
while (feat_desc->params[i++].name)
feat->nr_params++;
i = 0;
while (feat_desc->regs[i++].flags)
feat->nr_regs++;
feat->params = devm_kcalloc(dev, feat->nr_params,
sizeof(struct cs_cfg_parameter),
GFP_KERNEL);
if (!feat->params)
return -ENOMEM;
feat->regs = devm_kcalloc(dev, feat->nr_regs,
sizeof(struct cs_cfg_reg), GFP_KERNEL);
if (!feat->regs)
return -ENOMEM;
/* load the parameters default values */
for (i = 0; i < feat->nr_params; i++)
feat->params[i].feat = feat;
/* process the register descriptions */
for (i = 0; i < feat->nr_regs; i++) {
offset = feat_desc->regs[i].flags & CS_CFG_REG_ID_MASK;
err = etm4_cfg_map_reg_offset(drvdata, &feat->regs[i], offset);
if (err)
return err;
}
/* set sysfs representation of the feature */
err = coresight_cfg_sysfs_add_grp(feat);
/* add the feature to the device list and init default values */
if (!err) {
coresight_cfg_list_add_feat(feat);
coresight_cfg_set_def_ops(feat);
feat->ops.reset(feat);
}
return err;
+}
+/* load up the list of builtin features */ +int etm4_cfg_load_builtins(struct coresight_device *csdev) +{
int idx = 0, err = 0;
while (built_in_feats[idx] && !err)
err = etm4_cfg_load_feature(csdev, built_in_feats[idx++]);
return err;
+} diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h new file mode 100644 index 000000000000..703657cac2fc --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
- */
+#ifndef _CORESIGHT_CORESIGHT_ETM4X_CFG_H +#define _CORESIGHT_CORESIGHT_ETM4X_CFG_H
+#include "coresight-config.h"
+/* ETMv4 specific config defines */
+/* resource IDs */
+#define ETM4_CFG_RES_CTR 0x00001000 +#define ETM4_CFG_RES_CMP 0x00002000 +#define ETM4_CFG_RES_CMP_PAIR0 0x00003000 +#define ETM4_CFG_RES_CMP_PAIR1 0x00004000 +#define ETM4_CFG_RES_SEL 0x00005000 +#define ETM4_CFG_RES_SEL_PAIR0 0x00006000 +#define ETM4_CFG_RES_SEL_PAIR1 0x00007000 +#define ETM4_CFG_RES_SEQ 0x00008000 +#define ETM4_CFG_RES_TS 0x00009000 +#define ETM4_CFG_RES_MASK 0x0000F000
+/* ETM4 configuration handlers */ +int etm4_cfg_load_builtins(struct coresight_device *csdev);
+#endif /* _CORESIGHT_CORESIGHT_ETM4X_CFG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index b673e738bc9a..ec182544a064 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -9,6 +9,7 @@ #include <linux/sysfs.h> #include "coresight-etm4x.h" #include "coresight-priv.h" +#include "coresight-config.h"
static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) { @@ -269,6 +270,8 @@ static ssize_t reset_store(struct device *dev,
spin_unlock(&drvdata->spinlock);
coresight_cfg_reset_feats(to_coresight_device(dev));
return size;
} static DEVICE_ATTR_WO(reset); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 60a5133d801c..e69dbcf5cd4d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -34,6 +34,7 @@
#include "coresight-etm4x.h" #include "coresight-etm-perf.h" +#include "coresight-etm4x-cfg.h"
static int boot_enable; module_param(boot_enable, int, 0444); @@ -320,10 +321,11 @@ static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata) return ret; }
-static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, +static int etm4_parse_event_config(struct coresight_device *csdev, struct perf_event *event) { int ret = 0;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_config *config = &drvdata->config; struct perf_event_attr *attr = &event->attr;
@@ -383,6 +385,9 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, /* bit[12], Return stack enable bit */ config->cfg |= BIT(12);
/* set any enabled features */
ret = coresight_cfg_set_feats_on_ena(csdev);
out: return ret; } @@ -399,7 +404,7 @@ static int etm4_enable_perf(struct coresight_device *csdev, }
/* Configure the tracer based on the session's specifics */
ret = etm4_parse_event_config(drvdata, event);
ret = etm4_parse_event_config(csdev, event); if (ret) goto out; /* And enable it */
@@ -415,6 +420,8 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) struct etm4_enable_arg arg = { }; int ret;
coresight_cfg_set_feats_on_ena(csdev);
spin_lock(&drvdata->spinlock); /*
@@ -532,6 +539,7 @@ static int etm4_disable_perf(struct coresight_device *csdev, return -EINVAL;
etm4_disable_hw(drvdata);
coresight_cfg_save_feats_on_dis(csdev); /* * Check if the start/stop logic was active when the unit was stopped.
@@ -566,6 +574,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) smp_call_function_single(drvdata->cpu, etm4_disable_hw, drvdata, 1);
spin_unlock(&drvdata->spinlock);
coresight_cfg_save_feats_on_dis(csdev); cpus_read_unlock(); dev_dbg(&csdev->dev, "ETM tracing disabled\n");
@@ -1510,6 +1519,11 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) goto err_arch_supported; }
/* load builtin features */
ret = etm4_cfg_load_builtins(drvdata->csdev);
if (ret)
goto err_arch_supported;
pm_runtime_put(&adev->dev); dev_info(&drvdata->csdev->dev, "CPU%d: ETM v%d.%d initialized\n", drvdata->cpu, drvdata->arch >> 4, drvdata->arch & 0xf);
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index f3efbb3b2b4d..ac16d96124f6 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1335,6 +1335,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) ret = coresight_fixup_orphan_conns(csdev); if (!ret) cti_add_assoc_to_csdev(csdev);
INIT_LIST_HEAD(&csdev->feat_list); mutex_unlock(&coresight_mutex); if (ret) {
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index e3e9f0e3a878..0e8d529282dd 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -204,6 +204,8 @@ struct coresight_device { /* sysfs links between components */ int nr_links; bool has_conns_grp;
/* feature list */
struct list_head feat_list;
};
/*
2.17.1
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
Hi Mathieu,
On Tue, 7 Jul 2020 at 18:47, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Mon, 6 Jul 2020 at 03:08, Mike Leach mike.leach@linaro.org wrote:
Hi Mathieu,
On Thu, 2 Jul 2020 at 18:53, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Hi
On Wed, 1 Jul 2020 at 13:26, Mike Leach mike.leach@linaro.org wrote:
Hi Mathieu,
On Wed, 24 Jun 2020 at 18:59, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Good day,
I like what you did in this set. It took me a while to come to terms with all the new structures but this is something we can address in subsequent revisions if need be. I also found a couple of minor problems but that too can be dealt with later. I would like to discuss 3 mains points:
- Make the features configurable _and_ modular. Depending on how many features
people come up with it may become tedious to carry all that extra code. For the modularity I suggest you take inspiration from the governor model used in the cpufreq subsystem. Governors, such as the performance governor[1], are all modules that get registered with the cpufreq core[2]. From there when a governor is selected[3] the core increases the governor's module count[4], preventing the module from being unloaded while a governor is used.
That is certainly the intention. But rather than compilable C modules, new features will be binary data , loadable at runtime, using the same format as the builtin example in this patch.
What command do you envision using to do the loading at runtime?
Copy of a file into the configfs directory managed by the coregisht system driver (this is added by the complex config work, though not in this set). The system driver will then load specific component drivers as required.
The conversion between text and binary is essentially compiling a module, hence suggesting that all features be a loadable module. We don't want to introduce a new tool in the mix to do the binary conversion. Moreover, as part of Google's GKI effort, there is a giant push to make CS drivers dynamically loadable.
I agree that it is a compilation type operation, but there are other considerations that having a separate tool provides - such as validation of the feature for a system. The binary can then be loaded as a binary config item into configfs. This does not change if the CS dirvers are dynamically loadable also.
My concern with loadable modules is that these are overly complex and would require far more access to the kernel than a simple configfs userspace load. I look at tools like ftrace, eBPF, perf etc, these allow an element of hooking into the kernel, or simple programming in the case of eBPF, without loading new modules.
Coresight complex configuration sets can actually be quite simple in terms of a set of registers to program. Haven't nailed down the syntax yet, but something like strobing could be 20 lines or so. This is then validated and compiled into a file to load in configfs. The appropriate parts are passed to the ETMv4 driver which then processes the data (effectively a second interpretation / compilation operation) into a set of register loads to use when the feature is activated. This process also ensures that only permitted registers can be used - so is safe in that there are no direct memory accesses, kernel API calls, etc which a loadable module might need.
The intention is that these data structures can be generated as a binary file using some scripted tool interpreting a simple text file description. This is then loaded as described below.
- Please explore the possibility of using configfs rather than sysfs. That way
users can create their own features without the need to add a new module.
This is also underway. A coresight system config driver will have a configfs interface that will allow the loading of new system configurations and features - where a configuration is a set of new or existing features to use as part of a system wide setup. The driver will interpret the incoming binary file, and distribute features to the correct drivers - e.g. a new ETMv4 feature will be loaded into all the ETMv4 instances. New features can be loaded in this to supplement any "builtin" that exist such as the one in the current patch.
Sysfs is retained to control features as in this example - the features do the same job as the other registers / files in sysfs in programming up the device.
Does that mean we'd have two different places for configuring features? I'm fine with feature names showing up in sysfs but all configuration specifics should be in configfs.
Features and configurations are loaded using configfs as described above. Configurations may consist of new complex features - such as strobing, but can also include any of the intrinsic features such as address filtering, timestamps, etc.
Right now it is possible to modify feature attributes and parameters from sysfs - that should all be moved to configfs.
That was initially because the feature attributes and parameters were programming the same set of hardware registers as the rest of the sysfs attributes - but there is also a limitation on how configfs works.
There appears to be no way for the "built in" strobing feature to exist in configfs. According to the configfs documentation, a config item cannot exist until it is created from user space. Drivers supporting confgfs react to entries being created and destroyed from userspace - but don't create and destroy entries themselves. This breaks the paradigm that people are currently using for auto-fdo with the strobing feature being available in the driver, something that ARM is getting pressure to get upstream. It is not feasible to upstream the existing strobing patch that is in use at customers (the one Rob has maintained as an out of tree patch) as it suffers from the same flaw as this current patch - it overrides perfs settings of certain ETM registers to acheive strobing without pref being aware (although it does it in a less obvious way in the etm4_enable_hw() function).
I'll look again into configfs, so see if drivers themselves can set up config items, but that is not my current understanding.
Regards
Mike
So perf users will select a configuration by name, which will load the indicated features on any ETM it uses.
sysfs users can control any loaded complex features in the same way as they would intrinsic features, maintaining a logical programming structure.
Isolation between perf and sysfs is therefore maintained as it was previously.
So the current infrastructure that may seem more than is necessary for just strobing in ETMv4, is there to prepare for the modularity that is required.
- Perf sessions need to be able to choose which feature they want to use rather
than defaulting to a system wide configuration. If you don't want to deal with that at this stage - something I would certainly understand - simpy don't take features into account from the perf interface.
Ultimately this too is the goal. If "auto-fdo" is the name of a configuration, that selects the "strobing" feature on all etms used in the perf session then the command:- perf record -e cs_etm/@auto-fdo/ <other perf options> will allow the selection of the feature. A later patch set will introduce this concept.
Right, that's the optimal solution.
Here we are replacing the sink ID with a configuration ID (and selecting a default sink instead) - which is why default sink was important for this work.
The current patch is actually a functionally identical patch to one that used to be on perf-opencsd that enabled etm strobing for autoFDO and perf. That patch also added etm strobing without selection by perf
- but just did it in a way that was less obvious. It is being used by
customers as an out of tree patch, so there is a requirement from them to get it up upstreamed so it can be used in production environments.
The patch here introduces a limited amount of the complex config API, sufficient to support etm strobing for those who are actively using it.
The intention being to introduce the rest of complex config in separate stages to make is easier for reviewers to understand what is being done.
Right, I'm all in favour of that. On the flip side I want to make sure not to introduce anything we'll have to maintain in the long run. Right now the global nature of the feature selection in sysfs can't be mixed with perf.
Good point.
OK - then this set really doesn't work in terms of getting strobing upstream to those key customers who need it. I will have to add more to make this viable - i.e. some of the pref selection code.
Regards
Mike
Regards
Mike
Thanks, Mathieu
[1]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq_perfo... [2]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L23... [3]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L76... [4]. https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/cpufreq.c#L63...
On Wed, Jun 10, 2020 at 10:13:32AM +0100, Mike Leach wrote:
This set introduces the concept of complex features for CoreSight devices, and adds a feature to ETMv4 to enable strobing of the etm capture.
- Complex Features: These are programming settings for devices, especially
ETM which have many programmable register that can be combined to create complex functionality. Features appear as sub-directories in sysfs, having an enable parameter to set the full program setting.
- ETM strobing. This feature is introduced to ETMv4 to allow the trace
capture to be periodically switched on and off - strobed - to a given mark / space ratio to generate statistical trace data for workflows such as auto-fdo.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/Makefile | 7 +- .../hwtracing/coresight/coresight-config.c | 380 ++++++++++++++++++ .../hwtracing/coresight/coresight-config.h | 156 +++++++ .../hwtracing/coresight/coresight-etm4x-cfg.c | 325 +++++++++++++++ .../hwtracing/coresight/coresight-etm4x-cfg.h | 29 ++ .../coresight/coresight-etm4x-sysfs.c | 3 + drivers/hwtracing/coresight/coresight-etm4x.c | 18 +- drivers/hwtracing/coresight/coresight.c | 1 + include/linux/coresight.h | 2 + 9 files changed, 917 insertions(+), 4 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-config.c create mode 100644 drivers/hwtracing/coresight/coresight-config.h create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.c create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-cfg.h
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 19497d1d92bf..b6c28f4cfca7 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -3,7 +3,8 @@ # Makefile for CoreSight drivers. # obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o \
coresight-platform.o coresight-sysfs.o
coresight-platform.o coresight-sysfs.o \
coresight-config.o
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \ coresight-tmc-etf.o \ coresight-tmc-etr.o @@ -14,7 +15,9 @@ obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \
coresight-etm4x-sysfs.o
coresight-etm4x-sysfs.o \
coresight-etm4x-cfg.o
obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c new file mode 100644 index 000000000000..10c2d546ca82 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/sysfs.h> +#include "coresight-config.h" +#include "coresight-priv.h"
+static DEFINE_MUTEX(coresight_cfg_mutex);
+/*
- Handlers for creating and manipulating the feature representation
- in sysfs.
- */
+/* base feature attribute info */ +struct feat_attr_info {
const char *name;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
+};
+static ssize_t cs_cfg_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
int val;
spin_lock(feat->dev_spinlock);
val = feat->enabled;
spin_unlock(feat->dev_spinlock);
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+static ssize_t cs_cfg_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
int val;
spin_lock(feat->dev_spinlock);
if (!kstrtoint(buf, 0, &val))
feat->enabled = val;
spin_unlock(feat->dev_spinlock);
return size;
+}
+static ssize_t cs_cfg_desc_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_feature *feat = (struct cs_cfg_feature *)eattr->var;
return scnprintf(buf, PAGE_SIZE, "%s\n", feat->desc->brief);
+}
+struct feat_attr_info feature_std_attr[] = {
{
.name = "enable",
.show = cs_cfg_enable_show,
.store = cs_cfg_enable_store,
},
{
.name = "description",
.show = cs_cfg_desc_show,
},
{ 0 },
+};
+static ssize_t cs_cfg_param_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
u64 val;
spin_lock(param->feat->dev_spinlock);
val = param->current_value;
spin_unlock(param->feat->dev_spinlock);
return scnprintf(buf, PAGE_SIZE, "%lld\n", val);
+}
+static ssize_t cs_cfg_param_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct dev_ext_attribute *eattr =
container_of(attr, struct dev_ext_attribute, attr);
struct cs_cfg_parameter *param = (struct cs_cfg_parameter *)eattr->var;
u64 val;
spin_lock(param->feat->dev_spinlock);
if (!kstrtoull(buf, 0, &val))
param->current_value = val;
spin_unlock(param->feat->dev_spinlock);
return size;
+}
+static struct attribute * +cs_cfg_sysfs_create_feat_attr(struct device *dev, struct cs_cfg_feature *feat,
struct feat_attr_info *info)
+{
struct dev_ext_attribute *eattr;
eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
if (!eattr)
return NULL;
eattr->attr.attr.name = devm_kstrdup(dev, info->name, GFP_KERNEL);
eattr->attr.attr.mode = info->store ? 0644 : 0444;
eattr->attr.show = info->show;
eattr->attr.store = info->store;
eattr->var = feat;
return &eattr->attr.attr;
+}
+static struct attribute * +cs_cfg_sysfs_create_param_attr(struct device *dev, const char *name,
struct cs_cfg_parameter *param)
+{
struct dev_ext_attribute *eattr;
eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
GFP_KERNEL);
if (!eattr)
return NULL;
eattr->attr.attr.name = devm_kstrdup(dev, name, GFP_KERNEL);
eattr->attr.attr.mode = 0644;
eattr->attr.show = cs_cfg_param_show;
eattr->attr.store = cs_cfg_param_store;
eattr->var = param;
return &eattr->attr.attr;
+}
+int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat) +{
int p, attr_idx = 0;
struct attribute **attrs;
const char *name;
struct feat_attr_info *info;
struct device *dev = feat->csdev->dev.parent;
/* create sysfs group in csdev */
feat->param_group = devm_kzalloc(dev, sizeof(struct attribute_group),
GFP_KERNEL);
if (!feat->param_group)
return -ENOMEM;
feat->param_group->name = devm_kasprintf(dev, GFP_KERNEL, "%s.feat",
feat->desc->name);
if (!feat->param_group->name)
return -ENOMEM;
/* attributes - nr_params + enable + desc */
attrs = devm_kcalloc(dev, feat->nr_params + 2,
sizeof(struct attribute *), GFP_KERNEL);
if (!attrs)
return -ENOMEM;
/* create base attributes in group */
while (feature_std_attr[attr_idx].name) {
info = &feature_std_attr[attr_idx];
attrs[attr_idx] = cs_cfg_sysfs_create_feat_attr(dev, feat,
info);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
}
/* create params in group */
for (p = 0; p < feat->nr_params; p++) {
name = feat->desc->params[p].name;
attrs[attr_idx] =
cs_cfg_sysfs_create_param_attr(dev, name,
&feat->params[p]);
if (!attrs[attr_idx])
return -ENOMEM;
attr_idx++;
}
feat->param_group->attrs = attrs;
return sysfs_create_group(&feat->csdev->dev.kobj, feat->param_group);
+}
+/*
- standard routines to set/save/reset enabled features, and iterate
- groups of features on a device.
- */
+void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat) +{
mutex_lock(&coresight_cfg_mutex);
list_add(&feat->node, &feat->csdev->feat_list);
mutex_unlock(&coresight_cfg_mutex);
+}
+inline void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags) +{
u32 *p_val32 = (u32 *)reg->drv_store;
u32 tmp32;
if (!(flags & CS_CFG_REG_VAL_64BIT)) {
if (flags & CS_CFG_REG_VAL_MASK) {
tmp32 = *p_val32;
tmp32 &= ~reg->value.mask32;
tmp32 |= reg->value.val32 & reg->value.mask32;
*p_val32 = tmp32;
} else
*p_val32 = reg->value.val32;
} else
*((u64 *)reg->drv_store) = reg->value.val64;
+}
+inline void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags) +{
if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = *(u64 *)(reg->drv_store);
else
reg->value.val32 = *(u32 *)(reg->drv_store);
+}
+inline void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc)
+{
reg->value.val64 = desc->value.val64;
+}
+inline void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param)
+{
if (flags & CS_CFG_REG_VAL_64BIT)
reg->value.val64 = param->current_value;
else
reg->value.val32 = (u32)param->current_value;
+}
+/* default set - will set values without resource checking */ +static int cs_cfg_set_on_enable(struct cs_cfg_feature *feat) +{
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
coresight_cfg_set_reg(&feat->regs[i], flags);
}
}
dev_dbg(&feat->csdev->dev, "Feature %s: %s", feat->desc->name,
feat->enabled ? "Set enabled" : "Skip disabled");
spin_unlock(feat->dev_spinlock);
return 0;
+}
+static void cs_cfg_save_on_disable(struct cs_cfg_feature *feat) +{
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
if (feat->enabled) {
for (i = 0; i < feat->nr_regs; i++) {
flags = feat->desc->regs[i].flags;
if (flags & CS_CFG_REG_VAL_SAVE)
coresight_cfg_save_reg(&feat->regs[i], flags);
}
}
spin_unlock(feat->dev_spinlock);
+}
+/* default reset - restore default values, disable feature */ +static void cs_cfg_reset_feat(struct cs_cfg_feature *feat) +{
const struct cs_cfg_feature_desc *feat_desc = feat->desc;
struct cs_cfg_reg_desc *reg_desc;
struct cs_cfg_parameter *param;
struct cs_cfg_reg *reg;
int i;
u32 flags;
spin_lock(feat->dev_spinlock);
feat->enabled = false;
/*
* set the default values for all parameters and regs from the
* relevant static descriptors.
*/
for (i = 0; i < feat->nr_params; i++)
feat->params[i].current_value = feat_desc->params[i].value;
for (i = 0; i < feat->nr_regs; i++) {
reg_desc = &feat_desc->regs[i];
flags = reg_desc->flags;
reg = &feat->regs[i];
/* check if reg set from a parameter otherwise desc default */
if (flags & CS_CFG_REG_VAL_PARAM) {
param = &feat->params[reg_desc->value.val32];
coresight_cfg_init_reg_param(reg, flags, param);
} else
coresight_cfg_init_reg(reg, reg_desc);
}
spin_unlock(feat->dev_spinlock);
+}
+void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat) +{
feat->ops.set_on_enable = cs_cfg_set_on_enable;
feat->ops.save_on_disable = cs_cfg_save_on_disable;
feat->ops.reset = cs_cfg_reset_feat;
+}
+int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
int err = 0;
if (list_empty(&csdev->feat_list))
return 0;
list_for_each_entry(feat, &csdev->feat_list, node) {
dev_dbg(&csdev->dev, "Found feature:%s", feat->desc->name);
if (feat->ops.set_on_enable) {
err = feat->ops.set_on_enable(feat);
dev_dbg(&csdev->dev, "Feature %s: %s",
feat->desc->name, err ? "Set failed" : "OK");
if (!err)
break;
}
}
return err;
+}
+void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
if (list_empty(&csdev->feat_list))
return;
list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.save_on_disable)
feat->ops.save_on_disable(feat);
}
+}
+void coresight_cfg_reset_feats(struct coresight_device *csdev) +{
struct cs_cfg_feature *feat;
if (list_empty(&csdev->feat_list))
return;
list_for_each_entry(feat, &csdev->feat_list, node) {
if (feat->ops.reset)
feat->ops.reset(feat);
}
+} diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h new file mode 100644 index 000000000000..ce69ad5d2bbf --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2020 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CONFIG_H +#define _CORESIGHT_CORESIGHT_CONFIG_H
+/* CoreSight Configuration - complex component and system config handling */
+/* generic register flags */ +#define CS_CFG_REG_STD 0x80000000 /* reg is standard reg */ +#define CS_CFG_REG_RESOURCE 0x40000000 /* reg is a resource */ +#define CS_CFG_REG_VAL_PARAM 0x08000000 /* reg value uses param */ +#define CS_CFG_REG_VAL_MASK 0x04000000 /* reg value bit masked */ +#define CS_CFG_REG_VAL_64BIT 0x02000000 /* reg value 64 bit */ +#define CS_CFG_REG_VAL_SAVE 0x01000000 /* reg value save on disable */ +#define CS_CFG_REG_ID_MASK 0x00000FFF /* reg offset / id in device */ +#define CS_CFG_REG_DEV_MASK 0x00FFF000 /* device specific flags */
+/**
- Parameter descriptor for a device feature.
- @name: Name of parameter.
- @value: Initial or default value.
- */
+struct cs_cfg_parameter_desc {
const char *name;
const u64 value;
+};
+/**
- Representation of register value.
- Supports full 64 bit register value, or 32 bit value with optional mask
- value.
- */
+union cs_cfg_regval {
u64 val64;
struct {
u32 val32;
u32 mask32;
};
+};
+/**
- Register descriptor for a device feature
- @flags: flags defining the handling of the register.
- @value: Initial value & optional mask.
- */
+struct cs_cfg_reg_desc {
const u32 flags;
const union cs_cfg_regval value;
+};
+/**
- Device feature descriptor - combination of registers and parameters to
- program a device to implement a specific complex function.
- @name: feature name.
- @brief: brief description of the feature.
- @params: list of parameters used - 0 terminated.
- @reg: list of registers used.
- */
+struct cs_cfg_feature_desc {
const char *name;
const char *brief;
struct cs_cfg_parameter_desc *params;
struct cs_cfg_reg_desc *regs;
+};
+/**
- config parameter instance - part of a loaded feature.
- @feat: parent feature
- @current_value: current value of parameter - may be set by user via
sysfs, or modified during device operation.
- */
+struct cs_cfg_parameter {
struct cs_cfg_feature *feat;
u64 current_value;
+};
+/**
- config register instance - part of a loaded feature.
maps register values to driver structures
- @value: value to use when setting feature on device / store for
readback of volatile values.
- @drv_store: pointer to internal driver element used to set the value
in hardware.
- */
+struct cs_cfg_reg {
union cs_cfg_regval value;
void *drv_store;
+};
+/**
- cs_cfg_ops - standard operations for features.
- CS config core provides basic defaults for these, which can be overridden by
- device specific versions.
- @set_on_enable: Set the feature on the driver before hw enable.
- @save_on_disable: Save any volatile register values after hw disable.
- @reset: Reset feature to default values.
- */
+struct cs_cfg_ops {
int (*set_on_enable)(struct cs_cfg_feature *feat);
void (*save_on_disable)(struct cs_cfg_feature *feat);
void (*reset)(struct cs_cfg_feature *feat);
+};
+/**
- Feature instance loaded into a CoreSight device driver.
- @desc: pointer to the static descriptor for this feature.
- @csdev: parent CoreSight device instance.
- @node: list entry into feature list for this device.
- @dev_spinlock: device spinlock.
- @enabled: feature is enabled on this device.
- @nr_params: number of parameters.
- @params: current parameter values on this device
- @param_group: sysfs group for feature parameters.
- @ops: standard ops to enable and disable features on devices.
- */
+struct cs_cfg_feature {
const struct cs_cfg_feature_desc *desc;
struct coresight_device *csdev;
struct list_head node;
spinlock_t *dev_spinlock;
bool enabled;
int nr_params;
struct cs_cfg_parameter *params;
struct attribute_group *param_group;
int nr_regs;
struct cs_cfg_reg *regs;
struct cs_cfg_ops ops;
+};
+/* coresight config API functions */ +int coresight_cfg_sysfs_add_grp(struct cs_cfg_feature *feat); +int coresight_cfg_set_feats_on_ena(struct coresight_device *csdev); +void coresight_cfg_save_feats_on_dis(struct coresight_device *csdev); +void coresight_cfg_reset_feats(struct coresight_device *csdev); +void coresight_cfg_set_def_ops(struct cs_cfg_feature *feat); +void coresight_cfg_list_add_feat(struct cs_cfg_feature *feat); +void coresight_cfg_set_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_save_reg(struct cs_cfg_reg *reg, u32 flags); +void coresight_cfg_init_reg(struct cs_cfg_reg *reg,
const struct cs_cfg_reg_desc *desc);
+void coresight_cfg_init_reg_param(struct cs_cfg_reg *reg, u32 flags,
struct cs_cfg_parameter *param);
+#endif /* _CORESIGHT_CORESIGHT_CONFIG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c new file mode 100644 index 000000000000..240e0235c130 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright(C) 2020 Linaro Limited. All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include "coresight-etm4x.h" +#include "coresight-etm4x-cfg.h" +#include "coresight-priv.h"
+/* built in features for ETMv4 */
+/* strobe feature */
+/* register defines */ +#define STRB_REG_CTR (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_CTR) +#define STRB_REG_CTR_RB (STRB_REG_CTR | CS_CFG_REG_VAL_SAVE) +#define STRB_REG_CTR_PRM (STRB_REG_CTR | CS_CFG_REG_VAL_PARAM) +#define STRB_REG_SEQ (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEQ) +#define STRB_REG_SEL (CS_CFG_REG_RESOURCE | ETM4_CFG_RES_SEL) +#define STRB_REG_VI (CS_CFG_REG_STD | CS_CFG_REG_VAL_MASK)
+static struct cs_cfg_parameter_desc strobe_params[] = {
{
.name = "window",
.value = 1000,
},
{
.name = "period",
.value = 10000,
},
{ 0 },
+};
+static struct cs_cfg_reg_desc strob_regs[] = {
/* resource selectors */
{
.flags = STRB_REG_SEL | TRCRSCTLRn(2),
.value = {
.val32 = 0x20001,
},
},
{
.flags = STRB_REG_SEQ | TRCRSCTLRn(3),
.value = {
.val32 = 0x20002,
}
},
/* strobe window counter 0 - reload from param 0 */
{
.flags = STRB_REG_CTR_RB | TRCCNTVRn(0),
},
{
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(0),
.value = {
.val32 = 0,
},
},
{
.flags = STRB_REG_CTR | TRCCNTCTLRn(0),
.value = {
.val32 = 0x10001,
},
},
/* strobe period counter 1 - reload from param 1 */
{
.flags = STRB_REG_CTR_RB | TRCCNTVRn(1),
},
{
.flags = STRB_REG_CTR_PRM | TRCCNTRLDVRn(1),
.value = {
.val32 = 1,
},
},
{
.flags = STRB_REG_CTR | TRCCNTCTLRn(1),
.value = {
.val32 = 0x8102,
},
},
/* sequencer */
{
.flags = STRB_REG_SEQ | TRCSEQEVRn(0),
.value = {
.val32 = 0x0081,
},
},
{
.flags = STRB_REG_SEQ | TRCSEQEVRn(1),
.value = {
.val32 = 0x0000,
},
},
/* view-inst */
{
.flags = STRB_REG_VI | TRCVICTLR,
.value = {
.val32 = 0x0003,
.mask32 = 0x0003,
},
},
/* end of regs */
{ 0 },
+};
+static struct cs_cfg_feature_desc strobe = {
.name = "strobing",
.brief = "Generate periodic trace capture windows.",
.params = strobe_params,
.regs = strob_regs,
+};
+static struct cs_cfg_feature_desc *built_in_feats[] = {
&strobe,
0
+};
+/**
- etm4_cfg_map_reg_offset - validate and map the register offset into a
location in the driver config struct.
- Limits the number of registers that can be accessed and programmed in
- features, to those which are used to control the trace capture parameters.
- Omits or limits access to those which the driver must use exclusively.
- Invalid offsets will result in fail code return and feature load failure.
- @drvdata: driver data to map into.
- @reg: register to map.
- @offset: device offset for the register
- */
+static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata,
struct cs_cfg_reg *reg, u32 offset)
+{
int err = 0, idx;
struct etmv4_config *drvcfg = &drvdata->config;
#define MAPREG(cval, elem) \
case cval: \
reg->drv_store = &drvcfg->elem; \
break;
#define MAPREGIDX(cval, elem, off_idx) \
case cval: \
reg->drv_store = &drvcfg->elem[off_idx]; \
break;
if (((offset >= TRCEVENTCTL0R) && (offset <= TRCVIPCSSCTLR)) ||
((offset >= TRCSEQRSTEVR) && (offset <= TRCEXTINSELR)) ||
((offset >= TRCCIDCCTLR0) && (offset <= TRCVMIDCCTLR1))) {
switch (offset) {
/* 32 bit single control and filter registers */
MAPREG(TRCEVENTCTL0R, eventctrl0);
MAPREG(TRCEVENTCTL1R, eventctrl1);
MAPREG(TRCSTALLCTLR, stall_ctrl);
MAPREG(TRCTSCTLR, ts_ctrl);
MAPREG(TRCSYNCPR, syncfreq);
MAPREG(TRCCCCTLR, ccctlr);
MAPREG(TRCBBCTLR, bb_ctrl);
MAPREG(TRCVICTLR, vinst_ctrl);
MAPREG(TRCVIIECTLR, viiectlr);
MAPREG(TRCVISSCTLR, vissctlr);
MAPREG(TRCVIPCSSCTLR, vipcssctlr);
MAPREG(TRCSEQRSTEVR, seq_rst);
MAPREG(TRCSEQSTR, seq_state);
MAPREG(TRCEXTINSELR, ext_inp);
MAPREG(TRCCIDCCTLR0, ctxid_mask0);
MAPREG(TRCCIDCCTLR1, ctxid_mask1);
MAPREG(TRCVMIDCCTLR0, vmid_mask0);
MAPREG(TRCVMIDCCTLR1, vmid_mask1);
default:
err = -EINVAL;
break;
}
} else if ((offset & GENMASK(11, 4)) == TRCSEQEVRn(0)) {
/* sequencer state control registers */
idx = (offset & GENMASK(3, 0)) / 4;
if (idx < ETM_MAX_SEQ_STATES)
reg->drv_store = &drvcfg->seq_ctrl[idx];
else
err = -EINVAL;
} else if ((offset >= TRCSSCCRn(0)) && (offset <= TRCSSPCICRn(7))) {
/* 32 bit, 8 off indexed register sets */
idx = (offset & GENMASK(4, 0)) / 4;
switch (offset & GENMASK(11, 5)) {
MAPREGIDX(TRCSSCCRn(0), ss_ctrl, idx);
MAPREGIDX(TRCSSCSRn(0), ss_status, idx);
MAPREGIDX(TRCSSPCICRn(0), ss_pe_cmp, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCCIDCVRn(0)) && (offset <= TRCVMIDCVRn(7))) {
/* 64 bit, 8 off indexed register sets */
idx = (offset & GENMASK(5, 0)) / 8;
switch (offset & GENMASK(11, 6)) {
MAPREGIDX(TRCCIDCVRn(0), ctxid_pid, idx);
MAPREGIDX(TRCVMIDCVRn(0), vmid_val, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCRSCTLRn(2)) &&
(offset <= TRCRSCTLRn((ETM_MAX_RES_SEL - 1)))) {
/* 32 bit resource selection regs, 32 off, skip fixed 0,1 */
idx = (offset & GENMASK(6, 0)) / 4;
if (idx < ETM_MAX_RES_SEL)
reg->drv_store = &drvcfg->res_ctrl[idx];
else
err = -EINVAL;
} else if ((offset >= TRCACVRn(0)) &&
(offset <= TRCACATRn((ETM_MAX_SINGLE_ADDR_CMP - 1)))) {
/* 64 bit addr cmp regs, 16 off */
idx = (offset & GENMASK(6, 0)) / 8;
switch (offset & GENMASK(11, 7)) {
MAPREGIDX(TRCACVRn(0), addr_val, idx);
MAPREGIDX(TRCACATRn(0), addr_acc, idx);
default:
err = -EINVAL;
break;
}
} else if ((offset >= TRCCNTRLDVRn(0)) &&
(offset <= TRCCNTVRn((ETMv4_MAX_CNTR - 1)))) {
/* 32 bit counter regs, 4 off (ETMv4_MAX_CNTR - 1) */
idx = (offset & GENMASK(3, 0)) / 4;
switch (offset & GENMASK(11, 4)) {
MAPREGIDX(TRCCNTRLDVRn(0), cntrldvr, idx);
MAPREGIDX(TRCCNTCTLRn(0), cntr_ctrl, idx);
MAPREGIDX(TRCCNTVRn(0), cntr_val, idx);
default:
err = -EINVAL;
break;
}
} else {
err = -EINVAL;
}
return err;
+}
+/**
- etm4_cfg_load_feature - load a feature into a device instance.
- @csdev: An ETMv4 CoreSight device.
- @feat_desc: The static feature descriptor to be loaded.
- The function will load a feature instance into the device, based on the
- supplied descriptor. The descriptor will be checked to ensure a valid
- feature can be loaded for this ETMv4 device.
- Parameter and register definitions will be converted into internal
- structures that are used to set the values in the driver when the
- feature is enabled for the device.
- */
+int etm4_cfg_load_feature(struct coresight_device *csdev,
const struct cs_cfg_feature_desc *feat_desc)
+{
struct device *dev = csdev->dev.parent;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
struct cs_cfg_feature *feat;
u32 offset;
int i = 0, err = 0;
feat = devm_kzalloc(dev, sizeof(struct cs_cfg_feature), GFP_KERNEL);
if (!feat)
return -ENOMEM;
feat->desc = feat_desc;
feat->dev_spinlock = &drvdata->spinlock;
feat->csdev = csdev;
/* count the params regs and allocate space. */
while (feat_desc->params[i++].name)
feat->nr_params++;
i = 0;
while (feat_desc->regs[i++].flags)
feat->nr_regs++;
feat->params = devm_kcalloc(dev, feat->nr_params,
sizeof(struct cs_cfg_parameter),
GFP_KERNEL);
if (!feat->params)
return -ENOMEM;
feat->regs = devm_kcalloc(dev, feat->nr_regs,
sizeof(struct cs_cfg_reg), GFP_KERNEL);
if (!feat->regs)
return -ENOMEM;
/* load the parameters default values */
for (i = 0; i < feat->nr_params; i++)
feat->params[i].feat = feat;
/* process the register descriptions */
for (i = 0; i < feat->nr_regs; i++) {
offset = feat_desc->regs[i].flags & CS_CFG_REG_ID_MASK;
err = etm4_cfg_map_reg_offset(drvdata, &feat->regs[i], offset);
if (err)
return err;
}
/* set sysfs representation of the feature */
err = coresight_cfg_sysfs_add_grp(feat);
/* add the feature to the device list and init default values */
if (!err) {
coresight_cfg_list_add_feat(feat);
coresight_cfg_set_def_ops(feat);
feat->ops.reset(feat);
}
return err;
+}
+/* load up the list of builtin features */ +int etm4_cfg_load_builtins(struct coresight_device *csdev) +{
int idx = 0, err = 0;
while (built_in_feats[idx] && !err)
err = etm4_cfg_load_feature(csdev, built_in_feats[idx++]);
return err;
+} diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h new file mode 100644 index 000000000000..703657cac2fc --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
- */
+#ifndef _CORESIGHT_CORESIGHT_ETM4X_CFG_H +#define _CORESIGHT_CORESIGHT_ETM4X_CFG_H
+#include "coresight-config.h"
+/* ETMv4 specific config defines */
+/* resource IDs */
+#define ETM4_CFG_RES_CTR 0x00001000 +#define ETM4_CFG_RES_CMP 0x00002000 +#define ETM4_CFG_RES_CMP_PAIR0 0x00003000 +#define ETM4_CFG_RES_CMP_PAIR1 0x00004000 +#define ETM4_CFG_RES_SEL 0x00005000 +#define ETM4_CFG_RES_SEL_PAIR0 0x00006000 +#define ETM4_CFG_RES_SEL_PAIR1 0x00007000 +#define ETM4_CFG_RES_SEQ 0x00008000 +#define ETM4_CFG_RES_TS 0x00009000 +#define ETM4_CFG_RES_MASK 0x0000F000
+/* ETM4 configuration handlers */ +int etm4_cfg_load_builtins(struct coresight_device *csdev);
+#endif /* _CORESIGHT_CORESIGHT_ETM4X_CFG_H */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index b673e738bc9a..ec182544a064 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -9,6 +9,7 @@ #include <linux/sysfs.h> #include "coresight-etm4x.h" #include "coresight-priv.h" +#include "coresight-config.h"
static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) { @@ -269,6 +270,8 @@ static ssize_t reset_store(struct device *dev,
spin_unlock(&drvdata->spinlock);
coresight_cfg_reset_feats(to_coresight_device(dev));
return size;
} static DEVICE_ATTR_WO(reset); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 60a5133d801c..e69dbcf5cd4d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -34,6 +34,7 @@
#include "coresight-etm4x.h" #include "coresight-etm-perf.h" +#include "coresight-etm4x-cfg.h"
static int boot_enable; module_param(boot_enable, int, 0444); @@ -320,10 +321,11 @@ static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata) return ret; }
-static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, +static int etm4_parse_event_config(struct coresight_device *csdev, struct perf_event *event) { int ret = 0;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_config *config = &drvdata->config; struct perf_event_attr *attr = &event->attr;
@@ -383,6 +385,9 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, /* bit[12], Return stack enable bit */ config->cfg |= BIT(12);
/* set any enabled features */
ret = coresight_cfg_set_feats_on_ena(csdev);
out: return ret; } @@ -399,7 +404,7 @@ static int etm4_enable_perf(struct coresight_device *csdev, }
/* Configure the tracer based on the session's specifics */
ret = etm4_parse_event_config(drvdata, event);
ret = etm4_parse_event_config(csdev, event); if (ret) goto out; /* And enable it */
@@ -415,6 +420,8 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) struct etm4_enable_arg arg = { }; int ret;
coresight_cfg_set_feats_on_ena(csdev);
spin_lock(&drvdata->spinlock); /*
@@ -532,6 +539,7 @@ static int etm4_disable_perf(struct coresight_device *csdev, return -EINVAL;
etm4_disable_hw(drvdata);
coresight_cfg_save_feats_on_dis(csdev); /* * Check if the start/stop logic was active when the unit was stopped.
@@ -566,6 +574,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) smp_call_function_single(drvdata->cpu, etm4_disable_hw, drvdata, 1);
spin_unlock(&drvdata->spinlock);
coresight_cfg_save_feats_on_dis(csdev); cpus_read_unlock(); dev_dbg(&csdev->dev, "ETM tracing disabled\n");
@@ -1510,6 +1519,11 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) goto err_arch_supported; }
/* load builtin features */
ret = etm4_cfg_load_builtins(drvdata->csdev);
if (ret)
goto err_arch_supported;
pm_runtime_put(&adev->dev); dev_info(&drvdata->csdev->dev, "CPU%d: ETM v%d.%d initialized\n", drvdata->cpu, drvdata->arch >> 4, drvdata->arch & 0xf);
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index f3efbb3b2b4d..ac16d96124f6 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1335,6 +1335,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) ret = coresight_fixup_orphan_conns(csdev); if (!ret) cti_add_assoc_to_csdev(csdev);
INIT_LIST_HEAD(&csdev->feat_list); mutex_unlock(&coresight_mutex); if (ret) {
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index e3e9f0e3a878..0e8d529282dd 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -204,6 +204,8 @@ struct coresight_device { /* sysfs links between components */ int nr_links; bool has_conns_grp;
/* feature list */
struct list_head feat_list;
};
/*
2.17.1
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK