159abd836SHeikki Krogerus // SPDX-License-Identifier: GPL-2.0 259abd836SHeikki Krogerus /* 359abd836SHeikki Krogerus * Software nodes for the firmware node framework. 459abd836SHeikki Krogerus * 559abd836SHeikki Krogerus * Copyright (C) 2018, Intel Corporation 659abd836SHeikki Krogerus * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> 759abd836SHeikki Krogerus */ 859abd836SHeikki Krogerus 959abd836SHeikki Krogerus #include <linux/device.h> 1059abd836SHeikki Krogerus #include <linux/kernel.h> 1159abd836SHeikki Krogerus #include <linux/property.h> 1259abd836SHeikki Krogerus #include <linux/slab.h> 1359abd836SHeikki Krogerus 1480488a6bSHeikki Krogerus struct swnode { 1559abd836SHeikki Krogerus int id; 1659abd836SHeikki Krogerus struct kobject kobj; 1759abd836SHeikki Krogerus struct fwnode_handle fwnode; 1880488a6bSHeikki Krogerus const struct software_node *node; 1959abd836SHeikki Krogerus 2059abd836SHeikki Krogerus /* hierarchy */ 2159abd836SHeikki Krogerus struct ida child_ids; 2259abd836SHeikki Krogerus struct list_head entry; 2359abd836SHeikki Krogerus struct list_head children; 2480488a6bSHeikki Krogerus struct swnode *parent; 2559abd836SHeikki Krogerus 2680488a6bSHeikki Krogerus unsigned int allocated:1; 2759abd836SHeikki Krogerus }; 2859abd836SHeikki Krogerus 2959abd836SHeikki Krogerus static DEFINE_IDA(swnode_root_ids); 3059abd836SHeikki Krogerus static struct kset *swnode_kset; 3159abd836SHeikki Krogerus 3280488a6bSHeikki Krogerus #define kobj_to_swnode(_kobj_) container_of(_kobj_, struct swnode, kobj) 3359abd836SHeikki Krogerus 3459abd836SHeikki Krogerus static const struct fwnode_operations software_node_ops; 3559abd836SHeikki Krogerus 3659abd836SHeikki Krogerus bool is_software_node(const struct fwnode_handle *fwnode) 3759abd836SHeikki Krogerus { 3859abd836SHeikki Krogerus return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &software_node_ops; 3959abd836SHeikki Krogerus } 4080488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(is_software_node); 4159abd836SHeikki Krogerus 4280488a6bSHeikki Krogerus #define to_swnode(__fwnode) \ 4359abd836SHeikki Krogerus ({ \ 4480488a6bSHeikki Krogerus typeof(__fwnode) __to_swnode_fwnode = __fwnode; \ 4559abd836SHeikki Krogerus \ 4680488a6bSHeikki Krogerus is_software_node(__to_swnode_fwnode) ? \ 4780488a6bSHeikki Krogerus container_of(__to_swnode_fwnode, \ 4880488a6bSHeikki Krogerus struct swnode, fwnode) : NULL; \ 4959abd836SHeikki Krogerus }) 5059abd836SHeikki Krogerus 5180488a6bSHeikki Krogerus static struct swnode * 5280488a6bSHeikki Krogerus software_node_to_swnode(const struct software_node *node) 5380488a6bSHeikki Krogerus { 5480488a6bSHeikki Krogerus struct swnode *swnode; 5580488a6bSHeikki Krogerus struct kobject *k; 5680488a6bSHeikki Krogerus 5780488a6bSHeikki Krogerus if (!node) 5880488a6bSHeikki Krogerus return NULL; 5980488a6bSHeikki Krogerus 6080488a6bSHeikki Krogerus spin_lock(&swnode_kset->list_lock); 6180488a6bSHeikki Krogerus 6280488a6bSHeikki Krogerus list_for_each_entry(k, &swnode_kset->list, entry) { 6380488a6bSHeikki Krogerus swnode = kobj_to_swnode(k); 6480488a6bSHeikki Krogerus if (swnode->node == node) 6580488a6bSHeikki Krogerus break; 6680488a6bSHeikki Krogerus swnode = NULL; 6780488a6bSHeikki Krogerus } 6880488a6bSHeikki Krogerus 6980488a6bSHeikki Krogerus spin_unlock(&swnode_kset->list_lock); 7080488a6bSHeikki Krogerus 7180488a6bSHeikki Krogerus return swnode; 7280488a6bSHeikki Krogerus } 7380488a6bSHeikki Krogerus 7480488a6bSHeikki Krogerus const struct software_node *to_software_node(struct fwnode_handle *fwnode) 7580488a6bSHeikki Krogerus { 7680488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 7780488a6bSHeikki Krogerus 7880488a6bSHeikki Krogerus return swnode ? swnode->node : NULL; 7980488a6bSHeikki Krogerus } 8080488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(to_software_node); 8180488a6bSHeikki Krogerus 8280488a6bSHeikki Krogerus struct fwnode_handle *software_node_fwnode(const struct software_node *node) 8380488a6bSHeikki Krogerus { 8480488a6bSHeikki Krogerus struct swnode *swnode = software_node_to_swnode(node); 8580488a6bSHeikki Krogerus 8680488a6bSHeikki Krogerus return swnode ? &swnode->fwnode : NULL; 8780488a6bSHeikki Krogerus } 8880488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_fwnode); 8980488a6bSHeikki Krogerus 9059abd836SHeikki Krogerus /* -------------------------------------------------------------------------- */ 9159abd836SHeikki Krogerus /* property_entry processing */ 9259abd836SHeikki Krogerus 9359abd836SHeikki Krogerus static const struct property_entry * 9459abd836SHeikki Krogerus property_entry_get(const struct property_entry *prop, const char *name) 9559abd836SHeikki Krogerus { 9659abd836SHeikki Krogerus if (!prop) 9759abd836SHeikki Krogerus return NULL; 9859abd836SHeikki Krogerus 9959abd836SHeikki Krogerus for (; prop->name; prop++) 10059abd836SHeikki Krogerus if (!strcmp(name, prop->name)) 10159abd836SHeikki Krogerus return prop; 10259abd836SHeikki Krogerus 10359abd836SHeikki Krogerus return NULL; 10459abd836SHeikki Krogerus } 10559abd836SHeikki Krogerus 106ed1cdf31SHeikki Krogerus static void 107ed1cdf31SHeikki Krogerus property_set_pointer(struct property_entry *prop, const void *pointer) 108ed1cdf31SHeikki Krogerus { 109ed1cdf31SHeikki Krogerus switch (prop->type) { 110ed1cdf31SHeikki Krogerus case DEV_PROP_U8: 111ed1cdf31SHeikki Krogerus if (prop->is_array) 112ed1cdf31SHeikki Krogerus prop->pointer.u8_data = pointer; 113ed1cdf31SHeikki Krogerus else 114ed1cdf31SHeikki Krogerus prop->value.u8_data = *((u8 *)pointer); 115ed1cdf31SHeikki Krogerus break; 116ed1cdf31SHeikki Krogerus case DEV_PROP_U16: 117ed1cdf31SHeikki Krogerus if (prop->is_array) 118ed1cdf31SHeikki Krogerus prop->pointer.u16_data = pointer; 119ed1cdf31SHeikki Krogerus else 120ed1cdf31SHeikki Krogerus prop->value.u16_data = *((u16 *)pointer); 121ed1cdf31SHeikki Krogerus break; 122ed1cdf31SHeikki Krogerus case DEV_PROP_U32: 123ed1cdf31SHeikki Krogerus if (prop->is_array) 124ed1cdf31SHeikki Krogerus prop->pointer.u32_data = pointer; 125ed1cdf31SHeikki Krogerus else 126ed1cdf31SHeikki Krogerus prop->value.u32_data = *((u32 *)pointer); 127ed1cdf31SHeikki Krogerus break; 128ed1cdf31SHeikki Krogerus case DEV_PROP_U64: 129ed1cdf31SHeikki Krogerus if (prop->is_array) 130ed1cdf31SHeikki Krogerus prop->pointer.u64_data = pointer; 131ed1cdf31SHeikki Krogerus else 132ed1cdf31SHeikki Krogerus prop->value.u64_data = *((u64 *)pointer); 133ed1cdf31SHeikki Krogerus break; 134ed1cdf31SHeikki Krogerus case DEV_PROP_STRING: 135ed1cdf31SHeikki Krogerus if (prop->is_array) 136ed1cdf31SHeikki Krogerus prop->pointer.str = pointer; 137ed1cdf31SHeikki Krogerus else 138ed1cdf31SHeikki Krogerus prop->value.str = pointer; 139ed1cdf31SHeikki Krogerus break; 140ed1cdf31SHeikki Krogerus default: 141ed1cdf31SHeikki Krogerus break; 142ed1cdf31SHeikki Krogerus } 143ed1cdf31SHeikki Krogerus } 144ed1cdf31SHeikki Krogerus 14559abd836SHeikki Krogerus static const void *property_get_pointer(const struct property_entry *prop) 14659abd836SHeikki Krogerus { 14759abd836SHeikki Krogerus switch (prop->type) { 14859abd836SHeikki Krogerus case DEV_PROP_U8: 14959abd836SHeikki Krogerus if (prop->is_array) 15059abd836SHeikki Krogerus return prop->pointer.u8_data; 15159abd836SHeikki Krogerus return &prop->value.u8_data; 15259abd836SHeikki Krogerus case DEV_PROP_U16: 15359abd836SHeikki Krogerus if (prop->is_array) 15459abd836SHeikki Krogerus return prop->pointer.u16_data; 15559abd836SHeikki Krogerus return &prop->value.u16_data; 15659abd836SHeikki Krogerus case DEV_PROP_U32: 15759abd836SHeikki Krogerus if (prop->is_array) 15859abd836SHeikki Krogerus return prop->pointer.u32_data; 15959abd836SHeikki Krogerus return &prop->value.u32_data; 16059abd836SHeikki Krogerus case DEV_PROP_U64: 16159abd836SHeikki Krogerus if (prop->is_array) 16259abd836SHeikki Krogerus return prop->pointer.u64_data; 16359abd836SHeikki Krogerus return &prop->value.u64_data; 16459abd836SHeikki Krogerus case DEV_PROP_STRING: 16559abd836SHeikki Krogerus if (prop->is_array) 16659abd836SHeikki Krogerus return prop->pointer.str; 16759abd836SHeikki Krogerus return &prop->value.str; 16859abd836SHeikki Krogerus default: 16959abd836SHeikki Krogerus return NULL; 17059abd836SHeikki Krogerus } 17159abd836SHeikki Krogerus } 17259abd836SHeikki Krogerus 17359abd836SHeikki Krogerus static const void *property_entry_find(const struct property_entry *props, 17459abd836SHeikki Krogerus const char *propname, size_t length) 17559abd836SHeikki Krogerus { 17659abd836SHeikki Krogerus const struct property_entry *prop; 17759abd836SHeikki Krogerus const void *pointer; 17859abd836SHeikki Krogerus 17959abd836SHeikki Krogerus prop = property_entry_get(props, propname); 18059abd836SHeikki Krogerus if (!prop) 18159abd836SHeikki Krogerus return ERR_PTR(-EINVAL); 18259abd836SHeikki Krogerus pointer = property_get_pointer(prop); 18359abd836SHeikki Krogerus if (!pointer) 18459abd836SHeikki Krogerus return ERR_PTR(-ENODATA); 18559abd836SHeikki Krogerus if (length > prop->length) 18659abd836SHeikki Krogerus return ERR_PTR(-EOVERFLOW); 18759abd836SHeikki Krogerus return pointer; 18859abd836SHeikki Krogerus } 18959abd836SHeikki Krogerus 19059abd836SHeikki Krogerus static int property_entry_read_u8_array(const struct property_entry *props, 19159abd836SHeikki Krogerus const char *propname, 19259abd836SHeikki Krogerus u8 *values, size_t nval) 19359abd836SHeikki Krogerus { 19459abd836SHeikki Krogerus const void *pointer; 19559abd836SHeikki Krogerus size_t length = nval * sizeof(*values); 19659abd836SHeikki Krogerus 19759abd836SHeikki Krogerus pointer = property_entry_find(props, propname, length); 19859abd836SHeikki Krogerus if (IS_ERR(pointer)) 19959abd836SHeikki Krogerus return PTR_ERR(pointer); 20059abd836SHeikki Krogerus 20159abd836SHeikki Krogerus memcpy(values, pointer, length); 20259abd836SHeikki Krogerus return 0; 20359abd836SHeikki Krogerus } 20459abd836SHeikki Krogerus 20559abd836SHeikki Krogerus static int property_entry_read_u16_array(const struct property_entry *props, 20659abd836SHeikki Krogerus const char *propname, 20759abd836SHeikki Krogerus u16 *values, size_t nval) 20859abd836SHeikki Krogerus { 20959abd836SHeikki Krogerus const void *pointer; 21059abd836SHeikki Krogerus size_t length = nval * sizeof(*values); 21159abd836SHeikki Krogerus 21259abd836SHeikki Krogerus pointer = property_entry_find(props, propname, length); 21359abd836SHeikki Krogerus if (IS_ERR(pointer)) 21459abd836SHeikki Krogerus return PTR_ERR(pointer); 21559abd836SHeikki Krogerus 21659abd836SHeikki Krogerus memcpy(values, pointer, length); 21759abd836SHeikki Krogerus return 0; 21859abd836SHeikki Krogerus } 21959abd836SHeikki Krogerus 22059abd836SHeikki Krogerus static int property_entry_read_u32_array(const struct property_entry *props, 22159abd836SHeikki Krogerus const char *propname, 22259abd836SHeikki Krogerus u32 *values, size_t nval) 22359abd836SHeikki Krogerus { 22459abd836SHeikki Krogerus const void *pointer; 22559abd836SHeikki Krogerus size_t length = nval * sizeof(*values); 22659abd836SHeikki Krogerus 22759abd836SHeikki Krogerus pointer = property_entry_find(props, propname, length); 22859abd836SHeikki Krogerus if (IS_ERR(pointer)) 22959abd836SHeikki Krogerus return PTR_ERR(pointer); 23059abd836SHeikki Krogerus 23159abd836SHeikki Krogerus memcpy(values, pointer, length); 23259abd836SHeikki Krogerus return 0; 23359abd836SHeikki Krogerus } 23459abd836SHeikki Krogerus 23559abd836SHeikki Krogerus static int property_entry_read_u64_array(const struct property_entry *props, 23659abd836SHeikki Krogerus const char *propname, 23759abd836SHeikki Krogerus u64 *values, size_t nval) 23859abd836SHeikki Krogerus { 23959abd836SHeikki Krogerus const void *pointer; 24059abd836SHeikki Krogerus size_t length = nval * sizeof(*values); 24159abd836SHeikki Krogerus 24259abd836SHeikki Krogerus pointer = property_entry_find(props, propname, length); 24359abd836SHeikki Krogerus if (IS_ERR(pointer)) 24459abd836SHeikki Krogerus return PTR_ERR(pointer); 24559abd836SHeikki Krogerus 24659abd836SHeikki Krogerus memcpy(values, pointer, length); 24759abd836SHeikki Krogerus return 0; 24859abd836SHeikki Krogerus } 24959abd836SHeikki Krogerus 25059abd836SHeikki Krogerus static int 25159abd836SHeikki Krogerus property_entry_count_elems_of_size(const struct property_entry *props, 25259abd836SHeikki Krogerus const char *propname, size_t length) 25359abd836SHeikki Krogerus { 25459abd836SHeikki Krogerus const struct property_entry *prop; 25559abd836SHeikki Krogerus 25659abd836SHeikki Krogerus prop = property_entry_get(props, propname); 25759abd836SHeikki Krogerus if (!prop) 25859abd836SHeikki Krogerus return -EINVAL; 25959abd836SHeikki Krogerus 26059abd836SHeikki Krogerus return prop->length / length; 26159abd836SHeikki Krogerus } 26259abd836SHeikki Krogerus 26359abd836SHeikki Krogerus static int property_entry_read_int_array(const struct property_entry *props, 26459abd836SHeikki Krogerus const char *name, 26559abd836SHeikki Krogerus unsigned int elem_size, void *val, 26659abd836SHeikki Krogerus size_t nval) 26759abd836SHeikki Krogerus { 26859abd836SHeikki Krogerus if (!val) 26959abd836SHeikki Krogerus return property_entry_count_elems_of_size(props, name, 27059abd836SHeikki Krogerus elem_size); 27159abd836SHeikki Krogerus switch (elem_size) { 27259abd836SHeikki Krogerus case sizeof(u8): 27359abd836SHeikki Krogerus return property_entry_read_u8_array(props, name, val, nval); 27459abd836SHeikki Krogerus case sizeof(u16): 27559abd836SHeikki Krogerus return property_entry_read_u16_array(props, name, val, nval); 27659abd836SHeikki Krogerus case sizeof(u32): 27759abd836SHeikki Krogerus return property_entry_read_u32_array(props, name, val, nval); 27859abd836SHeikki Krogerus case sizeof(u64): 27959abd836SHeikki Krogerus return property_entry_read_u64_array(props, name, val, nval); 28059abd836SHeikki Krogerus } 28159abd836SHeikki Krogerus 28259abd836SHeikki Krogerus return -ENXIO; 28359abd836SHeikki Krogerus } 28459abd836SHeikki Krogerus 28559abd836SHeikki Krogerus static int property_entry_read_string_array(const struct property_entry *props, 28659abd836SHeikki Krogerus const char *propname, 28759abd836SHeikki Krogerus const char **strings, size_t nval) 28859abd836SHeikki Krogerus { 28959abd836SHeikki Krogerus const struct property_entry *prop; 29059abd836SHeikki Krogerus const void *pointer; 29159abd836SHeikki Krogerus size_t array_len, length; 29259abd836SHeikki Krogerus 29359abd836SHeikki Krogerus /* Find out the array length. */ 29459abd836SHeikki Krogerus prop = property_entry_get(props, propname); 29559abd836SHeikki Krogerus if (!prop) 29659abd836SHeikki Krogerus return -EINVAL; 29759abd836SHeikki Krogerus 29859abd836SHeikki Krogerus if (prop->is_array) 29959abd836SHeikki Krogerus /* Find the length of an array. */ 30059abd836SHeikki Krogerus array_len = property_entry_count_elems_of_size(props, propname, 30159abd836SHeikki Krogerus sizeof(const char *)); 30259abd836SHeikki Krogerus else 30359abd836SHeikki Krogerus /* The array length for a non-array string property is 1. */ 30459abd836SHeikki Krogerus array_len = 1; 30559abd836SHeikki Krogerus 30659abd836SHeikki Krogerus /* Return how many there are if strings is NULL. */ 30759abd836SHeikki Krogerus if (!strings) 30859abd836SHeikki Krogerus return array_len; 30959abd836SHeikki Krogerus 31059abd836SHeikki Krogerus array_len = min(nval, array_len); 31159abd836SHeikki Krogerus length = array_len * sizeof(*strings); 31259abd836SHeikki Krogerus 31359abd836SHeikki Krogerus pointer = property_entry_find(props, propname, length); 31459abd836SHeikki Krogerus if (IS_ERR(pointer)) 31559abd836SHeikki Krogerus return PTR_ERR(pointer); 31659abd836SHeikki Krogerus 31759abd836SHeikki Krogerus memcpy(strings, pointer, length); 31859abd836SHeikki Krogerus 31959abd836SHeikki Krogerus return array_len; 32059abd836SHeikki Krogerus } 32159abd836SHeikki Krogerus 322ed1cdf31SHeikki Krogerus static void property_entry_free_data(const struct property_entry *p) 323ed1cdf31SHeikki Krogerus { 324ed1cdf31SHeikki Krogerus const void *pointer = property_get_pointer(p); 325ed1cdf31SHeikki Krogerus size_t i, nval; 326ed1cdf31SHeikki Krogerus 327ed1cdf31SHeikki Krogerus if (p->is_array) { 328ed1cdf31SHeikki Krogerus if (p->type == DEV_PROP_STRING && p->pointer.str) { 329ed1cdf31SHeikki Krogerus nval = p->length / sizeof(const char *); 330ed1cdf31SHeikki Krogerus for (i = 0; i < nval; i++) 331ed1cdf31SHeikki Krogerus kfree(p->pointer.str[i]); 332ed1cdf31SHeikki Krogerus } 333ed1cdf31SHeikki Krogerus kfree(pointer); 334ed1cdf31SHeikki Krogerus } else if (p->type == DEV_PROP_STRING) { 335ed1cdf31SHeikki Krogerus kfree(p->value.str); 336ed1cdf31SHeikki Krogerus } 337ed1cdf31SHeikki Krogerus kfree(p->name); 338ed1cdf31SHeikki Krogerus } 339ed1cdf31SHeikki Krogerus 340ed1cdf31SHeikki Krogerus static int property_copy_string_array(struct property_entry *dst, 341ed1cdf31SHeikki Krogerus const struct property_entry *src) 342ed1cdf31SHeikki Krogerus { 343ed1cdf31SHeikki Krogerus const char **d; 344ed1cdf31SHeikki Krogerus size_t nval = src->length / sizeof(*d); 345ed1cdf31SHeikki Krogerus int i; 346ed1cdf31SHeikki Krogerus 347ed1cdf31SHeikki Krogerus d = kcalloc(nval, sizeof(*d), GFP_KERNEL); 348ed1cdf31SHeikki Krogerus if (!d) 349ed1cdf31SHeikki Krogerus return -ENOMEM; 350ed1cdf31SHeikki Krogerus 351ed1cdf31SHeikki Krogerus for (i = 0; i < nval; i++) { 352ed1cdf31SHeikki Krogerus d[i] = kstrdup(src->pointer.str[i], GFP_KERNEL); 353ed1cdf31SHeikki Krogerus if (!d[i] && src->pointer.str[i]) { 354ed1cdf31SHeikki Krogerus while (--i >= 0) 355ed1cdf31SHeikki Krogerus kfree(d[i]); 356ed1cdf31SHeikki Krogerus kfree(d); 357ed1cdf31SHeikki Krogerus return -ENOMEM; 358ed1cdf31SHeikki Krogerus } 359ed1cdf31SHeikki Krogerus } 360ed1cdf31SHeikki Krogerus 361ed1cdf31SHeikki Krogerus dst->pointer.str = d; 362ed1cdf31SHeikki Krogerus return 0; 363ed1cdf31SHeikki Krogerus } 364ed1cdf31SHeikki Krogerus 365ed1cdf31SHeikki Krogerus static int property_entry_copy_data(struct property_entry *dst, 366ed1cdf31SHeikki Krogerus const struct property_entry *src) 367ed1cdf31SHeikki Krogerus { 368ed1cdf31SHeikki Krogerus const void *pointer = property_get_pointer(src); 369ed1cdf31SHeikki Krogerus const void *new; 370ed1cdf31SHeikki Krogerus int error; 371ed1cdf31SHeikki Krogerus 372ed1cdf31SHeikki Krogerus if (src->is_array) { 373ed1cdf31SHeikki Krogerus if (!src->length) 374ed1cdf31SHeikki Krogerus return -ENODATA; 375ed1cdf31SHeikki Krogerus 376ed1cdf31SHeikki Krogerus if (src->type == DEV_PROP_STRING) { 377ed1cdf31SHeikki Krogerus error = property_copy_string_array(dst, src); 378ed1cdf31SHeikki Krogerus if (error) 379ed1cdf31SHeikki Krogerus return error; 380ed1cdf31SHeikki Krogerus new = dst->pointer.str; 381ed1cdf31SHeikki Krogerus } else { 382ed1cdf31SHeikki Krogerus new = kmemdup(pointer, src->length, GFP_KERNEL); 383ed1cdf31SHeikki Krogerus if (!new) 384ed1cdf31SHeikki Krogerus return -ENOMEM; 385ed1cdf31SHeikki Krogerus } 386ed1cdf31SHeikki Krogerus } else if (src->type == DEV_PROP_STRING) { 387ed1cdf31SHeikki Krogerus new = kstrdup(src->value.str, GFP_KERNEL); 388ed1cdf31SHeikki Krogerus if (!new && src->value.str) 389ed1cdf31SHeikki Krogerus return -ENOMEM; 390ed1cdf31SHeikki Krogerus } else { 391ed1cdf31SHeikki Krogerus new = pointer; 392ed1cdf31SHeikki Krogerus } 393ed1cdf31SHeikki Krogerus 394ed1cdf31SHeikki Krogerus dst->length = src->length; 395ed1cdf31SHeikki Krogerus dst->is_array = src->is_array; 396ed1cdf31SHeikki Krogerus dst->type = src->type; 397ed1cdf31SHeikki Krogerus 398ed1cdf31SHeikki Krogerus property_set_pointer(dst, new); 399ed1cdf31SHeikki Krogerus 400ed1cdf31SHeikki Krogerus dst->name = kstrdup(src->name, GFP_KERNEL); 401ed1cdf31SHeikki Krogerus if (!dst->name) 402ed1cdf31SHeikki Krogerus goto out_free_data; 403ed1cdf31SHeikki Krogerus 404ed1cdf31SHeikki Krogerus return 0; 405ed1cdf31SHeikki Krogerus 406ed1cdf31SHeikki Krogerus out_free_data: 407ed1cdf31SHeikki Krogerus property_entry_free_data(dst); 408ed1cdf31SHeikki Krogerus return -ENOMEM; 409ed1cdf31SHeikki Krogerus } 410ed1cdf31SHeikki Krogerus 411ed1cdf31SHeikki Krogerus /** 412ed1cdf31SHeikki Krogerus * property_entries_dup - duplicate array of properties 413ed1cdf31SHeikki Krogerus * @properties: array of properties to copy 414ed1cdf31SHeikki Krogerus * 415ed1cdf31SHeikki Krogerus * This function creates a deep copy of the given NULL-terminated array 416ed1cdf31SHeikki Krogerus * of property entries. 417ed1cdf31SHeikki Krogerus */ 418ed1cdf31SHeikki Krogerus struct property_entry * 419ed1cdf31SHeikki Krogerus property_entries_dup(const struct property_entry *properties) 420ed1cdf31SHeikki Krogerus { 421ed1cdf31SHeikki Krogerus struct property_entry *p; 422ed1cdf31SHeikki Krogerus int i, n = 0; 423ed1cdf31SHeikki Krogerus int ret; 424ed1cdf31SHeikki Krogerus 425a7996986SHeikki Krogerus if (!properties) 426a7996986SHeikki Krogerus return NULL; 427a7996986SHeikki Krogerus 428ed1cdf31SHeikki Krogerus while (properties[n].name) 429ed1cdf31SHeikki Krogerus n++; 430ed1cdf31SHeikki Krogerus 431ed1cdf31SHeikki Krogerus p = kcalloc(n + 1, sizeof(*p), GFP_KERNEL); 432ed1cdf31SHeikki Krogerus if (!p) 433ed1cdf31SHeikki Krogerus return ERR_PTR(-ENOMEM); 434ed1cdf31SHeikki Krogerus 435ed1cdf31SHeikki Krogerus for (i = 0; i < n; i++) { 436ed1cdf31SHeikki Krogerus ret = property_entry_copy_data(&p[i], &properties[i]); 437ed1cdf31SHeikki Krogerus if (ret) { 438ed1cdf31SHeikki Krogerus while (--i >= 0) 439ed1cdf31SHeikki Krogerus property_entry_free_data(&p[i]); 440ed1cdf31SHeikki Krogerus kfree(p); 441ed1cdf31SHeikki Krogerus return ERR_PTR(ret); 442ed1cdf31SHeikki Krogerus } 443ed1cdf31SHeikki Krogerus } 444ed1cdf31SHeikki Krogerus 445ed1cdf31SHeikki Krogerus return p; 446ed1cdf31SHeikki Krogerus } 447ed1cdf31SHeikki Krogerus EXPORT_SYMBOL_GPL(property_entries_dup); 448ed1cdf31SHeikki Krogerus 449ed1cdf31SHeikki Krogerus /** 450ed1cdf31SHeikki Krogerus * property_entries_free - free previously allocated array of properties 451ed1cdf31SHeikki Krogerus * @properties: array of properties to destroy 452ed1cdf31SHeikki Krogerus * 453ed1cdf31SHeikki Krogerus * This function frees given NULL-terminated array of property entries, 454ed1cdf31SHeikki Krogerus * along with their data. 455ed1cdf31SHeikki Krogerus */ 456ed1cdf31SHeikki Krogerus void property_entries_free(const struct property_entry *properties) 457ed1cdf31SHeikki Krogerus { 458ed1cdf31SHeikki Krogerus const struct property_entry *p; 459ed1cdf31SHeikki Krogerus 460ed1cdf31SHeikki Krogerus if (!properties) 461ed1cdf31SHeikki Krogerus return; 462ed1cdf31SHeikki Krogerus 463ed1cdf31SHeikki Krogerus for (p = properties; p->name; p++) 464ed1cdf31SHeikki Krogerus property_entry_free_data(p); 465ed1cdf31SHeikki Krogerus 466ed1cdf31SHeikki Krogerus kfree(properties); 467ed1cdf31SHeikki Krogerus } 468ed1cdf31SHeikki Krogerus EXPORT_SYMBOL_GPL(property_entries_free); 469ed1cdf31SHeikki Krogerus 47059abd836SHeikki Krogerus /* -------------------------------------------------------------------------- */ 47159abd836SHeikki Krogerus /* fwnode operations */ 47259abd836SHeikki Krogerus 47359abd836SHeikki Krogerus static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode) 47459abd836SHeikki Krogerus { 47580488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 47659abd836SHeikki Krogerus 47759abd836SHeikki Krogerus kobject_get(&swnode->kobj); 47859abd836SHeikki Krogerus 47959abd836SHeikki Krogerus return &swnode->fwnode; 48059abd836SHeikki Krogerus } 48159abd836SHeikki Krogerus 48259abd836SHeikki Krogerus static void software_node_put(struct fwnode_handle *fwnode) 48359abd836SHeikki Krogerus { 48480488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 48559abd836SHeikki Krogerus 48659abd836SHeikki Krogerus kobject_put(&swnode->kobj); 48759abd836SHeikki Krogerus } 48859abd836SHeikki Krogerus 48959abd836SHeikki Krogerus static bool software_node_property_present(const struct fwnode_handle *fwnode, 49059abd836SHeikki Krogerus const char *propname) 49159abd836SHeikki Krogerus { 49280488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 49380488a6bSHeikki Krogerus 49480488a6bSHeikki Krogerus return !!property_entry_get(swnode->node->properties, propname); 49559abd836SHeikki Krogerus } 49659abd836SHeikki Krogerus 49759abd836SHeikki Krogerus static int software_node_read_int_array(const struct fwnode_handle *fwnode, 49859abd836SHeikki Krogerus const char *propname, 49959abd836SHeikki Krogerus unsigned int elem_size, void *val, 50059abd836SHeikki Krogerus size_t nval) 50159abd836SHeikki Krogerus { 50280488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 50359abd836SHeikki Krogerus 50480488a6bSHeikki Krogerus return property_entry_read_int_array(swnode->node->properties, propname, 50559abd836SHeikki Krogerus elem_size, val, nval); 50659abd836SHeikki Krogerus } 50759abd836SHeikki Krogerus 50859abd836SHeikki Krogerus static int software_node_read_string_array(const struct fwnode_handle *fwnode, 50959abd836SHeikki Krogerus const char *propname, 51059abd836SHeikki Krogerus const char **val, size_t nval) 51159abd836SHeikki Krogerus { 51280488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 51359abd836SHeikki Krogerus 51480488a6bSHeikki Krogerus return property_entry_read_string_array(swnode->node->properties, 51580488a6bSHeikki Krogerus propname, val, nval); 51659abd836SHeikki Krogerus } 51759abd836SHeikki Krogerus 5180e3edd94SYueHaibing static struct fwnode_handle * 51959abd836SHeikki Krogerus software_node_get_parent(const struct fwnode_handle *fwnode) 52059abd836SHeikki Krogerus { 52180488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 52259abd836SHeikki Krogerus 52380488a6bSHeikki Krogerus return swnode ? (swnode->parent ? &swnode->parent->fwnode : NULL) : NULL; 52459abd836SHeikki Krogerus } 52559abd836SHeikki Krogerus 5260e3edd94SYueHaibing static struct fwnode_handle * 52759abd836SHeikki Krogerus software_node_get_next_child(const struct fwnode_handle *fwnode, 52859abd836SHeikki Krogerus struct fwnode_handle *child) 52959abd836SHeikki Krogerus { 53080488a6bSHeikki Krogerus struct swnode *p = to_swnode(fwnode); 53180488a6bSHeikki Krogerus struct swnode *c = to_swnode(child); 53259abd836SHeikki Krogerus 5331d8f062eSColin Ian King if (!p || list_empty(&p->children) || 53459abd836SHeikki Krogerus (c && list_is_last(&c->entry, &p->children))) 53559abd836SHeikki Krogerus return NULL; 53659abd836SHeikki Krogerus 53759abd836SHeikki Krogerus if (c) 53859abd836SHeikki Krogerus c = list_next_entry(c, entry); 53959abd836SHeikki Krogerus else 54080488a6bSHeikki Krogerus c = list_first_entry(&p->children, struct swnode, entry); 54159abd836SHeikki Krogerus return &c->fwnode; 54259abd836SHeikki Krogerus } 54359abd836SHeikki Krogerus 54434479820SHeikki Krogerus static struct fwnode_handle * 54534479820SHeikki Krogerus software_node_get_named_child_node(const struct fwnode_handle *fwnode, 54634479820SHeikki Krogerus const char *childname) 54734479820SHeikki Krogerus { 54880488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 54980488a6bSHeikki Krogerus struct swnode *child; 55034479820SHeikki Krogerus 55134479820SHeikki Krogerus if (!swnode || list_empty(&swnode->children)) 55234479820SHeikki Krogerus return NULL; 55334479820SHeikki Krogerus 55434479820SHeikki Krogerus list_for_each_entry(child, &swnode->children, entry) { 555c959d0c2SHeikki Krogerus if (!strcmp(childname, kobject_name(&child->kobj))) { 55634479820SHeikki Krogerus kobject_get(&child->kobj); 55734479820SHeikki Krogerus return &child->fwnode; 55834479820SHeikki Krogerus } 55934479820SHeikki Krogerus } 56034479820SHeikki Krogerus return NULL; 56134479820SHeikki Krogerus } 56259abd836SHeikki Krogerus 563b06184acSHeikki Krogerus static int 564b06184acSHeikki Krogerus software_node_get_reference_args(const struct fwnode_handle *fwnode, 565b06184acSHeikki Krogerus const char *propname, const char *nargs_prop, 566b06184acSHeikki Krogerus unsigned int nargs, unsigned int index, 567b06184acSHeikki Krogerus struct fwnode_reference_args *args) 568b06184acSHeikki Krogerus { 569b06184acSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 570b06184acSHeikki Krogerus const struct software_node_reference *ref; 571b06184acSHeikki Krogerus const struct property_entry *prop; 572b06184acSHeikki Krogerus struct fwnode_handle *refnode; 573b06184acSHeikki Krogerus int i; 574b06184acSHeikki Krogerus 575b06184acSHeikki Krogerus if (!swnode || !swnode->node->references) 576b06184acSHeikki Krogerus return -ENOENT; 577b06184acSHeikki Krogerus 578b06184acSHeikki Krogerus for (ref = swnode->node->references; ref->name; ref++) 579b06184acSHeikki Krogerus if (!strcmp(ref->name, propname)) 580b06184acSHeikki Krogerus break; 581b06184acSHeikki Krogerus 582b06184acSHeikki Krogerus if (!ref->name || index > (ref->nrefs - 1)) 583b06184acSHeikki Krogerus return -ENOENT; 584b06184acSHeikki Krogerus 585b06184acSHeikki Krogerus refnode = software_node_fwnode(ref->refs[index].node); 586b06184acSHeikki Krogerus if (!refnode) 587b06184acSHeikki Krogerus return -ENOENT; 588b06184acSHeikki Krogerus 589b06184acSHeikki Krogerus if (nargs_prop) { 590b06184acSHeikki Krogerus prop = property_entry_get(swnode->node->properties, nargs_prop); 591b06184acSHeikki Krogerus if (!prop) 592b06184acSHeikki Krogerus return -EINVAL; 593b06184acSHeikki Krogerus 594b06184acSHeikki Krogerus nargs = prop->value.u32_data; 595b06184acSHeikki Krogerus } 596b06184acSHeikki Krogerus 597b06184acSHeikki Krogerus if (nargs > NR_FWNODE_REFERENCE_ARGS) 598b06184acSHeikki Krogerus return -EINVAL; 599b06184acSHeikki Krogerus 600b06184acSHeikki Krogerus args->fwnode = software_node_get(refnode); 601b06184acSHeikki Krogerus args->nargs = nargs; 602b06184acSHeikki Krogerus 603b06184acSHeikki Krogerus for (i = 0; i < nargs; i++) 604b06184acSHeikki Krogerus args->args[i] = ref->refs[index].args[i]; 605b06184acSHeikki Krogerus 606b06184acSHeikki Krogerus return 0; 607b06184acSHeikki Krogerus } 608b06184acSHeikki Krogerus 60959abd836SHeikki Krogerus static const struct fwnode_operations software_node_ops = { 61059abd836SHeikki Krogerus .get = software_node_get, 61159abd836SHeikki Krogerus .put = software_node_put, 61259abd836SHeikki Krogerus .property_present = software_node_property_present, 61359abd836SHeikki Krogerus .property_read_int_array = software_node_read_int_array, 61459abd836SHeikki Krogerus .property_read_string_array = software_node_read_string_array, 61559abd836SHeikki Krogerus .get_parent = software_node_get_parent, 61659abd836SHeikki Krogerus .get_next_child_node = software_node_get_next_child, 61734479820SHeikki Krogerus .get_named_child_node = software_node_get_named_child_node, 618b06184acSHeikki Krogerus .get_reference_args = software_node_get_reference_args 61959abd836SHeikki Krogerus }; 62059abd836SHeikki Krogerus 62159abd836SHeikki Krogerus /* -------------------------------------------------------------------------- */ 62259abd836SHeikki Krogerus 623*1666faedSHeikki Krogerus /** 624*1666faedSHeikki Krogerus * software_node_find_by_name - Find software node by name 625*1666faedSHeikki Krogerus * @parent: Parent of the software node 626*1666faedSHeikki Krogerus * @name: Name of the software node 627*1666faedSHeikki Krogerus * 628*1666faedSHeikki Krogerus * The function will find a node that is child of @parent and that is named 629*1666faedSHeikki Krogerus * @name. If no node is found, the function returns NULL. 630*1666faedSHeikki Krogerus * 631*1666faedSHeikki Krogerus * NOTE: you will need to drop the reference with fwnode_handle_put() after use. 632*1666faedSHeikki Krogerus */ 633*1666faedSHeikki Krogerus const struct software_node * 634*1666faedSHeikki Krogerus software_node_find_by_name(const struct software_node *parent, const char *name) 635*1666faedSHeikki Krogerus { 636*1666faedSHeikki Krogerus struct swnode *swnode; 637*1666faedSHeikki Krogerus struct kobject *k; 638*1666faedSHeikki Krogerus 639*1666faedSHeikki Krogerus if (!name) 640*1666faedSHeikki Krogerus return NULL; 641*1666faedSHeikki Krogerus 642*1666faedSHeikki Krogerus spin_lock(&swnode_kset->list_lock); 643*1666faedSHeikki Krogerus 644*1666faedSHeikki Krogerus list_for_each_entry(k, &swnode_kset->list, entry) { 645*1666faedSHeikki Krogerus swnode = kobj_to_swnode(k); 646*1666faedSHeikki Krogerus if (parent == swnode->node->parent && swnode->node->name && 647*1666faedSHeikki Krogerus !strcmp(name, swnode->node->name)) { 648*1666faedSHeikki Krogerus kobject_get(&swnode->kobj); 649*1666faedSHeikki Krogerus break; 650*1666faedSHeikki Krogerus } 651*1666faedSHeikki Krogerus swnode = NULL; 652*1666faedSHeikki Krogerus } 653*1666faedSHeikki Krogerus 654*1666faedSHeikki Krogerus spin_unlock(&swnode_kset->list_lock); 655*1666faedSHeikki Krogerus 656*1666faedSHeikki Krogerus return swnode ? swnode->node : NULL; 657*1666faedSHeikki Krogerus } 658*1666faedSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_find_by_name); 659*1666faedSHeikki Krogerus 66059abd836SHeikki Krogerus static int 66180488a6bSHeikki Krogerus software_node_register_properties(struct software_node *node, 66259abd836SHeikki Krogerus const struct property_entry *properties) 66359abd836SHeikki Krogerus { 66459abd836SHeikki Krogerus struct property_entry *props; 66559abd836SHeikki Krogerus 66659abd836SHeikki Krogerus props = property_entries_dup(properties); 66759abd836SHeikki Krogerus if (IS_ERR(props)) 66859abd836SHeikki Krogerus return PTR_ERR(props); 66959abd836SHeikki Krogerus 67080488a6bSHeikki Krogerus node->properties = props; 67159abd836SHeikki Krogerus 67259abd836SHeikki Krogerus return 0; 67359abd836SHeikki Krogerus } 67459abd836SHeikki Krogerus 67559abd836SHeikki Krogerus static void software_node_release(struct kobject *kobj) 67659abd836SHeikki Krogerus { 67780488a6bSHeikki Krogerus struct swnode *swnode = kobj_to_swnode(kobj); 67859abd836SHeikki Krogerus 67980488a6bSHeikki Krogerus if (swnode->allocated) { 68080488a6bSHeikki Krogerus property_entries_free(swnode->node->properties); 68180488a6bSHeikki Krogerus kfree(swnode->node); 68280488a6bSHeikki Krogerus } 68359abd836SHeikki Krogerus ida_destroy(&swnode->child_ids); 68459abd836SHeikki Krogerus kfree(swnode); 68559abd836SHeikki Krogerus } 68659abd836SHeikki Krogerus 68759abd836SHeikki Krogerus static struct kobj_type software_node_type = { 68859abd836SHeikki Krogerus .release = software_node_release, 68959abd836SHeikki Krogerus .sysfs_ops = &kobj_sysfs_ops, 69059abd836SHeikki Krogerus }; 69159abd836SHeikki Krogerus 69280488a6bSHeikki Krogerus static struct fwnode_handle * 69380488a6bSHeikki Krogerus swnode_register(const struct software_node *node, struct swnode *parent, 69480488a6bSHeikki Krogerus unsigned int allocated) 69580488a6bSHeikki Krogerus { 69680488a6bSHeikki Krogerus struct swnode *swnode; 69780488a6bSHeikki Krogerus int ret; 69880488a6bSHeikki Krogerus 69980488a6bSHeikki Krogerus swnode = kzalloc(sizeof(*swnode), GFP_KERNEL); 70080488a6bSHeikki Krogerus if (!swnode) { 70180488a6bSHeikki Krogerus ret = -ENOMEM; 70280488a6bSHeikki Krogerus goto out_err; 70380488a6bSHeikki Krogerus } 70480488a6bSHeikki Krogerus 70580488a6bSHeikki Krogerus ret = ida_simple_get(parent ? &parent->child_ids : &swnode_root_ids, 70680488a6bSHeikki Krogerus 0, 0, GFP_KERNEL); 70780488a6bSHeikki Krogerus if (ret < 0) { 70880488a6bSHeikki Krogerus kfree(swnode); 70980488a6bSHeikki Krogerus goto out_err; 71080488a6bSHeikki Krogerus } 71180488a6bSHeikki Krogerus 71280488a6bSHeikki Krogerus swnode->id = ret; 71380488a6bSHeikki Krogerus swnode->node = node; 71480488a6bSHeikki Krogerus swnode->parent = parent; 71580488a6bSHeikki Krogerus swnode->allocated = allocated; 71680488a6bSHeikki Krogerus swnode->kobj.kset = swnode_kset; 71780488a6bSHeikki Krogerus swnode->fwnode.ops = &software_node_ops; 71880488a6bSHeikki Krogerus 71980488a6bSHeikki Krogerus ida_init(&swnode->child_ids); 72080488a6bSHeikki Krogerus INIT_LIST_HEAD(&swnode->entry); 72180488a6bSHeikki Krogerus INIT_LIST_HEAD(&swnode->children); 72280488a6bSHeikki Krogerus 72380488a6bSHeikki Krogerus if (node->name) 72480488a6bSHeikki Krogerus ret = kobject_init_and_add(&swnode->kobj, &software_node_type, 72580488a6bSHeikki Krogerus parent ? &parent->kobj : NULL, 72680488a6bSHeikki Krogerus "%s", node->name); 72780488a6bSHeikki Krogerus else 72880488a6bSHeikki Krogerus ret = kobject_init_and_add(&swnode->kobj, &software_node_type, 72980488a6bSHeikki Krogerus parent ? &parent->kobj : NULL, 73080488a6bSHeikki Krogerus "node%d", swnode->id); 73180488a6bSHeikki Krogerus if (ret) { 73280488a6bSHeikki Krogerus kobject_put(&swnode->kobj); 73380488a6bSHeikki Krogerus return ERR_PTR(ret); 73480488a6bSHeikki Krogerus } 73580488a6bSHeikki Krogerus 73680488a6bSHeikki Krogerus if (parent) 73780488a6bSHeikki Krogerus list_add_tail(&swnode->entry, &parent->children); 73880488a6bSHeikki Krogerus 73980488a6bSHeikki Krogerus kobject_uevent(&swnode->kobj, KOBJ_ADD); 74080488a6bSHeikki Krogerus return &swnode->fwnode; 74180488a6bSHeikki Krogerus 74280488a6bSHeikki Krogerus out_err: 74380488a6bSHeikki Krogerus if (allocated) 74480488a6bSHeikki Krogerus property_entries_free(node->properties); 74580488a6bSHeikki Krogerus return ERR_PTR(ret); 74680488a6bSHeikki Krogerus } 74780488a6bSHeikki Krogerus 74880488a6bSHeikki Krogerus /** 74980488a6bSHeikki Krogerus * software_node_register_nodes - Register an array of software nodes 75080488a6bSHeikki Krogerus * @nodes: Zero terminated array of software nodes to be registered 75180488a6bSHeikki Krogerus * 75280488a6bSHeikki Krogerus * Register multiple software nodes at once. 75380488a6bSHeikki Krogerus */ 75480488a6bSHeikki Krogerus int software_node_register_nodes(const struct software_node *nodes) 75580488a6bSHeikki Krogerus { 75680488a6bSHeikki Krogerus int ret; 75780488a6bSHeikki Krogerus int i; 75880488a6bSHeikki Krogerus 75980488a6bSHeikki Krogerus for (i = 0; nodes[i].name; i++) { 76080488a6bSHeikki Krogerus ret = software_node_register(&nodes[i]); 76180488a6bSHeikki Krogerus if (ret) { 76280488a6bSHeikki Krogerus software_node_unregister_nodes(nodes); 76380488a6bSHeikki Krogerus return ret; 76480488a6bSHeikki Krogerus } 76580488a6bSHeikki Krogerus } 76680488a6bSHeikki Krogerus 76780488a6bSHeikki Krogerus return 0; 76880488a6bSHeikki Krogerus } 76980488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_register_nodes); 77080488a6bSHeikki Krogerus 77180488a6bSHeikki Krogerus /** 77280488a6bSHeikki Krogerus * software_node_unregister_nodes - Unregister an array of software nodes 77380488a6bSHeikki Krogerus * @nodes: Zero terminated array of software nodes to be unregistered 77480488a6bSHeikki Krogerus * 77580488a6bSHeikki Krogerus * Unregister multiple software nodes at once. 77680488a6bSHeikki Krogerus */ 77780488a6bSHeikki Krogerus void software_node_unregister_nodes(const struct software_node *nodes) 77880488a6bSHeikki Krogerus { 77980488a6bSHeikki Krogerus struct swnode *swnode; 78080488a6bSHeikki Krogerus int i; 78180488a6bSHeikki Krogerus 78280488a6bSHeikki Krogerus for (i = 0; nodes[i].name; i++) { 78380488a6bSHeikki Krogerus swnode = software_node_to_swnode(&nodes[i]); 78480488a6bSHeikki Krogerus if (swnode) 78580488a6bSHeikki Krogerus fwnode_remove_software_node(&swnode->fwnode); 78680488a6bSHeikki Krogerus } 78780488a6bSHeikki Krogerus } 78880488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_unregister_nodes); 78980488a6bSHeikki Krogerus 79080488a6bSHeikki Krogerus /** 79180488a6bSHeikki Krogerus * software_node_register - Register static software node 79280488a6bSHeikki Krogerus * @node: The software node to be registered 79380488a6bSHeikki Krogerus */ 79480488a6bSHeikki Krogerus int software_node_register(const struct software_node *node) 79580488a6bSHeikki Krogerus { 79680488a6bSHeikki Krogerus struct swnode *parent = software_node_to_swnode(node->parent); 79780488a6bSHeikki Krogerus 79880488a6bSHeikki Krogerus if (software_node_to_swnode(node)) 79980488a6bSHeikki Krogerus return -EEXIST; 80080488a6bSHeikki Krogerus 80180488a6bSHeikki Krogerus return PTR_ERR_OR_ZERO(swnode_register(node, parent, 0)); 80280488a6bSHeikki Krogerus } 80380488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_register); 80480488a6bSHeikki Krogerus 80559abd836SHeikki Krogerus struct fwnode_handle * 80659abd836SHeikki Krogerus fwnode_create_software_node(const struct property_entry *properties, 80759abd836SHeikki Krogerus const struct fwnode_handle *parent) 80859abd836SHeikki Krogerus { 80980488a6bSHeikki Krogerus struct software_node *node; 81080488a6bSHeikki Krogerus struct swnode *p = NULL; 81159abd836SHeikki Krogerus int ret; 81259abd836SHeikki Krogerus 81359abd836SHeikki Krogerus if (parent) { 81459abd836SHeikki Krogerus if (IS_ERR(parent)) 81559abd836SHeikki Krogerus return ERR_CAST(parent); 81659abd836SHeikki Krogerus if (!is_software_node(parent)) 81759abd836SHeikki Krogerus return ERR_PTR(-EINVAL); 81880488a6bSHeikki Krogerus p = to_swnode(parent); 81959abd836SHeikki Krogerus } 82059abd836SHeikki Krogerus 82180488a6bSHeikki Krogerus node = kzalloc(sizeof(*node), GFP_KERNEL); 82280488a6bSHeikki Krogerus if (!node) 82359abd836SHeikki Krogerus return ERR_PTR(-ENOMEM); 82459abd836SHeikki Krogerus 82580488a6bSHeikki Krogerus ret = software_node_register_properties(node, properties); 82659abd836SHeikki Krogerus if (ret) { 82780488a6bSHeikki Krogerus kfree(node); 82859abd836SHeikki Krogerus return ERR_PTR(ret); 82959abd836SHeikki Krogerus } 83059abd836SHeikki Krogerus 83180488a6bSHeikki Krogerus node->parent = p ? p->node : NULL; 83259abd836SHeikki Krogerus 83380488a6bSHeikki Krogerus return swnode_register(node, p, 1); 83459abd836SHeikki Krogerus } 83559abd836SHeikki Krogerus EXPORT_SYMBOL_GPL(fwnode_create_software_node); 83659abd836SHeikki Krogerus 83759abd836SHeikki Krogerus void fwnode_remove_software_node(struct fwnode_handle *fwnode) 83859abd836SHeikki Krogerus { 83980488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 84059abd836SHeikki Krogerus 84159abd836SHeikki Krogerus if (!swnode) 84259abd836SHeikki Krogerus return; 84359abd836SHeikki Krogerus 8443df85a1aSHeikki Krogerus if (swnode->parent) { 8453df85a1aSHeikki Krogerus ida_simple_remove(&swnode->parent->child_ids, swnode->id); 8463df85a1aSHeikki Krogerus list_del(&swnode->entry); 8473df85a1aSHeikki Krogerus } else { 8483df85a1aSHeikki Krogerus ida_simple_remove(&swnode_root_ids, swnode->id); 8493df85a1aSHeikki Krogerus } 8503df85a1aSHeikki Krogerus 85159abd836SHeikki Krogerus kobject_put(&swnode->kobj); 85259abd836SHeikki Krogerus } 85359abd836SHeikki Krogerus EXPORT_SYMBOL_GPL(fwnode_remove_software_node); 85459abd836SHeikki Krogerus 85559abd836SHeikki Krogerus int software_node_notify(struct device *dev, unsigned long action) 85659abd836SHeikki Krogerus { 85759abd836SHeikki Krogerus struct fwnode_handle *fwnode = dev_fwnode(dev); 85880488a6bSHeikki Krogerus struct swnode *swnode; 85959abd836SHeikki Krogerus int ret; 86059abd836SHeikki Krogerus 86159abd836SHeikki Krogerus if (!fwnode) 86259abd836SHeikki Krogerus return 0; 86359abd836SHeikki Krogerus 86459abd836SHeikki Krogerus if (!is_software_node(fwnode)) 86559abd836SHeikki Krogerus fwnode = fwnode->secondary; 86659abd836SHeikki Krogerus if (!is_software_node(fwnode)) 86759abd836SHeikki Krogerus return 0; 86859abd836SHeikki Krogerus 86980488a6bSHeikki Krogerus swnode = to_swnode(fwnode); 87059abd836SHeikki Krogerus 87159abd836SHeikki Krogerus switch (action) { 87259abd836SHeikki Krogerus case KOBJ_ADD: 87359abd836SHeikki Krogerus ret = sysfs_create_link(&dev->kobj, &swnode->kobj, 87459abd836SHeikki Krogerus "software_node"); 87559abd836SHeikki Krogerus if (ret) 87659abd836SHeikki Krogerus break; 87759abd836SHeikki Krogerus 87859abd836SHeikki Krogerus ret = sysfs_create_link(&swnode->kobj, &dev->kobj, 87959abd836SHeikki Krogerus dev_name(dev)); 88059abd836SHeikki Krogerus if (ret) { 88159abd836SHeikki Krogerus sysfs_remove_link(&dev->kobj, "software_node"); 88259abd836SHeikki Krogerus break; 88359abd836SHeikki Krogerus } 88459abd836SHeikki Krogerus kobject_get(&swnode->kobj); 88559abd836SHeikki Krogerus break; 88659abd836SHeikki Krogerus case KOBJ_REMOVE: 88759abd836SHeikki Krogerus sysfs_remove_link(&swnode->kobj, dev_name(dev)); 88859abd836SHeikki Krogerus sysfs_remove_link(&dev->kobj, "software_node"); 88959abd836SHeikki Krogerus kobject_put(&swnode->kobj); 89059abd836SHeikki Krogerus break; 89159abd836SHeikki Krogerus default: 89259abd836SHeikki Krogerus break; 89359abd836SHeikki Krogerus } 89459abd836SHeikki Krogerus 89559abd836SHeikki Krogerus return 0; 89659abd836SHeikki Krogerus } 89759abd836SHeikki Krogerus 89859abd836SHeikki Krogerus static int __init software_node_init(void) 89959abd836SHeikki Krogerus { 90059abd836SHeikki Krogerus swnode_kset = kset_create_and_add("software_nodes", NULL, kernel_kobj); 90159abd836SHeikki Krogerus if (!swnode_kset) 90259abd836SHeikki Krogerus return -ENOMEM; 90359abd836SHeikki Krogerus return 0; 90459abd836SHeikki Krogerus } 90559abd836SHeikki Krogerus postcore_initcall(software_node_init); 90659abd836SHeikki Krogerus 90759abd836SHeikki Krogerus static void __exit software_node_exit(void) 90859abd836SHeikki Krogerus { 90959abd836SHeikki Krogerus ida_destroy(&swnode_root_ids); 91059abd836SHeikki Krogerus kset_unregister(swnode_kset); 91159abd836SHeikki Krogerus } 91259abd836SHeikki Krogerus __exitcall(software_node_exit); 913