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