aboutsummaryrefslogtreecommitdiff
path: root/machine/fdt.c
diff options
context:
space:
mode:
Diffstat (limited to 'machine/fdt.c')
-rw-r--r--machine/fdt.c269
1 files changed, 167 insertions, 102 deletions
diff --git a/machine/fdt.c b/machine/fdt.c
index a040e87..5257f76 100644
--- a/machine/fdt.c
+++ b/machine/fdt.c
@@ -13,48 +13,59 @@ static inline uint32_t bswap(uint32_t x)
static const uint32_t *fdt_scan_helper(
const uint32_t *lex,
const char *strings,
- const char *name,
- struct fdt_scan_node *parent,
- fdt_cb cb,
- void *extra)
+ struct fdt_scan_node *node,
+ const struct fdt_cb *cb)
{
- struct fdt_scan_node node;
+ struct fdt_scan_node child;
struct fdt_scan_prop prop;
+ int last = 0;
- node.parent = parent;
- node.base = lex;
- node.name = name;
- node.address_cells = 2;
- node.size_cells = 1;
- prop.node = &node;
+ child.parent = node;
+ child.address_cells = 2;
+ child.size_cells = 1;
+ prop.node = node;
while (1) {
switch (bswap(lex[0])) {
- case FDT_BEGIN_NODE: {
- const char *child_name = (const char *)(lex+1);
- lex = fdt_scan_helper(
- lex + 2 + strlen(child_name)/4,
- strings, child_name, &node, cb, extra);
+ case FDT_NOP: {
+ lex += 1;
break;
}
case FDT_PROP: {
+ assert (!last);
prop.name = strings + bswap(lex[2]);
prop.len = bswap(lex[1]);
prop.value = lex + 3;
- if (!strcmp(prop.name, "#address-cells")) { node.address_cells = bswap(lex[3]); }
- if (!strcmp(prop.name, "#size-cells")) { node.size_cells = bswap(lex[3]); }
+ if (node && !strcmp(prop.name, "#address-cells")) { node->address_cells = bswap(lex[3]); }
+ if (node && !strcmp(prop.name, "#size-cells")) { node->size_cells = bswap(lex[3]); }
lex += 3 + (prop.len+3)/4;
- cb(&prop, extra);
+ cb->prop(&prop, cb->extra);
break;
}
- case FDT_END_NODE: return lex + 1;
- case FDT_NOP: lex += 1; break;
- default: return lex; // FDT_END
+ case FDT_BEGIN_NODE: {
+ if (!last && node && cb->done) cb->done(node, cb->extra);
+ last = 1;
+ child.name = (const char *)(lex+1);
+ if (cb->open) cb->open(&child, cb->extra);
+ lex = fdt_scan_helper(
+ lex + 2 + strlen(child.name)/4,
+ strings, &child, cb);
+ if (cb->close) cb->close(&child, cb->extra);
+ break;
+ }
+ case FDT_END_NODE: {
+ if (!last && node && cb->done) cb->done(node, cb->extra);
+ return lex + 1;
+ }
+ default: { // FDT_END
+ if (!last && node && cb->done) cb->done(node, cb->extra);
+ return lex;
+ }
}
}
}
-void fdt_scan(uintptr_t fdt, fdt_cb cb, void *extra)
+void fdt_scan(uintptr_t fdt, const struct fdt_cb *cb)
{
struct fdt_header *header = (struct fdt_header *)fdt;
@@ -65,7 +76,7 @@ void fdt_scan(uintptr_t fdt, fdt_cb cb, void *extra)
const char *strings = (const char *)(fdt + bswap(header->off_dt_strings));
const uint32_t *lex = (const uint32_t *)(fdt + bswap(header->off_dt_struct));
- fdt_scan_helper(lex, strings, "/", 0, cb, extra);
+ fdt_scan_helper(lex, strings, 0, cb);
}
uint32_t fdt_size(uintptr_t fdt)
@@ -92,37 +103,71 @@ const uint32_t *fdt_get_size(const struct fdt_scan_node *node, const uint32_t *v
return value;
}
-static void find_mem(const struct fdt_scan_prop *prop, void *extra)
+//////////////////////////////////////////// MEMORY SCAN /////////////////////////////////////////
+
+struct mem_scan {
+ int memory;
+ const uint32_t *reg_value;
+ int reg_len;
+};
+
+static void mem_open(const struct fdt_scan_node *node, void *extra)
{
- const uint32_t **base = (const uint32_t **)extra;
+ struct mem_scan *scan = (struct mem_scan *)extra;
+ memset(scan, 0, sizeof(*scan));
+}
+
+static void mem_prop(const struct fdt_scan_prop *prop, void *extra)
+{
+ struct mem_scan *scan = (struct mem_scan *)extra;
if (!strcmp(prop->name, "device_type") && !strcmp((const char*)prop->value, "memory")) {
- *base = prop->node->base;
- } else if (!strcmp(prop->name, "reg") && prop->node->base == *base) {
- const uint32_t *value = prop->value;
- const uint32_t *end = value + prop->len/4;
- assert (prop->len % 4 == 0);
- while (end - value > 0) {
- uintptr_t base, size;
- value = fdt_get_address(prop->node->parent, value, &base);
- value = fdt_get_size (prop->node->parent, value, &size);
- if (base == DRAM_BASE) { mem_size = size; }
- }
- assert (end == value);
+ scan->memory = 1;
+ } else if (!strcmp(prop->name, "reg")) {
+ scan->reg_value = prop->value;
+ scan->reg_len = prop->len;
+ }
+}
+
+static void mem_done(const struct fdt_scan_node *node, void *extra)
+{
+ struct mem_scan *scan = (struct mem_scan *)extra;
+ const uint32_t *value = scan->reg_value;
+ const uint32_t *end = value + scan->reg_len/4;
+ uintptr_t self = (uintptr_t)mem_done;
+
+ if (!scan->memory) return;
+ assert (scan->reg_value && scan->reg_len % 4 == 0);
+
+ while (end - value > 0) {
+ uintptr_t base, size;
+ value = fdt_get_address(node->parent, value, &base);
+ value = fdt_get_size (node->parent, value, &size);
+ if (base <= self && self <= base + size) { mem_size = size; }
}
+ assert (end == value);
}
void query_mem(uintptr_t fdt)
{
+ struct fdt_cb cb;
+ struct mem_scan scan;
+
+ memset(&cb, 0, sizeof(cb));
+ cb.open = mem_open;
+ cb.prop = mem_prop;
+ cb.done = mem_done;
+ cb.extra = &scan;
+
mem_size = 0;
- const uint32_t *base = 0;
- fdt_scan(fdt, &find_mem, &base);
+ fdt_scan(fdt, &cb);
assert (mem_size > 0);
}
+///////////////////////////////////////////// HART SCAN //////////////////////////////////////////
+
static uint32_t hart_phandles[MAX_HARTS];
struct hart_scan {
- const uint32_t *base;
int cpu;
int controller;
int cells;
@@ -130,36 +175,17 @@ struct hart_scan {
uint32_t phandle;
};
-static void init_hart(struct hart_scan *scan, const uint32_t *base)
+static void hart_open(const struct fdt_scan_node *node, void *extra)
{
- scan->base = base;
- scan->cpu = 0;
- scan->controller = 0;
+ struct hart_scan *scan = (struct hart_scan *)extra;
+ memset(scan, 0, sizeof(*scan));
scan->cells = -1;
scan->hart = -1;
- scan->phandle = 0;
}
-static void done_hart(struct hart_scan *scan)
-{
- if (scan->cpu) {
- assert (scan->controller == 1);
- assert (scan->cells == 1);
- assert (scan->hart >= 0);
- if (scan->hart < MAX_HARTS) {
- hart_phandles[scan->hart] = scan->phandle;
- if (scan->hart >= num_harts) num_harts = scan->hart + 1;
- }
- }
-}
-
-static void count_harts(const struct fdt_scan_prop *prop, void *extra)
+static void hart_prop(const struct fdt_scan_prop *prop, void *extra)
{
struct hart_scan *scan = (struct hart_scan *)extra;
- if (prop->node->base != scan->base) {
- done_hart(scan);
- init_hart(scan, prop->node->base);
- }
if (!strcmp(prop->name, "device_type") && !strcmp((const char*)prop->value, "cpu")) {
scan->cpu = 1;
} else if (!strcmp(prop->name, "interrupt-controller")) {
@@ -175,72 +201,111 @@ static void count_harts(const struct fdt_scan_prop *prop, void *extra)
}
}
+static void hart_done(const struct fdt_scan_node *node, void *extra)
+{
+ struct hart_scan *scan = (struct hart_scan *)extra;
+
+ if (!scan->cpu) return;
+ assert (scan->controller == 1);
+ assert (scan->cells == 1);
+ assert (scan->hart >= 0);
+
+ if (scan->hart < MAX_HARTS) {
+ hart_phandles[scan->hart] = scan->phandle;
+ if (scan->hart >= num_harts) num_harts = scan->hart + 1;
+ }
+}
+
void query_harts(uintptr_t fdt)
{
+ struct fdt_cb cb;
struct hart_scan scan;
+
+ memset(&cb, 0, sizeof(cb));
+ cb.open = hart_open;
+ cb.prop = hart_prop;
+ cb.done = hart_done;
+ cb.extra = &scan;
+
num_harts = 0;
- init_hart(&scan, 0);
- fdt_scan(fdt, &count_harts, &scan);
- done_hart(&scan);
+ fdt_scan(fdt, &cb);
assert (num_harts > 0);
}
+///////////////////////////////////////////// CLINT SCAN /////////////////////////////////////////
+
struct clint_scan
{
- const uint32_t *base;
- uintptr_t address;
+ int compat;
+ uintptr_t reg;
+ const uint32_t *int_value;
+ int int_len;
+ int done;
};
-static void find_clint_base(const struct fdt_scan_prop *prop, void *extra)
+static void clint_open(const struct fdt_scan_node *node, void *extra)
{
struct clint_scan *scan = (struct clint_scan *)extra;
- if (!strcmp(prop->name, "compatible") && !strcmp((const char*)prop->value, "riscv,clint0"))
- scan->base = prop->node->base;
+ scan->compat = 0;
+ scan->reg = 0;
+ scan->int_value = 0;
}
-static void find_clint_reg(const struct fdt_scan_prop *prop, void *extra)
+static void clint_prop(const struct fdt_scan_prop *prop, void *extra)
{
struct clint_scan *scan = (struct clint_scan *)extra;
- if (!strcmp(prop->name, "reg") && scan->base == prop->node->base)
- fdt_get_address(prop->node->parent, prop->value, &scan->address);
+ if (!strcmp(prop->name, "compatible") && !strcmp((const char*)prop->value, "riscv,clint0")) {
+ scan->compat = 1;
+ } else if (!strcmp(prop->name, "reg")) {
+ fdt_get_address(prop->node->parent, prop->value, &scan->reg);
+ } else if (!strcmp(prop->name, "interrupts-extended")) {
+ scan->int_value = prop->value;
+ scan->int_len = prop->len;
+ }
}
-static void setup_clint(const struct fdt_scan_prop *prop, void *extra)
+static void clint_done(const struct fdt_scan_node *node, void *extra)
{
struct clint_scan *scan = (struct clint_scan *)extra;
- if (!strcmp(prop->name, "interrupts-extended") && scan->base == prop->node->base) {
- const uint32_t *value = prop->value;
- const uint32_t *end = value + prop->len/4;
- assert (prop->len % 16 == 0);
- for (int index = 0; end - value > 0; ++index) {
- uint32_t phandle = bswap(value[0]);
- int hart;
- for (hart = 0; hart < MAX_HARTS; ++hart)
- if (hart_phandles[hart] == phandle)
- break;
- if (hart < MAX_HARTS) {
- hls_t *hls = hls_init(hart);
- hls->ipi = (void*)(scan->address + index * 4);
- hls->timecmp = (void*)(scan->address + 0x4000 + (index * 8));
- *hls->ipi = 1; // wakeup the hart
- }
- value += 4;
+ const uint32_t *value = scan->int_value;
+ const uint32_t *end = value + scan->int_len/4;
+
+ if (!scan->compat) return;
+ assert (scan->reg != 0);
+ assert (scan->int_value && scan->int_len % 16 == 0);
+ assert (!scan->done); // only one clint
+
+ scan->done = 1;
+ mtime = (void*)(scan->reg + 0xbff8);
+
+ for (int index = 0; end - value > 0; ++index) {
+ uint32_t phandle = bswap(value[0]);
+ int hart;
+ for (hart = 0; hart < MAX_HARTS; ++hart)
+ if (hart_phandles[hart] == phandle)
+ break;
+ if (hart < MAX_HARTS) {
+ hls_t *hls = hls_init(hart);
+ hls->ipi = (void*)(scan->reg + index * 4);
+ hls->timecmp = (void*)(scan->reg + 0x4000 + (index * 8));
+ *hls->ipi = 1; // wakeup the hart
}
+ value += 4;
}
}
void query_clint(uintptr_t fdt)
{
+ struct fdt_cb cb;
struct clint_scan scan;
- scan.base = 0;
- scan.address = 0;
-
- fdt_scan(fdt, &find_clint_base, &scan);
- assert (scan.base != 0);
- fdt_scan(fdt, &find_clint_reg, &scan);
- assert (scan.address != 0);
+ memset(&cb, 0, sizeof(cb));
+ cb.open = clint_open;
+ cb.prop = clint_prop;
+ cb.done = clint_done;
+ cb.extra = &scan;
- mtime = (void*)(scan.address + 0xbff8);
- fdt_scan(fdt, &setup_clint, &scan);
+ scan.done = 0;
+ fdt_scan(fdt, &cb);
+ assert (scan.done);
}