[RFC PATCH 6/8] coresight: etm4x: syscfg: Add resource management to etm4x.
Mathieu Poirier
mathieu.poirier at linaro.org
Wed May 26 10:51:58 PDT 2021
Good day,
Comments for this patch will come over multiple days...
On Wed, May 12, 2021 at 10:17:50PM +0100, Mike Leach wrote:
> Adds resource management to configuration and feature handling for ETM4
> using the system configuration resource management API.
>
> Allows specification of ETM4 resources when creating configurations
> and features.
>
> Adds in checking and validation of resources used by features to
> prevent over allocation when multiple features used in a configuration.
>
> Signed-off-by: Mike Leach <mike.leach at linaro.org>
> ---
> .../hwtracing/coresight/coresight-etm4x-cfg.c | 533 ++++++++++++++++++
> .../hwtracing/coresight/coresight-etm4x-cfg.h | 196 ++++++-
> include/linux/coresight.h | 2 +
> 3 files changed, 724 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
> index d2ea903231b2..ba6d20b58a9a 100644
> --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
> +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.c
> @@ -28,6 +28,11 @@
> } \
> }
>
> +/* check for a match to ts rate */
> +static bool etm4_cfg_feat_is_ts_rate(const struct cscfg_feature_desc *feat_desc);
> +
> +#define TS_RATE_REG_VAL_IDX 0
> +
> /**
> * etm4_cfg_map_reg_offset - validate and map the register offset into a
> * location in the driver config struct.
> @@ -128,6 +133,66 @@ static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata,
> return err;
> }
>
> +static void etm4_cfg_dump_res_mask(const char *name, struct etm4_cfg_resources *res)
> +{
> + pr_debug("Mask %s\n", name);
> + pr_debug("selectors %08x; addr_cmp %04x\n", res->selectors, res->addr_cmp);
> + pr_debug("cid_cmp %02x; vmid_cmp %02x; counters %02x\n", res->cid_cmp,
> + res->vmid_cmp, res->counters);
> + pr_debug("misc bits %08x\n", res->misc);
Even at a pr_debug() log level this is probably too much output. I also think
this is a purely debugging mechanism and only used when developing new features.
I would enclose the core of the function with an #if DEBUG and add #define DEBUG
0 at the top of the file.
> +}
> +
> +/*
> + * generate an address offset from a resource type and index
> + * Bit selected resources will return a ETM4_RES_OFFSET_SKIP value
> + * as these require special handling on enable / disable.
> + */
> +static u32 etm4_cfg_get_res_offset(u16 res_type, u32 res_idx)
> +{
> + u32 offset = ETM4_RES_OFFSET_ERR;
> +
> + switch (res_type & ETM4_CFG_RES_MASK) {
> + case ETM4_CFG_RES_CTR:
> + if (res_type & ETM4_CTR_VAL)
> + offset = TRCCNTVRn(res_idx);
> + else if (res_type & ETM4_CTR_RLD)
> + offset = TRCCNTRLDVRn(res_idx);
> + else if (res_type & ETM4_CTR_CTRL)
> + offset = TRCCNTCTLRn(res_idx);
> + break;
> + case ETM4_CFG_RES_CMP:
> + if (res_type & ETM4_CMP_VAL)
> + offset = TRCACVRn(res_idx);
> + else if (res_type & ETM4_CMP_CTL)
> + offset = TRCACATRn(res_idx);
> + break;
> + case ETM4_CFG_RES_SEL:
> + offset = TRCRSCTLRn(res_idx);
> + break;
> +
The above case statements don't have a space and the ones below do. The same
applies to function etm4_cfg_update_res_from_des(). I don't really mind which
one is used (though I'd prefer no space) but pick a scheme and stick with it.
> + case ETM4_CFG_RES_SEQ:
> + if (res_type & ETM4_SEQ_STATE_R)
> + offset = TRCSEQEVRn(res_idx);
> + else if (res_type & ETM4_SEQ_RESET_R)
> + offset = TRCSEQRSTEVR;
> + break;
> + case ETM4_CFG_RES_CID_CMP:
> + offset = TRCCIDCVRn(res_idx);
> + break;
> +
> + case ETM4_CFG_RES_VID_CMP:
> + offset = TRCVMIDCVRn(res_idx);
> + break;
> +
> + /* these two have dedicated enable functions, no address needed */
> + case ETM4_CFG_RES_BITCTRL:
> + case ETM4_CFG_RES_TS:
> + offset = ETM4_RES_OFFSET_SKIP;
> + break;
> + }
> + return offset;
> +}
> +
> /**
> * etm4_cfg_load_feature - load a feature into a device instance.
> *
> @@ -163,11 +228,349 @@ static int etm4_cfg_load_feature(struct coresight_device *csdev,
> /* process the register descriptions */
> for (i = 0; i < feat_csdev->nr_regs && !err; i++) {
> offset = feat_desc->regs_desc[i].offset;
> +
> + /* resource needs conversion to a register access value */
> + if (feat_desc->regs_desc[i].type & CS_CFG_REG_TYPE_RESOURCE) {
> + offset = etm4_cfg_get_res_offset(feat_desc->regs_desc[i].hw_info,
> + offset);
> + if (offset == ETM4_RES_OFFSET_ERR) {
> + err = -ENODEV;
> + break;
> + } else if (offset == ETM4_RES_OFFSET_SKIP)
> + continue;
> + }
> err = etm4_cfg_map_reg_offset(drvdata, &feat_csdev->regs_csdev[i], offset);
> }
> return err;
> }
>
> +/*
> + * ts rate - set a counter to emit timestamp requests at a set interval.
> + * if we have sufficient resources then we use a counter and resource
> + * selector to achieve this.
> + *
> + * However, if not then do the best possible - which prevents the perf
> + * event timestamp request from failing if any configuration selection
> + * is using resources. e.g. when profiling, timestamps do not really matter.
> + */
> +void etm4_cfg_set_ts_rate(struct coresight_device *csdev, u32 ts_rate_val)
> +{
> + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> + struct etmv4_config *drvcfg = &drvdata->config;
> + struct cscfg_res_impl_used *res_impl_used;
> + int counter_idx, res_sel_idx;
> + u32 tsctlr_val = 0;
> +
> + res_impl_used = (struct cscfg_res_impl_used *)csdev->cscfg_res_mask;
> +
> + /* look for resources */
> + counter_idx = etm4_res_find_counter(res_impl_used);
> + res_sel_idx = etm4_res_find_selector(res_impl_used);
> + if (counter_idx >= 0 && res_sel_idx >= 0) {
> + /* counter and selector - can set up ts rate normally */
> + /*
> + * counter @ 1 and reload @ rate supplied -
> + * immediate timestamp then every rate
> + */
> + drvcfg->cntr_val[counter_idx] = 1;
> + drvcfg->cntrldvr[counter_idx] = ts_rate_val;
> + /*
> + * counter ctrl - bit 16: 1 for reload self,
> + * bit 7: 0 single event,
> + * bit 6:0 res sel 1 - true
> + */
> + drvcfg->cntr_ctrl[counter_idx] = 0x1 << 16 | 0x1;
> +
> + /*
> + * set up resource selector for the counter.
> + * bits 19:16 - group 0b0010 counter
> + * bits 15:0 - bit select for counter idx
> + */
> + drvcfg->res_ctrl[res_sel_idx] = (0x2 << 16) | (0x1 << counter_idx);
> +
> + /* single selector bit 7 == 0, bit 6:0 - selector index */
> + tsctlr_val = res_sel_idx;
> +
> + } else if (ts_rate_val == 1) {
> + /*
> + * perf always tries to use a min value -
> + * emulate by setting the ts event to true
> + */
> + /* single selector bit 7 == 0, bit 6:0 - selector 1 - always true */
> + tsctlr_val = 0x1;
> + }
> +
> + /* set the configr reg to enable TS, and the ts control reg */
> + drvcfg->ts_ctrl = tsctlr_val;
> + drvcfg->cfg |= BIT(11);
> +}
> +
> +/*
> + * on enable a feature - called after generic routine has programmed other registers.
> + * handle bit selects and custom elements
> + */
> +static int etm4_cfg_on_enable_feat(struct cscfg_feature_csdev *feat_csdev)
> +{
> + int err = 0;
> + struct etm4_cfg_resources *res_feat;
> + struct device *dev = feat_csdev->csdev->dev.parent;
> + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
> + struct etmv4_config *drvcfg = &drvdata->config;
> + u32 ts_rate_val;
> +
> + /*
> + * look for the bit selected resources in this feature and set driver
> + * values to be programmed when enabling hardware.
> + */
> + res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used;
> +
> + /* if none of the bit selected resources in use, exit early */
> + if (!res_feat->misc)
> + return 0;
> +
> + /* otherwise check each and set as required */
> + if (res_feat->ctxt_id)
> + drvcfg->cfg |= BIT(6);
> +
> + if (res_feat->vm_id)
> + drvcfg->cfg |= BIT(7);
> +
> + /* return stack is bit 12 in config register */
> + if (res_feat->return_stack)
> + drvcfg->cfg |= BIT(12);
> +
> + /* branch broadcast - feature using this must program the bbctlr */
> + if (res_feat->branch_broadcast)
> + drvcfg->cfg |= BIT(3);
> +
> + /* cycle count */
> + if (res_feat->cycle_cnt) {
> + drvcfg->cfg |= BIT(4);
> + /* TRM: Must program this for cycacc to work - ensure mun permitted */
> + if (drvcfg->ccctlr < drvdata->ccitmin)
> + drvcfg->ccctlr = drvdata->ccitmin;
> + }
> +
> + /*
> + * timestamps - if not ts-rate just set to on, otherwise
> + * set using reload counter according to requested rate
> + */
> + if (res_feat->timestamp) {
> + /* the current feature is the ts-rate feature */
> + if (res_feat->ts_rate) {
> + ts_rate_val = feat_csdev->regs_csdev[TS_RATE_REG_VAL_IDX].reg_desc.val32;
> + etm4_cfg_set_ts_rate(feat_csdev->csdev, ts_rate_val);
> + } else
> + drvcfg->cfg |= BIT(11);
> + }
> + return err;
> +}
> +
> +/* set the overall available resource masks for the device */
> +static int etm4_cfg_set_res_mask(struct coresight_device *csdev)
> +{
> + struct device *dev = csdev->dev.parent;
> + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
> + struct etm4_cfg_resources *res;
> + struct cscfg_res_impl_used *res_impl_used;
> +
> + res_impl_used = devm_kzalloc(dev, sizeof(*res_impl_used), GFP_KERNEL);
> + if (!res_impl_used)
> + return -ENOMEM;
> + res = &res_impl_used->impl;
> +
> + /* selectors */
> + if (drvdata->nr_resource)
> + res->selectors = GENMASK((drvdata->nr_resource * 2) - 1, 0);
> +
> + /* comparators */
> + if (drvdata->nr_addr_cmp)
> + res->addr_cmp = GENMASK(drvdata->nr_addr_cmp - 1, 0);
> +
> + if (drvdata->numvmidc)
> + res->vmid_cmp = GENMASK(drvdata->numvmidc - 1, 0);
> +
> + if (drvdata->numcidc)
> + res->cid_cmp = GENMASK(drvdata->numcidc - 1, 0);
> +
> + /* misc resources */
> + if (drvdata->nr_cntr)
> + res->counters = GENMASK(drvdata->nr_cntr - 1, 0);
> +
> + if (drvdata->trccci)
> + res->cycle_cnt = 1;
> +
> + if (drvdata->trcbb)
> + res->branch_broadcast = 1;
> +
> + if (drvdata->ctxid_size)
> + res->ctxt_id = 1;
> +
> + if (drvdata->vmid_size)
> + res->vm_id = 1;
> +
> + if (drvdata->nrseqstate)
> + res->sequencer = 1;
> +
> + if (drvdata->retstack)
> + res->return_stack = 1;
> +
> + if (drvdata->ts_size) {
> + res->timestamp = 1;
> + if (drvdata->nr_cntr && drvdata->nr_resource)
> + res->ts_rate = 1;
> + }
> + etm4_cfg_dump_res_mask("device impl resources", &res_impl_used->impl);
> + csdev->cscfg_res_mask = res_impl_used;
> + return 0;
> +}
> +
> +/*
> + * reads a descriptor and updates the resource mask structure
> + * checks resource indexes are valid.
> + */
> +static int etm4_cfg_update_res_from_desc(const struct cscfg_feature_desc *feat_desc,
> + struct etm4_cfg_resources *res)
> +{
> + struct cscfg_regval_desc *regs_desc = &feat_desc->regs_desc[0];
> + u32 res_idx, hw_info;
> + int i;
> +
> + for (i = 0; i < feat_desc->nr_regs; i++) {
> + if (regs_desc[i].type & CS_CFG_REG_TYPE_RESOURCE) {
> + res_idx = regs_desc[i].offset;
> + hw_info = regs_desc[i].hw_info;
> + switch (hw_info & ETM4_CFG_RES_MASK) {
> + case ETM4_CFG_RES_CTR:
> + if (res_idx >= ETMv4_MAX_CNTR)
> + goto invalid_resource_err;
> + res->counters |= BIT(res_idx);
> + break;
> +
> + case ETM4_CFG_RES_CMP:
> + if (res_idx >= ETM_MAX_SINGLE_ADDR_CMP)
> + goto invalid_resource_err;
> + res->addr_cmp |= BIT(res_idx);
> + break;
> +
> + case ETM4_CFG_RES_SEL:
> + if (res_idx >= ETM_MAX_RES_SEL)
> + goto invalid_resource_err;
> + res->selectors |= BIT(res_idx);
> + break;
> +
> + case ETM4_CFG_RES_SEQ:
> + res->sequencer = 1;
> + break;
> +
> + case ETM4_CFG_RES_TS:
> + res->timestamp = 1;
> + if (etm4_cfg_feat_is_ts_rate(feat_desc))
> + res->ts_rate = 1;
> + break;
> +
> + case ETM4_CFG_RES_BITCTRL:
> + if (hw_info & ETM4_BITCTRL_BRANCH_BROADCAST)
> + res->branch_broadcast = 1;
> + if (hw_info & ETM4_BITCTRL_CYCLE_COUNT)
> + res->cycle_cnt = 1;
> + if (hw_info & ETM4_BITCTRL_CTXTID)
> + res->ctxt_id = 1;
> + if (hw_info & ETM4_BITCTRL_VMID)
> + res->vm_id = 1;
> + if (hw_info & ETM4_BITCTRL_RETSTACK)
> + res->return_stack = 1;
> + break;
> +
> + case ETM4_CFG_RES_CID_CMP:
> + if (res_idx >= ETMv4_MAX_CTXID_CMP)
> + goto invalid_resource_err;
> + res->cid_cmp |= BIT(res_idx);
> + break;
> +
> + case ETM4_CFG_RES_VID_CMP:
> + if (res_idx >= ETM_MAX_VMID_CMP)
> + goto invalid_resource_err;
> + res->vmid_cmp |= BIT(res_idx);
> + break;
> + }
> + }
> + }
> + return 0;
> +
> +invalid_resource_err:
> + pr_err("Error: Invalid resource values in feature %s\n", feat_desc->name);
> + return -EINVAL;
> +}
> +/*
> + * Check that the device contains the minimum resources required to support the
> + * described @feat_desc. Return -ENODEV if missing required resources.
> + */
> +static int etm4_cfg_check_feat_res(struct coresight_device *csdev,
> + struct cscfg_feature_desc *feat_desc)
> +{
> + struct etm4_cfg_resources req_res;
> + struct cscfg_res_impl_used *dev_res;
> + int err;
> +
> + /* create a resource mask from descriptor and validate */
> + memset(&req_res, 0, sizeof(req_res));
> + err = etm4_cfg_update_res_from_desc(feat_desc, &req_res);
> + etm4_cfg_dump_res_mask("check_feat_res", &req_res);
> + if (!err) {
> + dev_res = (struct cscfg_res_impl_used *)csdev->cscfg_res_mask;
> + if (!etm4_cfg_check_impl(&dev_res->impl, &req_res))
> + return -ENODEV;
> + }
> + return err;
> +}
> +
> +/*
> + * Allocate resource requirements for the feature before
> + * it is programmed into the system. Ensures that two or more features in a
> + * configuration do not try to use the same resources on the device.
> + *
> + * At this point we use the absolute programmed resources - we do not attempt
> + * to find alternate available resources. (e.g. if 2 features use selector 3,
> + * fail the 2nd feature - do not look for an alternative free selector).
> + */
> +static int etm4_cfg_alloc_feat_res(struct cscfg_feature_csdev *feat_csdev)
> +{
> + struct coresight_device *csdev = feat_csdev->csdev;
> + struct device *dev = csdev->dev.parent;
> + struct etm4_cfg_resources *res_feat, *res_inuse;
> + int err = 0;
> +
> + /* one off initialisation of resources required for this feature */
> + if (!feat_csdev->res_used) {
> + res_feat = devm_kzalloc(dev, sizeof(*res_feat), GFP_KERNEL);
> + if (!res_feat)
> + return -ENOMEM;
> + err = etm4_cfg_update_res_from_desc(feat_csdev->feat_desc, res_feat);
> + if (err)
> + return err;
> + feat_csdev->res_used = res_feat;
> + } else
> + res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used;
> +
> + /* check that the device resources reqiured are not in use */
> + res_inuse = &((struct cscfg_res_impl_used *)csdev->cscfg_res_mask)->used;
> + if (!etm4_cfg_check_set_inuse(res_inuse, res_feat))
> + err = -ENOSPC;
> +
> + return err;
> +}
> +
> +static void etm4_cfg_clear_feat_res(struct cscfg_feature_csdev *feat_csdev)
> +{
> + struct coresight_device *csdev = feat_csdev->csdev;
> + struct etm4_cfg_resources *res_feat, *res_inuse;
> +
> + res_feat = (struct etm4_cfg_resources *)feat_csdev->res_used;
> + res_inuse = &((struct cscfg_res_impl_used *)csdev->cscfg_res_mask)->used;
> + etm4_cfg_clear_inuse(res_inuse, res_feat);
> +}
> +
> /* match information when loading configurations */
> #define CS_CFG_ETM4_MATCH_FLAGS (CS_CFG_MATCH_CLASS_SRC_ALL | \
> CS_CFG_MATCH_CLASS_SRC_ETM4)
> @@ -175,8 +578,138 @@ static int etm4_cfg_load_feature(struct coresight_device *csdev,
> int etm4_cscfg_register(struct coresight_device *csdev)
> {
> struct cscfg_csdev_feat_ops ops;
> + int err = 0;
> +
> + err = etm4_cfg_set_res_mask(csdev);
> + if (err)
> + return err;
>
> ops.load_feat = &etm4_cfg_load_feature;
> + ops.check_feat_res = &etm4_cfg_check_feat_res;
> + ops.alloc_feat_res = &etm4_cfg_alloc_feat_res;
> + ops.clear_feat_res = &etm4_cfg_clear_feat_res;
> + ops.set_on_enable = &etm4_cfg_on_enable_feat;
> + ops.clear_on_disable = 0;
s/0/NULL
>
> return cscfg_register_csdev(csdev, CS_CFG_ETM4_MATCH_FLAGS, &ops);
> }
> +
> +/*
> + * find first available bit in implemented mask @impl, that is not set in @used mask.
> + * set bit in @used and return. Return -ENOSPC if no available bits.
> + */
> +int etm4_cfg_find_unused_idx(unsigned long *impl, unsigned long *used, int size)
> +{
> + unsigned long end_idx, unused_idx;
> +
> + end_idx = find_first_zero_bit(impl, size);
> + unused_idx = find_first_zero_bit(used, size);
> + if (unused_idx < end_idx) {
> + *used |= BIT(unused_idx);
> + return (int)unused_idx;
> + }
> + return -ENOSPC;
> +}
> +
> +/*
> + * find first available pair of bits in implemented mask @impl, that are not set in
> + * @used mask. First bit of pair will always be an even index.
> + * Set bits in @used and return. Return -ENOSPC if no available bits.
> + */
> +int etm4_cfg_find_unused_idx_pair(unsigned long *impl, unsigned long *used, int size)
> +{
> + unsigned long end_idx, first_unused_idx, next_unused_idx;
> +
> + end_idx = find_first_zero_bit(impl, size);
> + first_unused_idx = find_first_zero_bit(used, size);
> +
> + /*
> + * even indexes are the 1st in a pair, look through the comparators
> + * till a pair found or we are at the end of the list.
> + */
> + while (first_unused_idx < end_idx) {
> + /* first is an even number, if the next is free we have a pair */
> + if (!(first_unused_idx % 2)) {
> + next_unused_idx = find_next_zero_bit(used, size, first_unused_idx);
> + if (next_unused_idx == (first_unused_idx + 1)) {
> + *used |= BIT(first_unused_idx);
> + *used |= BIT(next_unused_idx);
> + return (int)first_unused_idx;
> + }
> + first_unused_idx = next_unused_idx;
> + } else
> + first_unused_idx = find_next_zero_bit(used, size, first_unused_idx);
> + }
> + return -ENOSPC;
> +}
> +
> +
> +/* built in timestamp rate for etm4x */
> +static struct cscfg_parameter_desc ts_rate_param[] = {
> + {
> + .name = "ts_rate_cycles",
> + .value = 100.
> + },
> +};
> +
> +static struct cscfg_regval_desc ts_rate_regs[] = {
> + {
> + .type = CS_CFG_REG_TYPE_RESOURCE | CS_CFG_REG_TYPE_VAL_PARAM,
> + .offset = 0,
> + .hw_info = ETM4_CFG_RES_TS,
> + .param_idx = 0,
> + },
> +};
> +
> +static struct cscfg_feature_desc ts_rate_etm4x = {
> + .name = "timestamp-rate",
> + .description = "Enable timestamps and set rate they appear in the trace.\n"
> + "Rate value is number of cycles between timestamp requests. Min value 1.\n",
> + .match_flags = CS_CFG_MATCH_CLASS_SRC_ETM4,
> + .nr_params = ARRAY_SIZE(ts_rate_param),
> + .params_desc = ts_rate_param,
> + .nr_regs = ARRAY_SIZE(ts_rate_regs),
> + .regs_desc = ts_rate_regs,
> +};
> +
> +static struct cscfg_feature_desc *etm4x_feats[] = {
> + &ts_rate_etm4x,
> + NULL,
> +};
> +
> +static struct cscfg_config_desc *etm4x_cfgs[] = {
> + NULL,
> +};
> +
> +static struct cscfg_load_owner_info etm4x_mod_owner = {
> + .type = CSCFG_OWNER_MODULE,
> + .owner_handle = THIS_MODULE,
> +};
> +
> +/*
> + * check if incoming feature is ts-rate
> + */
> +static bool etm4_cfg_feat_is_ts_rate(const struct cscfg_feature_desc *feat_desc)
> +{
> + if (!strcmp(feat_desc->name, ts_rate_etm4x.name))
> + return true;
> + return false;
> +}
> +
> +/* load the etm4 builtin ts_rate feature into the system */
> +int etm4_cscfg_load_builtin_cfg(void)
> +{
> + int err;
> +
> + err = cscfg_load_config_sets(etm4x_cfgs, etm4x_feats, &etm4x_mod_owner);
> +
> + /* if currently loaded matching devs ts_rate, still allow to load */
> + if (err == -ENODEV)
> + err = 0;
> + return err;
> +}
> +
> +void etm4_cscfg_unload_builtin_cfg(void)
> +{
> + cscfg_unload_config_sets(&etm4x_mod_owner);
> +}
> diff --git a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h
> index 32dab34c1dac..dd69a8ef522d 100644
> --- a/drivers/hwtracing/coresight/coresight-etm4x-cfg.h
> +++ b/drivers/hwtracing/coresight/coresight-etm4x-cfg.h
> @@ -13,18 +13,200 @@
>
> /* resource IDs */
>
> +/*
> + * 12 bit resource ID:
> + * 3:0 = resource type in use.
> + * 11:4 = additional resource specific information.
> + */
> #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_SEL 0x003
> +#define ETM4_CFG_RES_SEQ 0x004
> +#define ETM4_CFG_RES_TS 0x005
> +#define ETM4_CFG_RES_BITCTRL 0x006
> +#define ETM4_CFG_RES_CID_CMP 0x007
> +#define ETM4_CFG_RES_VID_CMP 0x008
> #define ETM4_CFG_RES_MASK 0x00F
>
> +/* additional bits to supplement _CFG_RES_CTR */
> +#define ETM4_CTR_VAL 0x010
> +#define ETM4_CTR_RLD 0x020
> +#define ETM4_CTR_CTRL 0x040
> +
> +/* additional bits for address comparators _CFG_RES_CMP */
> +#define ETM4_CMP_PAIR0 0x010
> +#define ETM4_CMP_PAIR1 0x020
> +#define ETM4_CMP_VAL 0x040
> +#define ETM4_CMP_CTL 0x080
> +
> +/* additional bits for resource selectors _CFG_RES_SEL */
> +#define ETM4_SEL_PAIR0 0x010
> +#define ETM4_SEL_PAIR1 0x020
> +
> +/* addtional bits for sequencer _CFG_RES_SEQ */
> +#define ETM4_SEQ_STATE_R 0x010
> +#define ETM4_SEQ_RESET_R 0x020
> +
> +/* additional bits to supplement _CFG_RES_BITCTRL */
> +#define ETM4_BITCTRL_BRANCH_BROADCAST 0x010
> +#define ETM4_BITCTRL_CYCLE_COUNT 0x020
> +#define ETM4_BITCTRL_CTXTID 0x040
> +#define ETM4_BITCTRL_VMID 0x080
> +#define ETM4_BITCTRL_RETSTACK 0x100
> +
> +/* error value when calculating resource register offset (max offset = 0xFFC) */
> +#define ETM4_RES_OFFSET_ERR 0xFFF
> +
> +/* skip value if a bit control that is resolved later */
> +#define ETM4_RES_OFFSET_SKIP 0xFFE
> +
> +/**
> + * Masks to indicate resource usage.
> + * @selectors: The resource selector regs - max 32 off
> + * @comparators: Comparators - address (16 max), context ID (8 max), VMID (8 max).
> + * @misc:- bitselected features, sequencer etc.
There is a discrepancy between the above and the structure itself.
> + */
> +struct etm4_cfg_resources {
I would name this etm4_cfg_resources_mask.
> + u32 selectors;
> + u16 addr_cmp;
> + u8 cid_cmp;
> + u8 vmid_cmp;
> + u8 counters;
> + union {
> + u32 misc;
> + struct {
> + u32 cycle_cnt:1;
> + u32 branch_broadcast:1;
> + u32 ctxt_id:1;
> + u32 vm_id:1;
> + u32 sequencer:1;
> + u32 return_stack:1;
> + u32 timestamp:1;
> + u32 ts_rate:1;
> + };
> + };
> +};
> +
> +/* structure to hold implemented & used resources for the coresight device */
> +struct cscfg_res_impl_used {
And this etm4_cfg_resources.
Also, I find it quite confusing that some structure and functions start with
etm4_cfg_ while other with etm4_cscfg_. Having a uniform naming convention
would be greatly appreciated.
More comments tomorrow...
> + struct etm4_cfg_resources impl;
> + struct etm4_cfg_resources used;
> +};
> +
> +/* resource mask tests */
> +/* check implmented - ensure that all bits in @req exist in @impl */
> +static inline bool etm4_cfg_check_impl(struct etm4_cfg_resources *impl,
> + struct etm4_cfg_resources *req)
> +{
> + /* invert impl then and req - anything set is outside impl mask */
> + if ((~impl->selectors & req->selectors) ||
> + (~impl->addr_cmp & req->addr_cmp) ||
> + (~impl->cid_cmp & req->cid_cmp) ||
> + (~impl->vmid_cmp & req->vmid_cmp) ||
> + (~impl->counters & req->counters) ||
> + (~impl->misc & req->misc))
> + return false;
> + return true;
> +}
> +
> +/* check @req not @inuse, & set @inuse if free (assumes @req passed the impl check) */
> +static inline bool etm4_cfg_check_set_inuse(struct etm4_cfg_resources *inuse,
> + struct etm4_cfg_resources *req)
> +{
> + /* first check for hits between inuse and requested bits */
> + if ((inuse->selectors & req->selectors) ||
> + (inuse->addr_cmp & req->addr_cmp) ||
> + (inuse->cid_cmp & req->cid_cmp) ||
> + (inuse->vmid_cmp & req->vmid_cmp) ||
> + (inuse->counters & req->counters) ||
> + (inuse->misc & req->misc))
> + return false;
> +
> + /* set all requested bits as inuse */
> + inuse->selectors |= req->selectors;
> + inuse->addr_cmp |= req->addr_cmp;
> + inuse->cid_cmp |= req->cid_cmp;
> + inuse->vmid_cmp |= req->vmid_cmp;
> + inuse->counters |= req->counters;
> + inuse->misc |= req->misc;
> + return true;
> +}
> +
> +static inline void etm4_cfg_clear_inuse(struct etm4_cfg_resources *inuse,
> + struct etm4_cfg_resources *req)
> +{
> + /* clear requested bits from inuse */
> + inuse->selectors &= ~req->selectors;
> + inuse->addr_cmp &= ~req->addr_cmp;
> + inuse->cid_cmp &= ~req->cid_cmp;
> + inuse->vmid_cmp &= ~req->vmid_cmp;
> + inuse->counters &= ~req->counters;
> + inuse->misc &= ~req->misc;
> +}
> +
> /* ETMv4 specific config functions */
> int etm4_cscfg_register(struct coresight_device *csdev);
> +int etm4_cfg_find_unused_idx(unsigned long *impl, unsigned long *used, int size);
> +int etm4_cfg_find_unused_idx_pair(unsigned long *impl, unsigned long *used, int size);
> +void etm4_cfg_set_ts_rate(struct coresight_device *csdev, u32 ts_rate_val);
> +
> +/* register etm4x builtins with cscfg on module load */
> +int etm4_cscfg_load_builtin_cfg(void);
> +void etm4_cscfg_unload_builtin_cfg(void);
> +
> +/*
> + * Set of functions to find an available resource from @res->impl, not already marked as used
> + * in @res->used.
> + * return index and mark as used in @res->used. return -ENOSPC if nothing available.
> + */
> +
> +static inline int etm4_res_find_selector(struct cscfg_res_impl_used *res)
> +{
> + unsigned long *impl, *used;
> +
> + if (!res->impl.selectors)
> + return -ENOSPC;
> +
> + impl = (unsigned long *)&res->impl.selectors;
> + used = (unsigned long *)&res->used.selectors;
> + return etm4_cfg_find_unused_idx(impl, used, ETM_MAX_RES_SEL);
> +}
> +
> +static inline int etm4_res_find_counter(struct cscfg_res_impl_used *res)
> +{
> + unsigned long *impl, *used;
> +
> + if (!res->impl.counters)
> + return -ENOSPC;
> +
> + impl = (unsigned long *)&res->impl.counters;
> + used = (unsigned long *)&res->used.counters;
> + return etm4_cfg_find_unused_idx(impl, used, ETMv4_MAX_CNTR);
> +}
> +
> +static inline int etm4_res_find_addr_comparator(struct cscfg_res_impl_used *res)
> +{
> + unsigned long *impl, *used;
> +
> + if (!res->impl.addr_cmp)
> + return -ENOSPC;
> +
> + impl = (unsigned long *)&res->impl.addr_cmp;
> + used = (unsigned long *)&res->used.addr_cmp;
> + return etm4_cfg_find_unused_idx(impl, used, ETM_MAX_SINGLE_ADDR_CMP);
> +}
> +
> +
> +static inline int etm4_res_find_addr_comp_pair(struct cscfg_res_impl_used *res)
> +{
> + unsigned long *impl, *used;
> +
> + if (!res->impl.addr_cmp)
> + return -ENOSPC;
> +
> + impl = (unsigned long *)&res->impl.addr_cmp;
> + used = (unsigned long *)&res->used.addr_cmp;
> + return etm4_cfg_find_unused_idx_pair(impl, used, ETM_MAX_SINGLE_ADDR_CMP);
> +}
>
> #endif /* CORESIGHT_ETM4X_CFG_H */
> diff --git a/include/linux/coresight.h b/include/linux/coresight.h
> index a348049ee08b..b513964b9305 100644
> --- a/include/linux/coresight.h
> +++ b/include/linux/coresight.h
> @@ -223,6 +223,7 @@ struct coresight_sysfs_link {
> * @feature_csdev_list: List of complex feature programming added to the device.
> * @config_csdev_list: List of system configurations added to the device.
> * @active_cscfg_ctxt: Context information for current active system configuration.
> + * @cscfg_res_mask: Available device specific resources usable in features.
> */
> struct coresight_device {
> struct coresight_platform_data *pdata;
> @@ -248,6 +249,7 @@ struct coresight_device {
> struct list_head feature_csdev_list;
> struct list_head config_csdev_list;
> void *active_cscfg_ctxt;
> + void *cscfg_res_mask;
> };
>
> /*
> --
> 2.17.1
>
More information about the linux-arm-kernel
mailing list