1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright(C) 2020 Linaro Limited. All rights reserved.
4 * Author: Mike Leach <mike.leach@linaro.org>
5 */
6
7#include <linux/sysfs.h>
8#include "coresight-config.h"
9#include "coresight-priv.h"
10
11/*
12 * This provides a set of generic functions that operate on configurations
13 * and features to manage the handling of parameters, the programming and
14 * saving of registers used by features on devices.
15 */
16
17/*
18 * Write the value held in the register structure into the driver internal memory
19 * location.
20 */
21static void cscfg_set_reg(struct cscfg_regval_csdev *reg_csdev)
22{
23 u32 *p_val32 = (u32 *)reg_csdev->driver_regval;
24 u32 tmp32 = reg_csdev->reg_desc.val32;
25
26 if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT) {
27 *((u64 *)reg_csdev->driver_regval) = reg_csdev->reg_desc.val64;
28 return;
29 }
30
31 if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_MASK) {
32 tmp32 = *p_val32;
33 tmp32 &= ~reg_csdev->reg_desc.mask32;
34 tmp32 |= reg_csdev->reg_desc.val32 & reg_csdev->reg_desc.mask32;
35 }
36 *p_val32 = tmp32;
37}
38
39/*
40 * Read the driver value into the reg if this is marked as one we want to save.
41 */
42static void cscfg_save_reg(struct cscfg_regval_csdev *reg_csdev)
43{
44 if (!(reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_SAVE))
45 return;
46 if (reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT)
47 reg_csdev->reg_desc.val64 = *(u64 *)(reg_csdev->driver_regval);
48 else
49 reg_csdev->reg_desc.val32 = *(u32 *)(reg_csdev->driver_regval);
50}
51
52/*
53 * Some register values are set from parameters. Initialise these registers
54 * from the current parameter values.
55 */
56static void cscfg_init_reg_param(struct cscfg_feature_csdev *feat_csdev,
57 struct cscfg_regval_desc *reg_desc,
58 struct cscfg_regval_csdev *reg_csdev)
59{
60 struct cscfg_parameter_csdev *param_csdev;
61
62 /* for param, load routines have validated the index */
63 param_csdev = &feat_csdev->params_csdev[reg_desc->param_idx];
64 param_csdev->reg_csdev = reg_csdev;
65 param_csdev->val64 = reg_csdev->reg_desc.type & CS_CFG_REG_TYPE_VAL_64BIT;
66
67 if (param_csdev->val64)
68 reg_csdev->reg_desc.val64 = param_csdev->current_value;
69 else
70 reg_csdev->reg_desc.val32 = (u32)param_csdev->current_value;
71}
72
73/* set values into the driver locations referenced in cscfg_reg_csdev */
74static int cscfg_set_on_enable(struct cscfg_feature_csdev *feat_csdev)
75{
76 unsigned long flags;
77 int i;
78
79 spin_lock_irqsave(feat_csdev->drv_spinlock, flags);
80 for (i = 0; i < feat_csdev->nr_regs; i++)
81 cscfg_set_reg(reg_csdev: &feat_csdev->regs_csdev[i]);
82 spin_unlock_irqrestore(lock: feat_csdev->drv_spinlock, flags);
83 dev_dbg(&feat_csdev->csdev->dev, "Feature %s: %s",
84 feat_csdev->feat_desc->name, "set on enable");
85 return 0;
86}
87
88/* copy back values from the driver locations referenced in cscfg_reg_csdev */
89static void cscfg_save_on_disable(struct cscfg_feature_csdev *feat_csdev)
90{
91 unsigned long flags;
92 int i;
93
94 spin_lock_irqsave(feat_csdev->drv_spinlock, flags);
95 for (i = 0; i < feat_csdev->nr_regs; i++)
96 cscfg_save_reg(reg_csdev: &feat_csdev->regs_csdev[i]);
97 spin_unlock_irqrestore(lock: feat_csdev->drv_spinlock, flags);
98 dev_dbg(&feat_csdev->csdev->dev, "Feature %s: %s",
99 feat_csdev->feat_desc->name, "save on disable");
100}
101
102/* default reset - restore default values */
103void cscfg_reset_feat(struct cscfg_feature_csdev *feat_csdev)
104{
105 struct cscfg_regval_desc *reg_desc;
106 struct cscfg_regval_csdev *reg_csdev;
107 int i;
108
109 /*
110 * set the default values for all parameters and regs from the
111 * relevant static descriptors.
112 */
113 for (i = 0; i < feat_csdev->nr_params; i++)
114 feat_csdev->params_csdev[i].current_value =
115 feat_csdev->feat_desc->params_desc[i].value;
116
117 for (i = 0; i < feat_csdev->nr_regs; i++) {
118 reg_desc = &feat_csdev->feat_desc->regs_desc[i];
119 reg_csdev = &feat_csdev->regs_csdev[i];
120 reg_csdev->reg_desc.type = reg_desc->type;
121
122 /* check if reg set from a parameter otherwise desc default */
123 if (reg_desc->type & CS_CFG_REG_TYPE_VAL_PARAM)
124 cscfg_init_reg_param(feat_csdev, reg_desc, reg_csdev);
125 else
126 /*
127 * for normal values the union between val64 & val32 + mask32
128 * allows us to init using the 64 bit value
129 */
130 reg_csdev->reg_desc.val64 = reg_desc->val64;
131 }
132}
133
134/*
135 * For the selected presets, we set the register associated with the parameter, to
136 * the value of the preset index associated with the parameter.
137 */
138static int cscfg_update_presets(struct cscfg_config_csdev *config_csdev, int preset)
139{
140 int i, j, val_idx = 0, nr_cfg_params;
141 struct cscfg_parameter_csdev *param_csdev;
142 struct cscfg_feature_csdev *feat_csdev;
143 const struct cscfg_config_desc *config_desc = config_csdev->config_desc;
144 const char *name;
145 const u64 *preset_base;
146 u64 val;
147
148 /* preset in range 1 to nr_presets */
149 if (preset < 1 || preset > config_desc->nr_presets)
150 return -EINVAL;
151 /*
152 * Go through the array of features, assigning preset values to
153 * feature parameters in the order they appear.
154 * There should be precisely the same number of preset values as the
155 * sum of number of parameters over all the features - but we will
156 * ensure there is no overrun.
157 */
158 nr_cfg_params = config_desc->nr_total_params;
159 preset_base = &config_desc->presets[(preset - 1) * nr_cfg_params];
160 for (i = 0; i < config_csdev->nr_feat; i++) {
161 feat_csdev = config_csdev->feats_csdev[i];
162 if (!feat_csdev->nr_params)
163 continue;
164
165 for (j = 0; j < feat_csdev->nr_params; j++) {
166 param_csdev = &feat_csdev->params_csdev[j];
167 name = feat_csdev->feat_desc->params_desc[j].name;
168 val = preset_base[val_idx++];
169 if (param_csdev->val64) {
170 dev_dbg(&config_csdev->csdev->dev,
171 "set param %s (%lld)", name, val);
172 param_csdev->reg_csdev->reg_desc.val64 = val;
173 } else {
174 param_csdev->reg_csdev->reg_desc.val32 = (u32)val;
175 dev_dbg(&config_csdev->csdev->dev,
176 "set param %s (%d)", name, (u32)val);
177 }
178 }
179
180 /* exit early if all params filled */
181 if (val_idx >= nr_cfg_params)
182 break;
183 }
184 return 0;
185}
186
187/*
188 * if we are not using a preset, then need to update the feature params
189 * with current values. This sets the register associated with the parameter
190 * with the current value of that parameter.
191 */
192static int cscfg_update_curr_params(struct cscfg_config_csdev *config_csdev)
193{
194 int i, j;
195 struct cscfg_feature_csdev *feat_csdev;
196 struct cscfg_parameter_csdev *param_csdev;
197 const char *name;
198 u64 val;
199
200 for (i = 0; i < config_csdev->nr_feat; i++) {
201 feat_csdev = config_csdev->feats_csdev[i];
202 if (!feat_csdev->nr_params)
203 continue;
204 for (j = 0; j < feat_csdev->nr_params; j++) {
205 param_csdev = &feat_csdev->params_csdev[j];
206 name = feat_csdev->feat_desc->params_desc[j].name;
207 val = param_csdev->current_value;
208 if (param_csdev->val64) {
209 dev_dbg(&config_csdev->csdev->dev,
210 "set param %s (%lld)", name, val);
211 param_csdev->reg_csdev->reg_desc.val64 = val;
212 } else {
213 param_csdev->reg_csdev->reg_desc.val32 = (u32)val;
214 dev_dbg(&config_csdev->csdev->dev,
215 "set param %s (%d)", name, (u32)val);
216 }
217 }
218 }
219 return 0;
220}
221
222/*
223 * Configuration values will be programmed into the driver locations if enabling, or read
224 * from relevant locations on disable.
225 */
226static int cscfg_prog_config(struct cscfg_config_csdev *config_csdev, bool enable)
227{
228 int i, err = 0;
229 struct cscfg_feature_csdev *feat_csdev;
230 struct coresight_device *csdev;
231
232 for (i = 0; i < config_csdev->nr_feat; i++) {
233 feat_csdev = config_csdev->feats_csdev[i];
234 csdev = feat_csdev->csdev;
235 dev_dbg(&csdev->dev, "cfg %s; %s feature:%s", config_csdev->config_desc->name,
236 enable ? "enable" : "disable", feat_csdev->feat_desc->name);
237
238 if (enable)
239 err = cscfg_set_on_enable(feat_csdev);
240 else
241 cscfg_save_on_disable(feat_csdev);
242
243 if (err)
244 break;
245 }
246 return err;
247}
248
249/*
250 * Enable configuration for the device. Will result in the internal driver data
251 * being updated ready for programming into the device.
252 *
253 * @config_csdev: config_csdev to set.
254 * @preset: preset values to use - 0 for default.
255 */
256int cscfg_csdev_enable_config(struct cscfg_config_csdev *config_csdev, int preset)
257{
258 int err = 0;
259
260 if (preset)
261 err = cscfg_update_presets(config_csdev, preset);
262 else
263 err = cscfg_update_curr_params(config_csdev);
264 if (!err)
265 err = cscfg_prog_config(config_csdev, enable: true);
266 return err;
267}
268
269void cscfg_csdev_disable_config(struct cscfg_config_csdev *config_csdev)
270{
271 cscfg_prog_config(config_csdev, enable: false);
272}
273

source code of linux/drivers/hwtracing/coresight/coresight-config.c