summaryrefslogtreecommitdiff
path: root/lib/utils
diff options
context:
space:
mode:
authorAtish Patra <atish.patra@wdc.com>2020-10-24 01:47:39 +0300
committerAnup Patel <anup@brainfault.org>2020-10-23 14:49:21 +0300
commit74c0ea1e835de38e13be7c8e6440bde94bcc07d0 (patch)
treefd38a33f2578a3ecd854d92bd03d482b15b6afcd /lib/utils
parentfdf5d5c322533223146d3fac8a6ecfb47f68fb6f (diff)
downloadopensbi-74c0ea1e835de38e13be7c8e6440bde94bcc07d0.tar.xz
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.c50
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;