| .. | .. |
|---|
| 18 | 18 | #include <linux/etherdevice.h> |
|---|
| 19 | 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 | 21 | struct fwnode_handle *dev_fwnode(struct device *dev) |
|---|
| 252 | 22 | { |
|---|
| 253 | 23 | return IS_ENABLED(CONFIG_OF) && dev->of_node ? |
|---|
| 254 | 24 | &dev->of_node->fwnode : dev->fwnode; |
|---|
| 255 | 25 | } |
|---|
| 256 | 26 | EXPORT_SYMBOL_GPL(dev_fwnode); |
|---|
| 257 | | - |
|---|
| 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 | 27 | |
|---|
| 303 | 28 | /** |
|---|
| 304 | 29 | * device_property_present - check if a property of a device is present |
|---|
| .. | .. |
|---|
| 759 | 484 | } |
|---|
| 760 | 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 | 487 | /** |
|---|
| 852 | | - * property_entries_dup - duplicate array of properties |
|---|
| 853 | | - * @properties: array of properties to copy |
|---|
| 488 | + * fwnode_find_reference - Find named reference to a fwnode_handle |
|---|
| 489 | + * @fwnode: Firmware node where to look for the reference |
|---|
| 490 | + * @name: The name of the reference |
|---|
| 491 | + * @index: Index of the reference |
|---|
| 854 | 492 | * |
|---|
| 855 | | - * This function creates a deep copy of the given NULL-terminated array |
|---|
| 856 | | - * of property entries. |
|---|
| 493 | + * @index can be used when the named reference holds a table of references. |
|---|
| 494 | + * |
|---|
| 495 | + * Returns pointer to the reference fwnode, or ERR_PTR. Caller is responsible to |
|---|
| 496 | + * call fwnode_handle_put() on the returned fwnode pointer. |
|---|
| 857 | 497 | */ |
|---|
| 858 | | -struct property_entry * |
|---|
| 859 | | -property_entries_dup(const struct property_entry *properties) |
|---|
| 498 | +struct fwnode_handle *fwnode_find_reference(const struct fwnode_handle *fwnode, |
|---|
| 499 | + const char *name, |
|---|
| 500 | + unsigned int index) |
|---|
| 860 | 501 | { |
|---|
| 861 | | - struct property_entry *p; |
|---|
| 862 | | - int i, n = 0; |
|---|
| 502 | + struct fwnode_reference_args args; |
|---|
| 503 | + int ret; |
|---|
| 863 | 504 | |
|---|
| 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; |
|---|
| 505 | + ret = fwnode_property_get_reference_args(fwnode, name, NULL, 0, index, |
|---|
| 506 | + &args); |
|---|
| 507 | + return ret ? ERR_PTR(ret) : args.fwnode; |
|---|
| 882 | 508 | } |
|---|
| 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 | | -} |
|---|
| 509 | +EXPORT_SYMBOL_GPL(fwnode_find_reference); |
|---|
| 947 | 510 | |
|---|
| 948 | 511 | /** |
|---|
| 949 | 512 | * device_remove_properties - Remove properties from a device object. |
|---|
| 950 | 513 | * @dev: Device whose properties to remove. |
|---|
| 951 | 514 | * |
|---|
| 952 | 515 | * The function removes properties previously associated to the device |
|---|
| 953 | | - * secondary firmware node with device_add_properties(). Memory allocated |
|---|
| 954 | | - * to the properties will also be released. |
|---|
| 516 | + * firmware node with device_add_properties(). Memory allocated to the |
|---|
| 517 | + * properties will also be released. |
|---|
| 955 | 518 | */ |
|---|
| 956 | 519 | void device_remove_properties(struct device *dev) |
|---|
| 957 | 520 | { |
|---|
| 958 | | - struct fwnode_handle *fwnode; |
|---|
| 959 | | - struct property_set *pset; |
|---|
| 521 | + struct fwnode_handle *fwnode = dev_fwnode(dev); |
|---|
| 960 | 522 | |
|---|
| 961 | | - fwnode = dev_fwnode(dev); |
|---|
| 962 | 523 | if (!fwnode) |
|---|
| 963 | 524 | return; |
|---|
| 964 | | - /* |
|---|
| 965 | | - * Pick either primary or secondary node depending which one holds |
|---|
| 966 | | - * the pset. If there is no real firmware node (ACPI/DT) primary |
|---|
| 967 | | - * will hold the pset. |
|---|
| 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); |
|---|
| 525 | + |
|---|
| 526 | + if (is_software_node(fwnode->secondary)) { |
|---|
| 527 | + fwnode_remove_software_node(fwnode->secondary); |
|---|
| 528 | + set_secondary_fwnode(dev, NULL); |
|---|
| 976 | 529 | } |
|---|
| 977 | | - if (pset && dev == pset->dev) |
|---|
| 978 | | - pset_free_set(pset); |
|---|
| 979 | 530 | } |
|---|
| 980 | 531 | EXPORT_SYMBOL_GPL(device_remove_properties); |
|---|
| 981 | 532 | |
|---|
| .. | .. |
|---|
| 985 | 536 | * @properties: Collection of properties to add. |
|---|
| 986 | 537 | * |
|---|
| 987 | 538 | * Associate a collection of device properties represented by @properties with |
|---|
| 988 | | - * @dev as its secondary firmware node. The function takes a copy of |
|---|
| 989 | | - * @properties. |
|---|
| 539 | + * @dev. The function takes a copy of @properties. |
|---|
| 540 | + * |
|---|
| 541 | + * WARNING: The callers should not use this function if it is known that there |
|---|
| 542 | + * is no real firmware node associated with @dev! In that case the callers |
|---|
| 543 | + * should create a software node and assign it to @dev directly. |
|---|
| 990 | 544 | */ |
|---|
| 991 | 545 | int device_add_properties(struct device *dev, |
|---|
| 992 | 546 | const struct property_entry *properties) |
|---|
| 993 | 547 | { |
|---|
| 994 | | - struct property_set *p, pset; |
|---|
| 548 | + struct fwnode_handle *fwnode; |
|---|
| 995 | 549 | |
|---|
| 996 | | - if (!properties) |
|---|
| 997 | | - return -EINVAL; |
|---|
| 550 | + fwnode = fwnode_create_software_node(properties, NULL); |
|---|
| 551 | + if (IS_ERR(fwnode)) |
|---|
| 552 | + return PTR_ERR(fwnode); |
|---|
| 998 | 553 | |
|---|
| 999 | | - pset.properties = properties; |
|---|
| 1000 | | - |
|---|
| 1001 | | - p = pset_copy_set(&pset); |
|---|
| 1002 | | - if (IS_ERR(p)) |
|---|
| 1003 | | - return PTR_ERR(p); |
|---|
| 1004 | | - |
|---|
| 1005 | | - p->fwnode.ops = &pset_fwnode_ops; |
|---|
| 1006 | | - set_secondary_fwnode(dev, &p->fwnode); |
|---|
| 1007 | | - p->dev = dev; |
|---|
| 554 | + set_secondary_fwnode(dev, fwnode); |
|---|
| 1008 | 555 | return 0; |
|---|
| 1009 | 556 | } |
|---|
| 1010 | 557 | EXPORT_SYMBOL_GPL(device_add_properties); |
|---|
| 558 | + |
|---|
| 559 | +/** |
|---|
| 560 | + * fwnode_get_name - Return the name of a node |
|---|
| 561 | + * @fwnode: The firmware node |
|---|
| 562 | + * |
|---|
| 563 | + * Returns a pointer to the node name. |
|---|
| 564 | + */ |
|---|
| 565 | +const char *fwnode_get_name(const struct fwnode_handle *fwnode) |
|---|
| 566 | +{ |
|---|
| 567 | + return fwnode_call_ptr_op(fwnode, get_name); |
|---|
| 568 | +} |
|---|
| 569 | +EXPORT_SYMBOL_GPL(fwnode_get_name); |
|---|
| 570 | + |
|---|
| 571 | +/** |
|---|
| 572 | + * fwnode_get_name_prefix - Return the prefix of node for printing purposes |
|---|
| 573 | + * @fwnode: The firmware node |
|---|
| 574 | + * |
|---|
| 575 | + * Returns the prefix of a node, intended to be printed right before the node. |
|---|
| 576 | + * The prefix works also as a separator between the nodes. |
|---|
| 577 | + */ |
|---|
| 578 | +const char *fwnode_get_name_prefix(const struct fwnode_handle *fwnode) |
|---|
| 579 | +{ |
|---|
| 580 | + return fwnode_call_ptr_op(fwnode, get_name_prefix); |
|---|
| 581 | +} |
|---|
| 582 | + |
|---|
| 583 | +/** |
|---|
| 584 | + * fwnode_get_parent - Return parent firwmare node |
|---|
| 585 | + * @fwnode: Firmware whose parent is retrieved |
|---|
| 586 | + * |
|---|
| 587 | + * Return parent firmware node of the given node if possible or %NULL if no |
|---|
| 588 | + * parent was available. |
|---|
| 589 | + */ |
|---|
| 590 | +struct fwnode_handle *fwnode_get_parent(const struct fwnode_handle *fwnode) |
|---|
| 591 | +{ |
|---|
| 592 | + return fwnode_call_ptr_op(fwnode, get_parent); |
|---|
| 593 | +} |
|---|
| 594 | +EXPORT_SYMBOL_GPL(fwnode_get_parent); |
|---|
| 1011 | 595 | |
|---|
| 1012 | 596 | /** |
|---|
| 1013 | 597 | * fwnode_get_next_parent - Iterate to the node's parent |
|---|
| .. | .. |
|---|
| 1031 | 615 | EXPORT_SYMBOL_GPL(fwnode_get_next_parent); |
|---|
| 1032 | 616 | |
|---|
| 1033 | 617 | /** |
|---|
| 1034 | | - * fwnode_get_parent - Return parent firwmare node |
|---|
| 1035 | | - * @fwnode: Firmware whose parent is retrieved |
|---|
| 618 | + * fwnode_get_next_parent_dev - Find device of closest ancestor fwnode |
|---|
| 619 | + * @fwnode: firmware node |
|---|
| 1036 | 620 | * |
|---|
| 1037 | | - * Return parent firmware node of the given node if possible or %NULL if no |
|---|
| 1038 | | - * parent was available. |
|---|
| 621 | + * Given a firmware node (@fwnode), this function finds its closest ancestor |
|---|
| 622 | + * firmware node that has a corresponding struct device and returns that struct |
|---|
| 623 | + * device. |
|---|
| 624 | + * |
|---|
| 625 | + * The caller of this function is expected to call put_device() on the returned |
|---|
| 626 | + * device when they are done. |
|---|
| 1039 | 627 | */ |
|---|
| 1040 | | -struct fwnode_handle *fwnode_get_parent(const struct fwnode_handle *fwnode) |
|---|
| 628 | +struct device *fwnode_get_next_parent_dev(struct fwnode_handle *fwnode) |
|---|
| 1041 | 629 | { |
|---|
| 1042 | | - return fwnode_call_ptr_op(fwnode, get_parent); |
|---|
| 630 | + struct device *dev = NULL; |
|---|
| 631 | + |
|---|
| 632 | + fwnode_handle_get(fwnode); |
|---|
| 633 | + do { |
|---|
| 634 | + fwnode = fwnode_get_next_parent(fwnode); |
|---|
| 635 | + if (fwnode) |
|---|
| 636 | + dev = get_dev_from_fwnode(fwnode); |
|---|
| 637 | + } while (fwnode && !dev); |
|---|
| 638 | + fwnode_handle_put(fwnode); |
|---|
| 639 | + return dev; |
|---|
| 1043 | 640 | } |
|---|
| 1044 | | -EXPORT_SYMBOL_GPL(fwnode_get_parent); |
|---|
| 641 | + |
|---|
| 642 | +/** |
|---|
| 643 | + * fwnode_count_parents - Return the number of parents a node has |
|---|
| 644 | + * @fwnode: The node the parents of which are to be counted |
|---|
| 645 | + * |
|---|
| 646 | + * Returns the number of parents a node has. |
|---|
| 647 | + */ |
|---|
| 648 | +unsigned int fwnode_count_parents(const struct fwnode_handle *fwnode) |
|---|
| 649 | +{ |
|---|
| 650 | + struct fwnode_handle *__fwnode; |
|---|
| 651 | + unsigned int count; |
|---|
| 652 | + |
|---|
| 653 | + __fwnode = fwnode_get_parent(fwnode); |
|---|
| 654 | + |
|---|
| 655 | + for (count = 0; __fwnode; count++) |
|---|
| 656 | + __fwnode = fwnode_get_next_parent(__fwnode); |
|---|
| 657 | + |
|---|
| 658 | + return count; |
|---|
| 659 | +} |
|---|
| 660 | +EXPORT_SYMBOL_GPL(fwnode_count_parents); |
|---|
| 661 | + |
|---|
| 662 | +/** |
|---|
| 663 | + * fwnode_get_nth_parent - Return an nth parent of a node |
|---|
| 664 | + * @fwnode: The node the parent of which is requested |
|---|
| 665 | + * @depth: Distance of the parent from the node |
|---|
| 666 | + * |
|---|
| 667 | + * Returns the nth parent of a node. If there is no parent at the requested |
|---|
| 668 | + * @depth, %NULL is returned. If @depth is 0, the functionality is equivalent to |
|---|
| 669 | + * fwnode_handle_get(). For @depth == 1, it is fwnode_get_parent() and so on. |
|---|
| 670 | + * |
|---|
| 671 | + * The caller is responsible for calling fwnode_handle_put() for the returned |
|---|
| 672 | + * node. |
|---|
| 673 | + */ |
|---|
| 674 | +struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwnode, |
|---|
| 675 | + unsigned int depth) |
|---|
| 676 | +{ |
|---|
| 677 | + unsigned int i; |
|---|
| 678 | + |
|---|
| 679 | + fwnode_handle_get(fwnode); |
|---|
| 680 | + |
|---|
| 681 | + for (i = 0; i < depth && fwnode; i++) |
|---|
| 682 | + fwnode = fwnode_get_next_parent(fwnode); |
|---|
| 683 | + |
|---|
| 684 | + return fwnode; |
|---|
| 685 | +} |
|---|
| 686 | +EXPORT_SYMBOL_GPL(fwnode_get_nth_parent); |
|---|
| 687 | + |
|---|
| 688 | +/** |
|---|
| 689 | + * fwnode_is_ancestor_of - Test if @test_ancestor is ancestor of @test_child |
|---|
| 690 | + * @test_ancestor: Firmware which is tested for being an ancestor |
|---|
| 691 | + * @test_child: Firmware which is tested for being the child |
|---|
| 692 | + * |
|---|
| 693 | + * A node is considered an ancestor of itself too. |
|---|
| 694 | + * |
|---|
| 695 | + * Returns true if @test_ancestor is an ancestor of @test_child. |
|---|
| 696 | + * Otherwise, returns false. |
|---|
| 697 | + */ |
|---|
| 698 | +bool fwnode_is_ancestor_of(struct fwnode_handle *test_ancestor, |
|---|
| 699 | + struct fwnode_handle *test_child) |
|---|
| 700 | +{ |
|---|
| 701 | + if (!test_ancestor) |
|---|
| 702 | + return false; |
|---|
| 703 | + |
|---|
| 704 | + fwnode_handle_get(test_child); |
|---|
| 705 | + while (test_child) { |
|---|
| 706 | + if (test_child == test_ancestor) { |
|---|
| 707 | + fwnode_handle_put(test_child); |
|---|
| 708 | + return true; |
|---|
| 709 | + } |
|---|
| 710 | + test_child = fwnode_get_next_parent(test_child); |
|---|
| 711 | + } |
|---|
| 712 | + return false; |
|---|
| 713 | +} |
|---|
| 1045 | 714 | |
|---|
| 1046 | 715 | /** |
|---|
| 1047 | 716 | * fwnode_get_next_child_node - Return the next child node handle for a node |
|---|
| .. | .. |
|---|
| 1091 | 760 | struct fwnode_handle *child) |
|---|
| 1092 | 761 | { |
|---|
| 1093 | 762 | struct acpi_device *adev = ACPI_COMPANION(dev); |
|---|
| 1094 | | - struct fwnode_handle *fwnode = NULL; |
|---|
| 763 | + struct fwnode_handle *fwnode = NULL, *next; |
|---|
| 1095 | 764 | |
|---|
| 1096 | 765 | if (dev->of_node) |
|---|
| 1097 | 766 | fwnode = &dev->of_node->fwnode; |
|---|
| 1098 | 767 | else if (adev) |
|---|
| 1099 | 768 | fwnode = acpi_fwnode_handle(adev); |
|---|
| 1100 | 769 | |
|---|
| 1101 | | - return fwnode_get_next_child_node(fwnode, child); |
|---|
| 770 | + /* Try to find a child in primary fwnode */ |
|---|
| 771 | + next = fwnode_get_next_child_node(fwnode, child); |
|---|
| 772 | + if (next) |
|---|
| 773 | + return next; |
|---|
| 774 | + |
|---|
| 775 | + /* When no more children in primary, continue with secondary */ |
|---|
| 776 | + if (fwnode && !IS_ERR_OR_NULL(fwnode->secondary)) |
|---|
| 777 | + next = fwnode_get_next_child_node(fwnode->secondary, child); |
|---|
| 778 | + |
|---|
| 779 | + return next; |
|---|
| 1102 | 780 | } |
|---|
| 1103 | 781 | EXPORT_SYMBOL_GPL(device_get_next_child_node); |
|---|
| 1104 | 782 | |
|---|
| .. | .. |
|---|
| 1341 | 1019 | EXPORT_SYMBOL(fwnode_irq_get); |
|---|
| 1342 | 1020 | |
|---|
| 1343 | 1021 | /** |
|---|
| 1344 | | - * device_graph_get_next_endpoint - Get next endpoint firmware node |
|---|
| 1022 | + * fwnode_graph_get_next_endpoint - Get next endpoint firmware node |
|---|
| 1345 | 1023 | * @fwnode: Pointer to the parent firmware node |
|---|
| 1346 | 1024 | * @prev: Previous endpoint node or %NULL to get the first |
|---|
| 1347 | 1025 | * |
|---|
| .. | .. |
|---|
| 1461 | 1139 | EXPORT_SYMBOL_GPL(fwnode_graph_get_remote_node); |
|---|
| 1462 | 1140 | |
|---|
| 1463 | 1141 | /** |
|---|
| 1142 | + * fwnode_graph_get_endpoint_by_id - get endpoint by port and endpoint numbers |
|---|
| 1143 | + * @fwnode: parent fwnode_handle containing the graph |
|---|
| 1144 | + * @port: identifier of the port node |
|---|
| 1145 | + * @endpoint: identifier of the endpoint node under the port node |
|---|
| 1146 | + * @flags: fwnode lookup flags |
|---|
| 1147 | + * |
|---|
| 1148 | + * Return the fwnode handle of the local endpoint corresponding the port and |
|---|
| 1149 | + * endpoint IDs or NULL if not found. |
|---|
| 1150 | + * |
|---|
| 1151 | + * If FWNODE_GRAPH_ENDPOINT_NEXT is passed in @flags and the specified endpoint |
|---|
| 1152 | + * has not been found, look for the closest endpoint ID greater than the |
|---|
| 1153 | + * specified one and return the endpoint that corresponds to it, if present. |
|---|
| 1154 | + * |
|---|
| 1155 | + * Do not return endpoints that belong to disabled devices, unless |
|---|
| 1156 | + * FWNODE_GRAPH_DEVICE_DISABLED is passed in @flags. |
|---|
| 1157 | + * |
|---|
| 1158 | + * The returned endpoint needs to be released by calling fwnode_handle_put() on |
|---|
| 1159 | + * it when it is not needed any more. |
|---|
| 1160 | + */ |
|---|
| 1161 | +struct fwnode_handle * |
|---|
| 1162 | +fwnode_graph_get_endpoint_by_id(const struct fwnode_handle *fwnode, |
|---|
| 1163 | + u32 port, u32 endpoint, unsigned long flags) |
|---|
| 1164 | +{ |
|---|
| 1165 | + struct fwnode_handle *ep = NULL, *best_ep = NULL; |
|---|
| 1166 | + unsigned int best_ep_id = 0; |
|---|
| 1167 | + bool endpoint_next = flags & FWNODE_GRAPH_ENDPOINT_NEXT; |
|---|
| 1168 | + bool enabled_only = !(flags & FWNODE_GRAPH_DEVICE_DISABLED); |
|---|
| 1169 | + |
|---|
| 1170 | + while ((ep = fwnode_graph_get_next_endpoint(fwnode, ep))) { |
|---|
| 1171 | + struct fwnode_endpoint fwnode_ep = { 0 }; |
|---|
| 1172 | + int ret; |
|---|
| 1173 | + |
|---|
| 1174 | + if (enabled_only) { |
|---|
| 1175 | + struct fwnode_handle *dev_node; |
|---|
| 1176 | + bool available; |
|---|
| 1177 | + |
|---|
| 1178 | + dev_node = fwnode_graph_get_remote_port_parent(ep); |
|---|
| 1179 | + available = fwnode_device_is_available(dev_node); |
|---|
| 1180 | + fwnode_handle_put(dev_node); |
|---|
| 1181 | + if (!available) |
|---|
| 1182 | + continue; |
|---|
| 1183 | + } |
|---|
| 1184 | + |
|---|
| 1185 | + ret = fwnode_graph_parse_endpoint(ep, &fwnode_ep); |
|---|
| 1186 | + if (ret < 0) |
|---|
| 1187 | + continue; |
|---|
| 1188 | + |
|---|
| 1189 | + if (fwnode_ep.port != port) |
|---|
| 1190 | + continue; |
|---|
| 1191 | + |
|---|
| 1192 | + if (fwnode_ep.id == endpoint) |
|---|
| 1193 | + return ep; |
|---|
| 1194 | + |
|---|
| 1195 | + if (!endpoint_next) |
|---|
| 1196 | + continue; |
|---|
| 1197 | + |
|---|
| 1198 | + /* |
|---|
| 1199 | + * If the endpoint that has just been found is not the first |
|---|
| 1200 | + * matching one and the ID of the one found previously is closer |
|---|
| 1201 | + * to the requested endpoint ID, skip it. |
|---|
| 1202 | + */ |
|---|
| 1203 | + if (fwnode_ep.id < endpoint || |
|---|
| 1204 | + (best_ep && best_ep_id < fwnode_ep.id)) |
|---|
| 1205 | + continue; |
|---|
| 1206 | + |
|---|
| 1207 | + fwnode_handle_put(best_ep); |
|---|
| 1208 | + best_ep = fwnode_handle_get(ep); |
|---|
| 1209 | + best_ep_id = fwnode_ep.id; |
|---|
| 1210 | + } |
|---|
| 1211 | + |
|---|
| 1212 | + return best_ep; |
|---|
| 1213 | +} |
|---|
| 1214 | +EXPORT_SYMBOL_GPL(fwnode_graph_get_endpoint_by_id); |
|---|
| 1215 | + |
|---|
| 1216 | +/** |
|---|
| 1464 | 1217 | * fwnode_graph_parse_endpoint - parse common endpoint node properties |
|---|
| 1465 | 1218 | * @fwnode: pointer to endpoint fwnode_handle |
|---|
| 1466 | 1219 | * @endpoint: pointer to the fwnode endpoint data structure |
|---|
| .. | .. |
|---|
| 1483 | 1236 | return fwnode_call_ptr_op(dev_fwnode(dev), device_get_match_data, dev); |
|---|
| 1484 | 1237 | } |
|---|
| 1485 | 1238 | EXPORT_SYMBOL_GPL(device_get_match_data); |
|---|
| 1239 | + |
|---|
| 1240 | +static void * |
|---|
| 1241 | +fwnode_graph_devcon_match(struct fwnode_handle *fwnode, const char *con_id, |
|---|
| 1242 | + void *data, devcon_match_fn_t match) |
|---|
| 1243 | +{ |
|---|
| 1244 | + struct fwnode_handle *node; |
|---|
| 1245 | + struct fwnode_handle *ep; |
|---|
| 1246 | + void *ret; |
|---|
| 1247 | + |
|---|
| 1248 | + fwnode_graph_for_each_endpoint(fwnode, ep) { |
|---|
| 1249 | + node = fwnode_graph_get_remote_port_parent(ep); |
|---|
| 1250 | + if (!fwnode_device_is_available(node)) { |
|---|
| 1251 | + fwnode_handle_put(node); |
|---|
| 1252 | + continue; |
|---|
| 1253 | + } |
|---|
| 1254 | + |
|---|
| 1255 | + ret = match(node, con_id, data); |
|---|
| 1256 | + fwnode_handle_put(node); |
|---|
| 1257 | + if (ret) { |
|---|
| 1258 | + fwnode_handle_put(ep); |
|---|
| 1259 | + return ret; |
|---|
| 1260 | + } |
|---|
| 1261 | + } |
|---|
| 1262 | + return NULL; |
|---|
| 1263 | +} |
|---|
| 1264 | + |
|---|
| 1265 | +static void * |
|---|
| 1266 | +fwnode_devcon_match(struct fwnode_handle *fwnode, const char *con_id, |
|---|
| 1267 | + void *data, devcon_match_fn_t match) |
|---|
| 1268 | +{ |
|---|
| 1269 | + struct fwnode_handle *node; |
|---|
| 1270 | + void *ret; |
|---|
| 1271 | + int i; |
|---|
| 1272 | + |
|---|
| 1273 | + for (i = 0; ; i++) { |
|---|
| 1274 | + node = fwnode_find_reference(fwnode, con_id, i); |
|---|
| 1275 | + if (IS_ERR(node)) |
|---|
| 1276 | + break; |
|---|
| 1277 | + |
|---|
| 1278 | + ret = match(node, NULL, data); |
|---|
| 1279 | + fwnode_handle_put(node); |
|---|
| 1280 | + if (ret) |
|---|
| 1281 | + return ret; |
|---|
| 1282 | + } |
|---|
| 1283 | + |
|---|
| 1284 | + return NULL; |
|---|
| 1285 | +} |
|---|
| 1286 | + |
|---|
| 1287 | +/** |
|---|
| 1288 | + * fwnode_connection_find_match - Find connection from a device node |
|---|
| 1289 | + * @fwnode: Device node with the connection |
|---|
| 1290 | + * @con_id: Identifier for the connection |
|---|
| 1291 | + * @data: Data for the match function |
|---|
| 1292 | + * @match: Function to check and convert the connection description |
|---|
| 1293 | + * |
|---|
| 1294 | + * Find a connection with unique identifier @con_id between @fwnode and another |
|---|
| 1295 | + * device node. @match will be used to convert the connection description to |
|---|
| 1296 | + * data the caller is expecting to be returned. |
|---|
| 1297 | + */ |
|---|
| 1298 | +void *fwnode_connection_find_match(struct fwnode_handle *fwnode, |
|---|
| 1299 | + const char *con_id, void *data, |
|---|
| 1300 | + devcon_match_fn_t match) |
|---|
| 1301 | +{ |
|---|
| 1302 | + void *ret; |
|---|
| 1303 | + |
|---|
| 1304 | + if (!fwnode || !match) |
|---|
| 1305 | + return NULL; |
|---|
| 1306 | + |
|---|
| 1307 | + ret = fwnode_graph_devcon_match(fwnode, con_id, data, match); |
|---|
| 1308 | + if (ret) |
|---|
| 1309 | + return ret; |
|---|
| 1310 | + |
|---|
| 1311 | + return fwnode_devcon_match(fwnode, con_id, data, match); |
|---|
| 1312 | +} |
|---|
| 1313 | +EXPORT_SYMBOL_GPL(fwnode_connection_find_match); |
|---|