blob: fd667a73a201525d106e28f20ec15444271cbb13 [file] [log] [blame]
Heikki Krogerus59abd832018-11-09 17:21:36 +03001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Software nodes for the firmware node framework.
4 *
5 * Copyright (C) 2018, Intel Corporation
6 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
7 */
8
9#include <linux/device.h>
10#include <linux/kernel.h>
11#include <linux/property.h>
12#include <linux/slab.h>
13
Heikki Krogerus80488a62019-05-31 17:15:34 +030014struct swnode {
Heikki Krogerus59abd832018-11-09 17:21:36 +030015 int id;
16 struct kobject kobj;
17 struct fwnode_handle fwnode;
Heikki Krogerus80488a62019-05-31 17:15:34 +030018 const struct software_node *node;
Heikki Krogerus59abd832018-11-09 17:21:36 +030019
20 /* hierarchy */
21 struct ida child_ids;
22 struct list_head entry;
23 struct list_head children;
Heikki Krogerus80488a62019-05-31 17:15:34 +030024 struct swnode *parent;
Heikki Krogerus59abd832018-11-09 17:21:36 +030025
Heikki Krogerus80488a62019-05-31 17:15:34 +030026 unsigned int allocated:1;
Heikki Krogerus59abd832018-11-09 17:21:36 +030027};
28
29static DEFINE_IDA(swnode_root_ids);
30static struct kset *swnode_kset;
31
Heikki Krogerus80488a62019-05-31 17:15:34 +030032#define kobj_to_swnode(_kobj_) container_of(_kobj_, struct swnode, kobj)
Heikki Krogerus59abd832018-11-09 17:21:36 +030033
34static const struct fwnode_operations software_node_ops;
35
36bool is_software_node(const struct fwnode_handle *fwnode)
37{
38 return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &software_node_ops;
39}
Heikki Krogerus80488a62019-05-31 17:15:34 +030040EXPORT_SYMBOL_GPL(is_software_node);
Heikki Krogerus59abd832018-11-09 17:21:36 +030041
Heikki Krogerus80488a62019-05-31 17:15:34 +030042#define to_swnode(__fwnode) \
Heikki Krogerus59abd832018-11-09 17:21:36 +030043 ({ \
Heikki Krogerus80488a62019-05-31 17:15:34 +030044 typeof(__fwnode) __to_swnode_fwnode = __fwnode; \
Heikki Krogerus59abd832018-11-09 17:21:36 +030045 \
Heikki Krogerus80488a62019-05-31 17:15:34 +030046 is_software_node(__to_swnode_fwnode) ? \
47 container_of(__to_swnode_fwnode, \
48 struct swnode, fwnode) : NULL; \
Heikki Krogerus59abd832018-11-09 17:21:36 +030049 })
50
Heikki Krogerus80488a62019-05-31 17:15:34 +030051static struct swnode *
52software_node_to_swnode(const struct software_node *node)
53{
Heikki Krogerus61636872019-08-30 10:51:55 +030054 struct swnode *swnode = NULL;
Heikki Krogerus80488a62019-05-31 17:15:34 +030055 struct kobject *k;
56
57 if (!node)
58 return NULL;
59
60 spin_lock(&swnode_kset->list_lock);
61
62 list_for_each_entry(k, &swnode_kset->list, entry) {
63 swnode = kobj_to_swnode(k);
64 if (swnode->node == node)
65 break;
66 swnode = NULL;
67 }
68
69 spin_unlock(&swnode_kset->list_lock);
70
71 return swnode;
72}
73
Sakari Ailus56c9aa02019-10-03 15:32:09 +030074const struct software_node *to_software_node(const struct fwnode_handle *fwnode)
Heikki Krogerus80488a62019-05-31 17:15:34 +030075{
Sakari Ailus56c9aa02019-10-03 15:32:09 +030076 const struct swnode *swnode = to_swnode(fwnode);
Heikki Krogerus80488a62019-05-31 17:15:34 +030077
78 return swnode ? swnode->node : NULL;
79}
80EXPORT_SYMBOL_GPL(to_software_node);
81
82struct fwnode_handle *software_node_fwnode(const struct software_node *node)
83{
84 struct swnode *swnode = software_node_to_swnode(node);
85
86 return swnode ? &swnode->fwnode : NULL;
87}
88EXPORT_SYMBOL_GPL(software_node_fwnode);
89
Heikki Krogerus59abd832018-11-09 17:21:36 +030090/* -------------------------------------------------------------------------- */
91/* property_entry processing */
92
93static const struct property_entry *
94property_entry_get(const struct property_entry *prop, const char *name)
95{
96 if (!prop)
97 return NULL;
98
99 for (; prop->name; prop++)
100 if (!strcmp(name, prop->name))
101 return prop;
102
103 return NULL;
104}
105
106static const void *property_get_pointer(const struct property_entry *prop)
107{
Dmitry Torokhov1f74d702019-10-23 13:02:24 -0700108 if (!prop->length)
Heikki Krogerus59abd832018-11-09 17:21:36 +0300109 return NULL;
Dmitry Torokhov1f74d702019-10-23 13:02:24 -0700110
111 if (prop->is_array)
112 return prop->pointer;
113
114 return &prop->value;
Heikki Krogerus59abd832018-11-09 17:21:36 +0300115}
116
117static const void *property_entry_find(const struct property_entry *props,
118 const char *propname, size_t length)
119{
120 const struct property_entry *prop;
121 const void *pointer;
122
123 prop = property_entry_get(props, propname);
124 if (!prop)
125 return ERR_PTR(-EINVAL);
126 pointer = property_get_pointer(prop);
127 if (!pointer)
128 return ERR_PTR(-ENODATA);
129 if (length > prop->length)
130 return ERR_PTR(-EOVERFLOW);
131 return pointer;
132}
133
134static int property_entry_read_u8_array(const struct property_entry *props,
135 const char *propname,
136 u8 *values, size_t nval)
137{
138 const void *pointer;
139 size_t length = nval * sizeof(*values);
140
141 pointer = property_entry_find(props, propname, length);
142 if (IS_ERR(pointer))
143 return PTR_ERR(pointer);
144
145 memcpy(values, pointer, length);
146 return 0;
147}
148
149static int property_entry_read_u16_array(const struct property_entry *props,
150 const char *propname,
151 u16 *values, size_t nval)
152{
153 const void *pointer;
154 size_t length = nval * sizeof(*values);
155
156 pointer = property_entry_find(props, propname, length);
157 if (IS_ERR(pointer))
158 return PTR_ERR(pointer);
159
160 memcpy(values, pointer, length);
161 return 0;
162}
163
164static int property_entry_read_u32_array(const struct property_entry *props,
165 const char *propname,
166 u32 *values, size_t nval)
167{
168 const void *pointer;
169 size_t length = nval * sizeof(*values);
170
171 pointer = property_entry_find(props, propname, length);
172 if (IS_ERR(pointer))
173 return PTR_ERR(pointer);
174
175 memcpy(values, pointer, length);
176 return 0;
177}
178
179static int property_entry_read_u64_array(const struct property_entry *props,
180 const char *propname,
181 u64 *values, size_t nval)
182{
183 const void *pointer;
184 size_t length = nval * sizeof(*values);
185
186 pointer = property_entry_find(props, propname, length);
187 if (IS_ERR(pointer))
188 return PTR_ERR(pointer);
189
190 memcpy(values, pointer, length);
191 return 0;
192}
193
194static int
195property_entry_count_elems_of_size(const struct property_entry *props,
196 const char *propname, size_t length)
197{
198 const struct property_entry *prop;
199
200 prop = property_entry_get(props, propname);
201 if (!prop)
202 return -EINVAL;
203
204 return prop->length / length;
205}
206
207static int property_entry_read_int_array(const struct property_entry *props,
208 const char *name,
209 unsigned int elem_size, void *val,
210 size_t nval)
211{
212 if (!val)
213 return property_entry_count_elems_of_size(props, name,
214 elem_size);
215 switch (elem_size) {
216 case sizeof(u8):
217 return property_entry_read_u8_array(props, name, val, nval);
218 case sizeof(u16):
219 return property_entry_read_u16_array(props, name, val, nval);
220 case sizeof(u32):
221 return property_entry_read_u32_array(props, name, val, nval);
222 case sizeof(u64):
223 return property_entry_read_u64_array(props, name, val, nval);
224 }
225
226 return -ENXIO;
227}
228
229static int property_entry_read_string_array(const struct property_entry *props,
230 const char *propname,
231 const char **strings, size_t nval)
232{
233 const struct property_entry *prop;
234 const void *pointer;
235 size_t array_len, length;
236
237 /* Find out the array length. */
238 prop = property_entry_get(props, propname);
239 if (!prop)
240 return -EINVAL;
241
242 if (prop->is_array)
243 /* Find the length of an array. */
244 array_len = property_entry_count_elems_of_size(props, propname,
245 sizeof(const char *));
246 else
247 /* The array length for a non-array string property is 1. */
248 array_len = 1;
249
250 /* Return how many there are if strings is NULL. */
251 if (!strings)
252 return array_len;
253
254 array_len = min(nval, array_len);
255 length = array_len * sizeof(*strings);
256
257 pointer = property_entry_find(props, propname, length);
258 if (IS_ERR(pointer))
259 return PTR_ERR(pointer);
260
261 memcpy(strings, pointer, length);
262
263 return array_len;
264}
265
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300266static void property_entry_free_data(const struct property_entry *p)
267{
268 const void *pointer = property_get_pointer(p);
Dmitry Torokhov1f74d702019-10-23 13:02:24 -0700269 const char * const *src_str;
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300270 size_t i, nval;
271
272 if (p->is_array) {
Dmitry Torokhov1f74d702019-10-23 13:02:24 -0700273 if (p->type == DEV_PROP_STRING && p->pointer) {
274 src_str = p->pointer;
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300275 nval = p->length / sizeof(const char *);
276 for (i = 0; i < nval; i++)
Dmitry Torokhov1f74d702019-10-23 13:02:24 -0700277 kfree(src_str[i]);
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300278 }
279 kfree(pointer);
280 } else if (p->type == DEV_PROP_STRING) {
281 kfree(p->value.str);
282 }
283 kfree(p->name);
284}
285
Dmitry Torokhov75dd63c2019-10-23 13:02:23 -0700286static const char * const *
287property_copy_string_array(const struct property_entry *src)
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300288{
289 const char **d;
Dmitry Torokhov1f74d702019-10-23 13:02:24 -0700290 const char * const *src_str = src->pointer;
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300291 size_t nval = src->length / sizeof(*d);
292 int i;
293
294 d = kcalloc(nval, sizeof(*d), GFP_KERNEL);
295 if (!d)
Dmitry Torokhov75dd63c2019-10-23 13:02:23 -0700296 return NULL;
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300297
298 for (i = 0; i < nval; i++) {
Dmitry Torokhov1f74d702019-10-23 13:02:24 -0700299 d[i] = kstrdup(src_str[i], GFP_KERNEL);
300 if (!d[i] && src_str[i]) {
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300301 while (--i >= 0)
302 kfree(d[i]);
303 kfree(d);
Dmitry Torokhov75dd63c2019-10-23 13:02:23 -0700304 return NULL;
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300305 }
306 }
307
Dmitry Torokhov75dd63c2019-10-23 13:02:23 -0700308 return d;
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300309}
310
311static int property_entry_copy_data(struct property_entry *dst,
312 const struct property_entry *src)
313{
314 const void *pointer = property_get_pointer(src);
315 const void *new;
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300316
317 if (src->is_array) {
318 if (!src->length)
319 return -ENODATA;
320
321 if (src->type == DEV_PROP_STRING) {
Dmitry Torokhov75dd63c2019-10-23 13:02:23 -0700322 new = property_copy_string_array(src);
323 if (!new)
324 return -ENOMEM;
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300325 } else {
326 new = kmemdup(pointer, src->length, GFP_KERNEL);
327 if (!new)
328 return -ENOMEM;
329 }
Dmitry Torokhov1f74d702019-10-23 13:02:24 -0700330
331 dst->is_array = true;
332 dst->pointer = new;
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300333 } else if (src->type == DEV_PROP_STRING) {
334 new = kstrdup(src->value.str, GFP_KERNEL);
335 if (!new && src->value.str)
336 return -ENOMEM;
Dmitry Torokhov1f74d702019-10-23 13:02:24 -0700337
338 dst->value.str = new;
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300339 } else {
Dmitry Torokhov1f74d702019-10-23 13:02:24 -0700340 dst->value = src->value;
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300341 }
342
343 dst->length = src->length;
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300344 dst->type = src->type;
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300345 dst->name = kstrdup(src->name, GFP_KERNEL);
346 if (!dst->name)
347 goto out_free_data;
348
349 return 0;
350
351out_free_data:
352 property_entry_free_data(dst);
353 return -ENOMEM;
354}
355
356/**
357 * property_entries_dup - duplicate array of properties
358 * @properties: array of properties to copy
359 *
360 * This function creates a deep copy of the given NULL-terminated array
361 * of property entries.
362 */
363struct property_entry *
364property_entries_dup(const struct property_entry *properties)
365{
366 struct property_entry *p;
367 int i, n = 0;
368 int ret;
369
Heikki Krogerusa7996982019-05-31 17:15:32 +0300370 if (!properties)
371 return NULL;
372
Heikki Krogerused1cdf32018-11-09 17:21:37 +0300373 while (properties[n].name)
374 n++;
375
376 p = kcalloc(n + 1, sizeof(*p), GFP_KERNEL);
377 if (!p)
378 return ERR_PTR(-ENOMEM);
379
380 for (i = 0; i < n; i++) {
381 ret = property_entry_copy_data(&p[i], &properties[i]);
382 if (ret) {
383 while (--i >= 0)
384 property_entry_free_data(&p[i]);
385 kfree(p);
386 return ERR_PTR(ret);
387 }
388 }
389
390 return p;
391}
392EXPORT_SYMBOL_GPL(property_entries_dup);
393
394/**
395 * property_entries_free - free previously allocated array of properties
396 * @properties: array of properties to destroy
397 *
398 * This function frees given NULL-terminated array of property entries,
399 * along with their data.
400 */
401void property_entries_free(const struct property_entry *properties)
402{
403 const struct property_entry *p;
404
405 if (!properties)
406 return;
407
408 for (p = properties; p->name; p++)
409 property_entry_free_data(p);
410
411 kfree(properties);
412}
413EXPORT_SYMBOL_GPL(property_entries_free);
414
Heikki Krogerus59abd832018-11-09 17:21:36 +0300415/* -------------------------------------------------------------------------- */
416/* fwnode operations */
417
418static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode)
419{
Heikki Krogerus80488a62019-05-31 17:15:34 +0300420 struct swnode *swnode = to_swnode(fwnode);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300421
422 kobject_get(&swnode->kobj);
423
424 return &swnode->fwnode;
425}
426
427static void software_node_put(struct fwnode_handle *fwnode)
428{
Heikki Krogerus80488a62019-05-31 17:15:34 +0300429 struct swnode *swnode = to_swnode(fwnode);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300430
431 kobject_put(&swnode->kobj);
432}
433
434static bool software_node_property_present(const struct fwnode_handle *fwnode,
435 const char *propname)
436{
Heikki Krogerus80488a62019-05-31 17:15:34 +0300437 struct swnode *swnode = to_swnode(fwnode);
438
439 return !!property_entry_get(swnode->node->properties, propname);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300440}
441
442static int software_node_read_int_array(const struct fwnode_handle *fwnode,
443 const char *propname,
444 unsigned int elem_size, void *val,
445 size_t nval)
446{
Heikki Krogerus80488a62019-05-31 17:15:34 +0300447 struct swnode *swnode = to_swnode(fwnode);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300448
Heikki Krogerus80488a62019-05-31 17:15:34 +0300449 return property_entry_read_int_array(swnode->node->properties, propname,
Heikki Krogerus59abd832018-11-09 17:21:36 +0300450 elem_size, val, nval);
451}
452
453static int software_node_read_string_array(const struct fwnode_handle *fwnode,
454 const char *propname,
455 const char **val, size_t nval)
456{
Heikki Krogerus80488a62019-05-31 17:15:34 +0300457 struct swnode *swnode = to_swnode(fwnode);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300458
Heikki Krogerus80488a62019-05-31 17:15:34 +0300459 return property_entry_read_string_array(swnode->node->properties,
460 propname, val, nval);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300461}
462
Sakari Ailusbc0500c2019-10-03 15:32:12 +0300463static const char *
464software_node_get_name(const struct fwnode_handle *fwnode)
465{
466 const struct swnode *swnode = to_swnode(fwnode);
467
468 if (!swnode)
469 return "(null)";
470
471 return kobject_name(&swnode->kobj);
472}
473
Sakari Ailuse7e242b2019-10-03 15:32:13 +0300474static const char *
475software_node_get_name_prefix(const struct fwnode_handle *fwnode)
476{
477 struct fwnode_handle *parent;
478 const char *prefix;
479
480 parent = fwnode_get_parent(fwnode);
481 if (!parent)
482 return "";
483
484 /* Figure out the prefix from the parents. */
485 while (is_software_node(parent))
486 parent = fwnode_get_next_parent(parent);
487
488 prefix = fwnode_get_name_prefix(parent);
489 fwnode_handle_put(parent);
490
491 /* Guess something if prefix was NULL. */
492 return prefix ?: "/";
493}
494
YueHaibing0e3edd92019-03-19 23:20:42 +0800495static struct fwnode_handle *
Heikki Krogerus59abd832018-11-09 17:21:36 +0300496software_node_get_parent(const struct fwnode_handle *fwnode)
497{
Heikki Krogerus80488a62019-05-31 17:15:34 +0300498 struct swnode *swnode = to_swnode(fwnode);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300499
Sakari Ailus51c100a2019-10-03 15:32:08 +0300500 if (!swnode || !swnode->parent)
501 return NULL;
502
503 return fwnode_handle_get(&swnode->parent->fwnode);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300504}
505
YueHaibing0e3edd92019-03-19 23:20:42 +0800506static struct fwnode_handle *
Heikki Krogerus59abd832018-11-09 17:21:36 +0300507software_node_get_next_child(const struct fwnode_handle *fwnode,
508 struct fwnode_handle *child)
509{
Heikki Krogerus80488a62019-05-31 17:15:34 +0300510 struct swnode *p = to_swnode(fwnode);
511 struct swnode *c = to_swnode(child);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300512
Colin Ian King1d8f0622018-12-22 12:49:39 +0000513 if (!p || list_empty(&p->children) ||
Heikki Krogerus59abd832018-11-09 17:21:36 +0300514 (c && list_is_last(&c->entry, &p->children)))
515 return NULL;
516
517 if (c)
518 c = list_next_entry(c, entry);
519 else
Heikki Krogerus80488a62019-05-31 17:15:34 +0300520 c = list_first_entry(&p->children, struct swnode, entry);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300521 return &c->fwnode;
522}
523
Heikki Krogerus34479822019-02-13 14:55:49 +0300524static struct fwnode_handle *
525software_node_get_named_child_node(const struct fwnode_handle *fwnode,
526 const char *childname)
527{
Heikki Krogerus80488a62019-05-31 17:15:34 +0300528 struct swnode *swnode = to_swnode(fwnode);
Heikki Krogerus80488a62019-05-31 17:15:34 +0300529 struct swnode *child;
Heikki Krogerus34479822019-02-13 14:55:49 +0300530
531 if (!swnode || list_empty(&swnode->children))
532 return NULL;
533
534 list_for_each_entry(child, &swnode->children, entry) {
Heikki Krogerusc959d0c2019-05-31 17:15:35 +0300535 if (!strcmp(childname, kobject_name(&child->kobj))) {
Heikki Krogerus34479822019-02-13 14:55:49 +0300536 kobject_get(&child->kobj);
537 return &child->fwnode;
538 }
539 }
540 return NULL;
541}
Heikki Krogerus59abd832018-11-09 17:21:36 +0300542
Heikki Krogerusb06184a2019-05-31 17:15:36 +0300543static int
544software_node_get_reference_args(const struct fwnode_handle *fwnode,
545 const char *propname, const char *nargs_prop,
546 unsigned int nargs, unsigned int index,
547 struct fwnode_reference_args *args)
548{
549 struct swnode *swnode = to_swnode(fwnode);
550 const struct software_node_reference *ref;
551 const struct property_entry *prop;
552 struct fwnode_handle *refnode;
553 int i;
554
555 if (!swnode || !swnode->node->references)
556 return -ENOENT;
557
558 for (ref = swnode->node->references; ref->name; ref++)
559 if (!strcmp(ref->name, propname))
560 break;
561
562 if (!ref->name || index > (ref->nrefs - 1))
563 return -ENOENT;
564
565 refnode = software_node_fwnode(ref->refs[index].node);
566 if (!refnode)
567 return -ENOENT;
568
569 if (nargs_prop) {
570 prop = property_entry_get(swnode->node->properties, nargs_prop);
571 if (!prop)
572 return -EINVAL;
573
574 nargs = prop->value.u32_data;
575 }
576
577 if (nargs > NR_FWNODE_REFERENCE_ARGS)
578 return -EINVAL;
579
580 args->fwnode = software_node_get(refnode);
581 args->nargs = nargs;
582
583 for (i = 0; i < nargs; i++)
584 args->args[i] = ref->refs[index].args[i];
585
586 return 0;
587}
588
Heikki Krogerus59abd832018-11-09 17:21:36 +0300589static const struct fwnode_operations software_node_ops = {
590 .get = software_node_get,
591 .put = software_node_put,
592 .property_present = software_node_property_present,
593 .property_read_int_array = software_node_read_int_array,
594 .property_read_string_array = software_node_read_string_array,
Sakari Ailusbc0500c2019-10-03 15:32:12 +0300595 .get_name = software_node_get_name,
Sakari Ailuse7e242b2019-10-03 15:32:13 +0300596 .get_name_prefix = software_node_get_name_prefix,
Heikki Krogerus59abd832018-11-09 17:21:36 +0300597 .get_parent = software_node_get_parent,
598 .get_next_child_node = software_node_get_next_child,
Heikki Krogerus34479822019-02-13 14:55:49 +0300599 .get_named_child_node = software_node_get_named_child_node,
Heikki Krogerusb06184a2019-05-31 17:15:36 +0300600 .get_reference_args = software_node_get_reference_args
Heikki Krogerus59abd832018-11-09 17:21:36 +0300601};
602
603/* -------------------------------------------------------------------------- */
604
Heikki Krogerus1666fae2019-08-19 13:07:22 +0300605/**
606 * software_node_find_by_name - Find software node by name
607 * @parent: Parent of the software node
608 * @name: Name of the software node
609 *
610 * The function will find a node that is child of @parent and that is named
611 * @name. If no node is found, the function returns NULL.
612 *
613 * NOTE: you will need to drop the reference with fwnode_handle_put() after use.
614 */
615const struct software_node *
616software_node_find_by_name(const struct software_node *parent, const char *name)
617{
Heikki Krogerus016049a2019-08-30 10:51:56 +0300618 struct swnode *swnode = NULL;
Heikki Krogerus1666fae2019-08-19 13:07:22 +0300619 struct kobject *k;
620
621 if (!name)
622 return NULL;
623
624 spin_lock(&swnode_kset->list_lock);
625
626 list_for_each_entry(k, &swnode_kset->list, entry) {
627 swnode = kobj_to_swnode(k);
628 if (parent == swnode->node->parent && swnode->node->name &&
629 !strcmp(name, swnode->node->name)) {
630 kobject_get(&swnode->kobj);
631 break;
632 }
633 swnode = NULL;
634 }
635
636 spin_unlock(&swnode_kset->list_lock);
637
638 return swnode ? swnode->node : NULL;
639}
640EXPORT_SYMBOL_GPL(software_node_find_by_name);
641
Heikki Krogerus59abd832018-11-09 17:21:36 +0300642static int
Heikki Krogerus80488a62019-05-31 17:15:34 +0300643software_node_register_properties(struct software_node *node,
Heikki Krogerus59abd832018-11-09 17:21:36 +0300644 const struct property_entry *properties)
645{
646 struct property_entry *props;
647
648 props = property_entries_dup(properties);
649 if (IS_ERR(props))
650 return PTR_ERR(props);
651
Heikki Krogerus80488a62019-05-31 17:15:34 +0300652 node->properties = props;
Heikki Krogerus59abd832018-11-09 17:21:36 +0300653
654 return 0;
655}
656
657static void software_node_release(struct kobject *kobj)
658{
Heikki Krogerus80488a62019-05-31 17:15:34 +0300659 struct swnode *swnode = kobj_to_swnode(kobj);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300660
Heikki Krogerus80488a62019-05-31 17:15:34 +0300661 if (swnode->allocated) {
662 property_entries_free(swnode->node->properties);
663 kfree(swnode->node);
664 }
Heikki Krogerus59abd832018-11-09 17:21:36 +0300665 ida_destroy(&swnode->child_ids);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300666 kfree(swnode);
667}
668
669static struct kobj_type software_node_type = {
670 .release = software_node_release,
671 .sysfs_ops = &kobj_sysfs_ops,
672};
673
Heikki Krogerus80488a62019-05-31 17:15:34 +0300674static struct fwnode_handle *
675swnode_register(const struct software_node *node, struct swnode *parent,
676 unsigned int allocated)
677{
678 struct swnode *swnode;
679 int ret;
680
681 swnode = kzalloc(sizeof(*swnode), GFP_KERNEL);
682 if (!swnode) {
683 ret = -ENOMEM;
684 goto out_err;
685 }
686
687 ret = ida_simple_get(parent ? &parent->child_ids : &swnode_root_ids,
688 0, 0, GFP_KERNEL);
689 if (ret < 0) {
690 kfree(swnode);
691 goto out_err;
692 }
693
694 swnode->id = ret;
695 swnode->node = node;
696 swnode->parent = parent;
697 swnode->allocated = allocated;
698 swnode->kobj.kset = swnode_kset;
699 swnode->fwnode.ops = &software_node_ops;
700
701 ida_init(&swnode->child_ids);
702 INIT_LIST_HEAD(&swnode->entry);
703 INIT_LIST_HEAD(&swnode->children);
704
705 if (node->name)
706 ret = kobject_init_and_add(&swnode->kobj, &software_node_type,
707 parent ? &parent->kobj : NULL,
708 "%s", node->name);
709 else
710 ret = kobject_init_and_add(&swnode->kobj, &software_node_type,
711 parent ? &parent->kobj : NULL,
712 "node%d", swnode->id);
713 if (ret) {
714 kobject_put(&swnode->kobj);
715 return ERR_PTR(ret);
716 }
717
718 if (parent)
719 list_add_tail(&swnode->entry, &parent->children);
720
721 kobject_uevent(&swnode->kobj, KOBJ_ADD);
722 return &swnode->fwnode;
723
724out_err:
725 if (allocated)
726 property_entries_free(node->properties);
727 return ERR_PTR(ret);
728}
729
730/**
731 * software_node_register_nodes - Register an array of software nodes
732 * @nodes: Zero terminated array of software nodes to be registered
733 *
734 * Register multiple software nodes at once.
735 */
736int software_node_register_nodes(const struct software_node *nodes)
737{
738 int ret;
739 int i;
740
741 for (i = 0; nodes[i].name; i++) {
742 ret = software_node_register(&nodes[i]);
743 if (ret) {
744 software_node_unregister_nodes(nodes);
745 return ret;
746 }
747 }
748
749 return 0;
750}
751EXPORT_SYMBOL_GPL(software_node_register_nodes);
752
753/**
754 * software_node_unregister_nodes - Unregister an array of software nodes
755 * @nodes: Zero terminated array of software nodes to be unregistered
756 *
757 * Unregister multiple software nodes at once.
758 */
759void software_node_unregister_nodes(const struct software_node *nodes)
760{
761 struct swnode *swnode;
762 int i;
763
764 for (i = 0; nodes[i].name; i++) {
765 swnode = software_node_to_swnode(&nodes[i]);
766 if (swnode)
767 fwnode_remove_software_node(&swnode->fwnode);
768 }
769}
770EXPORT_SYMBOL_GPL(software_node_unregister_nodes);
771
772/**
773 * software_node_register - Register static software node
774 * @node: The software node to be registered
775 */
776int software_node_register(const struct software_node *node)
777{
778 struct swnode *parent = software_node_to_swnode(node->parent);
779
780 if (software_node_to_swnode(node))
781 return -EEXIST;
782
783 return PTR_ERR_OR_ZERO(swnode_register(node, parent, 0));
784}
785EXPORT_SYMBOL_GPL(software_node_register);
786
Heikki Krogerus59abd832018-11-09 17:21:36 +0300787struct fwnode_handle *
788fwnode_create_software_node(const struct property_entry *properties,
789 const struct fwnode_handle *parent)
790{
Heikki Krogerus80488a62019-05-31 17:15:34 +0300791 struct software_node *node;
792 struct swnode *p = NULL;
Heikki Krogerus59abd832018-11-09 17:21:36 +0300793 int ret;
794
795 if (parent) {
796 if (IS_ERR(parent))
797 return ERR_CAST(parent);
798 if (!is_software_node(parent))
799 return ERR_PTR(-EINVAL);
Heikki Krogerus80488a62019-05-31 17:15:34 +0300800 p = to_swnode(parent);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300801 }
802
Heikki Krogerus80488a62019-05-31 17:15:34 +0300803 node = kzalloc(sizeof(*node), GFP_KERNEL);
804 if (!node)
Heikki Krogerus59abd832018-11-09 17:21:36 +0300805 return ERR_PTR(-ENOMEM);
806
Heikki Krogerus80488a62019-05-31 17:15:34 +0300807 ret = software_node_register_properties(node, properties);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300808 if (ret) {
Heikki Krogerus80488a62019-05-31 17:15:34 +0300809 kfree(node);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300810 return ERR_PTR(ret);
811 }
812
Heikki Krogerus80488a62019-05-31 17:15:34 +0300813 node->parent = p ? p->node : NULL;
Heikki Krogerus59abd832018-11-09 17:21:36 +0300814
Heikki Krogerus80488a62019-05-31 17:15:34 +0300815 return swnode_register(node, p, 1);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300816}
817EXPORT_SYMBOL_GPL(fwnode_create_software_node);
818
819void fwnode_remove_software_node(struct fwnode_handle *fwnode)
820{
Heikki Krogerus80488a62019-05-31 17:15:34 +0300821 struct swnode *swnode = to_swnode(fwnode);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300822
823 if (!swnode)
824 return;
825
Heikki Krogerus3df85a12019-05-31 17:15:33 +0300826 if (swnode->parent) {
827 ida_simple_remove(&swnode->parent->child_ids, swnode->id);
828 list_del(&swnode->entry);
829 } else {
830 ida_simple_remove(&swnode_root_ids, swnode->id);
831 }
832
Heikki Krogerus59abd832018-11-09 17:21:36 +0300833 kobject_put(&swnode->kobj);
834}
835EXPORT_SYMBOL_GPL(fwnode_remove_software_node);
836
837int software_node_notify(struct device *dev, unsigned long action)
838{
839 struct fwnode_handle *fwnode = dev_fwnode(dev);
Heikki Krogerus80488a62019-05-31 17:15:34 +0300840 struct swnode *swnode;
Heikki Krogerus59abd832018-11-09 17:21:36 +0300841 int ret;
842
843 if (!fwnode)
844 return 0;
845
846 if (!is_software_node(fwnode))
847 fwnode = fwnode->secondary;
848 if (!is_software_node(fwnode))
849 return 0;
850
Heikki Krogerus80488a62019-05-31 17:15:34 +0300851 swnode = to_swnode(fwnode);
Heikki Krogerus59abd832018-11-09 17:21:36 +0300852
853 switch (action) {
854 case KOBJ_ADD:
855 ret = sysfs_create_link(&dev->kobj, &swnode->kobj,
856 "software_node");
857 if (ret)
858 break;
859
860 ret = sysfs_create_link(&swnode->kobj, &dev->kobj,
861 dev_name(dev));
862 if (ret) {
863 sysfs_remove_link(&dev->kobj, "software_node");
864 break;
865 }
866 kobject_get(&swnode->kobj);
867 break;
868 case KOBJ_REMOVE:
869 sysfs_remove_link(&swnode->kobj, dev_name(dev));
870 sysfs_remove_link(&dev->kobj, "software_node");
871 kobject_put(&swnode->kobj);
872 break;
873 default:
874 break;
875 }
876
877 return 0;
878}
879
880static int __init software_node_init(void)
881{
882 swnode_kset = kset_create_and_add("software_nodes", NULL, kernel_kobj);
883 if (!swnode_kset)
884 return -ENOMEM;
885 return 0;
886}
887postcore_initcall(software_node_init);
888
889static void __exit software_node_exit(void)
890{
891 ida_destroy(&swnode_root_ids);
892 kset_unregister(swnode_kset);
893}
894__exitcall(software_node_exit);