/* SPDX-License-Identifier: MIT */ #include "devicetree.h" #include "libfdt/libfdt.h" void dt_parse_ranges(void *dt, int node, struct dt_ranges_tbl *ranges) { int len; const struct fdt_property *ranges_prop = fdt_get_property(dt, node, "ranges", &len); if (ranges_prop && len > 0) { int idx = 0; int num_entries = len / sizeof(fdt64_t); if (num_entries > DT_MAX_RANGES) num_entries = DT_MAX_RANGES; const fdt64_t *entry = (const fdt64_t *)ranges_prop->data; for (int i = 0; i < num_entries; ++i) { u64 start = fdt64_ld(entry++); u64 parent = fdt64_ld(entry++); u64 size = fdt64_ld(entry++); if (size) { ranges[idx].start = start; ranges[idx].parent = parent; ranges[idx].size = size; idx++; } } } } u64 dt_translate(struct dt_ranges_tbl *ranges, const fdt64_t *reg) { u64 addr = fdt64_ld(reg); for (int idx = 0; idx < DT_MAX_RANGES; ++idx) { if (ranges[idx].size == 0) break; if (addr >= ranges[idx].start && addr < ranges[idx].start + ranges[idx].size) return ranges[idx].parent - ranges[idx].start + addr; } return addr; } u64 dt_get_address(void *dt, int node) { int parent = fdt_parent_offset(dt, node); // find parent with "ranges" property while (parent >= 0) { if (fdt_getprop(dt, parent, "ranges", NULL)) break; parent = fdt_parent_offset(dt, parent); } if (parent < 0) return 0; // parse ranges for address translation struct dt_ranges_tbl ranges[DT_MAX_RANGES] = {0}; dt_parse_ranges(dt, parent, ranges); const fdt64_t *reg = fdt_getprop(dt, node, "reg", NULL); if (!reg) return 0; return dt_translate(ranges, reg); }