diff options
Diffstat (limited to 'drivers/of/base.c')
-rw-r--r-- | drivers/of/base.c | 160 |
1 files changed, 105 insertions, 55 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c index ff85450d5683..1b95a405628f 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -342,27 +342,72 @@ struct device_node *of_get_cpu_node(int cpu, unsigned int *thread) } EXPORT_SYMBOL(of_get_cpu_node); -/** Checks if the given "compat" string matches one of the strings in - * the device's "compatible" property +/** + * __of_device_is_compatible() - Check if the node matches given constraints + * @device: pointer to node + * @compat: required compatible string, NULL or "" for any match + * @type: required device_type value, NULL or "" for any match + * @name: required node name, NULL or "" for any match + * + * Checks if the given @compat, @type and @name strings match the + * properties of the given @device. A constraints can be skipped by + * passing NULL or an empty string as the constraint. + * + * Returns 0 for no match, and a positive integer on match. The return + * value is a relative score with larger values indicating better + * matches. The score is weighted for the most specific compatible value + * to get the highest score. Matching type is next, followed by matching + * name. Practically speaking, this results in the following priority + * order for matches: + * + * 1. specific compatible && type && name + * 2. specific compatible && type + * 3. specific compatible && name + * 4. specific compatible + * 5. general compatible && type && name + * 6. general compatible && type + * 7. general compatible && name + * 8. general compatible + * 9. type && name + * 10. type + * 11. name */ static int __of_device_is_compatible(const struct device_node *device, - const char *compat) + const char *compat, const char *type, const char *name) { - const char* cp; - int cplen, l; + struct property *prop; + const char *cp; + int index = 0, score = 0; + + /* Compatible match has highest priority */ + if (compat && compat[0]) { + prop = __of_find_property(device, "compatible", NULL); + for (cp = of_prop_next_string(prop, NULL); cp; + cp = of_prop_next_string(prop, cp), index++) { + if (of_compat_cmp(cp, compat, strlen(compat)) == 0) { + score = INT_MAX/2 - (index << 2); + break; + } + } + if (!score) + return 0; + } - cp = __of_get_property(device, "compatible", &cplen); - if (cp == NULL) - return 0; - while (cplen > 0) { - if (of_compat_cmp(cp, compat, strlen(compat)) == 0) - return 1; - l = strlen(cp) + 1; - cp += l; - cplen -= l; + /* Matching type is better than matching name */ + if (type && type[0]) { + if (!device->type || of_node_cmp(type, device->type)) + return 0; + score += 2; } - return 0; + /* Matching name is a bit better than not */ + if (name && name[0]) { + if (!device->name || of_node_cmp(name, device->name)) + return 0; + score++; + } + + return score; } /** Checks if the given "compat" string matches one of the strings in @@ -375,7 +420,7 @@ int of_device_is_compatible(const struct device_node *device, int res; raw_spin_lock_irqsave(&devtree_lock, flags); - res = __of_device_is_compatible(device, compat); + res = __of_device_is_compatible(device, compat, NULL, NULL); raw_spin_unlock_irqrestore(&devtree_lock, flags); return res; } @@ -681,10 +726,7 @@ struct device_node *of_find_compatible_node(struct device_node *from, raw_spin_lock_irqsave(&devtree_lock, flags); np = from ? from->allnext : of_allnodes; for (; np; np = np->allnext) { - if (type - && !(np->type && (of_node_cmp(np->type, type) == 0))) - continue; - if (__of_device_is_compatible(np, compatible) && + if (__of_device_is_compatible(np, compatible, type, NULL) && of_node_get(np)) break; } @@ -734,43 +776,22 @@ static const struct of_device_id *__of_match_node(const struct of_device_id *matches, const struct device_node *node) { - const char *cp; - int cplen, l; + const struct of_device_id *best_match = NULL; + int score, best_score = 0; if (!matches) return NULL; - cp = __of_get_property(node, "compatible", &cplen); - do { - const struct of_device_id *m = matches; - - /* Check against matches with current compatible string */ - while (m->name[0] || m->type[0] || m->compatible[0]) { - int match = 1; - if (m->name[0]) - match &= node->name - && !strcmp(m->name, node->name); - if (m->type[0]) - match &= node->type - && !strcmp(m->type, node->type); - if (m->compatible[0]) - match &= cp - && !of_compat_cmp(m->compatible, cp, - strlen(m->compatible)); - if (match) - return m; - m++; - } - - /* Get node's next compatible string */ - if (cp) { - l = strlen(cp) + 1; - cp += l; - cplen -= l; + for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) { + score = __of_device_is_compatible(node, matches->compatible, + matches->type, matches->name); + if (score > best_score) { + best_match = matches; + best_score = score; } - } while (cp && (cplen > 0)); + } - return NULL; + return best_match; } /** @@ -778,10 +799,7 @@ const struct of_device_id *__of_match_node(const struct of_device_id *matches, * @matches: array of of device match structures to search in * @node: the of device structure to match against * - * Low level utility function used by device matching. Matching order - * is to compare each of the node's compatibles with all given matches - * first. This implies node's compatible is sorted from specific to - * generic while matches can be in any order. + * Low level utility function used by device matching. */ const struct of_device_id *of_match_node(const struct of_device_id *matches, const struct device_node *node) @@ -886,6 +904,38 @@ struct device_node *of_find_node_by_phandle(phandle handle) EXPORT_SYMBOL(of_find_node_by_phandle); /** + * of_property_count_elems_of_size - Count the number of elements in a property + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @elem_size: size of the individual element + * + * Search for a property in a device node and count the number of elements of + * size elem_size in it. Returns number of elements on sucess, -EINVAL if the + * property does not exist or its length does not match a multiple of elem_size + * and -ENODATA if the property does not have a value. + */ +int of_property_count_elems_of_size(const struct device_node *np, + const char *propname, int elem_size) +{ + struct property *prop = of_find_property(np, propname, NULL); + + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + + if (prop->length % elem_size != 0) { + pr_err("size of %s in node %s is not a multiple of %d\n", + propname, np->full_name, elem_size); + return -EINVAL; + } + + return prop->length / elem_size; +} +EXPORT_SYMBOL_GPL(of_property_count_elems_of_size); + +/** * of_find_property_value_of_size * * @np: device node from which the property value is to be read. |