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(&feat_csdev->regs_csdev[i]);
82 	spin_unlock_irqrestore(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(&feat_csdev->regs_csdev[i]);
97 	spin_unlock_irqrestore(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, 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, false);
272 }
273