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/configfs.h>
8  
9  #include "coresight-config.h"
10  #include "coresight-syscfg-configfs.h"
11  
12  /* create a default ci_type. */
cscfg_create_ci_type(void)13  static inline struct config_item_type *cscfg_create_ci_type(void)
14  {
15  	struct config_item_type *ci_type;
16  
17  	ci_type = devm_kzalloc(cscfg_device(), sizeof(*ci_type), GFP_KERNEL);
18  	if (ci_type)
19  		ci_type->ct_owner = THIS_MODULE;
20  
21  	return ci_type;
22  }
23  
24  /* configurations sub-group */
25  
26  /* attributes for the config view group */
cscfg_cfg_description_show(struct config_item * item,char * page)27  static ssize_t cscfg_cfg_description_show(struct config_item *item, char *page)
28  {
29  	struct cscfg_fs_config *fs_config = container_of(to_config_group(item),
30  							 struct cscfg_fs_config, group);
31  
32  	return scnprintf(page, PAGE_SIZE, "%s", fs_config->config_desc->description);
33  }
34  CONFIGFS_ATTR_RO(cscfg_cfg_, description);
35  
cscfg_cfg_feature_refs_show(struct config_item * item,char * page)36  static ssize_t cscfg_cfg_feature_refs_show(struct config_item *item, char *page)
37  {
38  	struct cscfg_fs_config *fs_config = container_of(to_config_group(item),
39  							 struct cscfg_fs_config, group);
40  	const struct cscfg_config_desc *config_desc = fs_config->config_desc;
41  	ssize_t ch_used = 0;
42  	int i;
43  
44  	for (i = 0; i < config_desc->nr_feat_refs; i++)
45  		ch_used += scnprintf(page + ch_used, PAGE_SIZE - ch_used,
46  				     "%s\n", config_desc->feat_ref_names[i]);
47  	return ch_used;
48  }
49  CONFIGFS_ATTR_RO(cscfg_cfg_, feature_refs);
50  
51  /* list preset values in order of features and params */
cscfg_cfg_values_show(struct config_item * item,char * page)52  static ssize_t cscfg_cfg_values_show(struct config_item *item, char *page)
53  {
54  	const struct cscfg_feature_desc *feat_desc;
55  	const struct cscfg_config_desc *config_desc;
56  	struct cscfg_fs_preset *fs_preset;
57  	int i, j, val_idx, preset_idx;
58  	ssize_t used = 0;
59  
60  	fs_preset = container_of(to_config_group(item), struct cscfg_fs_preset, group);
61  	config_desc = fs_preset->config_desc;
62  
63  	if (!config_desc->nr_presets)
64  		return 0;
65  
66  	preset_idx = fs_preset->preset_num - 1;
67  
68  	/* start index on the correct array line */
69  	val_idx = config_desc->nr_total_params * preset_idx;
70  
71  	/*
72  	 * A set of presets is the sum of all params in used features,
73  	 * in order of declaration of features and params in the features
74  	 */
75  	for (i = 0; i < config_desc->nr_feat_refs; i++) {
76  		feat_desc = cscfg_get_named_feat_desc(config_desc->feat_ref_names[i]);
77  		for (j = 0; j < feat_desc->nr_params; j++) {
78  			used += scnprintf(page + used, PAGE_SIZE - used,
79  					  "%s.%s = 0x%llx ",
80  					  feat_desc->name,
81  					  feat_desc->params_desc[j].name,
82  					  config_desc->presets[val_idx++]);
83  		}
84  	}
85  	used += scnprintf(page + used, PAGE_SIZE - used, "\n");
86  
87  	return used;
88  }
89  CONFIGFS_ATTR_RO(cscfg_cfg_, values);
90  
cscfg_cfg_enable_show(struct config_item * item,char * page)91  static ssize_t cscfg_cfg_enable_show(struct config_item *item, char *page)
92  {
93  	struct cscfg_fs_config *fs_config = container_of(to_config_group(item),
94  							 struct cscfg_fs_config, group);
95  
96  	return scnprintf(page, PAGE_SIZE, "%d\n", fs_config->active);
97  }
98  
cscfg_cfg_enable_store(struct config_item * item,const char * page,size_t count)99  static ssize_t cscfg_cfg_enable_store(struct config_item *item,
100  					const char *page, size_t count)
101  {
102  	struct cscfg_fs_config *fs_config = container_of(to_config_group(item),
103  							 struct cscfg_fs_config, group);
104  	int err;
105  	bool val;
106  
107  	err = kstrtobool(page, &val);
108  	if (!err)
109  		err = cscfg_config_sysfs_activate(fs_config->config_desc, val);
110  	if (!err) {
111  		fs_config->active = val;
112  		if (val)
113  			cscfg_config_sysfs_set_preset(fs_config->preset);
114  	}
115  	return err ? err : count;
116  }
117  CONFIGFS_ATTR(cscfg_cfg_, enable);
118  
cscfg_cfg_preset_show(struct config_item * item,char * page)119  static ssize_t cscfg_cfg_preset_show(struct config_item *item, char *page)
120  {
121  	struct cscfg_fs_config *fs_config = container_of(to_config_group(item),
122  							 struct cscfg_fs_config, group);
123  
124  	return scnprintf(page, PAGE_SIZE, "%d\n", fs_config->preset);
125  }
126  
cscfg_cfg_preset_store(struct config_item * item,const char * page,size_t count)127  static ssize_t cscfg_cfg_preset_store(struct config_item *item,
128  					     const char *page, size_t count)
129  {
130  	struct cscfg_fs_config *fs_config = container_of(to_config_group(item),
131  							 struct cscfg_fs_config, group);
132  	int preset, err;
133  
134  	err = kstrtoint(page, 0, &preset);
135  	if (!err) {
136  		/*
137  		 * presets start at 1, and go up to max (15),
138  		 * but the config may provide fewer.
139  		 */
140  		if ((preset < 1) || (preset > fs_config->config_desc->nr_presets))
141  			err = -EINVAL;
142  	}
143  
144  	if (!err) {
145  		/* set new value */
146  		fs_config->preset = preset;
147  		/* set on system if active */
148  		if (fs_config->active)
149  			cscfg_config_sysfs_set_preset(fs_config->preset);
150  	}
151  	return err ? err : count;
152  }
153  CONFIGFS_ATTR(cscfg_cfg_, preset);
154  
155  static struct configfs_attribute *cscfg_config_view_attrs[] = {
156  	&cscfg_cfg_attr_description,
157  	&cscfg_cfg_attr_feature_refs,
158  	&cscfg_cfg_attr_enable,
159  	&cscfg_cfg_attr_preset,
160  	NULL,
161  };
162  
163  static struct config_item_type cscfg_config_view_type = {
164  	.ct_owner = THIS_MODULE,
165  	.ct_attrs = cscfg_config_view_attrs,
166  };
167  
168  static struct configfs_attribute *cscfg_config_preset_attrs[] = {
169  	&cscfg_cfg_attr_values,
170  	NULL,
171  };
172  
173  static struct config_item_type cscfg_config_preset_type = {
174  	.ct_owner = THIS_MODULE,
175  	.ct_attrs = cscfg_config_preset_attrs,
176  };
177  
cscfg_add_preset_groups(struct cscfg_fs_config * cfg_view)178  static int cscfg_add_preset_groups(struct cscfg_fs_config *cfg_view)
179  {
180  	int preset_num;
181  	struct cscfg_fs_preset *cfg_fs_preset;
182  	struct cscfg_config_desc *config_desc = cfg_view->config_desc;
183  	char name[CONFIGFS_ITEM_NAME_LEN];
184  
185  	if (!config_desc->nr_presets)
186  		return 0;
187  
188  	for (preset_num = 1; preset_num <= config_desc->nr_presets; preset_num++) {
189  		cfg_fs_preset = devm_kzalloc(cscfg_device(),
190  					     sizeof(struct cscfg_fs_preset), GFP_KERNEL);
191  
192  		if (!cfg_fs_preset)
193  			return -ENOMEM;
194  
195  		snprintf(name, CONFIGFS_ITEM_NAME_LEN, "preset%d", preset_num);
196  		cfg_fs_preset->preset_num = preset_num;
197  		cfg_fs_preset->config_desc = cfg_view->config_desc;
198  		config_group_init_type_name(&cfg_fs_preset->group, name,
199  					    &cscfg_config_preset_type);
200  		configfs_add_default_group(&cfg_fs_preset->group, &cfg_view->group);
201  	}
202  	return 0;
203  }
204  
cscfg_create_config_group(struct cscfg_config_desc * config_desc)205  static struct config_group *cscfg_create_config_group(struct cscfg_config_desc *config_desc)
206  {
207  	struct cscfg_fs_config *cfg_view;
208  	struct device *dev = cscfg_device();
209  	int err;
210  
211  	if (!dev)
212  		return ERR_PTR(-EINVAL);
213  
214  	cfg_view = devm_kzalloc(dev, sizeof(struct cscfg_fs_config), GFP_KERNEL);
215  	if (!cfg_view)
216  		return ERR_PTR(-ENOMEM);
217  
218  	cfg_view->config_desc = config_desc;
219  	config_group_init_type_name(&cfg_view->group, config_desc->name, &cscfg_config_view_type);
220  
221  	/* add in a preset<n> dir for each preset */
222  	err = cscfg_add_preset_groups(cfg_view);
223  	if (err)
224  		return ERR_PTR(err);
225  
226  	return &cfg_view->group;
227  }
228  
229  /* attributes for features view */
230  
cscfg_feat_description_show(struct config_item * item,char * page)231  static ssize_t cscfg_feat_description_show(struct config_item *item, char *page)
232  {
233  	struct cscfg_fs_feature *fs_feat = container_of(to_config_group(item),
234  							struct cscfg_fs_feature, group);
235  
236  	return scnprintf(page, PAGE_SIZE, "%s", fs_feat->feat_desc->description);
237  }
238  CONFIGFS_ATTR_RO(cscfg_feat_, description);
239  
cscfg_feat_matches_show(struct config_item * item,char * page)240  static ssize_t cscfg_feat_matches_show(struct config_item *item, char *page)
241  {
242  	struct cscfg_fs_feature *fs_feat = container_of(to_config_group(item),
243  							struct cscfg_fs_feature, group);
244  	u32 match_flags = fs_feat->feat_desc->match_flags;
245  	int used = 0;
246  
247  	if (match_flags & CS_CFG_MATCH_CLASS_SRC_ALL)
248  		used = scnprintf(page, PAGE_SIZE, "SRC_ALL ");
249  
250  	if (match_flags & CS_CFG_MATCH_CLASS_SRC_ETM4)
251  		used += scnprintf(page + used, PAGE_SIZE - used, "SRC_ETMV4 ");
252  
253  	used += scnprintf(page + used, PAGE_SIZE - used, "\n");
254  	return used;
255  }
256  CONFIGFS_ATTR_RO(cscfg_feat_, matches);
257  
cscfg_feat_nr_params_show(struct config_item * item,char * page)258  static ssize_t cscfg_feat_nr_params_show(struct config_item *item, char *page)
259  {
260  	struct cscfg_fs_feature *fs_feat = container_of(to_config_group(item),
261  							struct cscfg_fs_feature, group);
262  
263  	return scnprintf(page, PAGE_SIZE, "%d\n", fs_feat->feat_desc->nr_params);
264  }
265  CONFIGFS_ATTR_RO(cscfg_feat_, nr_params);
266  
267  /* base feature desc attrib structures */
268  static struct configfs_attribute *cscfg_feature_view_attrs[] = {
269  	&cscfg_feat_attr_description,
270  	&cscfg_feat_attr_matches,
271  	&cscfg_feat_attr_nr_params,
272  	NULL,
273  };
274  
275  static struct config_item_type cscfg_feature_view_type = {
276  	.ct_owner = THIS_MODULE,
277  	.ct_attrs = cscfg_feature_view_attrs,
278  };
279  
cscfg_param_value_show(struct config_item * item,char * page)280  static ssize_t cscfg_param_value_show(struct config_item *item, char *page)
281  {
282  	struct cscfg_fs_param *param_item = container_of(to_config_group(item),
283  							 struct cscfg_fs_param, group);
284  	u64 value = param_item->feat_desc->params_desc[param_item->param_idx].value;
285  
286  	return scnprintf(page, PAGE_SIZE, "0x%llx\n", value);
287  }
288  
cscfg_param_value_store(struct config_item * item,const char * page,size_t size)289  static ssize_t cscfg_param_value_store(struct config_item *item,
290  				       const char *page, size_t size)
291  {
292  	struct cscfg_fs_param *param_item = container_of(to_config_group(item),
293  							 struct cscfg_fs_param, group);
294  	struct cscfg_feature_desc *feat_desc = param_item->feat_desc;
295  	int param_idx = param_item->param_idx;
296  	u64 value;
297  	int err;
298  
299  	err = kstrtoull(page, 0, &value);
300  	if (!err)
301  		err = cscfg_update_feat_param_val(feat_desc, param_idx, value);
302  
303  	return err ? err : size;
304  }
305  CONFIGFS_ATTR(cscfg_param_, value);
306  
307  static struct configfs_attribute *cscfg_param_view_attrs[] = {
308  	&cscfg_param_attr_value,
309  	NULL,
310  };
311  
312  static struct config_item_type cscfg_param_view_type = {
313  	.ct_owner = THIS_MODULE,
314  	.ct_attrs = cscfg_param_view_attrs,
315  };
316  
317  /*
318   * configfs has far less functionality provided to add attributes dynamically than sysfs,
319   * and the show and store fns pass the enclosing config_item so the actual attribute cannot
320   * be determined. Therefore we add each item as a group directory, with a value attribute.
321   */
cscfg_create_params_group_items(struct cscfg_feature_desc * feat_desc,struct config_group * params_group)322  static int cscfg_create_params_group_items(struct cscfg_feature_desc *feat_desc,
323  					   struct config_group *params_group)
324  {
325  	struct device *dev = cscfg_device();
326  	struct cscfg_fs_param *param_item;
327  	int i;
328  
329  	/* parameter items - as groups with default_value attribute */
330  	for (i = 0; i < feat_desc->nr_params; i++) {
331  		param_item = devm_kzalloc(dev, sizeof(struct cscfg_fs_param), GFP_KERNEL);
332  		if (!param_item)
333  			return -ENOMEM;
334  		param_item->feat_desc = feat_desc;
335  		param_item->param_idx = i;
336  		config_group_init_type_name(&param_item->group,
337  					    feat_desc->params_desc[i].name,
338  					    &cscfg_param_view_type);
339  		configfs_add_default_group(&param_item->group, params_group);
340  	}
341  	return 0;
342  }
343  
cscfg_create_feature_group(struct cscfg_feature_desc * feat_desc)344  static struct config_group *cscfg_create_feature_group(struct cscfg_feature_desc *feat_desc)
345  {
346  	struct cscfg_fs_feature *feat_view;
347  	struct config_item_type *params_group_type;
348  	struct config_group *params_group = NULL;
349  	struct device *dev = cscfg_device();
350  	int item_err;
351  
352  	if (!dev)
353  		return ERR_PTR(-EINVAL);
354  
355  	feat_view = devm_kzalloc(dev, sizeof(struct cscfg_fs_feature), GFP_KERNEL);
356  	if (!feat_view)
357  		return ERR_PTR(-ENOMEM);
358  
359  	if (feat_desc->nr_params) {
360  		params_group = devm_kzalloc(dev, sizeof(struct config_group), GFP_KERNEL);
361  		if (!params_group)
362  			return ERR_PTR(-ENOMEM);
363  
364  		params_group_type = cscfg_create_ci_type();
365  		if (!params_group_type)
366  			return ERR_PTR(-ENOMEM);
367  	}
368  
369  	feat_view->feat_desc = feat_desc;
370  	config_group_init_type_name(&feat_view->group,
371  				    feat_desc->name,
372  				    &cscfg_feature_view_type);
373  	if (params_group) {
374  		config_group_init_type_name(params_group, "params", params_group_type);
375  		configfs_add_default_group(params_group, &feat_view->group);
376  		item_err = cscfg_create_params_group_items(feat_desc, params_group);
377  		if (item_err)
378  			return ERR_PTR(item_err);
379  	}
380  	return &feat_view->group;
381  }
382  
383  static struct config_item_type cscfg_configs_type = {
384  	.ct_owner = THIS_MODULE,
385  };
386  
387  static struct config_group cscfg_configs_grp = {
388  	.cg_item = {
389  		.ci_namebuf = "configurations",
390  		.ci_type = &cscfg_configs_type,
391  	},
392  };
393  
394  /* add configuration to configurations group */
cscfg_configfs_add_config(struct cscfg_config_desc * config_desc)395  int cscfg_configfs_add_config(struct cscfg_config_desc *config_desc)
396  {
397  	struct config_group *new_group;
398  	int err;
399  
400  	new_group = cscfg_create_config_group(config_desc);
401  	if (IS_ERR(new_group))
402  		return PTR_ERR(new_group);
403  	err =  configfs_register_group(&cscfg_configs_grp, new_group);
404  	if (!err)
405  		config_desc->fs_group = new_group;
406  	return err;
407  }
408  
cscfg_configfs_del_config(struct cscfg_config_desc * config_desc)409  void cscfg_configfs_del_config(struct cscfg_config_desc *config_desc)
410  {
411  	if (config_desc->fs_group) {
412  		configfs_unregister_group(config_desc->fs_group);
413  		config_desc->fs_group = NULL;
414  	}
415  }
416  
417  static struct config_item_type cscfg_features_type = {
418  	.ct_owner = THIS_MODULE,
419  };
420  
421  static struct config_group cscfg_features_grp = {
422  	.cg_item = {
423  		.ci_namebuf = "features",
424  		.ci_type = &cscfg_features_type,
425  	},
426  };
427  
428  /* add feature to features group */
cscfg_configfs_add_feature(struct cscfg_feature_desc * feat_desc)429  int cscfg_configfs_add_feature(struct cscfg_feature_desc *feat_desc)
430  {
431  	struct config_group *new_group;
432  	int err;
433  
434  	new_group = cscfg_create_feature_group(feat_desc);
435  	if (IS_ERR(new_group))
436  		return PTR_ERR(new_group);
437  	err =  configfs_register_group(&cscfg_features_grp, new_group);
438  	if (!err)
439  		feat_desc->fs_group = new_group;
440  	return err;
441  }
442  
cscfg_configfs_del_feature(struct cscfg_feature_desc * feat_desc)443  void cscfg_configfs_del_feature(struct cscfg_feature_desc *feat_desc)
444  {
445  	if (feat_desc->fs_group) {
446  		configfs_unregister_group(feat_desc->fs_group);
447  		feat_desc->fs_group = NULL;
448  	}
449  }
450  
cscfg_configfs_init(struct cscfg_manager * cscfg_mgr)451  int cscfg_configfs_init(struct cscfg_manager *cscfg_mgr)
452  {
453  	struct configfs_subsystem *subsys;
454  	struct config_item_type *ci_type;
455  
456  	if (!cscfg_mgr)
457  		return -EINVAL;
458  
459  	ci_type = cscfg_create_ci_type();
460  	if (!ci_type)
461  		return -ENOMEM;
462  
463  	subsys = &cscfg_mgr->cfgfs_subsys;
464  	config_item_set_name(&subsys->su_group.cg_item, CSCFG_FS_SUBSYS_NAME);
465  	subsys->su_group.cg_item.ci_type = ci_type;
466  
467  	config_group_init(&subsys->su_group);
468  	mutex_init(&subsys->su_mutex);
469  
470  	/* Add default groups to subsystem */
471  	config_group_init(&cscfg_configs_grp);
472  	configfs_add_default_group(&cscfg_configs_grp, &subsys->su_group);
473  
474  	config_group_init(&cscfg_features_grp);
475  	configfs_add_default_group(&cscfg_features_grp, &subsys->su_group);
476  
477  	return configfs_register_subsystem(subsys);
478  }
479  
cscfg_configfs_release(struct cscfg_manager * cscfg_mgr)480  void cscfg_configfs_release(struct cscfg_manager *cscfg_mgr)
481  {
482  	configfs_unregister_subsystem(&cscfg_mgr->cfgfs_subsys);
483  }
484