diff mbox series

[RFC,5/8] coresight: syscfg: Add API to check and validate device resources.

Message ID 20210512211752.4103-6-mike.leach@linaro.org (mailing list archive)
State New, archived
Headers show
Series coresight: syscfg: dynamic load, resource management | expand

Commit Message

Mike Leach May 12, 2021, 9:17 p.m. UTC
Adds in API to allow features which define device resources, to check
resource availability on load, and allocate on enable.

1) Check on load ensures that the resources required by the feature do
not exceed the maximum avialable on the device.

2) Allocate on enable ensures that sufficient unused resources are
available to satifsfy the feature on enable. Allocate can also be
used to resolve any resource selection requirements - i.e. select
an available resource from a pool of resources.

Signed-off-by: Mike Leach <mike.leach@linaro.org>
---
 .../hwtracing/coresight/coresight-config.c    | 71 +++++++++++++++---
 .../hwtracing/coresight/coresight-config.h    | 36 +++++++++-
 .../hwtracing/coresight/coresight-syscfg.c    | 72 +++++++++++++++++--
 3 files changed, 163 insertions(+), 16 deletions(-)

Comments

Mathieu Poirier May 21, 2021, 5:56 p.m. UTC | #1
Hi Mike,
 
On Wed, May 12, 2021 at 10:17:49PM +0100, Mike Leach wrote:
> Adds in API to allow features which define device resources, to check
> resource availability on load, and allocate on enable.
> 
> 1) Check on load ensures that the resources required by the feature do
> not exceed the maximum avialable on the device.
> 
> 2) Allocate on enable ensures that sufficient unused resources are
> available to satifsfy the feature on enable. Allocate can also be
> used to resolve any resource selection requirements - i.e. select
> an available resource from a pool of resources.
> 
> Signed-off-by: Mike Leach <mike.leach@linaro.org>
> ---
>  .../hwtracing/coresight/coresight-config.c    | 71 +++++++++++++++---
>  .../hwtracing/coresight/coresight-config.h    | 36 +++++++++-
>  .../hwtracing/coresight/coresight-syscfg.c    | 72 +++++++++++++++++--
>  3 files changed, 163 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c
> index 3c501e027bc0..fdfda1975188 100644
> --- a/drivers/hwtracing/coresight/coresight-config.c
> +++ b/drivers/hwtracing/coresight/coresight-config.c
> @@ -23,6 +23,10 @@ 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;
>  
> +	/* resource mapped registers may have custom handling in the device */
> +	if (!reg_csdev->driver_regval)
> +		return;
> +

I am curious as to why it is needed now and wasn't in the patchset that
introduced the complex configuration feature.

>  	if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT) {
>  		*((u64 *)reg_csdev->driver_regval) = reg_csdev->reg_desc.val64;
>  		return;
> @@ -43,6 +47,8 @@ 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->driver_regval)
> +		return;
>  	if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT)
>  		reg_csdev->reg_desc.val64 = *(u64 *)(reg_csdev->driver_regval);
>  	else
> @@ -73,15 +79,20 @@ static void cscfg_init_reg_param(struct cscfg_feature_csdev *feat_csdev,
>  /* set values into the driver locations referenced in cscfg_reg_csdev */
>  static int cscfg_set_on_enable(struct cscfg_feature_csdev *feat_csdev)
>  {
> -	int i;
> +	int i, err = 0;
>  
>  	spin_lock(feat_csdev->drv_spinlock);
>  	for (i = 0; i < feat_csdev->nr_regs; i++)
>  		cscfg_set_reg(&feat_csdev->regs_csdev[i]);
>  	spin_unlock(feat_csdev->drv_spinlock);
> +
> +	if (feat_csdev->feat_ops->set_on_enable)
> +		err = feat_csdev->feat_ops->set_on_enable(feat_csdev);
> +
>  	dev_dbg(&feat_csdev->csdev->dev, "Feature %s: %s",
> -		feat_csdev->feat_desc->name, "set on enable");
> -	return 0;
> +		feat_csdev->feat_desc->name,
> +		err ? "error on enable" : "set on enable");
> +	return err;
>  }
>  
>  /* copy back values from the driver locations referenced in cscfg_reg_csdev */
> @@ -89,6 +100,9 @@ static void cscfg_save_on_disable(struct cscfg_feature_csdev *feat_csdev)
>  {
>  	int i;
>  
> +	if (feat_csdev->feat_ops->clear_on_disable)
> +		feat_csdev->feat_ops->clear_on_disable(feat_csdev);
> +
>  	spin_lock(feat_csdev->drv_spinlock);
>  	for (i = 0; i < feat_csdev->nr_regs; i++)
>  		cscfg_save_reg(&feat_csdev->regs_csdev[i]);
> @@ -217,6 +231,37 @@ static int cscfg_update_curr_params(struct cscfg_config_csdev *config_csdev)
>  	return 0;
>  }
>  
> +static void cscfg_clear_res_config(struct cscfg_config_csdev *config_csdev)
> +{
> +	int i;
> +	struct cscfg_feature_csdev *feat_csdev;
> +
> +	for (i = 0; i < config_csdev->nr_feat; i++) {
> +		feat_csdev = config_csdev->feats_csdev[i];
> +		if (feat_csdev->feat_ops->clear_feat_res)
> +			feat_csdev->feat_ops->clear_feat_res(feat_csdev);
> +	}
> +}
> +
> +#define FEAT_ALLOC_RES_FMT "coresight-syscfg: Insufficient resource to enable feature %s\n"
> +static int cscfg_alloc_res_config(struct cscfg_config_csdev *config_csdev)
> +{
> +	int err = 0, i;
> +	struct cscfg_feature_csdev *feat_csdev;
> +
> +	for (i = 0; i < config_csdev->nr_feat; i++) {
> +		feat_csdev = config_csdev->feats_csdev[i];
> +		if (feat_csdev->feat_ops->alloc_feat_res)
> +			err = feat_csdev->feat_ops->alloc_feat_res(feat_csdev);
> +		if (err) {
> +			pr_warn(FEAT_ALLOC_RES_FMT, feat_csdev->feat_desc->name);
> +			cscfg_clear_res_config(config_csdev);
> +			break;
> +		}
> +	}
> +	return err;
> +}
> +
>  /*
>   * Configuration values will be programmed into the driver locations if enabling, or read
>   * from relevant locations on disable.
> @@ -227,19 +272,29 @@ static int cscfg_prog_config(struct cscfg_config_csdev *config_csdev, bool enabl
>  	struct cscfg_feature_csdev *feat_csdev;
>  	struct coresight_device *csdev;
>  
> +	/* check we have resources to enable this */
> +	if (enable) {
> +		err = cscfg_alloc_res_config(config_csdev);
> +		if (err)
> +			return err;
> +	} else
> +		cscfg_clear_res_config(config_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)
> +		if (enable) {
>  			err = cscfg_set_on_enable(feat_csdev);
> -		else
> +			if (err) {
> +				/* failed to enable - release any resources */
> +				cscfg_clear_res_config(config_csdev);
> +				break;
> +			}
> +		} else
>  			cscfg_save_on_disable(feat_csdev);
> -
> -		if (err)
> -			break;

Is all of the above related to what this patchset is providing or an enhancement
of the original feature?

>  	}
>  	return err;
>  }
> diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h
> index 9bd44b940add..0e46832df993 100644
> --- a/drivers/hwtracing/coresight/coresight-config.h
> +++ b/drivers/hwtracing/coresight/coresight-config.h
> @@ -199,6 +199,8 @@ struct cscfg_parameter_csdev {
>   * @params_csdev:	current parameter values on this device
>   * @nr_regs:		number of registers to be programmed.
>   * @regs_csdev:		Programming details for the registers
> + * @res_used:		Device specific context for resources used on enable.
> + * @feat_ops:		Feature operations to support alloc/release of resources.
>   */
>  struct cscfg_feature_csdev {
>  	const struct cscfg_feature_desc *feat_desc;
> @@ -209,6 +211,8 @@ struct cscfg_feature_csdev {
>  	struct cscfg_parameter_csdev *params_csdev;
>  	int nr_regs;
>  	struct cscfg_regval_csdev *regs_csdev;
> +	void *res_used;

This isn't used in this patch.  Please move it to the patch that will do so.

> +	struct cscfg_csdev_feat_ops *feat_ops;
>  };
>  
>  /**
> @@ -238,14 +242,44 @@ struct cscfg_config_csdev {
>   * Coresight device operations.
>   *
>   * Registered coresight devices provide these operations to manage feature
> - * instances compatible with the device hardware and drivers
> + * instances compatible with the device hardware and drivers.
> + *
> + * The @check_feat_res function can be used at load time to see if the device has
> + * sufficient resources to support the feature. This takes into account all resources
> + * on the device. e.g. if the feature requires three counters, and the device has a

s/device./device,

> + * total of two implemented counters, this will return a -ENODEV error.
> + *
> + * The @alloc_feat_res function checks if there are sufficient unallocated resources
> + * left to enable the feature. This allows for multiple features being loaded that
> + * may compete for resources and also selects and allocates resources at enable time.

This is the second time so I'm wondering if this is an accepted way of writing
things in english.  

> + * e.g. if a feature requires two comparators, and there is only one left unallocated,
> + * then this will return a -ENOSPC error.
>   *
>   * @load_feat:	Pass a feature descriptor into the device and create the
>   *		loaded feature instance (struct cscfg_feature_csdev).
> + * @check_feat_res: Check that the coresight device has sufficient resources to
> + *		    load the feature. Optional function. Return -ENODEV if this device
> + *                  does not have enough resources. Called before @load_feat for feature
> + *		    to ensure all feature can be installed on device.
> + * @alloc_feat_res: Allocate release feature resources when enabling a feature on the device
> + *		  . Optional function. Returns -ENOSPC if the device has not enough available

The '.' at the beginning of the line looks wierd.

> + *		    resources to enable the feature. Called before registers programmed.
> + * @clear_feat_res: Release allocated resources. Must be implemented if @alloc_feat_res is
> + *		    implemented.
> + * @set_on_enable : Optional programming specific to device. Called immediately after
> + *		    generic register programming operation.
> + * @clear_on_disable: Optional programming specific to device. Called before generic register
> + *		      save operation.
>   */
>  struct cscfg_csdev_feat_ops {
>  	int (*load_feat)(struct coresight_device *csdev,
>  			 struct cscfg_feature_csdev *feat_csdev);
> +	int (*check_feat_res)(struct coresight_device *csdev,
> +			      struct cscfg_feature_desc *feat_desc);
> +	int (*alloc_feat_res)(struct cscfg_feature_csdev *feat_csdev);
> +	void (*clear_feat_res)(struct cscfg_feature_csdev *feat_csdev);
> +	int (*set_on_enable)(struct cscfg_feature_csdev *feat_csdev);
> +	void (*clear_on_disable)(struct cscfg_feature_csdev *feat_csdev);
>  };
>  
>  /* coresight config helper functions*/
> diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c
> index ab74e33b892b..984459c8f168 100644
> --- a/drivers/hwtracing/coresight/coresight-syscfg.c
> +++ b/drivers/hwtracing/coresight/coresight-syscfg.c
> @@ -121,7 +121,8 @@ static int cscfg_add_cfg_to_csdevs(struct cscfg_config_desc *config_desc)
>   * 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)
> +cscfg_alloc_csdev_feat(struct coresight_device *csdev, struct cscfg_feature_desc *feat_desc,
> +		       struct cscfg_csdev_feat_ops *ops)
>  {
>  	struct cscfg_feature_csdev *feat_csdev = NULL;
>  	struct device *dev = csdev->dev.parent;
> @@ -165,9 +166,16 @@ cscfg_alloc_csdev_feat(struct coresight_device *csdev, struct cscfg_feature_desc
>  	if (!feat_csdev->regs_csdev)
>  		return NULL;
>  
> +	/* copy the static register info */
> +	for (i = 0; i < feat_desc->nr_regs; i++) {
> +		memcpy(&feat_csdev->regs_csdev[i].reg_desc,
> +		       &feat_desc->regs_desc[i], sizeof(struct cscfg_regval_desc));
> +	}
> +

I really spend a lot of time on this snippet, especially since it duplicates
some of the work done in cscfg_reset_feat() and that we now have two places where
things get initialised.

>  	/* load the feature default values */
>  	feat_csdev->feat_desc = feat_desc;
>  	feat_csdev->csdev = csdev;
> +	feat_csdev->feat_ops = ops;
>  
>  	return feat_csdev;
>  }
> @@ -180,10 +188,7 @@ static int cscfg_load_feat_csdev(struct coresight_device *csdev,
>  	struct cscfg_feature_csdev *feat_csdev;
>  	int err;
>  
> -	if (!ops->load_feat)
> -		return -EINVAL;
> -
> -	feat_csdev = cscfg_alloc_csdev_feat(csdev, feat_desc);
> +	feat_csdev = cscfg_alloc_csdev_feat(csdev, feat_desc, ops);
>  	if (!feat_csdev)
>  		return -ENOMEM;
>  
> @@ -201,6 +206,57 @@ static int cscfg_load_feat_csdev(struct coresight_device *csdev,
>  	return 0;
>  }
>  
> +#define WARN_RES_FMT "coresight-syscfg: Insufficient resources to load feature %s in device %s\n"
> +#define PARAM_ERR_FMT "coresight-syscfg: Cannot load feature %s. Invalid parameter index\n"
> +
> +/*
> + * Check if device supports resource check function, and check for sufficient resources
> + * to load feature onto device. Validate parameter references.
> + *
> + * Load feature if sufficient resources & valid parameter references.
> + */
> +static int cscfg_check_feat_res_load(struct coresight_device *csdev,
> +				     struct cscfg_feature_desc *feat_desc,
> +				     struct cscfg_csdev_feat_ops *ops)
> +{
> +	int err = 0, i;
> +
> +	/* if we cannot load - fail early */
> +	if (!ops->load_feat)
> +		return -EINVAL;
> +
> +	/* ensure matched resource ops */
> +	if ((ops->alloc_feat_res && !ops->clear_feat_res) ||
> +	    (!ops->alloc_feat_res && ops->clear_feat_res))
> +		return -EINVAL;
> +
> +	/* ensure that the coresight device can support the feature */
> +	if (ops->check_feat_res) {
> +		err = ops->check_feat_res(csdev, feat_desc);
> +		if (err == -ENODEV) {
> +			/* treat as mismatch, warn and continue */
> +			pr_warn(WARN_RES_FMT, feat_desc->name,
> +				dev_name(&csdev->dev));
> +			return 0;
> +		}
> +	}
> +
> +	/* check parameter indexes are valid */
> +	for (i = 0; i < feat_desc->nr_regs; i++) {
> +		if (feat_desc->regs_desc[i].type & CS_CFG_REG_TYPE_VAL_PARAM) {
> +			if (feat_desc->regs_desc[i].param_idx >= feat_desc->nr_params) {
> +				pr_err(PARAM_ERR_FMT, feat_desc->name);
> +				return -EINVAL;
> +			}
> +		}
> +	}
> +
> +	/* sufficient resources - load if no other error */
> +	if (!err)
> +		err = cscfg_load_feat_csdev(csdev, feat_desc, ops);
> +	return err;
> +}
> +
>  /*
>   * Add feature to any matching devices - call with mutex locked.
>   * Iterates through devices - any device that matches the feature will be
> @@ -213,7 +269,9 @@ static int cscfg_add_feat_to_csdevs(struct cscfg_feature_desc *feat_desc)
>  
>  	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);
> +			/* check sufficient resources and load feature */
> +			err = cscfg_check_feat_res_load(csdev_item->csdev, feat_desc,
> +							&csdev_item->ops);

Checking the resources and loading them are two different operations and as such
we should keep them separate.  It will also make it easier to understand and
review this set. 

I will come back to all the operations introduced by struct cscfg_csdev_feat_ops
once I have a better understanding of what they do.

More comments to come next week.

Mathieu

>  			if (err)
>  				return err;
>  		}
> @@ -616,7 +674,7 @@ static int cscfg_add_feats_csdev(struct coresight_device *csdev,
>  
>  	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);
> +			err = cscfg_check_feat_res_load(csdev, feat_desc, ops);
>  			if (err)
>  				break;
>  		}
> -- 
> 2.17.1
>
Mike Leach July 8, 2021, 4:30 p.m. UTC | #2
Hi Mathieu,

On Fri, 21 May 2021 at 18:56, Mathieu Poirier
<mathieu.poirier@linaro.org> wrote:
>
> Hi Mike,
>
> On Wed, May 12, 2021 at 10:17:49PM +0100, Mike Leach wrote:
> > Adds in API to allow features which define device resources, to check
> > resource availability on load, and allocate on enable.
> >
> > 1) Check on load ensures that the resources required by the feature do
> > not exceed the maximum avialable on the device.
> >
> > 2) Allocate on enable ensures that sufficient unused resources are
> > available to satifsfy the feature on enable. Allocate can also be
> > used to resolve any resource selection requirements - i.e. select
> > an available resource from a pool of resources.
> >
> > Signed-off-by: Mike Leach <mike.leach@linaro.org>
> > ---
> >  .../hwtracing/coresight/coresight-config.c    | 71 +++++++++++++++---
> >  .../hwtracing/coresight/coresight-config.h    | 36 +++++++++-
> >  .../hwtracing/coresight/coresight-syscfg.c    | 72 +++++++++++++++++--
> >  3 files changed, 163 insertions(+), 16 deletions(-)
> >
> > diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c
> > index 3c501e027bc0..fdfda1975188 100644
> > --- a/drivers/hwtracing/coresight/coresight-config.c
> > +++ b/drivers/hwtracing/coresight/coresight-config.c
> > @@ -23,6 +23,10 @@ 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;
> >
> > +     /* resource mapped registers may have custom handling in the device */
> > +     if (!reg_csdev->driver_regval)
> > +             return;
> > +
>
> I am curious as to why it is needed now and wasn't in the patchset that
> introduced the complex configuration feature.
>

This is here now because we are adding in resource managemenrt with
this set. It was omitted from the earlier set, simply to reduce the
size and complexity of that set.

Resource mapped registers all a user to specify a resource to be used
(e.g. comparator) without specifying the particular comparator to use.
The enable routines will then find the first available resource and
use that.
This sort of thing happens already in the ETMv4 driver - but this set
expands and enhances this functionality.


> >       if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT) {
> >               *((u64 *)reg_csdev->driver_regval) = reg_csdev->reg_desc.val64;
> >               return;
> > @@ -43,6 +47,8 @@ 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->driver_regval)
> > +             return;
> >       if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT)
> >               reg_csdev->reg_desc.val64 = *(u64 *)(reg_csdev->driver_regval);
> >       else
> > @@ -73,15 +79,20 @@ static void cscfg_init_reg_param(struct cscfg_feature_csdev *feat_csdev,
> >  /* set values into the driver locations referenced in cscfg_reg_csdev */
> >  static int cscfg_set_on_enable(struct cscfg_feature_csdev *feat_csdev)
> >  {
> > -     int i;
> > +     int i, err = 0;
> >
> >       spin_lock(feat_csdev->drv_spinlock);
> >       for (i = 0; i < feat_csdev->nr_regs; i++)
> >               cscfg_set_reg(&feat_csdev->regs_csdev[i]);
> >       spin_unlock(feat_csdev->drv_spinlock);
> > +
> > +     if (feat_csdev->feat_ops->set_on_enable)
> > +             err = feat_csdev->feat_ops->set_on_enable(feat_csdev);
> > +
> >       dev_dbg(&feat_csdev->csdev->dev, "Feature %s: %s",
> > -             feat_csdev->feat_desc->name, "set on enable");
> > -     return 0;
> > +             feat_csdev->feat_desc->name,
> > +             err ? "error on enable" : "set on enable");
> > +     return err;
> >  }
> >
> >  /* copy back values from the driver locations referenced in cscfg_reg_csdev */
> > @@ -89,6 +100,9 @@ static void cscfg_save_on_disable(struct cscfg_feature_csdev *feat_csdev)
> >  {
> >       int i;
> >
> > +     if (feat_csdev->feat_ops->clear_on_disable)
> > +             feat_csdev->feat_ops->clear_on_disable(feat_csdev);
> > +
> >       spin_lock(feat_csdev->drv_spinlock);
> >       for (i = 0; i < feat_csdev->nr_regs; i++)
> >               cscfg_save_reg(&feat_csdev->regs_csdev[i]);
> > @@ -217,6 +231,37 @@ static int cscfg_update_curr_params(struct cscfg_config_csdev *config_csdev)
> >       return 0;
> >  }
> >
> > +static void cscfg_clear_res_config(struct cscfg_config_csdev *config_csdev)
> > +{
> > +     int i;
> > +     struct cscfg_feature_csdev *feat_csdev;
> > +
> > +     for (i = 0; i < config_csdev->nr_feat; i++) {
> > +             feat_csdev = config_csdev->feats_csdev[i];
> > +             if (feat_csdev->feat_ops->clear_feat_res)
> > +                     feat_csdev->feat_ops->clear_feat_res(feat_csdev);
> > +     }
> > +}
> > +
> > +#define FEAT_ALLOC_RES_FMT "coresight-syscfg: Insufficient resource to enable feature %s\n"
> > +static int cscfg_alloc_res_config(struct cscfg_config_csdev *config_csdev)
> > +{
> > +     int err = 0, i;
> > +     struct cscfg_feature_csdev *feat_csdev;
> > +
> > +     for (i = 0; i < config_csdev->nr_feat; i++) {
> > +             feat_csdev = config_csdev->feats_csdev[i];
> > +             if (feat_csdev->feat_ops->alloc_feat_res)
> > +                     err = feat_csdev->feat_ops->alloc_feat_res(feat_csdev);
> > +             if (err) {
> > +                     pr_warn(FEAT_ALLOC_RES_FMT, feat_csdev->feat_desc->name);
> > +                     cscfg_clear_res_config(config_csdev);
> > +                     break;
> > +             }
> > +     }
> > +     return err;
> > +}
> > +
> >  /*
> >   * Configuration values will be programmed into the driver locations if enabling, or read
> >   * from relevant locations on disable.
> > @@ -227,19 +272,29 @@ static int cscfg_prog_config(struct cscfg_config_csdev *config_csdev, bool enabl
> >       struct cscfg_feature_csdev *feat_csdev;
> >       struct coresight_device *csdev;
> >
> > +     /* check we have resources to enable this */
> > +     if (enable) {
> > +             err = cscfg_alloc_res_config(config_csdev);
> > +             if (err)
> > +                     return err;
> > +     } else
> > +             cscfg_clear_res_config(config_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)
> > +             if (enable) {
> >                       err = cscfg_set_on_enable(feat_csdev);
> > -             else
> > +                     if (err) {
> > +                             /* failed to enable - release any resources */
> > +                             cscfg_clear_res_config(config_csdev);
> > +                             break;
> > +                     }
> > +             } else
> >                       cscfg_save_on_disable(feat_csdev);
> > -
> > -             if (err)
> > -                     break;
>
> Is all of the above related to what this patchset is providing or an enhancement
> of the original feature?
>

Again - an enhancment to allow for resource management within the
coresight devices


> >       }
> >       return err;
> >  }
> > diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h
> > index 9bd44b940add..0e46832df993 100644
> > --- a/drivers/hwtracing/coresight/coresight-config.h
> > +++ b/drivers/hwtracing/coresight/coresight-config.h
> > @@ -199,6 +199,8 @@ struct cscfg_parameter_csdev {
> >   * @params_csdev:    current parameter values on this device
> >   * @nr_regs:         number of registers to be programmed.
> >   * @regs_csdev:              Programming details for the registers
> > + * @res_used:                Device specific context for resources used on enable.
> > + * @feat_ops:                Feature operations to support alloc/release of resources.
> >   */
> >  struct cscfg_feature_csdev {
> >       const struct cscfg_feature_desc *feat_desc;
> > @@ -209,6 +211,8 @@ struct cscfg_feature_csdev {
> >       struct cscfg_parameter_csdev *params_csdev;
> >       int nr_regs;
> >       struct cscfg_regval_csdev *regs_csdev;
> > +     void *res_used;
>
> This isn't used in this patch.  Please move it to the patch that will do so.
>

OK
> > +     struct cscfg_csdev_feat_ops *feat_ops;
> >  };
> >
> >  /**
> > @@ -238,14 +242,44 @@ struct cscfg_config_csdev {
> >   * Coresight device operations.
> >   *
> >   * Registered coresight devices provide these operations to manage feature
> > - * instances compatible with the device hardware and drivers
> > + * instances compatible with the device hardware and drivers.
> > + *
> > + * The @check_feat_res function can be used at load time to see if the device has
> > + * sufficient resources to support the feature. This takes into account all resources
> > + * on the device. e.g. if the feature requires three counters, and the device has a
>
> s/device./device,
>
> > + * total of two implemented counters, this will return a -ENODEV error.
> > + *
> > + * The @alloc_feat_res function checks if there are sufficient unallocated resources
> > + * left to enable the feature. This allows for multiple features being loaded that
> > + * may compete for resources and also selects and allocates resources at enable time.
>
> This is the second time so I'm wondering if this is an accepted way of writing
> things in english.
>

Not sure there are any hard and fast rules here - I am reading it as...

....elects and allocates resources at enable time. For example, if a
feature requires....

Perhaps it would be better if I used the expanded version of 'e.g.' here.


> > + * e.g. if a feature requires two comparators, and there is only one left unallocated,
> > + * then this will return a -ENOSPC error.
> >   *
> >   * @load_feat:       Pass a feature descriptor into the device and create the
> >   *           loaded feature instance (struct cscfg_feature_csdev).
> > + * @check_feat_res: Check that the coresight device has sufficient resources to
> > + *               load the feature. Optional function. Return -ENODEV if this device
> > + *                  does not have enough resources. Called before @load_feat for feature
> > + *               to ensure all feature can be installed on device.
> > + * @alloc_feat_res: Allocate release feature resources when enabling a feature on the device
> > + *             . Optional function. Returns -ENOSPC if the device has not enough available
>
> The '.' at the beginning of the line looks wierd.
OK

>
> > + *               resources to enable the feature. Called before registers programmed.
> > + * @clear_feat_res: Release allocated resources. Must be implemented if @alloc_feat_res is
> > + *               implemented.
> > + * @set_on_enable : Optional programming specific to device. Called immediately after
> > + *               generic register programming operation.
> > + * @clear_on_disable: Optional programming specific to device. Called before generic register
> > + *                 save operation.
> >   */
> >  struct cscfg_csdev_feat_ops {
> >       int (*load_feat)(struct coresight_device *csdev,
> >                        struct cscfg_feature_csdev *feat_csdev);
> > +     int (*check_feat_res)(struct coresight_device *csdev,
> > +                           struct cscfg_feature_desc *feat_desc);
> > +     int (*alloc_feat_res)(struct cscfg_feature_csdev *feat_csdev);
> > +     void (*clear_feat_res)(struct cscfg_feature_csdev *feat_csdev);
> > +     int (*set_on_enable)(struct cscfg_feature_csdev *feat_csdev);
> > +     void (*clear_on_disable)(struct cscfg_feature_csdev *feat_csdev);
> >  };
> >
> >  /* coresight config helper functions*/
> > diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c
> > index ab74e33b892b..984459c8f168 100644
> > --- a/drivers/hwtracing/coresight/coresight-syscfg.c
> > +++ b/drivers/hwtracing/coresight/coresight-syscfg.c
> > @@ -121,7 +121,8 @@ static int cscfg_add_cfg_to_csdevs(struct cscfg_config_desc *config_desc)
> >   * 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)
> > +cscfg_alloc_csdev_feat(struct coresight_device *csdev, struct cscfg_feature_desc *feat_desc,
> > +                    struct cscfg_csdev_feat_ops *ops)
> >  {
> >       struct cscfg_feature_csdev *feat_csdev = NULL;
> >       struct device *dev = csdev->dev.parent;
> > @@ -165,9 +166,16 @@ cscfg_alloc_csdev_feat(struct coresight_device *csdev, struct cscfg_feature_desc
> >       if (!feat_csdev->regs_csdev)
> >               return NULL;
> >
> > +     /* copy the static register info */
> > +     for (i = 0; i < feat_desc->nr_regs; i++) {
> > +             memcpy(&feat_csdev->regs_csdev[i].reg_desc,
> > +                    &feat_desc->regs_desc[i], sizeof(struct cscfg_regval_desc));
> > +     }
> > +
>
> I really spend a lot of time on this snippet, especially since it duplicates
> some of the work done in cscfg_reset_feat() and that we now have two places where
> things get initialised.

OK - I'll revisit this - not sure quite why I thought this was needed
here - other than that it moves more of the registered typing and
usage information than is moved by the reset_feat(), which is
important for resource management.

This resource info is essentially static - so there is benefit in only
moving it once - and rather than move only that it is simple at load
time to move everything. reset_feat resets that register _values_ and
parameterised values, that might have been altered by reading back
from the hardware (counters) or set by configfs.

If there is something that needs to be here and not in reset_feats
then I'll expand the comment .

>
> >       /* load the feature default values */
> >       feat_csdev->feat_desc = feat_desc;
> >       feat_csdev->csdev = csdev;
> > +     feat_csdev->feat_ops = ops;
> >
> >       return feat_csdev;
> >  }
> > @@ -180,10 +188,7 @@ static int cscfg_load_feat_csdev(struct coresight_device *csdev,
> >       struct cscfg_feature_csdev *feat_csdev;
> >       int err;
> >
> > -     if (!ops->load_feat)
> > -             return -EINVAL;
> > -
> > -     feat_csdev = cscfg_alloc_csdev_feat(csdev, feat_desc);
> > +     feat_csdev = cscfg_alloc_csdev_feat(csdev, feat_desc, ops);
> >       if (!feat_csdev)
> >               return -ENOMEM;
> >
> > @@ -201,6 +206,57 @@ static int cscfg_load_feat_csdev(struct coresight_device *csdev,
> >       return 0;
> >  }
> >
> > +#define WARN_RES_FMT "coresight-syscfg: Insufficient resources to load feature %s in device %s\n"
> > +#define PARAM_ERR_FMT "coresight-syscfg: Cannot load feature %s. Invalid parameter index\n"
> > +
> > +/*
> > + * Check if device supports resource check function, and check for sufficient resources
> > + * to load feature onto device. Validate parameter references.
> > + *
> > + * Load feature if sufficient resources & valid parameter references.
> > + */
> > +static int cscfg_check_feat_res_load(struct coresight_device *csdev,
> > +                                  struct cscfg_feature_desc *feat_desc,
> > +                                  struct cscfg_csdev_feat_ops *ops)
> > +{
> > +     int err = 0, i;
> > +
> > +     /* if we cannot load - fail early */
> > +     if (!ops->load_feat)
> > +             return -EINVAL;
> > +
> > +     /* ensure matched resource ops */
> > +     if ((ops->alloc_feat_res && !ops->clear_feat_res) ||
> > +         (!ops->alloc_feat_res && ops->clear_feat_res))
> > +             return -EINVAL;
> > +
> > +     /* ensure that the coresight device can support the feature */
> > +     if (ops->check_feat_res) {
> > +             err = ops->check_feat_res(csdev, feat_desc);
> > +             if (err == -ENODEV) {
> > +                     /* treat as mismatch, warn and continue */
> > +                     pr_warn(WARN_RES_FMT, feat_desc->name,
> > +                             dev_name(&csdev->dev));
> > +                     return 0;
> > +             }
> > +     }
> > +
> > +     /* check parameter indexes are valid */
> > +     for (i = 0; i < feat_desc->nr_regs; i++) {
> > +             if (feat_desc->regs_desc[i].type & CS_CFG_REG_TYPE_VAL_PARAM) {
> > +                     if (feat_desc->regs_desc[i].param_idx >= feat_desc->nr_params) {
> > +                             pr_err(PARAM_ERR_FMT, feat_desc->name);
> > +                             return -EINVAL;
> > +                     }
> > +             }
> > +     }
> > +
> > +     /* sufficient resources - load if no other error */
> > +     if (!err)
> > +             err = cscfg_load_feat_csdev(csdev, feat_desc, ops);
> > +     return err;
> > +}
> > +
> >  /*
> >   * Add feature to any matching devices - call with mutex locked.
> >   * Iterates through devices - any device that matches the feature will be
> > @@ -213,7 +269,9 @@ static int cscfg_add_feat_to_csdevs(struct cscfg_feature_desc *feat_desc)
> >
> >       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);
> > +                     /* check sufficient resources and load feature */
> > +                     err = cscfg_check_feat_res_load(csdev_item->csdev, feat_desc,
> > +                                                     &csdev_item->ops);
>
> Checking the resources and loading them are two different operations and as such
> we should keep them separate.  It will also make it easier to understand and
> review this set.

Will revisit this.

Regards

Mike

>I will come back to all the operations introduced by struct cscfg_csdev_feat_ops
> once I have a better understanding of what they do.
>
> More comments to come next week.
>
> Mathieu
>
> >                       if (err)
> >                               return err;
> >               }
> > @@ -616,7 +674,7 @@ static int cscfg_add_feats_csdev(struct coresight_device *csdev,
> >
> >       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);
> > +                     err = cscfg_check_feat_res_load(csdev, feat_desc, ops);
> >                       if (err)
> >                               break;
> >               }
> > --
> > 2.17.1
> >
diff mbox series

Patch

diff --git a/drivers/hwtracing/coresight/coresight-config.c b/drivers/hwtracing/coresight/coresight-config.c
index 3c501e027bc0..fdfda1975188 100644
--- a/drivers/hwtracing/coresight/coresight-config.c
+++ b/drivers/hwtracing/coresight/coresight-config.c
@@ -23,6 +23,10 @@  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;
 
+	/* resource mapped registers may have custom handling in the device */
+	if (!reg_csdev->driver_regval)
+		return;
+
 	if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT) {
 		*((u64 *)reg_csdev->driver_regval) = reg_csdev->reg_desc.val64;
 		return;
@@ -43,6 +47,8 @@  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->driver_regval)
+		return;
 	if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT)
 		reg_csdev->reg_desc.val64 = *(u64 *)(reg_csdev->driver_regval);
 	else
@@ -73,15 +79,20 @@  static void cscfg_init_reg_param(struct cscfg_feature_csdev *feat_csdev,
 /* set values into the driver locations referenced in cscfg_reg_csdev */
 static int cscfg_set_on_enable(struct cscfg_feature_csdev *feat_csdev)
 {
-	int i;
+	int i, err = 0;
 
 	spin_lock(feat_csdev->drv_spinlock);
 	for (i = 0; i < feat_csdev->nr_regs; i++)
 		cscfg_set_reg(&feat_csdev->regs_csdev[i]);
 	spin_unlock(feat_csdev->drv_spinlock);
+
+	if (feat_csdev->feat_ops->set_on_enable)
+		err = feat_csdev->feat_ops->set_on_enable(feat_csdev);
+
 	dev_dbg(&feat_csdev->csdev->dev, "Feature %s: %s",
-		feat_csdev->feat_desc->name, "set on enable");
-	return 0;
+		feat_csdev->feat_desc->name,
+		err ? "error on enable" : "set on enable");
+	return err;
 }
 
 /* copy back values from the driver locations referenced in cscfg_reg_csdev */
@@ -89,6 +100,9 @@  static void cscfg_save_on_disable(struct cscfg_feature_csdev *feat_csdev)
 {
 	int i;
 
+	if (feat_csdev->feat_ops->clear_on_disable)
+		feat_csdev->feat_ops->clear_on_disable(feat_csdev);
+
 	spin_lock(feat_csdev->drv_spinlock);
 	for (i = 0; i < feat_csdev->nr_regs; i++)
 		cscfg_save_reg(&feat_csdev->regs_csdev[i]);
@@ -217,6 +231,37 @@  static int cscfg_update_curr_params(struct cscfg_config_csdev *config_csdev)
 	return 0;
 }
 
+static void cscfg_clear_res_config(struct cscfg_config_csdev *config_csdev)
+{
+	int i;
+	struct cscfg_feature_csdev *feat_csdev;
+
+	for (i = 0; i < config_csdev->nr_feat; i++) {
+		feat_csdev = config_csdev->feats_csdev[i];
+		if (feat_csdev->feat_ops->clear_feat_res)
+			feat_csdev->feat_ops->clear_feat_res(feat_csdev);
+	}
+}
+
+#define FEAT_ALLOC_RES_FMT "coresight-syscfg: Insufficient resource to enable feature %s\n"
+static int cscfg_alloc_res_config(struct cscfg_config_csdev *config_csdev)
+{
+	int err = 0, i;
+	struct cscfg_feature_csdev *feat_csdev;
+
+	for (i = 0; i < config_csdev->nr_feat; i++) {
+		feat_csdev = config_csdev->feats_csdev[i];
+		if (feat_csdev->feat_ops->alloc_feat_res)
+			err = feat_csdev->feat_ops->alloc_feat_res(feat_csdev);
+		if (err) {
+			pr_warn(FEAT_ALLOC_RES_FMT, feat_csdev->feat_desc->name);
+			cscfg_clear_res_config(config_csdev);
+			break;
+		}
+	}
+	return err;
+}
+
 /*
  * Configuration values will be programmed into the driver locations if enabling, or read
  * from relevant locations on disable.
@@ -227,19 +272,29 @@  static int cscfg_prog_config(struct cscfg_config_csdev *config_csdev, bool enabl
 	struct cscfg_feature_csdev *feat_csdev;
 	struct coresight_device *csdev;
 
+	/* check we have resources to enable this */
+	if (enable) {
+		err = cscfg_alloc_res_config(config_csdev);
+		if (err)
+			return err;
+	} else
+		cscfg_clear_res_config(config_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)
+		if (enable) {
 			err = cscfg_set_on_enable(feat_csdev);
-		else
+			if (err) {
+				/* failed to enable - release any resources */
+				cscfg_clear_res_config(config_csdev);
+				break;
+			}
+		} else
 			cscfg_save_on_disable(feat_csdev);
-
-		if (err)
-			break;
 	}
 	return err;
 }
diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h
index 9bd44b940add..0e46832df993 100644
--- a/drivers/hwtracing/coresight/coresight-config.h
+++ b/drivers/hwtracing/coresight/coresight-config.h
@@ -199,6 +199,8 @@  struct cscfg_parameter_csdev {
  * @params_csdev:	current parameter values on this device
  * @nr_regs:		number of registers to be programmed.
  * @regs_csdev:		Programming details for the registers
+ * @res_used:		Device specific context for resources used on enable.
+ * @feat_ops:		Feature operations to support alloc/release of resources.
  */
 struct cscfg_feature_csdev {
 	const struct cscfg_feature_desc *feat_desc;
@@ -209,6 +211,8 @@  struct cscfg_feature_csdev {
 	struct cscfg_parameter_csdev *params_csdev;
 	int nr_regs;
 	struct cscfg_regval_csdev *regs_csdev;
+	void *res_used;
+	struct cscfg_csdev_feat_ops *feat_ops;
 };
 
 /**
@@ -238,14 +242,44 @@  struct cscfg_config_csdev {
  * Coresight device operations.
  *
  * Registered coresight devices provide these operations to manage feature
- * instances compatible with the device hardware and drivers
+ * instances compatible with the device hardware and drivers.
+ *
+ * The @check_feat_res function can be used at load time to see if the device has
+ * sufficient resources to support the feature. This takes into account all resources
+ * on the device. e.g. if the feature requires three counters, and the device has a
+ * total of two implemented counters, this will return a -ENODEV error.
+ *
+ * The @alloc_feat_res function checks if there are sufficient unallocated resources
+ * left to enable the feature. This allows for multiple features being loaded that
+ * may compete for resources and also selects and allocates resources at enable time.
+ * e.g. if a feature requires two comparators, and there is only one left unallocated,
+ * then this will return a -ENOSPC error.
  *
  * @load_feat:	Pass a feature descriptor into the device and create the
  *		loaded feature instance (struct cscfg_feature_csdev).
+ * @check_feat_res: Check that the coresight device has sufficient resources to
+ *		    load the feature. Optional function. Return -ENODEV if this device
+ *                  does not have enough resources. Called before @load_feat for feature
+ *		    to ensure all feature can be installed on device.
+ * @alloc_feat_res: Allocate release feature resources when enabling a feature on the device
+ *		  . Optional function. Returns -ENOSPC if the device has not enough available
+ *		    resources to enable the feature. Called before registers programmed.
+ * @clear_feat_res: Release allocated resources. Must be implemented if @alloc_feat_res is
+ *		    implemented.
+ * @set_on_enable : Optional programming specific to device. Called immediately after
+ *		    generic register programming operation.
+ * @clear_on_disable: Optional programming specific to device. Called before generic register
+ *		      save operation.
  */
 struct cscfg_csdev_feat_ops {
 	int (*load_feat)(struct coresight_device *csdev,
 			 struct cscfg_feature_csdev *feat_csdev);
+	int (*check_feat_res)(struct coresight_device *csdev,
+			      struct cscfg_feature_desc *feat_desc);
+	int (*alloc_feat_res)(struct cscfg_feature_csdev *feat_csdev);
+	void (*clear_feat_res)(struct cscfg_feature_csdev *feat_csdev);
+	int (*set_on_enable)(struct cscfg_feature_csdev *feat_csdev);
+	void (*clear_on_disable)(struct cscfg_feature_csdev *feat_csdev);
 };
 
 /* coresight config helper functions*/
diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c
index ab74e33b892b..984459c8f168 100644
--- a/drivers/hwtracing/coresight/coresight-syscfg.c
+++ b/drivers/hwtracing/coresight/coresight-syscfg.c
@@ -121,7 +121,8 @@  static int cscfg_add_cfg_to_csdevs(struct cscfg_config_desc *config_desc)
  * 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)
+cscfg_alloc_csdev_feat(struct coresight_device *csdev, struct cscfg_feature_desc *feat_desc,
+		       struct cscfg_csdev_feat_ops *ops)
 {
 	struct cscfg_feature_csdev *feat_csdev = NULL;
 	struct device *dev = csdev->dev.parent;
@@ -165,9 +166,16 @@  cscfg_alloc_csdev_feat(struct coresight_device *csdev, struct cscfg_feature_desc
 	if (!feat_csdev->regs_csdev)
 		return NULL;
 
+	/* copy the static register info */
+	for (i = 0; i < feat_desc->nr_regs; i++) {
+		memcpy(&feat_csdev->regs_csdev[i].reg_desc,
+		       &feat_desc->regs_desc[i], sizeof(struct cscfg_regval_desc));
+	}
+
 	/* load the feature default values */
 	feat_csdev->feat_desc = feat_desc;
 	feat_csdev->csdev = csdev;
+	feat_csdev->feat_ops = ops;
 
 	return feat_csdev;
 }
@@ -180,10 +188,7 @@  static int cscfg_load_feat_csdev(struct coresight_device *csdev,
 	struct cscfg_feature_csdev *feat_csdev;
 	int err;
 
-	if (!ops->load_feat)
-		return -EINVAL;
-
-	feat_csdev = cscfg_alloc_csdev_feat(csdev, feat_desc);
+	feat_csdev = cscfg_alloc_csdev_feat(csdev, feat_desc, ops);
 	if (!feat_csdev)
 		return -ENOMEM;
 
@@ -201,6 +206,57 @@  static int cscfg_load_feat_csdev(struct coresight_device *csdev,
 	return 0;
 }
 
+#define WARN_RES_FMT "coresight-syscfg: Insufficient resources to load feature %s in device %s\n"
+#define PARAM_ERR_FMT "coresight-syscfg: Cannot load feature %s. Invalid parameter index\n"
+
+/*
+ * Check if device supports resource check function, and check for sufficient resources
+ * to load feature onto device. Validate parameter references.
+ *
+ * Load feature if sufficient resources & valid parameter references.
+ */
+static int cscfg_check_feat_res_load(struct coresight_device *csdev,
+				     struct cscfg_feature_desc *feat_desc,
+				     struct cscfg_csdev_feat_ops *ops)
+{
+	int err = 0, i;
+
+	/* if we cannot load - fail early */
+	if (!ops->load_feat)
+		return -EINVAL;
+
+	/* ensure matched resource ops */
+	if ((ops->alloc_feat_res && !ops->clear_feat_res) ||
+	    (!ops->alloc_feat_res && ops->clear_feat_res))
+		return -EINVAL;
+
+	/* ensure that the coresight device can support the feature */
+	if (ops->check_feat_res) {
+		err = ops->check_feat_res(csdev, feat_desc);
+		if (err == -ENODEV) {
+			/* treat as mismatch, warn and continue */
+			pr_warn(WARN_RES_FMT, feat_desc->name,
+				dev_name(&csdev->dev));
+			return 0;
+		}
+	}
+
+	/* check parameter indexes are valid */
+	for (i = 0; i < feat_desc->nr_regs; i++) {
+		if (feat_desc->regs_desc[i].type & CS_CFG_REG_TYPE_VAL_PARAM) {
+			if (feat_desc->regs_desc[i].param_idx >= feat_desc->nr_params) {
+				pr_err(PARAM_ERR_FMT, feat_desc->name);
+				return -EINVAL;
+			}
+		}
+	}
+
+	/* sufficient resources - load if no other error */
+	if (!err)
+		err = cscfg_load_feat_csdev(csdev, feat_desc, ops);
+	return err;
+}
+
 /*
  * Add feature to any matching devices - call with mutex locked.
  * Iterates through devices - any device that matches the feature will be
@@ -213,7 +269,9 @@  static int cscfg_add_feat_to_csdevs(struct cscfg_feature_desc *feat_desc)
 
 	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);
+			/* check sufficient resources and load feature */
+			err = cscfg_check_feat_res_load(csdev_item->csdev, feat_desc,
+							&csdev_item->ops);
 			if (err)
 				return err;
 		}
@@ -616,7 +674,7 @@  static int cscfg_add_feats_csdev(struct coresight_device *csdev,
 
 	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);
+			err = cscfg_check_feat_res_load(csdev, feat_desc, ops);
 			if (err)
 				break;
 		}