xref: /linux/drivers/base/swnode.c (revision 4dc3d612)
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 
9*4dc3d612SAndy Shevchenko #include <linux/container_of.h>
1059abd836SHeikki Krogerus #include <linux/device.h>
11*4dc3d612SAndy Shevchenko #include <linux/err.h>
12*4dc3d612SAndy Shevchenko #include <linux/export.h>
13*4dc3d612SAndy Shevchenko #include <linux/idr.h>
14*4dc3d612SAndy Shevchenko #include <linux/init.h>
15*4dc3d612SAndy Shevchenko #include <linux/kobject.h>
16*4dc3d612SAndy Shevchenko #include <linux/kstrtox.h>
17*4dc3d612SAndy Shevchenko #include <linux/list.h>
1859abd836SHeikki Krogerus #include <linux/property.h>
1959abd836SHeikki Krogerus #include <linux/slab.h>
20*4dc3d612SAndy Shevchenko #include <linux/spinlock.h>
21*4dc3d612SAndy Shevchenko #include <linux/string.h>
22*4dc3d612SAndy Shevchenko #include <linux/sysfs.h>
23*4dc3d612SAndy Shevchenko #include <linux/types.h>
2459abd836SHeikki Krogerus 
25384f5a85SRafael J. Wysocki #include "base.h"
26384f5a85SRafael J. Wysocki 
2780488a6bSHeikki Krogerus struct swnode {
2859abd836SHeikki Krogerus 	struct kobject kobj;
2959abd836SHeikki Krogerus 	struct fwnode_handle fwnode;
3080488a6bSHeikki Krogerus 	const struct software_node *node;
314a32e384SAndy Shevchenko 	int id;
3259abd836SHeikki Krogerus 
3359abd836SHeikki Krogerus 	/* hierarchy */
3459abd836SHeikki Krogerus 	struct ida child_ids;
3559abd836SHeikki Krogerus 	struct list_head entry;
3659abd836SHeikki Krogerus 	struct list_head children;
3780488a6bSHeikki Krogerus 	struct swnode *parent;
3859abd836SHeikki Krogerus 
3980488a6bSHeikki Krogerus 	unsigned int allocated:1;
40151f6ff7SHeikki Krogerus 	unsigned int managed:1;
4159abd836SHeikki Krogerus };
4259abd836SHeikki Krogerus 
4359abd836SHeikki Krogerus static DEFINE_IDA(swnode_root_ids);
4459abd836SHeikki Krogerus static struct kset *swnode_kset;
4559abd836SHeikki Krogerus 
4680488a6bSHeikki Krogerus #define kobj_to_swnode(_kobj_) container_of(_kobj_, struct swnode, kobj)
4759abd836SHeikki Krogerus 
4859abd836SHeikki Krogerus static const struct fwnode_operations software_node_ops;
4959abd836SHeikki Krogerus 
is_software_node(const struct fwnode_handle * fwnode)5059abd836SHeikki Krogerus bool is_software_node(const struct fwnode_handle *fwnode)
5159abd836SHeikki Krogerus {
5259abd836SHeikki Krogerus 	return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &software_node_ops;
5359abd836SHeikki Krogerus }
5480488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(is_software_node);
5559abd836SHeikki Krogerus 
5680488a6bSHeikki Krogerus #define to_swnode(__fwnode)						\
5759abd836SHeikki Krogerus 	({								\
5880488a6bSHeikki Krogerus 		typeof(__fwnode) __to_swnode_fwnode = __fwnode;		\
5959abd836SHeikki Krogerus 									\
6080488a6bSHeikki Krogerus 		is_software_node(__to_swnode_fwnode) ?			\
6180488a6bSHeikki Krogerus 			container_of(__to_swnode_fwnode,		\
6280488a6bSHeikki Krogerus 				     struct swnode, fwnode) : NULL;	\
6359abd836SHeikki Krogerus 	})
6459abd836SHeikki Krogerus 
dev_to_swnode(struct device * dev)65e68d0119SHeikki Krogerus static inline struct swnode *dev_to_swnode(struct device *dev)
66e68d0119SHeikki Krogerus {
67e68d0119SHeikki Krogerus 	struct fwnode_handle *fwnode = dev_fwnode(dev);
68e68d0119SHeikki Krogerus 
69e68d0119SHeikki Krogerus 	if (!fwnode)
70e68d0119SHeikki Krogerus 		return NULL;
71e68d0119SHeikki Krogerus 
72e68d0119SHeikki Krogerus 	if (!is_software_node(fwnode))
73e68d0119SHeikki Krogerus 		fwnode = fwnode->secondary;
74e68d0119SHeikki Krogerus 
75e68d0119SHeikki Krogerus 	return to_swnode(fwnode);
76e68d0119SHeikki Krogerus }
77e68d0119SHeikki Krogerus 
7880488a6bSHeikki Krogerus static struct swnode *
software_node_to_swnode(const struct software_node * node)7980488a6bSHeikki Krogerus software_node_to_swnode(const struct software_node *node)
8080488a6bSHeikki Krogerus {
8161636873SHeikki Krogerus 	struct swnode *swnode = NULL;
8280488a6bSHeikki Krogerus 	struct kobject *k;
8380488a6bSHeikki Krogerus 
8480488a6bSHeikki Krogerus 	if (!node)
8580488a6bSHeikki Krogerus 		return NULL;
8680488a6bSHeikki Krogerus 
8780488a6bSHeikki Krogerus 	spin_lock(&swnode_kset->list_lock);
8880488a6bSHeikki Krogerus 
8980488a6bSHeikki Krogerus 	list_for_each_entry(k, &swnode_kset->list, entry) {
9080488a6bSHeikki Krogerus 		swnode = kobj_to_swnode(k);
9180488a6bSHeikki Krogerus 		if (swnode->node == node)
9280488a6bSHeikki Krogerus 			break;
9380488a6bSHeikki Krogerus 		swnode = NULL;
9480488a6bSHeikki Krogerus 	}
9580488a6bSHeikki Krogerus 
9680488a6bSHeikki Krogerus 	spin_unlock(&swnode_kset->list_lock);
9780488a6bSHeikki Krogerus 
9880488a6bSHeikki Krogerus 	return swnode;
9980488a6bSHeikki Krogerus }
10080488a6bSHeikki Krogerus 
to_software_node(const struct fwnode_handle * fwnode)10156c9aa07SSakari Ailus const struct software_node *to_software_node(const struct fwnode_handle *fwnode)
10280488a6bSHeikki Krogerus {
10356c9aa07SSakari Ailus 	const struct swnode *swnode = to_swnode(fwnode);
10480488a6bSHeikki Krogerus 
10580488a6bSHeikki Krogerus 	return swnode ? swnode->node : NULL;
10680488a6bSHeikki Krogerus }
10780488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(to_software_node);
10880488a6bSHeikki Krogerus 
software_node_fwnode(const struct software_node * node)10980488a6bSHeikki Krogerus struct fwnode_handle *software_node_fwnode(const struct software_node *node)
11080488a6bSHeikki Krogerus {
11180488a6bSHeikki Krogerus 	struct swnode *swnode = software_node_to_swnode(node);
11280488a6bSHeikki Krogerus 
11380488a6bSHeikki Krogerus 	return swnode ? &swnode->fwnode : NULL;
11480488a6bSHeikki Krogerus }
11580488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_fwnode);
11680488a6bSHeikki Krogerus 
11759abd836SHeikki Krogerus /* -------------------------------------------------------------------------- */
11859abd836SHeikki Krogerus /* property_entry processing */
11959abd836SHeikki Krogerus 
12059abd836SHeikki Krogerus static const struct property_entry *
property_entry_get(const struct property_entry * prop,const char * name)12159abd836SHeikki Krogerus property_entry_get(const struct property_entry *prop, const char *name)
12259abd836SHeikki Krogerus {
12359abd836SHeikki Krogerus 	if (!prop)
12459abd836SHeikki Krogerus 		return NULL;
12559abd836SHeikki Krogerus 
12659abd836SHeikki Krogerus 	for (; prop->name; prop++)
12759abd836SHeikki Krogerus 		if (!strcmp(name, prop->name))
12859abd836SHeikki Krogerus 			return prop;
12959abd836SHeikki Krogerus 
13059abd836SHeikki Krogerus 	return NULL;
13159abd836SHeikki Krogerus }
13259abd836SHeikki Krogerus 
property_get_pointer(const struct property_entry * prop)13359abd836SHeikki Krogerus static const void *property_get_pointer(const struct property_entry *prop)
13459abd836SHeikki Krogerus {
1351f74d70fSDmitry Torokhov 	if (!prop->length)
13659abd836SHeikki Krogerus 		return NULL;
1371f74d70fSDmitry Torokhov 
138e6bff466SDmitry Torokhov 	return prop->is_inline ? &prop->value : prop->pointer;
13959abd836SHeikki Krogerus }
14059abd836SHeikki Krogerus 
property_entry_find(const struct property_entry * props,const char * propname,size_t length)14159abd836SHeikki Krogerus static const void *property_entry_find(const struct property_entry *props,
14259abd836SHeikki Krogerus 				       const char *propname, size_t length)
14359abd836SHeikki Krogerus {
14459abd836SHeikki Krogerus 	const struct property_entry *prop;
14559abd836SHeikki Krogerus 	const void *pointer;
14659abd836SHeikki Krogerus 
14759abd836SHeikki Krogerus 	prop = property_entry_get(props, propname);
14859abd836SHeikki Krogerus 	if (!prop)
14959abd836SHeikki Krogerus 		return ERR_PTR(-EINVAL);
15059abd836SHeikki Krogerus 	pointer = property_get_pointer(prop);
15159abd836SHeikki Krogerus 	if (!pointer)
15259abd836SHeikki Krogerus 		return ERR_PTR(-ENODATA);
15359abd836SHeikki Krogerus 	if (length > prop->length)
15459abd836SHeikki Krogerus 		return ERR_PTR(-EOVERFLOW);
15559abd836SHeikki Krogerus 	return pointer;
15659abd836SHeikki Krogerus }
15759abd836SHeikki Krogerus 
15859abd836SHeikki Krogerus static int
property_entry_count_elems_of_size(const struct property_entry * props,const char * propname,size_t length)15959abd836SHeikki Krogerus property_entry_count_elems_of_size(const struct property_entry *props,
16059abd836SHeikki Krogerus 				   const char *propname, size_t length)
16159abd836SHeikki Krogerus {
16259abd836SHeikki Krogerus 	const struct property_entry *prop;
16359abd836SHeikki Krogerus 
16459abd836SHeikki Krogerus 	prop = property_entry_get(props, propname);
16559abd836SHeikki Krogerus 	if (!prop)
16659abd836SHeikki Krogerus 		return -EINVAL;
16759abd836SHeikki Krogerus 
16859abd836SHeikki Krogerus 	return prop->length / length;
16959abd836SHeikki Krogerus }
17059abd836SHeikki Krogerus 
property_entry_read_int_array(const struct property_entry * props,const char * name,unsigned int elem_size,void * val,size_t nval)17159abd836SHeikki Krogerus static int property_entry_read_int_array(const struct property_entry *props,
17259abd836SHeikki Krogerus 					 const char *name,
17359abd836SHeikki Krogerus 					 unsigned int elem_size, void *val,
17459abd836SHeikki Krogerus 					 size_t nval)
17559abd836SHeikki Krogerus {
1765236f5feSDmitry Torokhov 	const void *pointer;
1775236f5feSDmitry Torokhov 	size_t length;
1785236f5feSDmitry Torokhov 
17959abd836SHeikki Krogerus 	if (!val)
18059abd836SHeikki Krogerus 		return property_entry_count_elems_of_size(props, name,
18159abd836SHeikki Krogerus 							  elem_size);
18259abd836SHeikki Krogerus 
1835236f5feSDmitry Torokhov 	if (!is_power_of_2(elem_size) || elem_size > sizeof(u64))
18459abd836SHeikki Krogerus 		return -ENXIO;
1855236f5feSDmitry Torokhov 
1865236f5feSDmitry Torokhov 	length = nval * elem_size;
1875236f5feSDmitry Torokhov 
1885236f5feSDmitry Torokhov 	pointer = property_entry_find(props, name, length);
1895236f5feSDmitry Torokhov 	if (IS_ERR(pointer))
1905236f5feSDmitry Torokhov 		return PTR_ERR(pointer);
1915236f5feSDmitry Torokhov 
1925236f5feSDmitry Torokhov 	memcpy(val, pointer, length);
1935236f5feSDmitry Torokhov 	return 0;
19459abd836SHeikki Krogerus }
19559abd836SHeikki Krogerus 
property_entry_read_string_array(const struct property_entry * props,const char * propname,const char ** strings,size_t nval)19659abd836SHeikki Krogerus static int property_entry_read_string_array(const struct property_entry *props,
19759abd836SHeikki Krogerus 					    const char *propname,
19859abd836SHeikki Krogerus 					    const char **strings, size_t nval)
19959abd836SHeikki Krogerus {
20059abd836SHeikki Krogerus 	const void *pointer;
2011afc1403SDmitry Torokhov 	size_t length;
2021afc1403SDmitry Torokhov 	int array_len;
20359abd836SHeikki Krogerus 
20459abd836SHeikki Krogerus 	/* Find out the array length. */
20559abd836SHeikki Krogerus 	array_len = property_entry_count_elems_of_size(props, propname,
20659abd836SHeikki Krogerus 						       sizeof(const char *));
2071afc1403SDmitry Torokhov 	if (array_len < 0)
2081afc1403SDmitry Torokhov 		return array_len;
20959abd836SHeikki Krogerus 
21059abd836SHeikki Krogerus 	/* Return how many there are if strings is NULL. */
21159abd836SHeikki Krogerus 	if (!strings)
21259abd836SHeikki Krogerus 		return array_len;
21359abd836SHeikki Krogerus 
2141afc1403SDmitry Torokhov 	array_len = min_t(size_t, nval, array_len);
21559abd836SHeikki Krogerus 	length = array_len * sizeof(*strings);
21659abd836SHeikki Krogerus 
21759abd836SHeikki Krogerus 	pointer = property_entry_find(props, propname, length);
21859abd836SHeikki Krogerus 	if (IS_ERR(pointer))
21959abd836SHeikki Krogerus 		return PTR_ERR(pointer);
22059abd836SHeikki Krogerus 
22159abd836SHeikki Krogerus 	memcpy(strings, pointer, length);
22259abd836SHeikki Krogerus 
22359abd836SHeikki Krogerus 	return array_len;
22459abd836SHeikki Krogerus }
22559abd836SHeikki Krogerus 
property_entry_free_data(const struct property_entry * p)226ed1cdf31SHeikki Krogerus static void property_entry_free_data(const struct property_entry *p)
227ed1cdf31SHeikki Krogerus {
2281f74d70fSDmitry Torokhov 	const char * const *src_str;
229ed1cdf31SHeikki Krogerus 	size_t i, nval;
230ed1cdf31SHeikki Krogerus 
231996b0830SDmitry Torokhov 	if (p->type == DEV_PROP_STRING) {
232996b0830SDmitry Torokhov 		src_str = property_get_pointer(p);
233996b0830SDmitry Torokhov 		nval = p->length / sizeof(*src_str);
234ed1cdf31SHeikki Krogerus 		for (i = 0; i < nval; i++)
2351f74d70fSDmitry Torokhov 			kfree(src_str[i]);
236ed1cdf31SHeikki Krogerus 	}
237996b0830SDmitry Torokhov 
238996b0830SDmitry Torokhov 	if (!p->is_inline)
239996b0830SDmitry Torokhov 		kfree(p->pointer);
240996b0830SDmitry Torokhov 
241ed1cdf31SHeikki Krogerus 	kfree(p->name);
242ed1cdf31SHeikki Krogerus }
243ed1cdf31SHeikki Krogerus 
property_copy_string_array(const char ** dst_ptr,const char * const * src_ptr,size_t nval)244996b0830SDmitry Torokhov static bool property_copy_string_array(const char **dst_ptr,
245996b0830SDmitry Torokhov 				       const char * const *src_ptr,
246996b0830SDmitry Torokhov 				       size_t nval)
247ed1cdf31SHeikki Krogerus {
248ed1cdf31SHeikki Krogerus 	int i;
249ed1cdf31SHeikki Krogerus 
250ed1cdf31SHeikki Krogerus 	for (i = 0; i < nval; i++) {
251996b0830SDmitry Torokhov 		dst_ptr[i] = kstrdup(src_ptr[i], GFP_KERNEL);
252996b0830SDmitry Torokhov 		if (!dst_ptr[i] && src_ptr[i]) {
253ed1cdf31SHeikki Krogerus 			while (--i >= 0)
254996b0830SDmitry Torokhov 				kfree(dst_ptr[i]);
255996b0830SDmitry Torokhov 			return false;
256ed1cdf31SHeikki Krogerus 		}
257ed1cdf31SHeikki Krogerus 	}
258ed1cdf31SHeikki Krogerus 
259996b0830SDmitry Torokhov 	return true;
260ed1cdf31SHeikki Krogerus }
261ed1cdf31SHeikki Krogerus 
property_entry_copy_data(struct property_entry * dst,const struct property_entry * src)262ed1cdf31SHeikki Krogerus static int property_entry_copy_data(struct property_entry *dst,
263ed1cdf31SHeikki Krogerus 				    const struct property_entry *src)
264ed1cdf31SHeikki Krogerus {
265ed1cdf31SHeikki Krogerus 	const void *pointer = property_get_pointer(src);
266996b0830SDmitry Torokhov 	void *dst_ptr;
267996b0830SDmitry Torokhov 	size_t nval;
268ed1cdf31SHeikki Krogerus 
269996b0830SDmitry Torokhov 	/*
270996b0830SDmitry Torokhov 	 * Properties with no data should not be marked as stored
271996b0830SDmitry Torokhov 	 * out of line.
272996b0830SDmitry Torokhov 	 */
273996b0830SDmitry Torokhov 	if (!src->is_inline && !src->length)
274ed1cdf31SHeikki Krogerus 		return -ENODATA;
275ed1cdf31SHeikki Krogerus 
276e64b674bSDmitry Torokhov 	/*
277e64b674bSDmitry Torokhov 	 * Reference properties are never stored inline as
278e64b674bSDmitry Torokhov 	 * they are too big.
279e64b674bSDmitry Torokhov 	 */
280e64b674bSDmitry Torokhov 	if (src->type == DEV_PROP_REF && src->is_inline)
281e64b674bSDmitry Torokhov 		return -EINVAL;
282e64b674bSDmitry Torokhov 
283996b0830SDmitry Torokhov 	if (src->length <= sizeof(dst->value)) {
284996b0830SDmitry Torokhov 		dst_ptr = &dst->value;
285996b0830SDmitry Torokhov 		dst->is_inline = true;
286ed1cdf31SHeikki Krogerus 	} else {
287996b0830SDmitry Torokhov 		dst_ptr = kmalloc(src->length, GFP_KERNEL);
288996b0830SDmitry Torokhov 		if (!dst_ptr)
289ed1cdf31SHeikki Krogerus 			return -ENOMEM;
290996b0830SDmitry Torokhov 		dst->pointer = dst_ptr;
291ed1cdf31SHeikki Krogerus 	}
2921f74d70fSDmitry Torokhov 
293996b0830SDmitry Torokhov 	if (src->type == DEV_PROP_STRING) {
294996b0830SDmitry Torokhov 		nval = src->length / sizeof(const char *);
295996b0830SDmitry Torokhov 		if (!property_copy_string_array(dst_ptr, pointer, nval)) {
296996b0830SDmitry Torokhov 			if (!dst->is_inline)
297996b0830SDmitry Torokhov 				kfree(dst->pointer);
298ed1cdf31SHeikki Krogerus 			return -ENOMEM;
299996b0830SDmitry Torokhov 		}
300ed1cdf31SHeikki Krogerus 	} else {
301996b0830SDmitry Torokhov 		memcpy(dst_ptr, pointer, src->length);
302ed1cdf31SHeikki Krogerus 	}
303ed1cdf31SHeikki Krogerus 
304ed1cdf31SHeikki Krogerus 	dst->length = src->length;
305ed1cdf31SHeikki Krogerus 	dst->type = src->type;
306ed1cdf31SHeikki Krogerus 	dst->name = kstrdup(src->name, GFP_KERNEL);
307996b0830SDmitry Torokhov 	if (!dst->name) {
308ed1cdf31SHeikki Krogerus 		property_entry_free_data(dst);
309ed1cdf31SHeikki Krogerus 		return -ENOMEM;
310ed1cdf31SHeikki Krogerus 	}
311ed1cdf31SHeikki Krogerus 
312996b0830SDmitry Torokhov 	return 0;
313996b0830SDmitry Torokhov }
314996b0830SDmitry Torokhov 
315ed1cdf31SHeikki Krogerus /**
316ed1cdf31SHeikki Krogerus  * property_entries_dup - duplicate array of properties
317ed1cdf31SHeikki Krogerus  * @properties: array of properties to copy
318ed1cdf31SHeikki Krogerus  *
319ed1cdf31SHeikki Krogerus  * This function creates a deep copy of the given NULL-terminated array
320ed1cdf31SHeikki Krogerus  * of property entries.
321ed1cdf31SHeikki Krogerus  */
322ed1cdf31SHeikki Krogerus struct property_entry *
property_entries_dup(const struct property_entry * properties)323ed1cdf31SHeikki Krogerus property_entries_dup(const struct property_entry *properties)
324ed1cdf31SHeikki Krogerus {
325ed1cdf31SHeikki Krogerus 	struct property_entry *p;
326ed1cdf31SHeikki Krogerus 	int i, n = 0;
327ed1cdf31SHeikki Krogerus 	int ret;
328ed1cdf31SHeikki Krogerus 
329a7996986SHeikki Krogerus 	if (!properties)
330a7996986SHeikki Krogerus 		return NULL;
331a7996986SHeikki Krogerus 
332ed1cdf31SHeikki Krogerus 	while (properties[n].name)
333ed1cdf31SHeikki Krogerus 		n++;
334ed1cdf31SHeikki Krogerus 
335ed1cdf31SHeikki Krogerus 	p = kcalloc(n + 1, sizeof(*p), GFP_KERNEL);
336ed1cdf31SHeikki Krogerus 	if (!p)
337ed1cdf31SHeikki Krogerus 		return ERR_PTR(-ENOMEM);
338ed1cdf31SHeikki Krogerus 
339ed1cdf31SHeikki Krogerus 	for (i = 0; i < n; i++) {
340ed1cdf31SHeikki Krogerus 		ret = property_entry_copy_data(&p[i], &properties[i]);
341ed1cdf31SHeikki Krogerus 		if (ret) {
342ed1cdf31SHeikki Krogerus 			while (--i >= 0)
343ed1cdf31SHeikki Krogerus 				property_entry_free_data(&p[i]);
344ed1cdf31SHeikki Krogerus 			kfree(p);
345ed1cdf31SHeikki Krogerus 			return ERR_PTR(ret);
346ed1cdf31SHeikki Krogerus 		}
347ed1cdf31SHeikki Krogerus 	}
348ed1cdf31SHeikki Krogerus 
349ed1cdf31SHeikki Krogerus 	return p;
350ed1cdf31SHeikki Krogerus }
351ed1cdf31SHeikki Krogerus EXPORT_SYMBOL_GPL(property_entries_dup);
352ed1cdf31SHeikki Krogerus 
353ed1cdf31SHeikki Krogerus /**
354ed1cdf31SHeikki Krogerus  * property_entries_free - free previously allocated array of properties
355ed1cdf31SHeikki Krogerus  * @properties: array of properties to destroy
356ed1cdf31SHeikki Krogerus  *
357ed1cdf31SHeikki Krogerus  * This function frees given NULL-terminated array of property entries,
358ed1cdf31SHeikki Krogerus  * along with their data.
359ed1cdf31SHeikki Krogerus  */
property_entries_free(const struct property_entry * properties)360ed1cdf31SHeikki Krogerus void property_entries_free(const struct property_entry *properties)
361ed1cdf31SHeikki Krogerus {
362ed1cdf31SHeikki Krogerus 	const struct property_entry *p;
363ed1cdf31SHeikki Krogerus 
364ed1cdf31SHeikki Krogerus 	if (!properties)
365ed1cdf31SHeikki Krogerus 		return;
366ed1cdf31SHeikki Krogerus 
367ed1cdf31SHeikki Krogerus 	for (p = properties; p->name; p++)
368ed1cdf31SHeikki Krogerus 		property_entry_free_data(p);
369ed1cdf31SHeikki Krogerus 
370ed1cdf31SHeikki Krogerus 	kfree(properties);
371ed1cdf31SHeikki Krogerus }
372ed1cdf31SHeikki Krogerus EXPORT_SYMBOL_GPL(property_entries_free);
373ed1cdf31SHeikki Krogerus 
37459abd836SHeikki Krogerus /* -------------------------------------------------------------------------- */
37559abd836SHeikki Krogerus /* fwnode operations */
37659abd836SHeikki Krogerus 
software_node_get(struct fwnode_handle * fwnode)37759abd836SHeikki Krogerus static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode)
37859abd836SHeikki Krogerus {
37980488a6bSHeikki Krogerus 	struct swnode *swnode = to_swnode(fwnode);
38059abd836SHeikki Krogerus 
38159abd836SHeikki Krogerus 	kobject_get(&swnode->kobj);
38259abd836SHeikki Krogerus 
38359abd836SHeikki Krogerus 	return &swnode->fwnode;
38459abd836SHeikki Krogerus }
38559abd836SHeikki Krogerus 
software_node_put(struct fwnode_handle * fwnode)38659abd836SHeikki Krogerus static void software_node_put(struct fwnode_handle *fwnode)
38759abd836SHeikki Krogerus {
38880488a6bSHeikki Krogerus 	struct swnode *swnode = to_swnode(fwnode);
38959abd836SHeikki Krogerus 
39059abd836SHeikki Krogerus 	kobject_put(&swnode->kobj);
39159abd836SHeikki Krogerus }
39259abd836SHeikki Krogerus 
software_node_property_present(const struct fwnode_handle * fwnode,const char * propname)39359abd836SHeikki Krogerus static bool software_node_property_present(const struct fwnode_handle *fwnode,
39459abd836SHeikki Krogerus 					   const char *propname)
39559abd836SHeikki Krogerus {
39680488a6bSHeikki Krogerus 	struct swnode *swnode = to_swnode(fwnode);
39780488a6bSHeikki Krogerus 
39880488a6bSHeikki Krogerus 	return !!property_entry_get(swnode->node->properties, propname);
39959abd836SHeikki Krogerus }
40059abd836SHeikki Krogerus 
software_node_read_int_array(const struct fwnode_handle * fwnode,const char * propname,unsigned int elem_size,void * val,size_t nval)40159abd836SHeikki Krogerus static int software_node_read_int_array(const struct fwnode_handle *fwnode,
40259abd836SHeikki Krogerus 					const char *propname,
40359abd836SHeikki Krogerus 					unsigned int elem_size, void *val,
40459abd836SHeikki Krogerus 					size_t nval)
40559abd836SHeikki Krogerus {
40680488a6bSHeikki Krogerus 	struct swnode *swnode = to_swnode(fwnode);
40759abd836SHeikki Krogerus 
40880488a6bSHeikki Krogerus 	return property_entry_read_int_array(swnode->node->properties, propname,
40959abd836SHeikki Krogerus 					     elem_size, val, nval);
41059abd836SHeikki Krogerus }
41159abd836SHeikki Krogerus 
software_node_read_string_array(const struct fwnode_handle * fwnode,const char * propname,const char ** val,size_t nval)41259abd836SHeikki Krogerus static int software_node_read_string_array(const struct fwnode_handle *fwnode,
41359abd836SHeikki Krogerus 					   const char *propname,
41459abd836SHeikki Krogerus 					   const char **val, size_t nval)
41559abd836SHeikki Krogerus {
41680488a6bSHeikki Krogerus 	struct swnode *swnode = to_swnode(fwnode);
41759abd836SHeikki Krogerus 
41880488a6bSHeikki Krogerus 	return property_entry_read_string_array(swnode->node->properties,
41980488a6bSHeikki Krogerus 						propname, val, nval);
42059abd836SHeikki Krogerus }
42159abd836SHeikki Krogerus 
422bc0500c1SSakari Ailus static const char *
software_node_get_name(const struct fwnode_handle * fwnode)423bc0500c1SSakari Ailus software_node_get_name(const struct fwnode_handle *fwnode)
424bc0500c1SSakari Ailus {
425bc0500c1SSakari Ailus 	const struct swnode *swnode = to_swnode(fwnode);
426bc0500c1SSakari Ailus 
427bc0500c1SSakari Ailus 	return kobject_name(&swnode->kobj);
428bc0500c1SSakari Ailus }
429bc0500c1SSakari Ailus 
430e7e242bcSSakari Ailus static const char *
software_node_get_name_prefix(const struct fwnode_handle * fwnode)431e7e242bcSSakari Ailus software_node_get_name_prefix(const struct fwnode_handle *fwnode)
432e7e242bcSSakari Ailus {
433e7e242bcSSakari Ailus 	struct fwnode_handle *parent;
434e7e242bcSSakari Ailus 	const char *prefix;
435e7e242bcSSakari Ailus 
436e7e242bcSSakari Ailus 	parent = fwnode_get_parent(fwnode);
437e7e242bcSSakari Ailus 	if (!parent)
438e7e242bcSSakari Ailus 		return "";
439e7e242bcSSakari Ailus 
440e7e242bcSSakari Ailus 	/* Figure out the prefix from the parents. */
441e7e242bcSSakari Ailus 	while (is_software_node(parent))
442e7e242bcSSakari Ailus 		parent = fwnode_get_next_parent(parent);
443e7e242bcSSakari Ailus 
444e7e242bcSSakari Ailus 	prefix = fwnode_get_name_prefix(parent);
445e7e242bcSSakari Ailus 	fwnode_handle_put(parent);
446e7e242bcSSakari Ailus 
447e7e242bcSSakari Ailus 	/* Guess something if prefix was NULL. */
448e7e242bcSSakari Ailus 	return prefix ?: "/";
449e7e242bcSSakari Ailus }
450e7e242bcSSakari Ailus 
4510e3edd94SYueHaibing static struct fwnode_handle *
software_node_get_parent(const struct fwnode_handle * fwnode)45259abd836SHeikki Krogerus software_node_get_parent(const struct fwnode_handle *fwnode)
45359abd836SHeikki Krogerus {
45480488a6bSHeikki Krogerus 	struct swnode *swnode = to_swnode(fwnode);
45559abd836SHeikki Krogerus 
45651c100a6SSakari Ailus 	if (!swnode || !swnode->parent)
45751c100a6SSakari Ailus 		return NULL;
45851c100a6SSakari Ailus 
45951c100a6SSakari Ailus 	return fwnode_handle_get(&swnode->parent->fwnode);
46059abd836SHeikki Krogerus }
46159abd836SHeikki Krogerus 
4620e3edd94SYueHaibing static struct fwnode_handle *
software_node_get_next_child(const struct fwnode_handle * fwnode,struct fwnode_handle * child)46359abd836SHeikki Krogerus software_node_get_next_child(const struct fwnode_handle *fwnode,
46459abd836SHeikki Krogerus 			     struct fwnode_handle *child)
46559abd836SHeikki Krogerus {
46680488a6bSHeikki Krogerus 	struct swnode *p = to_swnode(fwnode);
46780488a6bSHeikki Krogerus 	struct swnode *c = to_swnode(child);
46859abd836SHeikki Krogerus 
4691d8f062eSColin Ian King 	if (!p || list_empty(&p->children) ||
470fb5ec981SDaniel Scally 	    (c && list_is_last(&c->entry, &p->children))) {
471fb5ec981SDaniel Scally 		fwnode_handle_put(child);
47259abd836SHeikki Krogerus 		return NULL;
473fb5ec981SDaniel Scally 	}
47459abd836SHeikki Krogerus 
47559abd836SHeikki Krogerus 	if (c)
47659abd836SHeikki Krogerus 		c = list_next_entry(c, entry);
47759abd836SHeikki Krogerus 	else
47880488a6bSHeikki Krogerus 		c = list_first_entry(&p->children, struct swnode, entry);
479fb5ec981SDaniel Scally 
480fb5ec981SDaniel Scally 	fwnode_handle_put(child);
481fb5ec981SDaniel Scally 	return fwnode_handle_get(&c->fwnode);
48259abd836SHeikki Krogerus }
48359abd836SHeikki Krogerus 
48434479820SHeikki Krogerus static struct fwnode_handle *
software_node_get_named_child_node(const struct fwnode_handle * fwnode,const char * childname)48534479820SHeikki Krogerus software_node_get_named_child_node(const struct fwnode_handle *fwnode,
48634479820SHeikki Krogerus 				   const char *childname)
48734479820SHeikki Krogerus {
48880488a6bSHeikki Krogerus 	struct swnode *swnode = to_swnode(fwnode);
48980488a6bSHeikki Krogerus 	struct swnode *child;
49034479820SHeikki Krogerus 
49134479820SHeikki Krogerus 	if (!swnode || list_empty(&swnode->children))
49234479820SHeikki Krogerus 		return NULL;
49334479820SHeikki Krogerus 
49434479820SHeikki Krogerus 	list_for_each_entry(child, &swnode->children, entry) {
495c959d0c2SHeikki Krogerus 		if (!strcmp(childname, kobject_name(&child->kobj))) {
49634479820SHeikki Krogerus 			kobject_get(&child->kobj);
49734479820SHeikki Krogerus 			return &child->fwnode;
49834479820SHeikki Krogerus 		}
49934479820SHeikki Krogerus 	}
50034479820SHeikki Krogerus 	return NULL;
50134479820SHeikki Krogerus }
50259abd836SHeikki Krogerus 
503b06184acSHeikki Krogerus static int
software_node_get_reference_args(const struct fwnode_handle * fwnode,const char * propname,const char * nargs_prop,unsigned int nargs,unsigned int index,struct fwnode_reference_args * args)504b06184acSHeikki Krogerus software_node_get_reference_args(const struct fwnode_handle *fwnode,
505b06184acSHeikki Krogerus 				 const char *propname, const char *nargs_prop,
506b06184acSHeikki Krogerus 				 unsigned int nargs, unsigned int index,
507b06184acSHeikki Krogerus 				 struct fwnode_reference_args *args)
508b06184acSHeikki Krogerus {
509b06184acSHeikki Krogerus 	struct swnode *swnode = to_swnode(fwnode);
510e64b674bSDmitry Torokhov 	const struct software_node_ref_args *ref_array;
511e933beddSDmitry Torokhov 	const struct software_node_ref_args *ref;
512b06184acSHeikki Krogerus 	const struct property_entry *prop;
513b06184acSHeikki Krogerus 	struct fwnode_handle *refnode;
514996b0830SDmitry Torokhov 	u32 nargs_prop_val;
515996b0830SDmitry Torokhov 	int error;
516b06184acSHeikki Krogerus 	int i;
517b06184acSHeikki Krogerus 
518e64b674bSDmitry Torokhov 	prop = property_entry_get(swnode->node->properties, propname);
519e933beddSDmitry Torokhov 	if (!prop)
520e933beddSDmitry Torokhov 		return -ENOENT;
521e933beddSDmitry Torokhov 
522e64b674bSDmitry Torokhov 	if (prop->type != DEV_PROP_REF)
523e64b674bSDmitry Torokhov 		return -EINVAL;
524e64b674bSDmitry Torokhov 
525e64b674bSDmitry Torokhov 	/*
526e64b674bSDmitry Torokhov 	 * We expect that references are never stored inline, even
527e64b674bSDmitry Torokhov 	 * single ones, as they are too big.
528e64b674bSDmitry Torokhov 	 */
529e64b674bSDmitry Torokhov 	if (prop->is_inline)
530e64b674bSDmitry Torokhov 		return -EINVAL;
531e64b674bSDmitry Torokhov 
532e933beddSDmitry Torokhov 	if (index * sizeof(*ref) >= prop->length)
533e64b674bSDmitry Torokhov 		return -ENOENT;
534e64b674bSDmitry Torokhov 
535e64b674bSDmitry Torokhov 	ref_array = prop->pointer;
536e933beddSDmitry Torokhov 	ref = &ref_array[index];
537b06184acSHeikki Krogerus 
538e933beddSDmitry Torokhov 	refnode = software_node_fwnode(ref->node);
539b06184acSHeikki Krogerus 	if (!refnode)
540b06184acSHeikki Krogerus 		return -ENOENT;
541b06184acSHeikki Krogerus 
542b06184acSHeikki Krogerus 	if (nargs_prop) {
543c5fc5ba8SClément Léger 		error = property_entry_read_int_array(ref->node->properties,
544996b0830SDmitry Torokhov 						      nargs_prop, sizeof(u32),
545996b0830SDmitry Torokhov 						      &nargs_prop_val, 1);
546996b0830SDmitry Torokhov 		if (error)
547996b0830SDmitry Torokhov 			return error;
548b06184acSHeikki Krogerus 
549996b0830SDmitry Torokhov 		nargs = nargs_prop_val;
550b06184acSHeikki Krogerus 	}
551b06184acSHeikki Krogerus 
552b06184acSHeikki Krogerus 	if (nargs > NR_FWNODE_REFERENCE_ARGS)
553b06184acSHeikki Krogerus 		return -EINVAL;
554b06184acSHeikki Krogerus 
5551eaea4b3SSakari Ailus 	if (!args)
5561eaea4b3SSakari Ailus 		return 0;
5571eaea4b3SSakari Ailus 
558b06184acSHeikki Krogerus 	args->fwnode = software_node_get(refnode);
559b06184acSHeikki Krogerus 	args->nargs = nargs;
560b06184acSHeikki Krogerus 
561b06184acSHeikki Krogerus 	for (i = 0; i < nargs; i++)
562e933beddSDmitry Torokhov 		args->args[i] = ref->args[i];
563b06184acSHeikki Krogerus 
564b06184acSHeikki Krogerus 	return 0;
565b06184acSHeikki Krogerus }
566b06184acSHeikki Krogerus 
567000c08fdSHeikki Krogerus static struct fwnode_handle *
swnode_graph_find_next_port(const struct fwnode_handle * parent,struct fwnode_handle * port)568000c08fdSHeikki Krogerus swnode_graph_find_next_port(const struct fwnode_handle *parent,
569000c08fdSHeikki Krogerus 			    struct fwnode_handle *port)
570000c08fdSHeikki Krogerus {
571000c08fdSHeikki Krogerus 	struct fwnode_handle *old = port;
572000c08fdSHeikki Krogerus 
573000c08fdSHeikki Krogerus 	while ((port = software_node_get_next_child(parent, old))) {
574000c08fdSHeikki Krogerus 		/*
575000c08fdSHeikki Krogerus 		 * fwnode ports have naming style "port@", so we search for any
576000c08fdSHeikki Krogerus 		 * children that follow that convention.
577000c08fdSHeikki Krogerus 		 */
578000c08fdSHeikki Krogerus 		if (!strncmp(to_swnode(port)->node->name, "port@",
579000c08fdSHeikki Krogerus 			     strlen("port@")))
580000c08fdSHeikki Krogerus 			return port;
581000c08fdSHeikki Krogerus 		old = port;
582000c08fdSHeikki Krogerus 	}
583000c08fdSHeikki Krogerus 
584000c08fdSHeikki Krogerus 	return NULL;
585000c08fdSHeikki Krogerus }
586000c08fdSHeikki Krogerus 
587000c08fdSHeikki Krogerus static struct fwnode_handle *
software_node_graph_get_next_endpoint(const struct fwnode_handle * fwnode,struct fwnode_handle * endpoint)588000c08fdSHeikki Krogerus software_node_graph_get_next_endpoint(const struct fwnode_handle *fwnode,
589000c08fdSHeikki Krogerus 				      struct fwnode_handle *endpoint)
590000c08fdSHeikki Krogerus {
591000c08fdSHeikki Krogerus 	struct swnode *swnode = to_swnode(fwnode);
592000c08fdSHeikki Krogerus 	struct fwnode_handle *parent;
593000c08fdSHeikki Krogerus 	struct fwnode_handle *port;
594000c08fdSHeikki Krogerus 
595000c08fdSHeikki Krogerus 	if (!swnode)
596000c08fdSHeikki Krogerus 		return NULL;
597000c08fdSHeikki Krogerus 
598000c08fdSHeikki Krogerus 	if (endpoint) {
599000c08fdSHeikki Krogerus 		port = software_node_get_parent(endpoint);
600000c08fdSHeikki Krogerus 		parent = software_node_get_parent(port);
601000c08fdSHeikki Krogerus 	} else {
602000c08fdSHeikki Krogerus 		parent = software_node_get_named_child_node(fwnode, "ports");
603000c08fdSHeikki Krogerus 		if (!parent)
604000c08fdSHeikki Krogerus 			parent = software_node_get(&swnode->fwnode);
605000c08fdSHeikki Krogerus 
606000c08fdSHeikki Krogerus 		port = swnode_graph_find_next_port(parent, NULL);
607000c08fdSHeikki Krogerus 	}
608000c08fdSHeikki Krogerus 
609000c08fdSHeikki Krogerus 	for (; port; port = swnode_graph_find_next_port(parent, port)) {
610000c08fdSHeikki Krogerus 		endpoint = software_node_get_next_child(port, endpoint);
611000c08fdSHeikki Krogerus 		if (endpoint) {
612000c08fdSHeikki Krogerus 			fwnode_handle_put(port);
613000c08fdSHeikki Krogerus 			break;
614000c08fdSHeikki Krogerus 		}
615000c08fdSHeikki Krogerus 	}
616000c08fdSHeikki Krogerus 
617000c08fdSHeikki Krogerus 	fwnode_handle_put(parent);
618000c08fdSHeikki Krogerus 
619000c08fdSHeikki Krogerus 	return endpoint;
620000c08fdSHeikki Krogerus }
621000c08fdSHeikki Krogerus 
622000c08fdSHeikki Krogerus static struct fwnode_handle *
software_node_graph_get_remote_endpoint(const struct fwnode_handle * fwnode)623000c08fdSHeikki Krogerus software_node_graph_get_remote_endpoint(const struct fwnode_handle *fwnode)
624000c08fdSHeikki Krogerus {
625000c08fdSHeikki Krogerus 	struct swnode *swnode = to_swnode(fwnode);
626000c08fdSHeikki Krogerus 	const struct software_node_ref_args *ref;
627000c08fdSHeikki Krogerus 	const struct property_entry *prop;
628000c08fdSHeikki Krogerus 
629000c08fdSHeikki Krogerus 	if (!swnode)
630000c08fdSHeikki Krogerus 		return NULL;
631000c08fdSHeikki Krogerus 
632000c08fdSHeikki Krogerus 	prop = property_entry_get(swnode->node->properties, "remote-endpoint");
633000c08fdSHeikki Krogerus 	if (!prop || prop->type != DEV_PROP_REF || prop->is_inline)
634000c08fdSHeikki Krogerus 		return NULL;
635000c08fdSHeikki Krogerus 
636000c08fdSHeikki Krogerus 	ref = prop->pointer;
637000c08fdSHeikki Krogerus 
638000c08fdSHeikki Krogerus 	return software_node_get(software_node_fwnode(ref[0].node));
639000c08fdSHeikki Krogerus }
640000c08fdSHeikki Krogerus 
641000c08fdSHeikki Krogerus static struct fwnode_handle *
software_node_graph_get_port_parent(struct fwnode_handle * fwnode)642000c08fdSHeikki Krogerus software_node_graph_get_port_parent(struct fwnode_handle *fwnode)
643000c08fdSHeikki Krogerus {
644000c08fdSHeikki Krogerus 	struct swnode *swnode = to_swnode(fwnode);
645000c08fdSHeikki Krogerus 
646000c08fdSHeikki Krogerus 	swnode = swnode->parent;
647000c08fdSHeikki Krogerus 	if (swnode && !strcmp(swnode->node->name, "ports"))
648000c08fdSHeikki Krogerus 		swnode = swnode->parent;
649000c08fdSHeikki Krogerus 
650000c08fdSHeikki Krogerus 	return swnode ? software_node_get(&swnode->fwnode) : NULL;
651000c08fdSHeikki Krogerus }
652000c08fdSHeikki Krogerus 
653000c08fdSHeikki Krogerus static int
software_node_graph_parse_endpoint(const struct fwnode_handle * fwnode,struct fwnode_endpoint * endpoint)654000c08fdSHeikki Krogerus software_node_graph_parse_endpoint(const struct fwnode_handle *fwnode,
655000c08fdSHeikki Krogerus 				   struct fwnode_endpoint *endpoint)
656000c08fdSHeikki Krogerus {
657000c08fdSHeikki Krogerus 	struct swnode *swnode = to_swnode(fwnode);
658000c08fdSHeikki Krogerus 	const char *parent_name = swnode->parent->node->name;
659000c08fdSHeikki Krogerus 	int ret;
660000c08fdSHeikki Krogerus 
661000c08fdSHeikki Krogerus 	if (strlen("port@") >= strlen(parent_name) ||
662000c08fdSHeikki Krogerus 	    strncmp(parent_name, "port@", strlen("port@")))
663000c08fdSHeikki Krogerus 		return -EINVAL;
664000c08fdSHeikki Krogerus 
665000c08fdSHeikki Krogerus 	/* Ports have naming style "port@n", we need to select the n */
666000c08fdSHeikki Krogerus 	ret = kstrtou32(parent_name + strlen("port@"), 10, &endpoint->port);
667000c08fdSHeikki Krogerus 	if (ret)
668000c08fdSHeikki Krogerus 		return ret;
669000c08fdSHeikki Krogerus 
670000c08fdSHeikki Krogerus 	endpoint->id = swnode->id;
671000c08fdSHeikki Krogerus 	endpoint->local_fwnode = fwnode;
672000c08fdSHeikki Krogerus 
673000c08fdSHeikki Krogerus 	return 0;
674000c08fdSHeikki Krogerus }
675000c08fdSHeikki Krogerus 
67659abd836SHeikki Krogerus static const struct fwnode_operations software_node_ops = {
67759abd836SHeikki Krogerus 	.get = software_node_get,
67859abd836SHeikki Krogerus 	.put = software_node_put,
67959abd836SHeikki Krogerus 	.property_present = software_node_property_present,
68059abd836SHeikki Krogerus 	.property_read_int_array = software_node_read_int_array,
68159abd836SHeikki Krogerus 	.property_read_string_array = software_node_read_string_array,
682bc0500c1SSakari Ailus 	.get_name = software_node_get_name,
683e7e242bcSSakari Ailus 	.get_name_prefix = software_node_get_name_prefix,
68459abd836SHeikki Krogerus 	.get_parent = software_node_get_parent,
68559abd836SHeikki Krogerus 	.get_next_child_node = software_node_get_next_child,
68634479820SHeikki Krogerus 	.get_named_child_node = software_node_get_named_child_node,
687000c08fdSHeikki Krogerus 	.get_reference_args = software_node_get_reference_args,
688000c08fdSHeikki Krogerus 	.graph_get_next_endpoint = software_node_graph_get_next_endpoint,
689000c08fdSHeikki Krogerus 	.graph_get_remote_endpoint = software_node_graph_get_remote_endpoint,
690000c08fdSHeikki Krogerus 	.graph_get_port_parent = software_node_graph_get_port_parent,
691000c08fdSHeikki Krogerus 	.graph_parse_endpoint = software_node_graph_parse_endpoint,
69259abd836SHeikki Krogerus };
69359abd836SHeikki Krogerus 
69459abd836SHeikki Krogerus /* -------------------------------------------------------------------------- */
69559abd836SHeikki Krogerus 
6961666faedSHeikki Krogerus /**
6971666faedSHeikki Krogerus  * software_node_find_by_name - Find software node by name
6981666faedSHeikki Krogerus  * @parent: Parent of the software node
6991666faedSHeikki Krogerus  * @name: Name of the software node
7001666faedSHeikki Krogerus  *
7011666faedSHeikki Krogerus  * The function will find a node that is child of @parent and that is named
7021666faedSHeikki Krogerus  * @name. If no node is found, the function returns NULL.
7031666faedSHeikki Krogerus  *
7041666faedSHeikki Krogerus  * NOTE: you will need to drop the reference with fwnode_handle_put() after use.
7051666faedSHeikki Krogerus  */
7061666faedSHeikki Krogerus const struct software_node *
software_node_find_by_name(const struct software_node * parent,const char * name)7071666faedSHeikki Krogerus software_node_find_by_name(const struct software_node *parent, const char *name)
7081666faedSHeikki Krogerus {
709016049a8SHeikki Krogerus 	struct swnode *swnode = NULL;
7101666faedSHeikki Krogerus 	struct kobject *k;
7111666faedSHeikki Krogerus 
7121666faedSHeikki Krogerus 	if (!name)
7131666faedSHeikki Krogerus 		return NULL;
7141666faedSHeikki Krogerus 
7151666faedSHeikki Krogerus 	spin_lock(&swnode_kset->list_lock);
7161666faedSHeikki Krogerus 
7171666faedSHeikki Krogerus 	list_for_each_entry(k, &swnode_kset->list, entry) {
7181666faedSHeikki Krogerus 		swnode = kobj_to_swnode(k);
7191666faedSHeikki Krogerus 		if (parent == swnode->node->parent && swnode->node->name &&
7201666faedSHeikki Krogerus 		    !strcmp(name, swnode->node->name)) {
7211666faedSHeikki Krogerus 			kobject_get(&swnode->kobj);
7221666faedSHeikki Krogerus 			break;
7231666faedSHeikki Krogerus 		}
7241666faedSHeikki Krogerus 		swnode = NULL;
7251666faedSHeikki Krogerus 	}
7261666faedSHeikki Krogerus 
7271666faedSHeikki Krogerus 	spin_unlock(&swnode_kset->list_lock);
7281666faedSHeikki Krogerus 
7291666faedSHeikki Krogerus 	return swnode ? swnode->node : NULL;
7301666faedSHeikki Krogerus }
7311666faedSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_find_by_name);
7321666faedSHeikki Krogerus 
software_node_alloc(const struct property_entry * properties)73306ad93c3SAndy Shevchenko static struct software_node *software_node_alloc(const struct property_entry *properties)
73459abd836SHeikki Krogerus {
73559abd836SHeikki Krogerus 	struct property_entry *props;
73606ad93c3SAndy Shevchenko 	struct software_node *node;
73759abd836SHeikki Krogerus 
73859abd836SHeikki Krogerus 	props = property_entries_dup(properties);
73959abd836SHeikki Krogerus 	if (IS_ERR(props))
74006ad93c3SAndy Shevchenko 		return ERR_CAST(props);
74106ad93c3SAndy Shevchenko 
74206ad93c3SAndy Shevchenko 	node = kzalloc(sizeof(*node), GFP_KERNEL);
74306ad93c3SAndy Shevchenko 	if (!node) {
74406ad93c3SAndy Shevchenko 		property_entries_free(props);
74506ad93c3SAndy Shevchenko 		return ERR_PTR(-ENOMEM);
74606ad93c3SAndy Shevchenko 	}
74759abd836SHeikki Krogerus 
74880488a6bSHeikki Krogerus 	node->properties = props;
74959abd836SHeikki Krogerus 
75006ad93c3SAndy Shevchenko 	return node;
75106ad93c3SAndy Shevchenko }
75206ad93c3SAndy Shevchenko 
software_node_free(const struct software_node * node)75306ad93c3SAndy Shevchenko static void software_node_free(const struct software_node *node)
75406ad93c3SAndy Shevchenko {
75506ad93c3SAndy Shevchenko 	property_entries_free(node->properties);
75606ad93c3SAndy Shevchenko 	kfree(node);
75759abd836SHeikki Krogerus }
75859abd836SHeikki Krogerus 
software_node_release(struct kobject * kobj)75959abd836SHeikki Krogerus static void software_node_release(struct kobject *kobj)
76059abd836SHeikki Krogerus {
76180488a6bSHeikki Krogerus 	struct swnode *swnode = kobj_to_swnode(kobj);
76259abd836SHeikki Krogerus 
7637589238aSBrendan Higgins 	if (swnode->parent) {
7644c095734SChristophe JAILLET 		ida_free(&swnode->parent->child_ids, swnode->id);
7657589238aSBrendan Higgins 		list_del(&swnode->entry);
7667589238aSBrendan Higgins 	} else {
7674c095734SChristophe JAILLET 		ida_free(&swnode_root_ids, swnode->id);
7687589238aSBrendan Higgins 	}
7697589238aSBrendan Higgins 
77006ad93c3SAndy Shevchenko 	if (swnode->allocated)
77106ad93c3SAndy Shevchenko 		software_node_free(swnode->node);
77206ad93c3SAndy Shevchenko 
77359abd836SHeikki Krogerus 	ida_destroy(&swnode->child_ids);
77459abd836SHeikki Krogerus 	kfree(swnode);
77559abd836SHeikki Krogerus }
77659abd836SHeikki Krogerus 
777c83d9ab4SThomas Weißschuh static const struct kobj_type software_node_type = {
77859abd836SHeikki Krogerus 	.release = software_node_release,
77959abd836SHeikki Krogerus 	.sysfs_ops = &kobj_sysfs_ops,
78059abd836SHeikki Krogerus };
78159abd836SHeikki Krogerus 
78280488a6bSHeikki Krogerus static struct fwnode_handle *
swnode_register(const struct software_node * node,struct swnode * parent,unsigned int allocated)78380488a6bSHeikki Krogerus swnode_register(const struct software_node *node, struct swnode *parent,
78480488a6bSHeikki Krogerus 		unsigned int allocated)
78580488a6bSHeikki Krogerus {
78680488a6bSHeikki Krogerus 	struct swnode *swnode;
78780488a6bSHeikki Krogerus 	int ret;
78880488a6bSHeikki Krogerus 
78980488a6bSHeikki Krogerus 	swnode = kzalloc(sizeof(*swnode), GFP_KERNEL);
7903f6b6536SAndy Shevchenko 	if (!swnode)
7913f6b6536SAndy Shevchenko 		return ERR_PTR(-ENOMEM);
79280488a6bSHeikki Krogerus 
7934c095734SChristophe JAILLET 	ret = ida_alloc(parent ? &parent->child_ids : &swnode_root_ids,
7944c095734SChristophe JAILLET 			GFP_KERNEL);
79580488a6bSHeikki Krogerus 	if (ret < 0) {
79680488a6bSHeikki Krogerus 		kfree(swnode);
7973f6b6536SAndy Shevchenko 		return ERR_PTR(ret);
79880488a6bSHeikki Krogerus 	}
79980488a6bSHeikki Krogerus 
80080488a6bSHeikki Krogerus 	swnode->id = ret;
80180488a6bSHeikki Krogerus 	swnode->node = node;
80280488a6bSHeikki Krogerus 	swnode->parent = parent;
80380488a6bSHeikki Krogerus 	swnode->kobj.kset = swnode_kset;
80401bb86b3SSaravana Kannan 	fwnode_init(&swnode->fwnode, &software_node_ops);
80580488a6bSHeikki Krogerus 
80680488a6bSHeikki Krogerus 	ida_init(&swnode->child_ids);
80780488a6bSHeikki Krogerus 	INIT_LIST_HEAD(&swnode->entry);
80880488a6bSHeikki Krogerus 	INIT_LIST_HEAD(&swnode->children);
80980488a6bSHeikki Krogerus 
81080488a6bSHeikki Krogerus 	if (node->name)
81180488a6bSHeikki Krogerus 		ret = kobject_init_and_add(&swnode->kobj, &software_node_type,
81280488a6bSHeikki Krogerus 					   parent ? &parent->kobj : NULL,
81380488a6bSHeikki Krogerus 					   "%s", node->name);
81480488a6bSHeikki Krogerus 	else
81580488a6bSHeikki Krogerus 		ret = kobject_init_and_add(&swnode->kobj, &software_node_type,
81680488a6bSHeikki Krogerus 					   parent ? &parent->kobj : NULL,
81780488a6bSHeikki Krogerus 					   "node%d", swnode->id);
81880488a6bSHeikki Krogerus 	if (ret) {
81980488a6bSHeikki Krogerus 		kobject_put(&swnode->kobj);
82080488a6bSHeikki Krogerus 		return ERR_PTR(ret);
82180488a6bSHeikki Krogerus 	}
82280488a6bSHeikki Krogerus 
8233f6b6536SAndy Shevchenko 	/*
8243f6b6536SAndy Shevchenko 	 * Assign the flag only in the successful case, so
8253f6b6536SAndy Shevchenko 	 * the above kobject_put() won't mess up with properties.
8263f6b6536SAndy Shevchenko 	 */
8273f6b6536SAndy Shevchenko 	swnode->allocated = allocated;
8283f6b6536SAndy Shevchenko 
82980488a6bSHeikki Krogerus 	if (parent)
83080488a6bSHeikki Krogerus 		list_add_tail(&swnode->entry, &parent->children);
83180488a6bSHeikki Krogerus 
83280488a6bSHeikki Krogerus 	kobject_uevent(&swnode->kobj, KOBJ_ADD);
83380488a6bSHeikki Krogerus 	return &swnode->fwnode;
83480488a6bSHeikki Krogerus }
83580488a6bSHeikki Krogerus 
83680488a6bSHeikki Krogerus /**
83702094d54SAndy Shevchenko  * software_node_register_node_group - Register a group of software nodes
83802094d54SAndy Shevchenko  * @node_group: NULL terminated array of software node pointers to be registered
83902094d54SAndy Shevchenko  *
8400b8bf06fSAndy Shevchenko  * Register multiple software nodes at once. If any node in the array
8410b8bf06fSAndy Shevchenko  * has its .parent pointer set (which can only be to another software_node),
8420b8bf06fSAndy Shevchenko  * then its parent **must** have been registered before it is; either outside
8430b8bf06fSAndy Shevchenko  * of this function or by ordering the array such that parent comes before
8440b8bf06fSAndy Shevchenko  * child.
84502094d54SAndy Shevchenko  */
software_node_register_node_group(const struct software_node ** node_group)84602094d54SAndy Shevchenko int software_node_register_node_group(const struct software_node **node_group)
84702094d54SAndy Shevchenko {
84802094d54SAndy Shevchenko 	unsigned int i;
84902094d54SAndy Shevchenko 	int ret;
85002094d54SAndy Shevchenko 
85102094d54SAndy Shevchenko 	if (!node_group)
85202094d54SAndy Shevchenko 		return 0;
85302094d54SAndy Shevchenko 
85402094d54SAndy Shevchenko 	for (i = 0; node_group[i]; i++) {
85502094d54SAndy Shevchenko 		ret = software_node_register(node_group[i]);
85602094d54SAndy Shevchenko 		if (ret) {
85702094d54SAndy Shevchenko 			software_node_unregister_node_group(node_group);
85802094d54SAndy Shevchenko 			return ret;
85902094d54SAndy Shevchenko 		}
86002094d54SAndy Shevchenko 	}
86102094d54SAndy Shevchenko 
86202094d54SAndy Shevchenko 	return 0;
86302094d54SAndy Shevchenko }
86402094d54SAndy Shevchenko EXPORT_SYMBOL_GPL(software_node_register_node_group);
86502094d54SAndy Shevchenko 
86602094d54SAndy Shevchenko /**
86702094d54SAndy Shevchenko  * software_node_unregister_node_group - Unregister a group of software nodes
86802094d54SAndy Shevchenko  * @node_group: NULL terminated array of software node pointers to be unregistered
86902094d54SAndy Shevchenko  *
8700b8bf06fSAndy Shevchenko  * Unregister multiple software nodes at once. If parent pointers are set up
8710b8bf06fSAndy Shevchenko  * in any of the software nodes then the array **must** be ordered such that
8720b8bf06fSAndy Shevchenko  * parents come before their children.
8730b8bf06fSAndy Shevchenko  *
8740b8bf06fSAndy Shevchenko  * NOTE: If you are uncertain whether the array is ordered such that
8750b8bf06fSAndy Shevchenko  * parents will be unregistered before their children, it is wiser to
8760b8bf06fSAndy Shevchenko  * remove the nodes individually, in the correct order (child before
8770b8bf06fSAndy Shevchenko  * parent).
87802094d54SAndy Shevchenko  */
software_node_unregister_node_group(const struct software_node ** node_group)879fc002f0fSDaniel Scally void software_node_unregister_node_group(
880fc002f0fSDaniel Scally 		const struct software_node **node_group)
88102094d54SAndy Shevchenko {
882fc002f0fSDaniel Scally 	unsigned int i = 0;
88302094d54SAndy Shevchenko 
88402094d54SAndy Shevchenko 	if (!node_group)
88502094d54SAndy Shevchenko 		return;
88602094d54SAndy Shevchenko 
887fc002f0fSDaniel Scally 	while (node_group[i])
888fc002f0fSDaniel Scally 		i++;
889fc002f0fSDaniel Scally 
890fc002f0fSDaniel Scally 	while (i--)
8919dcbac84SAndy Shevchenko 		software_node_unregister(node_group[i]);
89202094d54SAndy Shevchenko }
89302094d54SAndy Shevchenko EXPORT_SYMBOL_GPL(software_node_unregister_node_group);
89402094d54SAndy Shevchenko 
89502094d54SAndy Shevchenko /**
89680488a6bSHeikki Krogerus  * software_node_register - Register static software node
89780488a6bSHeikki Krogerus  * @node: The software node to be registered
89880488a6bSHeikki Krogerus  */
software_node_register(const struct software_node * node)89980488a6bSHeikki Krogerus int software_node_register(const struct software_node *node)
90080488a6bSHeikki Krogerus {
90180488a6bSHeikki Krogerus 	struct swnode *parent = software_node_to_swnode(node->parent);
90280488a6bSHeikki Krogerus 
90380488a6bSHeikki Krogerus 	if (software_node_to_swnode(node))
90480488a6bSHeikki Krogerus 		return -EEXIST;
90580488a6bSHeikki Krogerus 
9068891123fSHeikki Krogerus 	if (node->parent && !parent)
9078891123fSHeikki Krogerus 		return -EINVAL;
9088891123fSHeikki Krogerus 
90980488a6bSHeikki Krogerus 	return PTR_ERR_OR_ZERO(swnode_register(node, parent, 0));
91080488a6bSHeikki Krogerus }
91180488a6bSHeikki Krogerus EXPORT_SYMBOL_GPL(software_node_register);
91280488a6bSHeikki Krogerus 
91346d26819SGreg Kroah-Hartman /**
91446d26819SGreg Kroah-Hartman  * software_node_unregister - Unregister static software node
91546d26819SGreg Kroah-Hartman  * @node: The software node to be unregistered
91646d26819SGreg Kroah-Hartman  */
software_node_unregister(const struct software_node * node)91746d26819SGreg Kroah-Hartman void software_node_unregister(const struct software_node *node)
91846d26819SGreg Kroah-Hartman {
91946d26819SGreg Kroah-Hartman 	struct swnode *swnode;
92046d26819SGreg Kroah-Hartman 
92146d26819SGreg Kroah-Hartman 	swnode = software_node_to_swnode(node);
92246d26819SGreg Kroah-Hartman 	if (swnode)
92346d26819SGreg Kroah-Hartman 		fwnode_remove_software_node(&swnode->fwnode);
92446d26819SGreg Kroah-Hartman }
92546d26819SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(software_node_unregister);
92646d26819SGreg Kroah-Hartman 
92759abd836SHeikki Krogerus struct fwnode_handle *
fwnode_create_software_node(const struct property_entry * properties,const struct fwnode_handle * parent)92859abd836SHeikki Krogerus fwnode_create_software_node(const struct property_entry *properties,
92959abd836SHeikki Krogerus 			    const struct fwnode_handle *parent)
93059abd836SHeikki Krogerus {
9313f6b6536SAndy Shevchenko 	struct fwnode_handle *fwnode;
93280488a6bSHeikki Krogerus 	struct software_node *node;
93373c93426SAndy Shevchenko 	struct swnode *p;
93459abd836SHeikki Krogerus 
93559abd836SHeikki Krogerus 	if (IS_ERR(parent))
93659abd836SHeikki Krogerus 		return ERR_CAST(parent);
93773c93426SAndy Shevchenko 
93880488a6bSHeikki Krogerus 	p = to_swnode(parent);
93973c93426SAndy Shevchenko 	if (parent && !p)
94073c93426SAndy Shevchenko 		return ERR_PTR(-EINVAL);
94159abd836SHeikki Krogerus 
94206ad93c3SAndy Shevchenko 	node = software_node_alloc(properties);
94306ad93c3SAndy Shevchenko 	if (IS_ERR(node))
94406ad93c3SAndy Shevchenko 		return ERR_CAST(node);
94559abd836SHeikki Krogerus 
94680488a6bSHeikki Krogerus 	node->parent = p ? p->node : NULL;
94759abd836SHeikki Krogerus 
9483f6b6536SAndy Shevchenko 	fwnode = swnode_register(node, p, 1);
94906ad93c3SAndy Shevchenko 	if (IS_ERR(fwnode))
95006ad93c3SAndy Shevchenko 		software_node_free(node);
9513f6b6536SAndy Shevchenko 
9523f6b6536SAndy Shevchenko 	return fwnode;
95359abd836SHeikki Krogerus }
95459abd836SHeikki Krogerus EXPORT_SYMBOL_GPL(fwnode_create_software_node);
95559abd836SHeikki Krogerus 
fwnode_remove_software_node(struct fwnode_handle * fwnode)95659abd836SHeikki Krogerus void fwnode_remove_software_node(struct fwnode_handle *fwnode)
95759abd836SHeikki Krogerus {
95880488a6bSHeikki Krogerus 	struct swnode *swnode = to_swnode(fwnode);
95959abd836SHeikki Krogerus 
96059abd836SHeikki Krogerus 	if (!swnode)
96159abd836SHeikki Krogerus 		return;
96259abd836SHeikki Krogerus 
96359abd836SHeikki Krogerus 	kobject_put(&swnode->kobj);
96459abd836SHeikki Krogerus }
96559abd836SHeikki Krogerus EXPORT_SYMBOL_GPL(fwnode_remove_software_node);
96659abd836SHeikki Krogerus 
967e68d0119SHeikki Krogerus /**
968e68d0119SHeikki Krogerus  * device_add_software_node - Assign software node to a device
969e68d0119SHeikki Krogerus  * @dev: The device the software node is meant for.
9702a92c90fSHeikki Krogerus  * @node: The software node.
971e68d0119SHeikki Krogerus  *
9722a92c90fSHeikki Krogerus  * This function will make @node the secondary firmware node pointer of @dev. If
9732a92c90fSHeikki Krogerus  * @dev has no primary node, then @node will become the primary node. The
9742a92c90fSHeikki Krogerus  * function will register @node automatically if it wasn't already registered.
975e68d0119SHeikki Krogerus  */
device_add_software_node(struct device * dev,const struct software_node * node)9762a92c90fSHeikki Krogerus int device_add_software_node(struct device *dev, const struct software_node *node)
977e68d0119SHeikki Krogerus {
9782a92c90fSHeikki Krogerus 	struct swnode *swnode;
979e68d0119SHeikki Krogerus 	int ret;
980e68d0119SHeikki Krogerus 
981e68d0119SHeikki Krogerus 	/* Only one software node per device. */
982e68d0119SHeikki Krogerus 	if (dev_to_swnode(dev))
983e68d0119SHeikki Krogerus 		return -EBUSY;
984e68d0119SHeikki Krogerus 
9852a92c90fSHeikki Krogerus 	swnode = software_node_to_swnode(node);
9862a92c90fSHeikki Krogerus 	if (swnode) {
9872a92c90fSHeikki Krogerus 		kobject_get(&swnode->kobj);
9882a92c90fSHeikki Krogerus 	} else {
9892a92c90fSHeikki Krogerus 		ret = software_node_register(node);
990e68d0119SHeikki Krogerus 		if (ret)
991e68d0119SHeikki Krogerus 			return ret;
992e68d0119SHeikki Krogerus 
9932a92c90fSHeikki Krogerus 		swnode = software_node_to_swnode(node);
9942a92c90fSHeikki Krogerus 	}
9952a92c90fSHeikki Krogerus 
9962a92c90fSHeikki Krogerus 	set_secondary_fwnode(dev, &swnode->fwnode);
9975dca69e2SHeikki Krogerus 
9985dca69e2SHeikki Krogerus 	/*
9995dca69e2SHeikki Krogerus 	 * If the device has been fully registered by the time this function is
10005dca69e2SHeikki Krogerus 	 * called, software_node_notify() must be called separately so that the
10015dca69e2SHeikki Krogerus 	 * symlinks get created and the reference count of the node is kept in
10025dca69e2SHeikki Krogerus 	 * balance.
10035dca69e2SHeikki Krogerus 	 */
10045dca69e2SHeikki Krogerus 	if (device_is_registered(dev))
1005384f5a85SRafael J. Wysocki 		software_node_notify(dev);
1006e68d0119SHeikki Krogerus 
1007e68d0119SHeikki Krogerus 	return 0;
1008e68d0119SHeikki Krogerus }
1009e68d0119SHeikki Krogerus EXPORT_SYMBOL_GPL(device_add_software_node);
1010e68d0119SHeikki Krogerus 
1011e68d0119SHeikki Krogerus /**
1012e68d0119SHeikki Krogerus  * device_remove_software_node - Remove device's software node
1013e68d0119SHeikki Krogerus  * @dev: The device with the software node.
1014e68d0119SHeikki Krogerus  *
1015e68d0119SHeikki Krogerus  * This function will unregister the software node of @dev.
1016e68d0119SHeikki Krogerus  */
device_remove_software_node(struct device * dev)1017e68d0119SHeikki Krogerus void device_remove_software_node(struct device *dev)
1018e68d0119SHeikki Krogerus {
1019e68d0119SHeikki Krogerus 	struct swnode *swnode;
1020e68d0119SHeikki Krogerus 
1021e68d0119SHeikki Krogerus 	swnode = dev_to_swnode(dev);
1022e68d0119SHeikki Krogerus 	if (!swnode)
1023e68d0119SHeikki Krogerus 		return;
1024e68d0119SHeikki Krogerus 
10255dca69e2SHeikki Krogerus 	if (device_is_registered(dev))
1026384f5a85SRafael J. Wysocki 		software_node_notify_remove(dev);
1027384f5a85SRafael J. Wysocki 
1028e68d0119SHeikki Krogerus 	set_secondary_fwnode(dev, NULL);
1029e68d0119SHeikki Krogerus 	kobject_put(&swnode->kobj);
1030e68d0119SHeikki Krogerus }
1031e68d0119SHeikki Krogerus EXPORT_SYMBOL_GPL(device_remove_software_node);
1032e68d0119SHeikki Krogerus 
1033151f6ff7SHeikki Krogerus /**
1034151f6ff7SHeikki Krogerus  * device_create_managed_software_node - Create a software node for a device
1035151f6ff7SHeikki Krogerus  * @dev: The device the software node is assigned to.
1036151f6ff7SHeikki Krogerus  * @properties: Device properties for the software node.
1037151f6ff7SHeikki Krogerus  * @parent: Parent of the software node.
1038151f6ff7SHeikki Krogerus  *
1039151f6ff7SHeikki Krogerus  * Creates a software node as a managed resource for @dev, which means the
1040151f6ff7SHeikki Krogerus  * lifetime of the newly created software node is tied to the lifetime of @dev.
1041151f6ff7SHeikki Krogerus  * Software nodes created with this function should not be reused or shared
1042151f6ff7SHeikki Krogerus  * because of that. The function takes a deep copy of @properties for the
1043151f6ff7SHeikki Krogerus  * software node.
1044151f6ff7SHeikki Krogerus  *
1045151f6ff7SHeikki Krogerus  * Since the new software node is assigned directly to @dev, and since it should
1046151f6ff7SHeikki Krogerus  * not be shared, it is not returned to the caller. The function returns 0 on
1047151f6ff7SHeikki Krogerus  * success, and errno in case of an error.
1048151f6ff7SHeikki Krogerus  */
device_create_managed_software_node(struct device * dev,const struct property_entry * properties,const struct software_node * parent)1049151f6ff7SHeikki Krogerus int device_create_managed_software_node(struct device *dev,
1050151f6ff7SHeikki Krogerus 					const struct property_entry *properties,
1051151f6ff7SHeikki Krogerus 					const struct software_node *parent)
1052151f6ff7SHeikki Krogerus {
1053151f6ff7SHeikki Krogerus 	struct fwnode_handle *p = software_node_fwnode(parent);
1054151f6ff7SHeikki Krogerus 	struct fwnode_handle *fwnode;
1055151f6ff7SHeikki Krogerus 
1056151f6ff7SHeikki Krogerus 	if (parent && !p)
1057151f6ff7SHeikki Krogerus 		return -EINVAL;
1058151f6ff7SHeikki Krogerus 
1059151f6ff7SHeikki Krogerus 	fwnode = fwnode_create_software_node(properties, p);
1060151f6ff7SHeikki Krogerus 	if (IS_ERR(fwnode))
1061151f6ff7SHeikki Krogerus 		return PTR_ERR(fwnode);
1062151f6ff7SHeikki Krogerus 
1063151f6ff7SHeikki Krogerus 	to_swnode(fwnode)->managed = true;
1064151f6ff7SHeikki Krogerus 	set_secondary_fwnode(dev, fwnode);
1065151f6ff7SHeikki Krogerus 
10665aeb05b2SLaurentiu Tudor 	if (device_is_registered(dev))
10675aeb05b2SLaurentiu Tudor 		software_node_notify(dev);
10685aeb05b2SLaurentiu Tudor 
1069151f6ff7SHeikki Krogerus 	return 0;
1070151f6ff7SHeikki Krogerus }
1071151f6ff7SHeikki Krogerus EXPORT_SYMBOL_GPL(device_create_managed_software_node);
1072151f6ff7SHeikki Krogerus 
software_node_notify(struct device * dev)1073384f5a85SRafael J. Wysocki void software_node_notify(struct device *dev)
107459abd836SHeikki Krogerus {
107580488a6bSHeikki Krogerus 	struct swnode *swnode;
107659abd836SHeikki Krogerus 	int ret;
107759abd836SHeikki Krogerus 
1078e68d0119SHeikki Krogerus 	swnode = dev_to_swnode(dev);
1079e68d0119SHeikki Krogerus 	if (!swnode)
1080384f5a85SRafael J. Wysocki 		return;
108159abd836SHeikki Krogerus 
10825dca69e2SHeikki Krogerus 	ret = sysfs_create_link(&dev->kobj, &swnode->kobj, "software_node");
108359abd836SHeikki Krogerus 	if (ret)
1084384f5a85SRafael J. Wysocki 		return;
108559abd836SHeikki Krogerus 
1086384f5a85SRafael J. Wysocki 	ret = sysfs_create_link(&swnode->kobj, &dev->kobj, dev_name(dev));
108759abd836SHeikki Krogerus 	if (ret) {
108859abd836SHeikki Krogerus 		sysfs_remove_link(&dev->kobj, "software_node");
1089384f5a85SRafael J. Wysocki 		return;
109059abd836SHeikki Krogerus 	}
1091384f5a85SRafael J. Wysocki 
109259abd836SHeikki Krogerus 	kobject_get(&swnode->kobj);
1093384f5a85SRafael J. Wysocki }
1094384f5a85SRafael J. Wysocki 
software_node_notify_remove(struct device * dev)1095384f5a85SRafael J. Wysocki void software_node_notify_remove(struct device *dev)
1096384f5a85SRafael J. Wysocki {
1097384f5a85SRafael J. Wysocki 	struct swnode *swnode;
1098384f5a85SRafael J. Wysocki 
1099384f5a85SRafael J. Wysocki 	swnode = dev_to_swnode(dev);
1100384f5a85SRafael J. Wysocki 	if (!swnode)
1101384f5a85SRafael J. Wysocki 		return;
1102384f5a85SRafael J. Wysocki 
110359abd836SHeikki Krogerus 	sysfs_remove_link(&swnode->kobj, dev_name(dev));
110459abd836SHeikki Krogerus 	sysfs_remove_link(&dev->kobj, "software_node");
110559abd836SHeikki Krogerus 	kobject_put(&swnode->kobj);
1106151f6ff7SHeikki Krogerus 
1107151f6ff7SHeikki Krogerus 	if (swnode->managed) {
1108151f6ff7SHeikki Krogerus 		set_secondary_fwnode(dev, NULL);
1109151f6ff7SHeikki Krogerus 		kobject_put(&swnode->kobj);
1110151f6ff7SHeikki Krogerus 	}
111159abd836SHeikki Krogerus }
111259abd836SHeikki Krogerus 
software_node_init(void)111359abd836SHeikki Krogerus static int __init software_node_init(void)
111459abd836SHeikki Krogerus {
111559abd836SHeikki Krogerus 	swnode_kset = kset_create_and_add("software_nodes", NULL, kernel_kobj);
111659abd836SHeikki Krogerus 	if (!swnode_kset)
111759abd836SHeikki Krogerus 		return -ENOMEM;
111859abd836SHeikki Krogerus 	return 0;
111959abd836SHeikki Krogerus }
112059abd836SHeikki Krogerus postcore_initcall(software_node_init);
112159abd836SHeikki Krogerus 
software_node_exit(void)112259abd836SHeikki Krogerus static void __exit software_node_exit(void)
112359abd836SHeikki Krogerus {
112459abd836SHeikki Krogerus 	ida_destroy(&swnode_root_ids);
112559abd836SHeikki Krogerus 	kset_unregister(swnode_kset);
112659abd836SHeikki Krogerus }
112759abd836SHeikki Krogerus __exitcall(software_node_exit);
1128