diff options
author | Atish Patra <atish.patra@wdc.com> | 2020-10-23 15:47:39 -0700 |
---|---|---|
committer | Anup Patel <anup@brainfault.org> | 2020-10-23 17:19:21 +0530 |
commit | 74c0ea1e835de38e13be7c8e6440bde94bcc07d0 (patch) | |
tree | fd38a33f2578a3ecd854d92bd03d482b15b6afcd /lib/utils | |
parent | fdf5d5c322533223146d3fac8a6ecfb47f68fb6f (diff) |
lib: utils: Implement "ranges" property parsing
The "reg" property in a device node may not be the correct address always.
If a parent node defines a "ranges" property, the child address need to be
translated with respect to parents address. If the ranges property is not
present, it will just use 1:1 translation.
Signed-off-by: Atish Patra <atish.patra@wdc.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
Diffstat (limited to 'lib/utils')
-rw-r--r-- | lib/utils/fdt/fdt_helper.c | 50 |
1 files changed, 48 insertions, 2 deletions
diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c index aec73a0..bf19ff9 100644 --- a/lib/utils/fdt/fdt_helper.c +++ b/lib/utils/fdt/fdt_helper.c @@ -71,10 +71,48 @@ int fdt_find_match(void *fdt, int startoff, return SBI_ENODEV; } +static int fdt_translate_address(void *fdt, uint64_t reg, int parent, + unsigned long *addr) +{ + int i, rlen; + int cell_addr, cell_size; + const fdt32_t *ranges; + uint64_t offset = 0, caddr = 0, paddr = 0, rsize = 0; + + cell_addr = fdt_address_cells(fdt, parent); + if (cell_addr < 1) + return SBI_ENODEV; + + cell_size = fdt_size_cells(fdt, parent); + if (cell_size < 0) + return SBI_ENODEV; + + ranges = fdt_getprop(fdt, parent, "ranges", &rlen); + if (ranges && rlen > 0) { + for (i = 0; i < cell_addr; i++) + caddr = (caddr << 32) | fdt32_to_cpu(*ranges++); + for (i = 0; i < cell_addr; i++) + paddr = (paddr << 32) | fdt32_to_cpu(*ranges++); + for (i = 0; i < cell_size; i++) + rsize = (rsize << 32) | fdt32_to_cpu(*ranges++); + if (reg < caddr || caddr >= (reg + rsize )) { + sbi_printf("invalid address translation\n"); + return SBI_ENODEV; + } + offset = reg - caddr; + *addr = paddr + offset; + } else { + /* No translation required */ + *addr = reg; + } + + return 0; +} + int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr, unsigned long *size) { - int parent, len, i; + int parent, len, i, rc; int cell_addr, cell_size; const fdt32_t *prop_addr, *prop_size; uint64_t temp = 0; @@ -98,7 +136,15 @@ int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr, if (addr) { for (i = 0; i < cell_addr; i++) temp = (temp << 32) | fdt32_to_cpu(*prop_addr++); - *addr = temp; + do { + if (parent < 0) + break; + rc = fdt_translate_address(fdt, temp, parent, addr); + if (rc) + break; + parent = fdt_parent_offset(fdt, parent); + temp = *addr; + } while (1); } temp = 0; |