summaryrefslogtreecommitdiff
path: root/drivers/hwtracing/coresight
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwtracing/coresight')
-rw-r--r--drivers/hwtracing/coresight/Kconfig1
-rw-r--r--drivers/hwtracing/coresight/Makefile7
-rw-r--r--drivers/hwtracing/coresight/coresight-cfg-afdo.c153
-rw-r--r--drivers/hwtracing/coresight/coresight-cfg-preload.c31
-rw-r--r--drivers/hwtracing/coresight/coresight-cfg-preload.h13
-rw-r--r--drivers/hwtracing/coresight/coresight-config.c272
-rw-r--r--drivers/hwtracing/coresight/coresight-config.h253
-rw-r--r--drivers/hwtracing/coresight/coresight-core.c12
-rw-r--r--drivers/hwtracing/coresight/coresight-cpu-debug.c4
-rw-r--r--drivers/hwtracing/coresight/coresight-etm-perf.c150
-rw-r--r--drivers/hwtracing/coresight/coresight-etm-perf.h12
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-cfg.c182
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-cfg.h30
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-core.c38
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-sysfs.c3
-rw-r--r--drivers/hwtracing/coresight/coresight-syscfg-configfs.c396
-rw-r--r--drivers/hwtracing/coresight/coresight-syscfg-configfs.h45
-rw-r--r--drivers/hwtracing/coresight/coresight-syscfg.c847
-rw-r--r--drivers/hwtracing/coresight/coresight-syscfg.h81
19 files changed, 2492 insertions, 38 deletions
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index 84530fd80998..f026e5c0e777 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -8,6 +8,7 @@ menuconfig CORESIGHT
depends on OF || ACPI
select ARM_AMBA
select PERF_EVENTS
+ select CONFIGFS_FS
help
This framework provides a kernel interface for the CoreSight debug
and trace drivers to register themselves with. It's intended to build
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index d60816509755..b6c4a48140ec 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -4,7 +4,9 @@
#
obj-$(CONFIG_CORESIGHT) += coresight.o
coresight-y := coresight-core.o coresight-etm-perf.o coresight-platform.o \
- coresight-sysfs.o
+ coresight-sysfs.o coresight-syscfg.o coresight-config.o \
+ coresight-cfg-preload.o coresight-cfg-afdo.o \
+ coresight-syscfg-configfs.o
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o
coresight-tmc-y := coresight-tmc-core.o coresight-tmc-etf.o \
coresight-tmc-etr.o
@@ -16,7 +18,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-cfg-afdo.c b/drivers/hwtracing/coresight/coresight-cfg-afdo.c
new file mode 100644
index 000000000000..84b31184252b
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-cfg-afdo.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2020 Linaro Limited. All rights reserved.
+ * Author: Mike Leach <mike.leach@linaro.org>
+ */
+
+#include "coresight-config.h"
+
+/* ETMv4 includes and features */
+#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X)
+#include "coresight-etm4x-cfg.h"
+
+/* preload configurations and features */
+
+/* preload in features for ETMv4 */
+
+/* strobe feature */
+static struct cscfg_parameter_desc strobe_params[] = {
+ {
+ .name = "window",
+ .value = 5000,
+ },
+ {
+ .name = "period",
+ .value = 10000,
+ },
+};
+
+static struct cscfg_regval_desc strobe_regs[] = {
+ /* resource selectors */
+ {
+ .type = CS_CFG_REG_TYPE_RESOURCE,
+ .offset = TRCRSCTLRn(2),
+ .hw_info = ETM4_CFG_RES_SEL,
+ .val32 = 0x20001,
+ },
+ {
+ .type = CS_CFG_REG_TYPE_RESOURCE,
+ .offset = TRCRSCTLRn(3),
+ .hw_info = ETM4_CFG_RES_SEQ,
+ .val32 = 0x20002,
+ },
+ /* strobe window counter 0 - reload from param 0 */
+ {
+ .type = CS_CFG_REG_TYPE_RESOURCE | CS_CFG_REG_TYPE_VAL_SAVE,
+ .offset = TRCCNTVRn(0),
+ .hw_info = ETM4_CFG_RES_CTR,
+ },
+ {
+ .type = CS_CFG_REG_TYPE_RESOURCE | CS_CFG_REG_TYPE_VAL_PARAM,
+ .offset = TRCCNTRLDVRn(0),
+ .hw_info = ETM4_CFG_RES_CTR,
+ .val32 = 0,
+ },
+ {
+ .type = CS_CFG_REG_TYPE_RESOURCE,
+ .offset = TRCCNTCTLRn(0),
+ .hw_info = ETM4_CFG_RES_CTR,
+ .val32 = 0x10001,
+ },
+ /* strobe period counter 1 - reload from param 1 */
+ {
+ .type = CS_CFG_REG_TYPE_RESOURCE | CS_CFG_REG_TYPE_VAL_SAVE,
+ .offset = TRCCNTVRn(1),
+ .hw_info = ETM4_CFG_RES_CTR,
+ },
+ {
+ .type = CS_CFG_REG_TYPE_RESOURCE | CS_CFG_REG_TYPE_VAL_PARAM,
+ .offset = TRCCNTRLDVRn(1),
+ .hw_info = ETM4_CFG_RES_CTR,
+ .val32 = 1,
+ },
+ {
+ .type = CS_CFG_REG_TYPE_RESOURCE,
+ .offset = TRCCNTCTLRn(1),
+ .hw_info = ETM4_CFG_RES_CTR,
+ .val32 = 0x8102,
+ },
+ /* sequencer */
+ {
+ .type = CS_CFG_REG_TYPE_RESOURCE,
+ .offset = TRCSEQEVRn(0),
+ .hw_info = ETM4_CFG_RES_SEQ,
+ .val32 = 0x0081,
+ },
+ {
+ .type = CS_CFG_REG_TYPE_RESOURCE,
+ .offset = TRCSEQEVRn(1),
+ .hw_info = ETM4_CFG_RES_SEQ,
+ .val32 = 0x0000,
+ },
+ /* view-inst */
+ {
+ .type = CS_CFG_REG_TYPE_STD | CS_CFG_REG_TYPE_VAL_MASK,
+ .offset = TRCVICTLR,
+ .val32 = 0x0003,
+ .mask32 = 0x0003,
+ },
+ /* end of regs */
+};
+
+struct cscfg_feature_desc strobe_etm4x = {
+ .name = "strobing",
+ .description = "Generate periodic trace capture windows.\n"
+ "parameter \'window\': a number of CPU cycles (W)\n"
+ "parameter \'period\': trace enabled for W cycles every period x W cycles\n",
+ .match_flags = CS_CFG_MATCH_CLASS_SRC_ETM4,
+ .nr_params = ARRAY_SIZE(strobe_params),
+ .params_desc = strobe_params,
+ .nr_regs = ARRAY_SIZE(strobe_regs),
+ .regs_desc = strobe_regs,
+};
+
+/* create an autofdo configuration */
+
+/* we will provide 9 sets of preset parameter values */
+#define AFDO_NR_PRESETS 9
+/* the total number of parameters in used features */
+#define AFDO_NR_PARAMS ARRAY_SIZE(strobe_params)
+
+static const char *afdo_ref_names[] = {
+ "strobing",
+};
+
+/*
+ * set of presets leaves strobing window constant while varying period to allow
+ * experimentation with mark / space ratios for various workloads
+ */
+static u64 afdo_presets[AFDO_NR_PRESETS][AFDO_NR_PARAMS] = {
+ { 5000, 2 },
+ { 5000, 4 },
+ { 5000, 8 },
+ { 5000, 16 },
+ { 5000, 64 },
+ { 5000, 128 },
+ { 5000, 512 },
+ { 5000, 1024 },
+ { 5000, 4096 },
+};
+
+struct cscfg_config_desc afdo_etm4x = {
+ .name = "autofdo",
+ .description = "Setup ETMs with strobing for autofdo\n"
+ "Supplied presets allow experimentation with mark-space ratio for various loads\n",
+ .nr_feat_refs = ARRAY_SIZE(afdo_ref_names),
+ .feat_ref_names = afdo_ref_names,
+ .nr_presets = AFDO_NR_PRESETS,
+ .nr_total_params = AFDO_NR_PARAMS,
+ .presets = &afdo_presets[0][0],
+};
+
+/* end of ETM4x configurations */
+#endif /* IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X) */
diff --git a/drivers/hwtracing/coresight/coresight-cfg-preload.c b/drivers/hwtracing/coresight/coresight-cfg-preload.c
new file mode 100644
index 000000000000..751af3710d56
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-cfg-preload.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2020 Linaro Limited. All rights reserved.
+ * Author: Mike Leach <mike.leach@linaro.org>
+ */
+
+#include "coresight-cfg-preload.h"
+#include "coresight-config.h"
+#include "coresight-syscfg.h"
+
+/* Basic features and configurations pre-loaded on initialisation */
+
+static struct cscfg_feature_desc *preload_feats[] = {
+#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X)
+ &strobe_etm4x,
+#endif
+ NULL
+};
+
+static struct cscfg_config_desc *preload_cfgs[] = {
+#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X)
+ &afdo_etm4x,
+#endif
+ NULL
+};
+
+/* preload called on initialisation */
+int cscfg_preload(void)
+{
+ return cscfg_load_config_sets(preload_cfgs, preload_feats);
+}
diff --git a/drivers/hwtracing/coresight/coresight-cfg-preload.h b/drivers/hwtracing/coresight/coresight-cfg-preload.h
new file mode 100644
index 000000000000..21299e175477
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-cfg-preload.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(C) 2020 Linaro Limited. All rights reserved.
+ * Author: Mike Leach <mike.leach@linaro.org>
+ */
+
+/* declare preloaded configurations and features */
+
+/* from coresight-cfg-afdo.c - etm 4x features */
+#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X)
+extern struct cscfg_feature_desc strobe_etm4x;
+extern struct cscfg_config_desc afdo_etm4x;
+#endif
diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c
new file mode 100644
index 000000000000..4723bf7402a2
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-config.c
@@ -0,0 +1,272 @@
+// 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"
+
+/*
+ * This provides a set of generic functions that operate on configurations
+ * and features to manage the handling of parameters, the programming and
+ * saving of registers used by features on devices.
+ */
+
+/*
+ * Write the value held in the register structure into the driver internal memory
+ * location.
+ */
+static void cscfg_set_reg(struct cscfg_regval_csdev *reg_csdev)
+{
+ u32 *p_val32 = (u32 *)reg_csdev->driver_regval;
+ u32 tmp32 = reg_csdev->reg_desc.val32;
+
+ if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT) {
+ *((u64 *)reg_csdev->driver_regval) = reg_csdev->reg_desc.val64;
+ return;
+ }
+
+ if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_MASK) {
+ tmp32 = *p_val32;
+ tmp32 &= ~reg_csdev->reg_desc.mask32;
+ tmp32 |= reg_csdev->reg_desc.val32 & reg_csdev->reg_desc.mask32;
+ }
+ *p_val32 = tmp32;
+}
+
+/*
+ * Read the driver value into the reg if this is marked as one we want to save.
+ */
+static void cscfg_save_reg(struct cscfg_regval_csdev *reg_csdev)
+{
+ if (!(reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_SAVE))
+ return;
+ if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT)
+ reg_csdev->reg_desc.val64 = *(u64 *)(reg_csdev->driver_regval);
+ else
+ reg_csdev->reg_desc.val32 = *(u32 *)(reg_csdev->driver_regval);
+}
+
+/*
+ * Some register values are set from parameters. Initialise these registers
+ * from the current parameter values.
+ */
+static void cscfg_init_reg_param(struct cscfg_feature_csdev *feat_csdev,
+ struct cscfg_regval_desc *reg_desc,
+ struct cscfg_regval_csdev *reg_csdev)
+{
+ struct cscfg_parameter_csdev *param_csdev;
+
+ /* for param, load routines have validated the index */
+ param_csdev = &feat_csdev->params_csdev[reg_desc->param_idx];
+ param_csdev->reg_csdev = reg_csdev;
+ param_csdev->val64 = reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT;
+
+ if (param_csdev->val64)
+ reg_csdev->reg_desc.val64 = param_csdev->current_value;
+ else
+ reg_csdev->reg_desc.val32 = (u32)param_csdev->current_value;
+}
+
+/* set values into the driver locations referenced in cscfg_reg_csdev */
+static int cscfg_set_on_enable(struct cscfg_feature_csdev *feat_csdev)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(feat_csdev->drv_spinlock, flags);
+ for (i = 0; i < feat_csdev->nr_regs; i++)
+ cscfg_set_reg(&feat_csdev->regs_csdev[i]);
+ spin_unlock_irqrestore(feat_csdev->drv_spinlock, flags);
+ dev_dbg(&feat_csdev->csdev->dev, "Feature %s: %s",
+ feat_csdev->feat_desc->name, "set on enable");
+ return 0;
+}
+
+/* copy back values from the driver locations referenced in cscfg_reg_csdev */
+static void cscfg_save_on_disable(struct cscfg_feature_csdev *feat_csdev)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(feat_csdev->drv_spinlock, flags);
+ for (i = 0; i < feat_csdev->nr_regs; i++)
+ cscfg_save_reg(&feat_csdev->regs_csdev[i]);
+ spin_unlock_irqrestore(feat_csdev->drv_spinlock, flags);
+ dev_dbg(&feat_csdev->csdev->dev, "Feature %s: %s",
+ feat_csdev->feat_desc->name, "save on disable");
+}
+
+/* default reset - restore default values */
+void cscfg_reset_feat(struct cscfg_feature_csdev *feat_csdev)
+{
+ struct cscfg_regval_desc *reg_desc;
+ struct cscfg_regval_csdev *reg_csdev;
+ int i;
+
+ /*
+ * set the default values for all parameters and regs from the
+ * relevant static descriptors.
+ */
+ for (i = 0; i < feat_csdev->nr_params; i++)
+ feat_csdev->params_csdev[i].current_value =
+ feat_csdev->feat_desc->params_desc[i].value;
+
+ for (i = 0; i < feat_csdev->nr_regs; i++) {
+ reg_desc = &feat_csdev->feat_desc->regs_desc[i];
+ reg_csdev = &feat_csdev->regs_csdev[i];
+ reg_csdev->reg_desc.type = reg_desc->type;
+
+ /* check if reg set from a parameter otherwise desc default */
+ if (reg_desc->type & CS_CFG_REG_TYPE_VAL_PARAM)
+ cscfg_init_reg_param(feat_csdev, reg_desc, reg_csdev);
+ else
+ /*
+ * for normal values the union between val64 & val32 + mask32
+ * allows us to init using the 64 bit value
+ */
+ reg_csdev->reg_desc.val64 = reg_desc->val64;
+ }
+}
+
+/*
+ * For the selected presets, we set the register associated with the parameter, to
+ * the value of the preset index associated with the parameter.
+ */
+static int cscfg_update_presets(struct cscfg_config_csdev *config_csdev, int preset)
+{
+ int i, j, val_idx = 0, nr_cfg_params;
+ struct cscfg_parameter_csdev *param_csdev;
+ struct cscfg_feature_csdev *feat_csdev;
+ const struct cscfg_config_desc *config_desc = config_csdev->config_desc;
+ const char *name;
+ const u64 *preset_base;
+ u64 val;
+
+ /* preset in range 1 to nr_presets */
+ if (preset < 1 || preset > config_desc->nr_presets)
+ return -EINVAL;
+ /*
+ * Go through the array of features, assigning preset values to
+ * feature parameters in the order they appear.
+ * There should be precisely the same number of preset values as the
+ * sum of number of parameters over all the features - but we will
+ * ensure there is no overrun.
+ */
+ nr_cfg_params = config_desc->nr_total_params;
+ preset_base = &config_desc->presets[(preset - 1) * nr_cfg_params];
+ for (i = 0; i < config_csdev->nr_feat; i++) {
+ feat_csdev = config_csdev->feats_csdev[i];
+ if (!feat_csdev->nr_params)
+ continue;
+
+ for (j = 0; j < feat_csdev->nr_params; j++) {
+ param_csdev = &feat_csdev->params_csdev[j];
+ name = feat_csdev->feat_desc->params_desc[j].name;
+ val = preset_base[val_idx++];
+ if (param_csdev->val64) {
+ dev_dbg(&config_csdev->csdev->dev,
+ "set param %s (%lld)", name, val);
+ param_csdev->reg_csdev->reg_desc.val64 = val;
+ } else {
+ param_csdev->reg_csdev->reg_desc.val32 = (u32)val;
+ dev_dbg(&config_csdev->csdev->dev,
+ "set param %s (%d)", name, (u32)val);
+ }
+ }
+
+ /* exit early if all params filled */
+ if (val_idx >= nr_cfg_params)
+ break;
+ }
+ return 0;
+}
+
+/*
+ * if we are not using a preset, then need to update the feature params
+ * with current values. This sets the register associated with the parameter
+ * with the current value of that parameter.
+ */
+static int cscfg_update_curr_params(struct cscfg_config_csdev *config_csdev)
+{
+ int i, j;
+ struct cscfg_feature_csdev *feat_csdev;
+ struct cscfg_parameter_csdev *param_csdev;
+ const char *name;
+ u64 val;
+
+ for (i = 0; i < config_csdev->nr_feat; i++) {
+ feat_csdev = config_csdev->feats_csdev[i];
+ if (!feat_csdev->nr_params)
+ continue;
+ for (j = 0; j < feat_csdev->nr_params; j++) {
+ param_csdev = &feat_csdev->params_csdev[j];
+ name = feat_csdev->feat_desc->params_desc[j].name;
+ val = param_csdev->current_value;
+ if (param_csdev->val64) {
+ dev_dbg(&config_csdev->csdev->dev,
+ "set param %s (%lld)", name, val);
+ param_csdev->reg_csdev->reg_desc.val64 = val;
+ } else {
+ param_csdev->reg_csdev->reg_desc.val32 = (u32)val;
+ dev_dbg(&config_csdev->csdev->dev,
+ "set param %s (%d)", name, (u32)val);
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * Configuration values will be programmed into the driver locations if enabling, or read
+ * from relevant locations on disable.
+ */
+static int cscfg_prog_config(struct cscfg_config_csdev *config_csdev, bool enable)
+{
+ int i, err = 0;
+ struct cscfg_feature_csdev *feat_csdev;
+ struct coresight_device *csdev;
+
+ for (i = 0; i < config_csdev->nr_feat; i++) {
+ feat_csdev = config_csdev->feats_csdev[i];
+ csdev = feat_csdev->csdev;
+ dev_dbg(&csdev->dev, "cfg %s; %s feature:%s", config_csdev->config_desc->name,
+ enable ? "enable" : "disable", feat_csdev->feat_desc->name);
+
+ if (enable)
+ err = cscfg_set_on_enable(feat_csdev);
+ else
+ cscfg_save_on_disable(feat_csdev);
+
+ if (err)
+ break;
+ }
+ return err;
+}
+
+/*
+ * Enable configuration for the device. Will result in the internal driver data
+ * being updated ready for programming into the device.
+ *
+ * @config_csdev: config_csdev to set.
+ * @preset: preset values to use - 0 for default.
+ */
+int cscfg_csdev_enable_config(struct cscfg_config_csdev *config_csdev, int preset)
+{
+ int err = 0;
+
+ if (preset)
+ err = cscfg_update_presets(config_csdev, preset);
+ else
+ err = cscfg_update_curr_params(config_csdev);
+ if (!err)
+ err = cscfg_prog_config(config_csdev, true);
+ return err;
+}
+
+void cscfg_csdev_disable_config(struct cscfg_config_csdev *config_csdev)
+{
+ cscfg_prog_config(config_csdev, false);
+}
diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h
new file mode 100644
index 000000000000..25eb6c632692
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-config.h
@@ -0,0 +1,253 @@
+/* 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
+
+#include <linux/coresight.h>
+#include <linux/types.h>
+
+/* CoreSight Configuration Management - component and system wide configuration */
+
+/*
+ * Register type flags for register value descriptor:
+ * describe how the value is interpreted, and handled.
+ */
+#define CS_CFG_REG_TYPE_STD 0x80 /* reg is standard reg */
+#define CS_CFG_REG_TYPE_RESOURCE 0x40 /* reg is a resource */
+#define CS_CFG_REG_TYPE_VAL_PARAM 0x08 /* reg value uses param */
+#define CS_CFG_REG_TYPE_VAL_MASK 0x04 /* reg value bit masked */
+#define CS_CFG_REG_TYPE_VAL_64BIT 0x02 /* reg value 64 bit */
+#define CS_CFG_REG_TYPE_VAL_SAVE 0x01 /* reg value save on disable */
+
+/*
+ * flags defining what device class a feature will match to when processing a
+ * system configuration - used by config data and devices.
+ */
+#define CS_CFG_MATCH_CLASS_SRC_ALL 0x0001 /* match any source */
+#define CS_CFG_MATCH_CLASS_SRC_ETM4 0x0002 /* match any ETMv4 device */
+
+/* flags defining device instance matching - used in config match desc data. */
+#define CS_CFG_MATCH_INST_ANY 0x80000000 /* any instance of a class */
+
+/*
+ * Limit number of presets in a configuration
+ * This is related to the number of bits (4) we use to select the preset on
+ * the perf command line. Preset 0 is always none selected.
+ * See PMU_FORMAT_ATTR(preset, "config:0-3") in coresight-etm-perf.c
+ */
+#define CS_CFG_CONFIG_PRESET_MAX 15
+
+/**
+ * Parameter descriptor for a device feature.
+ *
+ * @name: Name of parameter.
+ * @value: Initial or default value.
+ */
+struct cscfg_parameter_desc {
+ const char *name;
+ u64 value;
+};
+
+/**
+ * Representation of register value and a descriptor of register usage.
+ *
+ * Used as a descriptor in the feature descriptors.
+ * Used as a value in when in a feature loading into a csdev.
+ *
+ * Supports full 64 bit register value, or 32 bit value with optional mask
+ * value.
+ *
+ * @type: define register usage and interpretation.
+ * @offset: the address offset for register in the hardware device (per device specification).
+ * @hw_info: optional hardware device type specific information. (ETM / CTI specific etc)
+ * @val64: 64 bit value.
+ * @val32: 32 bit value.
+ * @mask32: 32 bit mask when using 32 bit value to access device register - if mask type.
+ * @param_idx: parameter index value into parameter array if param type.
+ */
+struct cscfg_regval_desc {
+ struct {
+ u32 type:8;
+ u32 offset:12;
+ u32 hw_info:12;
+ };
+ union {
+ u64 val64;
+ struct {
+ u32 val32;
+ u32 mask32;
+ };
+ u32 param_idx;
+ };
+};
+
+/**
+ * Device feature descriptor - combination of registers and parameters to
+ * program a device to implement a specific complex function.
+ *
+ * @name: feature name.
+ * @description: brief description of the feature.
+ * @item: List entry.
+ * @match_flags: matching information if loading into a device
+ * @nr_params: number of parameters used.
+ * @params_desc: array of parameters used.
+ * @nr_regs: number of registers used.
+ * @regs_desc: array of registers used.
+ */
+struct cscfg_feature_desc {
+ const char *name;
+ const char *description;
+ struct list_head item;
+ u32 match_flags;
+ int nr_params;
+ struct cscfg_parameter_desc *params_desc;
+ int nr_regs;
+ struct cscfg_regval_desc *regs_desc;
+};
+
+/**
+ * Configuration descriptor - describes selectable system configuration.
+ *
+ * A configuration describes device features in use, and may provide preset
+ * values for the parameters in those features.
+ *
+ * A single set of presets is the sum of the parameters declared by
+ * all the features in use - this value is @nr_total_params.
+ *
+ * @name: name of the configuration - used for selection.
+ * @description: description of the purpose of the configuration.
+ * @item: list entry.
+ * @nr_feat_refs: Number of features used in this configuration.
+ * @feat_ref_names: references to features used in this configuration.
+ * @nr_presets: Number of sets of presets supplied by this configuration.
+ * @nr_total_params: Sum of all parameters declared by used features
+ * @presets: Array of preset values.
+ * @event_ea: Extended attribute for perf event value
+ * @active_cnt: ref count for activate on this configuration.
+ *
+ */
+struct cscfg_config_desc {
+ const char *name;
+ const char *description;
+ struct list_head item;
+ int nr_feat_refs;
+ const char **feat_ref_names;
+ int nr_presets;
+ int nr_total_params;
+ const u64 *presets; /* nr_presets * nr_total_params */
+ struct dev_ext_attribute *event_ea;
+ atomic_t active_cnt;
+};
+
+/**
+ * config register instance - part of a loaded feature.
+ * maps register values to csdev driver structures
+ *
+ * @reg_desc: value to use when setting feature on device / store for
+ * readback of volatile values.
+ * @driver_regval: pointer to internal driver element used to set the value
+ * in hardware.
+ */
+struct cscfg_regval_csdev {
+ struct cscfg_regval_desc reg_desc;
+ void *driver_regval;
+};
+
+/**
+ * config parameter instance - part of a loaded feature.
+ *
+ * @feat_csdev: parent feature
+ * @reg_csdev: register value updated by this parameter.
+ * @current_value: current value of parameter - may be set by user via
+ * sysfs, or modified during device operation.
+ * @val64: true if 64 bit value
+ */
+struct cscfg_parameter_csdev {
+ struct cscfg_feature_csdev *feat_csdev;
+ struct cscfg_regval_csdev *reg_csdev;
+ u64 current_value;
+ bool val64;
+};
+
+/**
+ * Feature instance loaded into a CoreSight device.
+ *
+ * When a feature is loaded into a specific device, then this structure holds
+ * the connections between the register / parameter values used and the
+ * internal data structures that are written when the feature is enabled.
+ *
+ * Since applying a feature modifies internal data structures in the device,
+ * then we have a reference to the device spinlock to protect access to these
+ * structures (@drv_spinlock).
+ *
+ * @feat_desc: pointer to the static descriptor for this feature.
+ * @csdev: parent CoreSight device instance.
+ * @node: list entry into feature list for this device.
+ * @drv_spinlock: device spinlock for access to driver register data.
+ * @nr_params: number of parameters.
+ * @params_csdev: current parameter values on this device
+ * @nr_regs: number of registers to be programmed.
+ * @regs_csdev: Programming details for the registers
+ */
+struct cscfg_feature_csdev {
+ const struct cscfg_feature_desc *feat_desc;
+ struct coresight_device *csdev;
+ struct list_head node;
+ spinlock_t *drv_spinlock;
+ int nr_params;
+ struct cscfg_parameter_csdev *params_csdev;
+ int nr_regs;
+ struct cscfg_regval_csdev *regs_csdev;
+};
+
+/**
+ * Configuration instance when loaded into a CoreSight device.
+ *
+ * The instance contains references to loaded features on this device that are
+ * used by the configuration.
+ *
+ * @config_desc:reference to the descriptor for this configuration
+ * @csdev: parent coresight device for this configuration instance.
+ * @enabled: true if configuration is enabled on this device.
+ * @node: list entry within the coresight device
+ * @nr_feat: Number of features on this device that are used in the
+ * configuration.
+ * @feats_csdev:references to the device features to enable.
+ */
+struct cscfg_config_csdev {
+ const struct cscfg_config_desc *config_desc;
+ struct coresight_device *csdev;
+ bool enabled;
+ struct list_head node;
+ int nr_feat;
+ struct cscfg_feature_csdev *feats_csdev[0];
+};
+
+/**
+ * Coresight device operations.
+ *
+ * Registered coresight devices provide these operations to manage feature
+ * instances compatible with the device hardware and drivers
+ *
+ * @load_feat: Pass a feature descriptor into the device and create the
+ * loaded feature instance (struct cscfg_feature_csdev).
+ */
+struct cscfg_csdev_feat_ops {
+ int (*load_feat)(struct coresight_device *csdev,
+ struct cscfg_feature_csdev *feat_csdev);
+};
+
+/* coresight config helper functions*/
+
+/* enable / disable config on a device - called with appropriate locks set.*/
+int cscfg_csdev_enable_config(struct cscfg_config_csdev *config_csdev, int preset);
+void cscfg_csdev_disable_config(struct cscfg_config_csdev *config_csdev);
+
+/* reset a feature to default values */
+void cscfg_reset_feat(struct cscfg_feature_csdev *feat_csdev);
+
+#endif /* _CORESIGHT_CORESIGHT_CONFIG_H */
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 1002605db8ba..8a18c71df37a 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -21,6 +21,7 @@
#include "coresight-etm-perf.h"
#include "coresight-priv.h"
+#include "coresight-syscfg.h"
static DEFINE_MUTEX(coresight_mutex);
static DEFINE_PER_CPU(struct coresight_device *, csdev_sink);
@@ -1763,13 +1764,22 @@ static int __init coresight_init(void)
ret = etm_perf_init();
if (ret)
- bus_unregister(&coresight_bustype);
+ goto exit_bus_unregister;
+ /* initialise the coresight syscfg API */
+ ret = cscfg_init();
+ if (!ret)
+ return 0;
+
+ etm_perf_exit();
+exit_bus_unregister:
+ bus_unregister(&coresight_bustype);
return ret;
}
static void __exit coresight_exit(void)
{
+ cscfg_exit();
etm_perf_exit();
bus_unregister(&coresight_bustype);
}
diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c
index 9731d3a96073..00de46565bc4 100644
--- a/drivers/hwtracing/coresight/coresight-cpu-debug.c
+++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c
@@ -588,11 +588,11 @@ static int debug_probe(struct amba_device *adev, const struct amba_id *id)
drvdata->base = base;
- get_online_cpus();
+ cpus_read_lock();
per_cpu(debug_drvdata, drvdata->cpu) = drvdata;
ret = smp_call_function_single(drvdata->cpu, debug_init_arch_data,
drvdata, 1);
- put_online_cpus();
+ cpus_read_unlock();
if (ret) {
dev_err(dev, "CPU%d debug arch init failed\n", drvdata->cpu);
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index 6f398377fec9..8ebd728d3a80 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -18,8 +18,10 @@
#include <linux/types.h>
#include <linux/workqueue.h>
+#include "coresight-config.h"
#include "coresight-etm-perf.h"
#include "coresight-priv.h"
+#include "coresight-syscfg.h"
static struct pmu etm_pmu;
static bool etm_perf_up;
@@ -57,8 +59,13 @@ PMU_FORMAT_ATTR(contextid1, "config:" __stringify(ETM_OPT_CTXTID));
PMU_FORMAT_ATTR(contextid2, "config:" __stringify(ETM_OPT_CTXTID2));
PMU_FORMAT_ATTR(timestamp, "config:" __stringify(ETM_OPT_TS));
PMU_FORMAT_ATTR(retstack, "config:" __stringify(ETM_OPT_RETSTK));
+/* preset - if sink ID is used as a configuration selector */
+PMU_FORMAT_ATTR(preset, "config:0-3");
/* Sink ID - same for all ETMs */
PMU_FORMAT_ATTR(sinkid, "config2:0-31");
+/* config ID - set if a system configuration is selected */
+PMU_FORMAT_ATTR(configid, "config2:32-63");
+
/*
* contextid always traces the "PID". The PID is in CONTEXTIDR_EL1
@@ -88,6 +95,8 @@ static struct attribute *etm_config_formats_attr[] = {
&format_attr_timestamp.attr,
&format_attr_retstack.attr,
&format_attr_sinkid.attr,
+ &format_attr_preset.attr,
+ &format_attr_configid.attr,
NULL,
};
@@ -105,9 +114,19 @@ static const struct attribute_group etm_pmu_sinks_group = {
.attrs = etm_config_sinks_attr,
};
+static struct attribute *etm_config_events_attr[] = {
+ NULL,
+};
+
+static const struct attribute_group etm_pmu_events_group = {
+ .name = "events",
+ .attrs = etm_config_events_attr,
+};
+
static const struct attribute_group *etm_pmu_attr_groups[] = {
&etm_pmu_format_group,
&etm_pmu_sinks_group,
+ &etm_pmu_events_group,
NULL,
};
@@ -196,6 +215,10 @@ static void free_event_data(struct work_struct *work)
/* Free the sink buffers, if there are any */
free_sink_buffer(event_data);
+ /* clear any configuration we were using */
+ if (event_data->cfg_hash)
+ cscfg_deactivate_config(event_data->cfg_hash);
+
for_each_cpu(cpu, mask) {
struct list_head **ppath;
@@ -273,7 +296,7 @@ static bool sinks_compatible(struct coresight_device *a,
static void *etm_setup_aux(struct perf_event *event, void **pages,
int nr_pages, bool overwrite)
{
- u32 id;
+ u32 id, cfg_hash;
int cpu = event->cpu;
cpumask_t *mask;
struct coresight_device *sink = NULL;
@@ -286,11 +309,19 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
INIT_WORK(&event_data->work, free_event_data);
/* First get the selected sink from user space. */
- if (event->attr.config2) {
+ if (event->attr.config2 & GENMASK_ULL(31, 0)) {
id = (u32)event->attr.config2;
sink = user_sink = coresight_get_sink_by_id(id);
}
+ /* check if user wants a coresight configuration selected */
+ cfg_hash = (u32)((event->attr.config2 & GENMASK_ULL(63, 32)) >> 32);
+ if (cfg_hash) {
+ if (cscfg_activate_config(cfg_hash))
+ goto err;
+ event_data->cfg_hash = cfg_hash;
+ }
+
mask = &event_data->mask;
/*
@@ -658,68 +689,127 @@ static ssize_t etm_perf_sink_name_show(struct device *dev,
return scnprintf(buf, PAGE_SIZE, "0x%lx\n", (unsigned long)(ea->var));
}
-int etm_perf_add_symlink_sink(struct coresight_device *csdev)
+static struct dev_ext_attribute *
+etm_perf_add_symlink_group(struct device *dev, const char *name, const char *group_name)
{
- int ret;
+ struct dev_ext_attribute *ea;
unsigned long hash;
- const char *name;
+ int ret;
struct device *pmu_dev = etm_pmu.dev;
- struct device *dev = &csdev->dev;
- struct dev_ext_attribute *ea;
-
- if (csdev->type != CORESIGHT_DEV_TYPE_SINK &&
- csdev->type != CORESIGHT_DEV_TYPE_LINKSINK)
- return -EINVAL;
-
- if (csdev->ea != NULL)
- return -EINVAL;
if (!etm_perf_up)
- return -EPROBE_DEFER;
+ return ERR_PTR(-EPROBE_DEFER);
ea = devm_kzalloc(dev, sizeof(*ea), GFP_KERNEL);
if (!ea)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
- name = dev_name(dev);
- /* See function coresight_get_sink_by_id() to know where this is used */
+ /*
+ * If this function is called adding a sink then the hash is used for
+ * sink selection - see function coresight_get_sink_by_id().
+ * If adding a configuration then the hash is used for selection in
+ * cscfg_activate_config()
+ */
hash = hashlen_hash(hashlen_string(NULL, name));
sysfs_attr_init(&ea->attr.attr);
ea->attr.attr.name = devm_kstrdup(dev, name, GFP_KERNEL);
if (!ea->attr.attr.name)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
ea->attr.attr.mode = 0444;
- ea->attr.show = etm_perf_sink_name_show;
ea->var = (unsigned long *)hash;
ret = sysfs_add_file_to_group(&pmu_dev->kobj,
- &ea->attr.attr, "sinks");
+ &ea->attr.attr, group_name);
- if (!ret)
- csdev->ea = ea;
+ return ret ? ERR_PTR(ret) : ea;
+}
- return ret;
+int etm_perf_add_symlink_sink(struct coresight_device *csdev)
+{
+ const char *name;
+ struct device *dev = &csdev->dev;
+ int err = 0;
+
+ if (csdev->type != CORESIGHT_DEV_TYPE_SINK &&
+ csdev->type != CORESIGHT_DEV_TYPE_LINKSINK)
+ return -EINVAL;
+
+ if (csdev->ea != NULL)
+ return -EINVAL;
+
+ name = dev_name(dev);
+ csdev->ea = etm_perf_add_symlink_group(dev, name, "sinks");
+ if (IS_ERR(csdev->ea)) {
+ err = PTR_ERR(csdev->ea);
+ csdev->ea = NULL;
+ } else
+ csdev->ea->attr.show = etm_perf_sink_name_show;
+
+ return err;
}
-void etm_perf_del_symlink_sink(struct coresight_device *csdev)
+static void etm_perf_del_symlink_group(struct dev_ext_attribute *ea, const char *group_name)
{
struct device *pmu_dev = etm_pmu.dev;
- struct dev_ext_attribute *ea = csdev->ea;
+ sysfs_remove_file_from_group(&pmu_dev->kobj,
+ &ea->attr.attr, group_name);
+}
+
+void etm_perf_del_symlink_sink(struct coresight_device *csdev)
+{
if (csdev->type != CORESIGHT_DEV_TYPE_SINK &&
csdev->type != CORESIGHT_DEV_TYPE_LINKSINK)
return;
- if (!ea)
+ if (!csdev->ea)
return;
- sysfs_remove_file_from_group(&pmu_dev->kobj,
- &ea->attr.attr, "sinks");
+ etm_perf_del_symlink_group(csdev->ea, "sinks");
csdev->ea = NULL;
}
+static ssize_t etm_perf_cscfg_event_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct dev_ext_attribute *ea;
+
+ ea = container_of(dattr, struct dev_ext_attribute, attr);
+ return scnprintf(buf, PAGE_SIZE, "configid=0x%lx\n", (unsigned long)(ea->var));
+}
+
+int etm_perf_add_symlink_cscfg(struct device *dev, struct cscfg_config_desc *config_desc)
+{
+ int err = 0;
+
+ if (config_desc->event_ea != NULL)
+ return 0;
+
+ config_desc->event_ea = etm_perf_add_symlink_group(dev, config_desc->name, "events");
+
+ /* set the show function to the custom cscfg event */
+ if (!IS_ERR(config_desc->event_ea))
+ config_desc->event_ea->attr.show = etm_perf_cscfg_event_show;
+ else {
+ err = PTR_ERR(config_desc->event_ea);
+ config_desc->event_ea = NULL;
+ }
+
+ return err;
+}
+
+void etm_perf_del_symlink_cscfg(struct cscfg_config_desc *config_desc)
+{
+ if (!config_desc->event_ea)
+ return;
+
+ etm_perf_del_symlink_group(config_desc->event_ea, "events");
+ config_desc->event_ea = NULL;
+}
+
int __init etm_perf_init(void)
{
int ret;
@@ -748,7 +838,7 @@ int __init etm_perf_init(void)
return ret;
}
-void __exit etm_perf_exit(void)
+void etm_perf_exit(void)
{
perf_pmu_unregister(&etm_pmu);
}
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h
index 3e4f2ad5e193..468f7799ab4f 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.h
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.h
@@ -11,6 +11,7 @@
#include "coresight-priv.h"
struct coresight_device;
+struct cscfg_config_desc;
/*
* In both ETMv3 and v4 the maximum number of address comparator implentable
@@ -48,12 +49,14 @@ struct etm_filters {
* @work: Handle to free allocated memory outside IRQ context.
* @mask: Hold the CPU(s) this event was set for.
* @snk_config: The sink configuration.
+ * @cfg_hash: The hash id of any coresight config selected.
* @path: An array of path, each slot for one CPU.
*/
struct etm_event_data {
struct work_struct work;
cpumask_t mask;
void *snk_config;
+ u32 cfg_hash;
struct list_head * __percpu *path;
};
@@ -69,6 +72,9 @@ static inline void *etm_perf_sink_config(struct perf_output_handle *handle)
return data->snk_config;
return NULL;
}
+int etm_perf_add_symlink_cscfg(struct device *dev,
+ struct cscfg_config_desc *config_desc);
+void etm_perf_del_symlink_cscfg(struct cscfg_config_desc *config_desc);
#else
static inline int etm_perf_symlink(struct coresight_device *csdev, bool link)
{ return -EINVAL; }
@@ -79,10 +85,14 @@ static inline void *etm_perf_sink_config(struct perf_output_handle *handle)
{
return NULL;
}
+int etm_perf_add_symlink_cscfg(struct device *dev,
+ struct cscfg_config_desc *config_desc)
+{ return -EINVAL; }
+void etm_perf_del_symlink_cscfg(struct cscfg_config_desc *config_desc) {}
#endif /* CONFIG_CORESIGHT */
int __init etm_perf_init(void);
-void __exit etm_perf_exit(void);
+void etm_perf_exit(void);
#endif
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
new file mode 100644
index 000000000000..d2ea903231b2
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
@@ -0,0 +1,182 @@
+// 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"
+#include "coresight-syscfg.h"
+
+/* defines to associate register IDs with driver data locations */
+#define CHECKREG(cval, elem) \
+ { \
+ if (offset == cval) { \
+ reg_csdev->driver_regval = &drvcfg->elem; \
+ err = 0; \
+ break; \
+ } \
+ }
+
+#define CHECKREGIDX(cval, elem, off_idx, mask) \
+ { \
+ if (mask == cval) { \
+ reg_csdev->driver_regval = &drvcfg->elem[off_idx]; \
+ err = 0; \
+ break; \
+ } \
+ }
+
+/**
+ * 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_regval_csdev *reg_csdev, u32 offset)
+{
+ int err = -EINVAL, idx;
+ struct etmv4_config *drvcfg = &drvdata->config;
+ u32 off_mask;
+
+ 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_csdev->driver_regval = &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_csdev->driver_regval = &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_csdev)
+{
+ struct device *dev = csdev->dev.parent;
+ struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
+ const struct cscfg_feature_desc *feat_desc = feat_csdev->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->drv_spinlock = &drvdata->spinlock;
+
+ /* process the register descriptions */
+ for (i = 0; i < feat_csdev->nr_regs && !err; i++) {
+ offset = feat_desc->regs_desc[i].offset;
+ err = etm4_cfg_map_reg_offset(drvdata, &feat_csdev->regs_csdev[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)
+{
+ struct cscfg_csdev_feat_ops ops;
+
+ ops.load_feat = &etm4_cfg_load_feature;
+
+ return cscfg_register_csdev(csdev, CS_CFG_ETM4_MATCH_FLAGS, &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..32dab34c1dac
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _CORESIGHT_ETM4X_CFG_H
+#define _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
+
+/* ETMv4 specific config functions */
+int etm4_cscfg_register(struct coresight_device *csdev);
+
+#endif /* CORESIGHT_ETM4X_CFG_H */
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index da27cd4a3c38..e24252eaf8e4 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -39,6 +39,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);
@@ -561,12 +563,15 @@ out:
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_hash;
+ int preset;
/* Clear configuration from previous run */
memset(config, 0, sizeof(struct etmv4_config));
@@ -632,6 +637,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_hash = (u32)(attr->config2 >> 32);
+ preset = attr->config & 0xF;
+ ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset);
+ }
+
out:
return ret;
}
@@ -648,7 +667,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 */
@@ -794,11 +813,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.
@@ -1939,6 +1965,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);
+ if (ret) {
+ coresight_unregister(drvdata->csdev);
+ return ret;
+ }
+
etmdrvdata[drvdata->cpu] = drvdata;
dev_info(&drvdata->csdev->dev, "CPU%d: %s v%d.%d initialized\n",
@@ -2025,6 +2058,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 007bad9e7ad8..a0640fa5c55b 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);
diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c
new file mode 100644
index 000000000000..c547816b9000
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c
@@ -0,0 +1,396 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Linaro Limited, All rights reserved.
+ * Author: Mike Leach <mike.leach@linaro.org>
+ */
+
+#include <linux/configfs.h>
+
+#include "coresight-syscfg-configfs.h"
+
+/* create a default ci_type. */
+static inline struct config_item_type *cscfg_create_ci_type(void)
+{
+ struct config_item_type *ci_type;
+
+ ci_type = devm_kzalloc(cscfg_device(), sizeof(*ci_type), GFP_KERNEL);
+ if (ci_type)
+ ci_type->ct_owner = THIS_MODULE;
+
+ return ci_type;
+}
+
+/* configurations sub-group */
+
+/* attributes for the config view group */
+static ssize_t cscfg_cfg_description_show(struct config_item *item, char *page)
+{
+ struct cscfg_fs_config *fs_config = container_of(to_config_group(item),
+ struct cscfg_fs_config, group);
+
+ return scnprintf(page, PAGE_SIZE, "%s", fs_config->config_desc->description);
+}
+CONFIGFS_ATTR_RO(cscfg_cfg_, description);
+
+static ssize_t cscfg_cfg_feature_refs_show(struct config_item *item, char *page)
+{
+ struct cscfg_fs_config *fs_config = container_of(to_config_group(item),
+ struct cscfg_fs_config, group);
+ const struct cscfg_config_desc *config_desc = fs_config->config_desc;
+ ssize_t ch_used = 0;
+ int i;
+
+ for (i = 0; i < config_desc->nr_feat_refs; i++)
+ ch_used += scnprintf(page + ch_used, PAGE_SIZE - ch_used,
+ "%s\n", config_desc->feat_ref_names[i]);
+ return ch_used;
+}
+CONFIGFS_ATTR_RO(cscfg_cfg_, feature_refs);
+
+/* list preset values in order of features and params */
+static ssize_t cscfg_cfg_values_show(struct config_item *item, char *page)
+{
+ const struct cscfg_feature_desc *feat_desc;
+ const struct cscfg_config_desc *config_desc;
+ struct cscfg_fs_preset *fs_preset;
+ int i, j, val_idx, preset_idx;
+ ssize_t used = 0;
+
+ fs_preset = container_of(to_config_group(item), struct cscfg_fs_preset, group);
+ config_desc = fs_preset->config_desc;
+
+ if (!config_desc->nr_presets)
+ return 0;
+
+ preset_idx = fs_preset->preset_num - 1;
+
+ /* start index on the correct array line */
+ val_idx = config_desc->nr_total_params * preset_idx;
+
+ /*
+ * A set of presets is the sum of all params in used features,
+ * in order of declaration of features and params in the features
+ */
+ for (i = 0; i < config_desc->nr_feat_refs; i++) {
+ feat_desc = cscfg_get_named_feat_desc(config_desc->feat_ref_names[i]);
+ for (j = 0; j < feat_desc->nr_params; j++) {
+ used += scnprintf(page + used, PAGE_SIZE - used,
+ "%s.%s = 0x%llx ",
+ feat_desc->name,
+ feat_desc->params_desc[j].name,
+ config_desc->presets[val_idx++]);
+ }
+ }
+ used += scnprintf(page + used, PAGE_SIZE - used, "\n");
+
+ return used;
+}
+CONFIGFS_ATTR_RO(cscfg_cfg_, values);
+
+static struct configfs_attribute *cscfg_config_view_attrs[] = {
+ &cscfg_cfg_attr_description,
+ &cscfg_cfg_attr_feature_refs,
+ NULL,
+};
+
+static struct config_item_type cscfg_config_view_type = {
+ .ct_owner = THIS_MODULE,
+ .ct_attrs = cscfg_config_view_attrs,
+};
+
+static struct configfs_attribute *cscfg_config_preset_attrs[] = {
+ &cscfg_cfg_attr_values,
+ NULL,
+};
+
+static struct config_item_type cscfg_config_preset_type = {
+ .ct_owner = THIS_MODULE,
+ .ct_attrs = cscfg_config_preset_attrs,
+};
+
+static int cscfg_add_preset_groups(struct cscfg_fs_config *cfg_view)
+{
+ int preset_num;
+ struct cscfg_fs_preset *cfg_fs_preset;
+ struct cscfg_config_desc *config_desc = cfg_view->config_desc;
+ char name[CONFIGFS_ITEM_NAME_LEN];
+
+ if (!config_desc->nr_presets)
+ return 0;
+
+ for (preset_num = 1; preset_num <= config_desc->nr_presets; preset_num++) {
+ cfg_fs_preset = devm_kzalloc(cscfg_device(),
+ sizeof(struct cscfg_fs_preset), GFP_KERNEL);
+
+ if (!cfg_fs_preset)
+ return -ENOMEM;
+
+ snprintf(name, CONFIGFS_ITEM_NAME_LEN, "preset%d", preset_num);
+ cfg_fs_preset->preset_num = preset_num;
+ cfg_fs_preset->config_desc = cfg_view->config_desc;
+ config_group_init_type_name(&cfg_fs_preset->group, name,
+ &cscfg_config_preset_type);
+ configfs_add_default_group(&cfg_fs_preset->group, &cfg_view->group);
+ }
+ return 0;
+}
+
+static struct config_group *cscfg_create_config_group(struct cscfg_config_desc *config_desc)
+{
+ struct cscfg_fs_config *cfg_view;
+ struct device *dev = cscfg_device();
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+
+ cfg_view = devm_kzalloc(dev, sizeof(struct cscfg_fs_config), GFP_KERNEL);
+ if (!cfg_view)
+ return ERR_PTR(-ENOMEM);
+
+ cfg_view->config_desc = config_desc;
+ config_group_init_type_name(&cfg_view->group, config_desc->name, &cscfg_config_view_type);
+
+ /* add in a preset<n> dir for each preset */
+ err = cscfg_add_preset_groups(cfg_view);
+ if (err)
+ return ERR_PTR(err);
+
+ return &cfg_view->group;
+}
+
+/* attributes for features view */
+
+static ssize_t cscfg_feat_description_show(struct config_item *item, char *page)
+{
+ struct cscfg_fs_feature *fs_feat = container_of(to_config_group(item),
+ struct cscfg_fs_feature, group);
+
+ return scnprintf(page, PAGE_SIZE, "%s", fs_feat->feat_desc->description);
+}
+CONFIGFS_ATTR_RO(cscfg_feat_, description);
+
+static ssize_t cscfg_feat_matches_show(struct config_item *item, char *page)
+{
+ struct cscfg_fs_feature *fs_feat = container_of(to_config_group(item),
+ struct cscfg_fs_feature, group);
+ u32 match_flags = fs_feat->feat_desc->match_flags;
+ int used = 0;
+
+ if (match_flags & CS_CFG_MATCH_CLASS_SRC_ALL)
+ used = scnprintf(page, PAGE_SIZE, "SRC_ALL ");
+
+ if (match_flags & CS_CFG_MATCH_CLASS_SRC_ETM4)
+ used += scnprintf(page + used, PAGE_SIZE - used, "SRC_ETMV4 ");
+
+ used += scnprintf(page + used, PAGE_SIZE - used, "\n");
+ return used;
+}
+CONFIGFS_ATTR_RO(cscfg_feat_, matches);
+
+static ssize_t cscfg_feat_nr_params_show(struct config_item *item, char *page)
+{
+ struct cscfg_fs_feature *fs_feat = container_of(to_config_group(item),
+ struct cscfg_fs_feature, group);
+
+ return scnprintf(page, PAGE_SIZE, "%d\n", fs_feat->feat_desc->nr_params);
+}
+CONFIGFS_ATTR_RO(cscfg_feat_, nr_params);
+
+/* base feature desc attrib structures */
+static struct configfs_attribute *cscfg_feature_view_attrs[] = {
+ &cscfg_feat_attr_description,
+ &cscfg_feat_attr_matches,
+ &cscfg_feat_attr_nr_params,
+ NULL,
+};
+
+static struct config_item_type cscfg_feature_view_type = {
+ .ct_owner = THIS_MODULE,
+ .ct_attrs = cscfg_feature_view_attrs,
+};
+
+static ssize_t cscfg_param_value_show(struct config_item *item, char *page)
+{
+ struct cscfg_fs_param *param_item = container_of(to_config_group(item),
+ struct cscfg_fs_param, group);
+ u64 value = param_item->feat_desc->params_desc[param_item->param_idx].value;
+
+ return scnprintf(page, PAGE_SIZE, "0x%llx\n", value);
+}
+
+static ssize_t cscfg_param_value_store(struct config_item *item,
+ const char *page, size_t size)
+{
+ struct cscfg_fs_param *param_item = container_of(to_config_group(item),
+ struct cscfg_fs_param, group);
+ struct cscfg_feature_desc *feat_desc = param_item->feat_desc;
+ int param_idx = param_item->param_idx;
+ u64 value;
+ int err;
+
+ err = kstrtoull(page, 0, &value);
+ if (!err)
+ err = cscfg_update_feat_param_val(feat_desc, param_idx, value);
+
+ return err ? err : size;
+}
+CONFIGFS_ATTR(cscfg_param_, value);
+
+static struct configfs_attribute *cscfg_param_view_attrs[] = {
+ &cscfg_param_attr_value,
+ NULL,
+};
+
+static struct config_item_type cscfg_param_view_type = {
+ .ct_owner = THIS_MODULE,
+ .ct_attrs = cscfg_param_view_attrs,
+};
+
+/*
+ * configfs has far less functionality provided to add attributes dynamically than sysfs,
+ * and the show and store fns pass the enclosing config_item so the actual attribute cannot
+ * be determined. Therefore we add each item as a group directory, with a value attribute.
+ */
+static int cscfg_create_params_group_items(struct cscfg_feature_desc *feat_desc,
+ struct config_group *params_group)
+{
+ struct device *dev = cscfg_device();
+ struct cscfg_fs_param *param_item;
+ int i;
+
+ /* parameter items - as groups with default_value attribute */
+ for (i = 0; i < feat_desc->nr_params; i++) {
+ param_item = devm_kzalloc(dev, sizeof(struct cscfg_fs_param), GFP_KERNEL);
+ if (!param_item)
+ return -ENOMEM;
+ param_item->feat_desc = feat_desc;
+ param_item->param_idx = i;
+ config_group_init_type_name(&param_item->group,
+ feat_desc->params_desc[i].name,
+ &cscfg_param_view_type);
+ configfs_add_default_group(&param_item->group, params_group);
+ }
+ return 0;
+}
+
+static struct config_group *cscfg_create_feature_group(struct cscfg_feature_desc *feat_desc)
+{
+ struct cscfg_fs_feature *feat_view;
+ struct config_item_type *params_group_type;
+ struct config_group *params_group = NULL;
+ struct device *dev = cscfg_device();
+ int item_err;
+
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+
+ feat_view = devm_kzalloc(dev, sizeof(struct cscfg_fs_feature), GFP_KERNEL);
+ if (!feat_view)
+ return ERR_PTR(-ENOMEM);
+
+ if (feat_desc->nr_params) {
+ params_group = devm_kzalloc(dev, sizeof(struct config_group), GFP_KERNEL);
+ if (!params_group)
+ return ERR_PTR(-ENOMEM);
+
+ params_group_type = cscfg_create_ci_type();
+ if (!params_group_type)
+ return ERR_PTR(-ENOMEM);
+ }
+
+ feat_view->feat_desc = feat_desc;
+ config_group_init_type_name(&feat_view->group,
+ feat_desc->name,
+ &cscfg_feature_view_type);
+ if (params_group) {
+ config_group_init_type_name(params_group, "params", params_group_type);
+ configfs_add_default_group(params_group, &feat_view->group);
+ item_err = cscfg_create_params_group_items(feat_desc, params_group);
+ if (item_err)
+ return ERR_PTR(item_err);
+ }
+ return &feat_view->group;
+}
+
+static struct config_item_type cscfg_configs_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group cscfg_configs_grp = {
+ .cg_item = {
+ .ci_namebuf = "configurations",
+ .ci_type = &cscfg_configs_type,
+ },
+};
+
+/* add configuration to configurations group */
+int cscfg_configfs_add_config(struct cscfg_config_desc *config_desc)
+{
+ struct config_group *new_group;
+ int err;
+
+ new_group = cscfg_create_config_group(config_desc);
+ if (IS_ERR(new_group))
+ return PTR_ERR(new_group);
+ err = configfs_register_group(&cscfg_configs_grp, new_group);
+ return err;
+}
+
+static struct config_item_type cscfg_features_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group cscfg_features_grp = {
+ .cg_item = {
+ .ci_namebuf = "features",
+ .ci_type = &cscfg_features_type,
+ },
+};
+
+/* add feature to features group */
+int cscfg_configfs_add_feature(struct cscfg_feature_desc *feat_desc)
+{
+ struct config_group *new_group;
+ int err;
+
+ new_group = cscfg_create_feature_group(feat_desc);
+ if (IS_ERR(new_group))
+ return PTR_ERR(new_group);
+ err = configfs_register_group(&cscfg_features_grp, new_group);
+ return err;
+}
+
+int cscfg_configfs_init(struct cscfg_manager *cscfg_mgr)
+{
+ struct configfs_subsystem *subsys;
+ struct config_item_type *ci_type;
+
+ if (!cscfg_mgr)
+ return -EINVAL;
+
+ ci_type = cscfg_create_ci_type();
+ if (!ci_type)
+ return -ENOMEM;
+
+ subsys = &cscfg_mgr->cfgfs_subsys;
+ config_item_set_name(&subsys->su_group.cg_item, CSCFG_FS_SUBSYS_NAME);
+ subsys->su_group.cg_item.ci_type = ci_type;
+
+ config_group_init(&subsys->su_group);
+ mutex_init(&subsys->su_mutex);
+
+ /* Add default groups to subsystem */
+ config_group_init(&cscfg_configs_grp);
+ configfs_add_default_group(&cscfg_configs_grp, &subsys->su_group);
+
+ config_group_init(&cscfg_features_grp);
+ configfs_add_default_group(&cscfg_features_grp, &subsys->su_group);
+
+ return configfs_register_subsystem(subsys);
+}
+
+void cscfg_configfs_release(struct cscfg_manager *cscfg_mgr)
+{
+ configfs_unregister_subsystem(&cscfg_mgr->cfgfs_subsys);
+}
diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h
new file mode 100644
index 000000000000..7d6ffe35ca4c
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Coresight system configuration driver - support for configfs.
+ */
+
+#ifndef CORESIGHT_SYSCFG_CONFIGFS_H
+#define CORESIGHT_SYSCFG_CONFIGFS_H
+
+#include <linux/configfs.h>
+#include "coresight-syscfg.h"
+
+#define CSCFG_FS_SUBSYS_NAME "cs-syscfg"
+
+/* container for configuration view */
+struct cscfg_fs_config {
+ struct cscfg_config_desc *config_desc;
+ struct config_group group;
+};
+
+/* container for feature view */
+struct cscfg_fs_feature {
+ struct cscfg_feature_desc *feat_desc;
+ struct config_group group;
+};
+
+/* container for parameter view */
+struct cscfg_fs_param {
+ int param_idx;
+ struct cscfg_feature_desc *feat_desc;
+ struct config_group group;
+};
+
+/* container for preset view */
+struct cscfg_fs_preset {
+ int preset_num;
+ struct cscfg_config_desc *config_desc;
+ struct config_group group;
+};
+
+int cscfg_configfs_init(struct cscfg_manager *cscfg_mgr);
+void cscfg_configfs_release(struct cscfg_manager *cscfg_mgr);
+int cscfg_configfs_add_config(struct cscfg_config_desc *config_desc);
+int cscfg_configfs_add_feature(struct cscfg_feature_desc *feat_desc);
+
+#endif /* CORESIGHT_SYSCFG_CONFIGFS_H */
diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c
new file mode 100644
index 000000000000..fc0760f55c53
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-syscfg.c
@@ -0,0 +1,847 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Linaro Limited, All rights reserved.
+ * Author: Mike Leach <mike.leach@linaro.org>
+ */
+
+#include <linux/platform_device.h>
+
+#include "coresight-config.h"
+#include "coresight-etm-perf.h"
+#include "coresight-syscfg.h"
+#include "coresight-syscfg-configfs.h"
+
+/*
+ * cscfg_ API manages configurations and features for the entire coresight
+ * infrastructure.
+ *
+ * It allows the loading of configurations and features, and loads these into
+ * coresight devices as appropriate.
+ */
+
+/* protect the cscsg_data and device */
+static DEFINE_MUTEX(cscfg_mutex);
+
+/* only one of these */
+static struct cscfg_manager *cscfg_mgr;
+
+/* load features and configuations into the lists */
+
+/* get name feature instance from a coresight device list of features */
+static struct cscfg_feature_csdev *
+cscfg_get_feat_csdev(struct coresight_device *csdev, const char *name)
+{
+ struct cscfg_feature_csdev *feat_csdev = NULL;
+
+ list_for_each_entry(feat_csdev, &csdev->feature_csdev_list, node) {
+ if (strcmp(feat_csdev->feat_desc->name, name) == 0)
+ return feat_csdev;
+ }
+ return NULL;
+}
+
+/* allocate the device config instance - with max number of used features */
+static struct cscfg_config_csdev *
+cscfg_alloc_csdev_cfg(struct coresight_device *csdev, int nr_feats)
+{
+ struct cscfg_config_csdev *config_csdev = NULL;
+ struct device *dev = csdev->dev.parent;
+
+ /* this is being allocated using the devm for the coresight device */
+ config_csdev = devm_kzalloc(dev,
+ offsetof(struct cscfg_config_csdev, feats_csdev[nr_feats]),
+ GFP_KERNEL);
+ if (!config_csdev)
+ return NULL;
+
+ config_csdev->csdev = csdev;
+ return config_csdev;
+}
+
+/* Load a config into a device if there are any feature matches between config and device */
+static int cscfg_add_csdev_cfg(struct coresight_device *csdev,
+ struct cscfg_config_desc *config_desc)
+{
+ struct cscfg_config_csdev *config_csdev = NULL;
+ struct cscfg_feature_csdev *feat_csdev;
+ unsigned long flags;
+ int i;
+
+ /* look at each required feature and see if it matches any feature on the device */
+ for (i = 0; i < config_desc->nr_feat_refs; i++) {
+ /* look for a matching name */
+ feat_csdev = cscfg_get_feat_csdev(csdev, config_desc->feat_ref_names[i]);
+ if (feat_csdev) {
+ /*
+ * At least one feature on this device matches the config
+ * add a config instance to the device and a reference to the feature.
+ */
+ if (!config_csdev) {
+ config_csdev = cscfg_alloc_csdev_cfg(csdev,
+ config_desc->nr_feat_refs);
+ if (!config_csdev)
+ return -ENOMEM;
+ config_csdev->config_desc = config_desc;
+ }
+ config_csdev->feats_csdev[config_csdev->nr_feat++] = feat_csdev;
+ }
+ }
+ /* if matched features, add config to device.*/
+ if (config_csdev) {
+ spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
+ list_add(&config_csdev->node, &csdev->config_csdev_list);
+ spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
+ }
+
+ return 0;
+}
+
+/*
+ * Add the config to the set of registered devices - call with mutex locked.
+ * Iterates through devices - any device that matches one or more of the
+ * configuration features will load it, the others will ignore it.
+ */
+static int cscfg_add_cfg_to_csdevs(struct cscfg_config_desc *config_desc)
+{
+ struct cscfg_registered_csdev *csdev_item;
+ int err;
+
+ list_for_each_entry(csdev_item, &cscfg_mgr->csdev_desc_list, item) {
+ err = cscfg_add_csdev_cfg(csdev_item->csdev, config_desc);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+/*
+ * Allocate a feature object for load into a csdev.
+ * memory allocated using the csdev->dev object using devm managed allocator.
+ */
+static struct cscfg_feature_csdev *
+cscfg_alloc_csdev_feat(struct coresight_device *csdev, struct cscfg_feature_desc *feat_desc)
+{
+ struct cscfg_feature_csdev *feat_csdev = NULL;
+ struct device *dev = csdev->dev.parent;
+ int i;
+
+ feat_csdev = devm_kzalloc(dev, sizeof(struct cscfg_feature_csdev), GFP_KERNEL);
+ if (!feat_csdev)
+ return NULL;
+
+ /* parameters are optional - could be 0 */
+ feat_csdev->nr_params = feat_desc->nr_params;
+
+ /*
+ * if we need parameters, zero alloc the space here, the load routine in
+ * the csdev device driver will fill out some information according to
+ * feature descriptor.
+ */
+ if (feat_csdev->nr_params) {
+ feat_csdev->params_csdev = devm_kcalloc(dev, feat_csdev->nr_params,
+ sizeof(struct cscfg_parameter_csdev),
+ GFP_KERNEL);
+ if (!feat_csdev->params_csdev)
+ return NULL;
+
+ /*
+ * fill in the feature reference in the param - other fields
+ * handled by loader in csdev.
+ */
+ for (i = 0; i < feat_csdev->nr_params; i++)
+ feat_csdev->params_csdev[i].feat_csdev = feat_csdev;
+ }
+
+ /*
+ * Always have registers to program - again the load routine in csdev device
+ * will fill out according to feature descriptor and device requirements.
+ */
+ feat_csdev->nr_regs = feat_desc->nr_regs;
+ feat_csdev->regs_csdev = devm_kcalloc(dev, feat_csdev->nr_regs,
+ sizeof(struct cscfg_regval_csdev),
+ GFP_KERNEL);
+ if (!feat_csdev->regs_csdev)
+ return NULL;
+
+ /* load the feature default values */
+ feat_csdev->feat_desc = feat_desc;
+ feat_csdev->csdev = csdev;
+
+ return feat_csdev;
+}
+
+/* load one feature into one coresight device */
+static int cscfg_load_feat_csdev(struct coresight_device *csdev,
+ struct cscfg_feature_desc *feat_desc,
+ struct cscfg_csdev_feat_ops *ops)
+{
+ struct cscfg_feature_csdev *feat_csdev;
+ unsigned long flags;
+ int err;
+
+ if (!ops->load_feat)
+ return -EINVAL;
+
+ feat_csdev = cscfg_alloc_csdev_feat(csdev, feat_desc);
+ if (!feat_csdev)
+ return -ENOMEM;
+
+ /* load the feature into the device */
+ err = ops->load_feat(csdev, feat_csdev);
+ if (err)
+ return err;
+
+ /* add to internal csdev feature list & initialise using reset call */
+ cscfg_reset_feat(feat_csdev);
+ spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
+ list_add(&feat_csdev->node, &csdev->feature_csdev_list);
+ spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
+
+ return 0;
+}
+
+/*
+ * Add feature to any matching devices - call with mutex locked.
+ * Iterates through devices - any device that matches the feature will be
+ * called to load it.
+ */
+static int cscfg_add_feat_to_csdevs(struct cscfg_feature_desc *feat_desc)
+{
+ struct cscfg_registered_csdev *csdev_item;
+ int err;
+
+ list_for_each_entry(csdev_item, &cscfg_mgr->csdev_desc_list, item) {
+ if (csdev_item->match_flags & feat_desc->match_flags) {
+ err = cscfg_load_feat_csdev(csdev_item->csdev, feat_desc, &csdev_item->ops);
+ if (err)
+ return err;
+ }
+ }
+ return 0;
+}
+
+/* check feature list for a named feature - call with mutex locked. */
+static bool cscfg_match_list_feat(const char *name)
+{
+ struct cscfg_feature_desc *feat_desc;
+
+ list_for_each_entry(feat_desc, &cscfg_mgr->feat_desc_list, item) {
+ if (strcmp(feat_desc->name, name) == 0)
+ return true;
+ }
+ return false;
+}
+
+/* check all feat needed for cfg are in the list - call with mutex locked. */
+static int cscfg_check_feat_for_cfg(struct cscfg_config_desc *config_desc)
+{
+ int i;
+
+ for (i = 0; i < config_desc->nr_feat_refs; i++)
+ if (!cscfg_match_list_feat(config_desc->feat_ref_names[i]))
+ return -EINVAL;
+ return 0;
+}
+
+/*
+ * load feature - add to feature list.
+ */
+static int cscfg_load_feat(struct cscfg_feature_desc *feat_desc)
+{
+ int err;
+
+ /* add feature to any matching registered devices */
+ err = cscfg_add_feat_to_csdevs(feat_desc);
+ if (err)
+ return err;
+
+ list_add(&feat_desc->item, &cscfg_mgr->feat_desc_list);
+ return 0;
+}
+
+/*
+ * load config into the system - validate used features exist then add to
+ * config list.
+ */
+static int cscfg_load_config(struct cscfg_config_desc *config_desc)
+{
+ int err;
+
+ /* validate features are present */
+ err = cscfg_check_feat_for_cfg(config_desc);
+ if (err)
+ return err;
+
+ /* add config to any matching registered device */
+ err = cscfg_add_cfg_to_csdevs(config_desc);
+ if (err)
+ return err;
+
+ /* add config to perf fs to allow selection */
+ err = etm_perf_add_symlink_cscfg(cscfg_device(), config_desc);
+ if (err)
+ return err;
+
+ list_add(&config_desc->item, &cscfg_mgr->config_desc_list);
+ atomic_set(&config_desc->active_cnt, 0);
+ return 0;
+}
+
+/* get a feature descriptor by name */
+const struct cscfg_feature_desc *cscfg_get_named_feat_desc(const char *name)
+{
+ const struct cscfg_feature_desc *feat_desc = NULL, *feat_desc_item;
+
+ mutex_lock(&cscfg_mutex);
+
+ list_for_each_entry(feat_desc_item, &cscfg_mgr->feat_desc_list, item) {
+ if (strcmp(feat_desc_item->name, name) == 0) {
+ feat_desc = feat_desc_item;
+ break;
+ }
+ }
+
+ mutex_unlock(&cscfg_mutex);
+ return feat_desc;
+}
+
+/* called with cscfg_mutex held */
+static struct cscfg_feature_csdev *
+cscfg_csdev_get_feat_from_desc(struct coresight_device *csdev,
+ struct cscfg_feature_desc *feat_desc)
+{
+ struct cscfg_feature_csdev *feat_csdev;
+
+ list_for_each_entry(feat_csdev, &csdev->feature_csdev_list, node) {
+ if (feat_csdev->feat_desc == feat_desc)
+ return feat_csdev;
+ }
+ return NULL;
+}
+
+int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc,
+ int param_idx, u64 value)
+{
+ int err = 0;
+ struct cscfg_feature_csdev *feat_csdev;
+ struct cscfg_registered_csdev *csdev_item;
+
+ mutex_lock(&cscfg_mutex);
+
+ /* check if any config active & return busy */
+ if (atomic_read(&cscfg_mgr->sys_active_cnt)) {
+ err = -EBUSY;
+ goto unlock_exit;
+ }
+
+ /* set the value */
+ if ((param_idx < 0) || (param_idx >= feat_desc->nr_params)) {
+ err = -EINVAL;
+ goto unlock_exit;
+ }
+ feat_desc->params_desc[param_idx].value = value;
+
+ /* update loaded instances.*/
+ list_for_each_entry(csdev_item, &cscfg_mgr->csdev_desc_list, item) {
+ feat_csdev = cscfg_csdev_get_feat_from_desc(csdev_item->csdev, feat_desc);
+ if (feat_csdev)
+ feat_csdev->params_csdev[param_idx].current_value = value;
+ }
+
+unlock_exit:
+ mutex_unlock(&cscfg_mutex);
+ return err;
+}
+
+/**
+ * cscfg_load_config_sets - API function to load feature and config sets.
+ *
+ * Take a 0 terminated array of feature descriptors and/or configuration
+ * descriptors and load into the system.
+ * Features are loaded first to ensure configuration dependencies can be met.
+ *
+ * @config_descs: 0 terminated array of configuration descriptors.
+ * @feat_descs: 0 terminated array of feature descriptors.
+ */
+int cscfg_load_config_sets(struct cscfg_config_desc **config_descs,
+ struct cscfg_feature_desc **feat_descs)
+{
+ int err, i = 0;
+
+ mutex_lock(&cscfg_mutex);
+
+ /* load features first */
+ if (feat_descs) {
+ while (feat_descs[i]) {
+ err = cscfg_load_feat(feat_descs[i]);
+ if (!err)
+ err = cscfg_configfs_add_feature(feat_descs[i]);
+ if (err) {
+ pr_err("coresight-syscfg: Failed to load feature %s\n",
+ feat_descs[i]->name);
+ goto exit_unlock;
+ }
+ i++;
+ }
+ }
+
+ /* next any configurations to check feature dependencies */
+ i = 0;
+ if (config_descs) {
+ while (config_descs[i]) {
+ err = cscfg_load_config(config_descs[i]);
+ if (!err)
+ err = cscfg_configfs_add_config(config_descs[i]);
+ if (err) {
+ pr_err("coresight-syscfg: Failed to load configuration %s\n",
+ config_descs[i]->name);
+ goto exit_unlock;
+ }
+ i++;
+ }
+ }
+
+exit_unlock:
+ mutex_unlock(&cscfg_mutex);
+ return err;
+}
+EXPORT_SYMBOL_GPL(cscfg_load_config_sets);
+
+/* Handle coresight device registration and add configs and features to devices */
+
+/* iterate through config lists and load matching configs to device */
+static int cscfg_add_cfgs_csdev(struct coresight_device *csdev)
+{
+ struct cscfg_config_desc *config_desc;
+ int err = 0;
+
+ list_for_each_entry(config_desc, &cscfg_mgr->config_desc_list, item) {
+ err = cscfg_add_csdev_cfg(csdev, config_desc);
+ if (err)
+ break;
+ }
+ return err;
+}
+
+/* iterate through feature lists and load matching features to device */
+static int cscfg_add_feats_csdev(struct coresight_device *csdev,
+ u32 match_flags,
+ struct cscfg_csdev_feat_ops *ops)
+{
+ struct cscfg_feature_desc *feat_desc;
+ int err = 0;
+
+ if (!ops->load_feat)
+ return -EINVAL;
+
+ list_for_each_entry(feat_desc, &cscfg_mgr->feat_desc_list, item) {
+ if (feat_desc->match_flags & match_flags) {
+ err = cscfg_load_feat_csdev(csdev, feat_desc, ops);
+ if (err)
+ break;
+ }
+ }
+ return err;
+}
+
+/* Add coresight device to list and copy its matching info */
+static int cscfg_list_add_csdev(struct coresight_device *csdev,
+ u32 match_flags,
+ struct cscfg_csdev_feat_ops *ops)
+{
+ struct cscfg_registered_csdev *csdev_item;
+
+ /* allocate the list entry structure */
+ csdev_item = kzalloc(sizeof(struct cscfg_registered_csdev), GFP_KERNEL);
+ if (!csdev_item)
+ return -ENOMEM;
+
+ csdev_item->csdev = csdev;
+ csdev_item->match_flags = match_flags;
+ csdev_item->ops.load_feat = ops->load_feat;
+ list_add(&csdev_item->item, &cscfg_mgr->csdev_desc_list);
+
+ INIT_LIST_HEAD(&csdev->feature_csdev_list);
+ INIT_LIST_HEAD(&csdev->config_csdev_list);
+ spin_lock_init(&csdev->cscfg_csdev_lock);
+
+ return 0;
+}
+
+/* remove a coresight device from the list and free data */
+static void cscfg_list_remove_csdev(struct coresight_device *csdev)
+{
+ struct cscfg_registered_csdev *csdev_item, *tmp;
+
+ list_for_each_entry_safe(csdev_item, tmp, &cscfg_mgr->csdev_desc_list, item) {
+ if (csdev_item->csdev == csdev) {
+ list_del(&csdev_item->item);
+ kfree(csdev_item);
+ break;
+ }
+ }
+}
+
+/**
+ * cscfg_register_csdev - register a coresight device with the syscfg manager.
+ *
+ * Registers the coresight device with the system. @match_flags used to check
+ * if the device is a match for registered features. Any currently registered
+ * configurations and features that match the device will be loaded onto it.
+ *
+ * @csdev: The coresight device to register.
+ * @match_flags: Matching information to load features.
+ * @ops: Standard operations supported by the device.
+ */
+int cscfg_register_csdev(struct coresight_device *csdev,
+ u32 match_flags,
+ struct cscfg_csdev_feat_ops *ops)
+{
+ int ret = 0;
+
+ mutex_lock(&cscfg_mutex);
+
+ /* add device to list of registered devices */
+ ret = cscfg_list_add_csdev(csdev, match_flags, ops);
+ if (ret)
+ goto reg_csdev_unlock;
+
+ /* now load any registered features and configs matching the device. */
+ ret = cscfg_add_feats_csdev(csdev, match_flags, ops);
+ if (ret) {
+ cscfg_list_remove_csdev(csdev);
+ goto reg_csdev_unlock;
+ }
+
+ ret = cscfg_add_cfgs_csdev(csdev);
+ if (ret) {
+ cscfg_list_remove_csdev(csdev);
+ goto reg_csdev_unlock;
+ }
+
+ pr_info("CSCFG registered %s", dev_name(&csdev->dev));
+
+reg_csdev_unlock:
+ mutex_unlock(&cscfg_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cscfg_register_csdev);
+
+/**
+ * cscfg_unregister_csdev - remove coresight device from syscfg manager.
+ *
+ * @csdev: Device to remove.
+ */
+void cscfg_unregister_csdev(struct coresight_device *csdev)
+{
+ mutex_lock(&cscfg_mutex);
+ cscfg_list_remove_csdev(csdev);
+ mutex_unlock(&cscfg_mutex);
+}
+EXPORT_SYMBOL_GPL(cscfg_unregister_csdev);
+
+/**
+ * cscfg_csdev_reset_feats - reset features for a CoreSight device.
+ *
+ * Resets all parameters and register values for any features loaded
+ * into @csdev to their default values.
+ *
+ * @csdev: The CoreSight device.
+ */
+void cscfg_csdev_reset_feats(struct coresight_device *csdev)
+{
+ struct cscfg_feature_csdev *feat_csdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
+ if (list_empty(&csdev->feature_csdev_list))
+ goto unlock_exit;
+
+ list_for_each_entry(feat_csdev, &csdev->feature_csdev_list, node)
+ cscfg_reset_feat(feat_csdev);
+
+unlock_exit:
+ spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
+}
+EXPORT_SYMBOL_GPL(cscfg_csdev_reset_feats);
+
+/**
+ * cscfg_activate_config - Mark a configuration descriptor as active.
+ *
+ * This will be seen when csdev devices are enabled in the system.
+ * Only activated configurations can be enabled on individual devices.
+ * Activation protects the configuration from alteration or removal while
+ * active.
+ *
+ * Selection by hash value - generated from the configuration name when it
+ * was loaded and added to the cs_etm/configurations file system for selection
+ * by perf.
+ *
+ * Increments the configuration descriptor active count and the global active
+ * count.
+ *
+ * @cfg_hash: Hash value of the selected configuration name.
+ */
+int cscfg_activate_config(unsigned long cfg_hash)
+{
+ struct cscfg_config_desc *config_desc;
+ int err = -EINVAL;
+
+ mutex_lock(&cscfg_mutex);
+
+ list_for_each_entry(config_desc, &cscfg_mgr->config_desc_list, item) {
+ if ((unsigned long)config_desc->event_ea->var == cfg_hash) {
+ /*
+ * increment the global active count - control changes to
+ * active configurations
+ */
+ atomic_inc(&cscfg_mgr->sys_active_cnt);
+
+ /*
+ * mark the descriptor as active so enable config on a
+ * device instance will use it
+ */
+ atomic_inc(&config_desc->active_cnt);
+
+ err = 0;
+ dev_dbg(cscfg_device(), "Activate config %s.\n", config_desc->name);
+ break;
+ }
+ }
+ mutex_unlock(&cscfg_mutex);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(cscfg_activate_config);
+
+/**
+ * cscfg_deactivate_config - Mark a config descriptor as inactive.
+ *
+ * Decrement the configuration and global active counts.
+ *
+ * @cfg_hash: Hash value of the selected configuration name.
+ */
+void cscfg_deactivate_config(unsigned long cfg_hash)
+{
+ struct cscfg_config_desc *config_desc;
+
+ mutex_lock(&cscfg_mutex);
+
+ list_for_each_entry(config_desc, &cscfg_mgr->config_desc_list, item) {
+ if ((unsigned long)config_desc->event_ea->var == cfg_hash) {
+ atomic_dec(&config_desc->active_cnt);
+ atomic_dec(&cscfg_mgr->sys_active_cnt);
+ dev_dbg(cscfg_device(), "Deactivate config %s.\n", config_desc->name);
+ break;
+ }
+ }
+ mutex_unlock(&cscfg_mutex);
+}
+EXPORT_SYMBOL_GPL(cscfg_deactivate_config);
+
+/**
+ * cscfg_csdev_enable_active_config - Enable matching active configuration for device.
+ *
+ * Enables the configuration selected by @cfg_hash if the configuration is supported
+ * on the device and has been activated.
+ *
+ * If active and supported the CoreSight device @csdev will be programmed with the
+ * configuration, using @preset parameters.
+ *
+ * Should be called before driver hardware enable for the requested device, prior to
+ * programming and enabling the physical hardware.
+ *
+ * @csdev: CoreSight device to program.
+ * @cfg_hash: Selector for the configuration.
+ * @preset: Preset parameter values to use, 0 for current / default values.
+ */
+int cscfg_csdev_enable_active_config(struct coresight_device *csdev,
+ unsigned long cfg_hash, int preset)
+{
+ struct cscfg_config_csdev *config_csdev_active = NULL, *config_csdev_item;
+ const struct cscfg_config_desc *config_desc;
+ unsigned long flags;
+ int err = 0;
+
+ /* quickly check global count */
+ if (!atomic_read(&cscfg_mgr->sys_active_cnt))
+ return 0;
+
+ /*
+ * Look for matching configuration - set the active configuration
+ * context if found.
+ */
+ spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
+ list_for_each_entry(config_csdev_item, &csdev->config_csdev_list, node) {
+ config_desc = config_csdev_item->config_desc;
+ if ((atomic_read(&config_desc->active_cnt)) &&
+ ((unsigned long)config_desc->event_ea->var == cfg_hash)) {
+ config_csdev_active = config_csdev_item;
+ csdev->active_cscfg_ctxt = (void *)config_csdev_active;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
+
+ /*
+ * If found, attempt to enable
+ */
+ if (config_csdev_active) {
+ /*
+ * Call the generic routine that will program up the internal
+ * driver structures prior to programming up the hardware.
+ * This routine takes the driver spinlock saved in the configs.
+ */
+ err = cscfg_csdev_enable_config(config_csdev_active, preset);
+ if (!err) {
+ /*
+ * Successful programming. Check the active_cscfg_ctxt
+ * pointer to ensure no pre-emption disabled it via
+ * cscfg_csdev_disable_active_config() before
+ * we could start.
+ *
+ * Set enabled if OK, err if not.
+ */
+ spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
+ if (csdev->active_cscfg_ctxt)
+ config_csdev_active->enabled = true;
+ else
+ err = -EBUSY;
+ spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
+ }
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(cscfg_csdev_enable_active_config);
+
+/**
+ * cscfg_csdev_disable_active_config - disable an active config on the device.
+ *
+ * Disables the active configuration on the CoreSight device @csdev.
+ * Disable will save the values of any registers marked in the configurations
+ * as save on disable.
+ *
+ * Should be called after driver hardware disable for the requested device,
+ * after disabling the physical hardware and reading back registers.
+ *
+ * @csdev: The CoreSight device.
+ */
+void cscfg_csdev_disable_active_config(struct coresight_device *csdev)
+{
+ struct cscfg_config_csdev *config_csdev;
+ unsigned long flags;
+
+ /*
+ * Check if we have an active config, and that it was successfully enabled.
+ * If it was not enabled, we have no work to do, otherwise mark as disabled.
+ * Clear the active config pointer.
+ */
+ spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
+ config_csdev = (struct cscfg_config_csdev *)csdev->active_cscfg_ctxt;
+ if (config_csdev) {
+ if (!config_csdev->enabled)
+ config_csdev = NULL;
+ else
+ config_csdev->enabled = false;
+ }
+ csdev->active_cscfg_ctxt = NULL;
+ spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
+
+ /* true if there was an enabled active config */
+ if (config_csdev)
+ cscfg_csdev_disable_config(config_csdev);
+}
+EXPORT_SYMBOL_GPL(cscfg_csdev_disable_active_config);
+
+/* Initialise system configuration management device. */
+
+struct device *cscfg_device(void)
+{
+ return cscfg_mgr ? &cscfg_mgr->dev : NULL;
+}
+
+/* Must have a release function or the kernel will complain on module unload */
+static void cscfg_dev_release(struct device *dev)
+{
+ kfree(cscfg_mgr);
+ cscfg_mgr = NULL;
+}
+
+/* a device is needed to "own" some kernel elements such as sysfs entries. */
+static int cscfg_create_device(void)
+{
+ struct device *dev;
+ int err = -ENOMEM;
+
+ mutex_lock(&cscfg_mutex);
+ if (cscfg_mgr) {
+ err = -EINVAL;
+ goto create_dev_exit_unlock;
+ }
+
+ cscfg_mgr = kzalloc(sizeof(struct cscfg_manager), GFP_KERNEL);
+ if (!cscfg_mgr)
+ goto create_dev_exit_unlock;
+
+ /* setup the device */
+ dev = cscfg_device();
+ dev->release = cscfg_dev_release;
+ dev->init_name = "cs_system_cfg";
+
+ err = device_register(dev);
+ if (err)
+ cscfg_dev_release(dev);
+
+create_dev_exit_unlock:
+ mutex_unlock(&cscfg_mutex);
+ return err;
+}
+
+static void cscfg_clear_device(void)
+{
+ struct cscfg_config_desc *cfg_desc;
+
+ mutex_lock(&cscfg_mutex);
+ list_for_each_entry(cfg_desc, &cscfg_mgr->config_desc_list, item) {
+ etm_perf_del_symlink_cscfg(cfg_desc);
+ }
+ cscfg_configfs_release(cscfg_mgr);
+ device_unregister(cscfg_device());
+ mutex_unlock(&cscfg_mutex);
+}
+
+/* Initialise system config management API device */
+int __init cscfg_init(void)
+{
+ int err = 0;
+
+ err = cscfg_create_device();
+ if (err)
+ return err;
+
+ err = cscfg_configfs_init(cscfg_mgr);
+ if (err)
+ goto exit_err;
+
+ INIT_LIST_HEAD(&cscfg_mgr->csdev_desc_list);
+ INIT_LIST_HEAD(&cscfg_mgr->feat_desc_list);
+ INIT_LIST_HEAD(&cscfg_mgr->config_desc_list);
+ atomic_set(&cscfg_mgr->sys_active_cnt, 0);
+
+ /* preload built-in configurations */
+ err = cscfg_preload();
+ if (err)
+ goto exit_err;
+
+ dev_info(cscfg_device(), "CoreSight Configuration manager initialised");
+ return 0;
+
+exit_err:
+ cscfg_clear_device();
+ return err;
+}
+
+void cscfg_exit(void)
+{
+ cscfg_clear_device();
+}
diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h
new file mode 100644
index 000000000000..8d018efd6ead
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-syscfg.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Coresight system configuration driver.
+ */
+
+#ifndef CORESIGHT_SYSCFG_H
+#define CORESIGHT_SYSCFG_H
+
+#include <linux/configfs.h>
+#include <linux/coresight.h>
+#include <linux/device.h>
+
+#include "coresight-config.h"
+
+/**
+ * System configuration manager device.
+ *
+ * Contains lists of the loaded configurations and features, plus a list of CoreSight devices
+ * registered with the system as supporting configuration management.
+ *
+ * Need a device to 'own' some coresight system wide sysfs entries in
+ * perf events, configfs etc.
+ *
+ * @dev: The device.
+ * @csdev_desc_list: List of coresight devices registered with the configuration manager.
+ * @feat_desc_list: List of feature descriptors to load into registered devices.
+ * @config_desc_list: List of system configuration descriptors to load into registered devices.
+ * @sys_active_cnt: Total number of active config descriptor references.
+ * @cfgfs_subsys: configfs subsystem used to manage configurations.
+ */
+struct cscfg_manager {
+ struct device dev;
+ struct list_head csdev_desc_list;
+ struct list_head feat_desc_list;
+ struct list_head config_desc_list;
+ atomic_t sys_active_cnt;
+ struct configfs_subsystem cfgfs_subsys;
+};
+
+/* get reference to dev in cscfg_manager */
+struct device *cscfg_device(void);
+
+/**
+ * List entry for Coresight devices that are registered as supporting complex
+ * config operations.
+ *
+ * @csdev: The registered device.
+ * @match_flags: The matching type information for adding features.
+ * @ops: Operations supported by the registered device.
+ * @item: list entry.
+ */
+struct cscfg_registered_csdev {
+ struct coresight_device *csdev;
+ u32 match_flags;
+ struct cscfg_csdev_feat_ops ops;
+ struct list_head item;
+};
+
+/* internal core operations for cscfg */
+int __init cscfg_init(void);
+void cscfg_exit(void);
+int cscfg_preload(void);
+const struct cscfg_feature_desc *cscfg_get_named_feat_desc(const char *name);
+int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc,
+ int param_idx, u64 value);
+
+
+/* syscfg manager external API */
+int cscfg_load_config_sets(struct cscfg_config_desc **cfg_descs,
+ struct cscfg_feature_desc **feat_descs);
+int cscfg_register_csdev(struct coresight_device *csdev, u32 match_flags,
+ struct cscfg_csdev_feat_ops *ops);
+void cscfg_unregister_csdev(struct coresight_device *csdev);
+int cscfg_activate_config(unsigned long cfg_hash);
+void cscfg_deactivate_config(unsigned long cfg_hash);
+void cscfg_csdev_reset_feats(struct coresight_device *csdev);
+int cscfg_csdev_enable_active_config(struct coresight_device *csdev,
+ unsigned long cfg_hash, int preset);
+void cscfg_csdev_disable_active_config(struct coresight_device *csdev);
+
+#endif /* CORESIGHT_SYSCFG_H */