summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-devices-software_node10
-rw-r--r--drivers/acpi/bus.c1
-rw-r--r--drivers/acpi/glue.c21
-rw-r--r--drivers/acpi/internal.h1
-rw-r--r--drivers/base/Makefile2
-rw-r--r--drivers/base/core.c34
-rw-r--r--drivers/base/platform.c1
-rw-r--r--drivers/base/property.c513
-rw-r--r--drivers/base/swnode.c675
-rw-r--r--include/linux/acpi.h10
-rw-r--r--include/linux/property.h12
11 files changed, 766 insertions, 514 deletions
diff --git a/Documentation/ABI/testing/sysfs-devices-software_node b/Documentation/ABI/testing/sysfs-devices-software_node
new file mode 100644
index 000000000000..85df37de359f
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-software_node
@@ -0,0 +1,10 @@
1What: /sys/devices/.../software_node/
2Date: January 2019
3Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
4Description:
5 This directory contains the details about the device that are
6 assigned in kernel (i.e. software), as opposed to the
7 firmware_node directory which contains the details that are
8 assigned for the device in firmware. The main attributes in the
9 directory will show the properties the device has, and the
10 relationship it has to some of the other devices.
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index bb3d96dea6db..99d820a693a8 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -1237,7 +1237,6 @@ static int __init acpi_init(void)
1237 acpi_kobj = NULL; 1237 acpi_kobj = NULL;
1238 } 1238 }
1239 1239
1240 init_acpi_device_notify();
1241 result = acpi_bus_init(); 1240 result = acpi_bus_init();
1242 if (result) { 1241 if (result) {
1243 disable_acpi(); 1242 disable_acpi();
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index 12ba2bee8789..edd10b3c7ec8 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -296,7 +296,7 @@ int acpi_unbind_one(struct device *dev)
296} 296}
297EXPORT_SYMBOL_GPL(acpi_unbind_one); 297EXPORT_SYMBOL_GPL(acpi_unbind_one);
298 298
299static int acpi_platform_notify(struct device *dev) 299static int acpi_device_notify(struct device *dev)
300{ 300{
301 struct acpi_bus_type *type = acpi_get_bus_type(dev); 301 struct acpi_bus_type *type = acpi_get_bus_type(dev);
302 struct acpi_device *adev; 302 struct acpi_device *adev;
@@ -343,7 +343,7 @@ static int acpi_platform_notify(struct device *dev)
343 return ret; 343 return ret;
344} 344}
345 345
346static int acpi_platform_notify_remove(struct device *dev) 346static int acpi_device_notify_remove(struct device *dev)
347{ 347{
348 struct acpi_device *adev = ACPI_COMPANION(dev); 348 struct acpi_device *adev = ACPI_COMPANION(dev);
349 struct acpi_bus_type *type; 349 struct acpi_bus_type *type;
@@ -361,12 +361,17 @@ static int acpi_platform_notify_remove(struct device *dev)
361 return 0; 361 return 0;
362} 362}
363 363
364void __init init_acpi_device_notify(void) 364int acpi_platform_notify(struct device *dev, enum kobject_action action)
365{ 365{
366 if (platform_notify || platform_notify_remove) { 366 switch (action) {
367 printk(KERN_ERR PREFIX "Can't use platform_notify\n"); 367 case KOBJ_ADD:
368 return; 368 acpi_device_notify(dev);
369 break;
370 case KOBJ_REMOVE:
371 acpi_device_notify_remove(dev);
372 break;
373 default:
374 break;
369 } 375 }
370 platform_notify = acpi_platform_notify; 376 return 0;
371 platform_notify_remove = acpi_platform_notify_remove;
372} 377}
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index c112dccc76b5..7e6952edb5b0 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -23,7 +23,6 @@
23int early_acpi_osi_init(void); 23int early_acpi_osi_init(void);
24int acpi_osi_init(void); 24int acpi_osi_init(void);
25acpi_status acpi_os_initialize1(void); 25acpi_status acpi_os_initialize1(void);
26void init_acpi_device_notify(void);
27int acpi_scan_init(void); 26int acpi_scan_init(void);
28#ifdef CONFIG_PCI 27#ifdef CONFIG_PCI
29void acpi_pci_root_init(void); 28void acpi_pci_root_init(void);
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 704f44295810..157452080f3d 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -6,7 +6,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \
6 cpu.o firmware.o init.o map.o devres.o \ 6 cpu.o firmware.o init.o map.o devres.o \
7 attribute_container.o transport_class.o \ 7 attribute_container.o transport_class.o \
8 topology.o container.o property.o cacheinfo.o \ 8 topology.o container.o property.o cacheinfo.o \
9 devcon.o 9 devcon.o swnode.o
10obj-$(CONFIG_DEVTMPFS) += devtmpfs.o 10obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
11obj-y += power/ 11obj-y += power/
12obj-$(CONFIG_ISA_BUS_API) += isa.o 12obj-$(CONFIG_ISA_BUS_API) += isa.o
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 04bbcd779e11..a2f14098663f 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -8,6 +8,7 @@
8 * Copyright (c) 2006 Novell, Inc. 8 * Copyright (c) 2006 Novell, Inc.
9 */ 9 */
10 10
11#include <linux/acpi.h>
11#include <linux/device.h> 12#include <linux/device.h>
12#include <linux/err.h> 13#include <linux/err.h>
13#include <linux/fwnode.h> 14#include <linux/fwnode.h>
@@ -728,6 +729,26 @@ static inline int device_is_not_partition(struct device *dev)
728} 729}
729#endif 730#endif
730 731
732static int
733device_platform_notify(struct device *dev, enum kobject_action action)
734{
735 int ret;
736
737 ret = acpi_platform_notify(dev, action);
738 if (ret)
739 return ret;
740
741 ret = software_node_notify(dev, action);
742 if (ret)
743 return ret;
744
745 if (platform_notify && action == KOBJ_ADD)
746 platform_notify(dev);
747 else if (platform_notify_remove && action == KOBJ_REMOVE)
748 platform_notify_remove(dev);
749 return 0;
750}
751
731/** 752/**
732 * dev_driver_string - Return a device's driver name, if at all possible 753 * dev_driver_string - Return a device's driver name, if at all possible
733 * @dev: struct device to get the name of 754 * @dev: struct device to get the name of
@@ -1883,8 +1904,9 @@ int device_add(struct device *dev)
1883 } 1904 }
1884 1905
1885 /* notify platform of device entry */ 1906 /* notify platform of device entry */
1886 if (platform_notify) 1907 error = device_platform_notify(dev, KOBJ_ADD);
1887 platform_notify(dev); 1908 if (error)
1909 goto platform_error;
1888 1910
1889 error = device_create_file(dev, &dev_attr_uevent); 1911 error = device_create_file(dev, &dev_attr_uevent);
1890 if (error) 1912 if (error)
@@ -1960,6 +1982,8 @@ done:
1960 SymlinkError: 1982 SymlinkError:
1961 device_remove_file(dev, &dev_attr_uevent); 1983 device_remove_file(dev, &dev_attr_uevent);
1962 attrError: 1984 attrError:
1985 device_platform_notify(dev, KOBJ_REMOVE);
1986platform_error:
1963 kobject_uevent(&dev->kobj, KOBJ_REMOVE); 1987 kobject_uevent(&dev->kobj, KOBJ_REMOVE);
1964 glue_dir = get_glue_dir(dev); 1988 glue_dir = get_glue_dir(dev);
1965 kobject_del(&dev->kobj); 1989 kobject_del(&dev->kobj);
@@ -2077,14 +2101,10 @@ void device_del(struct device *dev)
2077 bus_remove_device(dev); 2101 bus_remove_device(dev);
2078 device_pm_remove(dev); 2102 device_pm_remove(dev);
2079 driver_deferred_probe_del(dev); 2103 driver_deferred_probe_del(dev);
2104 device_platform_notify(dev, KOBJ_REMOVE);
2080 device_remove_properties(dev); 2105 device_remove_properties(dev);
2081 device_links_purge(dev); 2106 device_links_purge(dev);
2082 2107
2083 /* Notify the platform of the removal, in case they
2084 * need to do anything...
2085 */
2086 if (platform_notify_remove)
2087 platform_notify_remove(dev);
2088 if (dev->bus) 2108 if (dev->bus)
2089 blocking_notifier_call_chain(&dev->bus->p->bus_notifier, 2109 blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
2090 BUS_NOTIFY_REMOVED_DEVICE, dev); 2110 BUS_NOTIFY_REMOVED_DEVICE, dev);
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 41b91af95afb..0fb5f140f1b0 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -448,7 +448,6 @@ void platform_device_del(struct platform_device *pdev)
448 int i; 448 int i;
449 449
450 if (pdev) { 450 if (pdev) {
451 device_remove_properties(&pdev->dev);
452 device_del(&pdev->dev); 451 device_del(&pdev->dev);
453 452
454 if (pdev->id_auto) { 453 if (pdev->id_auto) {
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 240ab5230ff6..8b91ab380d14 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -18,236 +18,6 @@
18#include <linux/etherdevice.h> 18#include <linux/etherdevice.h>
19#include <linux/phy.h> 19#include <linux/phy.h>
20 20
21struct property_set {
22 struct device *dev;
23 struct fwnode_handle fwnode;
24 const struct property_entry *properties;
25};
26
27static const struct fwnode_operations pset_fwnode_ops;
28
29static inline bool is_pset_node(const struct fwnode_handle *fwnode)
30{
31 return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &pset_fwnode_ops;
32}
33
34#define to_pset_node(__fwnode) \
35 ({ \
36 typeof(__fwnode) __to_pset_node_fwnode = __fwnode; \
37 \
38 is_pset_node(__to_pset_node_fwnode) ? \
39 container_of(__to_pset_node_fwnode, \
40 struct property_set, fwnode) : \
41 NULL; \
42 })
43
44static const struct property_entry *
45pset_prop_get(const struct property_set *pset, const char *name)
46{
47 const struct property_entry *prop;
48
49 if (!pset || !pset->properties)
50 return NULL;
51
52 for (prop = pset->properties; prop->name; prop++)
53 if (!strcmp(name, prop->name))
54 return prop;
55
56 return NULL;
57}
58
59static const void *property_get_pointer(const struct property_entry *prop)
60{
61 switch (prop->type) {
62 case DEV_PROP_U8:
63 if (prop->is_array)
64 return prop->pointer.u8_data;
65 return &prop->value.u8_data;
66 case DEV_PROP_U16:
67 if (prop->is_array)
68 return prop->pointer.u16_data;
69 return &prop->value.u16_data;
70 case DEV_PROP_U32:
71 if (prop->is_array)
72 return prop->pointer.u32_data;
73 return &prop->value.u32_data;
74 case DEV_PROP_U64:
75 if (prop->is_array)
76 return prop->pointer.u64_data;
77 return &prop->value.u64_data;
78 case DEV_PROP_STRING:
79 if (prop->is_array)
80 return prop->pointer.str;
81 return &prop->value.str;
82 default:
83 return NULL;
84 }
85}
86
87static void property_set_pointer(struct property_entry *prop, const void *pointer)
88{
89 switch (prop->type) {
90 case DEV_PROP_U8:
91 if (prop->is_array)
92 prop->pointer.u8_data = pointer;
93 else
94 prop->value.u8_data = *((u8 *)pointer);
95 break;
96 case DEV_PROP_U16:
97 if (prop->is_array)
98 prop->pointer.u16_data = pointer;
99 else
100 prop->value.u16_data = *((u16 *)pointer);
101 break;
102 case DEV_PROP_U32:
103 if (prop->is_array)
104 prop->pointer.u32_data = pointer;
105 else
106 prop->value.u32_data = *((u32 *)pointer);
107 break;
108 case DEV_PROP_U64:
109 if (prop->is_array)
110 prop->pointer.u64_data = pointer;
111 else
112 prop->value.u64_data = *((u64 *)pointer);
113 break;
114 case DEV_PROP_STRING:
115 if (prop->is_array)
116 prop->pointer.str = pointer;
117 else
118 prop->value.str = pointer;
119 break;
120 default:
121 break;
122 }
123}
124
125static const void *pset_prop_find(const struct property_set *pset,
126 const char *propname, size_t length)
127{
128 const struct property_entry *prop;
129 const void *pointer;
130
131 prop = pset_prop_get(pset, propname);
132 if (!prop)
133 return ERR_PTR(-EINVAL);
134 pointer = property_get_pointer(prop);
135 if (!pointer)
136 return ERR_PTR(-ENODATA);
137 if (length > prop->length)
138 return ERR_PTR(-EOVERFLOW);
139 return pointer;
140}
141
142static int pset_prop_read_u8_array(const struct property_set *pset,
143 const char *propname,
144 u8 *values, size_t nval)
145{
146 const void *pointer;
147 size_t length = nval * sizeof(*values);
148
149 pointer = pset_prop_find(pset, propname, length);
150 if (IS_ERR(pointer))
151 return PTR_ERR(pointer);
152
153 memcpy(values, pointer, length);
154 return 0;
155}
156
157static int pset_prop_read_u16_array(const struct property_set *pset,
158 const char *propname,
159 u16 *values, size_t nval)
160{
161 const void *pointer;
162 size_t length = nval * sizeof(*values);
163
164 pointer = pset_prop_find(pset, propname, length);
165 if (IS_ERR(pointer))
166 return PTR_ERR(pointer);
167
168 memcpy(values, pointer, length);
169 return 0;
170}
171
172static int pset_prop_read_u32_array(const struct property_set *pset,
173 const char *propname,
174 u32 *values, size_t nval)
175{
176 const void *pointer;
177 size_t length = nval * sizeof(*values);
178
179 pointer = pset_prop_find(pset, propname, length);
180 if (IS_ERR(pointer))
181 return PTR_ERR(pointer);
182
183 memcpy(values, pointer, length);
184 return 0;
185}
186
187static int pset_prop_read_u64_array(const struct property_set *pset,
188 const char *propname,
189 u64 *values, size_t nval)
190{
191 const void *pointer;
192 size_t length = nval * sizeof(*values);
193
194 pointer = pset_prop_find(pset, propname, length);
195 if (IS_ERR(pointer))
196 return PTR_ERR(pointer);
197
198 memcpy(values, pointer, length);
199 return 0;
200}
201
202static int pset_prop_count_elems_of_size(const struct property_set *pset,
203 const char *propname, size_t length)
204{
205 const struct property_entry *prop;
206
207 prop = pset_prop_get(pset, propname);
208 if (!prop)
209 return -EINVAL;
210
211 return prop->length / length;
212}
213
214static int pset_prop_read_string_array(const struct property_set *pset,
215 const char *propname,
216 const char **strings, size_t nval)
217{
218 const struct property_entry *prop;
219 const void *pointer;
220 size_t array_len, length;
221
222 /* Find out the array length. */
223 prop = pset_prop_get(pset, propname);
224 if (!prop)
225 return -EINVAL;
226
227 if (!prop->is_array)
228 /* The array length for a non-array string property is 1. */
229 array_len = 1;
230 else
231 /* Find the length of an array. */
232 array_len = pset_prop_count_elems_of_size(pset, propname,
233 sizeof(const char *));
234
235 /* Return how many there are if strings is NULL. */
236 if (!strings)
237 return array_len;
238
239 array_len = min(nval, array_len);
240 length = array_len * sizeof(*strings);
241
242 pointer = pset_prop_find(pset, propname, length);
243 if (IS_ERR(pointer))
244 return PTR_ERR(pointer);
245
246 memcpy(strings, pointer, length);
247
248 return array_len;
249}
250
251struct fwnode_handle *dev_fwnode(struct device *dev) 21struct fwnode_handle *dev_fwnode(struct device *dev)
252{ 22{
253 return IS_ENABLED(CONFIG_OF) && dev->of_node ? 23 return IS_ENABLED(CONFIG_OF) && dev->of_node ?
@@ -255,51 +25,6 @@ struct fwnode_handle *dev_fwnode(struct device *dev)
255} 25}
256EXPORT_SYMBOL_GPL(dev_fwnode); 26EXPORT_SYMBOL_GPL(dev_fwnode);
257 27
258static bool pset_fwnode_property_present(const struct fwnode_handle *fwnode,
259 const char *propname)
260{
261 return !!pset_prop_get(to_pset_node(fwnode), propname);
262}
263
264static int pset_fwnode_read_int_array(const struct fwnode_handle *fwnode,
265 const char *propname,
266 unsigned int elem_size, void *val,
267 size_t nval)
268{
269 const struct property_set *node = to_pset_node(fwnode);
270
271 if (!val)
272 return pset_prop_count_elems_of_size(node, propname, elem_size);
273
274 switch (elem_size) {
275 case sizeof(u8):
276 return pset_prop_read_u8_array(node, propname, val, nval);
277 case sizeof(u16):
278 return pset_prop_read_u16_array(node, propname, val, nval);
279 case sizeof(u32):
280 return pset_prop_read_u32_array(node, propname, val, nval);
281 case sizeof(u64):
282 return pset_prop_read_u64_array(node, propname, val, nval);
283 }
284
285 return -ENXIO;
286}
287
288static int
289pset_fwnode_property_read_string_array(const struct fwnode_handle *fwnode,
290 const char *propname,
291 const char **val, size_t nval)
292{
293 return pset_prop_read_string_array(to_pset_node(fwnode), propname,
294 val, nval);
295}
296
297static const struct fwnode_operations pset_fwnode_ops = {
298 .property_present = pset_fwnode_property_present,
299 .property_read_int_array = pset_fwnode_read_int_array,
300 .property_read_string_array = pset_fwnode_property_read_string_array,
301};
302
303/** 28/**
304 * device_property_present - check if a property of a device is present 29 * device_property_present - check if a property of a device is present
305 * @dev: Device whose property is being checked 30 * @dev: Device whose property is being checked
@@ -759,223 +484,25 @@ int fwnode_property_get_reference_args(const struct fwnode_handle *fwnode,
759} 484}
760EXPORT_SYMBOL_GPL(fwnode_property_get_reference_args); 485EXPORT_SYMBOL_GPL(fwnode_property_get_reference_args);
761 486
762static void property_entry_free_data(const struct property_entry *p)
763{
764 const void *pointer = property_get_pointer(p);
765 size_t i, nval;
766
767 if (p->is_array) {
768 if (p->type == DEV_PROP_STRING && p->pointer.str) {
769 nval = p->length / sizeof(const char *);
770 for (i = 0; i < nval; i++)
771 kfree(p->pointer.str[i]);
772 }
773 kfree(pointer);
774 } else if (p->type == DEV_PROP_STRING) {
775 kfree(p->value.str);
776 }
777 kfree(p->name);
778}
779
780static int property_copy_string_array(struct property_entry *dst,
781 const struct property_entry *src)
782{
783 const char **d;
784 size_t nval = src->length / sizeof(*d);
785 int i;
786
787 d = kcalloc(nval, sizeof(*d), GFP_KERNEL);
788 if (!d)
789 return -ENOMEM;
790
791 for (i = 0; i < nval; i++) {
792 d[i] = kstrdup(src->pointer.str[i], GFP_KERNEL);
793 if (!d[i] && src->pointer.str[i]) {
794 while (--i >= 0)
795 kfree(d[i]);
796 kfree(d);
797 return -ENOMEM;
798 }
799 }
800
801 dst->pointer.str = d;
802 return 0;
803}
804
805static int property_entry_copy_data(struct property_entry *dst,
806 const struct property_entry *src)
807{
808 const void *pointer = property_get_pointer(src);
809 const void *new;
810 int error;
811
812 if (src->is_array) {
813 if (!src->length)
814 return -ENODATA;
815
816 if (src->type == DEV_PROP_STRING) {
817 error = property_copy_string_array(dst, src);
818 if (error)
819 return error;
820 new = dst->pointer.str;
821 } else {
822 new = kmemdup(pointer, src->length, GFP_KERNEL);
823 if (!new)
824 return -ENOMEM;
825 }
826 } else if (src->type == DEV_PROP_STRING) {
827 new = kstrdup(src->value.str, GFP_KERNEL);
828 if (!new && src->value.str)
829 return -ENOMEM;
830 } else {
831 new = pointer;
832 }
833
834 dst->length = src->length;
835 dst->is_array = src->is_array;
836 dst->type = src->type;
837
838 property_set_pointer(dst, new);
839
840 dst->name = kstrdup(src->name, GFP_KERNEL);
841 if (!dst->name)
842 goto out_free_data;
843
844 return 0;
845
846out_free_data:
847 property_entry_free_data(dst);
848 return -ENOMEM;
849}
850
851/**
852 * property_entries_dup - duplicate array of properties
853 * @properties: array of properties to copy
854 *
855 * This function creates a deep copy of the given NULL-terminated array
856 * of property entries.
857 */
858struct property_entry *
859property_entries_dup(const struct property_entry *properties)
860{
861 struct property_entry *p;
862 int i, n = 0;
863
864 while (properties[n].name)
865 n++;
866
867 p = kcalloc(n + 1, sizeof(*p), GFP_KERNEL);
868 if (!p)
869 return ERR_PTR(-ENOMEM);
870
871 for (i = 0; i < n; i++) {
872 int ret = property_entry_copy_data(&p[i], &properties[i]);
873 if (ret) {
874 while (--i >= 0)
875 property_entry_free_data(&p[i]);
876 kfree(p);
877 return ERR_PTR(ret);
878 }
879 }
880
881 return p;
882}
883EXPORT_SYMBOL_GPL(property_entries_dup);
884
885/**
886 * property_entries_free - free previously allocated array of properties
887 * @properties: array of properties to destroy
888 *
889 * This function frees given NULL-terminated array of property entries,
890 * along with their data.
891 */
892void property_entries_free(const struct property_entry *properties)
893{
894 const struct property_entry *p;
895
896 for (p = properties; p->name; p++)
897 property_entry_free_data(p);
898
899 kfree(properties);
900}
901EXPORT_SYMBOL_GPL(property_entries_free);
902
903/**
904 * pset_free_set - releases memory allocated for copied property set
905 * @pset: Property set to release
906 *
907 * Function takes previously copied property set and releases all the
908 * memory allocated to it.
909 */
910static void pset_free_set(struct property_set *pset)
911{
912 if (!pset)
913 return;
914
915 property_entries_free(pset->properties);
916 kfree(pset);
917}
918
919/**
920 * pset_copy_set - copies property set
921 * @pset: Property set to copy
922 *
923 * This function takes a deep copy of the given property set and returns
924 * pointer to the copy. Call device_free_property_set() to free resources
925 * allocated in this function.
926 *
927 * Return: Pointer to the new property set or error pointer.
928 */
929static struct property_set *pset_copy_set(const struct property_set *pset)
930{
931 struct property_entry *properties;
932 struct property_set *p;
933
934 p = kzalloc(sizeof(*p), GFP_KERNEL);
935 if (!p)
936 return ERR_PTR(-ENOMEM);
937
938 properties = property_entries_dup(pset->properties);
939 if (IS_ERR(properties)) {
940 kfree(p);
941 return ERR_CAST(properties);
942 }
943
944 p->properties = properties;
945 return p;
946}
947
948/** 487/**
949 * device_remove_properties - Remove properties from a device object. 488 * device_remove_properties - Remove properties from a device object.
950 * @dev: Device whose properties to remove. 489 * @dev: Device whose properties to remove.
951 * 490 *
952 * The function removes properties previously associated to the device 491 * The function removes properties previously associated to the device
953 * secondary firmware node with device_add_properties(). Memory allocated 492 * firmware node with device_add_properties(). Memory allocated to the
954 * to the properties will also be released. 493 * properties will also be released.
955 */ 494 */
956void device_remove_properties(struct device *dev) 495void device_remove_properties(struct device *dev)
957{ 496{
958 struct fwnode_handle *fwnode; 497 struct fwnode_handle *fwnode = dev_fwnode(dev);
959 struct property_set *pset;
960 498
961 fwnode = dev_fwnode(dev);
962 if (!fwnode) 499 if (!fwnode)
963 return; 500 return;
964 /* 501
965 * Pick either primary or secondary node depending which one holds 502 if (is_software_node(fwnode->secondary)) {
966 * the pset. If there is no real firmware node (ACPI/DT) primary 503 fwnode_remove_software_node(fwnode->secondary);
967 * will hold the pset. 504 set_secondary_fwnode(dev, NULL);
968 */
969 pset = to_pset_node(fwnode);
970 if (pset) {
971 set_primary_fwnode(dev, NULL);
972 } else {
973 pset = to_pset_node(fwnode->secondary);
974 if (pset && dev == pset->dev)
975 set_secondary_fwnode(dev, NULL);
976 } 505 }
977 if (pset && dev == pset->dev)
978 pset_free_set(pset);
979} 506}
980EXPORT_SYMBOL_GPL(device_remove_properties); 507EXPORT_SYMBOL_GPL(device_remove_properties);
981 508
@@ -985,26 +512,22 @@ EXPORT_SYMBOL_GPL(device_remove_properties);
985 * @properties: Collection of properties to add. 512 * @properties: Collection of properties to add.
986 * 513 *
987 * Associate a collection of device properties represented by @properties with 514 * Associate a collection of device properties represented by @properties with
988 * @dev as its secondary firmware node. The function takes a copy of 515 * @dev. The function takes a copy of @properties.
989 * @properties. 516 *
517 * WARNING: The callers should not use this function if it is known that there
518 * is no real firmware node associated with @dev! In that case the callers
519 * should create a software node and assign it to @dev directly.
990 */ 520 */
991int device_add_properties(struct device *dev, 521int device_add_properties(struct device *dev,
992 const struct property_entry *properties) 522 const struct property_entry *properties)
993{ 523{
994 struct property_set *p, pset; 524 struct fwnode_handle *fwnode;
995
996 if (!properties)
997 return -EINVAL;
998
999 pset.properties = properties;
1000 525
1001 p = pset_copy_set(&pset); 526 fwnode = fwnode_create_software_node(properties, NULL);
1002 if (IS_ERR(p)) 527 if (IS_ERR(fwnode))
1003 return PTR_ERR(p); 528 return PTR_ERR(fwnode);
1004 529
1005 p->fwnode.ops = &pset_fwnode_ops; 530 set_secondary_fwnode(dev, fwnode);
1006 set_secondary_fwnode(dev, &p->fwnode);
1007 p->dev = dev;
1008 return 0; 531 return 0;
1009} 532}
1010EXPORT_SYMBOL_GPL(device_add_properties); 533EXPORT_SYMBOL_GPL(device_add_properties);
@@ -1341,7 +864,7 @@ int fwnode_irq_get(struct fwnode_handle *fwnode, unsigned int index)
1341EXPORT_SYMBOL(fwnode_irq_get); 864EXPORT_SYMBOL(fwnode_irq_get);
1342 865
1343/** 866/**
1344 * device_graph_get_next_endpoint - Get next endpoint firmware node 867 * fwnode_graph_get_next_endpoint - Get next endpoint firmware node
1345 * @fwnode: Pointer to the parent firmware node 868 * @fwnode: Pointer to the parent firmware node
1346 * @prev: Previous endpoint node or %NULL to get the first 869 * @prev: Previous endpoint node or %NULL to get the first
1347 * 870 *
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
new file mode 100644
index 000000000000..306bb93287af
--- /dev/null
+++ b/drivers/base/swnode.c
@@ -0,0 +1,675 @@
1// 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
14struct software_node {
15 int id;
16 struct kobject kobj;
17 struct fwnode_handle fwnode;
18
19 /* hierarchy */
20 struct ida child_ids;
21 struct list_head entry;
22 struct list_head children;
23 struct software_node *parent;
24
25 /* properties */
26 const struct property_entry *properties;
27};
28
29static DEFINE_IDA(swnode_root_ids);
30static struct kset *swnode_kset;
31
32#define kobj_to_swnode(_kobj_) container_of(_kobj_, struct software_node, kobj)
33
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}
40
41#define to_software_node(__fwnode) \
42 ({ \
43 typeof(__fwnode) __to_software_node_fwnode = __fwnode; \
44 \
45 is_software_node(__to_software_node_fwnode) ? \
46 container_of(__to_software_node_fwnode, \
47 struct software_node, fwnode) : \
48 NULL; \
49 })
50
51/* -------------------------------------------------------------------------- */
52/* property_entry processing */
53
54static const struct property_entry *
55property_entry_get(const struct property_entry *prop, const char *name)
56{
57 if (!prop)
58 return NULL;
59
60 for (; prop->name; prop++)
61 if (!strcmp(name, prop->name))
62 return prop;
63
64 return NULL;
65}
66
67static void
68property_set_pointer(struct property_entry *prop, const void *pointer)
69{
70 switch (prop->type) {
71 case DEV_PROP_U8:
72 if (prop->is_array)
73 prop->pointer.u8_data = pointer;
74 else
75 prop->value.u8_data = *((u8 *)pointer);
76 break;
77 case DEV_PROP_U16:
78 if (prop->is_array)
79 prop->pointer.u16_data = pointer;
80 else
81 prop->value.u16_data = *((u16 *)pointer);
82 break;
83 case DEV_PROP_U32:
84 if (prop->is_array)
85 prop->pointer.u32_data = pointer;
86 else
87 prop->value.u32_data = *((u32 *)pointer);
88 break;
89 case DEV_PROP_U64:
90 if (prop->is_array)
91 prop->pointer.u64_data = pointer;
92 else
93 prop->value.u64_data = *((u64 *)pointer);
94 break;
95 case DEV_PROP_STRING:
96 if (prop->is_array)
97 prop->pointer.str = pointer;
98 else
99 prop->value.str = pointer;
100 break;
101 default:
102 break;
103 }
104}
105
106static const void *property_get_pointer(const struct property_entry *prop)
107{
108 switch (prop->type) {
109 case DEV_PROP_U8:
110 if (prop->is_array)
111 return prop->pointer.u8_data;
112 return &prop->value.u8_data;
113 case DEV_PROP_U16:
114 if (prop->is_array)
115 return prop->pointer.u16_data;
116 return &prop->value.u16_data;
117 case DEV_PROP_U32:
118 if (prop->is_array)
119 return prop->pointer.u32_data;
120 return &prop->value.u32_data;
121 case DEV_PROP_U64:
122 if (prop->is_array)
123 return prop->pointer.u64_data;
124 return &prop->value.u64_data;
125 case DEV_PROP_STRING:
126 if (prop->is_array)
127 return prop->pointer.str;
128 return &prop->value.str;
129 default:
130 return NULL;
131 }
132}
133
134static const void *property_entry_find(const struct property_entry *props,
135 const char *propname, size_t length)
136{
137 const struct property_entry *prop;
138 const void *pointer;
139
140 prop = property_entry_get(props, propname);
141 if (!prop)
142 return ERR_PTR(-EINVAL);
143 pointer = property_get_pointer(prop);
144 if (!pointer)
145 return ERR_PTR(-ENODATA);
146 if (length > prop->length)
147 return ERR_PTR(-EOVERFLOW);
148 return pointer;
149}
150
151static int property_entry_read_u8_array(const struct property_entry *props,
152 const char *propname,
153 u8 *values, size_t nval)
154{
155 const void *pointer;
156 size_t length = nval * sizeof(*values);
157
158 pointer = property_entry_find(props, propname, length);
159 if (IS_ERR(pointer))
160 return PTR_ERR(pointer);
161
162 memcpy(values, pointer, length);
163 return 0;
164}
165
166static int property_entry_read_u16_array(const struct property_entry *props,
167 const char *propname,
168 u16 *values, size_t nval)
169{
170 const void *pointer;
171 size_t length = nval * sizeof(*values);
172
173 pointer = property_entry_find(props, propname, length);
174 if (IS_ERR(pointer))
175 return PTR_ERR(pointer);
176
177 memcpy(values, pointer, length);
178 return 0;
179}
180
181static int property_entry_read_u32_array(const struct property_entry *props,
182 const char *propname,
183 u32 *values, size_t nval)
184{
185 const void *pointer;
186 size_t length = nval * sizeof(*values);
187
188 pointer = property_entry_find(props, propname, length);
189 if (IS_ERR(pointer))
190 return PTR_ERR(pointer);
191
192 memcpy(values, pointer, length);
193 return 0;
194}
195
196static int property_entry_read_u64_array(const struct property_entry *props,
197 const char *propname,
198 u64 *values, size_t nval)
199{
200 const void *pointer;
201 size_t length = nval * sizeof(*values);
202
203 pointer = property_entry_find(props, propname, length);
204 if (IS_ERR(pointer))
205 return PTR_ERR(pointer);
206
207 memcpy(values, pointer, length);
208 return 0;
209}
210
211static int
212property_entry_count_elems_of_size(const struct property_entry *props,
213 const char *propname, size_t length)
214{
215 const struct property_entry *prop;
216
217 prop = property_entry_get(props, propname);
218 if (!prop)
219 return -EINVAL;
220
221 return prop->length / length;
222}
223
224static int property_entry_read_int_array(const struct property_entry *props,
225 const char *name,
226 unsigned int elem_size, void *val,
227 size_t nval)
228{
229 if (!val)
230 return property_entry_count_elems_of_size(props, name,
231 elem_size);
232 switch (elem_size) {
233 case sizeof(u8):
234 return property_entry_read_u8_array(props, name, val, nval);
235 case sizeof(u16):
236 return property_entry_read_u16_array(props, name, val, nval);
237 case sizeof(u32):
238 return property_entry_read_u32_array(props, name, val, nval);
239 case sizeof(u64):
240 return property_entry_read_u64_array(props, name, val, nval);
241 }
242
243 return -ENXIO;
244}
245
246static int property_entry_read_string_array(const struct property_entry *props,
247 const char *propname,
248 const char **strings, size_t nval)
249{
250 const struct property_entry *prop;
251 const void *pointer;
252 size_t array_len, length;
253
254 /* Find out the array length. */
255 prop = property_entry_get(props, propname);
256 if (!prop)
257 return -EINVAL;
258
259 if (prop->is_array)
260 /* Find the length of an array. */
261 array_len = property_entry_count_elems_of_size(props, propname,
262 sizeof(const char *));
263 else
264 /* The array length for a non-array string property is 1. */
265 array_len = 1;
266
267 /* Return how many there are if strings is NULL. */
268 if (!strings)
269 return array_len;
270
271 array_len = min(nval, array_len);
272 length = array_len * sizeof(*strings);
273
274 pointer = property_entry_find(props, propname, length);
275 if (IS_ERR(pointer))
276 return PTR_ERR(pointer);
277
278 memcpy(strings, pointer, length);
279
280 return array_len;
281}
282
283static void property_entry_free_data(const struct property_entry *p)
284{
285 const void *pointer = property_get_pointer(p);
286 size_t i, nval;
287
288 if (p->is_array) {
289 if (p->type == DEV_PROP_STRING && p->pointer.str) {
290 nval = p->length / sizeof(const char *);
291 for (i = 0; i < nval; i++)
292 kfree(p->pointer.str[i]);
293 }
294 kfree(pointer);
295 } else if (p->type == DEV_PROP_STRING) {
296 kfree(p->value.str);
297 }
298 kfree(p->name);
299}
300
301static int property_copy_string_array(struct property_entry *dst,
302 const struct property_entry *src)
303{
304 const char **d;
305 size_t nval = src->length / sizeof(*d);
306 int i;
307
308 d = kcalloc(nval, sizeof(*d), GFP_KERNEL);
309 if (!d)
310 return -ENOMEM;
311
312 for (i = 0; i < nval; i++) {
313 d[i] = kstrdup(src->pointer.str[i], GFP_KERNEL);
314 if (!d[i] && src->pointer.str[i]) {
315 while (--i >= 0)
316 kfree(d[i]);
317 kfree(d);
318 return -ENOMEM;
319 }
320 }
321
322 dst->pointer.str = d;
323 return 0;
324}
325
326static int property_entry_copy_data(struct property_entry *dst,
327 const struct property_entry *src)
328{
329 const void *pointer = property_get_pointer(src);
330 const void *new;
331 int error;
332
333 if (src->is_array) {
334 if (!src->length)
335 return -ENODATA;
336
337 if (src->type == DEV_PROP_STRING) {
338 error = property_copy_string_array(dst, src);
339 if (error)
340 return error;
341 new = dst->pointer.str;
342 } else {
343 new = kmemdup(pointer, src->length, GFP_KERNEL);
344 if (!new)
345 return -ENOMEM;
346 }
347 } else if (src->type == DEV_PROP_STRING) {
348 new = kstrdup(src->value.str, GFP_KERNEL);
349 if (!new && src->value.str)
350 return -ENOMEM;
351 } else {
352 new = pointer;
353 }
354
355 dst->length = src->length;
356 dst->is_array = src->is_array;
357 dst->type = src->type;
358
359 property_set_pointer(dst, new);
360
361 dst->name = kstrdup(src->name, GFP_KERNEL);
362 if (!dst->name)
363 goto out_free_data;
364
365 return 0;
366
367out_free_data:
368 property_entry_free_data(dst);
369 return -ENOMEM;
370}
371
372/**
373 * property_entries_dup - duplicate array of properties
374 * @properties: array of properties to copy
375 *
376 * This function creates a deep copy of the given NULL-terminated array
377 * of property entries.
378 */
379struct property_entry *
380property_entries_dup(const struct property_entry *properties)
381{
382 struct property_entry *p;
383 int i, n = 0;
384 int ret;
385
386 while (properties[n].name)
387 n++;
388
389 p = kcalloc(n + 1, sizeof(*p), GFP_KERNEL);
390 if (!p)
391 return ERR_PTR(-ENOMEM);
392
393 for (i = 0; i < n; i++) {
394 ret = property_entry_copy_data(&p[i], &properties[i]);
395 if (ret) {
396 while (--i >= 0)
397 property_entry_free_data(&p[i]);
398 kfree(p);
399 return ERR_PTR(ret);
400 }
401 }
402
403 return p;
404}
405EXPORT_SYMBOL_GPL(property_entries_dup);
406
407/**
408 * property_entries_free - free previously allocated array of properties
409 * @properties: array of properties to destroy
410 *
411 * This function frees given NULL-terminated array of property entries,
412 * along with their data.
413 */
414void property_entries_free(const struct property_entry *properties)
415{
416 const struct property_entry *p;
417
418 if (!properties)
419 return;
420
421 for (p = properties; p->name; p++)
422 property_entry_free_data(p);
423
424 kfree(properties);
425}
426EXPORT_SYMBOL_GPL(property_entries_free);
427
428/* -------------------------------------------------------------------------- */
429/* fwnode operations */
430
431static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode)
432{
433 struct software_node *swnode = to_software_node(fwnode);
434
435 kobject_get(&swnode->kobj);
436
437 return &swnode->fwnode;
438}
439
440static void software_node_put(struct fwnode_handle *fwnode)
441{
442 struct software_node *swnode = to_software_node(fwnode);
443
444 kobject_put(&swnode->kobj);
445}
446
447static bool software_node_property_present(const struct fwnode_handle *fwnode,
448 const char *propname)
449{
450 return !!property_entry_get(to_software_node(fwnode)->properties,
451 propname);
452}
453
454static int software_node_read_int_array(const struct fwnode_handle *fwnode,
455 const char *propname,
456 unsigned int elem_size, void *val,
457 size_t nval)
458{
459 struct software_node *swnode = to_software_node(fwnode);
460
461 return property_entry_read_int_array(swnode->properties, propname,
462 elem_size, val, nval);
463}
464
465static int software_node_read_string_array(const struct fwnode_handle *fwnode,
466 const char *propname,
467 const char **val, size_t nval)
468{
469 struct software_node *swnode = to_software_node(fwnode);
470
471 return property_entry_read_string_array(swnode->properties, propname,
472 val, nval);
473}
474
475struct fwnode_handle *
476software_node_get_parent(const struct fwnode_handle *fwnode)
477{
478 struct software_node *swnode = to_software_node(fwnode);
479
480 return swnode->parent ? &swnode->parent->fwnode : NULL;
481}
482
483struct fwnode_handle *
484software_node_get_next_child(const struct fwnode_handle *fwnode,
485 struct fwnode_handle *child)
486{
487 struct software_node *p = to_software_node(fwnode);
488 struct software_node *c = to_software_node(child);
489
490 if (list_empty(&p->children) ||
491 (c && list_is_last(&c->entry, &p->children)))
492 return NULL;
493
494 if (c)
495 c = list_next_entry(c, entry);
496 else
497 c = list_first_entry(&p->children, struct software_node, entry);
498 return &c->fwnode;
499}
500
501
502static const struct fwnode_operations software_node_ops = {
503 .get = software_node_get,
504 .put = software_node_put,
505 .property_present = software_node_property_present,
506 .property_read_int_array = software_node_read_int_array,
507 .property_read_string_array = software_node_read_string_array,
508 .get_parent = software_node_get_parent,
509 .get_next_child_node = software_node_get_next_child,
510};
511
512/* -------------------------------------------------------------------------- */
513
514static int
515software_node_register_properties(struct software_node *swnode,
516 const struct property_entry *properties)
517{
518 struct property_entry *props;
519
520 props = property_entries_dup(properties);
521 if (IS_ERR(props))
522 return PTR_ERR(props);
523
524 swnode->properties = props;
525
526 return 0;
527}
528
529static void software_node_release(struct kobject *kobj)
530{
531 struct software_node *swnode = kobj_to_swnode(kobj);
532
533 if (swnode->parent) {
534 ida_simple_remove(&swnode->parent->child_ids, swnode->id);
535 list_del(&swnode->entry);
536 } else {
537 ida_simple_remove(&swnode_root_ids, swnode->id);
538 }
539
540 ida_destroy(&swnode->child_ids);
541 property_entries_free(swnode->properties);
542 kfree(swnode);
543}
544
545static struct kobj_type software_node_type = {
546 .release = software_node_release,
547 .sysfs_ops = &kobj_sysfs_ops,
548};
549
550struct fwnode_handle *
551fwnode_create_software_node(const struct property_entry *properties,
552 const struct fwnode_handle *parent)
553{
554 struct software_node *p = NULL;
555 struct software_node *swnode;
556 int ret;
557
558 if (parent) {
559 if (IS_ERR(parent))
560 return ERR_CAST(parent);
561 if (!is_software_node(parent))
562 return ERR_PTR(-EINVAL);
563 p = to_software_node(parent);
564 }
565
566 swnode = kzalloc(sizeof(*swnode), GFP_KERNEL);
567 if (!swnode)
568 return ERR_PTR(-ENOMEM);
569
570 ret = ida_simple_get(p ? &p->child_ids : &swnode_root_ids, 0, 0,
571 GFP_KERNEL);
572 if (ret < 0) {
573 kfree(swnode);
574 return ERR_PTR(ret);
575 }
576
577 swnode->id = ret;
578 swnode->kobj.kset = swnode_kset;
579 swnode->fwnode.ops = &software_node_ops;
580
581 ida_init(&swnode->child_ids);
582 INIT_LIST_HEAD(&swnode->entry);
583 INIT_LIST_HEAD(&swnode->children);
584 swnode->parent = p;
585
586 if (p)
587 list_add_tail(&swnode->entry, &p->children);
588
589 ret = kobject_init_and_add(&swnode->kobj, &software_node_type,
590 p ? &p->kobj : NULL, "node%d", swnode->id);
591 if (ret) {
592 kobject_put(&swnode->kobj);
593 return ERR_PTR(ret);
594 }
595
596 ret = software_node_register_properties(swnode, properties);
597 if (ret) {
598 kobject_put(&swnode->kobj);
599 return ERR_PTR(ret);
600 }
601
602 kobject_uevent(&swnode->kobj, KOBJ_ADD);
603 return &swnode->fwnode;
604}
605EXPORT_SYMBOL_GPL(fwnode_create_software_node);
606
607void fwnode_remove_software_node(struct fwnode_handle *fwnode)
608{
609 struct software_node *swnode = to_software_node(fwnode);
610
611 if (!swnode)
612 return;
613
614 kobject_put(&swnode->kobj);
615}
616EXPORT_SYMBOL_GPL(fwnode_remove_software_node);
617
618int software_node_notify(struct device *dev, unsigned long action)
619{
620 struct fwnode_handle *fwnode = dev_fwnode(dev);
621 struct software_node *swnode;
622 int ret;
623
624 if (!fwnode)
625 return 0;
626
627 if (!is_software_node(fwnode))
628 fwnode = fwnode->secondary;
629 if (!is_software_node(fwnode))
630 return 0;
631
632 swnode = to_software_node(fwnode);
633
634 switch (action) {
635 case KOBJ_ADD:
636 ret = sysfs_create_link(&dev->kobj, &swnode->kobj,
637 "software_node");
638 if (ret)
639 break;
640
641 ret = sysfs_create_link(&swnode->kobj, &dev->kobj,
642 dev_name(dev));
643 if (ret) {
644 sysfs_remove_link(&dev->kobj, "software_node");
645 break;
646 }
647 kobject_get(&swnode->kobj);
648 break;
649 case KOBJ_REMOVE:
650 sysfs_remove_link(&swnode->kobj, dev_name(dev));
651 sysfs_remove_link(&dev->kobj, "software_node");
652 kobject_put(&swnode->kobj);
653 break;
654 default:
655 break;
656 }
657
658 return 0;
659}
660
661static int __init software_node_init(void)
662{
663 swnode_kset = kset_create_and_add("software_nodes", NULL, kernel_kobj);
664 if (!swnode_kset)
665 return -ENOMEM;
666 return 0;
667}
668postcore_initcall(software_node_init);
669
670static void __exit software_node_exit(void)
671{
672 ida_destroy(&swnode_root_ids);
673 kset_unregister(swnode_kset);
674}
675__exitcall(software_node_exit);
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 4b3283cb09ab..87715f20b69a 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -1331,4 +1331,14 @@ static inline int find_acpi_cpu_cache_topology(unsigned int cpu, int level)
1331} 1331}
1332#endif 1332#endif
1333 1333
1334#ifdef CONFIG_ACPI
1335extern int acpi_platform_notify(struct device *dev, enum kobject_action action);
1336#else
1337static inline int
1338acpi_platform_notify(struct device *dev, enum kobject_action action)
1339{
1340 return 0;
1341}
1342#endif
1343
1334#endif /*_LINUX_ACPI_H*/ 1344#endif /*_LINUX_ACPI_H*/
diff --git a/include/linux/property.h b/include/linux/property.h
index ac8a1ebc4c1b..3789ec755fb6 100644
--- a/include/linux/property.h
+++ b/include/linux/property.h
@@ -311,4 +311,16 @@ fwnode_graph_get_remote_node(const struct fwnode_handle *fwnode, u32 port,
311int fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode, 311int fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode,
312 struct fwnode_endpoint *endpoint); 312 struct fwnode_endpoint *endpoint);
313 313
314/* -------------------------------------------------------------------------- */
315/* Software fwnode support - when HW description is incomplete or missing */
316
317bool is_software_node(const struct fwnode_handle *fwnode);
318
319int software_node_notify(struct device *dev, unsigned long action);
320
321struct fwnode_handle *
322fwnode_create_software_node(const struct property_entry *properties,
323 const struct fwnode_handle *parent);
324void fwnode_remove_software_node(struct fwnode_handle *fwnode);
325
314#endif /* _LINUX_PROPERTY_H_ */ 326#endif /* _LINUX_PROPERTY_H_ */