This patch adds a function for finding a node in flat tree based on an alias name. It can be used in early boot code.
Typical use case is a situation when early code needs data from a arbitrary tree node, eg. base address of "serial0" to initialise debug output or some sort of ID register to probe hardware. The tree source could look like that: / { aliases { serial0 = &uart0; }; uart0: uart@f0001000 { reg = <0xf0001000 0x1000>; }; } Signed-off-by: Pawel Moll <pawel.m...@arm.com> --- drivers/of/fdt.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of_fdt.h | 3 + 2 files changed, 120 insertions(+), 0 deletions(-) diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 65200af..0968809 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -502,6 +502,123 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node, } /** + * Routines used by of_flat_dt_find_node_by_alias() + */ + +struct of_flat_dt_find_node_by_alias_state { + const char *alias; + unsigned long node; + const char *path; + int depth; + int token_len; + +}; + +static int __init of_flat_dt_scan_path_legacy(unsigned long node, + const char *uname, int depth, void *data) +{ + struct of_flat_dt_find_node_by_alias_state *state = data; + + pr_debug("%s: uname='%s', state->path='%s'\n", + __func__, uname, state->path); + + return strcmp(state->path, uname) == 0; +} + +static int __init of_flat_dt_scan_path(unsigned long node, + const char *uname, int depth, void *data) +{ + struct of_flat_dt_find_node_by_alias_state *state = data; + + pr_debug("%s: uname='%s', depth=%d, state->path='%s', state->depth=%d," + " state->token_len=%d\n", __func__, uname, depth, + state->path, state->depth, state->token_len); + + /* If the depth decreases, we didn't find the path */ + if (depth < state->depth) + return -ENODEV; + + /* Check if path ~= /^uname[\/\0]/ */ + if (strncmp(state->path, uname, state->token_len) == 0 && + uname[state->token_len] == 0) { + const char *slash; + + state->depth++; + state->path += state->token_len; /* Next token */ + if (*state->path == 0) { /* All path tokens processed? */ + state->node = node; + return 1; /* Success! */ + } + BUG_ON(*state->path != '/'); + state->path++; /* Skip leading slash */ + slash = strchr(state->path, '/'); + if (!slash) + state->token_len = strlen(state->path); + else + state->token_len = slash - state->path; + } + + return 0; +} + +static int __init of_flat_dt_scan_aliases(unsigned long node, + const char *uname, int depth, void *data) +{ + int err; + struct of_flat_dt_find_node_by_alias_state *state = data; + + if (depth != 1 || strcmp(uname, "aliases") != 0) + return 0; + + state->path = of_get_flat_dt_prop(node, state->alias, NULL); + if (!state->path) + return -ENXIO; + if (*state->path != '/') + return -EFAULT; + + state->token_len = 0; /* Root node has no name */ + + /* FDTs prior to version 16 (0x10) had full path in every node */ + if (be32_to_cpu(initial_boot_params->version) < 0x10) + err = of_scan_flat_dt(of_flat_dt_scan_path_legacy, state); + else + err = of_scan_flat_dt(of_flat_dt_scan_path, state); + + if (err == 0) /* Whole tree scanned, no path found */ + err = -ENODEV; + + return err; +} + +/** + * of_flat_dt_find_node_by_alias - find a root node referenced by an alias + * + * @alias: alias name + * @node: pointer to a node reference to be filled on success + * + * returns 0 on success, negative value on error. + * + * This function can be used at boot time, before the tree is unflattened. + */ +int __init of_flat_dt_find_node_by_alias(const char *alias, unsigned long *node) +{ + int err; + struct of_flat_dt_find_node_by_alias_state state = { + .alias = alias, + }; + + err = of_scan_flat_dt(of_flat_dt_scan_aliases, &state); + if (err == 0) /* No 'aliases' node */ + err = -ENOENT; + if (err > 0) { + /* Node found */ + *node = state.node; + err = 0; + } + return err; +} + +/** * of_get_flat_dt_root - find the root node in the flat blob */ unsigned long __init of_get_flat_dt_root(void) diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index c84d900..8212e47 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -85,6 +85,9 @@ extern char *find_flat_dt_string(u32 offset); extern int of_scan_flat_dt(int (*it)(unsigned long node, const char *uname, int depth, void *data), void *data); +extern int of_flat_dt_find_node_by_alias(const char *alias, + unsigned long *node); + extern void *of_get_flat_dt_prop(unsigned long node, const char *name, unsigned long *size); extern int of_flat_dt_is_compatible(unsigned long node, const char *name); -- 1.6.3.3 _______________________________________________ devicetree-discuss mailing list devicetree-discuss@lists.ozlabs.org https://lists.ozlabs.org/listinfo/devicetree-discuss