summaryrefslogtreecommitdiff
path: root/tools/src/devicetree.c
blob: f0c9193c2306713de70a81fd70be8117ab68446d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/* 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);
}