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 51*e68d0119SHeikki Krogerus static inline struct swnode *dev_to_swnode(struct device *dev) 52*e68d0119SHeikki Krogerus { 53*e68d0119SHeikki Krogerus struct fwnode_handle *fwnode = dev_fwnode(dev); 54*e68d0119SHeikki Krogerus 55*e68d0119SHeikki Krogerus if (!fwnode) 56*e68d0119SHeikki Krogerus return NULL; 57*e68d0119SHeikki Krogerus 58*e68d0119SHeikki Krogerus if (!is_software_node(fwnode)) 59*e68d0119SHeikki Krogerus fwnode = fwnode->secondary; 60*e68d0119SHeikki Krogerus 61*e68d0119SHeikki Krogerus return to_swnode(fwnode); 62*e68d0119SHeikki Krogerus } 63*e68d0119SHeikki Krogerus 6480488a6bSHeikki Krogerus static struct swnode * 6580488a6bSHeikki Krogerus software_node_to_swnode(const struct software_node *node) 6680488a6bSHeikki Krogerus { 6761636873SHeikki Krogerus struct swnode *swnode = NULL; 6880488a6bSHeikki Krogerus struct kobject *k; 6980488a6bSHeikki Krogerus 7080488a6bSHeikki Krogerus if (!node) 7180488a6bSHeikki Krogerus return NULL; 7280488a6bSHeikki Krogerus 7380488a6bSHeikki Krogerus spin_lock(&swnode_kset->list_lock); 7480488a6bSHeikki Krogerus 7580488a6bSHeikki Krogerus list_for_each_entry(k, &swnode_kset->list, entry) { 7680488a6bSHeikki Krogerus swnode = kobj_to_swnode(k); 7780488a6bSHeikki Krogerus if (swnode->node == node) 7880488a6bSHeikki Krogerus break; 7980488a6bSHeikki Krogerus swnode = NULL; 8080488a6bSHeikki Krogerus } 8180488a6bSHeikki Krogerus 8280488a6bSHeikki Krogerus spin_unlock(&swnode_kset->list_lock); 8380488a6bSHeikki Krogerus 8480488a6bSHeikki Krogerus return swnode; 8580488a6bSHeikki Krogerus } 8680488a6bSHeikki Krogerus 8756c9aa07SSakari Ailus const struct software_node *to_software_node(const struct fwnode_handle *fwnode) 8880488a6bSHeikki Krogerus { 8956c9aa07SSakari Ailus const struct swnode *swnode = to_swnode(fwnode); 9080488a6bSHeikki Krogerus 9180488a6bSHeikki Krogerus return swnode ? swnode->node : NULL; 9280488a6bSHeikki Krogerus } 9380488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(to_software_node); 9480488a6bSHeikki Krogerus 9580488a6bSHeikki Krogerus struct fwnode_handle *software_node_fwnode(const struct software_node *node) 9680488a6bSHeikki Krogerus { 9780488a6bSHeikki Krogerus struct swnode *swnode = software_node_to_swnode(node); 9880488a6bSHeikki Krogerus 9980488a6bSHeikki Krogerus return swnode ? &swnode->fwnode : NULL; 10080488a6bSHeikki Krogerus } 10180488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_fwnode); 10280488a6bSHeikki Krogerus 10359abd836SHeikki Krogerus /* -------------------------------------------------------------------------- */ 10459abd836SHeikki Krogerus /* property_entry processing */ 10559abd836SHeikki Krogerus 10659abd836SHeikki Krogerus static const struct property_entry * 10759abd836SHeikki Krogerus property_entry_get(const struct property_entry *prop, const char *name) 10859abd836SHeikki Krogerus { 10959abd836SHeikki Krogerus if (!prop) 11059abd836SHeikki Krogerus return NULL; 11159abd836SHeikki Krogerus 11259abd836SHeikki Krogerus for (; prop->name; prop++) 11359abd836SHeikki Krogerus if (!strcmp(name, prop->name)) 11459abd836SHeikki Krogerus return prop; 11559abd836SHeikki Krogerus 11659abd836SHeikki Krogerus return NULL; 11759abd836SHeikki Krogerus } 11859abd836SHeikki Krogerus 11959abd836SHeikki Krogerus static const void *property_get_pointer(const struct property_entry *prop) 12059abd836SHeikki Krogerus { 1211f74d70fSDmitry Torokhov if (!prop->length) 12259abd836SHeikki Krogerus return NULL; 1231f74d70fSDmitry Torokhov 124e6bff466SDmitry Torokhov return prop->is_inline ? &prop->value : prop->pointer; 12559abd836SHeikki Krogerus } 12659abd836SHeikki Krogerus 12759abd836SHeikki Krogerus static const void *property_entry_find(const struct property_entry *props, 12859abd836SHeikki Krogerus const char *propname, size_t length) 12959abd836SHeikki Krogerus { 13059abd836SHeikki Krogerus const struct property_entry *prop; 13159abd836SHeikki Krogerus const void *pointer; 13259abd836SHeikki Krogerus 13359abd836SHeikki Krogerus prop = property_entry_get(props, propname); 13459abd836SHeikki Krogerus if (!prop) 13559abd836SHeikki Krogerus return ERR_PTR(-EINVAL); 13659abd836SHeikki Krogerus pointer = property_get_pointer(prop); 13759abd836SHeikki Krogerus if (!pointer) 13859abd836SHeikki Krogerus return ERR_PTR(-ENODATA); 13959abd836SHeikki Krogerus if (length > prop->length) 14059abd836SHeikki Krogerus return ERR_PTR(-EOVERFLOW); 14159abd836SHeikki Krogerus return pointer; 14259abd836SHeikki Krogerus } 14359abd836SHeikki Krogerus 14459abd836SHeikki Krogerus static int 14559abd836SHeikki Krogerus property_entry_count_elems_of_size(const struct property_entry *props, 14659abd836SHeikki Krogerus const char *propname, size_t length) 14759abd836SHeikki Krogerus { 14859abd836SHeikki Krogerus const struct property_entry *prop; 14959abd836SHeikki Krogerus 15059abd836SHeikki Krogerus prop = property_entry_get(props, propname); 15159abd836SHeikki Krogerus if (!prop) 15259abd836SHeikki Krogerus return -EINVAL; 15359abd836SHeikki Krogerus 15459abd836SHeikki Krogerus return prop->length / length; 15559abd836SHeikki Krogerus } 15659abd836SHeikki Krogerus 15759abd836SHeikki Krogerus static int property_entry_read_int_array(const struct property_entry *props, 15859abd836SHeikki Krogerus const char *name, 15959abd836SHeikki Krogerus unsigned int elem_size, void *val, 16059abd836SHeikki Krogerus size_t nval) 16159abd836SHeikki Krogerus { 1625236f5feSDmitry Torokhov const void *pointer; 1635236f5feSDmitry Torokhov size_t length; 1645236f5feSDmitry Torokhov 16559abd836SHeikki Krogerus if (!val) 16659abd836SHeikki Krogerus return property_entry_count_elems_of_size(props, name, 16759abd836SHeikki Krogerus elem_size); 16859abd836SHeikki Krogerus 1695236f5feSDmitry Torokhov if (!is_power_of_2(elem_size) || elem_size > sizeof(u64)) 17059abd836SHeikki Krogerus return -ENXIO; 1715236f5feSDmitry Torokhov 1725236f5feSDmitry Torokhov length = nval * elem_size; 1735236f5feSDmitry Torokhov 1745236f5feSDmitry Torokhov pointer = property_entry_find(props, name, length); 1755236f5feSDmitry Torokhov if (IS_ERR(pointer)) 1765236f5feSDmitry Torokhov return PTR_ERR(pointer); 1775236f5feSDmitry Torokhov 1785236f5feSDmitry Torokhov memcpy(val, pointer, length); 1795236f5feSDmitry Torokhov return 0; 18059abd836SHeikki Krogerus } 18159abd836SHeikki Krogerus 18259abd836SHeikki Krogerus static int property_entry_read_string_array(const struct property_entry *props, 18359abd836SHeikki Krogerus const char *propname, 18459abd836SHeikki Krogerus const char **strings, size_t nval) 18559abd836SHeikki Krogerus { 18659abd836SHeikki Krogerus const void *pointer; 1871afc1403SDmitry Torokhov size_t length; 1881afc1403SDmitry Torokhov int array_len; 18959abd836SHeikki Krogerus 19059abd836SHeikki Krogerus /* Find out the array length. */ 19159abd836SHeikki Krogerus array_len = property_entry_count_elems_of_size(props, propname, 19259abd836SHeikki Krogerus sizeof(const char *)); 1931afc1403SDmitry Torokhov if (array_len < 0) 1941afc1403SDmitry Torokhov return array_len; 19559abd836SHeikki Krogerus 19659abd836SHeikki Krogerus /* Return how many there are if strings is NULL. */ 19759abd836SHeikki Krogerus if (!strings) 19859abd836SHeikki Krogerus return array_len; 19959abd836SHeikki Krogerus 2001afc1403SDmitry Torokhov array_len = min_t(size_t, nval, array_len); 20159abd836SHeikki Krogerus length = array_len * sizeof(*strings); 20259abd836SHeikki Krogerus 20359abd836SHeikki Krogerus pointer = property_entry_find(props, propname, length); 20459abd836SHeikki Krogerus if (IS_ERR(pointer)) 20559abd836SHeikki Krogerus return PTR_ERR(pointer); 20659abd836SHeikki Krogerus 20759abd836SHeikki Krogerus memcpy(strings, pointer, length); 20859abd836SHeikki Krogerus 20959abd836SHeikki Krogerus return array_len; 21059abd836SHeikki Krogerus } 21159abd836SHeikki Krogerus 212ed1cdf31SHeikki Krogerus static void property_entry_free_data(const struct property_entry *p) 213ed1cdf31SHeikki Krogerus { 2141f74d70fSDmitry Torokhov const char * const *src_str; 215ed1cdf31SHeikki Krogerus size_t i, nval; 216ed1cdf31SHeikki Krogerus 217996b0830SDmitry Torokhov if (p->type == DEV_PROP_STRING) { 218996b0830SDmitry Torokhov src_str = property_get_pointer(p); 219996b0830SDmitry Torokhov nval = p->length / sizeof(*src_str); 220ed1cdf31SHeikki Krogerus for (i = 0; i < nval; i++) 2211f74d70fSDmitry Torokhov kfree(src_str[i]); 222ed1cdf31SHeikki Krogerus } 223996b0830SDmitry Torokhov 224996b0830SDmitry Torokhov if (!p->is_inline) 225996b0830SDmitry Torokhov kfree(p->pointer); 226996b0830SDmitry Torokhov 227ed1cdf31SHeikki Krogerus kfree(p->name); 228ed1cdf31SHeikki Krogerus } 229ed1cdf31SHeikki Krogerus 230996b0830SDmitry Torokhov static bool property_copy_string_array(const char **dst_ptr, 231996b0830SDmitry Torokhov const char * const *src_ptr, 232996b0830SDmitry Torokhov size_t nval) 233ed1cdf31SHeikki Krogerus { 234ed1cdf31SHeikki Krogerus int i; 235ed1cdf31SHeikki Krogerus 236ed1cdf31SHeikki Krogerus for (i = 0; i < nval; i++) { 237996b0830SDmitry Torokhov dst_ptr[i] = kstrdup(src_ptr[i], GFP_KERNEL); 238996b0830SDmitry Torokhov if (!dst_ptr[i] && src_ptr[i]) { 239ed1cdf31SHeikki Krogerus while (--i >= 0) 240996b0830SDmitry Torokhov kfree(dst_ptr[i]); 241996b0830SDmitry Torokhov return false; 242ed1cdf31SHeikki Krogerus } 243ed1cdf31SHeikki Krogerus } 244ed1cdf31SHeikki Krogerus 245996b0830SDmitry Torokhov return true; 246ed1cdf31SHeikki Krogerus } 247ed1cdf31SHeikki Krogerus 248ed1cdf31SHeikki Krogerus static int property_entry_copy_data(struct property_entry *dst, 249ed1cdf31SHeikki Krogerus const struct property_entry *src) 250ed1cdf31SHeikki Krogerus { 251ed1cdf31SHeikki Krogerus const void *pointer = property_get_pointer(src); 252996b0830SDmitry Torokhov void *dst_ptr; 253996b0830SDmitry Torokhov size_t nval; 254ed1cdf31SHeikki Krogerus 255996b0830SDmitry Torokhov /* 256996b0830SDmitry Torokhov * Properties with no data should not be marked as stored 257996b0830SDmitry Torokhov * out of line. 258996b0830SDmitry Torokhov */ 259996b0830SDmitry Torokhov if (!src->is_inline && !src->length) 260ed1cdf31SHeikki Krogerus return -ENODATA; 261ed1cdf31SHeikki Krogerus 262e64b674bSDmitry Torokhov /* 263e64b674bSDmitry Torokhov * Reference properties are never stored inline as 264e64b674bSDmitry Torokhov * they are too big. 265e64b674bSDmitry Torokhov */ 266e64b674bSDmitry Torokhov if (src->type == DEV_PROP_REF && src->is_inline) 267e64b674bSDmitry Torokhov return -EINVAL; 268e64b674bSDmitry Torokhov 269996b0830SDmitry Torokhov if (src->length <= sizeof(dst->value)) { 270996b0830SDmitry Torokhov dst_ptr = &dst->value; 271996b0830SDmitry Torokhov dst->is_inline = true; 272ed1cdf31SHeikki Krogerus } else { 273996b0830SDmitry Torokhov dst_ptr = kmalloc(src->length, GFP_KERNEL); 274996b0830SDmitry Torokhov if (!dst_ptr) 275ed1cdf31SHeikki Krogerus return -ENOMEM; 276996b0830SDmitry Torokhov dst->pointer = dst_ptr; 277ed1cdf31SHeikki Krogerus } 2781f74d70fSDmitry Torokhov 279996b0830SDmitry Torokhov if (src->type == DEV_PROP_STRING) { 280996b0830SDmitry Torokhov nval = src->length / sizeof(const char *); 281996b0830SDmitry Torokhov if (!property_copy_string_array(dst_ptr, pointer, nval)) { 282996b0830SDmitry Torokhov if (!dst->is_inline) 283996b0830SDmitry Torokhov kfree(dst->pointer); 284ed1cdf31SHeikki Krogerus return -ENOMEM; 285996b0830SDmitry Torokhov } 286ed1cdf31SHeikki Krogerus } else { 287996b0830SDmitry Torokhov memcpy(dst_ptr, pointer, src->length); 288ed1cdf31SHeikki Krogerus } 289ed1cdf31SHeikki Krogerus 290ed1cdf31SHeikki Krogerus dst->length = src->length; 291ed1cdf31SHeikki Krogerus dst->type = src->type; 292ed1cdf31SHeikki Krogerus dst->name = kstrdup(src->name, GFP_KERNEL); 293996b0830SDmitry Torokhov if (!dst->name) { 294ed1cdf31SHeikki Krogerus property_entry_free_data(dst); 295ed1cdf31SHeikki Krogerus return -ENOMEM; 296ed1cdf31SHeikki Krogerus } 297ed1cdf31SHeikki Krogerus 298996b0830SDmitry Torokhov return 0; 299996b0830SDmitry Torokhov } 300996b0830SDmitry Torokhov 301ed1cdf31SHeikki Krogerus /** 302ed1cdf31SHeikki Krogerus * property_entries_dup - duplicate array of properties 303ed1cdf31SHeikki Krogerus * @properties: array of properties to copy 304ed1cdf31SHeikki Krogerus * 305ed1cdf31SHeikki Krogerus * This function creates a deep copy of the given NULL-terminated array 306ed1cdf31SHeikki Krogerus * of property entries. 307ed1cdf31SHeikki Krogerus */ 308ed1cdf31SHeikki Krogerus struct property_entry * 309ed1cdf31SHeikki Krogerus property_entries_dup(const struct property_entry *properties) 310ed1cdf31SHeikki Krogerus { 311ed1cdf31SHeikki Krogerus struct property_entry *p; 312ed1cdf31SHeikki Krogerus int i, n = 0; 313ed1cdf31SHeikki Krogerus int ret; 314ed1cdf31SHeikki Krogerus 315a7996986SHeikki Krogerus if (!properties) 316a7996986SHeikki Krogerus return NULL; 317a7996986SHeikki Krogerus 318ed1cdf31SHeikki Krogerus while (properties[n].name) 319ed1cdf31SHeikki Krogerus n++; 320ed1cdf31SHeikki Krogerus 321ed1cdf31SHeikki Krogerus p = kcalloc(n + 1, sizeof(*p), GFP_KERNEL); 322ed1cdf31SHeikki Krogerus if (!p) 323ed1cdf31SHeikki Krogerus return ERR_PTR(-ENOMEM); 324ed1cdf31SHeikki Krogerus 325ed1cdf31SHeikki Krogerus for (i = 0; i < n; i++) { 326ed1cdf31SHeikki Krogerus ret = property_entry_copy_data(&p[i], &properties[i]); 327ed1cdf31SHeikki Krogerus if (ret) { 328ed1cdf31SHeikki Krogerus while (--i >= 0) 329ed1cdf31SHeikki Krogerus property_entry_free_data(&p[i]); 330ed1cdf31SHeikki Krogerus kfree(p); 331ed1cdf31SHeikki Krogerus return ERR_PTR(ret); 332ed1cdf31SHeikki Krogerus } 333ed1cdf31SHeikki Krogerus } 334ed1cdf31SHeikki Krogerus 335ed1cdf31SHeikki Krogerus return p; 336ed1cdf31SHeikki Krogerus } 337ed1cdf31SHeikki Krogerus EXPORT_SYMBOL_GPL(property_entries_dup); 338ed1cdf31SHeikki Krogerus 339ed1cdf31SHeikki Krogerus /** 340ed1cdf31SHeikki Krogerus * property_entries_free - free previously allocated array of properties 341ed1cdf31SHeikki Krogerus * @properties: array of properties to destroy 342ed1cdf31SHeikki Krogerus * 343ed1cdf31SHeikki Krogerus * This function frees given NULL-terminated array of property entries, 344ed1cdf31SHeikki Krogerus * along with their data. 345ed1cdf31SHeikki Krogerus */ 346ed1cdf31SHeikki Krogerus void property_entries_free(const struct property_entry *properties) 347ed1cdf31SHeikki Krogerus { 348ed1cdf31SHeikki Krogerus const struct property_entry *p; 349ed1cdf31SHeikki Krogerus 350ed1cdf31SHeikki Krogerus if (!properties) 351ed1cdf31SHeikki Krogerus return; 352ed1cdf31SHeikki Krogerus 353ed1cdf31SHeikki Krogerus for (p = properties; p->name; p++) 354ed1cdf31SHeikki Krogerus property_entry_free_data(p); 355ed1cdf31SHeikki Krogerus 356ed1cdf31SHeikki Krogerus kfree(properties); 357ed1cdf31SHeikki Krogerus } 358ed1cdf31SHeikki Krogerus EXPORT_SYMBOL_GPL(property_entries_free); 359ed1cdf31SHeikki Krogerus 36059abd836SHeikki Krogerus /* -------------------------------------------------------------------------- */ 36159abd836SHeikki Krogerus /* fwnode operations */ 36259abd836SHeikki Krogerus 36359abd836SHeikki Krogerus static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode) 36459abd836SHeikki Krogerus { 36580488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 36659abd836SHeikki Krogerus 36759abd836SHeikki Krogerus kobject_get(&swnode->kobj); 36859abd836SHeikki Krogerus 36959abd836SHeikki Krogerus return &swnode->fwnode; 37059abd836SHeikki Krogerus } 37159abd836SHeikki Krogerus 37259abd836SHeikki Krogerus static void software_node_put(struct fwnode_handle *fwnode) 37359abd836SHeikki Krogerus { 37480488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 37559abd836SHeikki Krogerus 37659abd836SHeikki Krogerus kobject_put(&swnode->kobj); 37759abd836SHeikki Krogerus } 37859abd836SHeikki Krogerus 37959abd836SHeikki Krogerus static bool software_node_property_present(const struct fwnode_handle *fwnode, 38059abd836SHeikki Krogerus const char *propname) 38159abd836SHeikki Krogerus { 38280488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 38380488a6bSHeikki Krogerus 38480488a6bSHeikki Krogerus return !!property_entry_get(swnode->node->properties, propname); 38559abd836SHeikki Krogerus } 38659abd836SHeikki Krogerus 38759abd836SHeikki Krogerus static int software_node_read_int_array(const struct fwnode_handle *fwnode, 38859abd836SHeikki Krogerus const char *propname, 38959abd836SHeikki Krogerus unsigned int elem_size, void *val, 39059abd836SHeikki Krogerus size_t nval) 39159abd836SHeikki Krogerus { 39280488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 39359abd836SHeikki Krogerus 39480488a6bSHeikki Krogerus return property_entry_read_int_array(swnode->node->properties, propname, 39559abd836SHeikki Krogerus elem_size, val, nval); 39659abd836SHeikki Krogerus } 39759abd836SHeikki Krogerus 39859abd836SHeikki Krogerus static int software_node_read_string_array(const struct fwnode_handle *fwnode, 39959abd836SHeikki Krogerus const char *propname, 40059abd836SHeikki Krogerus const char **val, size_t nval) 40159abd836SHeikki Krogerus { 40280488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 40359abd836SHeikki Krogerus 40480488a6bSHeikki Krogerus return property_entry_read_string_array(swnode->node->properties, 40580488a6bSHeikki Krogerus propname, val, nval); 40659abd836SHeikki Krogerus } 40759abd836SHeikki Krogerus 408bc0500c1SSakari Ailus static const char * 409bc0500c1SSakari Ailus software_node_get_name(const struct fwnode_handle *fwnode) 410bc0500c1SSakari Ailus { 411bc0500c1SSakari Ailus const struct swnode *swnode = to_swnode(fwnode); 412bc0500c1SSakari Ailus 413bc0500c1SSakari Ailus if (!swnode) 414bc0500c1SSakari Ailus return "(null)"; 415bc0500c1SSakari Ailus 416bc0500c1SSakari Ailus return kobject_name(&swnode->kobj); 417bc0500c1SSakari Ailus } 418bc0500c1SSakari Ailus 419e7e242bcSSakari Ailus static const char * 420e7e242bcSSakari Ailus software_node_get_name_prefix(const struct fwnode_handle *fwnode) 421e7e242bcSSakari Ailus { 422e7e242bcSSakari Ailus struct fwnode_handle *parent; 423e7e242bcSSakari Ailus const char *prefix; 424e7e242bcSSakari Ailus 425e7e242bcSSakari Ailus parent = fwnode_get_parent(fwnode); 426e7e242bcSSakari Ailus if (!parent) 427e7e242bcSSakari Ailus return ""; 428e7e242bcSSakari Ailus 429e7e242bcSSakari Ailus /* Figure out the prefix from the parents. */ 430e7e242bcSSakari Ailus while (is_software_node(parent)) 431e7e242bcSSakari Ailus parent = fwnode_get_next_parent(parent); 432e7e242bcSSakari Ailus 433e7e242bcSSakari Ailus prefix = fwnode_get_name_prefix(parent); 434e7e242bcSSakari Ailus fwnode_handle_put(parent); 435e7e242bcSSakari Ailus 436e7e242bcSSakari Ailus /* Guess something if prefix was NULL. */ 437e7e242bcSSakari Ailus return prefix ?: "/"; 438e7e242bcSSakari Ailus } 439e7e242bcSSakari Ailus 4400e3edd94SYueHaibing static struct fwnode_handle * 44159abd836SHeikki Krogerus software_node_get_parent(const struct fwnode_handle *fwnode) 44259abd836SHeikki Krogerus { 44380488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 44459abd836SHeikki Krogerus 44551c100a6SSakari Ailus if (!swnode || !swnode->parent) 44651c100a6SSakari Ailus return NULL; 44751c100a6SSakari Ailus 44851c100a6SSakari Ailus return fwnode_handle_get(&swnode->parent->fwnode); 44959abd836SHeikki Krogerus } 45059abd836SHeikki Krogerus 4510e3edd94SYueHaibing static struct fwnode_handle * 45259abd836SHeikki Krogerus software_node_get_next_child(const struct fwnode_handle *fwnode, 45359abd836SHeikki Krogerus struct fwnode_handle *child) 45459abd836SHeikki Krogerus { 45580488a6bSHeikki Krogerus struct swnode *p = to_swnode(fwnode); 45680488a6bSHeikki Krogerus struct swnode *c = to_swnode(child); 45759abd836SHeikki Krogerus 4581d8f062eSColin Ian King if (!p || list_empty(&p->children) || 45959abd836SHeikki Krogerus (c && list_is_last(&c->entry, &p->children))) 46059abd836SHeikki Krogerus return NULL; 46159abd836SHeikki Krogerus 46259abd836SHeikki Krogerus if (c) 46359abd836SHeikki Krogerus c = list_next_entry(c, entry); 46459abd836SHeikki Krogerus else 46580488a6bSHeikki Krogerus c = list_first_entry(&p->children, struct swnode, entry); 46659abd836SHeikki Krogerus return &c->fwnode; 46759abd836SHeikki Krogerus } 46859abd836SHeikki Krogerus 46934479820SHeikki Krogerus static struct fwnode_handle * 47034479820SHeikki Krogerus software_node_get_named_child_node(const struct fwnode_handle *fwnode, 47134479820SHeikki Krogerus const char *childname) 47234479820SHeikki Krogerus { 47380488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 47480488a6bSHeikki Krogerus struct swnode *child; 47534479820SHeikki Krogerus 47634479820SHeikki Krogerus if (!swnode || list_empty(&swnode->children)) 47734479820SHeikki Krogerus return NULL; 47834479820SHeikki Krogerus 47934479820SHeikki Krogerus list_for_each_entry(child, &swnode->children, entry) { 480c959d0c2SHeikki Krogerus if (!strcmp(childname, kobject_name(&child->kobj))) { 48134479820SHeikki Krogerus kobject_get(&child->kobj); 48234479820SHeikki Krogerus return &child->fwnode; 48334479820SHeikki Krogerus } 48434479820SHeikki Krogerus } 48534479820SHeikki Krogerus return NULL; 48634479820SHeikki Krogerus } 48759abd836SHeikki Krogerus 488b06184acSHeikki Krogerus static int 489b06184acSHeikki Krogerus software_node_get_reference_args(const struct fwnode_handle *fwnode, 490b06184acSHeikki Krogerus const char *propname, const char *nargs_prop, 491b06184acSHeikki Krogerus unsigned int nargs, unsigned int index, 492b06184acSHeikki Krogerus struct fwnode_reference_args *args) 493b06184acSHeikki Krogerus { 494b06184acSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 495e64b674bSDmitry Torokhov const struct software_node_ref_args *ref_array; 496e933beddSDmitry Torokhov const struct software_node_ref_args *ref; 497b06184acSHeikki Krogerus const struct property_entry *prop; 498b06184acSHeikki Krogerus struct fwnode_handle *refnode; 499996b0830SDmitry Torokhov u32 nargs_prop_val; 500996b0830SDmitry Torokhov int error; 501b06184acSHeikki Krogerus int i; 502b06184acSHeikki Krogerus 503e64b674bSDmitry Torokhov if (!swnode) 504e64b674bSDmitry Torokhov return -ENOENT; 505e64b674bSDmitry Torokhov 506e64b674bSDmitry Torokhov prop = property_entry_get(swnode->node->properties, propname); 507e933beddSDmitry Torokhov if (!prop) 508e933beddSDmitry Torokhov return -ENOENT; 509e933beddSDmitry Torokhov 510e64b674bSDmitry Torokhov if (prop->type != DEV_PROP_REF) 511e64b674bSDmitry Torokhov return -EINVAL; 512e64b674bSDmitry Torokhov 513e64b674bSDmitry Torokhov /* 514e64b674bSDmitry Torokhov * We expect that references are never stored inline, even 515e64b674bSDmitry Torokhov * single ones, as they are too big. 516e64b674bSDmitry Torokhov */ 517e64b674bSDmitry Torokhov if (prop->is_inline) 518e64b674bSDmitry Torokhov return -EINVAL; 519e64b674bSDmitry Torokhov 520e933beddSDmitry Torokhov if (index * sizeof(*ref) >= prop->length) 521e64b674bSDmitry Torokhov return -ENOENT; 522e64b674bSDmitry Torokhov 523e64b674bSDmitry Torokhov ref_array = prop->pointer; 524e933beddSDmitry Torokhov ref = &ref_array[index]; 525b06184acSHeikki Krogerus 526e933beddSDmitry Torokhov refnode = software_node_fwnode(ref->node); 527b06184acSHeikki Krogerus if (!refnode) 528b06184acSHeikki Krogerus return -ENOENT; 529b06184acSHeikki Krogerus 530b06184acSHeikki Krogerus if (nargs_prop) { 531996b0830SDmitry Torokhov error = property_entry_read_int_array(swnode->node->properties, 532996b0830SDmitry Torokhov nargs_prop, sizeof(u32), 533996b0830SDmitry Torokhov &nargs_prop_val, 1); 534996b0830SDmitry Torokhov if (error) 535996b0830SDmitry Torokhov return error; 536b06184acSHeikki Krogerus 537996b0830SDmitry Torokhov nargs = nargs_prop_val; 538b06184acSHeikki Krogerus } 539b06184acSHeikki Krogerus 540b06184acSHeikki Krogerus if (nargs > NR_FWNODE_REFERENCE_ARGS) 541b06184acSHeikki Krogerus return -EINVAL; 542b06184acSHeikki Krogerus 543b06184acSHeikki Krogerus args->fwnode = software_node_get(refnode); 544b06184acSHeikki Krogerus args->nargs = nargs; 545b06184acSHeikki Krogerus 546b06184acSHeikki Krogerus for (i = 0; i < nargs; i++) 547e933beddSDmitry Torokhov args->args[i] = ref->args[i]; 548b06184acSHeikki Krogerus 549b06184acSHeikki Krogerus return 0; 550b06184acSHeikki Krogerus } 551b06184acSHeikki Krogerus 55259abd836SHeikki Krogerus static const struct fwnode_operations software_node_ops = { 55359abd836SHeikki Krogerus .get = software_node_get, 55459abd836SHeikki Krogerus .put = software_node_put, 55559abd836SHeikki Krogerus .property_present = software_node_property_present, 55659abd836SHeikki Krogerus .property_read_int_array = software_node_read_int_array, 55759abd836SHeikki Krogerus .property_read_string_array = software_node_read_string_array, 558bc0500c1SSakari Ailus .get_name = software_node_get_name, 559e7e242bcSSakari Ailus .get_name_prefix = software_node_get_name_prefix, 56059abd836SHeikki Krogerus .get_parent = software_node_get_parent, 56159abd836SHeikki Krogerus .get_next_child_node = software_node_get_next_child, 56234479820SHeikki Krogerus .get_named_child_node = software_node_get_named_child_node, 563b06184acSHeikki Krogerus .get_reference_args = software_node_get_reference_args 56459abd836SHeikki Krogerus }; 56559abd836SHeikki Krogerus 56659abd836SHeikki Krogerus /* -------------------------------------------------------------------------- */ 56759abd836SHeikki Krogerus 5681666faedSHeikki Krogerus /** 5691666faedSHeikki Krogerus * software_node_find_by_name - Find software node by name 5701666faedSHeikki Krogerus * @parent: Parent of the software node 5711666faedSHeikki Krogerus * @name: Name of the software node 5721666faedSHeikki Krogerus * 5731666faedSHeikki Krogerus * The function will find a node that is child of @parent and that is named 5741666faedSHeikki Krogerus * @name. If no node is found, the function returns NULL. 5751666faedSHeikki Krogerus * 5761666faedSHeikki Krogerus * NOTE: you will need to drop the reference with fwnode_handle_put() after use. 5771666faedSHeikki Krogerus */ 5781666faedSHeikki Krogerus const struct software_node * 5791666faedSHeikki Krogerus software_node_find_by_name(const struct software_node *parent, const char *name) 5801666faedSHeikki Krogerus { 581016049a8SHeikki Krogerus struct swnode *swnode = NULL; 5821666faedSHeikki Krogerus struct kobject *k; 5831666faedSHeikki Krogerus 5841666faedSHeikki Krogerus if (!name) 5851666faedSHeikki Krogerus return NULL; 5861666faedSHeikki Krogerus 5871666faedSHeikki Krogerus spin_lock(&swnode_kset->list_lock); 5881666faedSHeikki Krogerus 5891666faedSHeikki Krogerus list_for_each_entry(k, &swnode_kset->list, entry) { 5901666faedSHeikki Krogerus swnode = kobj_to_swnode(k); 5911666faedSHeikki Krogerus if (parent == swnode->node->parent && swnode->node->name && 5921666faedSHeikki Krogerus !strcmp(name, swnode->node->name)) { 5931666faedSHeikki Krogerus kobject_get(&swnode->kobj); 5941666faedSHeikki Krogerus break; 5951666faedSHeikki Krogerus } 5961666faedSHeikki Krogerus swnode = NULL; 5971666faedSHeikki Krogerus } 5981666faedSHeikki Krogerus 5991666faedSHeikki Krogerus spin_unlock(&swnode_kset->list_lock); 6001666faedSHeikki Krogerus 6011666faedSHeikki Krogerus return swnode ? swnode->node : NULL; 6021666faedSHeikki Krogerus } 6031666faedSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_find_by_name); 6041666faedSHeikki Krogerus 60559abd836SHeikki Krogerus static int 60680488a6bSHeikki Krogerus software_node_register_properties(struct software_node *node, 60759abd836SHeikki Krogerus const struct property_entry *properties) 60859abd836SHeikki Krogerus { 60959abd836SHeikki Krogerus struct property_entry *props; 61059abd836SHeikki Krogerus 61159abd836SHeikki Krogerus props = property_entries_dup(properties); 61259abd836SHeikki Krogerus if (IS_ERR(props)) 61359abd836SHeikki Krogerus return PTR_ERR(props); 61459abd836SHeikki Krogerus 61580488a6bSHeikki Krogerus node->properties = props; 61659abd836SHeikki Krogerus 61759abd836SHeikki Krogerus return 0; 61859abd836SHeikki Krogerus } 61959abd836SHeikki Krogerus 62059abd836SHeikki Krogerus static void software_node_release(struct kobject *kobj) 62159abd836SHeikki Krogerus { 62280488a6bSHeikki Krogerus struct swnode *swnode = kobj_to_swnode(kobj); 62359abd836SHeikki Krogerus 6247589238aSBrendan Higgins if (swnode->parent) { 6257589238aSBrendan Higgins ida_simple_remove(&swnode->parent->child_ids, swnode->id); 6267589238aSBrendan Higgins list_del(&swnode->entry); 6277589238aSBrendan Higgins } else { 6287589238aSBrendan Higgins ida_simple_remove(&swnode_root_ids, swnode->id); 6297589238aSBrendan Higgins } 6307589238aSBrendan Higgins 63180488a6bSHeikki Krogerus if (swnode->allocated) { 63280488a6bSHeikki Krogerus property_entries_free(swnode->node->properties); 63380488a6bSHeikki Krogerus kfree(swnode->node); 63480488a6bSHeikki Krogerus } 63559abd836SHeikki Krogerus ida_destroy(&swnode->child_ids); 63659abd836SHeikki Krogerus kfree(swnode); 63759abd836SHeikki Krogerus } 63859abd836SHeikki Krogerus 63959abd836SHeikki Krogerus static struct kobj_type software_node_type = { 64059abd836SHeikki Krogerus .release = software_node_release, 64159abd836SHeikki Krogerus .sysfs_ops = &kobj_sysfs_ops, 64259abd836SHeikki Krogerus }; 64359abd836SHeikki Krogerus 64480488a6bSHeikki Krogerus static struct fwnode_handle * 64580488a6bSHeikki Krogerus swnode_register(const struct software_node *node, struct swnode *parent, 64680488a6bSHeikki Krogerus unsigned int allocated) 64780488a6bSHeikki Krogerus { 64880488a6bSHeikki Krogerus struct swnode *swnode; 64980488a6bSHeikki Krogerus int ret; 65080488a6bSHeikki Krogerus 65180488a6bSHeikki Krogerus swnode = kzalloc(sizeof(*swnode), GFP_KERNEL); 65280488a6bSHeikki Krogerus if (!swnode) { 65380488a6bSHeikki Krogerus ret = -ENOMEM; 65480488a6bSHeikki Krogerus goto out_err; 65580488a6bSHeikki Krogerus } 65680488a6bSHeikki Krogerus 65780488a6bSHeikki Krogerus ret = ida_simple_get(parent ? &parent->child_ids : &swnode_root_ids, 65880488a6bSHeikki Krogerus 0, 0, GFP_KERNEL); 65980488a6bSHeikki Krogerus if (ret < 0) { 66080488a6bSHeikki Krogerus kfree(swnode); 66180488a6bSHeikki Krogerus goto out_err; 66280488a6bSHeikki Krogerus } 66380488a6bSHeikki Krogerus 66480488a6bSHeikki Krogerus swnode->id = ret; 66580488a6bSHeikki Krogerus swnode->node = node; 66680488a6bSHeikki Krogerus swnode->parent = parent; 66780488a6bSHeikki Krogerus swnode->allocated = allocated; 66880488a6bSHeikki Krogerus swnode->kobj.kset = swnode_kset; 66901bb86b3SSaravana Kannan fwnode_init(&swnode->fwnode, &software_node_ops); 67080488a6bSHeikki Krogerus 67180488a6bSHeikki Krogerus ida_init(&swnode->child_ids); 67280488a6bSHeikki Krogerus INIT_LIST_HEAD(&swnode->entry); 67380488a6bSHeikki Krogerus INIT_LIST_HEAD(&swnode->children); 67480488a6bSHeikki Krogerus 67580488a6bSHeikki Krogerus if (node->name) 67680488a6bSHeikki Krogerus ret = kobject_init_and_add(&swnode->kobj, &software_node_type, 67780488a6bSHeikki Krogerus parent ? &parent->kobj : NULL, 67880488a6bSHeikki Krogerus "%s", node->name); 67980488a6bSHeikki Krogerus else 68080488a6bSHeikki Krogerus ret = kobject_init_and_add(&swnode->kobj, &software_node_type, 68180488a6bSHeikki Krogerus parent ? &parent->kobj : NULL, 68280488a6bSHeikki Krogerus "node%d", swnode->id); 68380488a6bSHeikki Krogerus if (ret) { 68480488a6bSHeikki Krogerus kobject_put(&swnode->kobj); 68580488a6bSHeikki Krogerus return ERR_PTR(ret); 68680488a6bSHeikki Krogerus } 68780488a6bSHeikki Krogerus 68880488a6bSHeikki Krogerus if (parent) 68980488a6bSHeikki Krogerus list_add_tail(&swnode->entry, &parent->children); 69080488a6bSHeikki Krogerus 69180488a6bSHeikki Krogerus kobject_uevent(&swnode->kobj, KOBJ_ADD); 69280488a6bSHeikki Krogerus return &swnode->fwnode; 69380488a6bSHeikki Krogerus 69480488a6bSHeikki Krogerus out_err: 69580488a6bSHeikki Krogerus if (allocated) 69680488a6bSHeikki Krogerus property_entries_free(node->properties); 69780488a6bSHeikki Krogerus return ERR_PTR(ret); 69880488a6bSHeikki Krogerus } 69980488a6bSHeikki Krogerus 70080488a6bSHeikki Krogerus /** 70180488a6bSHeikki Krogerus * software_node_register_nodes - Register an array of software nodes 70280488a6bSHeikki Krogerus * @nodes: Zero terminated array of software nodes to be registered 70380488a6bSHeikki Krogerus * 70480488a6bSHeikki Krogerus * Register multiple software nodes at once. 70580488a6bSHeikki Krogerus */ 70680488a6bSHeikki Krogerus int software_node_register_nodes(const struct software_node *nodes) 70780488a6bSHeikki Krogerus { 70880488a6bSHeikki Krogerus int ret; 70980488a6bSHeikki Krogerus int i; 71080488a6bSHeikki Krogerus 71180488a6bSHeikki Krogerus for (i = 0; nodes[i].name; i++) { 71280488a6bSHeikki Krogerus ret = software_node_register(&nodes[i]); 71380488a6bSHeikki Krogerus if (ret) { 71480488a6bSHeikki Krogerus software_node_unregister_nodes(nodes); 71580488a6bSHeikki Krogerus return ret; 71680488a6bSHeikki Krogerus } 71780488a6bSHeikki Krogerus } 71880488a6bSHeikki Krogerus 71980488a6bSHeikki Krogerus return 0; 72080488a6bSHeikki Krogerus } 72180488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_register_nodes); 72280488a6bSHeikki Krogerus 72380488a6bSHeikki Krogerus /** 72480488a6bSHeikki Krogerus * software_node_unregister_nodes - Unregister an array of software nodes 72580488a6bSHeikki Krogerus * @nodes: Zero terminated array of software nodes to be unregistered 72680488a6bSHeikki Krogerus * 72780488a6bSHeikki Krogerus * Unregister multiple software nodes at once. 72846d26819SGreg Kroah-Hartman * 72946d26819SGreg Kroah-Hartman * NOTE: Be careful using this call if the nodes had parent pointers set up in 73046d26819SGreg Kroah-Hartman * them before registering. If so, it is wiser to remove the nodes 73146d26819SGreg Kroah-Hartman * individually, in the correct order (child before parent) instead of relying 73246d26819SGreg Kroah-Hartman * on the sequential order of the list of nodes in the array. 73380488a6bSHeikki Krogerus */ 73480488a6bSHeikki Krogerus void software_node_unregister_nodes(const struct software_node *nodes) 73580488a6bSHeikki Krogerus { 73680488a6bSHeikki Krogerus int i; 73780488a6bSHeikki Krogerus 73846d26819SGreg Kroah-Hartman for (i = 0; nodes[i].name; i++) 73946d26819SGreg Kroah-Hartman software_node_unregister(&nodes[i]); 74080488a6bSHeikki Krogerus } 74180488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_unregister_nodes); 74280488a6bSHeikki Krogerus 74380488a6bSHeikki Krogerus /** 74402094d54SAndy Shevchenko * software_node_register_node_group - Register a group of software nodes 74502094d54SAndy Shevchenko * @node_group: NULL terminated array of software node pointers to be registered 74602094d54SAndy Shevchenko * 74702094d54SAndy Shevchenko * Register multiple software nodes at once. 74802094d54SAndy Shevchenko */ 74902094d54SAndy Shevchenko int software_node_register_node_group(const struct software_node **node_group) 75002094d54SAndy Shevchenko { 75102094d54SAndy Shevchenko unsigned int i; 75202094d54SAndy Shevchenko int ret; 75302094d54SAndy Shevchenko 75402094d54SAndy Shevchenko if (!node_group) 75502094d54SAndy Shevchenko return 0; 75602094d54SAndy Shevchenko 75702094d54SAndy Shevchenko for (i = 0; node_group[i]; i++) { 75802094d54SAndy Shevchenko ret = software_node_register(node_group[i]); 75902094d54SAndy Shevchenko if (ret) { 76002094d54SAndy Shevchenko software_node_unregister_node_group(node_group); 76102094d54SAndy Shevchenko return ret; 76202094d54SAndy Shevchenko } 76302094d54SAndy Shevchenko } 76402094d54SAndy Shevchenko 76502094d54SAndy Shevchenko return 0; 76602094d54SAndy Shevchenko } 76702094d54SAndy Shevchenko EXPORT_SYMBOL_GPL(software_node_register_node_group); 76802094d54SAndy Shevchenko 76902094d54SAndy Shevchenko /** 77002094d54SAndy Shevchenko * software_node_unregister_node_group - Unregister a group of software nodes 77102094d54SAndy Shevchenko * @node_group: NULL terminated array of software node pointers to be unregistered 77202094d54SAndy Shevchenko * 77302094d54SAndy Shevchenko * Unregister multiple software nodes at once. 77402094d54SAndy Shevchenko */ 77502094d54SAndy Shevchenko void software_node_unregister_node_group(const struct software_node **node_group) 77602094d54SAndy Shevchenko { 77702094d54SAndy Shevchenko unsigned int i; 77802094d54SAndy Shevchenko 77902094d54SAndy Shevchenko if (!node_group) 78002094d54SAndy Shevchenko return; 78102094d54SAndy Shevchenko 7829dcbac84SAndy Shevchenko for (i = 0; node_group[i]; i++) 7839dcbac84SAndy Shevchenko software_node_unregister(node_group[i]); 78402094d54SAndy Shevchenko } 78502094d54SAndy Shevchenko EXPORT_SYMBOL_GPL(software_node_unregister_node_group); 78602094d54SAndy Shevchenko 78702094d54SAndy Shevchenko /** 78880488a6bSHeikki Krogerus * software_node_register - Register static software node 78980488a6bSHeikki Krogerus * @node: The software node to be registered 79080488a6bSHeikki Krogerus */ 79180488a6bSHeikki Krogerus int software_node_register(const struct software_node *node) 79280488a6bSHeikki Krogerus { 79380488a6bSHeikki Krogerus struct swnode *parent = software_node_to_swnode(node->parent); 79480488a6bSHeikki Krogerus 79580488a6bSHeikki Krogerus if (software_node_to_swnode(node)) 79680488a6bSHeikki Krogerus return -EEXIST; 79780488a6bSHeikki Krogerus 79880488a6bSHeikki Krogerus return PTR_ERR_OR_ZERO(swnode_register(node, parent, 0)); 79980488a6bSHeikki Krogerus } 80080488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_register); 80180488a6bSHeikki Krogerus 80246d26819SGreg Kroah-Hartman /** 80346d26819SGreg Kroah-Hartman * software_node_unregister - Unregister static software node 80446d26819SGreg Kroah-Hartman * @node: The software node to be unregistered 80546d26819SGreg Kroah-Hartman */ 80646d26819SGreg Kroah-Hartman void software_node_unregister(const struct software_node *node) 80746d26819SGreg Kroah-Hartman { 80846d26819SGreg Kroah-Hartman struct swnode *swnode; 80946d26819SGreg Kroah-Hartman 81046d26819SGreg Kroah-Hartman swnode = software_node_to_swnode(node); 81146d26819SGreg Kroah-Hartman if (swnode) 81246d26819SGreg Kroah-Hartman fwnode_remove_software_node(&swnode->fwnode); 81346d26819SGreg Kroah-Hartman } 81446d26819SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(software_node_unregister); 81546d26819SGreg Kroah-Hartman 81659abd836SHeikki Krogerus struct fwnode_handle * 81759abd836SHeikki Krogerus fwnode_create_software_node(const struct property_entry *properties, 81859abd836SHeikki Krogerus const struct fwnode_handle *parent) 81959abd836SHeikki Krogerus { 82080488a6bSHeikki Krogerus struct software_node *node; 82180488a6bSHeikki Krogerus struct swnode *p = NULL; 82259abd836SHeikki Krogerus int ret; 82359abd836SHeikki Krogerus 82459abd836SHeikki Krogerus if (parent) { 82559abd836SHeikki Krogerus if (IS_ERR(parent)) 82659abd836SHeikki Krogerus return ERR_CAST(parent); 82759abd836SHeikki Krogerus if (!is_software_node(parent)) 82859abd836SHeikki Krogerus return ERR_PTR(-EINVAL); 82980488a6bSHeikki Krogerus p = to_swnode(parent); 83059abd836SHeikki Krogerus } 83159abd836SHeikki Krogerus 83280488a6bSHeikki Krogerus node = kzalloc(sizeof(*node), GFP_KERNEL); 83380488a6bSHeikki Krogerus if (!node) 83459abd836SHeikki Krogerus return ERR_PTR(-ENOMEM); 83559abd836SHeikki Krogerus 83680488a6bSHeikki Krogerus ret = software_node_register_properties(node, properties); 83759abd836SHeikki Krogerus if (ret) { 83880488a6bSHeikki Krogerus kfree(node); 83959abd836SHeikki Krogerus return ERR_PTR(ret); 84059abd836SHeikki Krogerus } 84159abd836SHeikki Krogerus 84280488a6bSHeikki Krogerus node->parent = p ? p->node : NULL; 84359abd836SHeikki Krogerus 84480488a6bSHeikki Krogerus return swnode_register(node, p, 1); 84559abd836SHeikki Krogerus } 84659abd836SHeikki Krogerus EXPORT_SYMBOL_GPL(fwnode_create_software_node); 84759abd836SHeikki Krogerus 84859abd836SHeikki Krogerus void fwnode_remove_software_node(struct fwnode_handle *fwnode) 84959abd836SHeikki Krogerus { 85080488a6bSHeikki Krogerus struct swnode *swnode = to_swnode(fwnode); 85159abd836SHeikki Krogerus 85259abd836SHeikki Krogerus if (!swnode) 85359abd836SHeikki Krogerus return; 85459abd836SHeikki Krogerus 85559abd836SHeikki Krogerus kobject_put(&swnode->kobj); 85659abd836SHeikki Krogerus } 85759abd836SHeikki Krogerus EXPORT_SYMBOL_GPL(fwnode_remove_software_node); 85859abd836SHeikki Krogerus 859*e68d0119SHeikki Krogerus /** 860*e68d0119SHeikki Krogerus * device_add_software_node - Assign software node to a device 861*e68d0119SHeikki Krogerus * @dev: The device the software node is meant for. 862*e68d0119SHeikki Krogerus * @swnode: The software node. 863*e68d0119SHeikki Krogerus * 864*e68d0119SHeikki Krogerus * This function will register @swnode and make it the secondary firmware node 865*e68d0119SHeikki Krogerus * pointer of @dev. If @dev has no primary node, then @swnode will become the primary 866*e68d0119SHeikki Krogerus * node. 867*e68d0119SHeikki Krogerus */ 868*e68d0119SHeikki Krogerus int device_add_software_node(struct device *dev, const struct software_node *swnode) 869*e68d0119SHeikki Krogerus { 870*e68d0119SHeikki Krogerus int ret; 871*e68d0119SHeikki Krogerus 872*e68d0119SHeikki Krogerus /* Only one software node per device. */ 873*e68d0119SHeikki Krogerus if (dev_to_swnode(dev)) 874*e68d0119SHeikki Krogerus return -EBUSY; 875*e68d0119SHeikki Krogerus 876*e68d0119SHeikki Krogerus ret = software_node_register(swnode); 877*e68d0119SHeikki Krogerus if (ret) 878*e68d0119SHeikki Krogerus return ret; 879*e68d0119SHeikki Krogerus 880*e68d0119SHeikki Krogerus set_secondary_fwnode(dev, software_node_fwnode(swnode)); 881*e68d0119SHeikki Krogerus 882*e68d0119SHeikki Krogerus return 0; 883*e68d0119SHeikki Krogerus } 884*e68d0119SHeikki Krogerus EXPORT_SYMBOL_GPL(device_add_software_node); 885*e68d0119SHeikki Krogerus 886*e68d0119SHeikki Krogerus /** 887*e68d0119SHeikki Krogerus * device_remove_software_node - Remove device's software node 888*e68d0119SHeikki Krogerus * @dev: The device with the software node. 889*e68d0119SHeikki Krogerus * 890*e68d0119SHeikki Krogerus * This function will unregister the software node of @dev. 891*e68d0119SHeikki Krogerus */ 892*e68d0119SHeikki Krogerus void device_remove_software_node(struct device *dev) 893*e68d0119SHeikki Krogerus { 894*e68d0119SHeikki Krogerus struct swnode *swnode; 895*e68d0119SHeikki Krogerus 896*e68d0119SHeikki Krogerus swnode = dev_to_swnode(dev); 897*e68d0119SHeikki Krogerus if (!swnode) 898*e68d0119SHeikki Krogerus return; 899*e68d0119SHeikki Krogerus 900*e68d0119SHeikki Krogerus software_node_notify(dev, KOBJ_REMOVE); 901*e68d0119SHeikki Krogerus set_secondary_fwnode(dev, NULL); 902*e68d0119SHeikki Krogerus kobject_put(&swnode->kobj); 903*e68d0119SHeikki Krogerus } 904*e68d0119SHeikki Krogerus EXPORT_SYMBOL_GPL(device_remove_software_node); 905*e68d0119SHeikki Krogerus 90659abd836SHeikki Krogerus int software_node_notify(struct device *dev, unsigned long action) 90759abd836SHeikki Krogerus { 90880488a6bSHeikki Krogerus struct swnode *swnode; 90959abd836SHeikki Krogerus int ret; 91059abd836SHeikki Krogerus 911*e68d0119SHeikki Krogerus swnode = dev_to_swnode(dev); 912*e68d0119SHeikki Krogerus if (!swnode) 91359abd836SHeikki Krogerus return 0; 91459abd836SHeikki Krogerus 91559abd836SHeikki Krogerus switch (action) { 91659abd836SHeikki Krogerus case KOBJ_ADD: 91759abd836SHeikki Krogerus ret = sysfs_create_link(&dev->kobj, &swnode->kobj, 91859abd836SHeikki Krogerus "software_node"); 91959abd836SHeikki Krogerus if (ret) 92059abd836SHeikki Krogerus break; 92159abd836SHeikki Krogerus 92259abd836SHeikki Krogerus ret = sysfs_create_link(&swnode->kobj, &dev->kobj, 92359abd836SHeikki Krogerus dev_name(dev)); 92459abd836SHeikki Krogerus if (ret) { 92559abd836SHeikki Krogerus sysfs_remove_link(&dev->kobj, "software_node"); 92659abd836SHeikki Krogerus break; 92759abd836SHeikki Krogerus } 92859abd836SHeikki Krogerus kobject_get(&swnode->kobj); 92959abd836SHeikki Krogerus break; 93059abd836SHeikki Krogerus case KOBJ_REMOVE: 93159abd836SHeikki Krogerus sysfs_remove_link(&swnode->kobj, dev_name(dev)); 93259abd836SHeikki Krogerus sysfs_remove_link(&dev->kobj, "software_node"); 93359abd836SHeikki Krogerus kobject_put(&swnode->kobj); 93459abd836SHeikki Krogerus break; 93559abd836SHeikki Krogerus default: 93659abd836SHeikki Krogerus break; 93759abd836SHeikki Krogerus } 93859abd836SHeikki Krogerus 93959abd836SHeikki Krogerus return 0; 94059abd836SHeikki Krogerus } 94159abd836SHeikki Krogerus 94259abd836SHeikki Krogerus static int __init software_node_init(void) 94359abd836SHeikki Krogerus { 94459abd836SHeikki Krogerus swnode_kset = kset_create_and_add("software_nodes", NULL, kernel_kobj); 94559abd836SHeikki Krogerus if (!swnode_kset) 94659abd836SHeikki Krogerus return -ENOMEM; 94759abd836SHeikki Krogerus return 0; 94859abd836SHeikki Krogerus } 94959abd836SHeikki Krogerus postcore_initcall(software_node_init); 95059abd836SHeikki Krogerus 95159abd836SHeikki Krogerus static void __exit software_node_exit(void) 95259abd836SHeikki Krogerus { 95359abd836SHeikki Krogerus ida_destroy(&swnode_root_ids); 95459abd836SHeikki Krogerus kset_unregister(swnode_kset); 95559abd836SHeikki Krogerus } 95659abd836SHeikki Krogerus __exitcall(software_node_exit); 957