diff options
Diffstat (limited to 'drivers/hwtracing/coresight')
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(¶m_item->group, + feat_desc->params_desc[i].name, + &cscfg_param_view_type); + configfs_add_default_group(¶m_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 */ |