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;
};
/*