diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-devices-software_node | 10 | ||||
-rw-r--r-- | drivers/acpi/bus.c | 1 | ||||
-rw-r--r-- | drivers/acpi/glue.c | 21 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 1 | ||||
-rw-r--r-- | drivers/base/Makefile | 2 | ||||
-rw-r--r-- | drivers/base/core.c | 34 | ||||
-rw-r--r-- | drivers/base/platform.c | 1 | ||||
-rw-r--r-- | drivers/base/property.c | 513 | ||||
-rw-r--r-- | drivers/base/swnode.c | 675 | ||||
-rw-r--r-- | include/linux/acpi.h | 10 | ||||
-rw-r--r-- | include/linux/property.h | 12 |
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 @@ | |||
1 | What: /sys/devices/.../software_node/ | ||
2 | Date: January 2019 | ||
3 | Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
4 | Description: | ||
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 | } |
297 | EXPORT_SYMBOL_GPL(acpi_unbind_one); | 297 | EXPORT_SYMBOL_GPL(acpi_unbind_one); |
298 | 298 | ||
299 | static int acpi_platform_notify(struct device *dev) | 299 | static 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 | ||
346 | static int acpi_platform_notify_remove(struct device *dev) | 346 | static 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 | ||
364 | void __init init_acpi_device_notify(void) | 364 | int 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 @@ | |||
23 | int early_acpi_osi_init(void); | 23 | int early_acpi_osi_init(void); |
24 | int acpi_osi_init(void); | 24 | int acpi_osi_init(void); |
25 | acpi_status acpi_os_initialize1(void); | 25 | acpi_status acpi_os_initialize1(void); |
26 | void init_acpi_device_notify(void); | ||
27 | int acpi_scan_init(void); | 26 | int acpi_scan_init(void); |
28 | #ifdef CONFIG_PCI | 27 | #ifdef CONFIG_PCI |
29 | void acpi_pci_root_init(void); | 28 | void 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 |
10 | obj-$(CONFIG_DEVTMPFS) += devtmpfs.o | 10 | obj-$(CONFIG_DEVTMPFS) += devtmpfs.o |
11 | obj-y += power/ | 11 | obj-y += power/ |
12 | obj-$(CONFIG_ISA_BUS_API) += isa.o | 12 | obj-$(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 | ||
732 | static int | ||
733 | device_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); | ||
1986 | platform_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 | ||
21 | struct property_set { | ||
22 | struct device *dev; | ||
23 | struct fwnode_handle fwnode; | ||
24 | const struct property_entry *properties; | ||
25 | }; | ||
26 | |||
27 | static const struct fwnode_operations pset_fwnode_ops; | ||
28 | |||
29 | static 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 | |||
44 | static const struct property_entry * | ||
45 | pset_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 | |||
59 | static 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 | |||
87 | static 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 | |||
125 | static 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 | |||
142 | static 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 | |||
157 | static 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 | |||
172 | static 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 | |||
187 | static 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 | |||
202 | static 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 | |||
214 | static 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 | |||
251 | struct fwnode_handle *dev_fwnode(struct device *dev) | 21 | struct 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 | } |
256 | EXPORT_SYMBOL_GPL(dev_fwnode); | 26 | EXPORT_SYMBOL_GPL(dev_fwnode); |
257 | 27 | ||
258 | static 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 | |||
264 | static 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 | |||
288 | static int | ||
289 | pset_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 | |||
297 | static 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 | } |
760 | EXPORT_SYMBOL_GPL(fwnode_property_get_reference_args); | 485 | EXPORT_SYMBOL_GPL(fwnode_property_get_reference_args); |
761 | 486 | ||
762 | static 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 | |||
780 | static 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 | |||
805 | static 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 | |||
846 | out_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 | */ | ||
858 | struct property_entry * | ||
859 | property_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 | } | ||
883 | EXPORT_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 | */ | ||
892 | void 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 | } | ||
901 | EXPORT_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 | */ | ||
910 | static 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 | */ | ||
929 | static 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 | */ |
956 | void device_remove_properties(struct device *dev) | 495 | void 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 | } |
980 | EXPORT_SYMBOL_GPL(device_remove_properties); | 507 | EXPORT_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 | */ |
991 | int device_add_properties(struct device *dev, | 521 | int 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 | } |
1010 | EXPORT_SYMBOL_GPL(device_add_properties); | 533 | EXPORT_SYMBOL_GPL(device_add_properties); |
@@ -1341,7 +864,7 @@ int fwnode_irq_get(struct fwnode_handle *fwnode, unsigned int index) | |||
1341 | EXPORT_SYMBOL(fwnode_irq_get); | 864 | EXPORT_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 | |||
14 | struct 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 | |||
29 | static DEFINE_IDA(swnode_root_ids); | ||
30 | static struct kset *swnode_kset; | ||
31 | |||
32 | #define kobj_to_swnode(_kobj_) container_of(_kobj_, struct software_node, kobj) | ||
33 | |||
34 | static const struct fwnode_operations software_node_ops; | ||
35 | |||
36 | bool 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 | |||
54 | static const struct property_entry * | ||
55 | property_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 | |||
67 | static void | ||
68 | property_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 | |||
106 | static 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 | |||
134 | static 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 | |||
151 | static 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 | |||
166 | static 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 | |||
181 | static 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 | |||
196 | static 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 | |||
211 | static int | ||
212 | property_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 | |||
224 | static 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 | |||
246 | static 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 | |||
283 | static 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 | |||
301 | static 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 | |||
326 | static 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 | |||
367 | out_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 | */ | ||
379 | struct property_entry * | ||
380 | property_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 | } | ||
405 | EXPORT_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 | */ | ||
414 | void 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 | } | ||
426 | EXPORT_SYMBOL_GPL(property_entries_free); | ||
427 | |||
428 | /* -------------------------------------------------------------------------- */ | ||
429 | /* fwnode operations */ | ||
430 | |||
431 | static 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 | |||
440 | static 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 | |||
447 | static 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 | |||
454 | static 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 | |||
465 | static 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 | |||
475 | struct fwnode_handle * | ||
476 | software_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 | |||
483 | struct fwnode_handle * | ||
484 | software_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 | |||
502 | static 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 | |||
514 | static int | ||
515 | software_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 | |||
529 | static 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 | |||
545 | static struct kobj_type software_node_type = { | ||
546 | .release = software_node_release, | ||
547 | .sysfs_ops = &kobj_sysfs_ops, | ||
548 | }; | ||
549 | |||
550 | struct fwnode_handle * | ||
551 | fwnode_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 | } | ||
605 | EXPORT_SYMBOL_GPL(fwnode_create_software_node); | ||
606 | |||
607 | void 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 | } | ||
616 | EXPORT_SYMBOL_GPL(fwnode_remove_software_node); | ||
617 | |||
618 | int 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 | |||
661 | static 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 | } | ||
668 | postcore_initcall(software_node_init); | ||
669 | |||
670 | static 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 | ||
1335 | extern int acpi_platform_notify(struct device *dev, enum kobject_action action); | ||
1336 | #else | ||
1337 | static inline int | ||
1338 | acpi_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, | |||
311 | int fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode, | 311 | int 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 | |||
317 | bool is_software_node(const struct fwnode_handle *fwnode); | ||
318 | |||
319 | int software_node_notify(struct device *dev, unsigned long action); | ||
320 | |||
321 | struct fwnode_handle * | ||
322 | fwnode_create_software_node(const struct property_entry *properties, | ||
323 | const struct fwnode_handle *parent); | ||
324 | void fwnode_remove_software_node(struct fwnode_handle *fwnode); | ||
325 | |||
314 | #endif /* _LINUX_PROPERTY_H_ */ | 326 | #endif /* _LINUX_PROPERTY_H_ */ |