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