Adds in handlers to allow the ETMv4 to use the complex configuration
support. Features and configurations can be loaded and selected in the
device.
Signed-off-by: Mike Leach <[email protected]>
---
drivers/hwtracing/coresight/Makefile | 3 +-
.../hwtracing/coresight/coresight-etm4x-cfg.c | 184 ++++++++++++++++++
.../hwtracing/coresight/coresight-etm4x-cfg.h | 29 +++
.../coresight/coresight-etm4x-core.c | 38 +++-
.../coresight/coresight-etm4x-sysfs.c | 3 +
5 files changed, 254 insertions(+), 3 deletions(-)
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 daad9f103a78..ea544206204d 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -16,7 +16,8 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o
coresight-etm3x-y := coresight-etm3x-core.o coresight-etm-cp14.o \
coresight-etm3x-sysfs.o
obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o
-coresight-etm4x-y := coresight-etm4x-core.o coresight-etm4x-sysfs.o
+coresight-etm4x-y := coresight-etm4x-core.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-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
new file mode 100644
index 000000000000..f237a8d02360
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2020 Linaro Limited. All rights reserved.
+ * Author: Mike Leach <[email protected]>
+ */
+
+#include "coresight-etm4x.h"
+#include "coresight-etm4x-cfg.h"
+#include "coresight-priv.h"
+#include "coresight-syscfg.h"
+
+/**
+ * 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 cscfg_reg_csdev *reg, u32 offset)
+{
+ int err = -EINVAL, idx;
+ struct etmv4_config *drvcfg = &drvdata->config;
+ u32 off_mask;
+
+#define CHECKREG(cval, elem) \
+ { \
+ if (offset == cval) { \
+ reg->drv_store = &drvcfg->elem; \
+ err = 0; \
+ break; \
+ } \
+ }
+
+#define CHECKREGIDX(cval, elem, off_idx, mask) \
+ { \
+ if (mask == cval) { \
+ reg->drv_store = &drvcfg->elem[off_idx]; \
+ err = 0; \
+ break; \
+ } \
+ }
+
+ if (((offset >= TRCEVENTCTL0R) && (offset <= TRCVIPCSSCTLR)) ||
+ ((offset >= TRCSEQRSTEVR) && (offset <= TRCEXTINSELR)) ||
+ ((offset >= TRCCIDCCTLR0) && (offset <= TRCVMIDCCTLR1))) {
+ do {
+ CHECKREG(TRCEVENTCTL0R, eventctrl0);
+ CHECKREG(TRCEVENTCTL1R, eventctrl1);
+ CHECKREG(TRCSTALLCTLR, stall_ctrl);
+ CHECKREG(TRCTSCTLR, ts_ctrl);
+ CHECKREG(TRCSYNCPR, syncfreq);
+ CHECKREG(TRCCCCTLR, ccctlr);
+ CHECKREG(TRCBBCTLR, bb_ctrl);
+ CHECKREG(TRCVICTLR, vinst_ctrl);
+ CHECKREG(TRCVIIECTLR, viiectlr);
+ CHECKREG(TRCVISSCTLR, vissctlr);
+ CHECKREG(TRCVIPCSSCTLR, vipcssctlr);
+ CHECKREG(TRCSEQRSTEVR, seq_rst);
+ CHECKREG(TRCSEQSTR, seq_state);
+ CHECKREG(TRCEXTINSELR, ext_inp);
+ CHECKREG(TRCCIDCCTLR0, ctxid_mask0);
+ CHECKREG(TRCCIDCCTLR1, ctxid_mask1);
+ CHECKREG(TRCVMIDCCTLR0, vmid_mask0);
+ CHECKREG(TRCVMIDCCTLR1, vmid_mask1);
+ } while (0);
+ } 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];
+ err = 0;
+ }
+ } else if ((offset >= TRCSSCCRn(0)) && (offset <= TRCSSPCICRn(7))) {
+ /* 32 bit, 8 off indexed register sets */
+ idx = (offset & GENMASK(4, 0)) / 4;
+ off_mask = (offset & GENMASK(11, 5));
+ do {
+ CHECKREGIDX(TRCSSCCRn(0), ss_ctrl, idx, off_mask);
+ CHECKREGIDX(TRCSSCSRn(0), ss_status, idx, off_mask);
+ CHECKREGIDX(TRCSSPCICRn(0), ss_pe_cmp, idx, off_mask);
+ } while (0);
+ } else if ((offset >= TRCCIDCVRn(0)) && (offset <= TRCVMIDCVRn(7))) {
+ /* 64 bit, 8 off indexed register sets */
+ idx = (offset & GENMASK(5, 0)) / 8;
+ off_mask = (offset & GENMASK(11, 6));
+ do {
+ CHECKREGIDX(TRCCIDCVRn(0), ctxid_pid, idx, off_mask);
+ CHECKREGIDX(TRCVMIDCVRn(0), vmid_val, idx, off_mask);
+ } while (0);
+ } 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];
+ err = 0;
+ }
+ } 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;
+ off_mask = offset & GENMASK(11, 7);
+ do {
+ CHECKREGIDX(TRCACVRn(0), addr_val, idx, off_mask);
+ CHECKREGIDX(TRCACATRn(0), addr_acc, idx, off_mask);
+ } while (0);
+ } 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;
+ off_mask = offset & GENMASK(11, 4);
+ do {
+ CHECKREGIDX(TRCCNTRLDVRn(0), cntrldvr, idx, off_mask);
+ CHECKREGIDX(TRCCNTCTLRn(0), cntr_ctrl, idx, off_mask);
+ CHECKREGIDX(TRCCNTVRn(0), cntr_val, idx, off_mask);
+ } while (0);
+ }
+ return err;
+}
+
+/**
+ * etm4_cfg_load_feature - load a feature into a device instance.
+ *
+ * @csdev: An ETMv4 CoreSight device.
+ * @feat: The feature to be loaded.
+ *
+ * The function will load a feature instance into the device, checking that
+ * the register definitions are valid for the 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.
+ *
+ * The feature spinlock pointer is initialised to the same spinlock
+ * that the driver uses to protect the internal register values.
+ */
+static int etm4_cfg_load_feature(struct coresight_device *csdev,
+ struct cscfg_feature_csdev *feat)
+{
+ struct device *dev = csdev->dev.parent;
+ struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
+ const struct cscfg_feature_desc *feat_desc = feat->desc;
+ u32 offset;
+ int i = 0, err = 0;
+
+ /*
+ * essential we set the device spinlock - this is used in the generic
+ * programming routines when copying values into the drvdata structures
+ * via the pointers setup in etm4_cfg_map_reg_offset().
+ */
+ feat->csdev_spinlock = &drvdata->spinlock;
+
+ /* process the register descriptions */
+ for (i = 0; i < feat->nr_regs && !err; i++) {
+ offset = feat_desc->regs[i].offset;
+ err = etm4_cfg_map_reg_offset(drvdata, &feat->regs[i], offset);
+ }
+ return err;
+}
+
+/* match information when loading configurations */
+#define CS_CFG_ETM4_MATCH_FLAGS (CS_CFG_MATCH_CLASS_SRC_ALL | \
+ CS_CFG_MATCH_CLASS_SRC_ETM4)
+
+int etm4_cscfg_register(struct coresight_device *csdev, const char *dev_name)
+{
+ struct cscfg_match_desc cfg_info;
+ struct cscfg_csdev_feat_ops ops;
+
+ cfg_info.match_flags = CS_CFG_ETM4_MATCH_FLAGS;
+
+ ops.load_feat = &etm4_cfg_load_feature;
+
+ return cscfg_register_csdev(csdev, &cfg_info, &ops);
+}
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h
new file mode 100644
index 000000000000..9e279c5da55d
--- /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"
+#include "coresight-etm4x.h"
+
+/* ETMv4 specific config defines */
+
+/* resource IDs */
+
+#define ETM4_CFG_RES_CTR 0x001
+#define ETM4_CFG_RES_CMP 0x002
+#define ETM4_CFG_RES_CMP_PAIR0 0x003
+#define ETM4_CFG_RES_CMP_PAIR1 0x004
+#define ETM4_CFG_RES_SEL 0x005
+#define ETM4_CFG_RES_SEL_PAIR0 0x006
+#define ETM4_CFG_RES_SEL_PAIR1 0x007
+#define ETM4_CFG_RES_SEQ 0x008
+#define ETM4_CFG_RES_TS 0x009
+#define ETM4_CFG_RES_MASK 0x00F
+
+int etm4_cscfg_register(struct coresight_device *csdev, const char *dev_name);
+
+#endif /* _CORESIGHT_CORESIGHT_ETM4X_CFG_H */
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index 473ab7480a36..5e501ce244dd 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -38,6 +38,8 @@
#include "coresight-etm4x.h"
#include "coresight-etm-perf.h"
+#include "coresight-etm4x-cfg.h"
+#include "coresight-syscfg.h"
static int boot_enable;
module_param(boot_enable, int, 0444);
@@ -491,12 +493,15 @@ 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;
+ unsigned long cfg_id;
+ int preset;
if (!attr) {
ret = -EINVAL;
@@ -554,6 +559,20 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
/* bit[12], Return stack enable bit */
config->cfg |= BIT(12);
+ /*
+ * Set any selected configuration and preset.
+ *
+ * This extracts the values of PMU_FORMAT_ATTR(configid) and PMU_FORMAT_ATTR(preset)
+ * in the perf attributes defined in coresight-etm-perf.c.
+ * configid uses bits 63:32 of attr->config2, preset uses bits 3:0 of attr->config.
+ * A zero configid means no configuration active, preset = 0 means no preset selected.
+ */
+ if (attr->config2 & GENMASK_ULL(63, 32)) {
+ cfg_id = (u32)(attr->config2 >> 32);
+ preset = attr->config & 0xF;
+ ret = cscfg_csdev_enable_active_config(csdev, cfg_id, preset);
+ }
+
out:
return ret;
}
@@ -570,7 +589,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 */
@@ -701,11 +720,18 @@ static int etm4_disable_perf(struct coresight_device *csdev,
u32 control;
struct etm_filters *filters = event->hw.addr_filters;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+ struct perf_event_attr *attr = &event->attr;
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
return -EINVAL;
etm4_disable_hw(drvdata);
+ /*
+ * The config_id occupies bits 63:32 of the config2 perf event attr
+ * field. If this is non-zero then we will have enabled a config.
+ */
+ if (attr->config2 & GENMASK_ULL(63, 32))
+ cscfg_csdev_disable_active_config(csdev);
/*
* Check if the start/stop logic was active when the unit was stopped.
@@ -1815,6 +1841,13 @@ static int etm4_probe(struct device *dev, void __iomem *base, u32 etm_pid)
return ret;
}
+ /* register with config infrastructure & load any current features */
+ ret = etm4_cscfg_register(drvdata->csdev, dev_name(dev));
+ if (ret) {
+ coresight_unregister(drvdata->csdev);
+ return ret;
+ }
+
etmdrvdata[drvdata->cpu] = drvdata;
dev_info(&drvdata->csdev->dev, "CPU%d: ETM v%d.%d initialized\n",
@@ -1902,6 +1935,7 @@ static int __exit etm4_remove_dev(struct etmv4_drvdata *drvdata)
cpus_read_unlock();
+ cscfg_unregister_csdev(drvdata->csdev);
coresight_unregister(drvdata->csdev);
return 0;
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
index b646d53a3133..cf2a51113436 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-syscfg.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);
+ cscfg_csdev_reset_feats(to_coresight_device(dev));
+
return size;
}
static DEVICE_ATTR_WO(reset);
--
2.17.1
On Thu, Jan 28, 2021 at 05:09:33PM +0000, Mike Leach wrote:
> Adds in handlers to allow the ETMv4 to use the complex configuration
> support. Features and configurations can be loaded and selected in the
> device.
>
> Signed-off-by: Mike Leach <[email protected]>
> ---
> drivers/hwtracing/coresight/Makefile | 3 +-
> .../hwtracing/coresight/coresight-etm4x-cfg.c | 184 ++++++++++++++++++
> .../hwtracing/coresight/coresight-etm4x-cfg.h | 29 +++
> .../coresight/coresight-etm4x-core.c | 38 +++-
> .../coresight/coresight-etm4x-sysfs.c | 3 +
> 5 files changed, 254 insertions(+), 3 deletions(-)
> 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 daad9f103a78..ea544206204d 100644
> --- a/drivers/hwtracing/coresight/Makefile
> +++ b/drivers/hwtracing/coresight/Makefile
> @@ -16,7 +16,8 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o
> coresight-etm3x-y := coresight-etm3x-core.o coresight-etm-cp14.o \
> coresight-etm3x-sysfs.o
> obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o
> -coresight-etm4x-y := coresight-etm4x-core.o coresight-etm4x-sysfs.o
> +coresight-etm4x-y := coresight-etm4x-core.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-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
> new file mode 100644
> index 000000000000..f237a8d02360
> --- /dev/null
> +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
> @@ -0,0 +1,184 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright(C) 2020 Linaro Limited. All rights reserved.
> + * Author: Mike Leach <[email protected]>
> + */
> +
> +#include "coresight-etm4x.h"
> +#include "coresight-etm4x-cfg.h"
> +#include "coresight-priv.h"
> +#include "coresight-syscfg.h"
> +
> +/**
> + * 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 cscfg_reg_csdev *reg, u32 offset)
> +{
> + int err = -EINVAL, idx;
> + struct etmv4_config *drvcfg = &drvdata->config;
> + u32 off_mask;
> +
> +#define CHECKREG(cval, elem) \
> + { \
> + if (offset == cval) { \
> + reg->drv_store = &drvcfg->elem; \
> + err = 0; \
> + break; \
> + } \
> + }
> +
> +#define CHECKREGIDX(cval, elem, off_idx, mask) \
> + { \
> + if (mask == cval) { \
> + reg->drv_store = &drvcfg->elem[off_idx]; \
> + err = 0; \
> + break; \
> + } \
> + }
> +
> + if (((offset >= TRCEVENTCTL0R) && (offset <= TRCVIPCSSCTLR)) ||
> + ((offset >= TRCSEQRSTEVR) && (offset <= TRCEXTINSELR)) ||
> + ((offset >= TRCCIDCCTLR0) && (offset <= TRCVMIDCCTLR1))) {
> + do {
> + CHECKREG(TRCEVENTCTL0R, eventctrl0);
> + CHECKREG(TRCEVENTCTL1R, eventctrl1);
> + CHECKREG(TRCSTALLCTLR, stall_ctrl);
> + CHECKREG(TRCTSCTLR, ts_ctrl);
> + CHECKREG(TRCSYNCPR, syncfreq);
> + CHECKREG(TRCCCCTLR, ccctlr);
> + CHECKREG(TRCBBCTLR, bb_ctrl);
> + CHECKREG(TRCVICTLR, vinst_ctrl);
> + CHECKREG(TRCVIIECTLR, viiectlr);
> + CHECKREG(TRCVISSCTLR, vissctlr);
> + CHECKREG(TRCVIPCSSCTLR, vipcssctlr);
> + CHECKREG(TRCSEQRSTEVR, seq_rst);
> + CHECKREG(TRCSEQSTR, seq_state);
> + CHECKREG(TRCEXTINSELR, ext_inp);
> + CHECKREG(TRCCIDCCTLR0, ctxid_mask0);
> + CHECKREG(TRCCIDCCTLR1, ctxid_mask1);
> + CHECKREG(TRCVMIDCCTLR0, vmid_mask0);
> + CHECKREG(TRCVMIDCCTLR1, vmid_mask1);
> + } while (0);
> + } 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];
> + err = 0;
> + }
> + } else if ((offset >= TRCSSCCRn(0)) && (offset <= TRCSSPCICRn(7))) {
> + /* 32 bit, 8 off indexed register sets */
> + idx = (offset & GENMASK(4, 0)) / 4;
> + off_mask = (offset & GENMASK(11, 5));
> + do {
> + CHECKREGIDX(TRCSSCCRn(0), ss_ctrl, idx, off_mask);
> + CHECKREGIDX(TRCSSCSRn(0), ss_status, idx, off_mask);
> + CHECKREGIDX(TRCSSPCICRn(0), ss_pe_cmp, idx, off_mask);
> + } while (0);
> + } else if ((offset >= TRCCIDCVRn(0)) && (offset <= TRCVMIDCVRn(7))) {
> + /* 64 bit, 8 off indexed register sets */
> + idx = (offset & GENMASK(5, 0)) / 8;
> + off_mask = (offset & GENMASK(11, 6));
> + do {
> + CHECKREGIDX(TRCCIDCVRn(0), ctxid_pid, idx, off_mask);
> + CHECKREGIDX(TRCVMIDCVRn(0), vmid_val, idx, off_mask);
> + } while (0);
> + } 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];
> + err = 0;
> + }
> + } 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;
> + off_mask = offset & GENMASK(11, 7);
> + do {
> + CHECKREGIDX(TRCACVRn(0), addr_val, idx, off_mask);
> + CHECKREGIDX(TRCACATRn(0), addr_acc, idx, off_mask);
> + } while (0);
> + } 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;
> + off_mask = offset & GENMASK(11, 4);
> + do {
> + CHECKREGIDX(TRCCNTRLDVRn(0), cntrldvr, idx, off_mask);
> + CHECKREGIDX(TRCCNTCTLRn(0), cntr_ctrl, idx, off_mask);
> + CHECKREGIDX(TRCCNTVRn(0), cntr_val, idx, off_mask);
> + } while (0);
> + }
> + return err;
> +}
> +
> +/**
> + * etm4_cfg_load_feature - load a feature into a device instance.
> + *
> + * @csdev: An ETMv4 CoreSight device.
> + * @feat: The feature to be loaded.
> + *
> + * The function will load a feature instance into the device, checking that
> + * the register definitions are valid for the 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.
> + *
> + * The feature spinlock pointer is initialised to the same spinlock
> + * that the driver uses to protect the internal register values.
> + */
> +static int etm4_cfg_load_feature(struct coresight_device *csdev,
> + struct cscfg_feature_csdev *feat)
> +{
> + struct device *dev = csdev->dev.parent;
> + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
> + const struct cscfg_feature_desc *feat_desc = feat->desc;
> + u32 offset;
> + int i = 0, err = 0;
> +
> + /*
> + * essential we set the device spinlock - this is used in the generic
> + * programming routines when copying values into the drvdata structures
> + * via the pointers setup in etm4_cfg_map_reg_offset().
> + */
> + feat->csdev_spinlock = &drvdata->spinlock;
> +
> + /* process the register descriptions */
> + for (i = 0; i < feat->nr_regs && !err; i++) {
> + offset = feat_desc->regs[i].offset;
> + err = etm4_cfg_map_reg_offset(drvdata, &feat->regs[i], offset);
> + }
> + return err;
> +}
> +
> +/* match information when loading configurations */
> +#define CS_CFG_ETM4_MATCH_FLAGS (CS_CFG_MATCH_CLASS_SRC_ALL | \
> + CS_CFG_MATCH_CLASS_SRC_ETM4)
Here the indentation is correct but not when the patch is applied. Have another
look on your side but don't worry about it if things are aligned.
> +
> +int etm4_cscfg_register(struct coresight_device *csdev, const char *dev_name)
> +{
> + struct cscfg_match_desc cfg_info;
> + struct cscfg_csdev_feat_ops ops;
> +
> + cfg_info.match_flags = CS_CFG_ETM4_MATCH_FLAGS;
> +
> + ops.load_feat = &etm4_cfg_load_feature;
> +
> + return cscfg_register_csdev(csdev, &cfg_info, &ops);
> +}
> diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h
> new file mode 100644
> index 000000000000..9e279c5da55d
> --- /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
Shouldn't that be "_CORESIGHT_ETM4X_CFG_H" ? The same denomination was used for
coresight-config.h but not coresight-syscfg.h...
> +
> +#include "coresight-config.h"
> +#include "coresight-etm4x.h"
> +
> +/* ETMv4 specific config defines */
> +
> +/* resource IDs */
> +
> +#define ETM4_CFG_RES_CTR 0x001
> +#define ETM4_CFG_RES_CMP 0x002
> +#define ETM4_CFG_RES_CMP_PAIR0 0x003
> +#define ETM4_CFG_RES_CMP_PAIR1 0x004
> +#define ETM4_CFG_RES_SEL 0x005
> +#define ETM4_CFG_RES_SEL_PAIR0 0x006
> +#define ETM4_CFG_RES_SEL_PAIR1 0x007
> +#define ETM4_CFG_RES_SEQ 0x008
> +#define ETM4_CFG_RES_TS 0x009
> +#define ETM4_CFG_RES_MASK 0x00F
These are used in patch 08, please move them there.
> +
> +int etm4_cscfg_register(struct coresight_device *csdev, const char *dev_name);
> +
> +#endif /* _CORESIGHT_CORESIGHT_ETM4X_CFG_H */
> diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
> index 473ab7480a36..5e501ce244dd 100644
> --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
> +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
> @@ -38,6 +38,8 @@
>
> #include "coresight-etm4x.h"
> #include "coresight-etm-perf.h"
> +#include "coresight-etm4x-cfg.h"
> +#include "coresight-syscfg.h"
>
> static int boot_enable;
> module_param(boot_enable, int, 0444);
> @@ -491,12 +493,15 @@ 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;
> + unsigned long cfg_id;
> + int preset;
>
Ok
> if (!attr) {
> ret = -EINVAL;
> @@ -554,6 +559,20 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
> /* bit[12], Return stack enable bit */
> config->cfg |= BIT(12);
>
> + /*
> + * Set any selected configuration and preset.
> + *
> + * This extracts the values of PMU_FORMAT_ATTR(configid) and PMU_FORMAT_ATTR(preset)
> + * in the perf attributes defined in coresight-etm-perf.c.
> + * configid uses bits 63:32 of attr->config2, preset uses bits 3:0 of attr->config.
> + * A zero configid means no configuration active, preset = 0 means no preset selected.
> + */
> + if (attr->config2 & GENMASK_ULL(63, 32)) {
> + cfg_id = (u32)(attr->config2 >> 32);
> + preset = attr->config & 0xF;
> + ret = cscfg_csdev_enable_active_config(csdev, cfg_id, preset);
> + }
> +
Ok
> out:
> return ret;
> }
> @@ -570,7 +589,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 */
> @@ -701,11 +720,18 @@ static int etm4_disable_perf(struct coresight_device *csdev,
> u32 control;
> struct etm_filters *filters = event->hw.addr_filters;
> struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> + struct perf_event_attr *attr = &event->attr;
>
> if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
> return -EINVAL;
>
> etm4_disable_hw(drvdata);
> + /*
> + * The config_id occupies bits 63:32 of the config2 perf event attr
> + * field. If this is non-zero then we will have enabled a config.
> + */
> + if (attr->config2 & GENMASK_ULL(63, 32))
> + cscfg_csdev_disable_active_config(csdev);
Ok
>
> /*
> * Check if the start/stop logic was active when the unit was stopped.
> @@ -1815,6 +1841,13 @@ static int etm4_probe(struct device *dev, void __iomem *base, u32 etm_pid)
> return ret;
> }
>
> + /* register with config infrastructure & load any current features */
> + ret = etm4_cscfg_register(drvdata->csdev, dev_name(dev));
> + if (ret) {
> + coresight_unregister(drvdata->csdev);
> + return ret;
> + }
> +
> etmdrvdata[drvdata->cpu] = drvdata;
>
> dev_info(&drvdata->csdev->dev, "CPU%d: ETM v%d.%d initialized\n",
> @@ -1902,6 +1935,7 @@ static int __exit etm4_remove_dev(struct etmv4_drvdata *drvdata)
>
> cpus_read_unlock();
>
> + cscfg_unregister_csdev(drvdata->csdev);
> coresight_unregister(drvdata->csdev);
>
> return 0;
> diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
> index b646d53a3133..cf2a51113436 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-syscfg.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);
>
> + cscfg_csdev_reset_feats(to_coresight_device(dev));
> +
Other than the cosmetic comments above, this patch is fine.
Thanks,
Mathieu
> return size;
> }
> static DEVICE_ATTR_WO(reset);
> --
> 2.17.1
>
On 1/28/21 5:09 PM, Mike Leach wrote:
> Adds in handlers to allow the ETMv4 to use the complex configuration
> support. Features and configurations can be loaded and selected in the
> device.
>
> Signed-off-by: Mike Leach <[email protected]>
> ---
> drivers/hwtracing/coresight/Makefile | 3 +-
> .../hwtracing/coresight/coresight-etm4x-cfg.c | 184 ++++++++++++++++++
> .../hwtracing/coresight/coresight-etm4x-cfg.h | 29 +++
> .../coresight/coresight-etm4x-core.c | 38 +++-
> .../coresight/coresight-etm4x-sysfs.c | 3 +
> 5 files changed, 254 insertions(+), 3 deletions(-)
> 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 daad9f103a78..ea544206204d 100644
> --- a/drivers/hwtracing/coresight/Makefile
> +++ b/drivers/hwtracing/coresight/Makefile
> @@ -16,7 +16,8 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o
> coresight-etm3x-y := coresight-etm3x-core.o coresight-etm-cp14.o \
> coresight-etm3x-sysfs.o
> obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o
> -coresight-etm4x-y := coresight-etm4x-core.o coresight-etm4x-sysfs.o
> +coresight-etm4x-y := coresight-etm4x-core.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-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
> new file mode 100644
> index 000000000000..f237a8d02360
> --- /dev/null
> +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
> @@ -0,0 +1,184 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright(C) 2020 Linaro Limited. All rights reserved.
> + * Author: Mike Leach <[email protected]>
> + */
> +
> +#include "coresight-etm4x.h"
> +#include "coresight-etm4x-cfg.h"
> +#include "coresight-priv.h"
> +#include "coresight-syscfg.h"
> +
> +/**
> + * 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 cscfg_reg_csdev *reg, u32 offset)
> +{
> + int err = -EINVAL, idx;
> + struct etmv4_config *drvcfg = &drvdata->config;
> + u32 off_mask;
> +
> +#define CHECKREG(cval, elem) \
> + { \
> + if (offset == cval) { \
> + reg->drv_store = &drvcfg->elem; \
> + err = 0; \
> + break; \
> + } \
> + }
> +
> +#define CHECKREGIDX(cval, elem, off_idx, mask) \
> + { \
> + if (mask == cval) { \
> + reg->drv_store = &drvcfg->elem[off_idx]; \
> + err = 0; \
> + break; \
> + } \
> + }
> +
I think we can live with switch-case, ignoring the Warnings generated by the
checkpatch, making the code much more readable and less error prone.
Otherwise, please could you move the above macro definitions to the top,
outside this function ?
> + if (((offset >= TRCEVENTCTL0R) && (offset <= TRCVIPCSSCTLR)) ||
> + ((offset >= TRCSEQRSTEVR) && (offset <= TRCEXTINSELR)) ||
> + ((offset >= TRCCIDCCTLR0) && (offset <= TRCVMIDCCTLR1))) {
> + do {
> + CHECKREG(TRCEVENTCTL0R, eventctrl0);
> + CHECKREG(TRCEVENTCTL1R, eventctrl1);
> + CHECKREG(TRCSTALLCTLR, stall_ctrl);
> + CHECKREG(TRCTSCTLR, ts_ctrl);
> + CHECKREG(TRCSYNCPR, syncfreq);
> + CHECKREG(TRCCCCTLR, ccctlr);
> + CHECKREG(TRCBBCTLR, bb_ctrl);
> + CHECKREG(TRCVICTLR, vinst_ctrl);
> + CHECKREG(TRCVIIECTLR, viiectlr);
> + CHECKREG(TRCVISSCTLR, vissctlr);
> + CHECKREG(TRCVIPCSSCTLR, vipcssctlr);
> + CHECKREG(TRCSEQRSTEVR, seq_rst);
> + CHECKREG(TRCSEQSTR, seq_state);
> + CHECKREG(TRCEXTINSELR, ext_inp);
> + CHECKREG(TRCCIDCCTLR0, ctxid_mask0);
> + CHECKREG(TRCCIDCCTLR1, ctxid_mask1);
> + CHECKREG(TRCVMIDCCTLR0, vmid_mask0);
> + CHECKREG(TRCVMIDCCTLR1, vmid_mask1);
> + } while (0);
> + } 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];
> + err = 0;
> + }
> + } else if ((offset >= TRCSSCCRn(0)) && (offset <= TRCSSPCICRn(7))) {
> + /* 32 bit, 8 off indexed register sets */
> + idx = (offset & GENMASK(4, 0)) / 4;
> + off_mask = (offset & GENMASK(11, 5));
> + do {
> + CHECKREGIDX(TRCSSCCRn(0), ss_ctrl, idx, off_mask);
> + CHECKREGIDX(TRCSSCSRn(0), ss_status, idx, off_mask);
> + CHECKREGIDX(TRCSSPCICRn(0), ss_pe_cmp, idx, off_mask);
> + } while (0);
> + } else if ((offset >= TRCCIDCVRn(0)) && (offset <= TRCVMIDCVRn(7))) {
> + /* 64 bit, 8 off indexed register sets */
> + idx = (offset & GENMASK(5, 0)) / 8;
> + off_mask = (offset & GENMASK(11, 6));
> + do {
> + CHECKREGIDX(TRCCIDCVRn(0), ctxid_pid, idx, off_mask);
> + CHECKREGIDX(TRCVMIDCVRn(0), vmid_val, idx, off_mask);
> + } while (0);
> + } 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];
> + err = 0;
> + }
> + } 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;
> + off_mask = offset & GENMASK(11, 7);
> + do {
> + CHECKREGIDX(TRCACVRn(0), addr_val, idx, off_mask);
> + CHECKREGIDX(TRCACATRn(0), addr_acc, idx, off_mask);
What happens if the ETM doesn't have enough resources ? Do we fail the load
here ? Or do we fail the activation during enabling the ETM ? Or do we assume
all ETMs have the resources required for statically defined configs ?
> + } while (0);
> + } 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;
> + off_mask = offset & GENMASK(11, 4);
> + do {
> + CHECKREGIDX(TRCCNTRLDVRn(0), cntrldvr, idx, off_mask);
> + CHECKREGIDX(TRCCNTCTLRn(0), cntr_ctrl, idx, off_mask);
> + CHECKREGIDX(TRCCNTVRn(0), cntr_val, idx, off_mask);
> + } while (0);
> + }
> + return err;
> +}
> +
> +/**
> + * etm4_cfg_load_feature - load a feature into a device instance.
> + *
> + * @csdev: An ETMv4 CoreSight device.
> + * @feat: The feature to be loaded.
> + *
> + * The function will load a feature instance into the device, checking that
> + * the register definitions are valid for the 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.
> + *
> + * The feature spinlock pointer is initialised to the same spinlock
> + * that the driver uses to protect the internal register values.
> + */
> +static int etm4_cfg_load_feature(struct coresight_device *csdev,
> + struct cscfg_feature_csdev *feat)
> +{
> + struct device *dev = csdev->dev.parent;
> + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
> + const struct cscfg_feature_desc *feat_desc = feat->desc;
> + u32 offset;
> + int i = 0, err = 0;
> +
> + /*
> + * essential we set the device spinlock - this is used in the generic
> + * programming routines when copying values into the drvdata structures
> + * via the pointers setup in etm4_cfg_map_reg_offset().
> + */
> + feat->csdev_spinlock = &drvdata->spinlock;
> +
> + /* process the register descriptions */
> + for (i = 0; i < feat->nr_regs && !err; i++) {
> + offset = feat_desc->regs[i].offset;
> + err = etm4_cfg_map_reg_offset(drvdata, &feat->regs[i], offset);
> + }
> + return err;
> +}
> +
> +/* match information when loading configurations */
> +#define CS_CFG_ETM4_MATCH_FLAGS (CS_CFG_MATCH_CLASS_SRC_ALL | \
> + CS_CFG_MATCH_CLASS_SRC_ETM4)
> +
> +int etm4_cscfg_register(struct coresight_device *csdev, const char *dev_name)
> +{
> + struct cscfg_match_desc cfg_info;
> + struct cscfg_csdev_feat_ops ops;
> +
> + cfg_info.match_flags = CS_CFG_ETM4_MATCH_FLAGS;
> +
> + ops.load_feat = &etm4_cfg_load_feature;
> +
> + return cscfg_register_csdev(csdev, &cfg_info, &ops);
> +}
> diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h
> new file mode 100644
> index 000000000000..9e279c5da55d
> --- /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"
> +#include "coresight-etm4x.h"
> +
> +/* ETMv4 specific config defines */
> +
> +/* resource IDs */
> +
> +#define ETM4_CFG_RES_CTR 0x001
> +#define ETM4_CFG_RES_CMP 0x002
> +#define ETM4_CFG_RES_CMP_PAIR0 0x003
> +#define ETM4_CFG_RES_CMP_PAIR1 0x004
> +#define ETM4_CFG_RES_SEL 0x005
> +#define ETM4_CFG_RES_SEL_PAIR0 0x006
> +#define ETM4_CFG_RES_SEL_PAIR1 0x007
> +#define ETM4_CFG_RES_SEQ 0x008
> +#define ETM4_CFG_RES_TS 0x009
> +#define ETM4_CFG_RES_MASK 0x00F
Where are these used ? Probably better to move it to the patch
where they are actually used.
Suzuki
Hi Suzuki,
Realised I hadn't answered the question below...
On Fri, 5 Mar 2021 at 10:18, Suzuki K Poulose <[email protected]> wrote:
>
> On 1/28/21 5:09 PM, Mike Leach wrote:
> > Adds in handlers to allow the ETMv4 to use the complex configuration
> > support. Features and configurations can be loaded and selected in the
> > device.
> >
> > Signed-off-by: Mike Leach <[email protected]>
> > ---
> > drivers/hwtracing/coresight/Makefile | 3 +-
> > .../hwtracing/coresight/coresight-etm4x-cfg.c | 184 ++++++++++++++++++
> > .../hwtracing/coresight/coresight-etm4x-cfg.h | 29 +++
> > .../coresight/coresight-etm4x-core.c | 38 +++-
> > .../coresight/coresight-etm4x-sysfs.c | 3 +
> > 5 files changed, 254 insertions(+), 3 deletions(-)
> > 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 daad9f103a78..ea544206204d 100644
> > --- a/drivers/hwtracing/coresight/Makefile
> > +++ b/drivers/hwtracing/coresight/Makefile
> > @@ -16,7 +16,8 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o
> > coresight-etm3x-y := coresight-etm3x-core.o coresight-etm-cp14.o \
> > coresight-etm3x-sysfs.o
> > obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o
> > -coresight-etm4x-y := coresight-etm4x-core.o coresight-etm4x-sysfs.o
> > +coresight-etm4x-y := coresight-etm4x-core.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-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
> > new file mode 100644
> > index 000000000000..f237a8d02360
> > --- /dev/null
> > +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
> > @@ -0,0 +1,184 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright(C) 2020 Linaro Limited. All rights reserved.
> > + * Author: Mike Leach <[email protected]>
> > + */
> > +
> > +#include "coresight-etm4x.h"
> > +#include "coresight-etm4x-cfg.h"
> > +#include "coresight-priv.h"
> > +#include "coresight-syscfg.h"
> > +
> > +/**
> > + * 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 cscfg_reg_csdev *reg, u32 offset)
> > +{
> > + int err = -EINVAL, idx;
> > + struct etmv4_config *drvcfg = &drvdata->config;
> > + u32 off_mask;
> > +
> > +#define CHECKREG(cval, elem) \
> > + { \
> > + if (offset == cval) { \
> > + reg->drv_store = &drvcfg->elem; \
> > + err = 0; \
> > + break; \
> > + } \
> > + }
> > +
> > +#define CHECKREGIDX(cval, elem, off_idx, mask) \
> > + { \
> > + if (mask == cval) { \
> > + reg->drv_store = &drvcfg->elem[off_idx]; \
> > + err = 0; \
> > + break; \
> > + } \
> > + }
> > +
>
> I think we can live with switch-case, ignoring the Warnings generated by the
> checkpatch, making the code much more readable and less error prone.
>
> Otherwise, please could you move the above macro definitions to the top,
> outside this function ?
>
> > + if (((offset >= TRCEVENTCTL0R) && (offset <= TRCVIPCSSCTLR)) ||
> > + ((offset >= TRCSEQRSTEVR) && (offset <= TRCEXTINSELR)) ||
> > + ((offset >= TRCCIDCCTLR0) && (offset <= TRCVMIDCCTLR1))) {
> > + do {
> > + CHECKREG(TRCEVENTCTL0R, eventctrl0);
> > + CHECKREG(TRCEVENTCTL1R, eventctrl1);
> > + CHECKREG(TRCSTALLCTLR, stall_ctrl);
> > + CHECKREG(TRCTSCTLR, ts_ctrl);
> > + CHECKREG(TRCSYNCPR, syncfreq);
> > + CHECKREG(TRCCCCTLR, ccctlr);
> > + CHECKREG(TRCBBCTLR, bb_ctrl);
> > + CHECKREG(TRCVICTLR, vinst_ctrl);
> > + CHECKREG(TRCVIIECTLR, viiectlr);
> > + CHECKREG(TRCVISSCTLR, vissctlr);
> > + CHECKREG(TRCVIPCSSCTLR, vipcssctlr);
> > + CHECKREG(TRCSEQRSTEVR, seq_rst);
> > + CHECKREG(TRCSEQSTR, seq_state);
> > + CHECKREG(TRCEXTINSELR, ext_inp);
> > + CHECKREG(TRCCIDCCTLR0, ctxid_mask0);
> > + CHECKREG(TRCCIDCCTLR1, ctxid_mask1);
> > + CHECKREG(TRCVMIDCCTLR0, vmid_mask0);
> > + CHECKREG(TRCVMIDCCTLR1, vmid_mask1);
> > + } while (0);
> > + } 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];
> > + err = 0;
> > + }
> > + } else if ((offset >= TRCSSCCRn(0)) && (offset <= TRCSSPCICRn(7))) {
> > + /* 32 bit, 8 off indexed register sets */
> > + idx = (offset & GENMASK(4, 0)) / 4;
> > + off_mask = (offset & GENMASK(11, 5));
> > + do {
> > + CHECKREGIDX(TRCSSCCRn(0), ss_ctrl, idx, off_mask);
> > + CHECKREGIDX(TRCSSCSRn(0), ss_status, idx, off_mask);
> > + CHECKREGIDX(TRCSSPCICRn(0), ss_pe_cmp, idx, off_mask);
> > + } while (0);
> > + } else if ((offset >= TRCCIDCVRn(0)) && (offset <= TRCVMIDCVRn(7))) {
> > + /* 64 bit, 8 off indexed register sets */
> > + idx = (offset & GENMASK(5, 0)) / 8;
> > + off_mask = (offset & GENMASK(11, 6));
> > + do {
> > + CHECKREGIDX(TRCCIDCVRn(0), ctxid_pid, idx, off_mask);
> > + CHECKREGIDX(TRCVMIDCVRn(0), vmid_val, idx, off_mask);
> > + } while (0);
> > + } 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];
> > + err = 0;
> > + }
> > + } 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;
> > + off_mask = offset & GENMASK(11, 7);
> > + do {
> > + CHECKREGIDX(TRCACVRn(0), addr_val, idx, off_mask);
> > + CHECKREGIDX(TRCACATRn(0), addr_acc, idx, off_mask);
>
> What happens if the ETM doesn't have enough resources ? Do we fail the load
> here ? Or do we fail the activation during enabling the ETM ? Or do we assume
> all ETMs have the resources required for statically defined configs ?
>
Resource management will be in a follow-up set - dynamic load of
features and configurations will be gated on sufficient resources.
There are two stages where this can fail:-
1) a feature requires resources that the ETM does not have - this will
fail on load. Any dependent configuration will fail at this point
too. Where this is the result of dynamic load of a configuration and
features the load of that module will be failed.
2) a configuration uses more features than devices have total
resources for - this will fail on enable as a feature cannot be
programmed due to lack of resources.
For the built in config - for now we assume any platform has the
resources - in future we will fail the load of the feature, but not
the load of coresight itself.
Moving forwards I would expect that we will avoid having too many
built-in configs and push for all new ones to be a separate loadable
module as a minimum.
Thanks
Mike
> > + } while (0);
> > + } 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;
> > + off_mask = offset & GENMASK(11, 4);
> > + do {
> > + CHECKREGIDX(TRCCNTRLDVRn(0), cntrldvr, idx, off_mask);
> > + CHECKREGIDX(TRCCNTCTLRn(0), cntr_ctrl, idx, off_mask);
> > + CHECKREGIDX(TRCCNTVRn(0), cntr_val, idx, off_mask);
> > + } while (0);
> > + }
> > + return err;
> > +}
> > +
> > +/**
> > + * etm4_cfg_load_feature - load a feature into a device instance.
> > + *
> > + * @csdev: An ETMv4 CoreSight device.
> > + * @feat: The feature to be loaded.
> > + *
> > + * The function will load a feature instance into the device, checking that
> > + * the register definitions are valid for the 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.
> > + *
> > + * The feature spinlock pointer is initialised to the same spinlock
> > + * that the driver uses to protect the internal register values.
> > + */
> > +static int etm4_cfg_load_feature(struct coresight_device *csdev,
> > + struct cscfg_feature_csdev *feat)
> > +{
> > + struct device *dev = csdev->dev.parent;
> > + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
> > + const struct cscfg_feature_desc *feat_desc = feat->desc;
> > + u32 offset;
> > + int i = 0, err = 0;
> > +
> > + /*
> > + * essential we set the device spinlock - this is used in the generic
> > + * programming routines when copying values into the drvdata structures
> > + * via the pointers setup in etm4_cfg_map_reg_offset().
> > + */
> > + feat->csdev_spinlock = &drvdata->spinlock;
> > +
> > + /* process the register descriptions */
> > + for (i = 0; i < feat->nr_regs && !err; i++) {
> > + offset = feat_desc->regs[i].offset;
> > + err = etm4_cfg_map_reg_offset(drvdata, &feat->regs[i], offset);
> > + }
> > + return err;
> > +}
> > +
> > +/* match information when loading configurations */
> > +#define CS_CFG_ETM4_MATCH_FLAGS (CS_CFG_MATCH_CLASS_SRC_ALL | \
> > + CS_CFG_MATCH_CLASS_SRC_ETM4)
> > +
> > +int etm4_cscfg_register(struct coresight_device *csdev, const char *dev_name)
> > +{
> > + struct cscfg_match_desc cfg_info;
> > + struct cscfg_csdev_feat_ops ops;
> > +
> > + cfg_info.match_flags = CS_CFG_ETM4_MATCH_FLAGS;
> > +
> > + ops.load_feat = &etm4_cfg_load_feature;
> > +
> > + return cscfg_register_csdev(csdev, &cfg_info, &ops);
> > +}
> > diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h
> > new file mode 100644
> > index 000000000000..9e279c5da55d
> > --- /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"
> > +#include "coresight-etm4x.h"
> > +
> > +/* ETMv4 specific config defines */
> > +
> > +/* resource IDs */
> > +
> > +#define ETM4_CFG_RES_CTR 0x001
> > +#define ETM4_CFG_RES_CMP 0x002
> > +#define ETM4_CFG_RES_CMP_PAIR0 0x003
> > +#define ETM4_CFG_RES_CMP_PAIR1 0x004
> > +#define ETM4_CFG_RES_SEL 0x005
> > +#define ETM4_CFG_RES_SEL_PAIR0 0x006
> > +#define ETM4_CFG_RES_SEL_PAIR1 0x007
> > +#define ETM4_CFG_RES_SEQ 0x008
> > +#define ETM4_CFG_RES_TS 0x009
> > +#define ETM4_CFG_RES_MASK 0x00F
>
> Where are these used ? Probably better to move it to the patch
> where they are actually used.
>
> Suzuki
--
Mike Leach
Principal Engineer, ARM Ltd.
Manchester Design Centre. UK