162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Generic Counter sysfs interface 462306a36Sopenharmony_ci * Copyright (C) 2020 William Breathitt Gray 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/counter.h> 762306a36Sopenharmony_ci#include <linux/device.h> 862306a36Sopenharmony_ci#include <linux/err.h> 962306a36Sopenharmony_ci#include <linux/gfp.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/kfifo.h> 1262306a36Sopenharmony_ci#include <linux/kstrtox.h> 1362306a36Sopenharmony_ci#include <linux/list.h> 1462306a36Sopenharmony_ci#include <linux/mutex.h> 1562306a36Sopenharmony_ci#include <linux/spinlock.h> 1662306a36Sopenharmony_ci#include <linux/string.h> 1762306a36Sopenharmony_ci#include <linux/sysfs.h> 1862306a36Sopenharmony_ci#include <linux/types.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "counter-sysfs.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic inline struct counter_device *counter_from_dev(struct device *dev) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci return container_of(dev, struct counter_device, dev); 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/** 2862306a36Sopenharmony_ci * struct counter_attribute - Counter sysfs attribute 2962306a36Sopenharmony_ci * @dev_attr: device attribute for sysfs 3062306a36Sopenharmony_ci * @l: node to add Counter attribute to attribute group list 3162306a36Sopenharmony_ci * @comp: Counter component callbacks and data 3262306a36Sopenharmony_ci * @scope: Counter scope of the attribute 3362306a36Sopenharmony_ci * @parent: pointer to the parent component 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_cistruct counter_attribute { 3662306a36Sopenharmony_ci struct device_attribute dev_attr; 3762306a36Sopenharmony_ci struct list_head l; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci struct counter_comp comp; 4062306a36Sopenharmony_ci enum counter_scope scope; 4162306a36Sopenharmony_ci void *parent; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define to_counter_attribute(_dev_attr) \ 4562306a36Sopenharmony_ci container_of(_dev_attr, struct counter_attribute, dev_attr) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/** 4862306a36Sopenharmony_ci * struct counter_attribute_group - container for attribute group 4962306a36Sopenharmony_ci * @name: name of the attribute group 5062306a36Sopenharmony_ci * @attr_list: list to keep track of created attributes 5162306a36Sopenharmony_ci * @num_attr: number of attributes 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_cistruct counter_attribute_group { 5462306a36Sopenharmony_ci const char *name; 5562306a36Sopenharmony_ci struct list_head attr_list; 5662306a36Sopenharmony_ci size_t num_attr; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic const char *const counter_function_str[] = { 6062306a36Sopenharmony_ci [COUNTER_FUNCTION_INCREASE] = "increase", 6162306a36Sopenharmony_ci [COUNTER_FUNCTION_DECREASE] = "decrease", 6262306a36Sopenharmony_ci [COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction", 6362306a36Sopenharmony_ci [COUNTER_FUNCTION_QUADRATURE_X1_A] = "quadrature x1 a", 6462306a36Sopenharmony_ci [COUNTER_FUNCTION_QUADRATURE_X1_B] = "quadrature x1 b", 6562306a36Sopenharmony_ci [COUNTER_FUNCTION_QUADRATURE_X2_A] = "quadrature x2 a", 6662306a36Sopenharmony_ci [COUNTER_FUNCTION_QUADRATURE_X2_B] = "quadrature x2 b", 6762306a36Sopenharmony_ci [COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4" 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic const char *const counter_signal_value_str[] = { 7162306a36Sopenharmony_ci [COUNTER_SIGNAL_LEVEL_LOW] = "low", 7262306a36Sopenharmony_ci [COUNTER_SIGNAL_LEVEL_HIGH] = "high" 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic const char *const counter_synapse_action_str[] = { 7662306a36Sopenharmony_ci [COUNTER_SYNAPSE_ACTION_NONE] = "none", 7762306a36Sopenharmony_ci [COUNTER_SYNAPSE_ACTION_RISING_EDGE] = "rising edge", 7862306a36Sopenharmony_ci [COUNTER_SYNAPSE_ACTION_FALLING_EDGE] = "falling edge", 7962306a36Sopenharmony_ci [COUNTER_SYNAPSE_ACTION_BOTH_EDGES] = "both edges" 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic const char *const counter_count_direction_str[] = { 8362306a36Sopenharmony_ci [COUNTER_COUNT_DIRECTION_FORWARD] = "forward", 8462306a36Sopenharmony_ci [COUNTER_COUNT_DIRECTION_BACKWARD] = "backward" 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic const char *const counter_count_mode_str[] = { 8862306a36Sopenharmony_ci [COUNTER_COUNT_MODE_NORMAL] = "normal", 8962306a36Sopenharmony_ci [COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit", 9062306a36Sopenharmony_ci [COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle", 9162306a36Sopenharmony_ci [COUNTER_COUNT_MODE_MODULO_N] = "modulo-n", 9262306a36Sopenharmony_ci [COUNTER_COUNT_MODE_INTERRUPT_ON_TERMINAL_COUNT] = "interrupt on terminal count", 9362306a36Sopenharmony_ci [COUNTER_COUNT_MODE_HARDWARE_RETRIGGERABLE_ONESHOT] = "hardware retriggerable one-shot", 9462306a36Sopenharmony_ci [COUNTER_COUNT_MODE_RATE_GENERATOR] = "rate generator", 9562306a36Sopenharmony_ci [COUNTER_COUNT_MODE_SQUARE_WAVE_MODE] = "square wave mode", 9662306a36Sopenharmony_ci [COUNTER_COUNT_MODE_SOFTWARE_TRIGGERED_STROBE] = "software triggered strobe", 9762306a36Sopenharmony_ci [COUNTER_COUNT_MODE_HARDWARE_TRIGGERED_STROBE] = "hardware triggered strobe", 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic const char *const counter_signal_polarity_str[] = { 10162306a36Sopenharmony_ci [COUNTER_SIGNAL_POLARITY_POSITIVE] = "positive", 10262306a36Sopenharmony_ci [COUNTER_SIGNAL_POLARITY_NEGATIVE] = "negative" 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic ssize_t counter_comp_u8_show(struct device *dev, 10662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci const struct counter_attribute *const a = to_counter_attribute(attr); 10962306a36Sopenharmony_ci struct counter_device *const counter = counter_from_dev(dev); 11062306a36Sopenharmony_ci int err; 11162306a36Sopenharmony_ci u8 data = 0; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci switch (a->scope) { 11462306a36Sopenharmony_ci case COUNTER_SCOPE_DEVICE: 11562306a36Sopenharmony_ci err = a->comp.device_u8_read(counter, &data); 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci case COUNTER_SCOPE_SIGNAL: 11862306a36Sopenharmony_ci err = a->comp.signal_u8_read(counter, a->parent, &data); 11962306a36Sopenharmony_ci break; 12062306a36Sopenharmony_ci case COUNTER_SCOPE_COUNT: 12162306a36Sopenharmony_ci err = a->comp.count_u8_read(counter, a->parent, &data); 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci default: 12462306a36Sopenharmony_ci return -EINVAL; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci if (err < 0) 12762306a36Sopenharmony_ci return err; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (a->comp.type == COUNTER_COMP_BOOL) 13062306a36Sopenharmony_ci /* data should already be boolean but ensure just to be safe */ 13162306a36Sopenharmony_ci data = !!data; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", (unsigned int)data); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic ssize_t counter_comp_u8_store(struct device *dev, 13762306a36Sopenharmony_ci struct device_attribute *attr, 13862306a36Sopenharmony_ci const char *buf, size_t len) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci const struct counter_attribute *const a = to_counter_attribute(attr); 14162306a36Sopenharmony_ci struct counter_device *const counter = counter_from_dev(dev); 14262306a36Sopenharmony_ci int err; 14362306a36Sopenharmony_ci bool bool_data = 0; 14462306a36Sopenharmony_ci u8 data = 0; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (a->comp.type == COUNTER_COMP_BOOL) { 14762306a36Sopenharmony_ci err = kstrtobool(buf, &bool_data); 14862306a36Sopenharmony_ci data = bool_data; 14962306a36Sopenharmony_ci } else 15062306a36Sopenharmony_ci err = kstrtou8(buf, 0, &data); 15162306a36Sopenharmony_ci if (err < 0) 15262306a36Sopenharmony_ci return err; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci switch (a->scope) { 15562306a36Sopenharmony_ci case COUNTER_SCOPE_DEVICE: 15662306a36Sopenharmony_ci err = a->comp.device_u8_write(counter, data); 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci case COUNTER_SCOPE_SIGNAL: 15962306a36Sopenharmony_ci err = a->comp.signal_u8_write(counter, a->parent, data); 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci case COUNTER_SCOPE_COUNT: 16262306a36Sopenharmony_ci err = a->comp.count_u8_write(counter, a->parent, data); 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci default: 16562306a36Sopenharmony_ci return -EINVAL; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci if (err < 0) 16862306a36Sopenharmony_ci return err; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return len; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic ssize_t counter_comp_u32_show(struct device *dev, 17462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci const struct counter_attribute *const a = to_counter_attribute(attr); 17762306a36Sopenharmony_ci struct counter_device *const counter = counter_from_dev(dev); 17862306a36Sopenharmony_ci const struct counter_available *const avail = a->comp.priv; 17962306a36Sopenharmony_ci int err; 18062306a36Sopenharmony_ci u32 data = 0; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci switch (a->scope) { 18362306a36Sopenharmony_ci case COUNTER_SCOPE_DEVICE: 18462306a36Sopenharmony_ci err = a->comp.device_u32_read(counter, &data); 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci case COUNTER_SCOPE_SIGNAL: 18762306a36Sopenharmony_ci err = a->comp.signal_u32_read(counter, a->parent, &data); 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci case COUNTER_SCOPE_COUNT: 19062306a36Sopenharmony_ci if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION) 19162306a36Sopenharmony_ci err = a->comp.action_read(counter, a->parent, 19262306a36Sopenharmony_ci a->comp.priv, &data); 19362306a36Sopenharmony_ci else 19462306a36Sopenharmony_ci err = a->comp.count_u32_read(counter, a->parent, &data); 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci default: 19762306a36Sopenharmony_ci return -EINVAL; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci if (err < 0) 20062306a36Sopenharmony_ci return err; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci switch (a->comp.type) { 20362306a36Sopenharmony_ci case COUNTER_COMP_FUNCTION: 20462306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", counter_function_str[data]); 20562306a36Sopenharmony_ci case COUNTER_COMP_SIGNAL_LEVEL: 20662306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", counter_signal_value_str[data]); 20762306a36Sopenharmony_ci case COUNTER_COMP_SYNAPSE_ACTION: 20862306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", counter_synapse_action_str[data]); 20962306a36Sopenharmony_ci case COUNTER_COMP_ENUM: 21062306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", avail->strs[data]); 21162306a36Sopenharmony_ci case COUNTER_COMP_COUNT_DIRECTION: 21262306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", counter_count_direction_str[data]); 21362306a36Sopenharmony_ci case COUNTER_COMP_COUNT_MODE: 21462306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", counter_count_mode_str[data]); 21562306a36Sopenharmony_ci case COUNTER_COMP_SIGNAL_POLARITY: 21662306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", counter_signal_polarity_str[data]); 21762306a36Sopenharmony_ci default: 21862306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", (unsigned int)data); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic int counter_find_enum(u32 *const enum_item, const u32 *const enums, 22362306a36Sopenharmony_ci const size_t num_enums, const char *const buf, 22462306a36Sopenharmony_ci const char *const string_array[]) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci size_t index; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci for (index = 0; index < num_enums; index++) { 22962306a36Sopenharmony_ci *enum_item = enums[index]; 23062306a36Sopenharmony_ci if (sysfs_streq(buf, string_array[*enum_item])) 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return -EINVAL; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic ssize_t counter_comp_u32_store(struct device *dev, 23862306a36Sopenharmony_ci struct device_attribute *attr, 23962306a36Sopenharmony_ci const char *buf, size_t len) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci const struct counter_attribute *const a = to_counter_attribute(attr); 24262306a36Sopenharmony_ci struct counter_device *const counter = counter_from_dev(dev); 24362306a36Sopenharmony_ci struct counter_count *const count = a->parent; 24462306a36Sopenharmony_ci struct counter_synapse *const synapse = a->comp.priv; 24562306a36Sopenharmony_ci const struct counter_available *const avail = a->comp.priv; 24662306a36Sopenharmony_ci int err; 24762306a36Sopenharmony_ci u32 data = 0; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci switch (a->comp.type) { 25062306a36Sopenharmony_ci case COUNTER_COMP_FUNCTION: 25162306a36Sopenharmony_ci err = counter_find_enum(&data, count->functions_list, 25262306a36Sopenharmony_ci count->num_functions, buf, 25362306a36Sopenharmony_ci counter_function_str); 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci case COUNTER_COMP_SYNAPSE_ACTION: 25662306a36Sopenharmony_ci err = counter_find_enum(&data, synapse->actions_list, 25762306a36Sopenharmony_ci synapse->num_actions, buf, 25862306a36Sopenharmony_ci counter_synapse_action_str); 25962306a36Sopenharmony_ci break; 26062306a36Sopenharmony_ci case COUNTER_COMP_ENUM: 26162306a36Sopenharmony_ci err = __sysfs_match_string(avail->strs, avail->num_items, buf); 26262306a36Sopenharmony_ci data = err; 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci case COUNTER_COMP_COUNT_MODE: 26562306a36Sopenharmony_ci err = counter_find_enum(&data, avail->enums, avail->num_items, 26662306a36Sopenharmony_ci buf, counter_count_mode_str); 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci case COUNTER_COMP_SIGNAL_POLARITY: 26962306a36Sopenharmony_ci err = counter_find_enum(&data, avail->enums, avail->num_items, 27062306a36Sopenharmony_ci buf, counter_signal_polarity_str); 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci default: 27362306a36Sopenharmony_ci err = kstrtou32(buf, 0, &data); 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci if (err < 0) 27762306a36Sopenharmony_ci return err; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci switch (a->scope) { 28062306a36Sopenharmony_ci case COUNTER_SCOPE_DEVICE: 28162306a36Sopenharmony_ci err = a->comp.device_u32_write(counter, data); 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci case COUNTER_SCOPE_SIGNAL: 28462306a36Sopenharmony_ci err = a->comp.signal_u32_write(counter, a->parent, data); 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci case COUNTER_SCOPE_COUNT: 28762306a36Sopenharmony_ci if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION) 28862306a36Sopenharmony_ci err = a->comp.action_write(counter, count, synapse, 28962306a36Sopenharmony_ci data); 29062306a36Sopenharmony_ci else 29162306a36Sopenharmony_ci err = a->comp.count_u32_write(counter, count, data); 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci default: 29462306a36Sopenharmony_ci return -EINVAL; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci if (err < 0) 29762306a36Sopenharmony_ci return err; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return len; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic ssize_t counter_comp_u64_show(struct device *dev, 30362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci const struct counter_attribute *const a = to_counter_attribute(attr); 30662306a36Sopenharmony_ci struct counter_device *const counter = counter_from_dev(dev); 30762306a36Sopenharmony_ci int err; 30862306a36Sopenharmony_ci u64 data = 0; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci switch (a->scope) { 31162306a36Sopenharmony_ci case COUNTER_SCOPE_DEVICE: 31262306a36Sopenharmony_ci err = a->comp.device_u64_read(counter, &data); 31362306a36Sopenharmony_ci break; 31462306a36Sopenharmony_ci case COUNTER_SCOPE_SIGNAL: 31562306a36Sopenharmony_ci err = a->comp.signal_u64_read(counter, a->parent, &data); 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci case COUNTER_SCOPE_COUNT: 31862306a36Sopenharmony_ci err = a->comp.count_u64_read(counter, a->parent, &data); 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci default: 32162306a36Sopenharmony_ci return -EINVAL; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci if (err < 0) 32462306a36Sopenharmony_ci return err; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return sysfs_emit(buf, "%llu\n", (unsigned long long)data); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic ssize_t counter_comp_u64_store(struct device *dev, 33062306a36Sopenharmony_ci struct device_attribute *attr, 33162306a36Sopenharmony_ci const char *buf, size_t len) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci const struct counter_attribute *const a = to_counter_attribute(attr); 33462306a36Sopenharmony_ci struct counter_device *const counter = counter_from_dev(dev); 33562306a36Sopenharmony_ci int err; 33662306a36Sopenharmony_ci u64 data = 0; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci err = kstrtou64(buf, 0, &data); 33962306a36Sopenharmony_ci if (err < 0) 34062306a36Sopenharmony_ci return err; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci switch (a->scope) { 34362306a36Sopenharmony_ci case COUNTER_SCOPE_DEVICE: 34462306a36Sopenharmony_ci err = a->comp.device_u64_write(counter, data); 34562306a36Sopenharmony_ci break; 34662306a36Sopenharmony_ci case COUNTER_SCOPE_SIGNAL: 34762306a36Sopenharmony_ci err = a->comp.signal_u64_write(counter, a->parent, data); 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci case COUNTER_SCOPE_COUNT: 35062306a36Sopenharmony_ci err = a->comp.count_u64_write(counter, a->parent, data); 35162306a36Sopenharmony_ci break; 35262306a36Sopenharmony_ci default: 35362306a36Sopenharmony_ci return -EINVAL; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci if (err < 0) 35662306a36Sopenharmony_ci return err; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return len; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic ssize_t counter_comp_array_u32_show(struct device *dev, 36262306a36Sopenharmony_ci struct device_attribute *attr, 36362306a36Sopenharmony_ci char *buf) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci const struct counter_attribute *const a = to_counter_attribute(attr); 36662306a36Sopenharmony_ci struct counter_device *const counter = counter_from_dev(dev); 36762306a36Sopenharmony_ci const struct counter_array *const element = a->comp.priv; 36862306a36Sopenharmony_ci int err; 36962306a36Sopenharmony_ci u32 data = 0; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (a->scope != COUNTER_SCOPE_SIGNAL || 37262306a36Sopenharmony_ci element->type != COUNTER_COMP_SIGNAL_POLARITY) 37362306a36Sopenharmony_ci return -EINVAL; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci err = a->comp.signal_array_u32_read(counter, a->parent, element->idx, 37662306a36Sopenharmony_ci &data); 37762306a36Sopenharmony_ci if (err < 0) 37862306a36Sopenharmony_ci return err; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", counter_signal_polarity_str[data]); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic ssize_t counter_comp_array_u32_store(struct device *dev, 38462306a36Sopenharmony_ci struct device_attribute *attr, 38562306a36Sopenharmony_ci const char *buf, size_t len) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci const struct counter_attribute *const a = to_counter_attribute(attr); 38862306a36Sopenharmony_ci struct counter_device *const counter = counter_from_dev(dev); 38962306a36Sopenharmony_ci const struct counter_array *const element = a->comp.priv; 39062306a36Sopenharmony_ci int err; 39162306a36Sopenharmony_ci u32 data = 0; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (element->type != COUNTER_COMP_SIGNAL_POLARITY || 39462306a36Sopenharmony_ci a->scope != COUNTER_SCOPE_SIGNAL) 39562306a36Sopenharmony_ci return -EINVAL; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci err = counter_find_enum(&data, element->avail->enums, 39862306a36Sopenharmony_ci element->avail->num_items, buf, 39962306a36Sopenharmony_ci counter_signal_polarity_str); 40062306a36Sopenharmony_ci if (err < 0) 40162306a36Sopenharmony_ci return err; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci err = a->comp.signal_array_u32_write(counter, a->parent, element->idx, 40462306a36Sopenharmony_ci data); 40562306a36Sopenharmony_ci if (err < 0) 40662306a36Sopenharmony_ci return err; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return len; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic ssize_t counter_comp_array_u64_show(struct device *dev, 41262306a36Sopenharmony_ci struct device_attribute *attr, 41362306a36Sopenharmony_ci char *buf) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci const struct counter_attribute *const a = to_counter_attribute(attr); 41662306a36Sopenharmony_ci struct counter_device *const counter = counter_from_dev(dev); 41762306a36Sopenharmony_ci const struct counter_array *const element = a->comp.priv; 41862306a36Sopenharmony_ci int err; 41962306a36Sopenharmony_ci u64 data = 0; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci switch (a->scope) { 42262306a36Sopenharmony_ci case COUNTER_SCOPE_DEVICE: 42362306a36Sopenharmony_ci err = a->comp.device_array_u64_read(counter, element->idx, 42462306a36Sopenharmony_ci &data); 42562306a36Sopenharmony_ci break; 42662306a36Sopenharmony_ci case COUNTER_SCOPE_SIGNAL: 42762306a36Sopenharmony_ci err = a->comp.signal_array_u64_read(counter, a->parent, 42862306a36Sopenharmony_ci element->idx, &data); 42962306a36Sopenharmony_ci break; 43062306a36Sopenharmony_ci case COUNTER_SCOPE_COUNT: 43162306a36Sopenharmony_ci err = a->comp.count_array_u64_read(counter, a->parent, 43262306a36Sopenharmony_ci element->idx, &data); 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci default: 43562306a36Sopenharmony_ci return -EINVAL; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci if (err < 0) 43862306a36Sopenharmony_ci return err; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return sysfs_emit(buf, "%llu\n", (unsigned long long)data); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic ssize_t counter_comp_array_u64_store(struct device *dev, 44462306a36Sopenharmony_ci struct device_attribute *attr, 44562306a36Sopenharmony_ci const char *buf, size_t len) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci const struct counter_attribute *const a = to_counter_attribute(attr); 44862306a36Sopenharmony_ci struct counter_device *const counter = counter_from_dev(dev); 44962306a36Sopenharmony_ci const struct counter_array *const element = a->comp.priv; 45062306a36Sopenharmony_ci int err; 45162306a36Sopenharmony_ci u64 data = 0; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci err = kstrtou64(buf, 0, &data); 45462306a36Sopenharmony_ci if (err < 0) 45562306a36Sopenharmony_ci return err; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci switch (a->scope) { 45862306a36Sopenharmony_ci case COUNTER_SCOPE_DEVICE: 45962306a36Sopenharmony_ci err = a->comp.device_array_u64_write(counter, element->idx, 46062306a36Sopenharmony_ci data); 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci case COUNTER_SCOPE_SIGNAL: 46362306a36Sopenharmony_ci err = a->comp.signal_array_u64_write(counter, a->parent, 46462306a36Sopenharmony_ci element->idx, data); 46562306a36Sopenharmony_ci break; 46662306a36Sopenharmony_ci case COUNTER_SCOPE_COUNT: 46762306a36Sopenharmony_ci err = a->comp.count_array_u64_write(counter, a->parent, 46862306a36Sopenharmony_ci element->idx, data); 46962306a36Sopenharmony_ci break; 47062306a36Sopenharmony_ci default: 47162306a36Sopenharmony_ci return -EINVAL; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci if (err < 0) 47462306a36Sopenharmony_ci return err; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return len; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic ssize_t enums_available_show(const u32 *const enums, 48062306a36Sopenharmony_ci const size_t num_enums, 48162306a36Sopenharmony_ci const char *const strs[], char *buf) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci size_t len = 0; 48462306a36Sopenharmony_ci size_t index; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci for (index = 0; index < num_enums; index++) 48762306a36Sopenharmony_ci len += sysfs_emit_at(buf, len, "%s\n", strs[enums[index]]); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return len; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic ssize_t strs_available_show(const struct counter_available *const avail, 49362306a36Sopenharmony_ci char *buf) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci size_t len = 0; 49662306a36Sopenharmony_ci size_t index; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci for (index = 0; index < avail->num_items; index++) 49962306a36Sopenharmony_ci len += sysfs_emit_at(buf, len, "%s\n", avail->strs[index]); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return len; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic ssize_t counter_comp_available_show(struct device *dev, 50562306a36Sopenharmony_ci struct device_attribute *attr, 50662306a36Sopenharmony_ci char *buf) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci const struct counter_attribute *const a = to_counter_attribute(attr); 50962306a36Sopenharmony_ci const struct counter_count *const count = a->parent; 51062306a36Sopenharmony_ci const struct counter_synapse *const synapse = a->comp.priv; 51162306a36Sopenharmony_ci const struct counter_available *const avail = a->comp.priv; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci switch (a->comp.type) { 51462306a36Sopenharmony_ci case COUNTER_COMP_FUNCTION: 51562306a36Sopenharmony_ci return enums_available_show(count->functions_list, 51662306a36Sopenharmony_ci count->num_functions, 51762306a36Sopenharmony_ci counter_function_str, buf); 51862306a36Sopenharmony_ci case COUNTER_COMP_SYNAPSE_ACTION: 51962306a36Sopenharmony_ci return enums_available_show(synapse->actions_list, 52062306a36Sopenharmony_ci synapse->num_actions, 52162306a36Sopenharmony_ci counter_synapse_action_str, buf); 52262306a36Sopenharmony_ci case COUNTER_COMP_ENUM: 52362306a36Sopenharmony_ci return strs_available_show(avail, buf); 52462306a36Sopenharmony_ci case COUNTER_COMP_COUNT_MODE: 52562306a36Sopenharmony_ci return enums_available_show(avail->enums, avail->num_items, 52662306a36Sopenharmony_ci counter_count_mode_str, buf); 52762306a36Sopenharmony_ci default: 52862306a36Sopenharmony_ci return -EINVAL; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic int counter_avail_attr_create(struct device *const dev, 53362306a36Sopenharmony_ci struct counter_attribute_group *const group, 53462306a36Sopenharmony_ci const struct counter_comp *const comp, void *const parent) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct counter_attribute *counter_attr; 53762306a36Sopenharmony_ci struct device_attribute *dev_attr; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL); 54062306a36Sopenharmony_ci if (!counter_attr) 54162306a36Sopenharmony_ci return -ENOMEM; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* Configure Counter attribute */ 54462306a36Sopenharmony_ci counter_attr->comp.type = comp->type; 54562306a36Sopenharmony_ci counter_attr->comp.priv = comp->priv; 54662306a36Sopenharmony_ci counter_attr->parent = parent; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* Initialize sysfs attribute */ 54962306a36Sopenharmony_ci dev_attr = &counter_attr->dev_attr; 55062306a36Sopenharmony_ci sysfs_attr_init(&dev_attr->attr); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* Configure device attribute */ 55362306a36Sopenharmony_ci dev_attr->attr.name = devm_kasprintf(dev, GFP_KERNEL, "%s_available", 55462306a36Sopenharmony_ci comp->name); 55562306a36Sopenharmony_ci if (!dev_attr->attr.name) 55662306a36Sopenharmony_ci return -ENOMEM; 55762306a36Sopenharmony_ci dev_attr->attr.mode = 0444; 55862306a36Sopenharmony_ci dev_attr->show = counter_comp_available_show; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* Store list node */ 56162306a36Sopenharmony_ci list_add(&counter_attr->l, &group->attr_list); 56262306a36Sopenharmony_ci group->num_attr++; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return 0; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int counter_attr_create(struct device *const dev, 56862306a36Sopenharmony_ci struct counter_attribute_group *const group, 56962306a36Sopenharmony_ci const struct counter_comp *const comp, 57062306a36Sopenharmony_ci const enum counter_scope scope, 57162306a36Sopenharmony_ci void *const parent) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci const struct counter_array *const array = comp->priv; 57462306a36Sopenharmony_ci struct counter_attribute *counter_attr; 57562306a36Sopenharmony_ci struct device_attribute *dev_attr; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL); 57862306a36Sopenharmony_ci if (!counter_attr) 57962306a36Sopenharmony_ci return -ENOMEM; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* Configure Counter attribute */ 58262306a36Sopenharmony_ci counter_attr->comp = *comp; 58362306a36Sopenharmony_ci counter_attr->scope = scope; 58462306a36Sopenharmony_ci counter_attr->parent = parent; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* Configure device attribute */ 58762306a36Sopenharmony_ci dev_attr = &counter_attr->dev_attr; 58862306a36Sopenharmony_ci sysfs_attr_init(&dev_attr->attr); 58962306a36Sopenharmony_ci dev_attr->attr.name = comp->name; 59062306a36Sopenharmony_ci switch (comp->type) { 59162306a36Sopenharmony_ci case COUNTER_COMP_U8: 59262306a36Sopenharmony_ci case COUNTER_COMP_BOOL: 59362306a36Sopenharmony_ci if (comp->device_u8_read) { 59462306a36Sopenharmony_ci dev_attr->attr.mode |= 0444; 59562306a36Sopenharmony_ci dev_attr->show = counter_comp_u8_show; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci if (comp->device_u8_write) { 59862306a36Sopenharmony_ci dev_attr->attr.mode |= 0200; 59962306a36Sopenharmony_ci dev_attr->store = counter_comp_u8_store; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci break; 60262306a36Sopenharmony_ci case COUNTER_COMP_SIGNAL_LEVEL: 60362306a36Sopenharmony_ci case COUNTER_COMP_FUNCTION: 60462306a36Sopenharmony_ci case COUNTER_COMP_SYNAPSE_ACTION: 60562306a36Sopenharmony_ci case COUNTER_COMP_ENUM: 60662306a36Sopenharmony_ci case COUNTER_COMP_COUNT_DIRECTION: 60762306a36Sopenharmony_ci case COUNTER_COMP_COUNT_MODE: 60862306a36Sopenharmony_ci case COUNTER_COMP_SIGNAL_POLARITY: 60962306a36Sopenharmony_ci if (comp->device_u32_read) { 61062306a36Sopenharmony_ci dev_attr->attr.mode |= 0444; 61162306a36Sopenharmony_ci dev_attr->show = counter_comp_u32_show; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci if (comp->device_u32_write) { 61462306a36Sopenharmony_ci dev_attr->attr.mode |= 0200; 61562306a36Sopenharmony_ci dev_attr->store = counter_comp_u32_store; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci break; 61862306a36Sopenharmony_ci case COUNTER_COMP_U64: 61962306a36Sopenharmony_ci if (comp->device_u64_read) { 62062306a36Sopenharmony_ci dev_attr->attr.mode |= 0444; 62162306a36Sopenharmony_ci dev_attr->show = counter_comp_u64_show; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci if (comp->device_u64_write) { 62462306a36Sopenharmony_ci dev_attr->attr.mode |= 0200; 62562306a36Sopenharmony_ci dev_attr->store = counter_comp_u64_store; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci break; 62862306a36Sopenharmony_ci case COUNTER_COMP_ARRAY: 62962306a36Sopenharmony_ci switch (array->type) { 63062306a36Sopenharmony_ci case COUNTER_COMP_SIGNAL_POLARITY: 63162306a36Sopenharmony_ci if (comp->signal_array_u32_read) { 63262306a36Sopenharmony_ci dev_attr->attr.mode |= 0444; 63362306a36Sopenharmony_ci dev_attr->show = counter_comp_array_u32_show; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci if (comp->signal_array_u32_write) { 63662306a36Sopenharmony_ci dev_attr->attr.mode |= 0200; 63762306a36Sopenharmony_ci dev_attr->store = counter_comp_array_u32_store; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci break; 64062306a36Sopenharmony_ci case COUNTER_COMP_U64: 64162306a36Sopenharmony_ci if (comp->device_array_u64_read) { 64262306a36Sopenharmony_ci dev_attr->attr.mode |= 0444; 64362306a36Sopenharmony_ci dev_attr->show = counter_comp_array_u64_show; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci if (comp->device_array_u64_write) { 64662306a36Sopenharmony_ci dev_attr->attr.mode |= 0200; 64762306a36Sopenharmony_ci dev_attr->store = counter_comp_array_u64_store; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci break; 65062306a36Sopenharmony_ci default: 65162306a36Sopenharmony_ci return -EINVAL; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci break; 65462306a36Sopenharmony_ci default: 65562306a36Sopenharmony_ci return -EINVAL; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci /* Store list node */ 65962306a36Sopenharmony_ci list_add(&counter_attr->l, &group->attr_list); 66062306a36Sopenharmony_ci group->num_attr++; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* Create "*_available" attribute if needed */ 66362306a36Sopenharmony_ci switch (comp->type) { 66462306a36Sopenharmony_ci case COUNTER_COMP_FUNCTION: 66562306a36Sopenharmony_ci case COUNTER_COMP_SYNAPSE_ACTION: 66662306a36Sopenharmony_ci case COUNTER_COMP_ENUM: 66762306a36Sopenharmony_ci case COUNTER_COMP_COUNT_MODE: 66862306a36Sopenharmony_ci return counter_avail_attr_create(dev, group, comp, parent); 66962306a36Sopenharmony_ci default: 67062306a36Sopenharmony_ci return 0; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic ssize_t counter_comp_name_show(struct device *dev, 67562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", to_counter_attribute(attr)->comp.name); 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic int counter_name_attr_create(struct device *const dev, 68162306a36Sopenharmony_ci struct counter_attribute_group *const group, 68262306a36Sopenharmony_ci const char *const name) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci struct counter_attribute *counter_attr; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL); 68762306a36Sopenharmony_ci if (!counter_attr) 68862306a36Sopenharmony_ci return -ENOMEM; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* Configure Counter attribute */ 69162306a36Sopenharmony_ci counter_attr->comp.name = name; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* Configure device attribute */ 69462306a36Sopenharmony_ci sysfs_attr_init(&counter_attr->dev_attr.attr); 69562306a36Sopenharmony_ci counter_attr->dev_attr.attr.name = "name"; 69662306a36Sopenharmony_ci counter_attr->dev_attr.attr.mode = 0444; 69762306a36Sopenharmony_ci counter_attr->dev_attr.show = counter_comp_name_show; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* Store list node */ 70062306a36Sopenharmony_ci list_add(&counter_attr->l, &group->attr_list); 70162306a36Sopenharmony_ci group->num_attr++; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci return 0; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic ssize_t counter_comp_id_show(struct device *dev, 70762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci const size_t id = (size_t)to_counter_attribute(attr)->comp.priv; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci return sysfs_emit(buf, "%zu\n", id); 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cistatic int counter_comp_id_attr_create(struct device *const dev, 71562306a36Sopenharmony_ci struct counter_attribute_group *const group, 71662306a36Sopenharmony_ci const char *name, const size_t id) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct counter_attribute *counter_attr; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* Allocate Counter attribute */ 72162306a36Sopenharmony_ci counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL); 72262306a36Sopenharmony_ci if (!counter_attr) 72362306a36Sopenharmony_ci return -ENOMEM; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci /* Generate component ID name */ 72662306a36Sopenharmony_ci name = devm_kasprintf(dev, GFP_KERNEL, "%s_component_id", name); 72762306a36Sopenharmony_ci if (!name) 72862306a36Sopenharmony_ci return -ENOMEM; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* Configure Counter attribute */ 73162306a36Sopenharmony_ci counter_attr->comp.priv = (void *)id; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* Configure device attribute */ 73462306a36Sopenharmony_ci sysfs_attr_init(&counter_attr->dev_attr.attr); 73562306a36Sopenharmony_ci counter_attr->dev_attr.attr.name = name; 73662306a36Sopenharmony_ci counter_attr->dev_attr.attr.mode = 0444; 73762306a36Sopenharmony_ci counter_attr->dev_attr.show = counter_comp_id_show; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* Store list node */ 74062306a36Sopenharmony_ci list_add(&counter_attr->l, &group->attr_list); 74162306a36Sopenharmony_ci group->num_attr++; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return 0; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic int counter_ext_attrs_create(struct device *const dev, 74762306a36Sopenharmony_ci struct counter_attribute_group *const group, 74862306a36Sopenharmony_ci const struct counter_comp *const ext, 74962306a36Sopenharmony_ci const enum counter_scope scope, 75062306a36Sopenharmony_ci void *const parent, const size_t id) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci int err; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* Create main extension attribute */ 75562306a36Sopenharmony_ci err = counter_attr_create(dev, group, ext, scope, parent); 75662306a36Sopenharmony_ci if (err < 0) 75762306a36Sopenharmony_ci return err; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* Create extension id attribute */ 76062306a36Sopenharmony_ci return counter_comp_id_attr_create(dev, group, ext->name, id); 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic int counter_array_attrs_create(struct device *const dev, 76462306a36Sopenharmony_ci struct counter_attribute_group *const group, 76562306a36Sopenharmony_ci const struct counter_comp *const comp, 76662306a36Sopenharmony_ci const enum counter_scope scope, 76762306a36Sopenharmony_ci void *const parent, const size_t id) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci const struct counter_array *const array = comp->priv; 77062306a36Sopenharmony_ci struct counter_comp ext = *comp; 77162306a36Sopenharmony_ci struct counter_array *element; 77262306a36Sopenharmony_ci size_t idx; 77362306a36Sopenharmony_ci int err; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci /* Create an attribute for each array element */ 77662306a36Sopenharmony_ci for (idx = 0; idx < array->length; idx++) { 77762306a36Sopenharmony_ci /* Generate array element attribute name */ 77862306a36Sopenharmony_ci ext.name = devm_kasprintf(dev, GFP_KERNEL, "%s%zu", comp->name, 77962306a36Sopenharmony_ci idx); 78062306a36Sopenharmony_ci if (!ext.name) 78162306a36Sopenharmony_ci return -ENOMEM; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* Allocate and configure array element */ 78462306a36Sopenharmony_ci element = devm_kzalloc(dev, sizeof(*element), GFP_KERNEL); 78562306a36Sopenharmony_ci if (!element) 78662306a36Sopenharmony_ci return -ENOMEM; 78762306a36Sopenharmony_ci element->type = array->type; 78862306a36Sopenharmony_ci element->avail = array->avail; 78962306a36Sopenharmony_ci element->idx = idx; 79062306a36Sopenharmony_ci ext.priv = element; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci /* Create all attributes associated with the array element */ 79362306a36Sopenharmony_ci err = counter_ext_attrs_create(dev, group, &ext, scope, parent, 79462306a36Sopenharmony_ci id + idx); 79562306a36Sopenharmony_ci if (err < 0) 79662306a36Sopenharmony_ci return err; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci return 0; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic int counter_sysfs_exts_add(struct device *const dev, 80362306a36Sopenharmony_ci struct counter_attribute_group *const group, 80462306a36Sopenharmony_ci const struct counter_comp *const exts, 80562306a36Sopenharmony_ci const size_t num_ext, 80662306a36Sopenharmony_ci const enum counter_scope scope, 80762306a36Sopenharmony_ci void *const parent) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci size_t i; 81062306a36Sopenharmony_ci const struct counter_comp *ext; 81162306a36Sopenharmony_ci int err; 81262306a36Sopenharmony_ci size_t id = 0; 81362306a36Sopenharmony_ci const struct counter_array *array; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci /* Create attributes for each extension */ 81662306a36Sopenharmony_ci for (i = 0; i < num_ext; i++) { 81762306a36Sopenharmony_ci ext = &exts[i]; 81862306a36Sopenharmony_ci if (ext->type == COUNTER_COMP_ARRAY) { 81962306a36Sopenharmony_ci err = counter_array_attrs_create(dev, group, ext, scope, 82062306a36Sopenharmony_ci parent, id); 82162306a36Sopenharmony_ci array = ext->priv; 82262306a36Sopenharmony_ci id += array->length; 82362306a36Sopenharmony_ci } else { 82462306a36Sopenharmony_ci err = counter_ext_attrs_create(dev, group, ext, scope, 82562306a36Sopenharmony_ci parent, id); 82662306a36Sopenharmony_ci id++; 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci if (err < 0) 82962306a36Sopenharmony_ci return err; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci return 0; 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic struct counter_comp counter_signal_comp = { 83662306a36Sopenharmony_ci .type = COUNTER_COMP_SIGNAL_LEVEL, 83762306a36Sopenharmony_ci .name = "signal", 83862306a36Sopenharmony_ci}; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cistatic int counter_signal_attrs_create(struct counter_device *const counter, 84162306a36Sopenharmony_ci struct counter_attribute_group *const cattr_group, 84262306a36Sopenharmony_ci struct counter_signal *const signal) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci const enum counter_scope scope = COUNTER_SCOPE_SIGNAL; 84562306a36Sopenharmony_ci struct device *const dev = &counter->dev; 84662306a36Sopenharmony_ci int err; 84762306a36Sopenharmony_ci struct counter_comp comp; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* Create main Signal attribute */ 85062306a36Sopenharmony_ci comp = counter_signal_comp; 85162306a36Sopenharmony_ci comp.signal_u32_read = counter->ops->signal_read; 85262306a36Sopenharmony_ci err = counter_attr_create(dev, cattr_group, &comp, scope, signal); 85362306a36Sopenharmony_ci if (err < 0) 85462306a36Sopenharmony_ci return err; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* Create Signal name attribute */ 85762306a36Sopenharmony_ci err = counter_name_attr_create(dev, cattr_group, signal->name); 85862306a36Sopenharmony_ci if (err < 0) 85962306a36Sopenharmony_ci return err; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* Add Signal extensions */ 86262306a36Sopenharmony_ci return counter_sysfs_exts_add(dev, cattr_group, signal->ext, 86362306a36Sopenharmony_ci signal->num_ext, scope, signal); 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic int counter_sysfs_signals_add(struct counter_device *const counter, 86762306a36Sopenharmony_ci struct counter_attribute_group *const groups) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci size_t i; 87062306a36Sopenharmony_ci int err; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* Add each Signal */ 87362306a36Sopenharmony_ci for (i = 0; i < counter->num_signals; i++) { 87462306a36Sopenharmony_ci /* Generate Signal attribute directory name */ 87562306a36Sopenharmony_ci groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL, 87662306a36Sopenharmony_ci "signal%zu", i); 87762306a36Sopenharmony_ci if (!groups[i].name) 87862306a36Sopenharmony_ci return -ENOMEM; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* Create all attributes associated with Signal */ 88162306a36Sopenharmony_ci err = counter_signal_attrs_create(counter, groups + i, 88262306a36Sopenharmony_ci counter->signals + i); 88362306a36Sopenharmony_ci if (err < 0) 88462306a36Sopenharmony_ci return err; 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci return 0; 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic int counter_sysfs_synapses_add(struct counter_device *const counter, 89162306a36Sopenharmony_ci struct counter_attribute_group *const group, 89262306a36Sopenharmony_ci struct counter_count *const count) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci size_t i; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci /* Add each Synapse */ 89762306a36Sopenharmony_ci for (i = 0; i < count->num_synapses; i++) { 89862306a36Sopenharmony_ci struct device *const dev = &counter->dev; 89962306a36Sopenharmony_ci struct counter_synapse *synapse; 90062306a36Sopenharmony_ci size_t id; 90162306a36Sopenharmony_ci struct counter_comp comp; 90262306a36Sopenharmony_ci int err; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci synapse = count->synapses + i; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci /* Generate Synapse action name */ 90762306a36Sopenharmony_ci id = synapse->signal - counter->signals; 90862306a36Sopenharmony_ci comp.name = devm_kasprintf(dev, GFP_KERNEL, "signal%zu_action", 90962306a36Sopenharmony_ci id); 91062306a36Sopenharmony_ci if (!comp.name) 91162306a36Sopenharmony_ci return -ENOMEM; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci /* Create action attribute */ 91462306a36Sopenharmony_ci comp.type = COUNTER_COMP_SYNAPSE_ACTION; 91562306a36Sopenharmony_ci comp.action_read = counter->ops->action_read; 91662306a36Sopenharmony_ci comp.action_write = counter->ops->action_write; 91762306a36Sopenharmony_ci comp.priv = synapse; 91862306a36Sopenharmony_ci err = counter_attr_create(dev, group, &comp, 91962306a36Sopenharmony_ci COUNTER_SCOPE_COUNT, count); 92062306a36Sopenharmony_ci if (err < 0) 92162306a36Sopenharmony_ci return err; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* Create Synapse component ID attribute */ 92462306a36Sopenharmony_ci err = counter_comp_id_attr_create(dev, group, comp.name, i); 92562306a36Sopenharmony_ci if (err < 0) 92662306a36Sopenharmony_ci return err; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci return 0; 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic struct counter_comp counter_count_comp = 93362306a36Sopenharmony_ci COUNTER_COMP_COUNT_U64("count", NULL, NULL); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic struct counter_comp counter_function_comp = { 93662306a36Sopenharmony_ci .type = COUNTER_COMP_FUNCTION, 93762306a36Sopenharmony_ci .name = "function", 93862306a36Sopenharmony_ci}; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic int counter_count_attrs_create(struct counter_device *const counter, 94162306a36Sopenharmony_ci struct counter_attribute_group *const cattr_group, 94262306a36Sopenharmony_ci struct counter_count *const count) 94362306a36Sopenharmony_ci{ 94462306a36Sopenharmony_ci const enum counter_scope scope = COUNTER_SCOPE_COUNT; 94562306a36Sopenharmony_ci struct device *const dev = &counter->dev; 94662306a36Sopenharmony_ci int err; 94762306a36Sopenharmony_ci struct counter_comp comp; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci /* Create main Count attribute */ 95062306a36Sopenharmony_ci comp = counter_count_comp; 95162306a36Sopenharmony_ci comp.count_u64_read = counter->ops->count_read; 95262306a36Sopenharmony_ci comp.count_u64_write = counter->ops->count_write; 95362306a36Sopenharmony_ci err = counter_attr_create(dev, cattr_group, &comp, scope, count); 95462306a36Sopenharmony_ci if (err < 0) 95562306a36Sopenharmony_ci return err; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* Create Count name attribute */ 95862306a36Sopenharmony_ci err = counter_name_attr_create(dev, cattr_group, count->name); 95962306a36Sopenharmony_ci if (err < 0) 96062306a36Sopenharmony_ci return err; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci /* Create Count function attribute */ 96362306a36Sopenharmony_ci comp = counter_function_comp; 96462306a36Sopenharmony_ci comp.count_u32_read = counter->ops->function_read; 96562306a36Sopenharmony_ci comp.count_u32_write = counter->ops->function_write; 96662306a36Sopenharmony_ci err = counter_attr_create(dev, cattr_group, &comp, scope, count); 96762306a36Sopenharmony_ci if (err < 0) 96862306a36Sopenharmony_ci return err; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci /* Add Count extensions */ 97162306a36Sopenharmony_ci return counter_sysfs_exts_add(dev, cattr_group, count->ext, 97262306a36Sopenharmony_ci count->num_ext, scope, count); 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic int counter_sysfs_counts_add(struct counter_device *const counter, 97662306a36Sopenharmony_ci struct counter_attribute_group *const groups) 97762306a36Sopenharmony_ci{ 97862306a36Sopenharmony_ci size_t i; 97962306a36Sopenharmony_ci struct counter_count *count; 98062306a36Sopenharmony_ci int err; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci /* Add each Count */ 98362306a36Sopenharmony_ci for (i = 0; i < counter->num_counts; i++) { 98462306a36Sopenharmony_ci count = counter->counts + i; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci /* Generate Count attribute directory name */ 98762306a36Sopenharmony_ci groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL, 98862306a36Sopenharmony_ci "count%zu", i); 98962306a36Sopenharmony_ci if (!groups[i].name) 99062306a36Sopenharmony_ci return -ENOMEM; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci /* Add sysfs attributes of the Synapses */ 99362306a36Sopenharmony_ci err = counter_sysfs_synapses_add(counter, groups + i, count); 99462306a36Sopenharmony_ci if (err < 0) 99562306a36Sopenharmony_ci return err; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci /* Create all attributes associated with Count */ 99862306a36Sopenharmony_ci err = counter_count_attrs_create(counter, groups + i, count); 99962306a36Sopenharmony_ci if (err < 0) 100062306a36Sopenharmony_ci return err; 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci return 0; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic int counter_num_signals_read(struct counter_device *counter, u8 *val) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci *val = counter->num_signals; 100962306a36Sopenharmony_ci return 0; 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cistatic int counter_num_counts_read(struct counter_device *counter, u8 *val) 101362306a36Sopenharmony_ci{ 101462306a36Sopenharmony_ci *val = counter->num_counts; 101562306a36Sopenharmony_ci return 0; 101662306a36Sopenharmony_ci} 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_cistatic int counter_events_queue_size_read(struct counter_device *counter, 101962306a36Sopenharmony_ci u64 *val) 102062306a36Sopenharmony_ci{ 102162306a36Sopenharmony_ci *val = kfifo_size(&counter->events); 102262306a36Sopenharmony_ci return 0; 102362306a36Sopenharmony_ci} 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_cistatic int counter_events_queue_size_write(struct counter_device *counter, 102662306a36Sopenharmony_ci u64 val) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci DECLARE_KFIFO_PTR(events, struct counter_event); 102962306a36Sopenharmony_ci int err; 103062306a36Sopenharmony_ci unsigned long flags; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci /* Allocate new events queue */ 103362306a36Sopenharmony_ci err = kfifo_alloc(&events, val, GFP_KERNEL); 103462306a36Sopenharmony_ci if (err) 103562306a36Sopenharmony_ci return err; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci /* Swap in new events queue */ 103862306a36Sopenharmony_ci mutex_lock(&counter->events_out_lock); 103962306a36Sopenharmony_ci spin_lock_irqsave(&counter->events_in_lock, flags); 104062306a36Sopenharmony_ci kfifo_free(&counter->events); 104162306a36Sopenharmony_ci counter->events.kfifo = events.kfifo; 104262306a36Sopenharmony_ci spin_unlock_irqrestore(&counter->events_in_lock, flags); 104362306a36Sopenharmony_ci mutex_unlock(&counter->events_out_lock); 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci return 0; 104662306a36Sopenharmony_ci} 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_cistatic struct counter_comp counter_num_signals_comp = 104962306a36Sopenharmony_ci COUNTER_COMP_DEVICE_U8("num_signals", counter_num_signals_read, NULL); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_cistatic struct counter_comp counter_num_counts_comp = 105262306a36Sopenharmony_ci COUNTER_COMP_DEVICE_U8("num_counts", counter_num_counts_read, NULL); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic struct counter_comp counter_events_queue_size_comp = 105562306a36Sopenharmony_ci COUNTER_COMP_DEVICE_U64("events_queue_size", 105662306a36Sopenharmony_ci counter_events_queue_size_read, 105762306a36Sopenharmony_ci counter_events_queue_size_write); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic int counter_sysfs_attr_add(struct counter_device *const counter, 106062306a36Sopenharmony_ci struct counter_attribute_group *cattr_group) 106162306a36Sopenharmony_ci{ 106262306a36Sopenharmony_ci const enum counter_scope scope = COUNTER_SCOPE_DEVICE; 106362306a36Sopenharmony_ci struct device *const dev = &counter->dev; 106462306a36Sopenharmony_ci int err; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci /* Add Signals sysfs attributes */ 106762306a36Sopenharmony_ci err = counter_sysfs_signals_add(counter, cattr_group); 106862306a36Sopenharmony_ci if (err < 0) 106962306a36Sopenharmony_ci return err; 107062306a36Sopenharmony_ci cattr_group += counter->num_signals; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci /* Add Counts sysfs attributes */ 107362306a36Sopenharmony_ci err = counter_sysfs_counts_add(counter, cattr_group); 107462306a36Sopenharmony_ci if (err < 0) 107562306a36Sopenharmony_ci return err; 107662306a36Sopenharmony_ci cattr_group += counter->num_counts; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci /* Create name attribute */ 107962306a36Sopenharmony_ci err = counter_name_attr_create(dev, cattr_group, counter->name); 108062306a36Sopenharmony_ci if (err < 0) 108162306a36Sopenharmony_ci return err; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci /* Create num_signals attribute */ 108462306a36Sopenharmony_ci err = counter_attr_create(dev, cattr_group, &counter_num_signals_comp, 108562306a36Sopenharmony_ci scope, NULL); 108662306a36Sopenharmony_ci if (err < 0) 108762306a36Sopenharmony_ci return err; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* Create num_counts attribute */ 109062306a36Sopenharmony_ci err = counter_attr_create(dev, cattr_group, &counter_num_counts_comp, 109162306a36Sopenharmony_ci scope, NULL); 109262306a36Sopenharmony_ci if (err < 0) 109362306a36Sopenharmony_ci return err; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci /* Create events_queue_size attribute */ 109662306a36Sopenharmony_ci err = counter_attr_create(dev, cattr_group, 109762306a36Sopenharmony_ci &counter_events_queue_size_comp, scope, NULL); 109862306a36Sopenharmony_ci if (err < 0) 109962306a36Sopenharmony_ci return err; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci /* Add device extensions */ 110262306a36Sopenharmony_ci return counter_sysfs_exts_add(dev, cattr_group, counter->ext, 110362306a36Sopenharmony_ci counter->num_ext, scope, NULL); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci return 0; 110662306a36Sopenharmony_ci} 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci/** 110962306a36Sopenharmony_ci * counter_sysfs_add - Adds Counter sysfs attributes to the device structure 111062306a36Sopenharmony_ci * @counter: Pointer to the Counter device structure 111162306a36Sopenharmony_ci * 111262306a36Sopenharmony_ci * Counter sysfs attributes are created and added to the respective device 111362306a36Sopenharmony_ci * structure for later registration to the system. Resource-managed memory 111462306a36Sopenharmony_ci * allocation is performed by this function, and this memory should be freed 111562306a36Sopenharmony_ci * when no longer needed (automatically by a device_unregister call, or 111662306a36Sopenharmony_ci * manually by a devres_release_all call). 111762306a36Sopenharmony_ci */ 111862306a36Sopenharmony_ciint counter_sysfs_add(struct counter_device *const counter) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci struct device *const dev = &counter->dev; 112162306a36Sopenharmony_ci const size_t num_groups = counter->num_signals + counter->num_counts + 1; 112262306a36Sopenharmony_ci struct counter_attribute_group *cattr_groups; 112362306a36Sopenharmony_ci size_t i, j; 112462306a36Sopenharmony_ci int err; 112562306a36Sopenharmony_ci struct attribute_group *groups; 112662306a36Sopenharmony_ci struct counter_attribute *p; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci /* Allocate space for attribute groups (signals, counts, and ext) */ 112962306a36Sopenharmony_ci cattr_groups = devm_kcalloc(dev, num_groups, sizeof(*cattr_groups), 113062306a36Sopenharmony_ci GFP_KERNEL); 113162306a36Sopenharmony_ci if (!cattr_groups) 113262306a36Sopenharmony_ci return -ENOMEM; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* Initialize attribute lists */ 113562306a36Sopenharmony_ci for (i = 0; i < num_groups; i++) 113662306a36Sopenharmony_ci INIT_LIST_HEAD(&cattr_groups[i].attr_list); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci /* Add Counter device sysfs attributes */ 113962306a36Sopenharmony_ci err = counter_sysfs_attr_add(counter, cattr_groups); 114062306a36Sopenharmony_ci if (err < 0) 114162306a36Sopenharmony_ci return err; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci /* Allocate attribute group pointers for association with device */ 114462306a36Sopenharmony_ci dev->groups = devm_kcalloc(dev, num_groups + 1, sizeof(*dev->groups), 114562306a36Sopenharmony_ci GFP_KERNEL); 114662306a36Sopenharmony_ci if (!dev->groups) 114762306a36Sopenharmony_ci return -ENOMEM; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci /* Allocate space for attribute groups */ 115062306a36Sopenharmony_ci groups = devm_kcalloc(dev, num_groups, sizeof(*groups), GFP_KERNEL); 115162306a36Sopenharmony_ci if (!groups) 115262306a36Sopenharmony_ci return -ENOMEM; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci /* Prepare each group of attributes for association */ 115562306a36Sopenharmony_ci for (i = 0; i < num_groups; i++) { 115662306a36Sopenharmony_ci groups[i].name = cattr_groups[i].name; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci /* Allocate space for attribute pointers */ 115962306a36Sopenharmony_ci groups[i].attrs = devm_kcalloc(dev, 116062306a36Sopenharmony_ci cattr_groups[i].num_attr + 1, 116162306a36Sopenharmony_ci sizeof(*groups[i].attrs), 116262306a36Sopenharmony_ci GFP_KERNEL); 116362306a36Sopenharmony_ci if (!groups[i].attrs) 116462306a36Sopenharmony_ci return -ENOMEM; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci /* Add attribute pointers to attribute group */ 116762306a36Sopenharmony_ci j = 0; 116862306a36Sopenharmony_ci list_for_each_entry(p, &cattr_groups[i].attr_list, l) 116962306a36Sopenharmony_ci groups[i].attrs[j++] = &p->dev_attr.attr; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci /* Associate attribute group */ 117262306a36Sopenharmony_ci dev->groups[i] = &groups[i]; 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci return 0; 117662306a36Sopenharmony_ci} 1177