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 { 5461636873SHeikki Krogerus struct swnode *swnode = NULL; 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 7456c9aa07SSakari Ailus const struct software_node *to_software_node(const struct fwnode_handle *fwnode) 7580488a6bSHeikki Krogerus { 7656c9aa07SSakari Ailus const 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 518*bc0500c1SSakari Ailus static const char * 519*bc0500c1SSakari Ailus software_node_get_name(const struct fwnode_handle *fwnode) 520*bc0500c1SSakari Ailus { 521*bc0500c1SSakari Ailus const struct swnode *swnode = to_swnode(fwnode); 522*bc0500c1SSakari Ailus 523*bc0500c1SSakari Ailus if (!swnode) 524*bc0500c1SSakari Ailus return "(null)"; 525*bc0500c1SSakari Ailus 526*bc0500c1SSakari Ailus return kobject_name(&swnode->kobj); 527*bc0500c1SSakari Ailus } 528*bc0500c1SSakari Ailus 5290e3edd94SYueHaibing static struct fwnode_handle * 53059abd836SHeikki Krogerus software_node_get_parent(const struct fwnode_handle *fwnode) 53159abd836SHeikki Krogerus { 53280488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 53359abd836SHeikki Krogerus 53451c100a6SSakari Ailus if (!swnode || !swnode->parent) 53551c100a6SSakari Ailus return NULL; 53651c100a6SSakari Ailus 53751c100a6SSakari Ailus return fwnode_handle_get(&swnode->parent->fwnode); 53859abd836SHeikki Krogerus } 53959abd836SHeikki Krogerus 5400e3edd94SYueHaibing static struct fwnode_handle * 54159abd836SHeikki Krogerus software_node_get_next_child(const struct fwnode_handle *fwnode, 54259abd836SHeikki Krogerus struct fwnode_handle *child) 54359abd836SHeikki Krogerus { 54480488a6bSHeikki Krogerus struct swnode *p = to_swnode(fwnode); 54580488a6bSHeikki Krogerus struct swnode *c = to_swnode(child); 54659abd836SHeikki Krogerus 5471d8f062eSColin Ian King if (!p || list_empty(&p->children) || 54859abd836SHeikki Krogerus (c && list_is_last(&c->entry, &p->children))) 54959abd836SHeikki Krogerus return NULL; 55059abd836SHeikki Krogerus 55159abd836SHeikki Krogerus if (c) 55259abd836SHeikki Krogerus c = list_next_entry(c, entry); 55359abd836SHeikki Krogerus else 55480488a6bSHeikki Krogerus c = list_first_entry(&p->children, struct swnode, entry); 55559abd836SHeikki Krogerus return &c->fwnode; 55659abd836SHeikki Krogerus } 55759abd836SHeikki Krogerus 55834479820SHeikki Krogerus static struct fwnode_handle * 55934479820SHeikki Krogerus software_node_get_named_child_node(const struct fwnode_handle *fwnode, 56034479820SHeikki Krogerus const char *childname) 56134479820SHeikki Krogerus { 56280488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 56380488a6bSHeikki Krogerus struct swnode *child; 56434479820SHeikki Krogerus 56534479820SHeikki Krogerus if (!swnode || list_empty(&swnode->children)) 56634479820SHeikki Krogerus return NULL; 56734479820SHeikki Krogerus 56834479820SHeikki Krogerus list_for_each_entry(child, &swnode->children, entry) { 569c959d0c2SHeikki Krogerus if (!strcmp(childname, kobject_name(&child->kobj))) { 57034479820SHeikki Krogerus kobject_get(&child->kobj); 57134479820SHeikki Krogerus return &child->fwnode; 57234479820SHeikki Krogerus } 57334479820SHeikki Krogerus } 57434479820SHeikki Krogerus return NULL; 57534479820SHeikki Krogerus } 57659abd836SHeikki Krogerus 577b06184acSHeikki Krogerus static int 578b06184acSHeikki Krogerus software_node_get_reference_args(const struct fwnode_handle *fwnode, 579b06184acSHeikki Krogerus const char *propname, const char *nargs_prop, 580b06184acSHeikki Krogerus unsigned int nargs, unsigned int index, 581b06184acSHeikki Krogerus struct fwnode_reference_args *args) 582b06184acSHeikki Krogerus { 583b06184acSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 584b06184acSHeikki Krogerus const struct software_node_reference *ref; 585b06184acSHeikki Krogerus const struct property_entry *prop; 586b06184acSHeikki Krogerus struct fwnode_handle *refnode; 587b06184acSHeikki Krogerus int i; 588b06184acSHeikki Krogerus 589b06184acSHeikki Krogerus if (!swnode || !swnode->node->references) 590b06184acSHeikki Krogerus return -ENOENT; 591b06184acSHeikki Krogerus 592b06184acSHeikki Krogerus for (ref = swnode->node->references; ref->name; ref++) 593b06184acSHeikki Krogerus if (!strcmp(ref->name, propname)) 594b06184acSHeikki Krogerus break; 595b06184acSHeikki Krogerus 596b06184acSHeikki Krogerus if (!ref->name || index > (ref->nrefs - 1)) 597b06184acSHeikki Krogerus return -ENOENT; 598b06184acSHeikki Krogerus 599b06184acSHeikki Krogerus refnode = software_node_fwnode(ref->refs[index].node); 600b06184acSHeikki Krogerus if (!refnode) 601b06184acSHeikki Krogerus return -ENOENT; 602b06184acSHeikki Krogerus 603b06184acSHeikki Krogerus if (nargs_prop) { 604b06184acSHeikki Krogerus prop = property_entry_get(swnode->node->properties, nargs_prop); 605b06184acSHeikki Krogerus if (!prop) 606b06184acSHeikki Krogerus return -EINVAL; 607b06184acSHeikki Krogerus 608b06184acSHeikki Krogerus nargs = prop->value.u32_data; 609b06184acSHeikki Krogerus } 610b06184acSHeikki Krogerus 611b06184acSHeikki Krogerus if (nargs > NR_FWNODE_REFERENCE_ARGS) 612b06184acSHeikki Krogerus return -EINVAL; 613b06184acSHeikki Krogerus 614b06184acSHeikki Krogerus args->fwnode = software_node_get(refnode); 615b06184acSHeikki Krogerus args->nargs = nargs; 616b06184acSHeikki Krogerus 617b06184acSHeikki Krogerus for (i = 0; i < nargs; i++) 618b06184acSHeikki Krogerus args->args[i] = ref->refs[index].args[i]; 619b06184acSHeikki Krogerus 620b06184acSHeikki Krogerus return 0; 621b06184acSHeikki Krogerus } 622b06184acSHeikki Krogerus 62359abd836SHeikki Krogerus static const struct fwnode_operations software_node_ops = { 62459abd836SHeikki Krogerus .get = software_node_get, 62559abd836SHeikki Krogerus .put = software_node_put, 62659abd836SHeikki Krogerus .property_present = software_node_property_present, 62759abd836SHeikki Krogerus .property_read_int_array = software_node_read_int_array, 62859abd836SHeikki Krogerus .property_read_string_array = software_node_read_string_array, 629*bc0500c1SSakari Ailus .get_name = software_node_get_name, 63059abd836SHeikki Krogerus .get_parent = software_node_get_parent, 63159abd836SHeikki Krogerus .get_next_child_node = software_node_get_next_child, 63234479820SHeikki Krogerus .get_named_child_node = software_node_get_named_child_node, 633b06184acSHeikki Krogerus .get_reference_args = software_node_get_reference_args 63459abd836SHeikki Krogerus }; 63559abd836SHeikki Krogerus 63659abd836SHeikki Krogerus /* -------------------------------------------------------------------------- */ 63759abd836SHeikki Krogerus 6381666faedSHeikki Krogerus /** 6391666faedSHeikki Krogerus * software_node_find_by_name - Find software node by name 6401666faedSHeikki Krogerus * @parent: Parent of the software node 6411666faedSHeikki Krogerus * @name: Name of the software node 6421666faedSHeikki Krogerus * 6431666faedSHeikki Krogerus * The function will find a node that is child of @parent and that is named 6441666faedSHeikki Krogerus * @name. If no node is found, the function returns NULL. 6451666faedSHeikki Krogerus * 6461666faedSHeikki Krogerus * NOTE: you will need to drop the reference with fwnode_handle_put() after use. 6471666faedSHeikki Krogerus */ 6481666faedSHeikki Krogerus const struct software_node * 6491666faedSHeikki Krogerus software_node_find_by_name(const struct software_node *parent, const char *name) 6501666faedSHeikki Krogerus { 651016049a8SHeikki Krogerus struct swnode *swnode = NULL; 6521666faedSHeikki Krogerus struct kobject *k; 6531666faedSHeikki Krogerus 6541666faedSHeikki Krogerus if (!name) 6551666faedSHeikki Krogerus return NULL; 6561666faedSHeikki Krogerus 6571666faedSHeikki Krogerus spin_lock(&swnode_kset->list_lock); 6581666faedSHeikki Krogerus 6591666faedSHeikki Krogerus list_for_each_entry(k, &swnode_kset->list, entry) { 6601666faedSHeikki Krogerus swnode = kobj_to_swnode(k); 6611666faedSHeikki Krogerus if (parent == swnode->node->parent && swnode->node->name && 6621666faedSHeikki Krogerus !strcmp(name, swnode->node->name)) { 6631666faedSHeikki Krogerus kobject_get(&swnode->kobj); 6641666faedSHeikki Krogerus break; 6651666faedSHeikki Krogerus } 6661666faedSHeikki Krogerus swnode = NULL; 6671666faedSHeikki Krogerus } 6681666faedSHeikki Krogerus 6691666faedSHeikki Krogerus spin_unlock(&swnode_kset->list_lock); 6701666faedSHeikki Krogerus 6711666faedSHeikki Krogerus return swnode ? swnode->node : NULL; 6721666faedSHeikki Krogerus } 6731666faedSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_find_by_name); 6741666faedSHeikki Krogerus 67559abd836SHeikki Krogerus static int 67680488a6bSHeikki Krogerus software_node_register_properties(struct software_node *node, 67759abd836SHeikki Krogerus const struct property_entry *properties) 67859abd836SHeikki Krogerus { 67959abd836SHeikki Krogerus struct property_entry *props; 68059abd836SHeikki Krogerus 68159abd836SHeikki Krogerus props = property_entries_dup(properties); 68259abd836SHeikki Krogerus if (IS_ERR(props)) 68359abd836SHeikki Krogerus return PTR_ERR(props); 68459abd836SHeikki Krogerus 68580488a6bSHeikki Krogerus node->properties = props; 68659abd836SHeikki Krogerus 68759abd836SHeikki Krogerus return 0; 68859abd836SHeikki Krogerus } 68959abd836SHeikki Krogerus 69059abd836SHeikki Krogerus static void software_node_release(struct kobject *kobj) 69159abd836SHeikki Krogerus { 69280488a6bSHeikki Krogerus struct swnode *swnode = kobj_to_swnode(kobj); 69359abd836SHeikki Krogerus 69480488a6bSHeikki Krogerus if (swnode->allocated) { 69580488a6bSHeikki Krogerus property_entries_free(swnode->node->properties); 69680488a6bSHeikki Krogerus kfree(swnode->node); 69780488a6bSHeikki Krogerus } 69859abd836SHeikki Krogerus ida_destroy(&swnode->child_ids); 69959abd836SHeikki Krogerus kfree(swnode); 70059abd836SHeikki Krogerus } 70159abd836SHeikki Krogerus 70259abd836SHeikki Krogerus static struct kobj_type software_node_type = { 70359abd836SHeikki Krogerus .release = software_node_release, 70459abd836SHeikki Krogerus .sysfs_ops = &kobj_sysfs_ops, 70559abd836SHeikki Krogerus }; 70659abd836SHeikki Krogerus 70780488a6bSHeikki Krogerus static struct fwnode_handle * 70880488a6bSHeikki Krogerus swnode_register(const struct software_node *node, struct swnode *parent, 70980488a6bSHeikki Krogerus unsigned int allocated) 71080488a6bSHeikki Krogerus { 71180488a6bSHeikki Krogerus struct swnode *swnode; 71280488a6bSHeikki Krogerus int ret; 71380488a6bSHeikki Krogerus 71480488a6bSHeikki Krogerus swnode = kzalloc(sizeof(*swnode), GFP_KERNEL); 71580488a6bSHeikki Krogerus if (!swnode) { 71680488a6bSHeikki Krogerus ret = -ENOMEM; 71780488a6bSHeikki Krogerus goto out_err; 71880488a6bSHeikki Krogerus } 71980488a6bSHeikki Krogerus 72080488a6bSHeikki Krogerus ret = ida_simple_get(parent ? &parent->child_ids : &swnode_root_ids, 72180488a6bSHeikki Krogerus 0, 0, GFP_KERNEL); 72280488a6bSHeikki Krogerus if (ret < 0) { 72380488a6bSHeikki Krogerus kfree(swnode); 72480488a6bSHeikki Krogerus goto out_err; 72580488a6bSHeikki Krogerus } 72680488a6bSHeikki Krogerus 72780488a6bSHeikki Krogerus swnode->id = ret; 72880488a6bSHeikki Krogerus swnode->node = node; 72980488a6bSHeikki Krogerus swnode->parent = parent; 73080488a6bSHeikki Krogerus swnode->allocated = allocated; 73180488a6bSHeikki Krogerus swnode->kobj.kset = swnode_kset; 73280488a6bSHeikki Krogerus swnode->fwnode.ops = &software_node_ops; 73380488a6bSHeikki Krogerus 73480488a6bSHeikki Krogerus ida_init(&swnode->child_ids); 73580488a6bSHeikki Krogerus INIT_LIST_HEAD(&swnode->entry); 73680488a6bSHeikki Krogerus INIT_LIST_HEAD(&swnode->children); 73780488a6bSHeikki Krogerus 73880488a6bSHeikki Krogerus if (node->name) 73980488a6bSHeikki Krogerus ret = kobject_init_and_add(&swnode->kobj, &software_node_type, 74080488a6bSHeikki Krogerus parent ? &parent->kobj : NULL, 74180488a6bSHeikki Krogerus "%s", node->name); 74280488a6bSHeikki Krogerus else 74380488a6bSHeikki Krogerus ret = kobject_init_and_add(&swnode->kobj, &software_node_type, 74480488a6bSHeikki Krogerus parent ? &parent->kobj : NULL, 74580488a6bSHeikki Krogerus "node%d", swnode->id); 74680488a6bSHeikki Krogerus if (ret) { 74780488a6bSHeikki Krogerus kobject_put(&swnode->kobj); 74880488a6bSHeikki Krogerus return ERR_PTR(ret); 74980488a6bSHeikki Krogerus } 75080488a6bSHeikki Krogerus 75180488a6bSHeikki Krogerus if (parent) 75280488a6bSHeikki Krogerus list_add_tail(&swnode->entry, &parent->children); 75380488a6bSHeikki Krogerus 75480488a6bSHeikki Krogerus kobject_uevent(&swnode->kobj, KOBJ_ADD); 75580488a6bSHeikki Krogerus return &swnode->fwnode; 75680488a6bSHeikki Krogerus 75780488a6bSHeikki Krogerus out_err: 75880488a6bSHeikki Krogerus if (allocated) 75980488a6bSHeikki Krogerus property_entries_free(node->properties); 76080488a6bSHeikki Krogerus return ERR_PTR(ret); 76180488a6bSHeikki Krogerus } 76280488a6bSHeikki Krogerus 76380488a6bSHeikki Krogerus /** 76480488a6bSHeikki Krogerus * software_node_register_nodes - Register an array of software nodes 76580488a6bSHeikki Krogerus * @nodes: Zero terminated array of software nodes to be registered 76680488a6bSHeikki Krogerus * 76780488a6bSHeikki Krogerus * Register multiple software nodes at once. 76880488a6bSHeikki Krogerus */ 76980488a6bSHeikki Krogerus int software_node_register_nodes(const struct software_node *nodes) 77080488a6bSHeikki Krogerus { 77180488a6bSHeikki Krogerus int ret; 77280488a6bSHeikki Krogerus int i; 77380488a6bSHeikki Krogerus 77480488a6bSHeikki Krogerus for (i = 0; nodes[i].name; i++) { 77580488a6bSHeikki Krogerus ret = software_node_register(&nodes[i]); 77680488a6bSHeikki Krogerus if (ret) { 77780488a6bSHeikki Krogerus software_node_unregister_nodes(nodes); 77880488a6bSHeikki Krogerus return ret; 77980488a6bSHeikki Krogerus } 78080488a6bSHeikki Krogerus } 78180488a6bSHeikki Krogerus 78280488a6bSHeikki Krogerus return 0; 78380488a6bSHeikki Krogerus } 78480488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_register_nodes); 78580488a6bSHeikki Krogerus 78680488a6bSHeikki Krogerus /** 78780488a6bSHeikki Krogerus * software_node_unregister_nodes - Unregister an array of software nodes 78880488a6bSHeikki Krogerus * @nodes: Zero terminated array of software nodes to be unregistered 78980488a6bSHeikki Krogerus * 79080488a6bSHeikki Krogerus * Unregister multiple software nodes at once. 79180488a6bSHeikki Krogerus */ 79280488a6bSHeikki Krogerus void software_node_unregister_nodes(const struct software_node *nodes) 79380488a6bSHeikki Krogerus { 79480488a6bSHeikki Krogerus struct swnode *swnode; 79580488a6bSHeikki Krogerus int i; 79680488a6bSHeikki Krogerus 79780488a6bSHeikki Krogerus for (i = 0; nodes[i].name; i++) { 79880488a6bSHeikki Krogerus swnode = software_node_to_swnode(&nodes[i]); 79980488a6bSHeikki Krogerus if (swnode) 80080488a6bSHeikki Krogerus fwnode_remove_software_node(&swnode->fwnode); 80180488a6bSHeikki Krogerus } 80280488a6bSHeikki Krogerus } 80380488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_unregister_nodes); 80480488a6bSHeikki Krogerus 80580488a6bSHeikki Krogerus /** 80680488a6bSHeikki Krogerus * software_node_register - Register static software node 80780488a6bSHeikki Krogerus * @node: The software node to be registered 80880488a6bSHeikki Krogerus */ 80980488a6bSHeikki Krogerus int software_node_register(const struct software_node *node) 81080488a6bSHeikki Krogerus { 81180488a6bSHeikki Krogerus struct swnode *parent = software_node_to_swnode(node->parent); 81280488a6bSHeikki Krogerus 81380488a6bSHeikki Krogerus if (software_node_to_swnode(node)) 81480488a6bSHeikki Krogerus return -EEXIST; 81580488a6bSHeikki Krogerus 81680488a6bSHeikki Krogerus return PTR_ERR_OR_ZERO(swnode_register(node, parent, 0)); 81780488a6bSHeikki Krogerus } 81880488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_register); 81980488a6bSHeikki Krogerus 82059abd836SHeikki Krogerus struct fwnode_handle * 82159abd836SHeikki Krogerus fwnode_create_software_node(const struct property_entry *properties, 82259abd836SHeikki Krogerus const struct fwnode_handle *parent) 82359abd836SHeikki Krogerus { 82480488a6bSHeikki Krogerus struct software_node *node; 82580488a6bSHeikki Krogerus struct swnode *p = NULL; 82659abd836SHeikki Krogerus int ret; 82759abd836SHeikki Krogerus 82859abd836SHeikki Krogerus if (parent) { 82959abd836SHeikki Krogerus if (IS_ERR(parent)) 83059abd836SHeikki Krogerus return ERR_CAST(parent); 83159abd836SHeikki Krogerus if (!is_software_node(parent)) 83259abd836SHeikki Krogerus return ERR_PTR(-EINVAL); 83380488a6bSHeikki Krogerus p = to_swnode(parent); 83459abd836SHeikki Krogerus } 83559abd836SHeikki Krogerus 83680488a6bSHeikki Krogerus node = kzalloc(sizeof(*node), GFP_KERNEL); 83780488a6bSHeikki Krogerus if (!node) 83859abd836SHeikki Krogerus return ERR_PTR(-ENOMEM); 83959abd836SHeikki Krogerus 84080488a6bSHeikki Krogerus ret = software_node_register_properties(node, properties); 84159abd836SHeikki Krogerus if (ret) { 84280488a6bSHeikki Krogerus kfree(node); 84359abd836SHeikki Krogerus return ERR_PTR(ret); 84459abd836SHeikki Krogerus } 84559abd836SHeikki Krogerus 84680488a6bSHeikki Krogerus node->parent = p ? p->node : NULL; 84759abd836SHeikki Krogerus 84880488a6bSHeikki Krogerus return swnode_register(node, p, 1); 84959abd836SHeikki Krogerus } 85059abd836SHeikki Krogerus EXPORT_SYMBOL_GPL(fwnode_create_software_node); 85159abd836SHeikki Krogerus 85259abd836SHeikki Krogerus void fwnode_remove_software_node(struct fwnode_handle *fwnode) 85359abd836SHeikki Krogerus { 85480488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 85559abd836SHeikki Krogerus 85659abd836SHeikki Krogerus if (!swnode) 85759abd836SHeikki Krogerus return; 85859abd836SHeikki Krogerus 8593df85a1aSHeikki Krogerus if (swnode->parent) { 8603df85a1aSHeikki Krogerus ida_simple_remove(&swnode->parent->child_ids, swnode->id); 8613df85a1aSHeikki Krogerus list_del(&swnode->entry); 8623df85a1aSHeikki Krogerus } else { 8633df85a1aSHeikki Krogerus ida_simple_remove(&swnode_root_ids, swnode->id); 8643df85a1aSHeikki Krogerus } 8653df85a1aSHeikki Krogerus 86659abd836SHeikki Krogerus kobject_put(&swnode->kobj); 86759abd836SHeikki Krogerus } 86859abd836SHeikki Krogerus EXPORT_SYMBOL_GPL(fwnode_remove_software_node); 86959abd836SHeikki Krogerus 87059abd836SHeikki Krogerus int software_node_notify(struct device *dev, unsigned long action) 87159abd836SHeikki Krogerus { 87259abd836SHeikki Krogerus struct fwnode_handle *fwnode = dev_fwnode(dev); 87380488a6bSHeikki Krogerus struct swnode *swnode; 87459abd836SHeikki Krogerus int ret; 87559abd836SHeikki Krogerus 87659abd836SHeikki Krogerus if (!fwnode) 87759abd836SHeikki Krogerus return 0; 87859abd836SHeikki Krogerus 87959abd836SHeikki Krogerus if (!is_software_node(fwnode)) 88059abd836SHeikki Krogerus fwnode = fwnode->secondary; 88159abd836SHeikki Krogerus if (!is_software_node(fwnode)) 88259abd836SHeikki Krogerus return 0; 88359abd836SHeikki Krogerus 88480488a6bSHeikki Krogerus swnode = to_swnode(fwnode); 88559abd836SHeikki Krogerus 88659abd836SHeikki Krogerus switch (action) { 88759abd836SHeikki Krogerus case KOBJ_ADD: 88859abd836SHeikki Krogerus ret = sysfs_create_link(&dev->kobj, &swnode->kobj, 88959abd836SHeikki Krogerus "software_node"); 89059abd836SHeikki Krogerus if (ret) 89159abd836SHeikki Krogerus break; 89259abd836SHeikki Krogerus 89359abd836SHeikki Krogerus ret = sysfs_create_link(&swnode->kobj, &dev->kobj, 89459abd836SHeikki Krogerus dev_name(dev)); 89559abd836SHeikki Krogerus if (ret) { 89659abd836SHeikki Krogerus sysfs_remove_link(&dev->kobj, "software_node"); 89759abd836SHeikki Krogerus break; 89859abd836SHeikki Krogerus } 89959abd836SHeikki Krogerus kobject_get(&swnode->kobj); 90059abd836SHeikki Krogerus break; 90159abd836SHeikki Krogerus case KOBJ_REMOVE: 90259abd836SHeikki Krogerus sysfs_remove_link(&swnode->kobj, dev_name(dev)); 90359abd836SHeikki Krogerus sysfs_remove_link(&dev->kobj, "software_node"); 90459abd836SHeikki Krogerus kobject_put(&swnode->kobj); 90559abd836SHeikki Krogerus break; 90659abd836SHeikki Krogerus default: 90759abd836SHeikki Krogerus break; 90859abd836SHeikki Krogerus } 90959abd836SHeikki Krogerus 91059abd836SHeikki Krogerus return 0; 91159abd836SHeikki Krogerus } 91259abd836SHeikki Krogerus 91359abd836SHeikki Krogerus static int __init software_node_init(void) 91459abd836SHeikki Krogerus { 91559abd836SHeikki Krogerus swnode_kset = kset_create_and_add("software_nodes", NULL, kernel_kobj); 91659abd836SHeikki Krogerus if (!swnode_kset) 91759abd836SHeikki Krogerus return -ENOMEM; 91859abd836SHeikki Krogerus return 0; 91959abd836SHeikki Krogerus } 92059abd836SHeikki Krogerus postcore_initcall(software_node_init); 92159abd836SHeikki Krogerus 92259abd836SHeikki Krogerus static void __exit software_node_exit(void) 92359abd836SHeikki Krogerus { 92459abd836SHeikki Krogerus ida_destroy(&swnode_root_ids); 92559abd836SHeikki Krogerus kset_unregister(swnode_kset); 92659abd836SHeikki Krogerus } 92759abd836SHeikki Krogerus __exitcall(software_node_exit); 928