484 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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 features74 */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 cannot320 * 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(¶m_item->group,337 feat_desc->params_desc[i].name,338 &cscfg_param_view_type);339 configfs_add_default_group(¶m_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