diff options
Diffstat (limited to 'drivers/of/base.c')
-rw-r--r-- | drivers/of/base.c | 286 |
1 files changed, 233 insertions, 53 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c index b91ee92e1b..f0d3574148 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -17,12 +17,28 @@ #include <linux/sizes.h> #include <of_graph.h> #include <string.h> +#include <libfile.h> #include <linux/clk.h> #include <linux/ctype.h> #include <linux/err.h> static struct device_node *root_node; +/** + * of_node_has_prefix - Test if a node name has a given prefix + * @np: The node name to test + * @prefix: The prefix to see if @np starts with + * + * Returns: + * * strlen(@prefix) if @np starts with @prefix + * * 0 if @np does not start with @prefix + */ +size_t of_node_has_prefix(const struct device_node *np, const char *prefix) +{ + return np ? str_has_prefix(kbasename(np->full_name), prefix) : 0; +} +EXPORT_SYMBOL(of_node_has_prefix); + bool of_node_name_eq(const struct device_node *np, const char *name) { const char *node_name; @@ -32,7 +48,7 @@ bool of_node_name_eq(const struct device_node *np, const char *name) return false; node_name = kbasename(np->full_name); - len = strchrnul(node_name, '@') - node_name; + len = strchrnul(node_name, '@') - node_name; return (strlen(name) == len) && (strncmp(node_name, name, len) == 0); } @@ -153,8 +169,8 @@ static void of_alias_add(struct alias_prop *ap, struct device_node *np, strncpy(ap->stem, stem, stem_len); ap->stem[stem_len] = 0; list_add_tail(&ap->link, &aliases_lookup); - pr_debug("adding DT alias:%s: stem=%s id=%i node=%s\n", - ap->alias, ap->stem, ap->id, np->full_name); + pr_debug("adding DT alias:%s: stem=%s id=%i node=%pOF\n", + ap->alias, ap->stem, ap->id, np); } static struct device_node *of_alias_resolve(struct device_node *root, struct property *pp) @@ -530,7 +546,9 @@ 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 + * the device's "compatible" property. Returns 0 on mismatch and a + * positive score on match with the maximum being OF_DEVICE_COMPATIBLE_MAX_SCORE, + * which is only returned if the first compatible matched. */ int of_device_is_compatible(const struct device_node *device, const char *compat) @@ -543,7 +561,7 @@ int of_device_is_compatible(const struct device_node *device, 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); + score = OF_DEVICE_COMPATIBLE_MAX_SCORE - (index << 2); break; } } @@ -699,6 +717,9 @@ const struct of_device_id *of_match_node(const struct of_device_id *matches, if (score > best_score) { best_match = matches; best_score = score; + + if (score == OF_DEVICE_COMPATIBLE_MAX_SCORE) + break; } } @@ -739,11 +760,11 @@ struct device_node *of_find_matching_node_and_match(struct device_node *from, } EXPORT_SYMBOL(of_find_matching_node_and_match); -int of_match(struct device_d *dev, struct driver_d *drv) +int of_match(struct device *dev, struct driver *drv) { const struct of_device_id *id; - id = of_match_node(drv->of_compatible, dev->device_node); + id = of_match_node(drv->of_compatible, dev->of_node); if (!id) return 1; @@ -832,7 +853,7 @@ int of_property_count_elems_of_size(const struct device_node *np, if (!prop) return -EINVAL; - if (!prop->value) + if (!of_property_get_value(prop)) return -ENODATA; if (prop->length % elem_size != 0) { @@ -1049,7 +1070,7 @@ EXPORT_SYMBOL_GPL(of_property_read_string); * This function searches a string list property and returns the index * of a specific string value. */ -int of_property_match_string(struct device_node *np, const char *propname, +int of_property_match_string(const struct device_node *np, const char *propname, const char *string) { struct property *prop = of_find_property(np, propname, NULL); @@ -1130,6 +1151,7 @@ EXPORT_SYMBOL_GPL(of_prop_next_string); * * @np: device node from which the property is to be set. * @propname: name of the property to be set. + * @value true to set, false to delete * * Search for a property in a device node and create or delete the property. * If the property already exists and write value is false, the property is @@ -1472,15 +1494,13 @@ static int __of_parse_phandle_with_args(const struct device_node *np, */ node = of_find_node_by_phandle(phandle); if (!node) { - pr_err("%s: could not find phandle\n", - np->full_name); + pr_err("%pOF: could not find phandle\n", np); goto err; } if (cells_name && of_property_read_u32(node, cells_name, &count)) { - pr_err("%s: could not get %s for %s\n", - np->full_name, cells_name, - node->full_name); + pr_err("%pOF: could not get %s for %pOF\n", + np, cells_name, node); goto err; } @@ -1489,8 +1509,7 @@ static int __of_parse_phandle_with_args(const struct device_node *np, * remaining property data length */ if (list + count > list_end) { - pr_err("%s: arguments longer than property\n", - np->full_name); + pr_err("%pOF: arguments longer than property\n", np); goto err; } } @@ -1756,7 +1775,7 @@ int barebox_register_of(struct device_node *root) of_fix_tree(root); if (IS_ENABLED(CONFIG_OFDEVICE)) { - of_clk_init(root, NULL); + of_clk_init(); if (!deep_probe_is_supported()) return of_probe(); } @@ -1990,9 +2009,9 @@ int of_property_read_string_helper(const struct device_node *np, if (!prop) return -EINVAL; - if (!prop->value) + p = of_property_get_value(prop); + if (!p) return -ENODATA; - p = prop->value; end = p + prop->length; for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) { @@ -2209,8 +2228,8 @@ struct device_node *of_new_node(struct device_node *parent, const char *name) if (parent) { node->name = xstrdup(name); - node->full_name = basprintf("%s/%s", - node->parent->full_name, name); + node->full_name = basprintf("%pOF/%s", + node->parent, name); list_add(&node->list, &parent->list); } else { node->name = xstrdup(""); @@ -2301,6 +2320,41 @@ void of_delete_property(struct property *pp) free(pp); } +struct property *of_rename_property(struct device_node *np, + const char *old_name, const char *new_name) +{ + struct property *pp; + + pp = of_find_property(np, old_name, NULL); + if (!pp) + return NULL; + + of_property_write_bool(np, new_name, false); + + free(pp->name); + pp->name = xstrdup(new_name); + return pp; +} + +struct property *of_copy_property(const struct device_node *src, + const char *propname, + struct device_node *dst) +{ + struct property *prop; + + prop = of_find_property(src, propname, NULL); + if (!prop) + return NULL; + + if (of_property_present(dst, propname)) + return ERR_PTR(-EEXIST); + + return of_new_property(dst, propname, + of_property_get_value(prop), prop->length); +} +EXPORT_SYMBOL_GPL(of_copy_property); + + /** * of_set_property - create a property for a given node * @node - the node @@ -2362,6 +2416,36 @@ int of_append_property(struct device_node *np, const char *name, const void *val return 0; } +int of_prepend_property(struct device_node *np, const char *name, const void *val, int len) +{ + struct property *pp; + const void *oldval; + void *buf; + int oldlen; + + pp = of_find_property(np, name, &oldlen); + if (!pp) { + of_new_property(np, name, val, len); + return 0; + } + + oldval = of_property_get_value(pp); + + buf = malloc(len + oldlen); + if (!buf) + return -ENOMEM; + + memcpy(buf, val, len); + memcpy(buf + len, oldval, oldlen); + + free(pp->value); + pp->value = buf; + pp->length = len + oldlen; + pp->value_const = NULL; + + return 0; +} + int of_property_sprintf(struct device_node *np, const char *propname, const char *fmt, ...) { @@ -2440,6 +2524,7 @@ const struct of_device_id of_default_bus_match_table[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, of_default_bus_match_table); static int of_probe_memory(void) { @@ -2468,7 +2553,7 @@ mem_initcall(of_probe_memory); static void of_platform_device_create_root(struct device_node *np) { - static struct device_d *dev; + static struct device *dev; int ret; if (dev) @@ -2476,7 +2561,7 @@ static void of_platform_device_create_root(struct device_node *np) dev = xzalloc(sizeof(*dev)); dev->id = DEVICE_ID_SINGLE; - dev->device_node = np; + dev->of_node = np; dev_set_name(dev, "machine"); ret = platform_device_register(dev); @@ -2488,6 +2573,7 @@ static const struct of_device_id reserved_mem_matches[] = { { .compatible = "nvmem-rmem" }, {} }; +MODULE_DEVICE_TABLE(of, reserved_mem_matches); /** * of_probe - Probe unflattened device tree starting at of_get_root_node @@ -2504,6 +2590,12 @@ int of_probe(void) return -ENODEV; /* + * We do this first thing, so board drivers can patch the device + * tree prior to device creation if needed. + */ + of_platform_device_create_root(root_node); + + /* * Handle certain compatibles explicitly, since we don't want to create * platform_devices for every node in /reserved-memory with a * "compatible", @@ -2515,8 +2607,6 @@ int of_probe(void) if (node) of_platform_populate(node, NULL, NULL); - of_platform_device_create_root(root_node); - of_platform_populate(root_node, of_default_bus_match_table, NULL); return 0; @@ -2566,25 +2656,35 @@ out: return dn; } -struct device_node *of_copy_node(struct device_node *parent, const struct device_node *other) +void of_merge_nodes(struct device_node *np, const struct device_node *other) { - struct device_node *np, *child; + struct device_node *child; struct property *pp; - np = of_new_node(parent, other->name); - np->phandle = other->phandle; - list_for_each_entry(pp, &other->properties, list) of_new_property(np, pp->name, pp->value, pp->length); for_each_child_of_node(other, child) of_copy_node(np, child); +} + +struct device_node *of_copy_node(struct device_node *parent, const struct device_node *other) +{ + struct device_node *np; + + np = of_new_node(parent, other->name); + np->phandle = other->phandle; + + of_merge_nodes(np, other); return np; } struct device_node *of_dup(const struct device_node *root) { + if (IS_ERR_OR_NULL(root)) + return ERR_CAST(root); + return of_copy_node(NULL, root); } @@ -2617,30 +2717,51 @@ void of_delete_node(struct device_node *node) free(node); } -struct device_node *of_get_stdoutpath(unsigned int *baudrate) +/* + * of_find_node_by_chosen - Find a node given a chosen property pointing at it + * @propname: the name of the property containing a path or alias + * The function will lookup the first string in the property + * value up to the first : character or till \0. + * @options The Remainder (without : or \0 at the end) will be written + * to *options if not NULL. + */ +struct device_node *of_find_node_by_chosen(const char *propname, + const char **options) { + const char *value, *p; + char *buf; struct device_node *dn; - const char *name; - const char *p; - char *q; - name = of_get_property(of_chosen, "stdout-path", NULL); - if (!name) - name = of_get_property(of_chosen, "linux,stdout-path", NULL); + value = of_get_property(of_chosen, propname, NULL); + if (!value) + return NULL; - if (!name) - return 0; + p = strchrnul(value, ':'); + buf = xstrndup(value, p - value); - p = strchrnul(name, ':'); + dn = of_find_node_by_path_or_alias(NULL, buf); - q = xstrndup(name, p - name); + free(buf); - dn = of_find_node_by_path_or_alias(NULL, q); + if (options && *p) + *options = p + 1; - free(q); + return dn; +} - if (baudrate && *p) { - unsigned rate = simple_strtoul(p + 1, NULL, 10); +struct device_node *of_get_stdoutpath(unsigned int *baudrate) +{ + const char *opts = NULL; + struct device_node *dn; + + dn = of_find_node_by_chosen("stdout-path", &opts); + if (!dn) + dn = of_find_node_by_chosen("linux,stdout-path", &opts); + if (!dn) + return NULL; + + if (baudrate && opts) { + unsigned rate = simple_strtoul(opts, NULL, 10); if (rate) *baudrate = rate; } @@ -2648,11 +2769,11 @@ struct device_node *of_get_stdoutpath(unsigned int *baudrate) return dn; } -int of_device_is_stdout_path(struct device_d *dev, unsigned int *baudrate) +int of_device_is_stdout_path(struct device *dev, unsigned int *baudrate) { unsigned int tmp = *baudrate; - if (!dev || !dev->device_node || dev->device_node != of_get_stdoutpath(&tmp)) + if (!dev || !dev->of_node || dev->of_node != of_get_stdoutpath(&tmp)) return false; *baudrate = tmp; @@ -2740,6 +2861,21 @@ int of_device_enable_path(const char *path) } /** + * of_device_enable_by_alias - enable a device node by alias + * @alias - the alias of the device tree node to enable + */ +int of_device_enable_by_alias(const char *alias) +{ + struct device_node *node; + + node = of_find_node_by_alias(NULL, alias); + if (!node) + return -ENODEV; + + return of_device_enable(node); +} + +/** * of_device_disable - disable a devicenode device * @node - the node to disable * @@ -2783,6 +2919,37 @@ int of_device_disable_by_alias(const char *alias) } /** + * of_read_file - unflatten oftree file + * @filename - path to file to unflatten its contents + * + * Returns the root node of the tree or an error pointer on error. + */ +struct device_node *of_read_file(const char *filename) +{ + void *fdt; + size_t size; + struct device_node *root; + + fdt = read_file(filename, &size); + if (!fdt) { + pr_err("unable to read %s: %m\n", filename); + return ERR_PTR(-errno); + } + + if (IS_ENABLED(CONFIG_FILETYPE) && file_detect_type(fdt, size) != filetype_oftree) { + pr_err("%s is not a flat device tree file.\n", filename); + root = ERR_PTR(-EINVAL); + goto out; + } + + root = of_unflatten_dtb(fdt, size); +out: + free(fdt); + + return root; +} + +/** * of_get_reproducible_name() - get a reproducible name of a node * @node: The node to get a name from * @@ -2865,6 +3032,19 @@ struct device_node *of_find_node_by_reproducible_name(struct device_node *from, return NULL; } +struct device_node *of_get_node_by_reproducible_name(struct device_node *dstroot, + struct device_node *srcnp) +{ + struct device_node *dstnp; + char *name; + + name = of_get_reproducible_name(srcnp); + dstnp = of_find_node_by_reproducible_name(dstroot, name); + free(name); + + return dstnp; +} + /** * of_graph_parse_endpoint() - parse common endpoint node properties * @node: pointer to endpoint device_node @@ -2876,8 +3056,8 @@ int of_graph_parse_endpoint(const struct device_node *node, struct device_node *port_node = of_get_parent(node); if (!port_node) - pr_warn("%s(): endpoint %s has no parent node\n", - __func__, node->full_name); + pr_warn("%s(): endpoint %pOF has no parent node\n", + __func__, node); memset(endpoint, 0, sizeof(*endpoint)); @@ -2949,15 +3129,15 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, port = of_get_child_by_name(parent, "port"); if (!port) { - pr_err("%s(): no port node found in %s\n", - __func__, parent->full_name); + pr_err("%s(): no port node found in %pOF\n", + __func__, parent); return NULL; } } else { port = of_get_parent(prev); if (!port) { - pr_warn("%s(): endpoint %s has no parent node\n", - __func__, prev->full_name); + pr_warn("%s(): endpoint %pOF has no parent node\n", + __func__, prev); return NULL; } } |