From 0ee0496de97c6a511ce915f9b44f5b3db15350b7 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 27 Mar 2014 07:24:57 -0500 Subject: of/fdt: remove some unneeded includes Whatever needed powerpc machdep.h appears to have been removed, so the include can be dropped. module.h is not needed as this code is always built-in. Signed-off-by: Rob Herring Cc: Grant Likely Tested-by: Michal Simek Tested-by: Grant Likely Tested-by: Stephen Chivers --- drivers/of/fdt.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 7a2ef7bb8022..63bdcee473fa 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -22,10 +21,6 @@ #include #include /* for COMMAND_LINE_SIZE */ -#ifdef CONFIG_PPC -#include -#endif /* CONFIG_PPC */ - #include char *of_fdt_get_string(struct boot_param_header *blob, u32 offset) -- cgit From bba04d965d06abbbe10afd3687742389107e198e Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sat, 29 Mar 2014 14:14:17 -0500 Subject: of/fdt: remove unused of_scan_flat_dt_by_path of_scan_flat_dt_by_path is unused anywhere in the kernel, so remove it. Signed-off-by: Rob Herring Tested-by: Michal Simek Tested-by: Grant Likely Tested-by: Stephen Chivers --- drivers/of/fdt.c | 67 -------------------------------------------------------- 1 file changed, 67 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 63bdcee473fa..9c8535291909 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -671,73 +671,6 @@ struct fdt_scan_status { void *data; }; -/** - * fdt_scan_node_by_path - iterator for of_scan_flat_dt_by_path function - */ -static int __init fdt_scan_node_by_path(unsigned long node, const char *uname, - int depth, void *data) -{ - struct fdt_scan_status *st = data; - - /* - * if scan at the requested fdt node has been completed, - * return -ENXIO to abort further scanning - */ - if (depth <= st->depth) - return -ENXIO; - - /* requested fdt node has been found, so call iterator function */ - if (st->found) - return st->iterator(node, uname, depth, st->data); - - /* check if scanning automata is entering next level of fdt nodes */ - if (depth == st->depth + 1 && - strncmp(st->name, uname, st->namelen) == 0 && - uname[st->namelen] == 0) { - st->depth += 1; - if (st->name[st->namelen] == 0) { - st->found = 1; - } else { - const char *next = st->name + st->namelen + 1; - st->name = next; - st->namelen = strcspn(next, "/"); - } - return 0; - } - - /* scan next fdt node */ - return 0; -} - -/** - * of_scan_flat_dt_by_path - scan flattened tree blob and call callback on each - * child of the given path. - * @path: path to start searching for children - * @it: callback function - * @data: context data pointer - * - * This function is used to scan the flattened device-tree starting from the - * node given by path. It is used to extract information (like reserved - * memory), which is required on ealy boot before we can unflatten the tree. - */ -int __init of_scan_flat_dt_by_path(const char *path, - int (*it)(unsigned long node, const char *name, int depth, void *data), - void *data) -{ - struct fdt_scan_status st = {path, 0, -1, 0, it, data}; - int ret = 0; - - if (initial_boot_params) - ret = of_scan_flat_dt(fdt_scan_node_by_path, &st); - - if (!st.found) - return -ENOENT; - else if (ret == -ENXIO) /* scan has been completed */ - return 0; - else - return ret; -} - const char * __init of_flat_dt_get_machine_name(void) { const char *name; -- cgit From 9d0c4dfedd96ee54fc075b16d02f82499c8cc3a6 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 1 Apr 2014 23:49:03 -0500 Subject: of/fdt: update of_get_flat_dt_prop in prep for libfdt Make of_get_flat_dt_prop arguments compatible with libfdt fdt_getprop call in preparation to convert FDT code to use libfdt. Make the return value const and the property length ptr type an int. Signed-off-by: Rob Herring Tested-by: Michal Simek Tested-by: Grant Likely Tested-by: Stephen Chivers --- drivers/of/fdt.c | 39 ++++++++++++++++++++------------------- drivers/of/of_reserved_mem.c | 4 ++-- 2 files changed, 22 insertions(+), 21 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 9c8535291909..1d1582bb81fb 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -35,7 +35,7 @@ char *of_fdt_get_string(struct boot_param_header *blob, u32 offset) */ void *of_fdt_get_property(struct boot_param_header *blob, unsigned long node, const char *name, - unsigned long *size) + int *size) { unsigned long p = node; @@ -85,7 +85,8 @@ int of_fdt_is_compatible(struct boot_param_header *blob, unsigned long node, const char *compat) { const char *cp; - unsigned long cplen, l, score = 0; + int cplen; + unsigned long l, score = 0; cp = of_fdt_get_property(blob, node, "compatible", &cplen); if (cp == NULL) @@ -444,8 +445,8 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, { int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); phys_addr_t base, size; - unsigned long len; - __be32 *prop; + int len; + const __be32 *prop; int nomap, first = 1; prop = of_get_flat_dt_prop(node, "reg", &len); @@ -488,7 +489,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, */ static int __init __reserved_mem_check_root(unsigned long node) { - __be32 *prop; + const __be32 *prop; prop = of_get_flat_dt_prop(node, "#size-cells", NULL); if (!prop || be32_to_cpup(prop) != dt_root_size_cells) @@ -638,8 +639,8 @@ unsigned long __init of_get_flat_dt_root(void) * This function can be used within scan_flattened_dt callback to get * access to properties */ -void *__init of_get_flat_dt_prop(unsigned long node, const char *name, - unsigned long *size) +const void *__init of_get_flat_dt_prop(unsigned long node, const char *name, + int *size) { return of_fdt_get_property(initial_boot_params, node, name, size); } @@ -710,7 +711,7 @@ const void * __init of_flat_dt_match_machine(const void *default_match, } if (!best_data) { const char *prop; - long size; + int size; pr_err("\n unrecognized device tree list:\n[ "); @@ -739,8 +740,8 @@ const void * __init of_flat_dt_match_machine(const void *default_match, static void __init early_init_dt_check_for_initrd(unsigned long node) { u64 start, end; - unsigned long len; - __be32 *prop; + int len; + const __be32 *prop; pr_debug("Looking for initrd properties... "); @@ -773,7 +774,7 @@ static inline void early_init_dt_check_for_initrd(unsigned long node) int __init early_init_dt_scan_root(unsigned long node, const char *uname, int depth, void *data) { - __be32 *prop; + const __be32 *prop; if (depth != 0) return 0; @@ -795,9 +796,9 @@ int __init early_init_dt_scan_root(unsigned long node, const char *uname, return 1; } -u64 __init dt_mem_next_cell(int s, __be32 **cellp) +u64 __init dt_mem_next_cell(int s, const __be32 **cellp) { - __be32 *p = *cellp; + const __be32 *p = *cellp; *cellp = p + s; return of_read_number(p, s); @@ -809,9 +810,9 @@ u64 __init dt_mem_next_cell(int s, __be32 **cellp) int __init early_init_dt_scan_memory(unsigned long node, const char *uname, int depth, void *data) { - char *type = of_get_flat_dt_prop(node, "device_type", NULL); - __be32 *reg, *endp; - unsigned long l; + const char *type = of_get_flat_dt_prop(node, "device_type", NULL); + const __be32 *reg, *endp; + int l; /* We are scanning "memory" nodes only */ if (type == NULL) { @@ -832,7 +833,7 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname, endp = reg + (l / sizeof(__be32)); - pr_debug("memory scan node %s, reg size %ld, data: %x %x %x %x,\n", + pr_debug("memory scan node %s, reg size %d, data: %x %x %x %x,\n", uname, l, reg[0], reg[1], reg[2], reg[3]); while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { @@ -855,8 +856,8 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname, int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, int depth, void *data) { - unsigned long l; - char *p; + int l; + const char *p; pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname); diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index daaaf935911d..e420eb52e5c9 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -95,8 +95,8 @@ static int __init __reserved_mem_alloc_size(unsigned long node, int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); phys_addr_t start = 0, end = 0; phys_addr_t base = 0, align = 0, size; - unsigned long len; - __be32 *prop; + int len; + const __be32 *prop; int nomap; int ret; -- cgit From e6a6928c3ea1d0195ed75a091e345696b916c09b Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 2 Apr 2014 15:10:14 -0500 Subject: of/fdt: Convert FDT functions to use libfdt The kernel FDT functions predate libfdt and are much more limited in functionality. Also, the kernel functions and libfdt functions are not compatible with each other because they have different definitions of node offsets. To avoid this incompatibility and in preparation to add more FDT parsing functions which will need libfdt, let's first convert the existing code to use libfdt. The FDT unflattening, top-level FDT scanning, and property retrieval functions are converted to use libfdt. The scanning code should be re-worked to be more efficient and understandable by using libfdt to find nodes directly by path or compatible strings. Signed-off-by: Rob Herring Tested-by: Michal Simek Tested-by: Grant Likely Tested-by: Stephen Chivers --- drivers/of/Kconfig | 1 + drivers/of/Makefile | 2 + drivers/of/fdt.c | 209 ++++++++++++++-------------------------------------- 3 files changed, 60 insertions(+), 152 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 889005fa4d04..2dcb0541012d 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -20,6 +20,7 @@ config OF_SELFTEST config OF_FLATTREE bool select DTC + select LIBFDT config OF_EARLY_FLATTREE bool diff --git a/drivers/of/Makefile b/drivers/of/Makefile index ed9660adad77..9891232f999e 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -10,3 +10,5 @@ obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o obj-$(CONFIG_OF_MTD) += of_mtd.o obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o + +CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 1d1582bb81fb..8e820a2b106d 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -19,58 +19,11 @@ #include #include #include +#include #include /* for COMMAND_LINE_SIZE */ #include -char *of_fdt_get_string(struct boot_param_header *blob, u32 offset) -{ - return ((char *)blob) + - be32_to_cpu(blob->off_dt_strings) + offset; -} - -/** - * of_fdt_get_property - Given a node in the given flat blob, return - * the property ptr - */ -void *of_fdt_get_property(struct boot_param_header *blob, - unsigned long node, const char *name, - int *size) -{ - unsigned long p = node; - - do { - u32 tag = be32_to_cpup((__be32 *)p); - u32 sz, noff; - const char *nstr; - - p += 4; - if (tag == OF_DT_NOP) - continue; - if (tag != OF_DT_PROP) - return NULL; - - sz = be32_to_cpup((__be32 *)p); - noff = be32_to_cpup((__be32 *)(p + 4)); - p += 8; - if (be32_to_cpu(blob->version) < 0x10) - p = ALIGN(p, sz >= 8 ? 8 : 4); - - nstr = of_fdt_get_string(blob, noff); - if (nstr == NULL) { - pr_warning("Can't find property index name !\n"); - return NULL; - } - if (strcmp(name, nstr) == 0) { - if (size) - *size = sz; - return (void *)p; - } - p += sz; - p = ALIGN(p, 4); - } while (1); -} - /** * of_fdt_is_compatible - Return true if given node from the given blob has * compat in its compatible list @@ -88,7 +41,7 @@ int of_fdt_is_compatible(struct boot_param_header *blob, int cplen; unsigned long l, score = 0; - cp = of_fdt_get_property(blob, node, "compatible", &cplen); + cp = fdt_getprop(blob, node, "compatible", &cplen); if (cp == NULL) return 0; while (cplen > 0) { @@ -147,28 +100,27 @@ static void *unflatten_dt_alloc(void **mem, unsigned long size, */ static void * unflatten_dt_node(struct boot_param_header *blob, void *mem, - void **p, + int *poffset, struct device_node *dad, struct device_node ***allnextpp, unsigned long fpsize) { + const __be32 *p; struct device_node *np; struct property *pp, **prev_pp = NULL; - char *pathp; - u32 tag; + const char *pathp; unsigned int l, allocl; + static int depth = 0; + int old_depth; + int offset; int has_name = 0; int new_format = 0; - tag = be32_to_cpup(*p); - if (tag != OF_DT_BEGIN_NODE) { - pr_err("Weird tag at start of node: %x\n", tag); + pathp = fdt_get_name(blob, *poffset, &l); + if (!pathp) return mem; - } - *p += 4; - pathp = *p; - l = allocl = strlen(pathp) + 1; - *p = PTR_ALIGN(*p + l, 4); + + allocl = l++; /* version 0x10 has a more compact unit name here instead of the full * path. we accumulate the full path size using "fpsize", we'll rebuild @@ -186,7 +138,7 @@ static void * unflatten_dt_node(struct boot_param_header *blob, fpsize = 1; allocl = 2; l = 1; - *pathp = '\0'; + pathp = ""; } else { /* account for '/' and path size minus terminal 0 * already in 'l' @@ -233,32 +185,23 @@ static void * unflatten_dt_node(struct boot_param_header *blob, } } /* process properties */ - while (1) { - u32 sz, noff; - char *pname; - - tag = be32_to_cpup(*p); - if (tag == OF_DT_NOP) { - *p += 4; - continue; - } - if (tag != OF_DT_PROP) + for (offset = fdt_first_property_offset(blob, *poffset); + (offset >= 0); + (offset = fdt_next_property_offset(blob, offset))) { + const char *pname; + u32 sz; + + if (!(p = fdt_getprop_by_offset(blob, offset, &pname, &sz))) { + offset = -FDT_ERR_INTERNAL; break; - *p += 4; - sz = be32_to_cpup(*p); - noff = be32_to_cpup(*p + 4); - *p += 8; - if (be32_to_cpu(blob->version) < 0x10) - *p = PTR_ALIGN(*p, sz >= 8 ? 8 : 4); - - pname = of_fdt_get_string(blob, noff); + } + if (pname == NULL) { pr_info("Can't find property name in list !\n"); break; } if (strcmp(pname, "name") == 0) has_name = 1; - l = strlen(pname) + 1; pp = unflatten_dt_alloc(&mem, sizeof(struct property), __alignof__(struct property)); if (allnextpp) { @@ -270,26 +213,25 @@ static void * unflatten_dt_node(struct boot_param_header *blob, if ((strcmp(pname, "phandle") == 0) || (strcmp(pname, "linux,phandle") == 0)) { if (np->phandle == 0) - np->phandle = be32_to_cpup((__be32*)*p); + np->phandle = be32_to_cpup(p); } /* And we process the "ibm,phandle" property * used in pSeries dynamic device tree * stuff */ if (strcmp(pname, "ibm,phandle") == 0) - np->phandle = be32_to_cpup((__be32 *)*p); - pp->name = pname; + np->phandle = be32_to_cpup(p); + pp->name = (char *)pname; pp->length = sz; - pp->value = *p; + pp->value = (__be32 *)p; *prev_pp = pp; prev_pp = &pp->next; } - *p = PTR_ALIGN((*p) + sz, 4); } /* with version 0x10 we may not have the name property, recreate * it here from the unit name if absent */ if (!has_name) { - char *p1 = pathp, *ps = pathp, *pa = NULL; + const char *p1 = pathp, *ps = pathp, *pa = NULL; int sz; while (*p1) { @@ -326,19 +268,18 @@ static void * unflatten_dt_node(struct boot_param_header *blob, if (!np->type) np->type = ""; } - while (tag == OF_DT_BEGIN_NODE || tag == OF_DT_NOP) { - if (tag == OF_DT_NOP) - *p += 4; - else - mem = unflatten_dt_node(blob, mem, p, np, allnextpp, - fpsize); - tag = be32_to_cpup(*p); - } - if (tag != OF_DT_END_NODE) { - pr_err("Weird tag at end of node: %x\n", tag); - return mem; - } - *p += 4; + + old_depth = depth; + *poffset = fdt_next_node(blob, *poffset, &depth); + if (depth < 0) + depth = 0; + while (*poffset > 0 && depth > old_depth) + mem = unflatten_dt_node(blob, mem, poffset, np, allnextpp, + fpsize); + + if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND) + pr_err("unflatten: error %d processing FDT\n", *poffset); + return mem; } @@ -359,7 +300,8 @@ static void __unflatten_device_tree(struct boot_param_header *blob, void * (*dt_alloc)(u64 size, u64 align)) { unsigned long size; - void *start, *mem; + int start; + void *mem; struct device_node **allnextp = mynodes; pr_debug(" -> unflatten_device_tree()\n"); @@ -380,7 +322,7 @@ static void __unflatten_device_tree(struct boot_param_header *blob, } /* First pass, scan for size */ - start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct); + start = 0; size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0); size = ALIGN(size, 4); @@ -395,10 +337,8 @@ static void __unflatten_device_tree(struct boot_param_header *blob, pr_debug(" unflattening %p...\n", mem); /* Second pass, do actual unflattening */ - start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct); + start = 0; unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0); - if (be32_to_cpup(start) != OF_DT_END) - pr_warning("Weird tag at end of tree: %08x\n", be32_to_cpup(start)); if (be32_to_cpup(mem + size) != 0xdeadbeef) pr_warning("End of tree marker overwritten: %08x\n", be32_to_cpup(mem + size)); @@ -574,47 +514,19 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node, void *data), void *data) { - unsigned long p = ((unsigned long)initial_boot_params) + - be32_to_cpu(initial_boot_params->off_dt_struct); - int rc = 0; - int depth = -1; - - do { - u32 tag = be32_to_cpup((__be32 *)p); - const char *pathp; - - p += 4; - if (tag == OF_DT_END_NODE) { - depth--; - continue; - } - if (tag == OF_DT_NOP) - continue; - if (tag == OF_DT_END) - break; - if (tag == OF_DT_PROP) { - u32 sz = be32_to_cpup((__be32 *)p); - p += 8; - if (be32_to_cpu(initial_boot_params->version) < 0x10) - p = ALIGN(p, sz >= 8 ? 8 : 4); - p += sz; - p = ALIGN(p, 4); - continue; - } - if (tag != OF_DT_BEGIN_NODE) { - pr_err("Invalid tag %x in flat device tree!\n", tag); - return -EINVAL; - } - depth++; - pathp = (char *)p; - p = ALIGN(p + strlen(pathp) + 1, 4); + const void *blob = initial_boot_params; + const char *pathp; + int offset, rc = 0, depth = -1; + + for (offset = fdt_next_node(blob, -1, &depth); + offset >= 0 && depth >= 0 && !rc; + offset = fdt_next_node(blob, offset, &depth)) { + + pathp = fdt_get_name(blob, offset, NULL); if (*pathp == '/') pathp = kbasename(pathp); - rc = it(p, pathp, depth, data); - if (rc != 0) - break; - } while (1); - + rc = it(offset, pathp, depth, data); + } return rc; } @@ -623,14 +535,7 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node, */ unsigned long __init of_get_flat_dt_root(void) { - unsigned long p = ((unsigned long)initial_boot_params) + - be32_to_cpu(initial_boot_params->off_dt_struct); - - while (be32_to_cpup((__be32 *)p) == OF_DT_NOP) - p += 4; - BUG_ON(be32_to_cpup((__be32 *)p) != OF_DT_BEGIN_NODE); - p += 4; - return ALIGN(p + strlen((char *)p) + 1, 4); + return 0; } /** @@ -642,7 +547,7 @@ unsigned long __init of_get_flat_dt_root(void) const void *__init of_get_flat_dt_prop(unsigned long node, const char *name, int *size) { - return of_fdt_get_property(initial_boot_params, node, name, size); + return fdt_getprop(initial_boot_params, node, name, size); } /** -- cgit From c972de14971f1482ab482f0a7abc85679a23326a Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 1 Apr 2014 22:48:01 -0500 Subject: of/fdt: use libfdt accessors for header data With libfdt support, we can take advantage of helper accessors in libfdt for accessing the FDT header data. This makes the code more readable and makes the FDT blob structure more opaque to the kernel. This also prepares for removing struct boot_param_header completely. Signed-off-by: Rob Herring Cc: Max Filippov Tested-by: Michal Simek Tested-by: Grant Likely Tested-by: Stephen Chivers --- drivers/of/fdt.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 8e820a2b106d..0b38a6aa8603 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -34,7 +34,7 @@ * On match, returns a non-zero value with smaller values returned for more * specific compatible values. */ -int of_fdt_is_compatible(struct boot_param_header *blob, +int of_fdt_is_compatible(const void *blob, unsigned long node, const char *compat) { const char *cp; @@ -59,7 +59,7 @@ int of_fdt_is_compatible(struct boot_param_header *blob, /** * of_fdt_match - Return true if node matches a list of compatible values */ -int of_fdt_match(struct boot_param_header *blob, unsigned long node, +int of_fdt_match(const void *blob, unsigned long node, const char *const *compat) { unsigned int tmp, score = 0; @@ -98,7 +98,7 @@ static void *unflatten_dt_alloc(void **mem, unsigned long size, * @allnextpp: pointer to ->allnext from last allocated device_node * @fpsize: Size of the node path up at the current depth. */ -static void * unflatten_dt_node(struct boot_param_header *blob, +static void * unflatten_dt_node(void *blob, void *mem, int *poffset, struct device_node *dad, @@ -295,7 +295,7 @@ static void * unflatten_dt_node(struct boot_param_header *blob, * @dt_alloc: An allocator that provides a virtual address to memory * for the resulting tree */ -static void __unflatten_device_tree(struct boot_param_header *blob, +static void __unflatten_device_tree(void *blob, struct device_node **mynodes, void * (*dt_alloc)(u64 size, u64 align)) { @@ -312,11 +312,11 @@ static void __unflatten_device_tree(struct boot_param_header *blob, } pr_debug("Unflattening device tree:\n"); - pr_debug("magic: %08x\n", be32_to_cpu(blob->magic)); - pr_debug("size: %08x\n", be32_to_cpu(blob->totalsize)); - pr_debug("version: %08x\n", be32_to_cpu(blob->version)); + pr_debug("magic: %08x\n", fdt_magic(blob)); + pr_debug("size: %08x\n", fdt_totalsize(blob)); + pr_debug("version: %08x\n", fdt_version(blob)); - if (be32_to_cpu(blob->magic) != OF_DT_HEADER) { + if (fdt_check_header(blob)) { pr_err("Invalid device tree blob header\n"); return; } @@ -363,9 +363,7 @@ static void *kernel_tree_alloc(u64 size, u64 align) void of_fdt_unflatten_tree(unsigned long *blob, struct device_node **mynodes) { - struct boot_param_header *device_tree = - (struct boot_param_header *)blob; - __unflatten_device_tree(device_tree, mynodes, &kernel_tree_alloc); + __unflatten_device_tree(blob, mynodes, &kernel_tree_alloc); } EXPORT_SYMBOL_GPL(of_fdt_unflatten_tree); @@ -852,7 +850,7 @@ bool __init early_init_dt_scan(void *params) initial_boot_params = params; /* check device tree validity */ - if (be32_to_cpu(initial_boot_params->magic) != OF_DT_HEADER) { + if (fdt_check_header(params)) { initial_boot_params = NULL; return false; } @@ -907,9 +905,9 @@ void __init unflatten_and_copy_device_tree(void) return; } - size = __be32_to_cpu(initial_boot_params->totalsize); + size = fdt_totalsize(initial_boot_params); dt = early_init_dt_alloc_memory_arch(size, - __alignof__(struct boot_param_header)); + roundup_pow_of_two(FDT_V17_SIZE)); if (dt) { memcpy(dt, initial_boot_params, size); -- cgit From b0a6fb36a49f720c93c3da0b3f040e49e42435ad Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 2 Apr 2014 16:56:48 -0500 Subject: of/fdt: create common debugfs Both powerpc and microblaze have the same FDT blob in debugfs feature. Move this to common location and remove the powerpc and microblaze implementations. This feature could become more useful when FDT overlay support is added. This changes the path of the blob from "$arch/flat-device-tree" to "device-tree/flat-device-tree". Signed-off-by: Rob Herring Tested-by: Michal Simek Cc: Michal Simek Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: linuxppc-dev@lists.ozlabs.org Tested-by: Grant Likely Tested-by: Stephen Chivers --- drivers/of/fdt.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers/of') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 0b38a6aa8603..4129f7442244 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -20,6 +20,7 @@ #include #include #include +#include #include /* for COMMAND_LINE_SIZE */ #include @@ -916,4 +917,27 @@ void __init unflatten_and_copy_device_tree(void) unflatten_device_tree(); } +#if defined(CONFIG_DEBUG_FS) && defined(DEBUG) +static struct debugfs_blob_wrapper flat_dt_blob; + +static int __init of_flat_dt_debugfs_export_fdt(void) +{ + struct dentry *d = debugfs_create_dir("device-tree", NULL); + + if (!d) + return -ENOENT; + + flat_dt_blob.data = initial_boot_params; + flat_dt_blob.size = fdt_totalsize(initial_boot_params); + + d = debugfs_create_blob("flat-device-tree", S_IFREG | S_IRUSR, + d, &flat_dt_blob); + if (!d) + return -ENOENT; + + return 0; +} +module_init(of_flat_dt_debugfs_export_fdt); +#endif + #endif /* CONFIG_OF_EARLY_FLATTREE */ -- cgit From d1552ce449eb0a8d2f0bd6599da3a8a3d7f77a84 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 1 Apr 2014 22:46:48 -0500 Subject: of/fdt: move memreserve and dtb memory reservations into core Move the /memreserve/ processing and dtb memory reservations into early_init_fdt_scan_reserved_mem. This converts arm, arm64, and powerpc as they are the only users of early_init_fdt_scan_reserved_mem. memblock_reserve is safe to call on the same region twice, so the reservation check for the dtb in powerpc 32-bit reservations is safe to remove. Signed-off-by: Rob Herring Tested-by: Michal Simek Cc: Russell King Cc: Catalin Marinas Cc: Will Deacon Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Tested-by: Grant Likely Tested-by: Stephen Chivers --- drivers/of/fdt.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/of') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 4129f7442244..051be4ca25b9 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -492,9 +492,25 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, */ void __init early_init_fdt_scan_reserved_mem(void) { + int n; + u64 base, size; + if (!initial_boot_params) return; + /* Reserve the dtb region */ + early_init_dt_reserve_memory_arch(__pa(initial_boot_params), + fdt_totalsize(initial_boot_params), + 0); + + /* Process header /memreserve/ fields */ + for (n = 0; ; n++) { + fdt_get_mem_rsv(initial_boot_params, n, &base, &size); + if (!size) + break; + early_init_dt_reserve_memory_arch(base, size, 0); + } + of_scan_flat_dt(__fdt_scan_reserved_mem, NULL); fdt_init_reserved_mem(); } -- cgit From 1d1a661da4c8468b3fa6f567b2b1f8cdeafa847a Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 22 Apr 2014 12:50:24 -0500 Subject: of/fdt: fix phys_addr_t related print size warnings Fix warnings in early_init_dt_reserve_memory_arch when phys_addr_t is 32-bit and memblock is not enabled. Signed-off-by: Rob Herring Tested-by: Grant Likely Tested-by: Stephen Chivers --- drivers/of/fdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 051be4ca25b9..d9e64504cda0 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -852,8 +852,8 @@ void * __init __weak early_init_dt_alloc_memory_arch(u64 size, u64 align) int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size, bool nomap) { - pr_err("Reserved memory not supported, ignoring range 0x%llx - 0x%llx%s\n", - base, size, nomap ? " (nomap)" : ""); + pr_err("Reserved memory not supported, ignoring range 0x%pa - 0x%pa%s\n", + &base, &size, nomap ? " (nomap)" : ""); return -ENOSYS; } #endif -- cgit From c0556d3f2c3f42eaed049139ce6f0899ecdb0217 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 22 Apr 2014 12:55:10 -0500 Subject: of/fdt: introduce of_get_flat_dt_size Add a wrapper function to retrieve the FDT size from the FDT header. This is primarily to avoid libfdt include paths for the whole kernel. Signed-off-by: Rob Herring Tested-by: Grant Likely Tested-by: Stephen Chivers --- drivers/of/fdt.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/of') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index d9e64504cda0..358bcf0500d2 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -553,6 +553,14 @@ unsigned long __init of_get_flat_dt_root(void) return 0; } +/** + * of_get_flat_dt_size - Return the total size of the FDT + */ +int __init of_get_flat_dt_size(void) +{ + return fdt_totalsize(initial_boot_params); +} + /** * of_get_flat_dt_prop - Given a node in the flat blob, return the property ptr * -- cgit From 1daa0c4ced334f18f458aba6ace7e01e8cdc2ecf Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 31 Mar 2014 15:25:04 -0500 Subject: of/fdt: convert initial_boot_params to opaque pointer Now that all accesses to FDT header data has been converted to accessor helpers, initial_boot_params can become an opaque pointer. Signed-off-by: Rob Herring Tested-by: Michal Simek Tested-by: Grant Likely Tested-by: Stephen Chivers --- drivers/of/fdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/of') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 358bcf0500d2..a6f83ea107ae 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -372,7 +372,7 @@ EXPORT_SYMBOL_GPL(of_fdt_unflatten_tree); int __initdata dt_root_addr_cells; int __initdata dt_root_size_cells; -struct boot_param_header *initial_boot_params; +void *initial_boot_params; #ifdef CONFIG_OF_EARLY_FLATTREE -- cgit From b8acee3ef83f0fc50e57a3d4c91234982befda95 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 7 May 2014 15:17:26 -0500 Subject: of/platform: fix device naming for non-translatable addresses Using non-translatable addresses in platform device names is wrong because they may not be globally unique. Just use the default naming with a global index if the address cannot be translated instead. of_can_translate_address has the same checks as of_translate_address, so we can remove it here as well. Reported-by: "Ivan T. Ivanov" Cc: Josh Cartwright Cc: Courtney Cavin Cc: Bjorn Andersson Cc: Grant Likely Signed-off-by: Rob Herring Tested-by: Ivan T. Ivanov Tested-by: Frank Rowand Reviewed-by: Frank Rowand --- drivers/of/platform.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/platform.c b/drivers/of/platform.c index bd47fbc53dc9..0602eb5b1be2 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -78,7 +78,6 @@ void of_device_make_bus_id(struct device *dev) struct device_node *node = dev->of_node; const __be32 *reg; u64 addr; - const __be32 *addrp; int magic; #ifdef CONFIG_PPC_DCR @@ -106,15 +105,7 @@ void of_device_make_bus_id(struct device *dev) */ reg = of_get_property(node, "reg", NULL); if (reg) { - if (of_can_translate_address(node)) { - addr = of_translate_address(node, reg); - } else { - addrp = of_get_address(node, 0, NULL, NULL); - if (addrp) - addr = of_read_number(addrp, 1); - else - addr = OF_BAD_ADDR; - } + addr = of_translate_address(node, reg); if (addr != OF_BAD_ADDR) { dev_set_name(dev, "%llx.%s", (unsigned long long)addr, node->name); -- cgit From d9c6866be8a145e32da616d8dcbae806032d75b5 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 7 May 2014 15:23:56 -0500 Subject: of: kill off of_can_translate_address of_can_translate_address only checks some conditions for address translation, but does not check other conditions like having range properties. The checks it does do are redundant with __of_address_translate. The only difference is printing a message or not. Since we only have a single caller that does the full translation anyway, just remove of_can_translate_address and quiet the error message. Cc: Grant Likely Signed-off-by: Rob Herring Tested-by: Frank Rowand Reviewed-by: Frank Rowand --- drivers/of/address.c | 22 +--------------------- drivers/of/platform.c | 5 ++--- 2 files changed, 3 insertions(+), 24 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/address.c b/drivers/of/address.c index cb4242a69cd5..95351b2a112c 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -498,8 +498,7 @@ static u64 __of_translate_address(struct device_node *dev, /* Count address cells & copy address locally */ bus->count_cells(dev, &na, &ns); if (!OF_CHECK_COUNTS(na, ns)) { - printk(KERN_ERR "prom_parse: Bad cell count for %s\n", - of_node_full_name(dev)); + pr_debug("OF: Bad cell count for %s\n", of_node_full_name(dev)); goto bail; } memcpy(addr, in_addr, na * 4); @@ -564,25 +563,6 @@ u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr) } EXPORT_SYMBOL(of_translate_dma_address); -bool of_can_translate_address(struct device_node *dev) -{ - struct device_node *parent; - struct of_bus *bus; - int na, ns; - - parent = of_get_parent(dev); - if (parent == NULL) - return false; - - bus = of_match_bus(parent); - bus->count_cells(dev, &na, &ns); - - of_node_put(parent); - - return OF_CHECK_COUNTS(na, ns); -} -EXPORT_SYMBOL(of_can_translate_address); - const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, unsigned int *flags) { diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 0602eb5b1be2..d0009b3614af 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -140,9 +140,8 @@ struct platform_device *of_device_alloc(struct device_node *np, return NULL; /* count the io and irq resources */ - if (of_can_translate_address(np)) - while (of_address_to_resource(np, num_reg, &temp_res) == 0) - num_reg++; + while (of_address_to_resource(np, num_reg, &temp_res) == 0) + num_reg++; num_irq = of_irq_count(np); /* Populate the resource table */ -- cgit From 7d1cdc89c54d2cb8157cf1f36fc65e8583d26484 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 13 May 2014 10:07:29 -0500 Subject: of/selftest: clean-up of_selftest_platform_populate pass/fail handling Move the pass/fail checks into selftest() calls instead of a separate if condition. Unconditionally calling pass was wrong. Signed-off-by: Rob Herring Cc: Grant Likely --- drivers/of/selftest.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index fe70b86bcffb..c1d7d38009f1 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -440,22 +440,18 @@ static void __init of_selftest_platform_populate(void) /* Test that a missing irq domain returns -EPROBE_DEFER */ np = of_find_node_by_path("/testcase-data/testcase-device1"); pdev = of_find_device_by_node(np); - if (!pdev) - selftest(0, "device 1 creation failed\n"); + selftest(pdev, "device 1 creation failed\n"); + irq = platform_get_irq(pdev, 0); - if (irq != -EPROBE_DEFER) - selftest(0, "device deferred probe failed - %d\n", irq); + selftest(irq == -EPROBE_DEFER, "device deferred probe failed - %d\n", irq); /* Test that a parsing failure does not return -EPROBE_DEFER */ np = of_find_node_by_path("/testcase-data/testcase-device2"); pdev = of_find_device_by_node(np); - if (!pdev) - selftest(0, "device 2 creation failed\n"); + selftest(pdev, "device 2 creation failed\n"); irq = platform_get_irq(pdev, 0); - if (irq >= 0 || irq == -EPROBE_DEFER) - selftest(0, "device parsing error failed - %d\n", irq); + selftest(irq < 0 && irq != -EPROBE_DEFER, "device parsing error failed - %d\n", irq); - selftest(1, "passed"); } static int __init of_selftest(void) -- cgit From fb2caa50fbacd21719a90dd66b617ce3cb4fd6d7 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 13 May 2014 10:07:54 -0500 Subject: of/selftest: add testcase for nodes with same name and address Add a test case for nodes which have the same name and same non-translatable unit address. Signed-off-by: Rob Herring Reviewed-by: Grant Likely Reviewed-by: Frank Rowand --- drivers/of/selftest.c | 20 +++++++++++++++- drivers/of/testcase-data/testcases.dtsi | 1 + drivers/of/testcase-data/tests-platform.dtsi | 35 ++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 drivers/of/testcase-data/tests-platform.dtsi (limited to 'drivers/of') diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index c1d7d38009f1..2588faaaa305 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -431,8 +431,12 @@ static void __init of_selftest_match_node(void) static void __init of_selftest_platform_populate(void) { int irq; - struct device_node *np; + struct device_node *np, *child; struct platform_device *pdev; + struct of_device_id match[] = { + { .compatible = "test-device", }, + {} + }; np = of_find_node_by_path("/testcase-data"); of_platform_populate(np, of_default_bus_match_table, NULL, NULL); @@ -452,6 +456,20 @@ static void __init of_selftest_platform_populate(void) irq = platform_get_irq(pdev, 0); selftest(irq < 0 && irq != -EPROBE_DEFER, "device parsing error failed - %d\n", irq); + np = of_find_node_by_path("/testcase-data/platform-tests"); + if (!np) { + pr_err("No testcase data in device tree\n"); + return; + } + + for_each_child_of_node(np, child) { + struct device_node *grandchild; + of_platform_populate(child, match, NULL, NULL); + for_each_child_of_node(child, grandchild) + selftest(of_find_device_by_node(grandchild), + "Could not create device for node '%s'\n", + grandchild->name); + } } static int __init of_selftest(void) diff --git a/drivers/of/testcase-data/testcases.dtsi b/drivers/of/testcase-data/testcases.dtsi index 3a5b75a8e4d7..6d8d980ac858 100644 --- a/drivers/of/testcase-data/testcases.dtsi +++ b/drivers/of/testcase-data/testcases.dtsi @@ -1,3 +1,4 @@ #include "tests-phandle.dtsi" #include "tests-interrupts.dtsi" #include "tests-match.dtsi" +#include "tests-platform.dtsi" diff --git a/drivers/of/testcase-data/tests-platform.dtsi b/drivers/of/testcase-data/tests-platform.dtsi new file mode 100644 index 000000000000..eb20eeb2b062 --- /dev/null +++ b/drivers/of/testcase-data/tests-platform.dtsi @@ -0,0 +1,35 @@ + +/ { + testcase-data { + platform-tests { + #address-cells = <1>; + #size-cells = <0>; + + test-device@0 { + compatible = "test-device"; + reg = <0x0>; + + #address-cells = <1>; + #size-cells = <0>; + + dev@100 { + compatible = "test-sub-device"; + reg = <0x100>; + }; + }; + + test-device@1 { + compatible = "test-device"; + reg = <0x1>; + + #address-cells = <1>; + #size-cells = <0>; + + dev@100 { + compatible = "test-sub-device"; + reg = <0x100>; + }; + }; + }; + }; +}; -- cgit From 9dd3107576c4bbd40e1c2c8b24d560abf9a7b991 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 8 May 2014 16:06:17 -0500 Subject: of: align RESERVEDMEM_OF_DECLARE function callbacks to other callbacks All the parameters for RESERVEDMEM_OF_DECLARE function callbacks are members of struct reserved_mem, so just pass the struct ptr to callback functions so the function callback is more in line with other OF match table callbacks. Acked-by: Marek Szyprowski Acked-by: Grant Likely Signed-off-by: Rob Herring --- drivers/of/of_reserved_mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/of') diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index e420eb52e5c9..632aae861375 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -188,7 +188,7 @@ static int __init __reserved_mem_init_node(struct reserved_mem *rmem) if (!of_flat_dt_is_compatible(rmem->fdt_node, compat)) continue; - if (initfn(rmem, rmem->fdt_node, rmem->name) == 0) { + if (initfn(rmem) == 0) { pr_info("Reserved memory: initialized node %s, compatible id %s\n", rmem->name, compat); return 0; -- cgit From e06e8b27082852bdab417af884241a4ed2037c73 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 27 Mar 2014 07:37:43 -0500 Subject: of/fdt: add FDT address translation support Copy u-boot's FDT address translation code from common/fdt_support. This code was originally based on the kernel's unflattened DT address parsing code. This commit can be reverted once relicensing of this code to GPLv2/BSD is done and it is added to libfdt. Signed-off-by: Rob Herring Acked-by: Grant Likely --- drivers/of/Makefile | 2 + drivers/of/fdt_address.c | 241 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 243 insertions(+) create mode 100644 drivers/of/fdt_address.c (limited to 'drivers/of') diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 9891232f999e..099b1fb00af4 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,5 +1,6 @@ obj-y = base.o device.o platform.o obj-$(CONFIG_OF_FLATTREE) += fdt.o +obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o obj-$(CONFIG_OF_PROMTREE) += pdt.o obj-$(CONFIG_OF_ADDRESS) += address.o obj-$(CONFIG_OF_IRQ) += irq.o @@ -12,3 +13,4 @@ obj-$(CONFIG_OF_MTD) += of_mtd.o obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt +CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt diff --git a/drivers/of/fdt_address.c b/drivers/of/fdt_address.c new file mode 100644 index 000000000000..8d3dc6fbdb7a --- /dev/null +++ b/drivers/of/fdt_address.c @@ -0,0 +1,241 @@ +/* + * FDT Address translation based on u-boot fdt_support.c which in turn was + * based on the kernel unflattened DT address translation code. + * + * (C) Copyright 2007 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + */ +#include +#include +#include +#include +#include + +/* Max address size we deal with */ +#define OF_MAX_ADDR_CELLS 4 +#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \ + (ns) > 0) + +/* Debug utility */ +#ifdef DEBUG +static void __init of_dump_addr(const char *s, const __be32 *addr, int na) +{ + pr_debug("%s", s); + while(na--) + pr_cont(" %08x", *(addr++)); + pr_debug("\n"); +} +#else +static void __init of_dump_addr(const char *s, const __be32 *addr, int na) { } +#endif + +/* Callbacks for bus specific translators */ +struct of_bus { + void (*count_cells)(const void *blob, int parentoffset, + int *addrc, int *sizec); + u64 (*map)(__be32 *addr, const __be32 *range, + int na, int ns, int pna); + int (*translate)(__be32 *addr, u64 offset, int na); +}; + +/* Default translator (generic bus) */ +static void __init fdt_bus_default_count_cells(const void *blob, int parentoffset, + int *addrc, int *sizec) +{ + const __be32 *prop; + + if (addrc) { + prop = fdt_getprop(blob, parentoffset, "#address-cells", NULL); + if (prop) + *addrc = be32_to_cpup(prop); + else + *addrc = dt_root_addr_cells; + } + + if (sizec) { + prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL); + if (prop) + *sizec = be32_to_cpup(prop); + else + *sizec = dt_root_size_cells; + } +} + +static u64 __init fdt_bus_default_map(__be32 *addr, const __be32 *range, + int na, int ns, int pna) +{ + u64 cp, s, da; + + cp = of_read_number(range, na); + s = of_read_number(range + na + pna, ns); + da = of_read_number(addr, na); + + pr_debug("FDT: default map, cp=%llx, s=%llx, da=%llx\n", + cp, s, da); + + if (da < cp || da >= (cp + s)) + return OF_BAD_ADDR; + return da - cp; +} + +static int __init fdt_bus_default_translate(__be32 *addr, u64 offset, int na) +{ + u64 a = of_read_number(addr, na); + memset(addr, 0, na * 4); + a += offset; + if (na > 1) + addr[na - 2] = cpu_to_fdt32(a >> 32); + addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu); + + return 0; +} + +/* Array of bus specific translators */ +static const struct of_bus of_busses[] __initconst = { + /* Default */ + { + .count_cells = fdt_bus_default_count_cells, + .map = fdt_bus_default_map, + .translate = fdt_bus_default_translate, + }, +}; + +static int __init fdt_translate_one(const void *blob, int parent, + const struct of_bus *bus, + const struct of_bus *pbus, __be32 *addr, + int na, int ns, int pna, const char *rprop) +{ + const __be32 *ranges; + int rlen; + int rone; + u64 offset = OF_BAD_ADDR; + + ranges = fdt_getprop(blob, parent, rprop, &rlen); + if (!ranges) + return 1; + if (rlen == 0) { + offset = of_read_number(addr, na); + memset(addr, 0, pna * 4); + pr_debug("FDT: empty ranges, 1:1 translation\n"); + goto finish; + } + + pr_debug("FDT: walking ranges...\n"); + + /* Now walk through the ranges */ + rlen /= 4; + rone = na + pna + ns; + for (; rlen >= rone; rlen -= rone, ranges += rone) { + offset = bus->map(addr, ranges, na, ns, pna); + if (offset != OF_BAD_ADDR) + break; + } + if (offset == OF_BAD_ADDR) { + pr_debug("FDT: not found !\n"); + return 1; + } + memcpy(addr, ranges + na, 4 * pna); + + finish: + of_dump_addr("FDT: parent translation for:", addr, pna); + pr_debug("FDT: with offset: %llx\n", offset); + + /* Translate it into parent bus space */ + return pbus->translate(addr, offset, pna); +} + +/* + * Translate an address from the device-tree into a CPU physical address, + * this walks up the tree and applies the various bus mappings on the + * way. + * + * Note: We consider that crossing any level with #size-cells == 0 to mean + * that translation is impossible (that is we are not dealing with a value + * that can be mapped to a cpu physical address). This is not really specified + * that way, but this is traditionally the way IBM at least do things + */ +u64 __init fdt_translate_address(const void *blob, int node_offset) +{ + int parent, len; + const struct of_bus *bus, *pbus; + const __be32 *reg; + __be32 addr[OF_MAX_ADDR_CELLS]; + int na, ns, pna, pns; + u64 result = OF_BAD_ADDR; + + pr_debug("FDT: ** translation for device %s **\n", + fdt_get_name(blob, node_offset, NULL)); + + reg = fdt_getprop(blob, node_offset, "reg", &len); + if (!reg) { + pr_err("FDT: warning: device tree node '%s' has no address.\n", + fdt_get_name(blob, node_offset, NULL)); + goto bail; + } + + /* Get parent & match bus type */ + parent = fdt_parent_offset(blob, node_offset); + if (parent < 0) + goto bail; + bus = &of_busses[0]; + + /* Cound address cells & copy address locally */ + bus->count_cells(blob, parent, &na, &ns); + if (!OF_CHECK_COUNTS(na, ns)) { + pr_err("FDT: Bad cell count for %s\n", + fdt_get_name(blob, node_offset, NULL)); + goto bail; + } + memcpy(addr, reg, na * 4); + + pr_debug("FDT: bus (na=%d, ns=%d) on %s\n", + na, ns, fdt_get_name(blob, parent, NULL)); + of_dump_addr("OF: translating address:", addr, na); + + /* Translate */ + for (;;) { + /* Switch to parent bus */ + node_offset = parent; + parent = fdt_parent_offset(blob, node_offset); + + /* If root, we have finished */ + if (parent < 0) { + pr_debug("FDT: reached root node\n"); + result = of_read_number(addr, na); + break; + } + + /* Get new parent bus and counts */ + pbus = &of_busses[0]; + pbus->count_cells(blob, parent, &pna, &pns); + if (!OF_CHECK_COUNTS(pna, pns)) { + pr_err("FDT: Bad cell count for %s\n", + fdt_get_name(blob, node_offset, NULL)); + break; + } + + pr_debug("FDT: parent bus (na=%d, ns=%d) on %s\n", + pna, pns, fdt_get_name(blob, parent, NULL)); + + /* Apply bus translation */ + if (fdt_translate_one(blob, node_offset, bus, pbus, + addr, na, ns, pna, "ranges")) + break; + + /* Complete the move up one level */ + na = pna; + ns = pns; + bus = pbus; + + of_dump_addr("FDT: one level translation:", addr, na); + } + bail: + return result; +} -- cgit From fb11ffe74c794a510a29ad8cd81d4e9e3b1c9158 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 27 Mar 2014 08:07:01 -0500 Subject: of/fdt: add FDT serial scanning for earlycon This adds FDT parsing of {linux,}stdout-path to setup an early serial console. Enabling of the early console is triggered with "earlycon" (with no options) on the kernel command line. Platforms must either have fixmap permanent mapping support, have a functioning ioremap when early params are parsed, or explicitly call early_init_dt_scan_chosen_serial from architecture code. Signed-off-by: Rob Herring Acked-by: Grant Likely --- drivers/of/fdt.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'drivers/of') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index a6f83ea107ae..1fbeab27075f 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -21,6 +21,7 @@ #include #include #include +#include #include /* for COMMAND_LINE_SIZE */ #include @@ -696,6 +697,61 @@ static inline void early_init_dt_check_for_initrd(unsigned long node) } #endif /* CONFIG_BLK_DEV_INITRD */ +#ifdef CONFIG_SERIAL_EARLYCON +extern struct of_device_id __earlycon_of_table[]; + +int __init early_init_dt_scan_chosen_serial(void) +{ + int offset; + const char *p; + int l; + const struct of_device_id *match = __earlycon_of_table; + const void *fdt = initial_boot_params; + + offset = fdt_path_offset(fdt, "/chosen"); + if (offset < 0) + offset = fdt_path_offset(fdt, "/chosen@0"); + if (offset < 0) + return -ENOENT; + + p = fdt_getprop(fdt, offset, "stdout-path", &l); + if (!p) + p = fdt_getprop(fdt, offset, "linux,stdout-path", &l); + if (!p || !l) + return -ENOENT; + + /* Get the node specified by stdout-path */ + offset = fdt_path_offset(fdt, p); + if (offset < 0) + return -ENODEV; + + while (match->compatible) { + unsigned long addr; + if (fdt_node_check_compatible(fdt, offset, match->compatible)) { + match++; + continue; + } + + addr = fdt_translate_address(fdt, offset); + if (!addr) + return -ENXIO; + + of_setup_earlycon(addr, match->data); + return 0; + } + return -ENODEV; +} + +static int __init setup_of_earlycon(char *buf) +{ + if (buf) + return 0; + + return early_init_dt_scan_chosen_serial(); +} +early_param("earlycon", setup_of_earlycon); +#endif + /** * early_init_dt_scan_root - fetch the top level address and size cells */ -- cgit From 07e461cd7e73a84f0e3757932b93cc80976fd749 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 21 May 2014 15:40:31 +0900 Subject: of: Ensure unique names without sacrificing determinism The way the driver core is implemented, every device using the same bus type is required to have a unique name because a symlink to each device is created in the appropriate /sys/bus/*/devices directory, and two identical names causes a collision. The current code handles the requirement by using an globally incremented counter that is appended to the device name. It works, but it means any change to device registration will change the assigned numbers. Instead, if we build up the name by using information from the parent nodes, then it can be guaranteed to be unique without adding a random number to the end of it. Signed-off-by: Grant Likely Cc: Ezequiel Garcia Cc: Rob Herring --- drivers/of/platform.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/platform.c b/drivers/of/platform.c index d0009b3614af..95c133a0554b 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -68,17 +68,15 @@ EXPORT_SYMBOL(of_find_device_by_node); * of_device_make_bus_id - Use the device node data to assign a unique name * @dev: pointer to device structure that is linked to a device tree node * - * This routine will first try using either the dcr-reg or the reg property - * value to derive a unique name. As a last resort it will use the node - * name followed by a unique number. + * This routine will first try using the translated bus address to + * derive a unique name. If it cannot, then it will prepend names from + * parent nodes until a unique name can be derived. */ void of_device_make_bus_id(struct device *dev) { - static atomic_t bus_no_reg_magic; struct device_node *node = dev->of_node; const __be32 *reg; u64 addr; - int magic; #ifdef CONFIG_PPC_DCR /* @@ -100,25 +98,25 @@ void of_device_make_bus_id(struct device *dev) } #endif /* CONFIG_PPC_DCR */ - /* - * For MMIO, get the physical address - */ - reg = of_get_property(node, "reg", NULL); - if (reg) { - addr = of_translate_address(node, reg); - if (addr != OF_BAD_ADDR) { - dev_set_name(dev, "%llx.%s", - (unsigned long long)addr, node->name); + /* Construct the name, using parent nodes if necessary to ensure uniqueness */ + while (node->parent) { + /* + * If the address can be translated, then that is as much + * uniqueness as we need. Make it the first component and return + */ + reg = of_get_property(node, "reg", NULL); + if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { + dev_set_name(dev, dev_name(dev) ? "%llx.%s:%s" : "%llx.%s", + (unsigned long long)addr, node->name, + dev_name(dev)); return; } - } - /* - * No BusID, use the node name and add a globally incremented - * counter (and pray...) - */ - magic = atomic_add_return(1, &bus_no_reg_magic); - dev_set_name(dev, "%s.%d", node->name, magic - 1); + /* format arguments only used if dev_name() resolves to NULL */ + dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s", + strrchr(node->full_name, '/') + 1, dev_name(dev)); + node = node->parent; + } } /** -- cgit From ba52464a629fab2493925007b00f2ca65d02ed4e Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 23 May 2014 07:50:50 +0900 Subject: of: Stop naming platform_device using dcr address There is now a way to ensure all platform devices get a unique name when populated from the device tree, and the DCR_NATIVE code path is broken anyway. PowerPC Cell (PS3) is the only platform that actually uses this path. Most likely nobody will notice if it is killed. Remove the code and associated ugly #ifdef. The user-visible impact of this patch is that any DCR device on Cell will get a new name in the /sys/devices hierarchy. Signed-off-by: Grant Likely Cc: Rob Herring Cc: Benjamin Herrenschmidt --- drivers/of/platform.c | 24 ------------------------ 1 file changed, 24 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 95c133a0554b..52780a72d09d 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -51,10 +51,6 @@ struct platform_device *of_find_device_by_node(struct device_node *np) } EXPORT_SYMBOL(of_find_device_by_node); -#if defined(CONFIG_PPC_DCR) -#include -#endif - #ifdef CONFIG_OF_ADDRESS /* * The following routines scan a subtree and registers a device for @@ -78,26 +74,6 @@ void of_device_make_bus_id(struct device *dev) const __be32 *reg; u64 addr; -#ifdef CONFIG_PPC_DCR - /* - * If it's a DCR based device, use 'd' for native DCRs - * and 'D' for MMIO DCRs. - */ - reg = of_get_property(node, "dcr-reg", NULL); - if (reg) { -#ifdef CONFIG_PPC_DCR_NATIVE - dev_set_name(dev, "d%x.%s", *reg, node->name); -#else /* CONFIG_PPC_DCR_NATIVE */ - u64 addr = of_translate_dcr_address(node, *reg, NULL); - if (addr != OF_BAD_ADDR) { - dev_set_name(dev, "D%llx.%s", - (unsigned long long)addr, node->name); - return; - } -#endif /* !CONFIG_PPC_DCR_NATIVE */ - } -#endif /* CONFIG_PPC_DCR */ - /* Construct the name, using parent nodes if necessary to ensure uniqueness */ while (node->parent) { /* -- cgit From d2d3d7cd81e90e1ffac1a6eed7b3edcbf11f4c97 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 2 Apr 2014 09:47:01 +0200 Subject: of: Use NULL for pointers Commit 4485681939b9 (of/fdt: Clean up casting in unflattening path) modified unflatten_dt_node() to take a void * for the mem parameter instead of an unsigned long. One of the call sites wasn't updated. Signed-off-by: Thierry Reding Signed-off-by: Grant Likely Conflicts: drivers/of/fdt.c --- drivers/of/fdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/of') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index a6f83ea107ae..d429826b61a5 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -324,7 +324,7 @@ static void __unflatten_device_tree(void *blob, /* First pass, scan for size */ start = 0; - size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0); + size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0); size = ALIGN(size, 4); pr_debug(" size is %lx, allocating...\n", size); -- cgit From 947fdaad0627e277c5f3a2573203c4fab3db513b Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Thu, 17 Apr 2014 15:48:29 +0800 Subject: of: fix race between search and remove in of_update_property() The of_update_property() is intented to update a property in a node and if the property does not exist, will add it. The second search of the property is possibly won't be found, that maybe removed by other thread just before the second search begain. Using the __of_find_property() and __of_add_property() instead and move them into lock operations. Signed-off-by: Xiubo Li [grant.likely: conflict with another change in same function] Signed-off-by: Grant Likely --- drivers/of/base.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/base.c b/drivers/of/base.c index 6d4ee22708c9..63ae00ec72ff 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1800,7 +1800,7 @@ int of_update_property(struct device_node *np, struct property *newprop) { struct property **next, *oldprop; unsigned long flags; - int rc, found = 0; + int rc; rc = of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop); if (rc) @@ -1809,30 +1809,34 @@ int of_update_property(struct device_node *np, struct property *newprop) if (!newprop->name) return -EINVAL; - oldprop = of_find_property(np, newprop->name, NULL); - if (!oldprop) - return of_add_property(np, newprop); - raw_spin_lock_irqsave(&devtree_lock, flags); next = &np->properties; - while (*next) { + oldprop = __of_find_property(np, newprop->name, NULL); + if (!oldprop) { + /* add the new node */ + rc = __of_add_property(np, newprop); + } else while (*next) { + /* replace the node */ if (*next == oldprop) { - /* found the node */ newprop->next = oldprop->next; *next = newprop; oldprop->next = np->deadprops; np->deadprops = oldprop; - found = 1; break; } next = &(*next)->next; } raw_spin_unlock_irqrestore(&devtree_lock, flags); - if (!found) - return -ENODEV; + if (rc) + return rc; + + /* At early boot, bail out and defer setup to of_init() */ + if (!of_kset) + return 0; /* Update the sysfs attribute */ - sysfs_remove_bin_file(&np->kobj, &oldprop->attr); + if (oldprop) + sysfs_remove_bin_file(&np->kobj, &oldprop->attr); __of_add_property_sysfs(np, newprop); return 0; -- cgit From ff5f762b44237deb83be7bf6db965eeafadfa3e1 Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Mon, 28 Apr 2014 15:32:21 +0100 Subject: pci/of: Remove dead code Commit 98d9f30c820d509145757e6ecbc36013aa02f7bc "pci/of: Match PCI devices to OF nodes dynamically" introduced a lot of code derived from the PPC PCI code, including some likes which were redundant. Remove these lines. Reviewed-by: William Towle Signed-off-by: Ian Molton Signed-off-by: Grant Likely --- drivers/of/of_pci_irq.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c index 8736bc7676c5..7e4e21438e28 100644 --- a/drivers/of/of_pci_irq.c +++ b/drivers/of/of_pci_irq.c @@ -19,7 +19,6 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq struct device_node *dn, *ppnode; struct pci_dev *ppdev; u32 lspec; - __be32 lspec_be; __be32 laddr[3]; u8 pin; int rc; @@ -87,7 +86,6 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq out_irq->np = ppnode; out_irq->args_count = 1; out_irq->args[0] = lspec; - lspec_be = cpu_to_be32(lspec); laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8)); laddr[1] = laddr[2] = cpu_to_be32(0); return of_irq_parse_raw(laddr, out_irq); -- cgit From b44aa25d20e2ef6b824901cbc50a281791f3b421 Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Thu, 17 Apr 2014 18:42:01 +0100 Subject: of: Handle memory@0 node on PPC32 only In order to deal with an firmware bug on a specific ppc32 platform (longtrail), early_init_dt_scan_memory() looks for a node called memory@0 on all platforms. Restrict this quirk to ppc32 kernels only. Signed-off-by: Leif Lindholm Cc: linuxppc-dev@lists.ozlabs.org Cc: Grant Likely Cc: Mark Rutland Cc: devicetree@vger.kernel.org Cc: linux-kernel@vger.kernel.org --- drivers/of/fdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/of') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index d429826b61a5..17be90f5445f 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -748,7 +748,7 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname, * The longtrail doesn't have a device_type on the * /memory node, so look for the node called /memory@0. */ - if (depth != 1 || strcmp(uname, "memory@0") != 0) + if (!IS_ENABLED(CONFIG_PPC32) || depth != 1 || strcmp(uname, "memory@0") != 0) return 0; } else if (strcmp(type, "memory") != 0) return 0; -- cgit From 0d0e02d605c5696a5076510f564fefe659127aa4 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 22 May 2014 01:04:17 +0900 Subject: of: Create unlocked version of for_each_child_of_node() When iterating over nodes, sometimes it needs to be done when the DT lock is already held. This patch makes an unlocked version of the for_each_child_of_node() macro. Signed-off-by: Grant Likely --- drivers/of/base.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/base.c b/drivers/of/base.c index 63ae00ec72ff..9df50c74162c 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -695,6 +695,22 @@ struct device_node *of_get_next_parent(struct device_node *node) } EXPORT_SYMBOL(of_get_next_parent); +static struct device_node *__of_get_next_child(const struct device_node *node, + struct device_node *prev) +{ + struct device_node *next; + + next = prev ? prev->sibling : node->child; + for (; next; next = next->sibling) + if (of_node_get(next)) + break; + of_node_put(prev); + return next; +} +#define __for_each_child_of_node(parent, child) \ + for (child = __of_get_next_child(parent, NULL); child != NULL; \ + child = __of_get_next_child(parent, child)) + /** * of_get_next_child - Iterate a node childs * @node: parent node @@ -710,11 +726,7 @@ struct device_node *of_get_next_child(const struct device_node *node, unsigned long flags; raw_spin_lock_irqsave(&devtree_lock, flags); - next = prev ? prev->sibling : node->child; - for (; next; next = next->sibling) - if (of_node_get(next)) - break; - of_node_put(prev); + next = __of_get_next_child(node, prev); raw_spin_unlock_irqrestore(&devtree_lock, flags); return next; } -- cgit From c22e650e66b862babe9c00bebb20b8029c7b0362 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 14 Mar 2014 17:07:12 +0000 Subject: of: Make of_find_node_by_path() handle /aliases Make of_find_node_by_path() handle aliases as prefixes. To make this work the name search is refactored to search by path component instead of by full string. This should be a more efficient search, and it makes it possible to start a search at a subnode of a tree. Signed-off-by: David Daney Signed-off-by: Pantelis Antoniou [grant.likely: Rework to not require allocating at runtime] Acked-by: Rob Herring Signed-off-by: Grant Likely --- drivers/of/base.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 6 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/base.c b/drivers/of/base.c index 9df50c74162c..e67b308819c9 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -783,23 +783,78 @@ struct device_node *of_get_child_by_name(const struct device_node *node, } EXPORT_SYMBOL(of_get_child_by_name); +static struct device_node *__of_find_node_by_path(struct device_node *parent, + const char *path) +{ + struct device_node *child; + int len = strchrnul(path, '/') - path; + + if (!len) + return NULL; + + __for_each_child_of_node(parent, child) { + const char *name = strrchr(child->full_name, '/'); + if (WARN(!name, "malformed device_node %s\n", child->full_name)) + continue; + name++; + if (strncmp(path, name, len) == 0 && (strlen(name) == len)) + return child; + } + return NULL; +} + /** * of_find_node_by_path - Find a node matching a full OF path - * @path: The full path to match + * @path: Either the full path to match, or if the path does not + * start with '/', the name of a property of the /aliases + * node (an alias). In the case of an alias, the node + * matching the alias' value will be returned. + * + * Valid paths: + * /foo/bar Full path + * foo Valid alias + * foo/bar Valid alias + relative path * * Returns a node pointer with refcount incremented, use * of_node_put() on it when done. */ struct device_node *of_find_node_by_path(const char *path) { - struct device_node *np = of_allnodes; + struct device_node *np = NULL; + struct property *pp; unsigned long flags; + if (strcmp(path, "/") == 0) + return of_node_get(of_allnodes); + + /* The path could begin with an alias */ + if (*path != '/') { + char *p = strchrnul(path, '/'); + int len = p - path; + + /* of_aliases must not be NULL */ + if (!of_aliases) + return NULL; + + for_each_property_of_node(of_aliases, pp) { + if (strlen(pp->name) == len && !strncmp(pp->name, path, len)) { + np = of_find_node_by_path(pp->value); + break; + } + } + if (!np) + return NULL; + path = p; + } + + /* Step down the tree matching path components */ raw_spin_lock_irqsave(&devtree_lock, flags); - for (; np; np = np->allnext) { - if (np->full_name && (of_node_cmp(np->full_name, path) == 0) - && of_node_get(np)) - break; + if (!np) + np = of_node_get(of_allnodes); + while (np && *path == '/') { + path++; /* Increment past '/' delimiter */ + np = __of_find_node_by_path(np, path); + path = strchrnul(path, '/'); } raw_spin_unlock_irqrestore(&devtree_lock, flags); return np; -- cgit From ae91ff72e9132abe47f726424d9420fce004ca04 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 14 Mar 2014 13:53:10 +0000 Subject: of: Add a testcase for of_find_node_by_path() Add a testcase for the find_node_by_path() function to make sure it handles all the valid scenarios. Signed-off-by: Grant Likely --- drivers/of/selftest.c | 46 +++++++++++++++++++++++++++++ drivers/of/testcase-data/tests-phandle.dtsi | 6 +++- 2 files changed, 51 insertions(+), 1 deletion(-) (limited to 'drivers/of') diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index 2588faaaa305..077314eebb95 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -31,6 +31,51 @@ static struct selftest_results { } \ } +static void __init of_selftest_find_node_by_name(void) +{ + struct device_node *np; + + np = of_find_node_by_path("/testcase-data"); + selftest(np && !strcmp("/testcase-data", np->full_name), + "find /testcase-data failed\n"); + of_node_put(np); + + /* Test if trailing '/' works */ + np = of_find_node_by_path("/testcase-data/"); + selftest(!np, "trailing '/' on /testcase-data/ should fail\n"); + + np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); + selftest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", np->full_name), + "find /testcase-data/phandle-tests/consumer-a failed\n"); + of_node_put(np); + + np = of_find_node_by_path("testcase-alias"); + selftest(np && !strcmp("/testcase-data", np->full_name), + "find testcase-alias failed\n"); + of_node_put(np); + + /* Test if trailing '/' works on aliases */ + np = of_find_node_by_path("testcase-alias/"); + selftest(!np, "trailing '/' on testcase-alias/ should fail\n"); + + np = of_find_node_by_path("testcase-alias/phandle-tests/consumer-a"); + selftest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", np->full_name), + "find testcase-alias/phandle-tests/consumer-a failed\n"); + of_node_put(np); + + np = of_find_node_by_path("/testcase-data/missing-path"); + selftest(!np, "non-existent path returned node %s\n", np->full_name); + of_node_put(np); + + np = of_find_node_by_path("missing-alias"); + selftest(!np, "non-existent alias returned node %s\n", np->full_name); + of_node_put(np); + + np = of_find_node_by_path("testcase-alias/missing-path"); + selftest(!np, "non-existent alias with relative path returned node %s\n", np->full_name); + of_node_put(np); +} + static void __init of_selftest_dynamic(void) { struct device_node *np; @@ -484,6 +529,7 @@ static int __init of_selftest(void) of_node_put(np); pr_info("start of selftest - you will see error messages\n"); + of_selftest_find_node_by_name(); of_selftest_dynamic(); of_selftest_parse_phandle_with_args(); of_selftest_property_match_string(); diff --git a/drivers/of/testcase-data/tests-phandle.dtsi b/drivers/of/testcase-data/tests-phandle.dtsi index 788a4c24b8f5..ce0fe083d406 100644 --- a/drivers/of/testcase-data/tests-phandle.dtsi +++ b/drivers/of/testcase-data/tests-phandle.dtsi @@ -1,6 +1,10 @@ / { - testcase-data { + aliases { + testcase-alias = &testcase; + }; + + testcase: testcase-data { security-password = "password"; duplicate-name = "duplicate"; duplicate-name { }; -- cgit From ad69674e73a18dc3a8da557f4059ccf9389531a5 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 20 May 2014 13:42:02 +0300 Subject: of/irq: do irq resolution in platform_get_irq_byname() The commit 9ec36cafe43bf835f8f29273597a5b0cbc8267ef "of/irq: do irq resolution in platform_get_irq" from Rob Herring - moves resolving of the interrupt resources in platform_get_irq(). But this solution isn't complete because platform_get_irq_byname() need to be modified the same way. Hence, fix it by adding interrupt resolution code at the platform_get_irq_byname() function too. Cc: Russell King Cc: Rob Herring Cc: Tony Lindgren Cc: Grant Likely Cc: Thierry Reding Signed-off-by: Grygorii Strashko Signed-off-by: Grant Likely --- drivers/of/irq.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/of') diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 5aeb89411350..3e06a699352d 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -405,6 +405,28 @@ int of_irq_get(struct device_node *dev, int index) return irq_create_of_mapping(&oirq); } +/** + * of_irq_get_byname - Decode a node's IRQ and return it as a Linux irq number + * @dev: pointer to device tree node + * @name: irq name + * + * Returns Linux irq number on success, or -EPROBE_DEFER if the irq domain + * is not yet created, or error code in case of any other failure. + */ +int of_irq_get_byname(struct device_node *dev, const char *name) +{ + int index; + + if (unlikely(!name)) + return -EINVAL; + + index = of_property_match_string(dev, "interrupt-names", name); + if (index < 0) + return index; + + return of_irq_get(dev, index); +} + /** * of_irq_count - Count the number of IRQs a node uses * @dev: pointer to device tree node -- cgit From 08cf78ed41feaa21017487bad0a5a405c6cede43 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sun, 25 May 2014 22:50:06 +0400 Subject: of_pci_irq: kill useless variable in of_irq_parse_pci() The 'lspec' variable only caused pointless promotions from u8 to u32 on each loop iteration, while it's enough to promote only once, after the loop. Signed-off-by: Sergei Shtylyov Signed-off-by: Grant Likely --- drivers/of/of_pci_irq.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c index 7e4e21438e28..1710d9dc7fc2 100644 --- a/drivers/of/of_pci_irq.c +++ b/drivers/of/of_pci_irq.c @@ -18,7 +18,6 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq { struct device_node *dn, *ppnode; struct pci_dev *ppdev; - u32 lspec; __be32 laddr[3]; u8 pin; int rc; @@ -45,7 +44,6 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq return -ENODEV; /* Now we walk up the PCI tree */ - lspec = pin; for (;;) { /* Get the pci_dev of our parent */ ppdev = pdev->bus->self; @@ -79,13 +77,13 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq /* We can only get here if we hit a P2P bridge with no node, * let's do standard swizzling and try again */ - lspec = pci_swizzle_interrupt_pin(pdev, lspec); + pin = pci_swizzle_interrupt_pin(pdev, pin); pdev = ppdev; } out_irq->np = ppnode; out_irq->args_count = 1; - out_irq->args[0] = lspec; + out_irq->args[0] = pin; laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8)); laddr[1] = laddr[2] = cpu_to_be32(0); return of_irq_parse_raw(laddr, out_irq); -- cgit From 43cb43678705e39b175b325f17938295996aefc7 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 28 May 2014 10:39:02 -0700 Subject: of: handle NULL node in next_child iterators Add an early check for the node argument in __of_get_next_child and of_get_next_available_child() to avoid dereferencing a NULL node pointer a few lines after. CC: Daniel Mack Signed-off-by: Florian Fainelli Signed-off-by: Grant Likely --- drivers/of/base.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/of') diff --git a/drivers/of/base.c b/drivers/of/base.c index e67b308819c9..567e6e1b7921 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -700,6 +700,9 @@ static struct device_node *__of_get_next_child(const struct device_node *node, { struct device_node *next; + if (!node) + return NULL; + next = prev ? prev->sibling : node->child; for (; next; next = next->sibling) if (of_node_get(next)) @@ -746,6 +749,9 @@ struct device_node *of_get_next_available_child(const struct device_node *node, 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) { -- cgit