brintos

brintos / linux-shallow public Read only

0
0
Text · 31.8 KiB · 42c5233 Raw
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