[U-Boot] [RFC] fdt expert advice needed

Hi FDT experts,
some time ago I posted a patch to fix U-Boot's approach to set all ns16550 compatible nodes' clock-frequency property in ppc4xx device tree's:
do_fixup_by_compat_u32(blob, "ns16550", "clock-frequency", gd->uart_clk, 1);
Typically this works fine. It is intended to configure all CPU internal UART's clock. We have some additional UARTs attached to an external bus. From the device tree's point of view they have a different path, but are also ns16550 compatible.
So the above line will also overwrite thier clock-frequency. This must not be done, because they have a separate external clock and the value in the device tree is correct!
Now I want to replace the above code to only touch the ns16550 compatible nodes that are direct childs of /plb/opb. Not those under /plb/ebc and even not those under /plb/opb/ebc. This is not easy to do with the fdt API. Below you find my first and dirty hack.
Isn't there a more simple way to do so? Don't bother me about the printf, variable names etc. It's just for discussion.
Please tell me if I have to explain my code :-)
Matthias
From c915c5a8f38940cf0a5047863fb953712049e0be Mon Sep 17 00:00:00 2001 From: Matthias Fuchs matthias.fuchs@esd-electronics.com Date: Fri, 30 Jan 2009 11:00:33 +0100 Subject: [PATCH] ppc4xx: Only fixup opb attached (CPU internal) UARTs
Signed-off-by: Matthias Fuchs matthias.fuchs@esd-electronics.com --- cpu/ppc4xx/fdt.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 65 insertions(+), 0 deletions(-)
diff --git a/cpu/ppc4xx/fdt.c b/cpu/ppc4xx/fdt.c index c55e1cf..f668ac6 100644 --- a/cpu/ppc4xx/fdt.c +++ b/cpu/ppc4xx/fdt.c @@ -110,9 +110,48 @@ void fdt_pcie_setup(void *blob) } }
+int ft_path_end_offset(void *blob, char *path) +{ + int start, stop; + int off; + uint32_t tag; + int nextoffset; + int level = 0; + + off = fdt_path_offset(blob, path); + if (off < 0) + return -1; + + stop = start = off; + + while (stop == start) { + tag = fdt_next_tag(blob, off, &nextoffset); + switch(tag) { + case FDT_BEGIN_NODE: + level++; + break; + + case FDT_END_NODE: + if (level == 1) + stop = off; + level--; + break; + + default: + break; + } + off = nextoffset; + } + + return stop; +} + void ft_cpu_setup(void *blob, bd_t *bd) { sys_info_t sys_info; + int start, stop; + int start2, stop2; + int off;
get_sys_info(&sys_info);
@@ -135,7 +174,33 @@ void ft_cpu_setup(void *blob, bd_t *bd) /* * Setup all baudrates for the UARTs */ +#if 0 do_fixup_by_compat_u32(blob, "ns16550", "clock-frequency", gd->uart_clk, 1); +#endif + start = fdt_path_offset(blob, "/plb/opb"); + stop = ft_path_end_offset(blob, "/plb/opb"); + printf("/plb/opb start=%d, stop=%d\n", start, stop); + + start2 = fdt_path_offset(blob, "/plb/opb/ebc"); + stop2 = ft_path_end_offset(blob, "/plb/opb/ebc"); + printf("/plb/opb/obc start=%d, stop=%d\n", start, stop); + + off = start; + while (off != -FDT_ERR_NOTFOUND) { + off = fdt_node_offset_by_compatible(blob, off, "ns16550"); + printf("off=%d\n", off); + if ((off > start) && (off < stop)) { + if ((start2 < 0) || (off < start2) || (off > stop2)) { + printf("ns16550 directly on OPB\n"); + if (fdt_get_property(blob, off, + "clock-frequency", + 0) != NULL) + fdt_setprop(blob, off, + "clock-frequency", + (void*)&(gd->uart_clk), 4); + } + } + }
/* * Fixup all ethernet nodes

Matthias Fuchs wrote:
Now I want to replace the above code to only touch the ns16550 compatible nodes that are direct childs of /plb/opb. Not those under /plb/ebc and even not those under /plb/opb/ebc. This is not easy to do with the fdt API. Below you find my first and dirty hack.
Isn't there a more simple way to do so? Don't bother me about the printf, variable names etc. It's just for discussion.
Take the node that you want to search under (/plb/opb), and pass it to fdt_next_node(), with an initial depth of zero. Continue until depth returns to zero (or less).
-Scott

Scott Wood wrote:
Matthias Fuchs wrote:
Now I want to replace the above code to only touch the ns16550 compatible nodes that are direct childs of /plb/opb. Not those under /plb/ebc and even not those under /plb/opb/ebc. This is not easy to do with the fdt API. Below you find my first and dirty hack.
Isn't there a more simple way to do so? Don't bother me about the printf, variable names etc. It's just for discussion.
Take the node that you want to search under (/plb/opb), and pass it to fdt_next_node(), with an initial depth of zero. Continue until depth returns to zero (or less).
I just saw that you want to restrict it to direct children -- for that, ignore any nodes where depth != 1.
-Scott

Hi Scott,
thanks for your hint. Please see my updated code below. Do you think it's ok this way?
Matthias
--- cpu/ppc4xx/fdt.c | 24 ++++++++++++++++++++++-- 1 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/cpu/ppc4xx/fdt.c b/cpu/ppc4xx/fdt.c index c55e1cf..f9ff560 100644 --- a/cpu/ppc4xx/fdt.c +++ b/cpu/ppc4xx/fdt.c @@ -113,6 +113,7 @@ void fdt_pcie_setup(void *blob) void ft_cpu_setup(void *blob, bd_t *bd) { sys_info_t sys_info; + int off, ndepth = 0;
get_sys_info(&sys_info);
@@ -133,9 +134,28 @@ void ft_cpu_setup(void *blob, bd_t *bd) fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize);
/* - * Setup all baudrates for the UARTs + * Setup all UART clocks for CPU internal UARTs + * (only these UARTs are definetely clocked by gd->uart_clk) + * + * These UARTs are direct childs of /plb/opb. This code + * does not touch any UARTs that are connected to the ebc. */ - do_fixup_by_compat_u32(blob, "ns16550", "clock-frequency", gd->uart_clk, 1); + off = fdt_path_offset(blob, "/plb/opb"); + while ((off = fdt_next_node(blob, off, &ndepth)) >= 0) { + /* + * process all sub nodes and stop when we are back + * at the starting depth + */ + if (ndepth == 0) + break; + + /* only update direct childs */ + if ((ndepth == 1) && + (fdt_node_check_compatible(blob, off, "ns16550") == 0)) + fdt_setprop(blob, off, + "clock-frequency", + (void*)&(gd->uart_clk), 4); + }
/* * Fixup all ethernet nodes

On Sun, Feb 01, 2009 at 10:30:20PM +0100, Matthias Fuchs wrote:
- do_fixup_by_compat_u32(blob, "ns16550", "clock-frequency", gd->uart_clk, 1);
- off = fdt_path_offset(blob, "/plb/opb");
- while ((off = fdt_next_node(blob, off, &ndepth)) >= 0) {
/*
* process all sub nodes and stop when we are back
* at the starting depth
*/
if (ndepth == 0)
break;
Should be ndepth <= 0; it can be negative if opb is the last node under plb to be visited.
Looks OK otherwise.
-Scott
participants (2)
-
Matthias Fuchs
-
Scott Wood