| // 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); |
| } |