blob: 7cc4d1d523ea48697a9c0834d8a2221b98cd21f7 [file] [log] [blame]
William Breathitt Grayaaec1a02021-08-27 12:47:47 +09001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Generic Counter sysfs interface
4 * Copyright (C) 2020 William Breathitt Gray
5 */
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>
William Breathitt Grayfeff17a2021-09-29 12:16:04 +090011#include <linux/kfifo.h>
William Breathitt Grayaaec1a02021-08-27 12:47:47 +090012#include <linux/kstrtox.h>
13#include <linux/list.h>
William Breathitt Gray8ac33b82021-10-21 19:35:40 +090014#include <linux/mutex.h>
15#include <linux/spinlock.h>
William Breathitt Grayaaec1a02021-08-27 12:47:47 +090016#include <linux/string.h>
17#include <linux/sysfs.h>
18#include <linux/types.h>
19
20#include "counter-sysfs.h"
21
22/**
23 * struct counter_attribute - Counter sysfs attribute
24 * @dev_attr: device attribute for sysfs
25 * @l: node to add Counter attribute to attribute group list
26 * @comp: Counter component callbacks and data
27 * @scope: Counter scope of the attribute
28 * @parent: pointer to the parent component
29 */
30struct counter_attribute {
31 struct device_attribute dev_attr;
32 struct list_head l;
33
34 struct counter_comp comp;
35 enum counter_scope scope;
36 void *parent;
37};
38
39#define to_counter_attribute(_dev_attr) \
40 container_of(_dev_attr, struct counter_attribute, dev_attr)
41
42/**
43 * struct counter_attribute_group - container for attribute group
44 * @name: name of the attribute group
45 * @attr_list: list to keep track of created attributes
46 * @num_attr: number of attributes
47 */
48struct counter_attribute_group {
49 const char *name;
50 struct list_head attr_list;
51 size_t num_attr;
52};
53
54static const char *const counter_function_str[] = {
55 [COUNTER_FUNCTION_INCREASE] = "increase",
56 [COUNTER_FUNCTION_DECREASE] = "decrease",
57 [COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction",
58 [COUNTER_FUNCTION_QUADRATURE_X1_A] = "quadrature x1 a",
59 [COUNTER_FUNCTION_QUADRATURE_X1_B] = "quadrature x1 b",
60 [COUNTER_FUNCTION_QUADRATURE_X2_A] = "quadrature x2 a",
61 [COUNTER_FUNCTION_QUADRATURE_X2_B] = "quadrature x2 b",
62 [COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4"
63};
64
65static const char *const counter_signal_value_str[] = {
66 [COUNTER_SIGNAL_LEVEL_LOW] = "low",
67 [COUNTER_SIGNAL_LEVEL_HIGH] = "high"
68};
69
70static const char *const counter_synapse_action_str[] = {
71 [COUNTER_SYNAPSE_ACTION_NONE] = "none",
72 [COUNTER_SYNAPSE_ACTION_RISING_EDGE] = "rising edge",
73 [COUNTER_SYNAPSE_ACTION_FALLING_EDGE] = "falling edge",
74 [COUNTER_SYNAPSE_ACTION_BOTH_EDGES] = "both edges"
75};
76
77static const char *const counter_count_direction_str[] = {
78 [COUNTER_COUNT_DIRECTION_FORWARD] = "forward",
79 [COUNTER_COUNT_DIRECTION_BACKWARD] = "backward"
80};
81
82static const char *const counter_count_mode_str[] = {
83 [COUNTER_COUNT_MODE_NORMAL] = "normal",
84 [COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit",
85 [COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle",
86 [COUNTER_COUNT_MODE_MODULO_N] = "modulo-n"
87};
88
89static ssize_t counter_comp_u8_show(struct device *dev,
90 struct device_attribute *attr, char *buf)
91{
92 const struct counter_attribute *const a = to_counter_attribute(attr);
93 struct counter_device *const counter = dev_get_drvdata(dev);
94 int err;
95 u8 data = 0;
96
97 switch (a->scope) {
98 case COUNTER_SCOPE_DEVICE:
99 err = a->comp.device_u8_read(counter, &data);
100 break;
101 case COUNTER_SCOPE_SIGNAL:
102 err = a->comp.signal_u8_read(counter, a->parent, &data);
103 break;
104 case COUNTER_SCOPE_COUNT:
105 err = a->comp.count_u8_read(counter, a->parent, &data);
106 break;
107 default:
108 return -EINVAL;
109 }
110 if (err < 0)
111 return err;
112
113 if (a->comp.type == COUNTER_COMP_BOOL)
114 /* data should already be boolean but ensure just to be safe */
115 data = !!data;
116
David Lechnerc3ed7612021-10-17 14:01:06 -0500117 return sysfs_emit(buf, "%u\n", (unsigned int)data);
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900118}
119
120static ssize_t counter_comp_u8_store(struct device *dev,
121 struct device_attribute *attr,
122 const char *buf, size_t len)
123{
124 const struct counter_attribute *const a = to_counter_attribute(attr);
125 struct counter_device *const counter = dev_get_drvdata(dev);
126 int err;
127 bool bool_data = 0;
128 u8 data = 0;
129
130 if (a->comp.type == COUNTER_COMP_BOOL) {
131 err = kstrtobool(buf, &bool_data);
132 data = bool_data;
133 } else
134 err = kstrtou8(buf, 0, &data);
135 if (err < 0)
136 return err;
137
138 switch (a->scope) {
139 case COUNTER_SCOPE_DEVICE:
140 err = a->comp.device_u8_write(counter, data);
141 break;
142 case COUNTER_SCOPE_SIGNAL:
143 err = a->comp.signal_u8_write(counter, a->parent, data);
144 break;
145 case COUNTER_SCOPE_COUNT:
146 err = a->comp.count_u8_write(counter, a->parent, data);
147 break;
148 default:
149 return -EINVAL;
150 }
151 if (err < 0)
152 return err;
153
154 return len;
155}
156
157static ssize_t counter_comp_u32_show(struct device *dev,
158 struct device_attribute *attr, char *buf)
159{
160 const struct counter_attribute *const a = to_counter_attribute(attr);
161 struct counter_device *const counter = dev_get_drvdata(dev);
162 const struct counter_available *const avail = a->comp.priv;
163 int err;
164 u32 data = 0;
165
166 switch (a->scope) {
167 case COUNTER_SCOPE_DEVICE:
168 err = a->comp.device_u32_read(counter, &data);
169 break;
170 case COUNTER_SCOPE_SIGNAL:
171 err = a->comp.signal_u32_read(counter, a->parent, &data);
172 break;
173 case COUNTER_SCOPE_COUNT:
174 if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
175 err = a->comp.action_read(counter, a->parent,
176 a->comp.priv, &data);
177 else
178 err = a->comp.count_u32_read(counter, a->parent, &data);
179 break;
180 default:
181 return -EINVAL;
182 }
183 if (err < 0)
184 return err;
185
186 switch (a->comp.type) {
187 case COUNTER_COMP_FUNCTION:
188 return sysfs_emit(buf, "%s\n", counter_function_str[data]);
189 case COUNTER_COMP_SIGNAL_LEVEL:
190 return sysfs_emit(buf, "%s\n", counter_signal_value_str[data]);
191 case COUNTER_COMP_SYNAPSE_ACTION:
192 return sysfs_emit(buf, "%s\n", counter_synapse_action_str[data]);
193 case COUNTER_COMP_ENUM:
194 return sysfs_emit(buf, "%s\n", avail->strs[data]);
195 case COUNTER_COMP_COUNT_DIRECTION:
196 return sysfs_emit(buf, "%s\n", counter_count_direction_str[data]);
197 case COUNTER_COMP_COUNT_MODE:
198 return sysfs_emit(buf, "%s\n", counter_count_mode_str[data]);
199 default:
David Lechnerc3ed7612021-10-17 14:01:06 -0500200 return sysfs_emit(buf, "%u\n", (unsigned int)data);
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900201 }
202}
203
204static int counter_find_enum(u32 *const enum_item, const u32 *const enums,
205 const size_t num_enums, const char *const buf,
206 const char *const string_array[])
207{
208 size_t index;
209
210 for (index = 0; index < num_enums; index++) {
211 *enum_item = enums[index];
212 if (sysfs_streq(buf, string_array[*enum_item]))
213 return 0;
214 }
215
216 return -EINVAL;
217}
218
219static ssize_t counter_comp_u32_store(struct device *dev,
220 struct device_attribute *attr,
221 const char *buf, size_t len)
222{
223 const struct counter_attribute *const a = to_counter_attribute(attr);
224 struct counter_device *const counter = dev_get_drvdata(dev);
225 struct counter_count *const count = a->parent;
226 struct counter_synapse *const synapse = a->comp.priv;
227 const struct counter_available *const avail = a->comp.priv;
228 int err;
229 u32 data = 0;
230
231 switch (a->comp.type) {
232 case COUNTER_COMP_FUNCTION:
233 err = counter_find_enum(&data, count->functions_list,
234 count->num_functions, buf,
235 counter_function_str);
236 break;
237 case COUNTER_COMP_SYNAPSE_ACTION:
238 err = counter_find_enum(&data, synapse->actions_list,
239 synapse->num_actions, buf,
240 counter_synapse_action_str);
241 break;
242 case COUNTER_COMP_ENUM:
243 err = __sysfs_match_string(avail->strs, avail->num_items, buf);
244 data = err;
245 break;
246 case COUNTER_COMP_COUNT_MODE:
247 err = counter_find_enum(&data, avail->enums, avail->num_items,
248 buf, counter_count_mode_str);
249 break;
250 default:
251 err = kstrtou32(buf, 0, &data);
252 break;
253 }
254 if (err < 0)
255 return err;
256
257 switch (a->scope) {
258 case COUNTER_SCOPE_DEVICE:
259 err = a->comp.device_u32_write(counter, data);
260 break;
261 case COUNTER_SCOPE_SIGNAL:
262 err = a->comp.signal_u32_write(counter, a->parent, data);
263 break;
264 case COUNTER_SCOPE_COUNT:
265 if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
266 err = a->comp.action_write(counter, count, synapse,
267 data);
268 else
269 err = a->comp.count_u32_write(counter, count, data);
270 break;
271 default:
272 return -EINVAL;
273 }
274 if (err < 0)
275 return err;
276
277 return len;
278}
279
280static ssize_t counter_comp_u64_show(struct device *dev,
281 struct device_attribute *attr, char *buf)
282{
283 const struct counter_attribute *const a = to_counter_attribute(attr);
284 struct counter_device *const counter = dev_get_drvdata(dev);
285 int err;
286 u64 data = 0;
287
288 switch (a->scope) {
289 case COUNTER_SCOPE_DEVICE:
290 err = a->comp.device_u64_read(counter, &data);
291 break;
292 case COUNTER_SCOPE_SIGNAL:
293 err = a->comp.signal_u64_read(counter, a->parent, &data);
294 break;
295 case COUNTER_SCOPE_COUNT:
296 err = a->comp.count_u64_read(counter, a->parent, &data);
297 break;
298 default:
299 return -EINVAL;
300 }
301 if (err < 0)
302 return err;
303
David Lechnerc3ed7612021-10-17 14:01:06 -0500304 return sysfs_emit(buf, "%llu\n", (unsigned long long)data);
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900305}
306
307static ssize_t counter_comp_u64_store(struct device *dev,
308 struct device_attribute *attr,
309 const char *buf, size_t len)
310{
311 const struct counter_attribute *const a = to_counter_attribute(attr);
312 struct counter_device *const counter = dev_get_drvdata(dev);
313 int err;
314 u64 data = 0;
315
316 err = kstrtou64(buf, 0, &data);
317 if (err < 0)
318 return err;
319
320 switch (a->scope) {
321 case COUNTER_SCOPE_DEVICE:
322 err = a->comp.device_u64_write(counter, data);
323 break;
324 case COUNTER_SCOPE_SIGNAL:
325 err = a->comp.signal_u64_write(counter, a->parent, data);
326 break;
327 case COUNTER_SCOPE_COUNT:
328 err = a->comp.count_u64_write(counter, a->parent, data);
329 break;
330 default:
331 return -EINVAL;
332 }
333 if (err < 0)
334 return err;
335
336 return len;
337}
338
339static ssize_t enums_available_show(const u32 *const enums,
340 const size_t num_enums,
341 const char *const strs[], char *buf)
342{
343 size_t len = 0;
344 size_t index;
345
346 for (index = 0; index < num_enums; index++)
347 len += sysfs_emit_at(buf, len, "%s\n", strs[enums[index]]);
348
349 return len;
350}
351
352static ssize_t strs_available_show(const struct counter_available *const avail,
353 char *buf)
354{
355 size_t len = 0;
356 size_t index;
357
358 for (index = 0; index < avail->num_items; index++)
359 len += sysfs_emit_at(buf, len, "%s\n", avail->strs[index]);
360
361 return len;
362}
363
364static ssize_t counter_comp_available_show(struct device *dev,
365 struct device_attribute *attr,
366 char *buf)
367{
368 const struct counter_attribute *const a = to_counter_attribute(attr);
369 const struct counter_count *const count = a->parent;
370 const struct counter_synapse *const synapse = a->comp.priv;
371 const struct counter_available *const avail = a->comp.priv;
372
373 switch (a->comp.type) {
374 case COUNTER_COMP_FUNCTION:
375 return enums_available_show(count->functions_list,
376 count->num_functions,
377 counter_function_str, buf);
378 case COUNTER_COMP_SYNAPSE_ACTION:
379 return enums_available_show(synapse->actions_list,
380 synapse->num_actions,
381 counter_synapse_action_str, buf);
382 case COUNTER_COMP_ENUM:
383 return strs_available_show(avail, buf);
384 case COUNTER_COMP_COUNT_MODE:
385 return enums_available_show(avail->enums, avail->num_items,
386 counter_count_mode_str, buf);
387 default:
388 return -EINVAL;
389 }
390}
391
392static int counter_avail_attr_create(struct device *const dev,
393 struct counter_attribute_group *const group,
394 const struct counter_comp *const comp, void *const parent)
395{
396 struct counter_attribute *counter_attr;
397 struct device_attribute *dev_attr;
398
399 counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
400 if (!counter_attr)
401 return -ENOMEM;
402
403 /* Configure Counter attribute */
404 counter_attr->comp.type = comp->type;
405 counter_attr->comp.priv = comp->priv;
406 counter_attr->parent = parent;
407
408 /* Initialize sysfs attribute */
409 dev_attr = &counter_attr->dev_attr;
410 sysfs_attr_init(&dev_attr->attr);
411
412 /* Configure device attribute */
413 dev_attr->attr.name = devm_kasprintf(dev, GFP_KERNEL, "%s_available",
414 comp->name);
415 if (!dev_attr->attr.name)
416 return -ENOMEM;
417 dev_attr->attr.mode = 0444;
418 dev_attr->show = counter_comp_available_show;
419
420 /* Store list node */
421 list_add(&counter_attr->l, &group->attr_list);
422 group->num_attr++;
423
424 return 0;
425}
426
427static int counter_attr_create(struct device *const dev,
428 struct counter_attribute_group *const group,
429 const struct counter_comp *const comp,
430 const enum counter_scope scope,
431 void *const parent)
432{
433 struct counter_attribute *counter_attr;
434 struct device_attribute *dev_attr;
435
436 counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
437 if (!counter_attr)
438 return -ENOMEM;
439
440 /* Configure Counter attribute */
441 counter_attr->comp = *comp;
442 counter_attr->scope = scope;
443 counter_attr->parent = parent;
444
445 /* Configure device attribute */
446 dev_attr = &counter_attr->dev_attr;
447 sysfs_attr_init(&dev_attr->attr);
448 dev_attr->attr.name = comp->name;
449 switch (comp->type) {
450 case COUNTER_COMP_U8:
451 case COUNTER_COMP_BOOL:
452 if (comp->device_u8_read) {
453 dev_attr->attr.mode |= 0444;
454 dev_attr->show = counter_comp_u8_show;
455 }
456 if (comp->device_u8_write) {
457 dev_attr->attr.mode |= 0200;
458 dev_attr->store = counter_comp_u8_store;
459 }
460 break;
461 case COUNTER_COMP_SIGNAL_LEVEL:
462 case COUNTER_COMP_FUNCTION:
463 case COUNTER_COMP_SYNAPSE_ACTION:
464 case COUNTER_COMP_ENUM:
465 case COUNTER_COMP_COUNT_DIRECTION:
466 case COUNTER_COMP_COUNT_MODE:
467 if (comp->device_u32_read) {
468 dev_attr->attr.mode |= 0444;
469 dev_attr->show = counter_comp_u32_show;
470 }
471 if (comp->device_u32_write) {
472 dev_attr->attr.mode |= 0200;
473 dev_attr->store = counter_comp_u32_store;
474 }
475 break;
476 case COUNTER_COMP_U64:
477 if (comp->device_u64_read) {
478 dev_attr->attr.mode |= 0444;
479 dev_attr->show = counter_comp_u64_show;
480 }
481 if (comp->device_u64_write) {
482 dev_attr->attr.mode |= 0200;
483 dev_attr->store = counter_comp_u64_store;
484 }
485 break;
486 default:
487 return -EINVAL;
488 }
489
490 /* Store list node */
491 list_add(&counter_attr->l, &group->attr_list);
492 group->num_attr++;
493
494 /* Create "*_available" attribute if needed */
495 switch (comp->type) {
496 case COUNTER_COMP_FUNCTION:
497 case COUNTER_COMP_SYNAPSE_ACTION:
498 case COUNTER_COMP_ENUM:
499 case COUNTER_COMP_COUNT_MODE:
500 return counter_avail_attr_create(dev, group, comp, parent);
501 default:
502 return 0;
503 }
504}
505
506static ssize_t counter_comp_name_show(struct device *dev,
507 struct device_attribute *attr, char *buf)
508{
509 return sysfs_emit(buf, "%s\n", to_counter_attribute(attr)->comp.name);
510}
511
512static int counter_name_attr_create(struct device *const dev,
513 struct counter_attribute_group *const group,
514 const char *const name)
515{
516 struct counter_attribute *counter_attr;
517
518 counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
519 if (!counter_attr)
520 return -ENOMEM;
521
522 /* Configure Counter attribute */
523 counter_attr->comp.name = name;
524
525 /* Configure device attribute */
526 sysfs_attr_init(&counter_attr->dev_attr.attr);
527 counter_attr->dev_attr.attr.name = "name";
528 counter_attr->dev_attr.attr.mode = 0444;
529 counter_attr->dev_attr.show = counter_comp_name_show;
530
531 /* Store list node */
532 list_add(&counter_attr->l, &group->attr_list);
533 group->num_attr++;
534
535 return 0;
536}
537
William Breathitt Graybb6264a2021-09-29 12:16:02 +0900538static ssize_t counter_comp_id_show(struct device *dev,
539 struct device_attribute *attr, char *buf)
540{
541 const size_t id = (size_t)to_counter_attribute(attr)->comp.priv;
542
David Lechnerc3ed7612021-10-17 14:01:06 -0500543 return sysfs_emit(buf, "%zu\n", id);
William Breathitt Graybb6264a2021-09-29 12:16:02 +0900544}
545
546static int counter_comp_id_attr_create(struct device *const dev,
547 struct counter_attribute_group *const group,
548 const char *name, const size_t id)
549{
550 struct counter_attribute *counter_attr;
551
552 /* Allocate Counter attribute */
553 counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
554 if (!counter_attr)
555 return -ENOMEM;
556
557 /* Generate component ID name */
558 name = devm_kasprintf(dev, GFP_KERNEL, "%s_component_id", name);
559 if (!name)
560 return -ENOMEM;
561
562 /* Configure Counter attribute */
563 counter_attr->comp.priv = (void *)id;
564
565 /* Configure device attribute */
566 sysfs_attr_init(&counter_attr->dev_attr.attr);
567 counter_attr->dev_attr.attr.name = name;
568 counter_attr->dev_attr.attr.mode = 0444;
569 counter_attr->dev_attr.show = counter_comp_id_show;
570
571 /* Store list node */
572 list_add(&counter_attr->l, &group->attr_list);
573 group->num_attr++;
574
575 return 0;
576}
577
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900578static struct counter_comp counter_signal_comp = {
579 .type = COUNTER_COMP_SIGNAL_LEVEL,
580 .name = "signal",
581};
582
583static int counter_signal_attrs_create(struct counter_device *const counter,
584 struct counter_attribute_group *const cattr_group,
585 struct counter_signal *const signal)
586{
587 const enum counter_scope scope = COUNTER_SCOPE_SIGNAL;
588 struct device *const dev = &counter->dev;
589 int err;
590 struct counter_comp comp;
591 size_t i;
William Breathitt Gray4bdec612021-09-29 12:16:03 +0900592 struct counter_comp *ext;
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900593
594 /* Create main Signal attribute */
595 comp = counter_signal_comp;
596 comp.signal_u32_read = counter->ops->signal_read;
597 err = counter_attr_create(dev, cattr_group, &comp, scope, signal);
598 if (err < 0)
599 return err;
600
601 /* Create Signal name attribute */
602 err = counter_name_attr_create(dev, cattr_group, signal->name);
603 if (err < 0)
604 return err;
605
606 /* Create an attribute for each extension */
607 for (i = 0; i < signal->num_ext; i++) {
William Breathitt Gray4bdec612021-09-29 12:16:03 +0900608 ext = &signal->ext[i];
609
610 err = counter_attr_create(dev, cattr_group, ext, scope, signal);
611 if (err < 0)
612 return err;
613
614 err = counter_comp_id_attr_create(dev, cattr_group, ext->name,
615 i);
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900616 if (err < 0)
617 return err;
618 }
619
620 return 0;
621}
622
623static int counter_sysfs_signals_add(struct counter_device *const counter,
624 struct counter_attribute_group *const groups)
625{
626 size_t i;
627 int err;
628
629 /* Add each Signal */
630 for (i = 0; i < counter->num_signals; i++) {
631 /* Generate Signal attribute directory name */
632 groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL,
633 "signal%zu", i);
634 if (!groups[i].name)
635 return -ENOMEM;
636
637 /* Create all attributes associated with Signal */
638 err = counter_signal_attrs_create(counter, groups + i,
639 counter->signals + i);
640 if (err < 0)
641 return err;
642 }
643
644 return 0;
645}
646
647static int counter_sysfs_synapses_add(struct counter_device *const counter,
648 struct counter_attribute_group *const group,
649 struct counter_count *const count)
650{
651 size_t i;
652
653 /* Add each Synapse */
654 for (i = 0; i < count->num_synapses; i++) {
655 struct device *const dev = &counter->dev;
656 struct counter_synapse *synapse;
657 size_t id;
658 struct counter_comp comp;
659 int err;
660
661 synapse = count->synapses + i;
662
663 /* Generate Synapse action name */
664 id = synapse->signal - counter->signals;
665 comp.name = devm_kasprintf(dev, GFP_KERNEL, "signal%zu_action",
666 id);
667 if (!comp.name)
668 return -ENOMEM;
669
670 /* Create action attribute */
671 comp.type = COUNTER_COMP_SYNAPSE_ACTION;
672 comp.action_read = counter->ops->action_read;
673 comp.action_write = counter->ops->action_write;
674 comp.priv = synapse;
675 err = counter_attr_create(dev, group, &comp,
676 COUNTER_SCOPE_COUNT, count);
677 if (err < 0)
678 return err;
William Breathitt Graybb6264a2021-09-29 12:16:02 +0900679
680 /* Create Synapse component ID attribute */
681 err = counter_comp_id_attr_create(dev, group, comp.name, i);
682 if (err < 0)
683 return err;
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900684 }
685
686 return 0;
687}
688
689static struct counter_comp counter_count_comp =
690 COUNTER_COMP_COUNT_U64("count", NULL, NULL);
691
692static struct counter_comp counter_function_comp = {
693 .type = COUNTER_COMP_FUNCTION,
694 .name = "function",
695};
696
697static int counter_count_attrs_create(struct counter_device *const counter,
698 struct counter_attribute_group *const cattr_group,
699 struct counter_count *const count)
700{
701 const enum counter_scope scope = COUNTER_SCOPE_COUNT;
702 struct device *const dev = &counter->dev;
703 int err;
704 struct counter_comp comp;
705 size_t i;
William Breathitt Gray4bdec612021-09-29 12:16:03 +0900706 struct counter_comp *ext;
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900707
708 /* Create main Count attribute */
709 comp = counter_count_comp;
710 comp.count_u64_read = counter->ops->count_read;
711 comp.count_u64_write = counter->ops->count_write;
712 err = counter_attr_create(dev, cattr_group, &comp, scope, count);
713 if (err < 0)
714 return err;
715
716 /* Create Count name attribute */
717 err = counter_name_attr_create(dev, cattr_group, count->name);
718 if (err < 0)
719 return err;
720
721 /* Create Count function attribute */
722 comp = counter_function_comp;
723 comp.count_u32_read = counter->ops->function_read;
724 comp.count_u32_write = counter->ops->function_write;
725 err = counter_attr_create(dev, cattr_group, &comp, scope, count);
726 if (err < 0)
727 return err;
728
729 /* Create an attribute for each extension */
730 for (i = 0; i < count->num_ext; i++) {
William Breathitt Gray4bdec612021-09-29 12:16:03 +0900731 ext = &count->ext[i];
732
733 err = counter_attr_create(dev, cattr_group, ext, scope, count);
734 if (err < 0)
735 return err;
736
737 err = counter_comp_id_attr_create(dev, cattr_group, ext->name,
738 i);
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900739 if (err < 0)
740 return err;
741 }
742
743 return 0;
744}
745
746static int counter_sysfs_counts_add(struct counter_device *const counter,
747 struct counter_attribute_group *const groups)
748{
749 size_t i;
750 struct counter_count *count;
751 int err;
752
753 /* Add each Count */
754 for (i = 0; i < counter->num_counts; i++) {
755 count = counter->counts + i;
756
757 /* Generate Count attribute directory name */
758 groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL,
759 "count%zu", i);
760 if (!groups[i].name)
761 return -ENOMEM;
762
763 /* Add sysfs attributes of the Synapses */
764 err = counter_sysfs_synapses_add(counter, groups + i, count);
765 if (err < 0)
766 return err;
767
768 /* Create all attributes associated with Count */
769 err = counter_count_attrs_create(counter, groups + i, count);
770 if (err < 0)
771 return err;
772 }
773
774 return 0;
775}
776
777static int counter_num_signals_read(struct counter_device *counter, u8 *val)
778{
779 *val = counter->num_signals;
780 return 0;
781}
782
783static int counter_num_counts_read(struct counter_device *counter, u8 *val)
784{
785 *val = counter->num_counts;
786 return 0;
787}
788
William Breathitt Grayfeff17a2021-09-29 12:16:04 +0900789static int counter_events_queue_size_read(struct counter_device *counter,
790 u64 *val)
791{
792 *val = kfifo_size(&counter->events);
793 return 0;
794}
795
796static int counter_events_queue_size_write(struct counter_device *counter,
797 u64 val)
798{
799 DECLARE_KFIFO_PTR(events, struct counter_event);
David Lechnerf5245a52021-10-17 13:55:21 -0500800 int err;
William Breathitt Gray8ac33b82021-10-21 19:35:40 +0900801 unsigned long flags;
William Breathitt Grayfeff17a2021-09-29 12:16:04 +0900802
803 /* Allocate new events queue */
804 err = kfifo_alloc(&events, val, GFP_KERNEL);
805 if (err)
David Lechnerf5245a52021-10-17 13:55:21 -0500806 return err;
William Breathitt Grayfeff17a2021-09-29 12:16:04 +0900807
808 /* Swap in new events queue */
William Breathitt Gray8ac33b82021-10-21 19:35:40 +0900809 mutex_lock(&counter->events_out_lock);
810 spin_lock_irqsave(&counter->events_in_lock, flags);
William Breathitt Grayfeff17a2021-09-29 12:16:04 +0900811 kfifo_free(&counter->events);
812 counter->events.kfifo = events.kfifo;
William Breathitt Gray8ac33b82021-10-21 19:35:40 +0900813 spin_unlock_irqrestore(&counter->events_in_lock, flags);
814 mutex_unlock(&counter->events_out_lock);
William Breathitt Grayfeff17a2021-09-29 12:16:04 +0900815
David Lechnerf5245a52021-10-17 13:55:21 -0500816 return 0;
William Breathitt Grayfeff17a2021-09-29 12:16:04 +0900817}
818
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900819static struct counter_comp counter_num_signals_comp =
820 COUNTER_COMP_DEVICE_U8("num_signals", counter_num_signals_read, NULL);
821
822static struct counter_comp counter_num_counts_comp =
823 COUNTER_COMP_DEVICE_U8("num_counts", counter_num_counts_read, NULL);
824
William Breathitt Grayfeff17a2021-09-29 12:16:04 +0900825static struct counter_comp counter_events_queue_size_comp =
826 COUNTER_COMP_DEVICE_U64("events_queue_size",
827 counter_events_queue_size_read,
828 counter_events_queue_size_write);
829
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900830static int counter_sysfs_attr_add(struct counter_device *const counter,
831 struct counter_attribute_group *cattr_group)
832{
833 const enum counter_scope scope = COUNTER_SCOPE_DEVICE;
834 struct device *const dev = &counter->dev;
835 int err;
836 size_t i;
William Breathitt Gray4bdec612021-09-29 12:16:03 +0900837 struct counter_comp *ext;
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900838
839 /* Add Signals sysfs attributes */
840 err = counter_sysfs_signals_add(counter, cattr_group);
841 if (err < 0)
842 return err;
843 cattr_group += counter->num_signals;
844
845 /* Add Counts sysfs attributes */
846 err = counter_sysfs_counts_add(counter, cattr_group);
847 if (err < 0)
848 return err;
849 cattr_group += counter->num_counts;
850
851 /* Create name attribute */
852 err = counter_name_attr_create(dev, cattr_group, counter->name);
853 if (err < 0)
854 return err;
855
856 /* Create num_signals attribute */
857 err = counter_attr_create(dev, cattr_group, &counter_num_signals_comp,
858 scope, NULL);
859 if (err < 0)
860 return err;
861
862 /* Create num_counts attribute */
863 err = counter_attr_create(dev, cattr_group, &counter_num_counts_comp,
864 scope, NULL);
865 if (err < 0)
866 return err;
867
William Breathitt Grayfeff17a2021-09-29 12:16:04 +0900868 /* Create events_queue_size attribute */
869 err = counter_attr_create(dev, cattr_group,
870 &counter_events_queue_size_comp, scope, NULL);
871 if (err < 0)
872 return err;
873
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900874 /* Create an attribute for each extension */
875 for (i = 0; i < counter->num_ext; i++) {
William Breathitt Gray4bdec612021-09-29 12:16:03 +0900876 ext = &counter->ext[i];
877
878 err = counter_attr_create(dev, cattr_group, ext, scope, NULL);
879 if (err < 0)
880 return err;
881
882 err = counter_comp_id_attr_create(dev, cattr_group, ext->name,
883 i);
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900884 if (err < 0)
885 return err;
886 }
887
888 return 0;
889}
890
891/**
892 * counter_sysfs_add - Adds Counter sysfs attributes to the device structure
893 * @counter: Pointer to the Counter device structure
894 *
895 * Counter sysfs attributes are created and added to the respective device
896 * structure for later registration to the system. Resource-managed memory
897 * allocation is performed by this function, and this memory should be freed
898 * when no longer needed (automatically by a device_unregister call, or
899 * manually by a devres_release_all call).
900 */
901int counter_sysfs_add(struct counter_device *const counter)
902{
903 struct device *const dev = &counter->dev;
904 const size_t num_groups = counter->num_signals + counter->num_counts + 1;
905 struct counter_attribute_group *cattr_groups;
906 size_t i, j;
907 int err;
908 struct attribute_group *groups;
909 struct counter_attribute *p;
910
911 /* Allocate space for attribute groups (signals, counts, and ext) */
912 cattr_groups = devm_kcalloc(dev, num_groups, sizeof(*cattr_groups),
913 GFP_KERNEL);
914 if (!cattr_groups)
915 return -ENOMEM;
916
917 /* Initialize attribute lists */
918 for (i = 0; i < num_groups; i++)
919 INIT_LIST_HEAD(&cattr_groups[i].attr_list);
920
921 /* Add Counter device sysfs attributes */
922 err = counter_sysfs_attr_add(counter, cattr_groups);
923 if (err < 0)
924 return err;
925
926 /* Allocate attribute group pointers for association with device */
927 dev->groups = devm_kcalloc(dev, num_groups + 1, sizeof(*dev->groups),
928 GFP_KERNEL);
929 if (!dev->groups)
930 return -ENOMEM;
931
932 /* Allocate space for attribute groups */
933 groups = devm_kcalloc(dev, num_groups, sizeof(*groups), GFP_KERNEL);
934 if (!groups)
935 return -ENOMEM;
936
937 /* Prepare each group of attributes for association */
938 for (i = 0; i < num_groups; i++) {
939 groups[i].name = cattr_groups[i].name;
940
941 /* Allocate space for attribute pointers */
942 groups[i].attrs = devm_kcalloc(dev,
943 cattr_groups[i].num_attr + 1,
944 sizeof(*groups[i].attrs),
945 GFP_KERNEL);
946 if (!groups[i].attrs)
947 return -ENOMEM;
948
949 /* Add attribute pointers to attribute group */
950 j = 0;
951 list_for_each_entry(p, &cattr_groups[i].attr_list, l)
952 groups[i].attrs[j++] = &p->dev_attr.attr;
953
954 /* Associate attribute group */
955 dev->groups[i] = &groups[i];
956 }
957
958 return 0;
959}