diff options
Diffstat (limited to 'drivers/of/base.c')
-rw-r--r-- | drivers/of/base.c | 273 |
1 files changed, 180 insertions, 93 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c index b0ad8fc06e80..af6c68bbb427 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -16,6 +16,7 @@ #define pr_fmt(fmt) "OF: " fmt +#include <linux/cleanup.h> #include <linux/console.h> #include <linux/ctype.h> #include <linux/cpu.h> @@ -86,15 +87,26 @@ static bool __of_node_is_type(const struct device_node *np, const char *type) return np && match && type && !strcmp(match, type); } +#define EXCLUDED_DEFAULT_CELLS_PLATFORMS ( \ + IS_ENABLED(CONFIG_SPARC) || \ + of_find_compatible_node(NULL, NULL, "coreboot") \ +) + int of_bus_n_addr_cells(struct device_node *np) { u32 cells; - for (; np; np = np->parent) + for (; np; np = np->parent) { if (!of_property_read_u32(np, "#address-cells", &cells)) return cells; - - /* No #address-cells property for the root node */ + /* + * Default root value and walking parent nodes for "#address-cells" + * is deprecated. Any platforms which hit this warning should + * be added to the excluded list. + */ + WARN_ONCE(!EXCLUDED_DEFAULT_CELLS_PLATFORMS, + "Missing '#address-cells' in %pOF\n", np); + } return OF_ROOT_NODE_ADDR_CELLS_DEFAULT; } @@ -111,11 +123,17 @@ int of_bus_n_size_cells(struct device_node *np) { u32 cells; - for (; np; np = np->parent) + for (; np; np = np->parent) { if (!of_property_read_u32(np, "#size-cells", &cells)) return cells; - - /* No #size-cells property for the root node */ + /* + * Default root value and walking parent nodes for "#size-cells" + * is deprecated. Any platforms which hit this warning should + * be added to the excluded list. + */ + WARN_ONCE(!EXCLUDED_DEFAULT_CELLS_PLATFORMS, + "Missing '#size-cells' in %pOF\n", np); + } return OF_ROOT_NODE_SIZE_CELLS_DEFAULT; } @@ -269,7 +287,7 @@ EXPORT_SYMBOL(of_find_all_nodes); const void *__of_get_property(const struct device_node *np, const char *name, int *lenp) { - struct property *pp = __of_find_property(np, name, lenp); + const struct property *pp = __of_find_property(np, name, lenp); return pp ? pp->value : NULL; } @@ -281,7 +299,7 @@ const void *__of_get_property(const struct device_node *np, const void *of_get_property(const struct device_node *np, const char *name, int *lenp) { - struct property *pp = of_find_property(np, name, lenp); + const struct property *pp = of_find_property(np, name, lenp); return pp ? pp->value : NULL; } @@ -320,7 +338,7 @@ EXPORT_SYMBOL(of_get_property); static int __of_device_is_compatible(const struct device_node *device, const char *compat, const char *type, const char *name) { - struct property *prop; + const struct property *prop; const char *cp; int index = 0, score = 0; @@ -395,25 +413,57 @@ int of_device_compatible_match(const struct device_node *device, EXPORT_SYMBOL_GPL(of_device_compatible_match); /** - * of_machine_is_compatible - Test root of device tree for a given compatible value - * @compat: compatible string to look for in root node's compatible property. + * of_machine_compatible_match - Test root of device tree against a compatible array + * @compats: NULL terminated array of compatible strings to look for in root node's compatible property. * - * Return: A positive integer if the root node has the given value in its + * Returns true if the root node has any of the given compatible values in its * compatible property. */ -int of_machine_is_compatible(const char *compat) +bool of_machine_compatible_match(const char *const *compats) { struct device_node *root; int rc = 0; root = of_find_node_by_path("/"); if (root) { - rc = of_device_is_compatible(root, compat); + rc = of_device_compatible_match(root, compats); of_node_put(root); } - return rc; + + return rc != 0; +} +EXPORT_SYMBOL(of_machine_compatible_match); + +static bool __of_device_is_status(const struct device_node *device, + const char * const*strings) +{ + const char *status; + int statlen; + + if (!device) + return false; + + status = __of_get_property(device, "status", &statlen); + if (status == NULL) + return false; + + if (statlen > 0) { + while (*strings) { + unsigned int len = strlen(*strings); + + if ((*strings)[len - 1] == '-') { + if (!strncmp(status, *strings, len)) + return true; + } else { + if (!strcmp(status, *strings)) + return true; + } + strings++; + } + } + + return false; } -EXPORT_SYMBOL(of_machine_is_compatible); /** * __of_device_is_available - check if a device is available for use @@ -425,22 +475,27 @@ EXPORT_SYMBOL(of_machine_is_compatible); */ static bool __of_device_is_available(const struct device_node *device) { - const char *status; - int statlen; + static const char * const ok[] = {"okay", "ok", NULL}; if (!device) return false; - status = __of_get_property(device, "status", &statlen); - if (status == NULL) - return true; + return !__of_get_property(device, "status", NULL) || + __of_device_is_status(device, ok); +} - if (statlen > 0) { - if (!strcmp(status, "okay") || !strcmp(status, "ok")) - return true; - } +/** + * __of_device_is_reserved - check if a device is reserved + * + * @device: Node to check for availability, with locks already held + * + * Return: True if the status property is set to "reserved", false otherwise + */ +static bool __of_device_is_reserved(const struct device_node *device) +{ + static const char * const reserved[] = {"reserved", NULL}; - return false; + return __of_device_is_status(device, reserved); } /** @@ -474,16 +529,9 @@ EXPORT_SYMBOL(of_device_is_available); */ static bool __of_device_is_fail(const struct device_node *device) { - const char *status; - - if (!device) - return false; + static const char * const fail[] = {"fail", "fail-", NULL}; - status = __of_get_property(device, "status", NULL); - if (status == NULL) - return false; - - return !strcmp(status, "fail") || !strncmp(status, "fail-", 5); + return __of_device_is_status(device, fail); } /** @@ -598,15 +646,44 @@ struct device_node *of_get_next_child(const struct device_node *node, EXPORT_SYMBOL(of_get_next_child); /** - * of_get_next_available_child - Find the next available child node + * of_get_next_child_with_prefix - Find the next child node with prefix * @node: parent node * @prev: previous child of the parent node, or NULL to get first + * @prefix: prefix that the node name should have * - * This function is like of_get_next_child(), except that it - * automatically skips any disabled nodes (i.e. status = "disabled"). + * This function is like of_get_next_child(), except that it automatically + * skips any nodes whose name doesn't have the given prefix. + * + * Return: A node pointer with refcount incremented, use + * of_node_put() on it when done. */ -struct device_node *of_get_next_available_child(const struct device_node *node, - struct device_node *prev) +struct device_node *of_get_next_child_with_prefix(const struct device_node *node, + struct device_node *prev, + const char *prefix) +{ + struct device_node *next; + unsigned long flags; + + if (!node) + return NULL; + + raw_spin_lock_irqsave(&devtree_lock, flags); + next = prev ? prev->sibling : node->child; + for (; next; next = next->sibling) { + if (!of_node_name_prefix(next, prefix)) + continue; + if (of_node_get(next)) + break; + } + of_node_put(prev); + raw_spin_unlock_irqrestore(&devtree_lock, flags); + return next; +} +EXPORT_SYMBOL(of_get_next_child_with_prefix); + +static struct device_node *of_get_next_status_child(const struct device_node *node, + struct device_node *prev, + bool (*checker)(const struct device_node *)) { struct device_node *next; unsigned long flags; @@ -617,7 +694,7 @@ struct device_node *of_get_next_available_child(const struct device_node *node, raw_spin_lock_irqsave(&devtree_lock, flags); next = prev ? prev->sibling : node->child; for (; next; next = next->sibling) { - if (!__of_device_is_available(next)) + if (!checker(next)) continue; if (of_node_get(next)) break; @@ -626,9 +703,38 @@ struct device_node *of_get_next_available_child(const struct device_node *node, raw_spin_unlock_irqrestore(&devtree_lock, flags); return next; } + +/** + * of_get_next_available_child - Find the next available child node + * @node: parent node + * @prev: previous child of the parent node, or NULL to get first + * + * This function is like of_get_next_child(), except that it + * automatically skips any disabled nodes (i.e. status = "disabled"). + */ +struct device_node *of_get_next_available_child(const struct device_node *node, + struct device_node *prev) +{ + return of_get_next_status_child(node, prev, __of_device_is_available); +} EXPORT_SYMBOL(of_get_next_available_child); /** + * of_get_next_reserved_child - Find the next reserved child node + * @node: parent node + * @prev: previous child of the parent node, or NULL to get first + * + * This function is like of_get_next_child(), except that it + * automatically skips any disabled nodes (i.e. status = "disabled"). + */ +struct device_node *of_get_next_reserved_child(const struct device_node *node, + struct device_node *prev) +{ + return of_get_next_status_child(node, prev, __of_device_is_reserved); +} +EXPORT_SYMBOL(of_get_next_reserved_child); + +/** * of_get_next_cpu_node - Iterate on cpu nodes * @prev: previous child of the /cpus node, or NULL to get first * @@ -718,7 +824,7 @@ struct device_node *of_get_child_by_name(const struct device_node *node, } EXPORT_SYMBOL(of_get_child_by_name); -struct device_node *__of_find_node_by_path(struct device_node *parent, +struct device_node *__of_find_node_by_path(const struct device_node *parent, const char *path) { struct device_node *child; @@ -775,7 +881,7 @@ struct device_node *__of_find_node_by_full_path(struct device_node *node, struct device_node *of_find_node_opts_by_path(const char *path, const char **opts) { struct device_node *np = NULL; - struct property *pp; + const struct property *pp; unsigned long flags; const char *separator = strchr(path, ':'); @@ -788,10 +894,10 @@ struct device_node *of_find_node_opts_by_path(const char *path, const char **opt /* The path could begin with an alias */ if (*path != '/') { int len; - const char *p = separator; + const char *p = strchrnul(path, '/'); - if (!p) - p = strchrnul(path, '/'); + if (separator && separator < p) + p = separator; len = p - path; /* of_aliases must not be NULL */ @@ -921,19 +1027,15 @@ struct device_node *of_find_node_with_property(struct device_node *from, const char *prop_name) { struct device_node *np; - struct property *pp; unsigned long flags; raw_spin_lock_irqsave(&devtree_lock, flags); for_each_of_allnodes_from(from, np) { - for (pp = np->properties; pp; pp = pp->next) { - if (of_prop_cmp(pp->name, prop_name) == 0) { - of_node_get(np); - goto out; - } + if (__of_find_property(np, prop_name, NULL)) { + of_node_get(np); + break; } } -out: of_node_put(from); raw_spin_unlock_irqrestore(&devtree_lock, flags); return np; @@ -1341,12 +1443,14 @@ int of_parse_phandle_with_args_map(const struct device_node *np, const char *stem_name, int index, struct of_phandle_args *out_args) { - char *cells_name, *map_name = NULL, *mask_name = NULL; - char *pass_name = NULL; + char *cells_name __free(kfree) = kasprintf(GFP_KERNEL, "#%s-cells", stem_name); + char *map_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map", stem_name); + char *mask_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map-mask", stem_name); + char *pass_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map-pass-thru", stem_name); struct device_node *cur, *new = NULL; const __be32 *map, *mask, *pass; - static const __be32 dummy_mask[] = { [0 ... MAX_PHANDLE_ARGS] = ~0 }; - static const __be32 dummy_pass[] = { [0 ... MAX_PHANDLE_ARGS] = 0 }; + static const __be32 dummy_mask[] = { [0 ... (MAX_PHANDLE_ARGS - 1)] = cpu_to_be32(~0) }; + static const __be32 dummy_pass[] = { [0 ... (MAX_PHANDLE_ARGS - 1)] = cpu_to_be32(0) }; __be32 initial_match_array[MAX_PHANDLE_ARGS]; const __be32 *match_array = initial_match_array; int i, ret, map_len, match; @@ -1355,27 +1459,13 @@ int of_parse_phandle_with_args_map(const struct device_node *np, if (index < 0) return -EINVAL; - cells_name = kasprintf(GFP_KERNEL, "#%s-cells", stem_name); - if (!cells_name) + if (!cells_name || !map_name || !mask_name || !pass_name) return -ENOMEM; - ret = -ENOMEM; - map_name = kasprintf(GFP_KERNEL, "%s-map", stem_name); - if (!map_name) - goto free; - - mask_name = kasprintf(GFP_KERNEL, "%s-map-mask", stem_name); - if (!mask_name) - goto free; - - pass_name = kasprintf(GFP_KERNEL, "%s-map-pass-thru", stem_name); - if (!pass_name) - goto free; - ret = __of_parse_phandle_with_args(np, list_name, cells_name, -1, index, out_args); if (ret) - goto free; + return ret; /* Get the #<list>-cells property */ cur = out_args->np; @@ -1392,8 +1482,7 @@ int of_parse_phandle_with_args_map(const struct device_node *np, /* Get the <list>-map property */ map = of_get_property(cur, map_name, &map_len); if (!map) { - ret = 0; - goto free; + return 0; } map_len /= sizeof(u32); @@ -1415,8 +1504,10 @@ int of_parse_phandle_with_args_map(const struct device_node *np, map_len--; /* Check if not found */ - if (!new) + if (!new) { + ret = -EINVAL; goto put; + } if (!of_device_is_available(new)) match = 0; @@ -1426,17 +1517,20 @@ int of_parse_phandle_with_args_map(const struct device_node *np, goto put; /* Check for malformed properties */ - if (WARN_ON(new_size > MAX_PHANDLE_ARGS)) - goto put; - if (map_len < new_size) + if (WARN_ON(new_size > MAX_PHANDLE_ARGS) || + map_len < new_size) { + ret = -EINVAL; goto put; + } /* Move forward by new node's #<list>-cells amount */ map += new_size; map_len -= new_size; } - if (!match) + if (!match) { + ret = -ENOENT; goto put; + } /* Get the <list>-map-pass-thru property (optional) */ pass = of_get_property(cur, pass_name, NULL); @@ -1448,7 +1542,6 @@ int of_parse_phandle_with_args_map(const struct device_node *np, * specifier into the out_args structure, keeping the * bits specified in <list>-map-pass-thru. */ - match_array = map - new_size; for (i = 0; i < new_size; i++) { __be32 val = *(map - new_size + i); @@ -1457,6 +1550,7 @@ int of_parse_phandle_with_args_map(const struct device_node *np, val |= cpu_to_be32(out_args->args[i]) & pass[i]; } + initial_match_array[i] = val; out_args->args[i] = be32_to_cpu(val); } out_args->args_count = list_size = new_size; @@ -1469,12 +1563,6 @@ int of_parse_phandle_with_args_map(const struct device_node *np, put: of_node_put(cur); of_node_put(new); -free: - kfree(mask_name); - kfree(map_name); - kfree(cells_name); - kfree(pass_name); - return ret; } EXPORT_SYMBOL(of_parse_phandle_with_args_map); @@ -1730,12 +1818,11 @@ static void of_alias_add(struct alias_prop *ap, struct device_node *np, * for storing the resulting tree * * The function scans all the properties of the 'aliases' node and populates - * the global lookup table with the properties. It returns the - * number of alias properties found, or an error code in case of failure. + * the global lookup table with the properties. */ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) { - struct property *pp; + const struct property *pp; of_aliases = of_find_node_by_path("/aliases"); of_chosen = of_find_node_by_path("/chosen"); @@ -1806,7 +1893,7 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) * * Return: The alias id if found. */ -int of_alias_get_id(struct device_node *np, const char *stem) +int of_alias_get_id(const struct device_node *np, const char *stem) { struct alias_prop *app; int id = -ENODEV; @@ -1864,7 +1951,7 @@ EXPORT_SYMBOL_GPL(of_alias_get_highest_id); * * Return: TRUE if console successfully setup. Otherwise return FALSE. */ -bool of_console_check(struct device_node *dn, char *name, int index) +bool of_console_check(const struct device_node *dn, char *name, int index) { if (!dn || dn != of_stdout || console_set_on_cmdline) return false; @@ -1952,7 +2039,7 @@ int of_find_last_cache_level(unsigned int cpu) * * Return: 0 on success or a standard error code on failure. */ -int of_map_id(struct device_node *np, u32 id, +int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, struct device_node **target, u32 *id_out) { |