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. */
13static 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 */
27static 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}
34CONFIGFS_ATTR_RO(cscfg_cfg_, description);
35
36static 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}
49CONFIGFS_ATTR_RO(cscfg_cfg_, feature_refs);
50
51/* list preset values in order of features and params */
52static 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}
89CONFIGFS_ATTR_RO(cscfg_cfg_, values);
90
91static 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
99static 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}
117CONFIGFS_ATTR(cscfg_cfg_, enable);
118
119static 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
127static 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}
153CONFIGFS_ATTR(cscfg_cfg_, preset);
154
155static 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
163static struct config_item_type cscfg_config_view_type = {
164	.ct_owner = THIS_MODULE,
165	.ct_attrs = cscfg_config_view_attrs,
166};
167
168static struct configfs_attribute *cscfg_config_preset_attrs[] = {
169	&cscfg_cfg_attr_values,
170	NULL,
171};
172
173static struct config_item_type cscfg_config_preset_type = {
174	.ct_owner = THIS_MODULE,
175	.ct_attrs = cscfg_config_preset_attrs,
176};
177
178static 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
205static 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
231static 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}
238CONFIGFS_ATTR_RO(cscfg_feat_, description);
239
240static 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}
256CONFIGFS_ATTR_RO(cscfg_feat_, matches);
257
258static 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}
265CONFIGFS_ATTR_RO(cscfg_feat_, nr_params);
266
267/* base feature desc attrib structures */
268static 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
275static struct config_item_type cscfg_feature_view_type = {
276	.ct_owner = THIS_MODULE,
277	.ct_attrs = cscfg_feature_view_attrs,
278};
279
280static 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
289static 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}
305CONFIGFS_ATTR(cscfg_param_, value);
306
307static struct configfs_attribute *cscfg_param_view_attrs[] = {
308	&cscfg_param_attr_value,
309	NULL,
310};
311
312static 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 */
322static 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
344static 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
383static struct config_item_type cscfg_configs_type = {
384	.ct_owner = THIS_MODULE,
385};
386
387static 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 */
395int 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
409void 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
417static struct config_item_type cscfg_features_type = {
418	.ct_owner = THIS_MODULE,
419};
420
421static 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 */
429int 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
443void 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
451int 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
480void cscfg_configfs_release(struct cscfg_manager *cscfg_mgr)
481{
482	configfs_unregister_subsystem(&cscfg_mgr->cfgfs_subsys);
483}
484