diff options
Diffstat (limited to 'common/oftree.c')
-rw-r--r-- | common/oftree.c | 228 |
1 files changed, 193 insertions, 35 deletions
diff --git a/common/oftree.c b/common/oftree.c index 09cb660212..c12b3cfb16 100644 --- a/common/oftree.c +++ b/common/oftree.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <common.h> #include <environment.h> #include <fdt.h> @@ -14,6 +16,9 @@ #include <bootsource.h> #include <i2c/i2c.h> #include <reset_source.h> +#include <watchdog.h> +#include <globalvar.h> +#include <magicvar.h> #define MAX_LEVEL 32 /* how deeply nested we will go */ @@ -113,20 +118,16 @@ void of_print_cmdline(struct device_node *root) cmdline = of_get_property(node, "bootargs", NULL); - printf("commandline: %s\n", cmdline); + pr_info("commandline: %s\n", cmdline); } static int of_fixup_bootargs_bootsource(struct device_node *root, struct device_node *chosen) { - char *alias_name = bootsource_get_alias_name(); struct device_node *bootsource; int ret = 0; - if (!alias_name) - return 0; - - bootsource = of_find_node_by_alias(root, alias_name); + bootsource = bootsource_of_node_get(root); /* * If kernel DTB doesn't have the appropriate alias set up, * give up and exit early. No error is reported. @@ -135,29 +136,89 @@ static int of_fixup_bootargs_bootsource(struct device_node *root, ret = of_set_property(chosen, "bootsource", bootsource->full_name, strlen(bootsource->full_name) + 1, true); - free(alias_name); return ret; } -static int of_fixup_bootargs(struct device_node *root, void *unused) +static void watchdog_build_bootargs(struct watchdog *watchdog, struct device_node *root) +{ + int alias_id; + char *buf; + + if (!watchdog) + return; + + alias_id = watchdog_get_alias_id_from(watchdog, root); + if (alias_id < 0) + return; + + buf = basprintf("systemd.watchdog-device=/dev/watchdog%d", alias_id); + if (!buf) + return; + + globalvar_add_simple("linux.bootargs.dyn.watchdog", buf); + free(buf); +} + +static int bootargs_append = 0; +BAREBOX_MAGICVAR(global.linux.bootargs_append, "append to original oftree bootargs"); + +static int of_write_bootargs(struct device_node *node) { - struct device_node *node; const char *str; - int err; - int instance = reset_source_get_instance(); - struct device_d *dev; + char *buf = NULL; + int ret; + + if (IS_ENABLED(CONFIG_SYSTEMD_OF_WATCHDOG)) + watchdog_build_bootargs(boot_get_enabled_watchdog(), of_get_parent(node)); str = linux_bootargs_get(); if (!str) return 0; + str = skip_spaces(str); + if (strlen(str) == 0) + return 0; + + if (bootargs_append) { + const char *oldstr; + + ret = of_property_read_string(node, "bootargs", &oldstr); + if (!ret) { + str = buf = basprintf("%s %s", oldstr, str); + if (!buf) + return -ENOMEM; + } + } + + ret = of_property_write_string(node, "bootargs", str); + free(buf); + return ret; +} + +static int of_fixup_bootargs(struct device_node *root, void *unused) +{ + struct device_node *node; + int err; + int instance = reset_source_get_instance(); + struct device *dev; + const char *serialno; + const char *compat; + + serialno = barebox_get_serial_number(); + if (serialno) + of_property_write_string(root, "serial-number", serialno); + + compat = barebox_get_of_machine_compatible(); + if (compat) + of_prepend_machine_compatible(root, compat); + node = of_create_node(root, "/chosen"); if (!node) return -ENOMEM; of_property_write_string(node, "barebox-version", release_string); - err = of_property_write_string(node, "bootargs", str); + err = of_write_bootargs(node); if (err) return err; @@ -167,10 +228,10 @@ static int of_fixup_bootargs(struct device_node *root, void *unused) dev = reset_source_get_device(); - if (dev && dev->device_node) { + if (dev && dev->of_node) { phandle phandle; - phandle = of_node_create_phandle(dev->device_node); + phandle = of_node_create_phandle(dev->of_node); err = of_property_write_u32(node, "reset-source-device", phandle); @@ -178,15 +239,76 @@ static int of_fixup_bootargs(struct device_node *root, void *unused) return err; } - return of_fixup_bootargs_bootsource(root, node); + err = of_fixup_bootargs_bootsource(root, node); + if (err) + return err; + + if (IS_ENABLED(CONFIG_RISCV)) { + const char *hartid; + + hartid = getenv("global.hartid"); + if (hartid) { + unsigned long id; + + err = kstrtoul(hartid, 10, &id); + if (!err) + err = of_property_write_u32(node, "boot-hartid", id); + } + } + + return err; } static int of_register_bootargs_fixup(void) { + globalvar_add_simple_bool("linux.bootargs_append", &bootargs_append); return of_register_fixup(of_fixup_bootargs, NULL); } late_initcall(of_register_bootargs_fixup); +int of_fixup_reserved_memory(struct device_node *root, void *_res) +{ + struct resource *res = _res; + struct device_node *node, *child; + struct property *pp; + unsigned addr_n_cells = sizeof(void *) / sizeof(__be32), + size_n_cells = sizeof(void *) / sizeof(__be32); + unsigned rangelen = 0; + __be32 reg[4]; + int ret; + + node = of_get_child_by_name(root, "reserved-memory") ?: of_new_node(root, "reserved-memory"); + + ret = of_property_read_u32(node, "#address-cells", &addr_n_cells); + if (ret) + of_property_write_u32(node, "#address-cells", addr_n_cells); + + ret = of_property_read_u32(node, "#size-cells", &size_n_cells); + if (ret) + of_property_write_u32(node, "#size-cells", size_n_cells); + + pp = of_find_property(node, "ranges", &rangelen); + if (!pp) { + of_new_property(node, "ranges", NULL, 0); + } else if (rangelen) { + pr_warn("reserved-memory ranges not 1:1 mapped. Aborting fixup\n"); + return -EINVAL; + } + + child = of_get_child_by_name(node, res->name) ?: of_new_node(node, res->name); + + if (res->flags & IORESOURCE_BUSY) + of_property_write_bool(child, "no-map", true); + + of_write_number(reg, res->start, addr_n_cells); + of_write_number(reg + addr_n_cells, resource_size(res), size_n_cells); + + of_set_property(child, "reg", reg, + (addr_n_cells + size_n_cells) * sizeof(*reg), true); + + return 0; +} + struct of_fixup_status_data { const char *path; bool status; @@ -223,13 +345,12 @@ int of_register_set_status_fixup(const char *path, bool status) return of_register_fixup(of_fixup_status, (void *)data); } -struct of_fixup { - int (*fixup)(struct device_node *, void *); - void *context; - struct list_head list; -}; +LIST_HEAD(of_fixup_list); -static LIST_HEAD(of_fixup_list); +static inline bool of_fixup_disabled(struct of_fixup *fixup) +{ + return fixup->disabled; +} int of_register_fixup(int (*fixup)(struct device_node *, void *), void *context) { @@ -266,19 +387,22 @@ int of_unregister_fixup(int (*fixup)(struct device_node *, void *), * Apply registered fixups for the given fdt. The fdt must have * enough free space to apply the fixups. */ -int of_fix_tree(struct device_node *node) +void of_fix_tree(struct device_node *node) { struct of_fixup *of_fixup; int ret; + of_overlay_load_firmware_clear(); + list_for_each_entry(of_fixup, &of_fixup_list, list) { + if (of_fixup_disabled(of_fixup)) + continue; + ret = of_fixup->fixup(node, of_fixup->context); if (ret) pr_warn("Failed to fixup node in %pS: %s\n", of_fixup->fixup, strerror(-ret)); } - - return 0; } /* @@ -287,10 +411,10 @@ int of_fix_tree(struct device_node *node) * It increases the size of the tree and applies the registered * fixups. */ -struct fdt_header *of_get_fixed_tree(struct device_node *node) +struct fdt_header *of_get_fixed_tree(const struct device_node *node) { - int ret; - struct fdt_header *fdt; + struct fdt_header *fdt = NULL; + struct device_node *np; if (!node) { node = of_get_root_node(); @@ -298,14 +422,17 @@ struct fdt_header *of_get_fixed_tree(struct device_node *node) return NULL; } - ret = of_fix_tree(node); - if (ret) - return NULL; + np = of_dup(node); - fdt = of_flatten_dtb(node); - if (!fdt) + if (!np) return NULL; + of_fix_tree(np); + + fdt = of_flatten_dtb(np); + + of_delete_node(np); + return fdt; } @@ -323,7 +450,7 @@ int of_autoenable_device_by_path(char *path) struct device_node *node; int ret; - node = of_find_node_by_name(NULL, path); + node = of_find_node_by_name_address(NULL, path); if (!node) node = of_find_node_by_path(path); @@ -360,7 +487,7 @@ int of_autoenable_i2c_by_component(char *path) if (!IS_ENABLED(CONFIG_I2C)) return -ENODEV; - node = of_find_node_by_name(NULL, path); + node = of_find_node_by_name_address(NULL, path); if (!node) node = of_find_node_by_path(path); if (!node) @@ -396,3 +523,34 @@ int of_autoenable_i2c_by_component(char *path) return ret; } + +int of_prepend_machine_compatible(struct device_node *root, const char *compat) +{ + int cclen = 0, clen = strlen(compat) + 1; + const char *curcompat; + void *buf; + + if (!root) { + root = of_get_root_node(); + if (!root) + return -ENODEV; + } + + if (of_device_is_compatible(root, compat)) + return 0; + + curcompat = of_get_property(root, "compatible", &cclen); + + buf = xzalloc(cclen + clen); + + memcpy(buf, compat, clen); + + if (curcompat) + memcpy(buf + clen, curcompat, cclen); + + of_set_property(root, "compatible", buf, cclen + clen, true); + + free(buf); + + return 0; +} |