diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-01-11 19:07:46 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-01-11 19:07:46 -0500 |
commit | 989652871b06f1fb173bc5e8e2ea03bec8f8eeeb (patch) | |
tree | 1a9331bbbf04b5e04148e68214e52551b2f1317f /drivers/base | |
parent | afd2ff9b7e1b367172f18ba7f693dfb62bdcb2dc (diff) | |
parent | f6740c1899d2ee2c4c9ec5301d4b712d4e706a79 (diff) |
Merge branch 'device-properties'
* device-properties:
device property: avoid allocations of 0 length
device property: the secondary fwnode needs to depend on the primary
device property: add spaces to PROPERTY_ENTRY_STRING macro
include/linux/property.h: fix build issues with gcc-4.4.4
i2c: designware: Convert to use unified device property API
mfd: intel-lpss: Pass HSUART configuration via properties
mfd: intel-lpss: Pass SDA hold time to I2C host controller driver
mfd: intel-lpss: Add support for passing device properties
mfd: core: propagate device properties to sub devices drivers
driver core: Do not overwrite secondary fwnode with NULL if it is set
driver core: platform: Add support for built-in device properties
device property: Take a copy of the property set
device property: Fallback to secondary fwnode if primary misses the property
device property: return -EINVAL when property isn't found in ACPI
device property: improve readability of macros
device property: helper macros for property entry creation
device property: keep single value inplace
device property: refactor built-in properties support
device property: rename helper functions
device property: always check for fwnode type
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/core.c | 5 | ||||
-rw-r--r-- | drivers/base/platform.c | 25 | ||||
-rw-r--r-- | drivers/base/property.c | 495 |
3 files changed, 430 insertions, 95 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index b7d56c5ea3c6..0a8bdade53f2 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
@@ -2261,7 +2261,10 @@ void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode) | |||
2261 | if (fwnode_is_primary(fn)) | 2261 | if (fwnode_is_primary(fn)) |
2262 | fn = fn->secondary; | 2262 | fn = fn->secondary; |
2263 | 2263 | ||
2264 | fwnode->secondary = fn; | 2264 | if (fn) { |
2265 | WARN_ON(fwnode->secondary); | ||
2266 | fwnode->secondary = fn; | ||
2267 | } | ||
2265 | dev->fwnode = fwnode; | 2268 | dev->fwnode = fwnode; |
2266 | } else { | 2269 | } else { |
2267 | dev->fwnode = fwnode_is_primary(dev->fwnode) ? | 2270 | dev->fwnode = fwnode_is_primary(dev->fwnode) ? |
diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 1dd6d3bf1098..d77ed0c946dd 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/acpi.h> | 26 | #include <linux/acpi.h> |
27 | #include <linux/clk/clk-conf.h> | 27 | #include <linux/clk/clk-conf.h> |
28 | #include <linux/limits.h> | 28 | #include <linux/limits.h> |
29 | #include <linux/property.h> | ||
29 | 30 | ||
30 | #include "base.h" | 31 | #include "base.h" |
31 | #include "power/power.h" | 32 | #include "power/power.h" |
@@ -299,6 +300,22 @@ int platform_device_add_data(struct platform_device *pdev, const void *data, | |||
299 | EXPORT_SYMBOL_GPL(platform_device_add_data); | 300 | EXPORT_SYMBOL_GPL(platform_device_add_data); |
300 | 301 | ||
301 | /** | 302 | /** |
303 | * platform_device_add_properties - add built-in properties to a platform device | ||
304 | * @pdev: platform device to add properties to | ||
305 | * @pset: properties to add | ||
306 | * | ||
307 | * The function will take deep copy of the properties in @pset and attach | ||
308 | * the copy to the platform device. The memory associated with properties | ||
309 | * will be freed when the platform device is released. | ||
310 | */ | ||
311 | int platform_device_add_properties(struct platform_device *pdev, | ||
312 | const struct property_set *pset) | ||
313 | { | ||
314 | return device_add_property_set(&pdev->dev, pset); | ||
315 | } | ||
316 | EXPORT_SYMBOL_GPL(platform_device_add_properties); | ||
317 | |||
318 | /** | ||
302 | * platform_device_add - add a platform device to device hierarchy | 319 | * platform_device_add - add a platform device to device hierarchy |
303 | * @pdev: platform device we're adding | 320 | * @pdev: platform device we're adding |
304 | * | 321 | * |
@@ -409,6 +426,8 @@ void platform_device_del(struct platform_device *pdev) | |||
409 | if (r->parent) | 426 | if (r->parent) |
410 | release_resource(r); | 427 | release_resource(r); |
411 | } | 428 | } |
429 | |||
430 | device_remove_property_set(&pdev->dev); | ||
412 | } | 431 | } |
413 | } | 432 | } |
414 | EXPORT_SYMBOL_GPL(platform_device_del); | 433 | EXPORT_SYMBOL_GPL(platform_device_del); |
@@ -487,6 +506,12 @@ struct platform_device *platform_device_register_full( | |||
487 | if (ret) | 506 | if (ret) |
488 | goto err; | 507 | goto err; |
489 | 508 | ||
509 | if (pdevinfo->pset) { | ||
510 | ret = platform_device_add_properties(pdev, pdevinfo->pset); | ||
511 | if (ret) | ||
512 | goto err; | ||
513 | } | ||
514 | |||
490 | ret = platform_device_add(pdev); | 515 | ret = platform_device_add(pdev); |
491 | if (ret) { | 516 | if (ret) { |
492 | err: | 517 | err: |
diff --git a/drivers/base/property.c b/drivers/base/property.c index 1325ff225cc4..c359351d50f1 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c | |||
@@ -19,32 +19,14 @@ | |||
19 | #include <linux/etherdevice.h> | 19 | #include <linux/etherdevice.h> |
20 | #include <linux/phy.h> | 20 | #include <linux/phy.h> |
21 | 21 | ||
22 | /** | 22 | static inline bool is_pset_node(struct fwnode_handle *fwnode) |
23 | * device_add_property_set - Add a collection of properties to a device object. | ||
24 | * @dev: Device to add properties to. | ||
25 | * @pset: Collection of properties to add. | ||
26 | * | ||
27 | * Associate a collection of device properties represented by @pset with @dev | ||
28 | * as its secondary firmware node. | ||
29 | */ | ||
30 | void device_add_property_set(struct device *dev, struct property_set *pset) | ||
31 | { | ||
32 | if (!pset) | ||
33 | return; | ||
34 | |||
35 | pset->fwnode.type = FWNODE_PDATA; | ||
36 | set_secondary_fwnode(dev, &pset->fwnode); | ||
37 | } | ||
38 | EXPORT_SYMBOL_GPL(device_add_property_set); | ||
39 | |||
40 | static inline bool is_pset(struct fwnode_handle *fwnode) | ||
41 | { | 23 | { |
42 | return fwnode && fwnode->type == FWNODE_PDATA; | 24 | return fwnode && fwnode->type == FWNODE_PDATA; |
43 | } | 25 | } |
44 | 26 | ||
45 | static inline struct property_set *to_pset(struct fwnode_handle *fwnode) | 27 | static inline struct property_set *to_pset_node(struct fwnode_handle *fwnode) |
46 | { | 28 | { |
47 | return is_pset(fwnode) ? | 29 | return is_pset_node(fwnode) ? |
48 | container_of(fwnode, struct property_set, fwnode) : NULL; | 30 | container_of(fwnode, struct property_set, fwnode) : NULL; |
49 | } | 31 | } |
50 | 32 | ||
@@ -63,45 +45,135 @@ static struct property_entry *pset_prop_get(struct property_set *pset, | |||
63 | return NULL; | 45 | return NULL; |
64 | } | 46 | } |
65 | 47 | ||
66 | static int pset_prop_read_array(struct property_set *pset, const char *name, | 48 | static void *pset_prop_find(struct property_set *pset, const char *propname, |
67 | enum dev_prop_type type, void *val, size_t nval) | 49 | size_t length) |
68 | { | 50 | { |
69 | struct property_entry *prop; | 51 | struct property_entry *prop; |
70 | unsigned int item_size; | 52 | void *pointer; |
71 | 53 | ||
72 | prop = pset_prop_get(pset, name); | 54 | prop = pset_prop_get(pset, propname); |
73 | if (!prop) | 55 | if (!prop) |
74 | return -ENODATA; | 56 | return ERR_PTR(-EINVAL); |
57 | if (prop->is_array) | ||
58 | pointer = prop->pointer.raw_data; | ||
59 | else | ||
60 | pointer = &prop->value.raw_data; | ||
61 | if (!pointer) | ||
62 | return ERR_PTR(-ENODATA); | ||
63 | if (length > prop->length) | ||
64 | return ERR_PTR(-EOVERFLOW); | ||
65 | return pointer; | ||
66 | } | ||
67 | |||
68 | static int pset_prop_read_u8_array(struct property_set *pset, | ||
69 | const char *propname, | ||
70 | u8 *values, size_t nval) | ||
71 | { | ||
72 | void *pointer; | ||
73 | size_t length = nval * sizeof(*values); | ||
74 | |||
75 | pointer = pset_prop_find(pset, propname, length); | ||
76 | if (IS_ERR(pointer)) | ||
77 | return PTR_ERR(pointer); | ||
78 | |||
79 | memcpy(values, pointer, length); | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | static int pset_prop_read_u16_array(struct property_set *pset, | ||
84 | const char *propname, | ||
85 | u16 *values, size_t nval) | ||
86 | { | ||
87 | void *pointer; | ||
88 | size_t length = nval * sizeof(*values); | ||
89 | |||
90 | pointer = pset_prop_find(pset, propname, length); | ||
91 | if (IS_ERR(pointer)) | ||
92 | return PTR_ERR(pointer); | ||
93 | |||
94 | memcpy(values, pointer, length); | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static int pset_prop_read_u32_array(struct property_set *pset, | ||
99 | const char *propname, | ||
100 | u32 *values, size_t nval) | ||
101 | { | ||
102 | void *pointer; | ||
103 | size_t length = nval * sizeof(*values); | ||
104 | |||
105 | pointer = pset_prop_find(pset, propname, length); | ||
106 | if (IS_ERR(pointer)) | ||
107 | return PTR_ERR(pointer); | ||
108 | |||
109 | memcpy(values, pointer, length); | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static int pset_prop_read_u64_array(struct property_set *pset, | ||
114 | const char *propname, | ||
115 | u64 *values, size_t nval) | ||
116 | { | ||
117 | void *pointer; | ||
118 | size_t length = nval * sizeof(*values); | ||
119 | |||
120 | pointer = pset_prop_find(pset, propname, length); | ||
121 | if (IS_ERR(pointer)) | ||
122 | return PTR_ERR(pointer); | ||
123 | |||
124 | memcpy(values, pointer, length); | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static int pset_prop_count_elems_of_size(struct property_set *pset, | ||
129 | const char *propname, size_t length) | ||
130 | { | ||
131 | struct property_entry *prop; | ||
132 | |||
133 | prop = pset_prop_get(pset, propname); | ||
134 | if (!prop) | ||
135 | return -EINVAL; | ||
136 | |||
137 | return prop->length / length; | ||
138 | } | ||
139 | |||
140 | static int pset_prop_read_string_array(struct property_set *pset, | ||
141 | const char *propname, | ||
142 | const char **strings, size_t nval) | ||
143 | { | ||
144 | void *pointer; | ||
145 | size_t length = nval * sizeof(*strings); | ||
146 | |||
147 | pointer = pset_prop_find(pset, propname, length); | ||
148 | if (IS_ERR(pointer)) | ||
149 | return PTR_ERR(pointer); | ||
150 | |||
151 | memcpy(strings, pointer, length); | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | static int pset_prop_read_string(struct property_set *pset, | ||
156 | const char *propname, const char **strings) | ||
157 | { | ||
158 | struct property_entry *prop; | ||
159 | const char **pointer; | ||
75 | 160 | ||
76 | if (prop->type != type) | 161 | prop = pset_prop_get(pset, propname); |
77 | return -EPROTO; | 162 | if (!prop) |
78 | |||
79 | if (!val) | ||
80 | return prop->nval; | ||
81 | |||
82 | if (prop->nval < nval) | ||
83 | return -EOVERFLOW; | ||
84 | |||
85 | switch (type) { | ||
86 | case DEV_PROP_U8: | ||
87 | item_size = sizeof(u8); | ||
88 | break; | ||
89 | case DEV_PROP_U16: | ||
90 | item_size = sizeof(u16); | ||
91 | break; | ||
92 | case DEV_PROP_U32: | ||
93 | item_size = sizeof(u32); | ||
94 | break; | ||
95 | case DEV_PROP_U64: | ||
96 | item_size = sizeof(u64); | ||
97 | break; | ||
98 | case DEV_PROP_STRING: | ||
99 | item_size = sizeof(const char *); | ||
100 | break; | ||
101 | default: | ||
102 | return -EINVAL; | 163 | return -EINVAL; |
164 | if (!prop->is_string) | ||
165 | return -EILSEQ; | ||
166 | if (prop->is_array) { | ||
167 | pointer = prop->pointer.str; | ||
168 | if (!pointer) | ||
169 | return -ENODATA; | ||
170 | } else { | ||
171 | pointer = &prop->value.str; | ||
172 | if (*pointer && strnlen(*pointer, prop->length) >= prop->length) | ||
173 | return -EILSEQ; | ||
103 | } | 174 | } |
104 | memcpy(val, prop->value.raw_data, nval * item_size); | 175 | |
176 | *strings = *pointer; | ||
105 | return 0; | 177 | return 0; |
106 | } | 178 | } |
107 | 179 | ||
@@ -124,6 +196,18 @@ bool device_property_present(struct device *dev, const char *propname) | |||
124 | } | 196 | } |
125 | EXPORT_SYMBOL_GPL(device_property_present); | 197 | EXPORT_SYMBOL_GPL(device_property_present); |
126 | 198 | ||
199 | static bool __fwnode_property_present(struct fwnode_handle *fwnode, | ||
200 | const char *propname) | ||
201 | { | ||
202 | if (is_of_node(fwnode)) | ||
203 | return of_property_read_bool(to_of_node(fwnode), propname); | ||
204 | else if (is_acpi_node(fwnode)) | ||
205 | return !acpi_node_prop_get(fwnode, propname, NULL); | ||
206 | else if (is_pset_node(fwnode)) | ||
207 | return !!pset_prop_get(to_pset_node(fwnode), propname); | ||
208 | return false; | ||
209 | } | ||
210 | |||
127 | /** | 211 | /** |
128 | * fwnode_property_present - check if a property of a firmware node is present | 212 | * fwnode_property_present - check if a property of a firmware node is present |
129 | * @fwnode: Firmware node whose property to check | 213 | * @fwnode: Firmware node whose property to check |
@@ -131,12 +215,12 @@ EXPORT_SYMBOL_GPL(device_property_present); | |||
131 | */ | 215 | */ |
132 | bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname) | 216 | bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname) |
133 | { | 217 | { |
134 | if (is_of_node(fwnode)) | 218 | bool ret; |
135 | return of_property_read_bool(to_of_node(fwnode), propname); | ||
136 | else if (is_acpi_node(fwnode)) | ||
137 | return !acpi_node_prop_get(fwnode, propname, NULL); | ||
138 | 219 | ||
139 | return !!pset_prop_get(to_pset(fwnode), propname); | 220 | ret = __fwnode_property_present(fwnode, propname); |
221 | if (ret == false && fwnode && fwnode->secondary) | ||
222 | ret = __fwnode_property_present(fwnode->secondary, propname); | ||
223 | return ret; | ||
140 | } | 224 | } |
141 | EXPORT_SYMBOL_GPL(fwnode_property_present); | 225 | EXPORT_SYMBOL_GPL(fwnode_property_present); |
142 | 226 | ||
@@ -309,25 +393,40 @@ int device_property_match_string(struct device *dev, const char *propname, | |||
309 | } | 393 | } |
310 | EXPORT_SYMBOL_GPL(device_property_match_string); | 394 | EXPORT_SYMBOL_GPL(device_property_match_string); |
311 | 395 | ||
312 | #define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \ | 396 | #define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \ |
313 | (val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \ | 397 | (val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \ |
314 | : of_property_count_elems_of_size((node), (propname), sizeof(type)) | 398 | : of_property_count_elems_of_size((node), (propname), sizeof(type)) |
315 | 399 | ||
316 | #define FWNODE_PROP_READ_ARRAY(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \ | 400 | #define PSET_PROP_READ_ARRAY(node, propname, type, val, nval) \ |
317 | ({ \ | 401 | (val) ? pset_prop_read_##type##_array((node), (propname), (val), (nval)) \ |
318 | int _ret_; \ | 402 | : pset_prop_count_elems_of_size((node), (propname), sizeof(type)) |
319 | if (is_of_node(_fwnode_)) \ | 403 | |
320 | _ret_ = OF_DEV_PROP_READ_ARRAY(to_of_node(_fwnode_), _propname_, \ | 404 | #define FWNODE_PROP_READ(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \ |
321 | _type_, _val_, _nval_); \ | 405 | ({ \ |
322 | else if (is_acpi_node(_fwnode_)) \ | 406 | int _ret_; \ |
323 | _ret_ = acpi_node_prop_read(_fwnode_, _propname_, _proptype_, \ | 407 | if (is_of_node(_fwnode_)) \ |
324 | _val_, _nval_); \ | 408 | _ret_ = OF_DEV_PROP_READ_ARRAY(to_of_node(_fwnode_), _propname_, \ |
325 | else if (is_pset(_fwnode_)) \ | 409 | _type_, _val_, _nval_); \ |
326 | _ret_ = pset_prop_read_array(to_pset(_fwnode_), _propname_, \ | 410 | else if (is_acpi_node(_fwnode_)) \ |
327 | _proptype_, _val_, _nval_); \ | 411 | _ret_ = acpi_node_prop_read(_fwnode_, _propname_, _proptype_, \ |
328 | else \ | 412 | _val_, _nval_); \ |
329 | _ret_ = -ENXIO; \ | 413 | else if (is_pset_node(_fwnode_)) \ |
330 | _ret_; \ | 414 | _ret_ = PSET_PROP_READ_ARRAY(to_pset_node(_fwnode_), _propname_, \ |
415 | _type_, _val_, _nval_); \ | ||
416 | else \ | ||
417 | _ret_ = -ENXIO; \ | ||
418 | _ret_; \ | ||
419 | }) | ||
420 | |||
421 | #define FWNODE_PROP_READ_ARRAY(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \ | ||
422 | ({ \ | ||
423 | int _ret_; \ | ||
424 | _ret_ = FWNODE_PROP_READ(_fwnode_, _propname_, _type_, _proptype_, \ | ||
425 | _val_, _nval_); \ | ||
426 | if (_ret_ == -EINVAL && _fwnode_ && _fwnode_->secondary) \ | ||
427 | _ret_ = FWNODE_PROP_READ(_fwnode_->secondary, _propname_, _type_, \ | ||
428 | _proptype_, _val_, _nval_); \ | ||
429 | _ret_; \ | ||
331 | }) | 430 | }) |
332 | 431 | ||
333 | /** | 432 | /** |
@@ -434,6 +533,41 @@ int fwnode_property_read_u64_array(struct fwnode_handle *fwnode, | |||
434 | } | 533 | } |
435 | EXPORT_SYMBOL_GPL(fwnode_property_read_u64_array); | 534 | EXPORT_SYMBOL_GPL(fwnode_property_read_u64_array); |
436 | 535 | ||
536 | static int __fwnode_property_read_string_array(struct fwnode_handle *fwnode, | ||
537 | const char *propname, | ||
538 | const char **val, size_t nval) | ||
539 | { | ||
540 | if (is_of_node(fwnode)) | ||
541 | return val ? | ||
542 | of_property_read_string_array(to_of_node(fwnode), | ||
543 | propname, val, nval) : | ||
544 | of_property_count_strings(to_of_node(fwnode), propname); | ||
545 | else if (is_acpi_node(fwnode)) | ||
546 | return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING, | ||
547 | val, nval); | ||
548 | else if (is_pset_node(fwnode)) | ||
549 | return val ? | ||
550 | pset_prop_read_string_array(to_pset_node(fwnode), | ||
551 | propname, val, nval) : | ||
552 | pset_prop_count_elems_of_size(to_pset_node(fwnode), | ||
553 | propname, | ||
554 | sizeof(const char *)); | ||
555 | return -ENXIO; | ||
556 | } | ||
557 | |||
558 | static int __fwnode_property_read_string(struct fwnode_handle *fwnode, | ||
559 | const char *propname, const char **val) | ||
560 | { | ||
561 | if (is_of_node(fwnode)) | ||
562 | return of_property_read_string(to_of_node(fwnode), propname, val); | ||
563 | else if (is_acpi_node(fwnode)) | ||
564 | return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING, | ||
565 | val, 1); | ||
566 | else if (is_pset_node(fwnode)) | ||
567 | return pset_prop_read_string(to_pset_node(fwnode), propname, val); | ||
568 | return -ENXIO; | ||
569 | } | ||
570 | |||
437 | /** | 571 | /** |
438 | * fwnode_property_read_string_array - return string array property of a node | 572 | * fwnode_property_read_string_array - return string array property of a node |
439 | * @fwnode: Firmware node to get the property of | 573 | * @fwnode: Firmware node to get the property of |
@@ -456,18 +590,13 @@ int fwnode_property_read_string_array(struct fwnode_handle *fwnode, | |||
456 | const char *propname, const char **val, | 590 | const char *propname, const char **val, |
457 | size_t nval) | 591 | size_t nval) |
458 | { | 592 | { |
459 | if (is_of_node(fwnode)) | 593 | int ret; |
460 | return val ? | 594 | |
461 | of_property_read_string_array(to_of_node(fwnode), | 595 | ret = __fwnode_property_read_string_array(fwnode, propname, val, nval); |
462 | propname, val, nval) : | 596 | if (ret == -EINVAL && fwnode && fwnode->secondary) |
463 | of_property_count_strings(to_of_node(fwnode), propname); | 597 | ret = __fwnode_property_read_string_array(fwnode->secondary, |
464 | else if (is_acpi_node(fwnode)) | 598 | propname, val, nval); |
465 | return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING, | 599 | return ret; |
466 | val, nval); | ||
467 | else if (is_pset(fwnode)) | ||
468 | return pset_prop_read_array(to_pset(fwnode), propname, | ||
469 | DEV_PROP_STRING, val, nval); | ||
470 | return -ENXIO; | ||
471 | } | 600 | } |
472 | EXPORT_SYMBOL_GPL(fwnode_property_read_string_array); | 601 | EXPORT_SYMBOL_GPL(fwnode_property_read_string_array); |
473 | 602 | ||
@@ -489,14 +618,13 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_string_array); | |||
489 | int fwnode_property_read_string(struct fwnode_handle *fwnode, | 618 | int fwnode_property_read_string(struct fwnode_handle *fwnode, |
490 | const char *propname, const char **val) | 619 | const char *propname, const char **val) |
491 | { | 620 | { |
492 | if (is_of_node(fwnode)) | 621 | int ret; |
493 | return of_property_read_string(to_of_node(fwnode), propname, val); | ||
494 | else if (is_acpi_node(fwnode)) | ||
495 | return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING, | ||
496 | val, 1); | ||
497 | 622 | ||
498 | return pset_prop_read_array(to_pset(fwnode), propname, | 623 | ret = __fwnode_property_read_string(fwnode, propname, val); |
499 | DEV_PROP_STRING, val, 1); | 624 | if (ret == -EINVAL && fwnode && fwnode->secondary) |
625 | ret = __fwnode_property_read_string(fwnode->secondary, | ||
626 | propname, val); | ||
627 | return ret; | ||
500 | } | 628 | } |
501 | EXPORT_SYMBOL_GPL(fwnode_property_read_string); | 629 | EXPORT_SYMBOL_GPL(fwnode_property_read_string); |
502 | 630 | ||
@@ -525,6 +653,9 @@ int fwnode_property_match_string(struct fwnode_handle *fwnode, | |||
525 | if (nval < 0) | 653 | if (nval < 0) |
526 | return nval; | 654 | return nval; |
527 | 655 | ||
656 | if (nval == 0) | ||
657 | return -ENODATA; | ||
658 | |||
528 | values = kcalloc(nval, sizeof(*values), GFP_KERNEL); | 659 | values = kcalloc(nval, sizeof(*values), GFP_KERNEL); |
529 | if (!values) | 660 | if (!values) |
530 | return -ENOMEM; | 661 | return -ENOMEM; |
@@ -547,6 +678,182 @@ out: | |||
547 | EXPORT_SYMBOL_GPL(fwnode_property_match_string); | 678 | EXPORT_SYMBOL_GPL(fwnode_property_match_string); |
548 | 679 | ||
549 | /** | 680 | /** |
681 | * pset_free_set - releases memory allocated for copied property set | ||
682 | * @pset: Property set to release | ||
683 | * | ||
684 | * Function takes previously copied property set and releases all the | ||
685 | * memory allocated to it. | ||
686 | */ | ||
687 | static void pset_free_set(struct property_set *pset) | ||
688 | { | ||
689 | const struct property_entry *prop; | ||
690 | size_t i, nval; | ||
691 | |||
692 | if (!pset) | ||
693 | return; | ||
694 | |||
695 | for (prop = pset->properties; prop->name; prop++) { | ||
696 | if (prop->is_array) { | ||
697 | if (prop->is_string && prop->pointer.str) { | ||
698 | nval = prop->length / sizeof(const char *); | ||
699 | for (i = 0; i < nval; i++) | ||
700 | kfree(prop->pointer.str[i]); | ||
701 | } | ||
702 | kfree(prop->pointer.raw_data); | ||
703 | } else if (prop->is_string) { | ||
704 | kfree(prop->value.str); | ||
705 | } | ||
706 | kfree(prop->name); | ||
707 | } | ||
708 | |||
709 | kfree(pset->properties); | ||
710 | kfree(pset); | ||
711 | } | ||
712 | |||
713 | static int pset_copy_entry(struct property_entry *dst, | ||
714 | const struct property_entry *src) | ||
715 | { | ||
716 | const char **d, **s; | ||
717 | size_t i, nval; | ||
718 | |||
719 | dst->name = kstrdup(src->name, GFP_KERNEL); | ||
720 | if (!dst->name) | ||
721 | return -ENOMEM; | ||
722 | |||
723 | if (src->is_array) { | ||
724 | if (!src->length) | ||
725 | return -ENODATA; | ||
726 | |||
727 | if (src->is_string) { | ||
728 | nval = src->length / sizeof(const char *); | ||
729 | dst->pointer.str = kcalloc(nval, sizeof(const char *), | ||
730 | GFP_KERNEL); | ||
731 | if (!dst->pointer.str) | ||
732 | return -ENOMEM; | ||
733 | |||
734 | d = dst->pointer.str; | ||
735 | s = src->pointer.str; | ||
736 | for (i = 0; i < nval; i++) { | ||
737 | d[i] = kstrdup(s[i], GFP_KERNEL); | ||
738 | if (!d[i] && s[i]) | ||
739 | return -ENOMEM; | ||
740 | } | ||
741 | } else { | ||
742 | dst->pointer.raw_data = kmemdup(src->pointer.raw_data, | ||
743 | src->length, GFP_KERNEL); | ||
744 | if (!dst->pointer.raw_data) | ||
745 | return -ENOMEM; | ||
746 | } | ||
747 | } else if (src->is_string) { | ||
748 | dst->value.str = kstrdup(src->value.str, GFP_KERNEL); | ||
749 | if (!dst->value.str && src->value.str) | ||
750 | return -ENOMEM; | ||
751 | } else { | ||
752 | dst->value.raw_data = src->value.raw_data; | ||
753 | } | ||
754 | |||
755 | dst->length = src->length; | ||
756 | dst->is_array = src->is_array; | ||
757 | dst->is_string = src->is_string; | ||
758 | |||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | /** | ||
763 | * pset_copy_set - copies property set | ||
764 | * @pset: Property set to copy | ||
765 | * | ||
766 | * This function takes a deep copy of the given property set and returns | ||
767 | * pointer to the copy. Call device_free_property_set() to free resources | ||
768 | * allocated in this function. | ||
769 | * | ||
770 | * Return: Pointer to the new property set or error pointer. | ||
771 | */ | ||
772 | static struct property_set *pset_copy_set(const struct property_set *pset) | ||
773 | { | ||
774 | const struct property_entry *entry; | ||
775 | struct property_set *p; | ||
776 | size_t i, n = 0; | ||
777 | |||
778 | p = kzalloc(sizeof(*p), GFP_KERNEL); | ||
779 | if (!p) | ||
780 | return ERR_PTR(-ENOMEM); | ||
781 | |||
782 | while (pset->properties[n].name) | ||
783 | n++; | ||
784 | |||
785 | p->properties = kcalloc(n + 1, sizeof(*entry), GFP_KERNEL); | ||
786 | if (!p->properties) { | ||
787 | kfree(p); | ||
788 | return ERR_PTR(-ENOMEM); | ||
789 | } | ||
790 | |||
791 | for (i = 0; i < n; i++) { | ||
792 | int ret = pset_copy_entry(&p->properties[i], | ||
793 | &pset->properties[i]); | ||
794 | if (ret) { | ||
795 | pset_free_set(p); | ||
796 | return ERR_PTR(ret); | ||
797 | } | ||
798 | } | ||
799 | |||
800 | return p; | ||
801 | } | ||
802 | |||
803 | /** | ||
804 | * device_remove_property_set - Remove properties from a device object. | ||
805 | * @dev: Device whose properties to remove. | ||
806 | * | ||
807 | * The function removes properties previously associated to the device | ||
808 | * secondary firmware node with device_add_property_set(). Memory allocated | ||
809 | * to the properties will also be released. | ||
810 | */ | ||
811 | void device_remove_property_set(struct device *dev) | ||
812 | { | ||
813 | struct fwnode_handle *fwnode; | ||
814 | |||
815 | fwnode = dev_fwnode(dev); | ||
816 | if (!fwnode) | ||
817 | return; | ||
818 | /* | ||
819 | * Pick either primary or secondary node depending which one holds | ||
820 | * the pset. If there is no real firmware node (ACPI/DT) primary | ||
821 | * will hold the pset. | ||
822 | */ | ||
823 | if (!is_pset_node(fwnode)) | ||
824 | fwnode = fwnode->secondary; | ||
825 | if (!IS_ERR(fwnode) && is_pset_node(fwnode)) | ||
826 | pset_free_set(to_pset_node(fwnode)); | ||
827 | set_secondary_fwnode(dev, NULL); | ||
828 | } | ||
829 | EXPORT_SYMBOL_GPL(device_remove_property_set); | ||
830 | |||
831 | /** | ||
832 | * device_add_property_set - Add a collection of properties to a device object. | ||
833 | * @dev: Device to add properties to. | ||
834 | * @pset: Collection of properties to add. | ||
835 | * | ||
836 | * Associate a collection of device properties represented by @pset with @dev | ||
837 | * as its secondary firmware node. The function takes a copy of @pset. | ||
838 | */ | ||
839 | int device_add_property_set(struct device *dev, const struct property_set *pset) | ||
840 | { | ||
841 | struct property_set *p; | ||
842 | |||
843 | if (!pset) | ||
844 | return -EINVAL; | ||
845 | |||
846 | p = pset_copy_set(pset); | ||
847 | if (IS_ERR(p)) | ||
848 | return PTR_ERR(p); | ||
849 | |||
850 | p->fwnode.type = FWNODE_PDATA; | ||
851 | set_secondary_fwnode(dev, &p->fwnode); | ||
852 | return 0; | ||
853 | } | ||
854 | EXPORT_SYMBOL_GPL(device_add_property_set); | ||
855 | |||
856 | /** | ||
550 | * device_get_next_child_node - Return the next child node handle for a device | 857 | * device_get_next_child_node - Return the next child node handle for a device |
551 | * @dev: Device to find the next child node for. | 858 | * @dev: Device to find the next child node for. |
552 | * @child: Handle to one of the device's child nodes or a null handle. | 859 | * @child: Handle to one of the device's child nodes or a null handle. |