xref: /linux/drivers/base/swnode.c (revision e68d0119)
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