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