diff options
-rw-r--r-- | common/fdt_support.c | 94 | ||||
-rw-r--r-- | include/fdt_support.h | 2 |
2 files changed, 96 insertions, 0 deletions
diff --git a/common/fdt_support.c b/common/fdt_support.c index cc0bf76..fcd2523 100644 --- a/common/fdt_support.c +++ b/common/fdt_support.c @@ -1435,3 +1435,97 @@ u64 fdt_get_base_address(void *fdt, int node) return prop ? fdt_translate_address(fdt, node, prop + naddr) : 0; } + +/* + * Read a property of size <prop_len>. Currently only supports 1 or 2 cells. + */ +static int fdt_read_prop(const fdt32_t *prop, int prop_len, int cell_off, + uint64_t *val, int cells) +{ + const fdt32_t *prop32 = &prop[cell_off]; + const fdt64_t *prop64 = (const fdt64_t *)&prop[cell_off]; + + if ((cell_off + cells) > prop_len) + return -FDT_ERR_NOSPACE; + + switch (cells) { + case 1: + *val = fdt32_to_cpu(*prop32); + break; + case 2: + *val = fdt64_to_cpu(*prop64); + break; + default: + return -FDT_ERR_NOSPACE; + } + + return 0; +} + +/** + * fdt_read_range - Read a node's n'th range property + * + * @fdt: ptr to device tree + * @node: offset of node + * @n: range index + * @child_addr: pointer to storage for the "child address" field + * @addr: pointer to storage for the CPU view translated physical start + * @len: pointer to storage for the range length + * + * Convenience function that reads and interprets a specific range out of + * a number of the "ranges" property array. + */ +int fdt_read_range(void *fdt, int node, int n, uint64_t *child_addr, + uint64_t *addr, uint64_t *len) +{ + int pnode = fdt_parent_offset(fdt, node); + const fdt32_t *ranges; + int pacells; + int acells; + int scells; + int ranges_len; + int cell = 0; + int r = 0; + + /* + * The "ranges" property is an array of + * { <child address> <parent address> <size in child address space> } + * + * All 3 elements can span a diffent number of cells. Fetch their size. + */ + pacells = fdt_getprop_u32_default_node(fdt, pnode, 0, "#address-cells", 1); + acells = fdt_getprop_u32_default_node(fdt, node, 0, "#address-cells", 1); + scells = fdt_getprop_u32_default_node(fdt, node, 0, "#size-cells", 1); + + /* Now try to get the ranges property */ + ranges = fdt_getprop(fdt, node, "ranges", &ranges_len); + if (!ranges) + return -FDT_ERR_NOTFOUND; + ranges_len /= sizeof(uint32_t); + + /* Jump to the n'th entry */ + cell = n * (pacells + acells + scells); + + /* Read <child address> */ + if (child_addr) { + r = fdt_read_prop(ranges, ranges_len, cell, child_addr, + acells); + if (r) + return r; + } + cell += acells; + + /* Read <parent address> */ + if (addr) + *addr = fdt_translate_address(fdt, node, ranges + cell); + cell += pacells; + + /* Read <size in child address space> */ + if (len) { + r = fdt_read_prop(ranges, ranges_len, cell, len, scells); + if (r) + return r; + } + + return 0; +} diff --git a/include/fdt_support.h b/include/fdt_support.h index 61383dd..b77a0f1 100644 --- a/include/fdt_support.h +++ b/include/fdt_support.h @@ -87,6 +87,8 @@ int fdt_add_edid(void *blob, const char *compat, unsigned char *buf); int fdt_verify_alias_address(void *fdt, int anode, const char *alias, u64 addr); u64 fdt_get_base_address(void *fdt, int node); +int fdt_read_range(void *fdt, int node, int n, uint64_t *child_addr, + uint64_t *addr, uint64_t *len); enum fdt_status { FDT_STATUS_OKAY, |