forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
272 lines
8.1 KiB
272 lines
8.1 KiB
// 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); |
|
}
|
|
|