// SPDX-License-Identifier: GPL-2.0
/*
* Copyright(C) 2020 Linaro Limited. All rights reserved.
* Author: Mike Leach <[email protected]>
*/
#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);
}