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; + 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; }