18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Generic Counter interface 48c2ecf20Sopenharmony_ci * Copyright (C) 2018 William Breathitt Gray 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/counter.h> 78c2ecf20Sopenharmony_ci#include <linux/device.h> 88c2ecf20Sopenharmony_ci#include <linux/err.h> 98c2ecf20Sopenharmony_ci#include <linux/export.h> 108c2ecf20Sopenharmony_ci#include <linux/fs.h> 118c2ecf20Sopenharmony_ci#include <linux/gfp.h> 128c2ecf20Sopenharmony_ci#include <linux/idr.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/list.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/printk.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/string.h> 208c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 218c2ecf20Sopenharmony_ci#include <linux/types.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ciconst char *const counter_count_direction_str[2] = { 248c2ecf20Sopenharmony_ci [COUNTER_COUNT_DIRECTION_FORWARD] = "forward", 258c2ecf20Sopenharmony_ci [COUNTER_COUNT_DIRECTION_BACKWARD] = "backward" 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(counter_count_direction_str); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciconst char *const counter_count_mode_str[4] = { 308c2ecf20Sopenharmony_ci [COUNTER_COUNT_MODE_NORMAL] = "normal", 318c2ecf20Sopenharmony_ci [COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit", 328c2ecf20Sopenharmony_ci [COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle", 338c2ecf20Sopenharmony_ci [COUNTER_COUNT_MODE_MODULO_N] = "modulo-n" 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(counter_count_mode_str); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cissize_t counter_signal_enum_read(struct counter_device *counter, 388c2ecf20Sopenharmony_ci struct counter_signal *signal, void *priv, 398c2ecf20Sopenharmony_ci char *buf) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci const struct counter_signal_enum_ext *const e = priv; 428c2ecf20Sopenharmony_ci int err; 438c2ecf20Sopenharmony_ci size_t index; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (!e->get) 468c2ecf20Sopenharmony_ci return -EINVAL; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci err = e->get(counter, signal, &index); 498c2ecf20Sopenharmony_ci if (err) 508c2ecf20Sopenharmony_ci return err; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (index >= e->num_items) 538c2ecf20Sopenharmony_ci return -EINVAL; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", e->items[index]); 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(counter_signal_enum_read); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cissize_t counter_signal_enum_write(struct counter_device *counter, 608c2ecf20Sopenharmony_ci struct counter_signal *signal, void *priv, 618c2ecf20Sopenharmony_ci const char *buf, size_t len) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci const struct counter_signal_enum_ext *const e = priv; 648c2ecf20Sopenharmony_ci ssize_t index; 658c2ecf20Sopenharmony_ci int err; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (!e->set) 688c2ecf20Sopenharmony_ci return -EINVAL; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci index = __sysfs_match_string(e->items, e->num_items, buf); 718c2ecf20Sopenharmony_ci if (index < 0) 728c2ecf20Sopenharmony_ci return index; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci err = e->set(counter, signal, index); 758c2ecf20Sopenharmony_ci if (err) 768c2ecf20Sopenharmony_ci return err; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return len; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(counter_signal_enum_write); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cissize_t counter_signal_enum_available_read(struct counter_device *counter, 838c2ecf20Sopenharmony_ci struct counter_signal *signal, 848c2ecf20Sopenharmony_ci void *priv, char *buf) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci const struct counter_signal_enum_ext *const e = priv; 878c2ecf20Sopenharmony_ci size_t i; 888c2ecf20Sopenharmony_ci size_t len = 0; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (!e->num_items) 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci for (i = 0; i < e->num_items; i++) 948c2ecf20Sopenharmony_ci len += sprintf(buf + len, "%s\n", e->items[i]); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return len; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(counter_signal_enum_available_read); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cissize_t counter_count_enum_read(struct counter_device *counter, 1018c2ecf20Sopenharmony_ci struct counter_count *count, void *priv, 1028c2ecf20Sopenharmony_ci char *buf) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci const struct counter_count_enum_ext *const e = priv; 1058c2ecf20Sopenharmony_ci int err; 1068c2ecf20Sopenharmony_ci size_t index; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (!e->get) 1098c2ecf20Sopenharmony_ci return -EINVAL; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci err = e->get(counter, count, &index); 1128c2ecf20Sopenharmony_ci if (err) 1138c2ecf20Sopenharmony_ci return err; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (index >= e->num_items) 1168c2ecf20Sopenharmony_ci return -EINVAL; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", e->items[index]); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(counter_count_enum_read); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cissize_t counter_count_enum_write(struct counter_device *counter, 1238c2ecf20Sopenharmony_ci struct counter_count *count, void *priv, 1248c2ecf20Sopenharmony_ci const char *buf, size_t len) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci const struct counter_count_enum_ext *const e = priv; 1278c2ecf20Sopenharmony_ci ssize_t index; 1288c2ecf20Sopenharmony_ci int err; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (!e->set) 1318c2ecf20Sopenharmony_ci return -EINVAL; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci index = __sysfs_match_string(e->items, e->num_items, buf); 1348c2ecf20Sopenharmony_ci if (index < 0) 1358c2ecf20Sopenharmony_ci return index; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci err = e->set(counter, count, index); 1388c2ecf20Sopenharmony_ci if (err) 1398c2ecf20Sopenharmony_ci return err; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return len; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(counter_count_enum_write); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cissize_t counter_count_enum_available_read(struct counter_device *counter, 1468c2ecf20Sopenharmony_ci struct counter_count *count, 1478c2ecf20Sopenharmony_ci void *priv, char *buf) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci const struct counter_count_enum_ext *const e = priv; 1508c2ecf20Sopenharmony_ci size_t i; 1518c2ecf20Sopenharmony_ci size_t len = 0; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (!e->num_items) 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci for (i = 0; i < e->num_items; i++) 1578c2ecf20Sopenharmony_ci len += sprintf(buf + len, "%s\n", e->items[i]); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return len; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(counter_count_enum_available_read); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cissize_t counter_device_enum_read(struct counter_device *counter, void *priv, 1648c2ecf20Sopenharmony_ci char *buf) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci const struct counter_device_enum_ext *const e = priv; 1678c2ecf20Sopenharmony_ci int err; 1688c2ecf20Sopenharmony_ci size_t index; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (!e->get) 1718c2ecf20Sopenharmony_ci return -EINVAL; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci err = e->get(counter, &index); 1748c2ecf20Sopenharmony_ci if (err) 1758c2ecf20Sopenharmony_ci return err; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (index >= e->num_items) 1788c2ecf20Sopenharmony_ci return -EINVAL; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", e->items[index]); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(counter_device_enum_read); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cissize_t counter_device_enum_write(struct counter_device *counter, void *priv, 1858c2ecf20Sopenharmony_ci const char *buf, size_t len) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci const struct counter_device_enum_ext *const e = priv; 1888c2ecf20Sopenharmony_ci ssize_t index; 1898c2ecf20Sopenharmony_ci int err; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (!e->set) 1928c2ecf20Sopenharmony_ci return -EINVAL; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci index = __sysfs_match_string(e->items, e->num_items, buf); 1958c2ecf20Sopenharmony_ci if (index < 0) 1968c2ecf20Sopenharmony_ci return index; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci err = e->set(counter, index); 1998c2ecf20Sopenharmony_ci if (err) 2008c2ecf20Sopenharmony_ci return err; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return len; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(counter_device_enum_write); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cissize_t counter_device_enum_available_read(struct counter_device *counter, 2078c2ecf20Sopenharmony_ci void *priv, char *buf) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci const struct counter_device_enum_ext *const e = priv; 2108c2ecf20Sopenharmony_ci size_t i; 2118c2ecf20Sopenharmony_ci size_t len = 0; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (!e->num_items) 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci for (i = 0; i < e->num_items; i++) 2178c2ecf20Sopenharmony_ci len += sprintf(buf + len, "%s\n", e->items[i]); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return len; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(counter_device_enum_available_read); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistruct counter_attr_parm { 2248c2ecf20Sopenharmony_ci struct counter_device_attr_group *group; 2258c2ecf20Sopenharmony_ci const char *prefix; 2268c2ecf20Sopenharmony_ci const char *name; 2278c2ecf20Sopenharmony_ci ssize_t (*show)(struct device *dev, struct device_attribute *attr, 2288c2ecf20Sopenharmony_ci char *buf); 2298c2ecf20Sopenharmony_ci ssize_t (*store)(struct device *dev, struct device_attribute *attr, 2308c2ecf20Sopenharmony_ci const char *buf, size_t len); 2318c2ecf20Sopenharmony_ci void *component; 2328c2ecf20Sopenharmony_ci}; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistruct counter_device_attr { 2358c2ecf20Sopenharmony_ci struct device_attribute dev_attr; 2368c2ecf20Sopenharmony_ci struct list_head l; 2378c2ecf20Sopenharmony_ci void *component; 2388c2ecf20Sopenharmony_ci}; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic int counter_attribute_create(const struct counter_attr_parm *const parm) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct counter_device_attr *counter_attr; 2438c2ecf20Sopenharmony_ci struct device_attribute *dev_attr; 2448c2ecf20Sopenharmony_ci int err; 2458c2ecf20Sopenharmony_ci struct list_head *const attr_list = &parm->group->attr_list; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Allocate a Counter device attribute */ 2488c2ecf20Sopenharmony_ci counter_attr = kzalloc(sizeof(*counter_attr), GFP_KERNEL); 2498c2ecf20Sopenharmony_ci if (!counter_attr) 2508c2ecf20Sopenharmony_ci return -ENOMEM; 2518c2ecf20Sopenharmony_ci dev_attr = &counter_attr->dev_attr; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci sysfs_attr_init(&dev_attr->attr); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* Configure device attribute */ 2568c2ecf20Sopenharmony_ci dev_attr->attr.name = kasprintf(GFP_KERNEL, "%s%s", parm->prefix, 2578c2ecf20Sopenharmony_ci parm->name); 2588c2ecf20Sopenharmony_ci if (!dev_attr->attr.name) { 2598c2ecf20Sopenharmony_ci err = -ENOMEM; 2608c2ecf20Sopenharmony_ci goto err_free_counter_attr; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci if (parm->show) { 2638c2ecf20Sopenharmony_ci dev_attr->attr.mode |= 0444; 2648c2ecf20Sopenharmony_ci dev_attr->show = parm->show; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci if (parm->store) { 2678c2ecf20Sopenharmony_ci dev_attr->attr.mode |= 0200; 2688c2ecf20Sopenharmony_ci dev_attr->store = parm->store; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* Store associated Counter component with attribute */ 2728c2ecf20Sopenharmony_ci counter_attr->component = parm->component; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* Keep track of the attribute for later cleanup */ 2758c2ecf20Sopenharmony_ci list_add(&counter_attr->l, attr_list); 2768c2ecf20Sopenharmony_ci parm->group->num_attr++; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return 0; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cierr_free_counter_attr: 2818c2ecf20Sopenharmony_ci kfree(counter_attr); 2828c2ecf20Sopenharmony_ci return err; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci#define to_counter_attr(_dev_attr) \ 2868c2ecf20Sopenharmony_ci container_of(_dev_attr, struct counter_device_attr, dev_attr) 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistruct counter_signal_unit { 2898c2ecf20Sopenharmony_ci struct counter_signal *signal; 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic const char *const counter_signal_value_str[] = { 2938c2ecf20Sopenharmony_ci [COUNTER_SIGNAL_LOW] = "low", 2948c2ecf20Sopenharmony_ci [COUNTER_SIGNAL_HIGH] = "high" 2958c2ecf20Sopenharmony_ci}; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic ssize_t counter_signal_show(struct device *dev, 2988c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct counter_device *const counter = dev_get_drvdata(dev); 3018c2ecf20Sopenharmony_ci const struct counter_device_attr *const devattr = to_counter_attr(attr); 3028c2ecf20Sopenharmony_ci const struct counter_signal_unit *const component = devattr->component; 3038c2ecf20Sopenharmony_ci struct counter_signal *const signal = component->signal; 3048c2ecf20Sopenharmony_ci int err; 3058c2ecf20Sopenharmony_ci enum counter_signal_value val; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci err = counter->ops->signal_read(counter, signal, &val); 3088c2ecf20Sopenharmony_ci if (err) 3098c2ecf20Sopenharmony_ci return err; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", counter_signal_value_str[val]); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistruct counter_name_unit { 3158c2ecf20Sopenharmony_ci const char *name; 3168c2ecf20Sopenharmony_ci}; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic ssize_t counter_device_attr_name_show(struct device *dev, 3198c2ecf20Sopenharmony_ci struct device_attribute *attr, 3208c2ecf20Sopenharmony_ci char *buf) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci const struct counter_name_unit *const comp = to_counter_attr(attr)->component; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", comp->name); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic int counter_name_attribute_create( 3288c2ecf20Sopenharmony_ci struct counter_device_attr_group *const group, 3298c2ecf20Sopenharmony_ci const char *const name) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct counter_name_unit *name_comp; 3328c2ecf20Sopenharmony_ci struct counter_attr_parm parm; 3338c2ecf20Sopenharmony_ci int err; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* Skip if no name */ 3368c2ecf20Sopenharmony_ci if (!name) 3378c2ecf20Sopenharmony_ci return 0; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* Allocate name attribute component */ 3408c2ecf20Sopenharmony_ci name_comp = kmalloc(sizeof(*name_comp), GFP_KERNEL); 3418c2ecf20Sopenharmony_ci if (!name_comp) 3428c2ecf20Sopenharmony_ci return -ENOMEM; 3438c2ecf20Sopenharmony_ci name_comp->name = name; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* Allocate Signal name attribute */ 3468c2ecf20Sopenharmony_ci parm.group = group; 3478c2ecf20Sopenharmony_ci parm.prefix = ""; 3488c2ecf20Sopenharmony_ci parm.name = "name"; 3498c2ecf20Sopenharmony_ci parm.show = counter_device_attr_name_show; 3508c2ecf20Sopenharmony_ci parm.store = NULL; 3518c2ecf20Sopenharmony_ci parm.component = name_comp; 3528c2ecf20Sopenharmony_ci err = counter_attribute_create(&parm); 3538c2ecf20Sopenharmony_ci if (err) 3548c2ecf20Sopenharmony_ci goto err_free_name_comp; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cierr_free_name_comp: 3598c2ecf20Sopenharmony_ci kfree(name_comp); 3608c2ecf20Sopenharmony_ci return err; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistruct counter_signal_ext_unit { 3648c2ecf20Sopenharmony_ci struct counter_signal *signal; 3658c2ecf20Sopenharmony_ci const struct counter_signal_ext *ext; 3668c2ecf20Sopenharmony_ci}; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic ssize_t counter_signal_ext_show(struct device *dev, 3698c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci const struct counter_device_attr *const devattr = to_counter_attr(attr); 3728c2ecf20Sopenharmony_ci const struct counter_signal_ext_unit *const comp = devattr->component; 3738c2ecf20Sopenharmony_ci const struct counter_signal_ext *const ext = comp->ext; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci return ext->read(dev_get_drvdata(dev), comp->signal, ext->priv, buf); 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic ssize_t counter_signal_ext_store(struct device *dev, 3798c2ecf20Sopenharmony_ci struct device_attribute *attr, 3808c2ecf20Sopenharmony_ci const char *buf, size_t len) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci const struct counter_device_attr *const devattr = to_counter_attr(attr); 3838c2ecf20Sopenharmony_ci const struct counter_signal_ext_unit *const comp = devattr->component; 3848c2ecf20Sopenharmony_ci const struct counter_signal_ext *const ext = comp->ext; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return ext->write(dev_get_drvdata(dev), comp->signal, ext->priv, buf, 3878c2ecf20Sopenharmony_ci len); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic void counter_device_attr_list_free(struct list_head *attr_list) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct counter_device_attr *p, *n; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci list_for_each_entry_safe(p, n, attr_list, l) { 3958c2ecf20Sopenharmony_ci /* free attribute name and associated component memory */ 3968c2ecf20Sopenharmony_ci kfree(p->dev_attr.attr.name); 3978c2ecf20Sopenharmony_ci kfree(p->component); 3988c2ecf20Sopenharmony_ci list_del(&p->l); 3998c2ecf20Sopenharmony_ci kfree(p); 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic int counter_signal_ext_register( 4048c2ecf20Sopenharmony_ci struct counter_device_attr_group *const group, 4058c2ecf20Sopenharmony_ci struct counter_signal *const signal) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci const size_t num_ext = signal->num_ext; 4088c2ecf20Sopenharmony_ci size_t i; 4098c2ecf20Sopenharmony_ci const struct counter_signal_ext *ext; 4108c2ecf20Sopenharmony_ci struct counter_signal_ext_unit *signal_ext_comp; 4118c2ecf20Sopenharmony_ci struct counter_attr_parm parm; 4128c2ecf20Sopenharmony_ci int err; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* Create an attribute for each extension */ 4158c2ecf20Sopenharmony_ci for (i = 0 ; i < num_ext; i++) { 4168c2ecf20Sopenharmony_ci ext = signal->ext + i; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* Allocate signal_ext attribute component */ 4198c2ecf20Sopenharmony_ci signal_ext_comp = kmalloc(sizeof(*signal_ext_comp), GFP_KERNEL); 4208c2ecf20Sopenharmony_ci if (!signal_ext_comp) { 4218c2ecf20Sopenharmony_ci err = -ENOMEM; 4228c2ecf20Sopenharmony_ci goto err_free_attr_list; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci signal_ext_comp->signal = signal; 4258c2ecf20Sopenharmony_ci signal_ext_comp->ext = ext; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* Allocate a Counter device attribute */ 4288c2ecf20Sopenharmony_ci parm.group = group; 4298c2ecf20Sopenharmony_ci parm.prefix = ""; 4308c2ecf20Sopenharmony_ci parm.name = ext->name; 4318c2ecf20Sopenharmony_ci parm.show = (ext->read) ? counter_signal_ext_show : NULL; 4328c2ecf20Sopenharmony_ci parm.store = (ext->write) ? counter_signal_ext_store : NULL; 4338c2ecf20Sopenharmony_ci parm.component = signal_ext_comp; 4348c2ecf20Sopenharmony_ci err = counter_attribute_create(&parm); 4358c2ecf20Sopenharmony_ci if (err) { 4368c2ecf20Sopenharmony_ci kfree(signal_ext_comp); 4378c2ecf20Sopenharmony_ci goto err_free_attr_list; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cierr_free_attr_list: 4448c2ecf20Sopenharmony_ci counter_device_attr_list_free(&group->attr_list); 4458c2ecf20Sopenharmony_ci return err; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic int counter_signal_attributes_create( 4498c2ecf20Sopenharmony_ci struct counter_device_attr_group *const group, 4508c2ecf20Sopenharmony_ci const struct counter_device *const counter, 4518c2ecf20Sopenharmony_ci struct counter_signal *const signal) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct counter_signal_unit *signal_comp; 4548c2ecf20Sopenharmony_ci struct counter_attr_parm parm; 4558c2ecf20Sopenharmony_ci int err; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* Allocate Signal attribute component */ 4588c2ecf20Sopenharmony_ci signal_comp = kmalloc(sizeof(*signal_comp), GFP_KERNEL); 4598c2ecf20Sopenharmony_ci if (!signal_comp) 4608c2ecf20Sopenharmony_ci return -ENOMEM; 4618c2ecf20Sopenharmony_ci signal_comp->signal = signal; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* Create main Signal attribute */ 4648c2ecf20Sopenharmony_ci parm.group = group; 4658c2ecf20Sopenharmony_ci parm.prefix = ""; 4668c2ecf20Sopenharmony_ci parm.name = "signal"; 4678c2ecf20Sopenharmony_ci parm.show = (counter->ops->signal_read) ? counter_signal_show : NULL; 4688c2ecf20Sopenharmony_ci parm.store = NULL; 4698c2ecf20Sopenharmony_ci parm.component = signal_comp; 4708c2ecf20Sopenharmony_ci err = counter_attribute_create(&parm); 4718c2ecf20Sopenharmony_ci if (err) { 4728c2ecf20Sopenharmony_ci kfree(signal_comp); 4738c2ecf20Sopenharmony_ci return err; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* Create Signal name attribute */ 4778c2ecf20Sopenharmony_ci err = counter_name_attribute_create(group, signal->name); 4788c2ecf20Sopenharmony_ci if (err) 4798c2ecf20Sopenharmony_ci goto err_free_attr_list; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* Register Signal extension attributes */ 4828c2ecf20Sopenharmony_ci err = counter_signal_ext_register(group, signal); 4838c2ecf20Sopenharmony_ci if (err) 4848c2ecf20Sopenharmony_ci goto err_free_attr_list; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cierr_free_attr_list: 4898c2ecf20Sopenharmony_ci counter_device_attr_list_free(&group->attr_list); 4908c2ecf20Sopenharmony_ci return err; 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic int counter_signals_register( 4948c2ecf20Sopenharmony_ci struct counter_device_attr_group *const groups_list, 4958c2ecf20Sopenharmony_ci const struct counter_device *const counter) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci const size_t num_signals = counter->num_signals; 4988c2ecf20Sopenharmony_ci size_t i; 4998c2ecf20Sopenharmony_ci struct counter_signal *signal; 5008c2ecf20Sopenharmony_ci const char *name; 5018c2ecf20Sopenharmony_ci int err; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci /* Register each Signal */ 5048c2ecf20Sopenharmony_ci for (i = 0; i < num_signals; i++) { 5058c2ecf20Sopenharmony_ci signal = counter->signals + i; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci /* Generate Signal attribute directory name */ 5088c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "signal%d", signal->id); 5098c2ecf20Sopenharmony_ci if (!name) { 5108c2ecf20Sopenharmony_ci err = -ENOMEM; 5118c2ecf20Sopenharmony_ci goto err_free_attr_groups; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci groups_list[i].attr_group.name = name; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* Create all attributes associated with Signal */ 5168c2ecf20Sopenharmony_ci err = counter_signal_attributes_create(groups_list + i, counter, 5178c2ecf20Sopenharmony_ci signal); 5188c2ecf20Sopenharmony_ci if (err) 5198c2ecf20Sopenharmony_ci goto err_free_attr_groups; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci return 0; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cierr_free_attr_groups: 5258c2ecf20Sopenharmony_ci do { 5268c2ecf20Sopenharmony_ci kfree(groups_list[i].attr_group.name); 5278c2ecf20Sopenharmony_ci counter_device_attr_list_free(&groups_list[i].attr_list); 5288c2ecf20Sopenharmony_ci } while (i--); 5298c2ecf20Sopenharmony_ci return err; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic const char *const counter_synapse_action_str[] = { 5338c2ecf20Sopenharmony_ci [COUNTER_SYNAPSE_ACTION_NONE] = "none", 5348c2ecf20Sopenharmony_ci [COUNTER_SYNAPSE_ACTION_RISING_EDGE] = "rising edge", 5358c2ecf20Sopenharmony_ci [COUNTER_SYNAPSE_ACTION_FALLING_EDGE] = "falling edge", 5368c2ecf20Sopenharmony_ci [COUNTER_SYNAPSE_ACTION_BOTH_EDGES] = "both edges" 5378c2ecf20Sopenharmony_ci}; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistruct counter_action_unit { 5408c2ecf20Sopenharmony_ci struct counter_synapse *synapse; 5418c2ecf20Sopenharmony_ci struct counter_count *count; 5428c2ecf20Sopenharmony_ci}; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic ssize_t counter_action_show(struct device *dev, 5458c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci const struct counter_device_attr *const devattr = to_counter_attr(attr); 5488c2ecf20Sopenharmony_ci int err; 5498c2ecf20Sopenharmony_ci struct counter_device *const counter = dev_get_drvdata(dev); 5508c2ecf20Sopenharmony_ci const struct counter_action_unit *const component = devattr->component; 5518c2ecf20Sopenharmony_ci struct counter_count *const count = component->count; 5528c2ecf20Sopenharmony_ci struct counter_synapse *const synapse = component->synapse; 5538c2ecf20Sopenharmony_ci size_t action_index; 5548c2ecf20Sopenharmony_ci enum counter_synapse_action action; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci err = counter->ops->action_get(counter, count, synapse, &action_index); 5578c2ecf20Sopenharmony_ci if (err) 5588c2ecf20Sopenharmony_ci return err; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci synapse->action = action_index; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci action = synapse->actions_list[action_index]; 5638c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", counter_synapse_action_str[action]); 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic ssize_t counter_action_store(struct device *dev, 5678c2ecf20Sopenharmony_ci struct device_attribute *attr, 5688c2ecf20Sopenharmony_ci const char *buf, size_t len) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci const struct counter_device_attr *const devattr = to_counter_attr(attr); 5718c2ecf20Sopenharmony_ci const struct counter_action_unit *const component = devattr->component; 5728c2ecf20Sopenharmony_ci struct counter_synapse *const synapse = component->synapse; 5738c2ecf20Sopenharmony_ci size_t action_index; 5748c2ecf20Sopenharmony_ci const size_t num_actions = synapse->num_actions; 5758c2ecf20Sopenharmony_ci enum counter_synapse_action action; 5768c2ecf20Sopenharmony_ci int err; 5778c2ecf20Sopenharmony_ci struct counter_device *const counter = dev_get_drvdata(dev); 5788c2ecf20Sopenharmony_ci struct counter_count *const count = component->count; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* Find requested action mode */ 5818c2ecf20Sopenharmony_ci for (action_index = 0; action_index < num_actions; action_index++) { 5828c2ecf20Sopenharmony_ci action = synapse->actions_list[action_index]; 5838c2ecf20Sopenharmony_ci if (sysfs_streq(buf, counter_synapse_action_str[action])) 5848c2ecf20Sopenharmony_ci break; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci /* If requested action mode not found */ 5878c2ecf20Sopenharmony_ci if (action_index >= num_actions) 5888c2ecf20Sopenharmony_ci return -EINVAL; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci err = counter->ops->action_set(counter, count, synapse, action_index); 5918c2ecf20Sopenharmony_ci if (err) 5928c2ecf20Sopenharmony_ci return err; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci synapse->action = action_index; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci return len; 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistruct counter_action_avail_unit { 6008c2ecf20Sopenharmony_ci const enum counter_synapse_action *actions_list; 6018c2ecf20Sopenharmony_ci size_t num_actions; 6028c2ecf20Sopenharmony_ci}; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_cistatic ssize_t counter_synapse_action_available_show(struct device *dev, 6058c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci const struct counter_device_attr *const devattr = to_counter_attr(attr); 6088c2ecf20Sopenharmony_ci const struct counter_action_avail_unit *const component = devattr->component; 6098c2ecf20Sopenharmony_ci size_t i; 6108c2ecf20Sopenharmony_ci enum counter_synapse_action action; 6118c2ecf20Sopenharmony_ci ssize_t len = 0; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci for (i = 0; i < component->num_actions; i++) { 6148c2ecf20Sopenharmony_ci action = component->actions_list[i]; 6158c2ecf20Sopenharmony_ci len += sprintf(buf + len, "%s\n", 6168c2ecf20Sopenharmony_ci counter_synapse_action_str[action]); 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci return len; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic int counter_synapses_register( 6238c2ecf20Sopenharmony_ci struct counter_device_attr_group *const group, 6248c2ecf20Sopenharmony_ci const struct counter_device *const counter, 6258c2ecf20Sopenharmony_ci struct counter_count *const count, const char *const count_attr_name) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci size_t i; 6288c2ecf20Sopenharmony_ci struct counter_synapse *synapse; 6298c2ecf20Sopenharmony_ci const char *prefix; 6308c2ecf20Sopenharmony_ci struct counter_action_unit *action_comp; 6318c2ecf20Sopenharmony_ci struct counter_attr_parm parm; 6328c2ecf20Sopenharmony_ci int err; 6338c2ecf20Sopenharmony_ci struct counter_action_avail_unit *avail_comp; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* Register each Synapse */ 6368c2ecf20Sopenharmony_ci for (i = 0; i < count->num_synapses; i++) { 6378c2ecf20Sopenharmony_ci synapse = count->synapses + i; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci /* Generate attribute prefix */ 6408c2ecf20Sopenharmony_ci prefix = kasprintf(GFP_KERNEL, "signal%d_", 6418c2ecf20Sopenharmony_ci synapse->signal->id); 6428c2ecf20Sopenharmony_ci if (!prefix) { 6438c2ecf20Sopenharmony_ci err = -ENOMEM; 6448c2ecf20Sopenharmony_ci goto err_free_attr_list; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* Allocate action attribute component */ 6488c2ecf20Sopenharmony_ci action_comp = kmalloc(sizeof(*action_comp), GFP_KERNEL); 6498c2ecf20Sopenharmony_ci if (!action_comp) { 6508c2ecf20Sopenharmony_ci err = -ENOMEM; 6518c2ecf20Sopenharmony_ci goto err_free_prefix; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci action_comp->synapse = synapse; 6548c2ecf20Sopenharmony_ci action_comp->count = count; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* Create action attribute */ 6578c2ecf20Sopenharmony_ci parm.group = group; 6588c2ecf20Sopenharmony_ci parm.prefix = prefix; 6598c2ecf20Sopenharmony_ci parm.name = "action"; 6608c2ecf20Sopenharmony_ci parm.show = (counter->ops->action_get) ? counter_action_show : NULL; 6618c2ecf20Sopenharmony_ci parm.store = (counter->ops->action_set) ? counter_action_store : NULL; 6628c2ecf20Sopenharmony_ci parm.component = action_comp; 6638c2ecf20Sopenharmony_ci err = counter_attribute_create(&parm); 6648c2ecf20Sopenharmony_ci if (err) { 6658c2ecf20Sopenharmony_ci kfree(action_comp); 6668c2ecf20Sopenharmony_ci goto err_free_prefix; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci /* Allocate action available attribute component */ 6708c2ecf20Sopenharmony_ci avail_comp = kmalloc(sizeof(*avail_comp), GFP_KERNEL); 6718c2ecf20Sopenharmony_ci if (!avail_comp) { 6728c2ecf20Sopenharmony_ci err = -ENOMEM; 6738c2ecf20Sopenharmony_ci goto err_free_prefix; 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci avail_comp->actions_list = synapse->actions_list; 6768c2ecf20Sopenharmony_ci avail_comp->num_actions = synapse->num_actions; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* Create action_available attribute */ 6798c2ecf20Sopenharmony_ci parm.group = group; 6808c2ecf20Sopenharmony_ci parm.prefix = prefix; 6818c2ecf20Sopenharmony_ci parm.name = "action_available"; 6828c2ecf20Sopenharmony_ci parm.show = counter_synapse_action_available_show; 6838c2ecf20Sopenharmony_ci parm.store = NULL; 6848c2ecf20Sopenharmony_ci parm.component = avail_comp; 6858c2ecf20Sopenharmony_ci err = counter_attribute_create(&parm); 6868c2ecf20Sopenharmony_ci if (err) { 6878c2ecf20Sopenharmony_ci kfree(avail_comp); 6888c2ecf20Sopenharmony_ci goto err_free_prefix; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci kfree(prefix); 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci return 0; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cierr_free_prefix: 6978c2ecf20Sopenharmony_ci kfree(prefix); 6988c2ecf20Sopenharmony_cierr_free_attr_list: 6998c2ecf20Sopenharmony_ci counter_device_attr_list_free(&group->attr_list); 7008c2ecf20Sopenharmony_ci return err; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistruct counter_count_unit { 7048c2ecf20Sopenharmony_ci struct counter_count *count; 7058c2ecf20Sopenharmony_ci}; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic ssize_t counter_count_show(struct device *dev, 7088c2ecf20Sopenharmony_ci struct device_attribute *attr, 7098c2ecf20Sopenharmony_ci char *buf) 7108c2ecf20Sopenharmony_ci{ 7118c2ecf20Sopenharmony_ci struct counter_device *const counter = dev_get_drvdata(dev); 7128c2ecf20Sopenharmony_ci const struct counter_device_attr *const devattr = to_counter_attr(attr); 7138c2ecf20Sopenharmony_ci const struct counter_count_unit *const component = devattr->component; 7148c2ecf20Sopenharmony_ci struct counter_count *const count = component->count; 7158c2ecf20Sopenharmony_ci int err; 7168c2ecf20Sopenharmony_ci unsigned long val; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci err = counter->ops->count_read(counter, count, &val); 7198c2ecf20Sopenharmony_ci if (err) 7208c2ecf20Sopenharmony_ci return err; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci return sprintf(buf, "%lu\n", val); 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_cistatic ssize_t counter_count_store(struct device *dev, 7268c2ecf20Sopenharmony_ci struct device_attribute *attr, 7278c2ecf20Sopenharmony_ci const char *buf, size_t len) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci struct counter_device *const counter = dev_get_drvdata(dev); 7308c2ecf20Sopenharmony_ci const struct counter_device_attr *const devattr = to_counter_attr(attr); 7318c2ecf20Sopenharmony_ci const struct counter_count_unit *const component = devattr->component; 7328c2ecf20Sopenharmony_ci struct counter_count *const count = component->count; 7338c2ecf20Sopenharmony_ci int err; 7348c2ecf20Sopenharmony_ci unsigned long val; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci err = kstrtoul(buf, 0, &val); 7378c2ecf20Sopenharmony_ci if (err) 7388c2ecf20Sopenharmony_ci return err; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci err = counter->ops->count_write(counter, count, val); 7418c2ecf20Sopenharmony_ci if (err) 7428c2ecf20Sopenharmony_ci return err; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci return len; 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic const char *const counter_count_function_str[] = { 7488c2ecf20Sopenharmony_ci [COUNTER_COUNT_FUNCTION_INCREASE] = "increase", 7498c2ecf20Sopenharmony_ci [COUNTER_COUNT_FUNCTION_DECREASE] = "decrease", 7508c2ecf20Sopenharmony_ci [COUNTER_COUNT_FUNCTION_PULSE_DIRECTION] = "pulse-direction", 7518c2ecf20Sopenharmony_ci [COUNTER_COUNT_FUNCTION_QUADRATURE_X1_A] = "quadrature x1 a", 7528c2ecf20Sopenharmony_ci [COUNTER_COUNT_FUNCTION_QUADRATURE_X1_B] = "quadrature x1 b", 7538c2ecf20Sopenharmony_ci [COUNTER_COUNT_FUNCTION_QUADRATURE_X2_A] = "quadrature x2 a", 7548c2ecf20Sopenharmony_ci [COUNTER_COUNT_FUNCTION_QUADRATURE_X2_B] = "quadrature x2 b", 7558c2ecf20Sopenharmony_ci [COUNTER_COUNT_FUNCTION_QUADRATURE_X4] = "quadrature x4" 7568c2ecf20Sopenharmony_ci}; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic ssize_t counter_function_show(struct device *dev, 7598c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci int err; 7628c2ecf20Sopenharmony_ci struct counter_device *const counter = dev_get_drvdata(dev); 7638c2ecf20Sopenharmony_ci const struct counter_device_attr *const devattr = to_counter_attr(attr); 7648c2ecf20Sopenharmony_ci const struct counter_count_unit *const component = devattr->component; 7658c2ecf20Sopenharmony_ci struct counter_count *const count = component->count; 7668c2ecf20Sopenharmony_ci size_t func_index; 7678c2ecf20Sopenharmony_ci enum counter_count_function function; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci err = counter->ops->function_get(counter, count, &func_index); 7708c2ecf20Sopenharmony_ci if (err) 7718c2ecf20Sopenharmony_ci return err; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci count->function = func_index; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci function = count->functions_list[func_index]; 7768c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", counter_count_function_str[function]); 7778c2ecf20Sopenharmony_ci} 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic ssize_t counter_function_store(struct device *dev, 7808c2ecf20Sopenharmony_ci struct device_attribute *attr, 7818c2ecf20Sopenharmony_ci const char *buf, size_t len) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci const struct counter_device_attr *const devattr = to_counter_attr(attr); 7848c2ecf20Sopenharmony_ci const struct counter_count_unit *const component = devattr->component; 7858c2ecf20Sopenharmony_ci struct counter_count *const count = component->count; 7868c2ecf20Sopenharmony_ci const size_t num_functions = count->num_functions; 7878c2ecf20Sopenharmony_ci size_t func_index; 7888c2ecf20Sopenharmony_ci enum counter_count_function function; 7898c2ecf20Sopenharmony_ci int err; 7908c2ecf20Sopenharmony_ci struct counter_device *const counter = dev_get_drvdata(dev); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci /* Find requested Count function mode */ 7938c2ecf20Sopenharmony_ci for (func_index = 0; func_index < num_functions; func_index++) { 7948c2ecf20Sopenharmony_ci function = count->functions_list[func_index]; 7958c2ecf20Sopenharmony_ci if (sysfs_streq(buf, counter_count_function_str[function])) 7968c2ecf20Sopenharmony_ci break; 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci /* Return error if requested Count function mode not found */ 7998c2ecf20Sopenharmony_ci if (func_index >= num_functions) 8008c2ecf20Sopenharmony_ci return -EINVAL; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci err = counter->ops->function_set(counter, count, func_index); 8038c2ecf20Sopenharmony_ci if (err) 8048c2ecf20Sopenharmony_ci return err; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci count->function = func_index; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci return len; 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_cistruct counter_count_ext_unit { 8128c2ecf20Sopenharmony_ci struct counter_count *count; 8138c2ecf20Sopenharmony_ci const struct counter_count_ext *ext; 8148c2ecf20Sopenharmony_ci}; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_cistatic ssize_t counter_count_ext_show(struct device *dev, 8178c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci const struct counter_device_attr *const devattr = to_counter_attr(attr); 8208c2ecf20Sopenharmony_ci const struct counter_count_ext_unit *const comp = devattr->component; 8218c2ecf20Sopenharmony_ci const struct counter_count_ext *const ext = comp->ext; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci return ext->read(dev_get_drvdata(dev), comp->count, ext->priv, buf); 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic ssize_t counter_count_ext_store(struct device *dev, 8278c2ecf20Sopenharmony_ci struct device_attribute *attr, 8288c2ecf20Sopenharmony_ci const char *buf, size_t len) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci const struct counter_device_attr *const devattr = to_counter_attr(attr); 8318c2ecf20Sopenharmony_ci const struct counter_count_ext_unit *const comp = devattr->component; 8328c2ecf20Sopenharmony_ci const struct counter_count_ext *const ext = comp->ext; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci return ext->write(dev_get_drvdata(dev), comp->count, ext->priv, buf, 8358c2ecf20Sopenharmony_ci len); 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cistatic int counter_count_ext_register( 8398c2ecf20Sopenharmony_ci struct counter_device_attr_group *const group, 8408c2ecf20Sopenharmony_ci struct counter_count *const count) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci size_t i; 8438c2ecf20Sopenharmony_ci const struct counter_count_ext *ext; 8448c2ecf20Sopenharmony_ci struct counter_count_ext_unit *count_ext_comp; 8458c2ecf20Sopenharmony_ci struct counter_attr_parm parm; 8468c2ecf20Sopenharmony_ci int err; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* Create an attribute for each extension */ 8498c2ecf20Sopenharmony_ci for (i = 0 ; i < count->num_ext; i++) { 8508c2ecf20Sopenharmony_ci ext = count->ext + i; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci /* Allocate count_ext attribute component */ 8538c2ecf20Sopenharmony_ci count_ext_comp = kmalloc(sizeof(*count_ext_comp), GFP_KERNEL); 8548c2ecf20Sopenharmony_ci if (!count_ext_comp) { 8558c2ecf20Sopenharmony_ci err = -ENOMEM; 8568c2ecf20Sopenharmony_ci goto err_free_attr_list; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci count_ext_comp->count = count; 8598c2ecf20Sopenharmony_ci count_ext_comp->ext = ext; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci /* Allocate count_ext attribute */ 8628c2ecf20Sopenharmony_ci parm.group = group; 8638c2ecf20Sopenharmony_ci parm.prefix = ""; 8648c2ecf20Sopenharmony_ci parm.name = ext->name; 8658c2ecf20Sopenharmony_ci parm.show = (ext->read) ? counter_count_ext_show : NULL; 8668c2ecf20Sopenharmony_ci parm.store = (ext->write) ? counter_count_ext_store : NULL; 8678c2ecf20Sopenharmony_ci parm.component = count_ext_comp; 8688c2ecf20Sopenharmony_ci err = counter_attribute_create(&parm); 8698c2ecf20Sopenharmony_ci if (err) { 8708c2ecf20Sopenharmony_ci kfree(count_ext_comp); 8718c2ecf20Sopenharmony_ci goto err_free_attr_list; 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci return 0; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cierr_free_attr_list: 8788c2ecf20Sopenharmony_ci counter_device_attr_list_free(&group->attr_list); 8798c2ecf20Sopenharmony_ci return err; 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistruct counter_func_avail_unit { 8838c2ecf20Sopenharmony_ci const enum counter_count_function *functions_list; 8848c2ecf20Sopenharmony_ci size_t num_functions; 8858c2ecf20Sopenharmony_ci}; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_cistatic ssize_t counter_count_function_available_show(struct device *dev, 8888c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 8898c2ecf20Sopenharmony_ci{ 8908c2ecf20Sopenharmony_ci const struct counter_device_attr *const devattr = to_counter_attr(attr); 8918c2ecf20Sopenharmony_ci const struct counter_func_avail_unit *const component = devattr->component; 8928c2ecf20Sopenharmony_ci const enum counter_count_function *const func_list = component->functions_list; 8938c2ecf20Sopenharmony_ci const size_t num_functions = component->num_functions; 8948c2ecf20Sopenharmony_ci size_t i; 8958c2ecf20Sopenharmony_ci enum counter_count_function function; 8968c2ecf20Sopenharmony_ci ssize_t len = 0; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci for (i = 0; i < num_functions; i++) { 8998c2ecf20Sopenharmony_ci function = func_list[i]; 9008c2ecf20Sopenharmony_ci len += sprintf(buf + len, "%s\n", 9018c2ecf20Sopenharmony_ci counter_count_function_str[function]); 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci return len; 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_cistatic int counter_count_attributes_create( 9088c2ecf20Sopenharmony_ci struct counter_device_attr_group *const group, 9098c2ecf20Sopenharmony_ci const struct counter_device *const counter, 9108c2ecf20Sopenharmony_ci struct counter_count *const count) 9118c2ecf20Sopenharmony_ci{ 9128c2ecf20Sopenharmony_ci struct counter_count_unit *count_comp; 9138c2ecf20Sopenharmony_ci struct counter_attr_parm parm; 9148c2ecf20Sopenharmony_ci int err; 9158c2ecf20Sopenharmony_ci struct counter_count_unit *func_comp; 9168c2ecf20Sopenharmony_ci struct counter_func_avail_unit *avail_comp; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci /* Allocate count attribute component */ 9198c2ecf20Sopenharmony_ci count_comp = kmalloc(sizeof(*count_comp), GFP_KERNEL); 9208c2ecf20Sopenharmony_ci if (!count_comp) 9218c2ecf20Sopenharmony_ci return -ENOMEM; 9228c2ecf20Sopenharmony_ci count_comp->count = count; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci /* Create main Count attribute */ 9258c2ecf20Sopenharmony_ci parm.group = group; 9268c2ecf20Sopenharmony_ci parm.prefix = ""; 9278c2ecf20Sopenharmony_ci parm.name = "count"; 9288c2ecf20Sopenharmony_ci parm.show = (counter->ops->count_read) ? counter_count_show : NULL; 9298c2ecf20Sopenharmony_ci parm.store = (counter->ops->count_write) ? counter_count_store : NULL; 9308c2ecf20Sopenharmony_ci parm.component = count_comp; 9318c2ecf20Sopenharmony_ci err = counter_attribute_create(&parm); 9328c2ecf20Sopenharmony_ci if (err) { 9338c2ecf20Sopenharmony_ci kfree(count_comp); 9348c2ecf20Sopenharmony_ci return err; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci /* Allocate function attribute component */ 9388c2ecf20Sopenharmony_ci func_comp = kmalloc(sizeof(*func_comp), GFP_KERNEL); 9398c2ecf20Sopenharmony_ci if (!func_comp) { 9408c2ecf20Sopenharmony_ci err = -ENOMEM; 9418c2ecf20Sopenharmony_ci goto err_free_attr_list; 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci func_comp->count = count; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci /* Create Count function attribute */ 9468c2ecf20Sopenharmony_ci parm.group = group; 9478c2ecf20Sopenharmony_ci parm.prefix = ""; 9488c2ecf20Sopenharmony_ci parm.name = "function"; 9498c2ecf20Sopenharmony_ci parm.show = (counter->ops->function_get) ? counter_function_show : NULL; 9508c2ecf20Sopenharmony_ci parm.store = (counter->ops->function_set) ? counter_function_store : NULL; 9518c2ecf20Sopenharmony_ci parm.component = func_comp; 9528c2ecf20Sopenharmony_ci err = counter_attribute_create(&parm); 9538c2ecf20Sopenharmony_ci if (err) { 9548c2ecf20Sopenharmony_ci kfree(func_comp); 9558c2ecf20Sopenharmony_ci goto err_free_attr_list; 9568c2ecf20Sopenharmony_ci } 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci /* Allocate function available attribute component */ 9598c2ecf20Sopenharmony_ci avail_comp = kmalloc(sizeof(*avail_comp), GFP_KERNEL); 9608c2ecf20Sopenharmony_ci if (!avail_comp) { 9618c2ecf20Sopenharmony_ci err = -ENOMEM; 9628c2ecf20Sopenharmony_ci goto err_free_attr_list; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci avail_comp->functions_list = count->functions_list; 9658c2ecf20Sopenharmony_ci avail_comp->num_functions = count->num_functions; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci /* Create Count function_available attribute */ 9688c2ecf20Sopenharmony_ci parm.group = group; 9698c2ecf20Sopenharmony_ci parm.prefix = ""; 9708c2ecf20Sopenharmony_ci parm.name = "function_available"; 9718c2ecf20Sopenharmony_ci parm.show = counter_count_function_available_show; 9728c2ecf20Sopenharmony_ci parm.store = NULL; 9738c2ecf20Sopenharmony_ci parm.component = avail_comp; 9748c2ecf20Sopenharmony_ci err = counter_attribute_create(&parm); 9758c2ecf20Sopenharmony_ci if (err) { 9768c2ecf20Sopenharmony_ci kfree(avail_comp); 9778c2ecf20Sopenharmony_ci goto err_free_attr_list; 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* Create Count name attribute */ 9818c2ecf20Sopenharmony_ci err = counter_name_attribute_create(group, count->name); 9828c2ecf20Sopenharmony_ci if (err) 9838c2ecf20Sopenharmony_ci goto err_free_attr_list; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci /* Register Count extension attributes */ 9868c2ecf20Sopenharmony_ci err = counter_count_ext_register(group, count); 9878c2ecf20Sopenharmony_ci if (err) 9888c2ecf20Sopenharmony_ci goto err_free_attr_list; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci return 0; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_cierr_free_attr_list: 9938c2ecf20Sopenharmony_ci counter_device_attr_list_free(&group->attr_list); 9948c2ecf20Sopenharmony_ci return err; 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cistatic int counter_counts_register( 9988c2ecf20Sopenharmony_ci struct counter_device_attr_group *const groups_list, 9998c2ecf20Sopenharmony_ci const struct counter_device *const counter) 10008c2ecf20Sopenharmony_ci{ 10018c2ecf20Sopenharmony_ci size_t i; 10028c2ecf20Sopenharmony_ci struct counter_count *count; 10038c2ecf20Sopenharmony_ci const char *name; 10048c2ecf20Sopenharmony_ci int err; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci /* Register each Count */ 10078c2ecf20Sopenharmony_ci for (i = 0; i < counter->num_counts; i++) { 10088c2ecf20Sopenharmony_ci count = counter->counts + i; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci /* Generate Count attribute directory name */ 10118c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "count%d", count->id); 10128c2ecf20Sopenharmony_ci if (!name) { 10138c2ecf20Sopenharmony_ci err = -ENOMEM; 10148c2ecf20Sopenharmony_ci goto err_free_attr_groups; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci groups_list[i].attr_group.name = name; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci /* Register the Synapses associated with each Count */ 10198c2ecf20Sopenharmony_ci err = counter_synapses_register(groups_list + i, counter, count, 10208c2ecf20Sopenharmony_ci name); 10218c2ecf20Sopenharmony_ci if (err) 10228c2ecf20Sopenharmony_ci goto err_free_attr_groups; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci /* Create all attributes associated with Count */ 10258c2ecf20Sopenharmony_ci err = counter_count_attributes_create(groups_list + i, counter, 10268c2ecf20Sopenharmony_ci count); 10278c2ecf20Sopenharmony_ci if (err) 10288c2ecf20Sopenharmony_ci goto err_free_attr_groups; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci return 0; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_cierr_free_attr_groups: 10348c2ecf20Sopenharmony_ci do { 10358c2ecf20Sopenharmony_ci kfree(groups_list[i].attr_group.name); 10368c2ecf20Sopenharmony_ci counter_device_attr_list_free(&groups_list[i].attr_list); 10378c2ecf20Sopenharmony_ci } while (i--); 10388c2ecf20Sopenharmony_ci return err; 10398c2ecf20Sopenharmony_ci} 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_cistruct counter_size_unit { 10428c2ecf20Sopenharmony_ci size_t size; 10438c2ecf20Sopenharmony_ci}; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_cistatic ssize_t counter_device_attr_size_show(struct device *dev, 10468c2ecf20Sopenharmony_ci struct device_attribute *attr, 10478c2ecf20Sopenharmony_ci char *buf) 10488c2ecf20Sopenharmony_ci{ 10498c2ecf20Sopenharmony_ci const struct counter_size_unit *const comp = to_counter_attr(attr)->component; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci return sprintf(buf, "%zu\n", comp->size); 10528c2ecf20Sopenharmony_ci} 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_cistatic int counter_size_attribute_create( 10558c2ecf20Sopenharmony_ci struct counter_device_attr_group *const group, 10568c2ecf20Sopenharmony_ci const size_t size, const char *const name) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci struct counter_size_unit *size_comp; 10598c2ecf20Sopenharmony_ci struct counter_attr_parm parm; 10608c2ecf20Sopenharmony_ci int err; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci /* Allocate size attribute component */ 10638c2ecf20Sopenharmony_ci size_comp = kmalloc(sizeof(*size_comp), GFP_KERNEL); 10648c2ecf20Sopenharmony_ci if (!size_comp) 10658c2ecf20Sopenharmony_ci return -ENOMEM; 10668c2ecf20Sopenharmony_ci size_comp->size = size; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci parm.group = group; 10698c2ecf20Sopenharmony_ci parm.prefix = ""; 10708c2ecf20Sopenharmony_ci parm.name = name; 10718c2ecf20Sopenharmony_ci parm.show = counter_device_attr_size_show; 10728c2ecf20Sopenharmony_ci parm.store = NULL; 10738c2ecf20Sopenharmony_ci parm.component = size_comp; 10748c2ecf20Sopenharmony_ci err = counter_attribute_create(&parm); 10758c2ecf20Sopenharmony_ci if (err) 10768c2ecf20Sopenharmony_ci goto err_free_size_comp; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci return 0; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_cierr_free_size_comp: 10818c2ecf20Sopenharmony_ci kfree(size_comp); 10828c2ecf20Sopenharmony_ci return err; 10838c2ecf20Sopenharmony_ci} 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_cistruct counter_ext_unit { 10868c2ecf20Sopenharmony_ci const struct counter_device_ext *ext; 10878c2ecf20Sopenharmony_ci}; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_cistatic ssize_t counter_device_ext_show(struct device *dev, 10908c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 10918c2ecf20Sopenharmony_ci{ 10928c2ecf20Sopenharmony_ci const struct counter_device_attr *const devattr = to_counter_attr(attr); 10938c2ecf20Sopenharmony_ci const struct counter_ext_unit *const component = devattr->component; 10948c2ecf20Sopenharmony_ci const struct counter_device_ext *const ext = component->ext; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci return ext->read(dev_get_drvdata(dev), ext->priv, buf); 10978c2ecf20Sopenharmony_ci} 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_cistatic ssize_t counter_device_ext_store(struct device *dev, 11008c2ecf20Sopenharmony_ci struct device_attribute *attr, 11018c2ecf20Sopenharmony_ci const char *buf, size_t len) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci const struct counter_device_attr *const devattr = to_counter_attr(attr); 11048c2ecf20Sopenharmony_ci const struct counter_ext_unit *const component = devattr->component; 11058c2ecf20Sopenharmony_ci const struct counter_device_ext *const ext = component->ext; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci return ext->write(dev_get_drvdata(dev), ext->priv, buf, len); 11088c2ecf20Sopenharmony_ci} 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_cistatic int counter_device_ext_register( 11118c2ecf20Sopenharmony_ci struct counter_device_attr_group *const group, 11128c2ecf20Sopenharmony_ci struct counter_device *const counter) 11138c2ecf20Sopenharmony_ci{ 11148c2ecf20Sopenharmony_ci size_t i; 11158c2ecf20Sopenharmony_ci struct counter_ext_unit *ext_comp; 11168c2ecf20Sopenharmony_ci struct counter_attr_parm parm; 11178c2ecf20Sopenharmony_ci int err; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci /* Create an attribute for each extension */ 11208c2ecf20Sopenharmony_ci for (i = 0 ; i < counter->num_ext; i++) { 11218c2ecf20Sopenharmony_ci /* Allocate extension attribute component */ 11228c2ecf20Sopenharmony_ci ext_comp = kmalloc(sizeof(*ext_comp), GFP_KERNEL); 11238c2ecf20Sopenharmony_ci if (!ext_comp) { 11248c2ecf20Sopenharmony_ci err = -ENOMEM; 11258c2ecf20Sopenharmony_ci goto err_free_attr_list; 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci ext_comp->ext = counter->ext + i; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci /* Allocate extension attribute */ 11318c2ecf20Sopenharmony_ci parm.group = group; 11328c2ecf20Sopenharmony_ci parm.prefix = ""; 11338c2ecf20Sopenharmony_ci parm.name = counter->ext[i].name; 11348c2ecf20Sopenharmony_ci parm.show = (counter->ext[i].read) ? counter_device_ext_show : NULL; 11358c2ecf20Sopenharmony_ci parm.store = (counter->ext[i].write) ? counter_device_ext_store : NULL; 11368c2ecf20Sopenharmony_ci parm.component = ext_comp; 11378c2ecf20Sopenharmony_ci err = counter_attribute_create(&parm); 11388c2ecf20Sopenharmony_ci if (err) { 11398c2ecf20Sopenharmony_ci kfree(ext_comp); 11408c2ecf20Sopenharmony_ci goto err_free_attr_list; 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci return 0; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_cierr_free_attr_list: 11478c2ecf20Sopenharmony_ci counter_device_attr_list_free(&group->attr_list); 11488c2ecf20Sopenharmony_ci return err; 11498c2ecf20Sopenharmony_ci} 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_cistatic int counter_global_attr_register( 11528c2ecf20Sopenharmony_ci struct counter_device_attr_group *const group, 11538c2ecf20Sopenharmony_ci struct counter_device *const counter) 11548c2ecf20Sopenharmony_ci{ 11558c2ecf20Sopenharmony_ci int err; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci /* Create name attribute */ 11588c2ecf20Sopenharmony_ci err = counter_name_attribute_create(group, counter->name); 11598c2ecf20Sopenharmony_ci if (err) 11608c2ecf20Sopenharmony_ci return err; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci /* Create num_counts attribute */ 11638c2ecf20Sopenharmony_ci err = counter_size_attribute_create(group, counter->num_counts, 11648c2ecf20Sopenharmony_ci "num_counts"); 11658c2ecf20Sopenharmony_ci if (err) 11668c2ecf20Sopenharmony_ci goto err_free_attr_list; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci /* Create num_signals attribute */ 11698c2ecf20Sopenharmony_ci err = counter_size_attribute_create(group, counter->num_signals, 11708c2ecf20Sopenharmony_ci "num_signals"); 11718c2ecf20Sopenharmony_ci if (err) 11728c2ecf20Sopenharmony_ci goto err_free_attr_list; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci /* Register Counter device extension attributes */ 11758c2ecf20Sopenharmony_ci err = counter_device_ext_register(group, counter); 11768c2ecf20Sopenharmony_ci if (err) 11778c2ecf20Sopenharmony_ci goto err_free_attr_list; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci return 0; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_cierr_free_attr_list: 11828c2ecf20Sopenharmony_ci counter_device_attr_list_free(&group->attr_list); 11838c2ecf20Sopenharmony_ci return err; 11848c2ecf20Sopenharmony_ci} 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_cistatic void counter_device_groups_list_free( 11878c2ecf20Sopenharmony_ci struct counter_device_attr_group *const groups_list, 11888c2ecf20Sopenharmony_ci const size_t num_groups) 11898c2ecf20Sopenharmony_ci{ 11908c2ecf20Sopenharmony_ci struct counter_device_attr_group *group; 11918c2ecf20Sopenharmony_ci size_t i; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* loop through all attribute groups (signals, counts, global, etc.) */ 11948c2ecf20Sopenharmony_ci for (i = 0; i < num_groups; i++) { 11958c2ecf20Sopenharmony_ci group = groups_list + i; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci /* free all attribute group and associated attributes memory */ 11988c2ecf20Sopenharmony_ci kfree(group->attr_group.name); 11998c2ecf20Sopenharmony_ci kfree(group->attr_group.attrs); 12008c2ecf20Sopenharmony_ci counter_device_attr_list_free(&group->attr_list); 12018c2ecf20Sopenharmony_ci } 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci kfree(groups_list); 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_cistatic int counter_device_groups_list_prepare( 12078c2ecf20Sopenharmony_ci struct counter_device *const counter) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci const size_t total_num_groups = 12108c2ecf20Sopenharmony_ci counter->num_signals + counter->num_counts + 1; 12118c2ecf20Sopenharmony_ci struct counter_device_attr_group *groups_list; 12128c2ecf20Sopenharmony_ci size_t i; 12138c2ecf20Sopenharmony_ci int err; 12148c2ecf20Sopenharmony_ci size_t num_groups = 0; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci /* Allocate space for attribute groups (signals, counts, and ext) */ 12178c2ecf20Sopenharmony_ci groups_list = kcalloc(total_num_groups, sizeof(*groups_list), 12188c2ecf20Sopenharmony_ci GFP_KERNEL); 12198c2ecf20Sopenharmony_ci if (!groups_list) 12208c2ecf20Sopenharmony_ci return -ENOMEM; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci /* Initialize attribute lists */ 12238c2ecf20Sopenharmony_ci for (i = 0; i < total_num_groups; i++) 12248c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&groups_list[i].attr_list); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci /* Register Signals */ 12278c2ecf20Sopenharmony_ci err = counter_signals_register(groups_list, counter); 12288c2ecf20Sopenharmony_ci if (err) 12298c2ecf20Sopenharmony_ci goto err_free_groups_list; 12308c2ecf20Sopenharmony_ci num_groups += counter->num_signals; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci /* Register Counts and respective Synapses */ 12338c2ecf20Sopenharmony_ci err = counter_counts_register(groups_list + num_groups, counter); 12348c2ecf20Sopenharmony_ci if (err) 12358c2ecf20Sopenharmony_ci goto err_free_groups_list; 12368c2ecf20Sopenharmony_ci num_groups += counter->num_counts; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci /* Register Counter global attributes */ 12398c2ecf20Sopenharmony_ci err = counter_global_attr_register(groups_list + num_groups, counter); 12408c2ecf20Sopenharmony_ci if (err) 12418c2ecf20Sopenharmony_ci goto err_free_groups_list; 12428c2ecf20Sopenharmony_ci num_groups++; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci /* Store groups_list in device_state */ 12458c2ecf20Sopenharmony_ci counter->device_state->groups_list = groups_list; 12468c2ecf20Sopenharmony_ci counter->device_state->num_groups = num_groups; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci return 0; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cierr_free_groups_list: 12518c2ecf20Sopenharmony_ci counter_device_groups_list_free(groups_list, num_groups); 12528c2ecf20Sopenharmony_ci return err; 12538c2ecf20Sopenharmony_ci} 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_cistatic int counter_device_groups_prepare( 12568c2ecf20Sopenharmony_ci struct counter_device_state *const device_state) 12578c2ecf20Sopenharmony_ci{ 12588c2ecf20Sopenharmony_ci size_t i, j; 12598c2ecf20Sopenharmony_ci struct counter_device_attr_group *group; 12608c2ecf20Sopenharmony_ci int err; 12618c2ecf20Sopenharmony_ci struct counter_device_attr *p; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci /* Allocate attribute groups for association with device */ 12648c2ecf20Sopenharmony_ci device_state->groups = kcalloc(device_state->num_groups + 1, 12658c2ecf20Sopenharmony_ci sizeof(*device_state->groups), 12668c2ecf20Sopenharmony_ci GFP_KERNEL); 12678c2ecf20Sopenharmony_ci if (!device_state->groups) 12688c2ecf20Sopenharmony_ci return -ENOMEM; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci /* Prepare each group of attributes for association */ 12718c2ecf20Sopenharmony_ci for (i = 0; i < device_state->num_groups; i++) { 12728c2ecf20Sopenharmony_ci group = device_state->groups_list + i; 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci /* Allocate space for attribute pointers in attribute group */ 12758c2ecf20Sopenharmony_ci group->attr_group.attrs = kcalloc(group->num_attr + 1, 12768c2ecf20Sopenharmony_ci sizeof(*group->attr_group.attrs), GFP_KERNEL); 12778c2ecf20Sopenharmony_ci if (!group->attr_group.attrs) { 12788c2ecf20Sopenharmony_ci err = -ENOMEM; 12798c2ecf20Sopenharmony_ci goto err_free_groups; 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci /* Add attribute pointers to attribute group */ 12838c2ecf20Sopenharmony_ci j = 0; 12848c2ecf20Sopenharmony_ci list_for_each_entry(p, &group->attr_list, l) 12858c2ecf20Sopenharmony_ci group->attr_group.attrs[j++] = &p->dev_attr.attr; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci /* Group attributes in attribute group */ 12888c2ecf20Sopenharmony_ci device_state->groups[i] = &group->attr_group; 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci /* Associate attributes with device */ 12918c2ecf20Sopenharmony_ci device_state->dev.groups = device_state->groups; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci return 0; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_cierr_free_groups: 12968c2ecf20Sopenharmony_ci do { 12978c2ecf20Sopenharmony_ci group = device_state->groups_list + i; 12988c2ecf20Sopenharmony_ci kfree(group->attr_group.attrs); 12998c2ecf20Sopenharmony_ci group->attr_group.attrs = NULL; 13008c2ecf20Sopenharmony_ci } while (i--); 13018c2ecf20Sopenharmony_ci kfree(device_state->groups); 13028c2ecf20Sopenharmony_ci return err; 13038c2ecf20Sopenharmony_ci} 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci/* Provides a unique ID for each counter device */ 13068c2ecf20Sopenharmony_cistatic DEFINE_IDA(counter_ida); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_cistatic void counter_device_release(struct device *dev) 13098c2ecf20Sopenharmony_ci{ 13108c2ecf20Sopenharmony_ci struct counter_device *const counter = dev_get_drvdata(dev); 13118c2ecf20Sopenharmony_ci struct counter_device_state *const device_state = counter->device_state; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci kfree(device_state->groups); 13148c2ecf20Sopenharmony_ci counter_device_groups_list_free(device_state->groups_list, 13158c2ecf20Sopenharmony_ci device_state->num_groups); 13168c2ecf20Sopenharmony_ci ida_simple_remove(&counter_ida, device_state->id); 13178c2ecf20Sopenharmony_ci kfree(device_state); 13188c2ecf20Sopenharmony_ci} 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_cistatic struct device_type counter_device_type = { 13218c2ecf20Sopenharmony_ci .name = "counter_device", 13228c2ecf20Sopenharmony_ci .release = counter_device_release 13238c2ecf20Sopenharmony_ci}; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_cistatic struct bus_type counter_bus_type = { 13268c2ecf20Sopenharmony_ci .name = "counter" 13278c2ecf20Sopenharmony_ci}; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci/** 13308c2ecf20Sopenharmony_ci * counter_register - register Counter to the system 13318c2ecf20Sopenharmony_ci * @counter: pointer to Counter to register 13328c2ecf20Sopenharmony_ci * 13338c2ecf20Sopenharmony_ci * This function registers a Counter to the system. A sysfs "counter" directory 13348c2ecf20Sopenharmony_ci * will be created and populated with sysfs attributes correlating with the 13358c2ecf20Sopenharmony_ci * Counter Signals, Synapses, and Counts respectively. 13368c2ecf20Sopenharmony_ci */ 13378c2ecf20Sopenharmony_ciint counter_register(struct counter_device *const counter) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci struct counter_device_state *device_state; 13408c2ecf20Sopenharmony_ci int err; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci /* Allocate internal state container for Counter device */ 13438c2ecf20Sopenharmony_ci device_state = kzalloc(sizeof(*device_state), GFP_KERNEL); 13448c2ecf20Sopenharmony_ci if (!device_state) 13458c2ecf20Sopenharmony_ci return -ENOMEM; 13468c2ecf20Sopenharmony_ci counter->device_state = device_state; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci /* Acquire unique ID */ 13498c2ecf20Sopenharmony_ci device_state->id = ida_simple_get(&counter_ida, 0, 0, GFP_KERNEL); 13508c2ecf20Sopenharmony_ci if (device_state->id < 0) { 13518c2ecf20Sopenharmony_ci err = device_state->id; 13528c2ecf20Sopenharmony_ci goto err_free_device_state; 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci /* Configure device structure for Counter */ 13568c2ecf20Sopenharmony_ci device_state->dev.type = &counter_device_type; 13578c2ecf20Sopenharmony_ci device_state->dev.bus = &counter_bus_type; 13588c2ecf20Sopenharmony_ci if (counter->parent) { 13598c2ecf20Sopenharmony_ci device_state->dev.parent = counter->parent; 13608c2ecf20Sopenharmony_ci device_state->dev.of_node = counter->parent->of_node; 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci dev_set_name(&device_state->dev, "counter%d", device_state->id); 13638c2ecf20Sopenharmony_ci device_initialize(&device_state->dev); 13648c2ecf20Sopenharmony_ci dev_set_drvdata(&device_state->dev, counter); 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci /* Prepare device attributes */ 13678c2ecf20Sopenharmony_ci err = counter_device_groups_list_prepare(counter); 13688c2ecf20Sopenharmony_ci if (err) 13698c2ecf20Sopenharmony_ci goto err_free_id; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci /* Organize device attributes to groups and match to device */ 13728c2ecf20Sopenharmony_ci err = counter_device_groups_prepare(device_state); 13738c2ecf20Sopenharmony_ci if (err) 13748c2ecf20Sopenharmony_ci goto err_free_groups_list; 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci /* Add device to system */ 13778c2ecf20Sopenharmony_ci err = device_add(&device_state->dev); 13788c2ecf20Sopenharmony_ci if (err) 13798c2ecf20Sopenharmony_ci goto err_free_groups; 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci return 0; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cierr_free_groups: 13848c2ecf20Sopenharmony_ci kfree(device_state->groups); 13858c2ecf20Sopenharmony_cierr_free_groups_list: 13868c2ecf20Sopenharmony_ci counter_device_groups_list_free(device_state->groups_list, 13878c2ecf20Sopenharmony_ci device_state->num_groups); 13888c2ecf20Sopenharmony_cierr_free_id: 13898c2ecf20Sopenharmony_ci ida_simple_remove(&counter_ida, device_state->id); 13908c2ecf20Sopenharmony_cierr_free_device_state: 13918c2ecf20Sopenharmony_ci kfree(device_state); 13928c2ecf20Sopenharmony_ci return err; 13938c2ecf20Sopenharmony_ci} 13948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(counter_register); 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci/** 13978c2ecf20Sopenharmony_ci * counter_unregister - unregister Counter from the system 13988c2ecf20Sopenharmony_ci * @counter: pointer to Counter to unregister 13998c2ecf20Sopenharmony_ci * 14008c2ecf20Sopenharmony_ci * The Counter is unregistered from the system; all allocated memory is freed. 14018c2ecf20Sopenharmony_ci */ 14028c2ecf20Sopenharmony_civoid counter_unregister(struct counter_device *const counter) 14038c2ecf20Sopenharmony_ci{ 14048c2ecf20Sopenharmony_ci if (counter) 14058c2ecf20Sopenharmony_ci device_del(&counter->device_state->dev); 14068c2ecf20Sopenharmony_ci} 14078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(counter_unregister); 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_cistatic void devm_counter_unreg(struct device *dev, void *res) 14108c2ecf20Sopenharmony_ci{ 14118c2ecf20Sopenharmony_ci counter_unregister(*(struct counter_device **)res); 14128c2ecf20Sopenharmony_ci} 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci/** 14158c2ecf20Sopenharmony_ci * devm_counter_register - Resource-managed counter_register 14168c2ecf20Sopenharmony_ci * @dev: device to allocate counter_device for 14178c2ecf20Sopenharmony_ci * @counter: pointer to Counter to register 14188c2ecf20Sopenharmony_ci * 14198c2ecf20Sopenharmony_ci * Managed counter_register. The Counter registered with this function is 14208c2ecf20Sopenharmony_ci * automatically unregistered on driver detach. This function calls 14218c2ecf20Sopenharmony_ci * counter_register internally. Refer to that function for more information. 14228c2ecf20Sopenharmony_ci * 14238c2ecf20Sopenharmony_ci * If an Counter registered with this function needs to be unregistered 14248c2ecf20Sopenharmony_ci * separately, devm_counter_unregister must be used. 14258c2ecf20Sopenharmony_ci * 14268c2ecf20Sopenharmony_ci * RETURNS: 14278c2ecf20Sopenharmony_ci * 0 on success, negative error number on failure. 14288c2ecf20Sopenharmony_ci */ 14298c2ecf20Sopenharmony_ciint devm_counter_register(struct device *dev, 14308c2ecf20Sopenharmony_ci struct counter_device *const counter) 14318c2ecf20Sopenharmony_ci{ 14328c2ecf20Sopenharmony_ci struct counter_device **ptr; 14338c2ecf20Sopenharmony_ci int ret; 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci ptr = devres_alloc(devm_counter_unreg, sizeof(*ptr), GFP_KERNEL); 14368c2ecf20Sopenharmony_ci if (!ptr) 14378c2ecf20Sopenharmony_ci return -ENOMEM; 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci ret = counter_register(counter); 14408c2ecf20Sopenharmony_ci if (!ret) { 14418c2ecf20Sopenharmony_ci *ptr = counter; 14428c2ecf20Sopenharmony_ci devres_add(dev, ptr); 14438c2ecf20Sopenharmony_ci } else { 14448c2ecf20Sopenharmony_ci devres_free(ptr); 14458c2ecf20Sopenharmony_ci } 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci return ret; 14488c2ecf20Sopenharmony_ci} 14498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_counter_register); 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_cistatic int devm_counter_match(struct device *dev, void *res, void *data) 14528c2ecf20Sopenharmony_ci{ 14538c2ecf20Sopenharmony_ci struct counter_device **r = res; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci if (!r || !*r) { 14568c2ecf20Sopenharmony_ci WARN_ON(!r || !*r); 14578c2ecf20Sopenharmony_ci return 0; 14588c2ecf20Sopenharmony_ci } 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci return *r == data; 14618c2ecf20Sopenharmony_ci} 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci/** 14648c2ecf20Sopenharmony_ci * devm_counter_unregister - Resource-managed counter_unregister 14658c2ecf20Sopenharmony_ci * @dev: device this counter_device belongs to 14668c2ecf20Sopenharmony_ci * @counter: pointer to Counter associated with the device 14678c2ecf20Sopenharmony_ci * 14688c2ecf20Sopenharmony_ci * Unregister Counter registered with devm_counter_register. 14698c2ecf20Sopenharmony_ci */ 14708c2ecf20Sopenharmony_civoid devm_counter_unregister(struct device *dev, 14718c2ecf20Sopenharmony_ci struct counter_device *const counter) 14728c2ecf20Sopenharmony_ci{ 14738c2ecf20Sopenharmony_ci int rc; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci rc = devres_release(dev, devm_counter_unreg, devm_counter_match, 14768c2ecf20Sopenharmony_ci counter); 14778c2ecf20Sopenharmony_ci WARN_ON(rc); 14788c2ecf20Sopenharmony_ci} 14798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_counter_unregister); 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_cistatic int __init counter_init(void) 14828c2ecf20Sopenharmony_ci{ 14838c2ecf20Sopenharmony_ci return bus_register(&counter_bus_type); 14848c2ecf20Sopenharmony_ci} 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_cistatic void __exit counter_exit(void) 14878c2ecf20Sopenharmony_ci{ 14888c2ecf20Sopenharmony_ci bus_unregister(&counter_bus_type); 14898c2ecf20Sopenharmony_ci} 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_cisubsys_initcall(counter_init); 14928c2ecf20Sopenharmony_cimodule_exit(counter_exit); 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ciMODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); 14958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Generic Counter interface"); 14968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1497