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 | */ |
21 | static 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 | */ |
42 | static 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 | */ |
56 | static 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 */ |
74 | static 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 */ |
89 | static 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 */ |
103 | void 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 | */ |
138 | static 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 | */ |
192 | static 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 | */ |
226 | static 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 | */ |
256 | int 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 | |
269 | void cscfg_csdev_disable_config(struct cscfg_config_csdev *config_csdev) |
270 | { |
271 | cscfg_prog_config(config_csdev, enable: false); |
272 | } |
273 | |