1177 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Generic Counter sysfs interface4 * Copyright (C) 2020 William Breathitt Gray5 */6#include <linux/counter.h>7#include <linux/device.h>8#include <linux/err.h>9#include <linux/gfp.h>10#include <linux/kernel.h>11#include <linux/kfifo.h>12#include <linux/kstrtox.h>13#include <linux/list.h>14#include <linux/mutex.h>15#include <linux/spinlock.h>16#include <linux/string.h>17#include <linux/sysfs.h>18#include <linux/types.h>19 20#include "counter-sysfs.h"21 22static inline struct counter_device *counter_from_dev(struct device *dev)23{24 return container_of(dev, struct counter_device, dev);25}26 27/**28 * struct counter_attribute - Counter sysfs attribute29 * @dev_attr: device attribute for sysfs30 * @l: node to add Counter attribute to attribute group list31 * @comp: Counter component callbacks and data32 * @scope: Counter scope of the attribute33 * @parent: pointer to the parent component34 */35struct counter_attribute {36 struct device_attribute dev_attr;37 struct list_head l;38 39 struct counter_comp comp;40 enum counter_scope scope;41 void *parent;42};43 44#define to_counter_attribute(_dev_attr) \45 container_of(_dev_attr, struct counter_attribute, dev_attr)46 47/**48 * struct counter_attribute_group - container for attribute group49 * @name: name of the attribute group50 * @attr_list: list to keep track of created attributes51 * @num_attr: number of attributes52 */53struct counter_attribute_group {54 const char *name;55 struct list_head attr_list;56 size_t num_attr;57};58 59static const char *const counter_function_str[] = {60 [COUNTER_FUNCTION_INCREASE] = "increase",61 [COUNTER_FUNCTION_DECREASE] = "decrease",62 [COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction",63 [COUNTER_FUNCTION_QUADRATURE_X1_A] = "quadrature x1 a",64 [COUNTER_FUNCTION_QUADRATURE_X1_B] = "quadrature x1 b",65 [COUNTER_FUNCTION_QUADRATURE_X2_A] = "quadrature x2 a",66 [COUNTER_FUNCTION_QUADRATURE_X2_B] = "quadrature x2 b",67 [COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4"68};69 70static const char *const counter_signal_value_str[] = {71 [COUNTER_SIGNAL_LEVEL_LOW] = "low",72 [COUNTER_SIGNAL_LEVEL_HIGH] = "high"73};74 75static const char *const counter_synapse_action_str[] = {76 [COUNTER_SYNAPSE_ACTION_NONE] = "none",77 [COUNTER_SYNAPSE_ACTION_RISING_EDGE] = "rising edge",78 [COUNTER_SYNAPSE_ACTION_FALLING_EDGE] = "falling edge",79 [COUNTER_SYNAPSE_ACTION_BOTH_EDGES] = "both edges"80};81 82static const char *const counter_count_direction_str[] = {83 [COUNTER_COUNT_DIRECTION_FORWARD] = "forward",84 [COUNTER_COUNT_DIRECTION_BACKWARD] = "backward"85};86 87static const char *const counter_count_mode_str[] = {88 [COUNTER_COUNT_MODE_NORMAL] = "normal",89 [COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit",90 [COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle",91 [COUNTER_COUNT_MODE_MODULO_N] = "modulo-n",92 [COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT] = "interrupt on terminal count",93 [COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT] = "hardware retriggerable one-shot",94 [COUNTER_COUNT_MODE_RATE_GENERATOR] = "rate generator",95 [COUNTER_COUNT_MODE_SQUARE_WAVE_MODE] = "square wave mode",96 [COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE] = "software triggered strobe",97 [COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE] = "hardware triggered strobe",98};99 100static const char *const counter_signal_polarity_str[] = {101 [COUNTER_SIGNAL_POLARITY_POSITIVE] = "positive",102 [COUNTER_SIGNAL_POLARITY_NEGATIVE] = "negative"103};104 105static ssize_t counter_comp_u8_show(struct device *dev,106 struct device_attribute *attr, char *buf)107{108 const struct counter_attribute *const a = to_counter_attribute(attr);109 struct counter_device *const counter = counter_from_dev(dev);110 int err;111 u8 data = 0;112 113 switch (a->scope) {114 case COUNTER_SCOPE_DEVICE:115 err = a->comp.device_u8_read(counter, &data);116 break;117 case COUNTER_SCOPE_SIGNAL:118 err = a->comp.signal_u8_read(counter, a->parent, &data);119 break;120 case COUNTER_SCOPE_COUNT:121 err = a->comp.count_u8_read(counter, a->parent, &data);122 break;123 default:124 return -EINVAL;125 }126 if (err < 0)127 return err;128 129 if (a->comp.type == COUNTER_COMP_BOOL)130 /* data should already be boolean but ensure just to be safe */131 data = !!data;132 133 return sysfs_emit(buf, "%u\n", (unsigned int)data);134}135 136static ssize_t counter_comp_u8_store(struct device *dev,137 struct device_attribute *attr,138 const char *buf, size_t len)139{140 const struct counter_attribute *const a = to_counter_attribute(attr);141 struct counter_device *const counter = counter_from_dev(dev);142 int err;143 bool bool_data = 0;144 u8 data = 0;145 146 if (a->comp.type == COUNTER_COMP_BOOL) {147 err = kstrtobool(buf, &bool_data);148 data = bool_data;149 } else150 err = kstrtou8(buf, 0, &data);151 if (err < 0)152 return err;153 154 switch (a->scope) {155 case COUNTER_SCOPE_DEVICE:156 err = a->comp.device_u8_write(counter, data);157 break;158 case COUNTER_SCOPE_SIGNAL:159 err = a->comp.signal_u8_write(counter, a->parent, data);160 break;161 case COUNTER_SCOPE_COUNT:162 err = a->comp.count_u8_write(counter, a->parent, data);163 break;164 default:165 return -EINVAL;166 }167 if (err < 0)168 return err;169 170 return len;171}172 173static ssize_t counter_comp_u32_show(struct device *dev,174 struct device_attribute *attr, char *buf)175{176 const struct counter_attribute *const a = to_counter_attribute(attr);177 struct counter_device *const counter = counter_from_dev(dev);178 const struct counter_available *const avail = a->comp.priv;179 int err;180 u32 data = 0;181 182 switch (a->scope) {183 case COUNTER_SCOPE_DEVICE:184 err = a->comp.device_u32_read(counter, &data);185 break;186 case COUNTER_SCOPE_SIGNAL:187 err = a->comp.signal_u32_read(counter, a->parent, &data);188 break;189 case COUNTER_SCOPE_COUNT:190 if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)191 err = a->comp.action_read(counter, a->parent,192 a->comp.priv, &data);193 else194 err = a->comp.count_u32_read(counter, a->parent, &data);195 break;196 default:197 return -EINVAL;198 }199 if (err < 0)200 return err;201 202 switch (a->comp.type) {203 case COUNTER_COMP_FUNCTION:204 return sysfs_emit(buf, "%s\n", counter_function_str[data]);205 case COUNTER_COMP_SIGNAL_LEVEL:206 return sysfs_emit(buf, "%s\n", counter_signal_value_str[data]);207 case COUNTER_COMP_SYNAPSE_ACTION:208 return sysfs_emit(buf, "%s\n", counter_synapse_action_str[data]);209 case COUNTER_COMP_ENUM:210 return sysfs_emit(buf, "%s\n", avail->strs[data]);211 case COUNTER_COMP_COUNT_DIRECTION:212 return sysfs_emit(buf, "%s\n", counter_count_direction_str[data]);213 case COUNTER_COMP_COUNT_MODE:214 return sysfs_emit(buf, "%s\n", counter_count_mode_str[data]);215 case COUNTER_COMP_SIGNAL_POLARITY:216 return sysfs_emit(buf, "%s\n", counter_signal_polarity_str[data]);217 default:218 return sysfs_emit(buf, "%u\n", (unsigned int)data);219 }220}221 222static int counter_find_enum(u32 *const enum_item, const u32 *const enums,223 const size_t num_enums, const char *const buf,224 const char *const string_array[])225{226 size_t index;227 228 for (index = 0; index < num_enums; index++) {229 *enum_item = enums[index];230 if (sysfs_streq(buf, string_array[*enum_item]))231 return 0;232 }233 234 return -EINVAL;235}236 237static ssize_t counter_comp_u32_store(struct device *dev,238 struct device_attribute *attr,239 const char *buf, size_t len)240{241 const struct counter_attribute *const a = to_counter_attribute(attr);242 struct counter_device *const counter = counter_from_dev(dev);243 struct counter_count *const count = a->parent;244 struct counter_synapse *const synapse = a->comp.priv;245 const struct counter_available *const avail = a->comp.priv;246 int err;247 u32 data = 0;248 249 switch (a->comp.type) {250 case COUNTER_COMP_FUNCTION:251 err = counter_find_enum(&data, count->functions_list,252 count->num_functions, buf,253 counter_function_str);254 break;255 case COUNTER_COMP_SYNAPSE_ACTION:256 err = counter_find_enum(&data, synapse->actions_list,257 synapse->num_actions, buf,258 counter_synapse_action_str);259 break;260 case COUNTER_COMP_ENUM:261 err = __sysfs_match_string(avail->strs, avail->num_items, buf);262 data = err;263 break;264 case COUNTER_COMP_COUNT_MODE:265 err = counter_find_enum(&data, avail->enums, avail->num_items,266 buf, counter_count_mode_str);267 break;268 case COUNTER_COMP_SIGNAL_POLARITY:269 err = counter_find_enum(&data, avail->enums, avail->num_items,270 buf, counter_signal_polarity_str);271 break;272 default:273 err = kstrtou32(buf, 0, &data);274 break;275 }276 if (err < 0)277 return err;278 279 switch (a->scope) {280 case COUNTER_SCOPE_DEVICE:281 err = a->comp.device_u32_write(counter, data);282 break;283 case COUNTER_SCOPE_SIGNAL:284 err = a->comp.signal_u32_write(counter, a->parent, data);285 break;286 case COUNTER_SCOPE_COUNT:287 if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)288 err = a->comp.action_write(counter, count, synapse,289 data);290 else291 err = a->comp.count_u32_write(counter, count, data);292 break;293 default:294 return -EINVAL;295 }296 if (err < 0)297 return err;298 299 return len;300}301 302static ssize_t counter_comp_u64_show(struct device *dev,303 struct device_attribute *attr, char *buf)304{305 const struct counter_attribute *const a = to_counter_attribute(attr);306 struct counter_device *const counter = counter_from_dev(dev);307 int err;308 u64 data = 0;309 310 switch (a->scope) {311 case COUNTER_SCOPE_DEVICE:312 err = a->comp.device_u64_read(counter, &data);313 break;314 case COUNTER_SCOPE_SIGNAL:315 err = a->comp.signal_u64_read(counter, a->parent, &data);316 break;317 case COUNTER_SCOPE_COUNT:318 err = a->comp.count_u64_read(counter, a->parent, &data);319 break;320 default:321 return -EINVAL;322 }323 if (err < 0)324 return err;325 326 return sysfs_emit(buf, "%llu\n", (unsigned long long)data);327}328 329static ssize_t counter_comp_u64_store(struct device *dev,330 struct device_attribute *attr,331 const char *buf, size_t len)332{333 const struct counter_attribute *const a = to_counter_attribute(attr);334 struct counter_device *const counter = counter_from_dev(dev);335 int err;336 u64 data = 0;337 338 err = kstrtou64(buf, 0, &data);339 if (err < 0)340 return err;341 342 switch (a->scope) {343 case COUNTER_SCOPE_DEVICE:344 err = a->comp.device_u64_write(counter, data);345 break;346 case COUNTER_SCOPE_SIGNAL:347 err = a->comp.signal_u64_write(counter, a->parent, data);348 break;349 case COUNTER_SCOPE_COUNT:350 err = a->comp.count_u64_write(counter, a->parent, data);351 break;352 default:353 return -EINVAL;354 }355 if (err < 0)356 return err;357 358 return len;359}360 361static ssize_t counter_comp_array_u32_show(struct device *dev,362 struct device_attribute *attr,363 char *buf)364{365 const struct counter_attribute *const a = to_counter_attribute(attr);366 struct counter_device *const counter = counter_from_dev(dev);367 const struct counter_array *const element = a->comp.priv;368 int err;369 u32 data = 0;370 371 if (a->scope != COUNTER_SCOPE_SIGNAL ||372 element->type != COUNTER_COMP_SIGNAL_POLARITY)373 return -EINVAL;374 375 err = a->comp.signal_array_u32_read(counter, a->parent, element->idx,376 &data);377 if (err < 0)378 return err;379 380 return sysfs_emit(buf, "%s\n", counter_signal_polarity_str[data]);381}382 383static ssize_t counter_comp_array_u32_store(struct device *dev,384 struct device_attribute *attr,385 const char *buf, size_t len)386{387 const struct counter_attribute *const a = to_counter_attribute(attr);388 struct counter_device *const counter = counter_from_dev(dev);389 const struct counter_array *const element = a->comp.priv;390 int err;391 u32 data = 0;392 393 if (element->type != COUNTER_COMP_SIGNAL_POLARITY ||394 a->scope != COUNTER_SCOPE_SIGNAL)395 return -EINVAL;396 397 err = counter_find_enum(&data, element->avail->enums,398 element->avail->num_items, buf,399 counter_signal_polarity_str);400 if (err < 0)401 return err;402 403 err = a->comp.signal_array_u32_write(counter, a->parent, element->idx,404 data);405 if (err < 0)406 return err;407 408 return len;409}410 411static ssize_t counter_comp_array_u64_show(struct device *dev,412 struct device_attribute *attr,413 char *buf)414{415 const struct counter_attribute *const a = to_counter_attribute(attr);416 struct counter_device *const counter = counter_from_dev(dev);417 const struct counter_array *const element = a->comp.priv;418 int err;419 u64 data = 0;420 421 switch (a->scope) {422 case COUNTER_SCOPE_DEVICE:423 err = a->comp.device_array_u64_read(counter, element->idx,424 &data);425 break;426 case COUNTER_SCOPE_SIGNAL:427 err = a->comp.signal_array_u64_read(counter, a->parent,428 element->idx, &data);429 break;430 case COUNTER_SCOPE_COUNT:431 err = a->comp.count_array_u64_read(counter, a->parent,432 element->idx, &data);433 break;434 default:435 return -EINVAL;436 }437 if (err < 0)438 return err;439 440 return sysfs_emit(buf, "%llu\n", (unsigned long long)data);441}442 443static ssize_t counter_comp_array_u64_store(struct device *dev,444 struct device_attribute *attr,445 const char *buf, size_t len)446{447 const struct counter_attribute *const a = to_counter_attribute(attr);448 struct counter_device *const counter = counter_from_dev(dev);449 const struct counter_array *const element = a->comp.priv;450 int err;451 u64 data = 0;452 453 err = kstrtou64(buf, 0, &data);454 if (err < 0)455 return err;456 457 switch (a->scope) {458 case COUNTER_SCOPE_DEVICE:459 err = a->comp.device_array_u64_write(counter, element->idx,460 data);461 break;462 case COUNTER_SCOPE_SIGNAL:463 err = a->comp.signal_array_u64_write(counter, a->parent,464 element->idx, data);465 break;466 case COUNTER_SCOPE_COUNT:467 err = a->comp.count_array_u64_write(counter, a->parent,468 element->idx, data);469 break;470 default:471 return -EINVAL;472 }473 if (err < 0)474 return err;475 476 return len;477}478 479static ssize_t enums_available_show(const u32 *const enums,480 const size_t num_enums,481 const char *const strs[], char *buf)482{483 size_t len = 0;484 size_t index;485 486 for (index = 0; index < num_enums; index++)487 len += sysfs_emit_at(buf, len, "%s\n", strs[enums[index]]);488 489 return len;490}491 492static ssize_t strs_available_show(const struct counter_available *const avail,493 char *buf)494{495 size_t len = 0;496 size_t index;497 498 for (index = 0; index < avail->num_items; index++)499 len += sysfs_emit_at(buf, len, "%s\n", avail->strs[index]);500 501 return len;502}503 504static ssize_t counter_comp_available_show(struct device *dev,505 struct device_attribute *attr,506 char *buf)507{508 const struct counter_attribute *const a = to_counter_attribute(attr);509 const struct counter_count *const count = a->parent;510 const struct counter_synapse *const synapse = a->comp.priv;511 const struct counter_available *const avail = a->comp.priv;512 513 switch (a->comp.type) {514 case COUNTER_COMP_FUNCTION:515 return enums_available_show(count->functions_list,516 count->num_functions,517 counter_function_str, buf);518 case COUNTER_COMP_SYNAPSE_ACTION:519 return enums_available_show(synapse->actions_list,520 synapse->num_actions,521 counter_synapse_action_str, buf);522 case COUNTER_COMP_ENUM:523 return strs_available_show(avail, buf);524 case COUNTER_COMP_COUNT_MODE:525 return enums_available_show(avail->enums, avail->num_items,526 counter_count_mode_str, buf);527 default:528 return -EINVAL;529 }530}531 532static int counter_avail_attr_create(struct device *const dev,533 struct counter_attribute_group *const group,534 const struct counter_comp *const comp, void *const parent)535{536 struct counter_attribute *counter_attr;537 struct device_attribute *dev_attr;538 539 counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);540 if (!counter_attr)541 return -ENOMEM;542 543 /* Configure Counter attribute */544 counter_attr->comp.type = comp->type;545 counter_attr->comp.priv = comp->priv;546 counter_attr->parent = parent;547 548 /* Initialize sysfs attribute */549 dev_attr = &counter_attr->dev_attr;550 sysfs_attr_init(&dev_attr->attr);551 552 /* Configure device attribute */553 dev_attr->attr.name = devm_kasprintf(dev, GFP_KERNEL, "%s_available",554 comp->name);555 if (!dev_attr->attr.name)556 return -ENOMEM;557 dev_attr->attr.mode = 0444;558 dev_attr->show = counter_comp_available_show;559 560 /* Store list node */561 list_add(&counter_attr->l, &group->attr_list);562 group->num_attr++;563 564 return 0;565}566 567static int counter_attr_create(struct device *const dev,568 struct counter_attribute_group *const group,569 const struct counter_comp *const comp,570 const enum counter_scope scope,571 void *const parent)572{573 const struct counter_array *const array = comp->priv;574 struct counter_attribute *counter_attr;575 struct device_attribute *dev_attr;576 577 counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);578 if (!counter_attr)579 return -ENOMEM;580 581 /* Configure Counter attribute */582 counter_attr->comp = *comp;583 counter_attr->scope = scope;584 counter_attr->parent = parent;585 586 /* Configure device attribute */587 dev_attr = &counter_attr->dev_attr;588 sysfs_attr_init(&dev_attr->attr);589 dev_attr->attr.name = comp->name;590 switch (comp->type) {591 case COUNTER_COMP_U8:592 case COUNTER_COMP_BOOL:593 if (comp->device_u8_read) {594 dev_attr->attr.mode |= 0444;595 dev_attr->show = counter_comp_u8_show;596 }597 if (comp->device_u8_write) {598 dev_attr->attr.mode |= 0200;599 dev_attr->store = counter_comp_u8_store;600 }601 break;602 case COUNTER_COMP_SIGNAL_LEVEL:603 case COUNTER_COMP_FUNCTION:604 case COUNTER_COMP_SYNAPSE_ACTION:605 case COUNTER_COMP_ENUM:606 case COUNTER_COMP_COUNT_DIRECTION:607 case COUNTER_COMP_COUNT_MODE:608 case COUNTER_COMP_SIGNAL_POLARITY:609 if (comp->device_u32_read) {610 dev_attr->attr.mode |= 0444;611 dev_attr->show = counter_comp_u32_show;612 }613 if (comp->device_u32_write) {614 dev_attr->attr.mode |= 0200;615 dev_attr->store = counter_comp_u32_store;616 }617 break;618 case COUNTER_COMP_U64:619 if (comp->device_u64_read) {620 dev_attr->attr.mode |= 0444;621 dev_attr->show = counter_comp_u64_show;622 }623 if (comp->device_u64_write) {624 dev_attr->attr.mode |= 0200;625 dev_attr->store = counter_comp_u64_store;626 }627 break;628 case COUNTER_COMP_ARRAY:629 switch (array->type) {630 case COUNTER_COMP_SIGNAL_POLARITY:631 if (comp->signal_array_u32_read) {632 dev_attr->attr.mode |= 0444;633 dev_attr->show = counter_comp_array_u32_show;634 }635 if (comp->signal_array_u32_write) {636 dev_attr->attr.mode |= 0200;637 dev_attr->store = counter_comp_array_u32_store;638 }639 break;640 case COUNTER_COMP_U64:641 if (comp->device_array_u64_read) {642 dev_attr->attr.mode |= 0444;643 dev_attr->show = counter_comp_array_u64_show;644 }645 if (comp->device_array_u64_write) {646 dev_attr->attr.mode |= 0200;647 dev_attr->store = counter_comp_array_u64_store;648 }649 break;650 default:651 return -EINVAL;652 }653 break;654 default:655 return -EINVAL;656 }657 658 /* Store list node */659 list_add(&counter_attr->l, &group->attr_list);660 group->num_attr++;661 662 /* Create "*_available" attribute if needed */663 switch (comp->type) {664 case COUNTER_COMP_FUNCTION:665 case COUNTER_COMP_SYNAPSE_ACTION:666 case COUNTER_COMP_ENUM:667 case COUNTER_COMP_COUNT_MODE:668 return counter_avail_attr_create(dev, group, comp, parent);669 default:670 return 0;671 }672}673 674static ssize_t counter_comp_name_show(struct device *dev,675 struct device_attribute *attr, char *buf)676{677 return sysfs_emit(buf, "%s\n", to_counter_attribute(attr)->comp.name);678}679 680static int counter_name_attr_create(struct device *const dev,681 struct counter_attribute_group *const group,682 const char *const name)683{684 struct counter_attribute *counter_attr;685 686 counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);687 if (!counter_attr)688 return -ENOMEM;689 690 /* Configure Counter attribute */691 counter_attr->comp.name = name;692 693 /* Configure device attribute */694 sysfs_attr_init(&counter_attr->dev_attr.attr);695 counter_attr->dev_attr.attr.name = "name";696 counter_attr->dev_attr.attr.mode = 0444;697 counter_attr->dev_attr.show = counter_comp_name_show;698 699 /* Store list node */700 list_add(&counter_attr->l, &group->attr_list);701 group->num_attr++;702 703 return 0;704}705 706static ssize_t counter_comp_id_show(struct device *dev,707 struct device_attribute *attr, char *buf)708{709 const size_t id = (size_t)to_counter_attribute(attr)->comp.priv;710 711 return sysfs_emit(buf, "%zu\n", id);712}713 714static int counter_comp_id_attr_create(struct device *const dev,715 struct counter_attribute_group *const group,716 const char *name, const size_t id)717{718 struct counter_attribute *counter_attr;719 720 /* Allocate Counter attribute */721 counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);722 if (!counter_attr)723 return -ENOMEM;724 725 /* Generate component ID name */726 name = devm_kasprintf(dev, GFP_KERNEL, "%s_component_id", name);727 if (!name)728 return -ENOMEM;729 730 /* Configure Counter attribute */731 counter_attr->comp.priv = (void *)id;732 733 /* Configure device attribute */734 sysfs_attr_init(&counter_attr->dev_attr.attr);735 counter_attr->dev_attr.attr.name = name;736 counter_attr->dev_attr.attr.mode = 0444;737 counter_attr->dev_attr.show = counter_comp_id_show;738 739 /* Store list node */740 list_add(&counter_attr->l, &group->attr_list);741 group->num_attr++;742 743 return 0;744}745 746static int counter_ext_attrs_create(struct device *const dev,747 struct counter_attribute_group *const group,748 const struct counter_comp *const ext,749 const enum counter_scope scope,750 void *const parent, const size_t id)751{752 int err;753 754 /* Create main extension attribute */755 err = counter_attr_create(dev, group, ext, scope, parent);756 if (err < 0)757 return err;758 759 /* Create extension id attribute */760 return counter_comp_id_attr_create(dev, group, ext->name, id);761}762 763static int counter_array_attrs_create(struct device *const dev,764 struct counter_attribute_group *const group,765 const struct counter_comp *const comp,766 const enum counter_scope scope,767 void *const parent, const size_t id)768{769 const struct counter_array *const array = comp->priv;770 struct counter_comp ext = *comp;771 struct counter_array *element;772 size_t idx;773 int err;774 775 /* Create an attribute for each array element */776 for (idx = 0; idx < array->length; idx++) {777 /* Generate array element attribute name */778 ext.name = devm_kasprintf(dev, GFP_KERNEL, "%s%zu", comp->name,779 idx);780 if (!ext.name)781 return -ENOMEM;782 783 /* Allocate and configure array element */784 element = devm_kzalloc(dev, sizeof(*element), GFP_KERNEL);785 if (!element)786 return -ENOMEM;787 element->type = array->type;788 element->avail = array->avail;789 element->idx = idx;790 ext.priv = element;791 792 /* Create all attributes associated with the array element */793 err = counter_ext_attrs_create(dev, group, &ext, scope, parent,794 id + idx);795 if (err < 0)796 return err;797 }798 799 return 0;800}801 802static int counter_sysfs_exts_add(struct device *const dev,803 struct counter_attribute_group *const group,804 const struct counter_comp *const exts,805 const size_t num_ext,806 const enum counter_scope scope,807 void *const parent)808{809 size_t i;810 const struct counter_comp *ext;811 int err;812 size_t id = 0;813 const struct counter_array *array;814 815 /* Create attributes for each extension */816 for (i = 0; i < num_ext; i++) {817 ext = &exts[i];818 if (ext->type == COUNTER_COMP_ARRAY) {819 err = counter_array_attrs_create(dev, group, ext, scope,820 parent, id);821 array = ext->priv;822 id += array->length;823 } else {824 err = counter_ext_attrs_create(dev, group, ext, scope,825 parent, id);826 id++;827 }828 if (err < 0)829 return err;830 }831 832 return 0;833}834 835static struct counter_comp counter_signal_comp = {836 .type = COUNTER_COMP_SIGNAL_LEVEL,837 .name = "signal",838};839 840static int counter_signal_attrs_create(struct counter_device *const counter,841 struct counter_attribute_group *const cattr_group,842 struct counter_signal *const signal)843{844 const enum counter_scope scope = COUNTER_SCOPE_SIGNAL;845 struct device *const dev = &counter->dev;846 int err;847 struct counter_comp comp;848 849 /* Create main Signal attribute */850 comp = counter_signal_comp;851 comp.signal_u32_read = counter->ops->signal_read;852 err = counter_attr_create(dev, cattr_group, &comp, scope, signal);853 if (err < 0)854 return err;855 856 /* Create Signal name attribute */857 err = counter_name_attr_create(dev, cattr_group, signal->name);858 if (err < 0)859 return err;860 861 /* Add Signal extensions */862 return counter_sysfs_exts_add(dev, cattr_group, signal->ext,863 signal->num_ext, scope, signal);864}865 866static int counter_sysfs_signals_add(struct counter_device *const counter,867 struct counter_attribute_group *const groups)868{869 size_t i;870 int err;871 872 /* Add each Signal */873 for (i = 0; i < counter->num_signals; i++) {874 /* Generate Signal attribute directory name */875 groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL,876 "signal%zu", i);877 if (!groups[i].name)878 return -ENOMEM;879 880 /* Create all attributes associated with Signal */881 err = counter_signal_attrs_create(counter, groups + i,882 counter->signals + i);883 if (err < 0)884 return err;885 }886 887 return 0;888}889 890static int counter_sysfs_synapses_add(struct counter_device *const counter,891 struct counter_attribute_group *const group,892 struct counter_count *const count)893{894 size_t i;895 896 /* Add each Synapse */897 for (i = 0; i < count->num_synapses; i++) {898 struct device *const dev = &counter->dev;899 struct counter_synapse *synapse;900 size_t id;901 struct counter_comp comp;902 int err;903 904 synapse = count->synapses + i;905 906 /* Generate Synapse action name */907 id = synapse->signal - counter->signals;908 comp.name = devm_kasprintf(dev, GFP_KERNEL, "signal%zu_action",909 id);910 if (!comp.name)911 return -ENOMEM;912 913 /* Create action attribute */914 comp.type = COUNTER_COMP_SYNAPSE_ACTION;915 comp.action_read = counter->ops->action_read;916 comp.action_write = counter->ops->action_write;917 comp.priv = synapse;918 err = counter_attr_create(dev, group, &comp,919 COUNTER_SCOPE_COUNT, count);920 if (err < 0)921 return err;922 923 /* Create Synapse component ID attribute */924 err = counter_comp_id_attr_create(dev, group, comp.name, i);925 if (err < 0)926 return err;927 }928 929 return 0;930}931 932static struct counter_comp counter_count_comp =933 COUNTER_COMP_COUNT_U64("count", NULL, NULL);934 935static struct counter_comp counter_function_comp = {936 .type = COUNTER_COMP_FUNCTION,937 .name = "function",938};939 940static int counter_count_attrs_create(struct counter_device *const counter,941 struct counter_attribute_group *const cattr_group,942 struct counter_count *const count)943{944 const enum counter_scope scope = COUNTER_SCOPE_COUNT;945 struct device *const dev = &counter->dev;946 int err;947 struct counter_comp comp;948 949 /* Create main Count attribute */950 comp = counter_count_comp;951 comp.count_u64_read = counter->ops->count_read;952 comp.count_u64_write = counter->ops->count_write;953 err = counter_attr_create(dev, cattr_group, &comp, scope, count);954 if (err < 0)955 return err;956 957 /* Create Count name attribute */958 err = counter_name_attr_create(dev, cattr_group, count->name);959 if (err < 0)960 return err;961 962 /* Create Count function attribute */963 comp = counter_function_comp;964 comp.count_u32_read = counter->ops->function_read;965 comp.count_u32_write = counter->ops->function_write;966 err = counter_attr_create(dev, cattr_group, &comp, scope, count);967 if (err < 0)968 return err;969 970 /* Add Count extensions */971 return counter_sysfs_exts_add(dev, cattr_group, count->ext,972 count->num_ext, scope, count);973}974 975static int counter_sysfs_counts_add(struct counter_device *const counter,976 struct counter_attribute_group *const groups)977{978 size_t i;979 struct counter_count *count;980 int err;981 982 /* Add each Count */983 for (i = 0; i < counter->num_counts; i++) {984 count = counter->counts + i;985 986 /* Generate Count attribute directory name */987 groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL,988 "count%zu", i);989 if (!groups[i].name)990 return -ENOMEM;991 992 /* Add sysfs attributes of the Synapses */993 err = counter_sysfs_synapses_add(counter, groups + i, count);994 if (err < 0)995 return err;996 997 /* Create all attributes associated with Count */998 err = counter_count_attrs_create(counter, groups + i, count);999 if (err < 0)1000 return err;1001 }1002 1003 return 0;1004}1005 1006static int counter_num_signals_read(struct counter_device *counter, u8 *val)1007{1008 *val = counter->num_signals;1009 return 0;1010}1011 1012static int counter_num_counts_read(struct counter_device *counter, u8 *val)1013{1014 *val = counter->num_counts;1015 return 0;1016}1017 1018static int counter_events_queue_size_read(struct counter_device *counter,1019 u64 *val)1020{1021 *val = kfifo_size(&counter->events);1022 return 0;1023}1024 1025static int counter_events_queue_size_write(struct counter_device *counter,1026 u64 val)1027{1028 DECLARE_KFIFO_PTR(events, struct counter_event);1029 int err;1030 unsigned long flags;1031 1032 /* Allocate new events queue */1033 err = kfifo_alloc(&events, val, GFP_KERNEL);1034 if (err)1035 return err;1036 1037 /* Swap in new events queue */1038 mutex_lock(&counter->events_out_lock);1039 spin_lock_irqsave(&counter->events_in_lock, flags);1040 kfifo_free(&counter->events);1041 counter->events.kfifo = events.kfifo;1042 spin_unlock_irqrestore(&counter->events_in_lock, flags);1043 mutex_unlock(&counter->events_out_lock);1044 1045 return 0;1046}1047 1048static struct counter_comp counter_num_signals_comp =1049 COUNTER_COMP_DEVICE_U8("num_signals", counter_num_signals_read, NULL);1050 1051static struct counter_comp counter_num_counts_comp =1052 COUNTER_COMP_DEVICE_U8("num_counts", counter_num_counts_read, NULL);1053 1054static struct counter_comp counter_events_queue_size_comp =1055 COUNTER_COMP_DEVICE_U64("events_queue_size",1056 counter_events_queue_size_read,1057 counter_events_queue_size_write);1058 1059static int counter_sysfs_attr_add(struct counter_device *const counter,1060 struct counter_attribute_group *cattr_group)1061{1062 const enum counter_scope scope = COUNTER_SCOPE_DEVICE;1063 struct device *const dev = &counter->dev;1064 int err;1065 1066 /* Add Signals sysfs attributes */1067 err = counter_sysfs_signals_add(counter, cattr_group);1068 if (err < 0)1069 return err;1070 cattr_group += counter->num_signals;1071 1072 /* Add Counts sysfs attributes */1073 err = counter_sysfs_counts_add(counter, cattr_group);1074 if (err < 0)1075 return err;1076 cattr_group += counter->num_counts;1077 1078 /* Create name attribute */1079 err = counter_name_attr_create(dev, cattr_group, counter->name);1080 if (err < 0)1081 return err;1082 1083 /* Create num_signals attribute */1084 err = counter_attr_create(dev, cattr_group, &counter_num_signals_comp,1085 scope, NULL);1086 if (err < 0)1087 return err;1088 1089 /* Create num_counts attribute */1090 err = counter_attr_create(dev, cattr_group, &counter_num_counts_comp,1091 scope, NULL);1092 if (err < 0)1093 return err;1094 1095 /* Create events_queue_size attribute */1096 err = counter_attr_create(dev, cattr_group,1097 &counter_events_queue_size_comp, scope, NULL);1098 if (err < 0)1099 return err;1100 1101 /* Add device extensions */1102 return counter_sysfs_exts_add(dev, cattr_group, counter->ext,1103 counter->num_ext, scope, NULL);1104 1105 return 0;1106}1107 1108/**1109 * counter_sysfs_add - Adds Counter sysfs attributes to the device structure1110 * @counter: Pointer to the Counter device structure1111 *1112 * Counter sysfs attributes are created and added to the respective device1113 * structure for later registration to the system. Resource-managed memory1114 * allocation is performed by this function, and this memory should be freed1115 * when no longer needed (automatically by a device_unregister call, or1116 * manually by a devres_release_all call).1117 */1118int counter_sysfs_add(struct counter_device *const counter)1119{1120 struct device *const dev = &counter->dev;1121 const size_t num_groups = counter->num_signals + counter->num_counts + 1;1122 struct counter_attribute_group *cattr_groups;1123 size_t i, j;1124 int err;1125 struct attribute_group *groups;1126 struct counter_attribute *p;1127 1128 /* Allocate space for attribute groups (signals, counts, and ext) */1129 cattr_groups = devm_kcalloc(dev, num_groups, sizeof(*cattr_groups),1130 GFP_KERNEL);1131 if (!cattr_groups)1132 return -ENOMEM;1133 1134 /* Initialize attribute lists */1135 for (i = 0; i < num_groups; i++)1136 INIT_LIST_HEAD(&cattr_groups[i].attr_list);1137 1138 /* Add Counter device sysfs attributes */1139 err = counter_sysfs_attr_add(counter, cattr_groups);1140 if (err < 0)1141 return err;1142 1143 /* Allocate attribute group pointers for association with device */1144 dev->groups = devm_kcalloc(dev, num_groups + 1, sizeof(*dev->groups),1145 GFP_KERNEL);1146 if (!dev->groups)1147 return -ENOMEM;1148 1149 /* Allocate space for attribute groups */1150 groups = devm_kcalloc(dev, num_groups, sizeof(*groups), GFP_KERNEL);1151 if (!groups)1152 return -ENOMEM;1153 1154 /* Prepare each group of attributes for association */1155 for (i = 0; i < num_groups; i++) {1156 groups[i].name = cattr_groups[i].name;1157 1158 /* Allocate space for attribute pointers */1159 groups[i].attrs = devm_kcalloc(dev,1160 cattr_groups[i].num_attr + 1,1161 sizeof(*groups[i].attrs),1162 GFP_KERNEL);1163 if (!groups[i].attrs)1164 return -ENOMEM;1165 1166 /* Add attribute pointers to attribute group */1167 j = 0;1168 list_for_each_entry(p, &cattr_groups[i].attr_list, l)1169 groups[i].attrs[j++] = &p->dev_attr.attr;1170 1171 /* Associate attribute group */1172 dev->groups[i] = &groups[i];1173 }1174 1175 return 0;1176}1177