diff options
author | Tom Rini <trini@konsulko.com> | 2022-09-30 15:52:10 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2022-09-30 15:52:10 -0400 |
commit | 6ee6e15975cad3c99fad3a66223f3fd9287a369b (patch) | |
tree | 2a1798758c85d3010640cf6bacb1ad7caaa29133 /test/dm/ofnode.c | |
parent | 01c88e3dcd667281cf3aa6f6b47f90900177aff9 (diff) | |
parent | db1ef1e12b993275e09f116ebc3d23d675c7e28c (diff) |
Merge branch '2022-09-29-dm-core-support-multiple-device-trees-in-ofnode' into next
To quote the author:
At present the ofnode interface is somewhat limited, in that it cannot
access the device tree provided by the OS, only the one used by U-Boot
itself (assuming these are separate). This prevents using ofnode functions
to handle device tree fixups, i.e. ft_board_setup() and the like.
The ofnode interface was introduced to allow a consistent API to access
the device tree, whether a flat tree or a live tree (OF_LIVE) is in use.
With the flat tree, adding nodes and properties at the start of the tree
(as often happens when writing to the /chosen node) requires copying a
lot of data for each operation. With live tree, such operations are
quite a bit faster, since there is no memory copying required. This has to
be weighed against the required memory allocation with OF_LIVE, as well
as the cost of unflattening and flattening the device tree which U-Boot
is running.
This series enables support for access to multiple device trees with the
ofnode interface. This is already available to some extent with OF_LIVE,
but some of the ofnode functions need changes to allow the tree to be
specified.
The mechanism works by using the top 1-4 bits of the device tree offset.
The sign bit is not affected, since negative values must be supported.
With this implemented, it becomes possible to use the ofnode interface
to do device tree fixups. The only current user is the EVT_FT_FIXUP
event.
This has two main benefits:
- ofnode can now be used everywhere, in preference to the libfdt calls
- live tree can eventually be used everywhere, with potential speed
improvements when larger number of fixups are used
This series is only a step along the way. Firstly, while it is possible
to access the 'fix-up' tree using OF_LIVE, most of the fixup functions use
flat tree directly, rather than the ofnode interface. These need to be
updated. Also the tree must be flattened again before it is passed to the
OS. This is not currently implemented.
With OFNODE_MULTI_TREE disabled this has almost no effect on code size:
around 4 bytes if EVENT is enabled, 0 if not. With the feature enabled,
the increase is around 700 bytes, e.g. on venice2:
$ buildman -b ofn2a venice2 -sS --step 0
Summary of 2 commits for 1 boards (1 thread, 64 jobs per thread)
01: image: Drop some other #ifdefs in image-board.c
arm: w+ venice2
48: wip
arm: (for 1/1 boards) all +668.0 text +668.0
This size increase is not too bad, considering the extra functionality,
but is too large to enable everywhere. So for now this features needs to
be opt-in only, based on EVENT.
Diffstat (limited to 'test/dm/ofnode.c')
-rw-r--r-- | test/dm/ofnode.c | 696 |
1 files changed, 665 insertions, 31 deletions
diff --git a/test/dm/ofnode.c b/test/dm/ofnode.c index f80993f8927..41811ec3bb5 100644 --- a/test/dm/ofnode.c +++ b/test/dm/ofnode.c @@ -1,4 +1,20 @@ // SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022 Google LLC + * + * There are two types of tests in this file: + * - normal ones which act on the control FDT (gd->fdt_blob or gd->of_root) + * - 'other' ones which act on the 'other' FDT (other.dts) + * + * The 'other' ones have an _ot suffix. + * + * The latter are used to check behaviour with multiple device trees, + * particularly with flat tree, where a tree ID is included in ofnode as part of + * the node offset. These tests are typically just for making sure that the + * offset makes it to libfdt correctly and that the resulting return value is + * correctly turned into an ofnode. The 'other' tests do not fully check the + * behaviour of each ofnode function, since that is done by the normal ones. + */ #include <common.h> #include <dm.h> @@ -13,6 +29,68 @@ #include <test/test.h> #include <test/ut.h> +/** + * get_other_oftree() - Convert a flat tree into an oftree object + * + * @uts: Test state + * @return: oftree object for the 'other' FDT (see sandbox' other.dts) + */ +oftree get_other_oftree(struct unit_test_state *uts) +{ + oftree tree; + + if (of_live_active()) + tree = oftree_from_np(uts->of_other); + else + tree = oftree_from_fdt(uts->other_fdt); + + /* An invalid tree may cause failure or crashes */ + if (!oftree_valid(tree)) + ut_reportf("test needs the UT_TESTF_OTHER_FDT flag"); + + return tree; +} + +/** + * get_oftree() - Convert a flat tree into an oftree object + * + * @uts: Test state + * @fdt: Pointer to flat tree + * @treep: Returns the tree, on success + * Return: 0 if OK, 1 if the tree failed to unflatten, -EOVERFLOW if there are + * too many flat trees to allow another one to be registers (see + * oftree_ensure()) + */ +int get_oftree(struct unit_test_state *uts, void *fdt, oftree *treep) +{ + oftree tree; + + if (of_live_active()) { + struct device_node *root; + + ut_assertok(unflatten_device_tree(fdt, &root)); + tree = oftree_from_np(root); + } else { + tree = oftree_from_fdt(fdt); + if (!oftree_valid(tree)) + return -EOVERFLOW; + } + *treep = tree; + + return 0; +} + +/** + * free_oftree() - Free memory used by get_oftree() + * + * @tree: Tree to free + */ +void free_oftree(oftree tree) +{ + if (of_live_active()) + free(tree.np); +} + static int dm_test_ofnode_compatible(struct unit_test_state *uts) { ofnode root_node = ofnode_path("/"); @@ -22,7 +100,21 @@ static int dm_test_ofnode_compatible(struct unit_test_state *uts) return 0; } -DM_TEST(dm_test_ofnode_compatible, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +DM_TEST(dm_test_ofnode_compatible, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* check ofnode_device_is_compatible() with the 'other' FDT */ +static int dm_test_ofnode_compatible_ot(struct unit_test_state *uts) +{ + oftree otree = get_other_oftree(uts); + ofnode oroot = oftree_root(otree); + + ut_assert(ofnode_valid(oroot)); + ut_assert(ofnode_device_is_compatible(oroot, "sandbox-other")); + + return 0; +} +DM_TEST(dm_test_ofnode_compatible_ot, UT_TESTF_OTHER_FDT); static int dm_test_ofnode_get_by_phandle(struct unit_test_state *uts) { @@ -36,37 +128,76 @@ static int dm_test_ofnode_get_by_phandle(struct unit_test_state *uts) /* test unknown phandle */ ut_assert(!ofnode_valid(ofnode_get_by_phandle(0x1000000))); + ut_assert(ofnode_valid(oftree_get_by_phandle(oftree_default(), 1))); + return 0; } DM_TEST(dm_test_ofnode_get_by_phandle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); -static int dm_test_ofnode_by_prop_value(struct unit_test_state *uts) +static int dm_test_ofnode_get_by_phandle_ot(struct unit_test_state *uts) { - const char propname[] = "compatible"; - const char propval[] = "denx,u-boot-fdt-test"; + oftree otree = get_other_oftree(uts); + ofnode node; + + ut_assert(ofnode_valid(oftree_get_by_phandle(oftree_default(), 1))); + node = oftree_get_by_phandle(otree, 1); + ut_assert(ofnode_valid(node)); + ut_asserteq_str("target", ofnode_get_name(node)); + + return 0; +} +DM_TEST(dm_test_ofnode_get_by_phandle_ot, UT_TESTF_OTHER_FDT); + +static int check_prop_values(struct unit_test_state *uts, ofnode start, + const char *propname, const char *propval, + int expect_count) +{ + int proplen = strlen(propval) + 1; const char *str; - ofnode node = ofnode_null(); + ofnode node; + int count; /* Find first matching node, there should be at least one */ - node = ofnode_by_prop_value(node, propname, propval, sizeof(propval)); + node = ofnode_by_prop_value(start, propname, propval, proplen); ut_assert(ofnode_valid(node)); str = ofnode_read_string(node, propname); ut_assert(str && !strcmp(str, propval)); /* Find the rest of the matching nodes */ + count = 1; while (true) { - node = ofnode_by_prop_value(node, propname, propval, - sizeof(propval)); + node = ofnode_by_prop_value(node, propname, propval, proplen); if (!ofnode_valid(node)) break; str = ofnode_read_string(node, propname); - ut_assert(str && !strcmp(str, propval)); + ut_asserteq_str(propval, str); + count++; } + ut_asserteq(expect_count, count); + + return 0; +} + +static int dm_test_ofnode_by_prop_value(struct unit_test_state *uts) +{ + ut_assertok(check_prop_values(uts, ofnode_null(), "compatible", + "denx,u-boot-fdt-test", 11)); return 0; } DM_TEST(dm_test_ofnode_by_prop_value, UT_TESTF_SCAN_FDT); +static int dm_test_ofnode_by_prop_value_ot(struct unit_test_state *uts) +{ + oftree otree = get_other_oftree(uts); + + ut_assertok(check_prop_values(uts, oftree_root(otree), "str-prop", + "other", 2)); + + return 0; +} +DM_TEST(dm_test_ofnode_by_prop_value_ot, UT_TESTF_OTHER_FDT); + static int dm_test_ofnode_fmap(struct unit_test_state *uts) { struct fmap_entry entry; @@ -108,6 +239,25 @@ static int dm_test_ofnode_read(struct unit_test_state *uts) } DM_TEST(dm_test_ofnode_read, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +static int dm_test_ofnode_read_ot(struct unit_test_state *uts) +{ + oftree otree = get_other_oftree(uts); + const char *val; + ofnode node; + int size; + + node = oftree_path(otree, "/node/subnode"); + ut_assert(ofnode_valid(node)); + + val = ofnode_read_prop(node, "str-prop", &size); + ut_assertnonnull(val); + ut_asserteq_str("other", val); + ut_asserteq(6, size); + + return 0; +} +DM_TEST(dm_test_ofnode_read_ot, UT_TESTF_OTHER_FDT); + static int dm_test_ofnode_phandle(struct unit_test_state *uts) { struct ofnode_phandle_args args; @@ -183,6 +333,34 @@ static int dm_test_ofnode_phandle(struct unit_test_state *uts) } DM_TEST(dm_test_ofnode_phandle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +static int dm_test_ofnode_phandle_ot(struct unit_test_state *uts) +{ + oftree otree = get_other_oftree(uts); + struct ofnode_phandle_args args; + ofnode node; + int ret; + + node = oftree_path(otree, "/node"); + + /* Test ofnode_count_phandle_with_args with cell name */ + ret = ofnode_count_phandle_with_args(node, "missing", "#gpio-cells", 0); + ut_asserteq(-ENOENT, ret); + ret = ofnode_count_phandle_with_args(node, "target", "#invalid", 0); + ut_asserteq(-EINVAL, ret); + ret = ofnode_count_phandle_with_args(node, "target", "#gpio-cells", 0); + ut_asserteq(1, ret); + + ret = ofnode_parse_phandle_with_args(node, "target", "#gpio-cells", 0, + 0, &args); + ut_assertok(ret); + ut_asserteq(2, args.args_count); + ut_asserteq(3, args.args[0]); + ut_asserteq(4, args.args[1]); + + return 0; +} +DM_TEST(dm_test_ofnode_phandle_ot, UT_TESTF_OTHER_FDT); + static int dm_test_ofnode_read_chosen(struct unit_test_state *uts) { const char *str; @@ -255,6 +433,27 @@ static int dm_test_ofnode_get_child_count(struct unit_test_state *uts) DM_TEST(dm_test_ofnode_get_child_count, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +static int dm_test_ofnode_get_child_count_ot(struct unit_test_state *uts) +{ + oftree otree = get_other_oftree(uts); + ofnode node, child_node; + u32 val; + + node = oftree_path(otree, "/node"); + ut_assert(ofnode_valid(node)); + + val = ofnode_get_child_count(node); + ut_asserteq(2, val); + + child_node = ofnode_first_subnode(node); + ut_assert(ofnode_valid(child_node)); + val = ofnode_get_child_count(child_node); + ut_asserteq(0, val); + + return 0; +} +DM_TEST(dm_test_ofnode_get_child_count_ot, UT_TESTF_OTHER_FDT); + static int dm_test_ofnode_is_enabled(struct unit_test_state *uts) { ofnode root_node = ofnode_path("/"); @@ -267,6 +466,19 @@ static int dm_test_ofnode_is_enabled(struct unit_test_state *uts) } DM_TEST(dm_test_ofnode_is_enabled, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +static int dm_test_ofnode_is_enabled_ot(struct unit_test_state *uts) +{ + oftree otree = get_other_oftree(uts); + ofnode root_node = oftree_root(otree); + ofnode node = oftree_path(otree, "/target"); + + ut_assert(ofnode_is_enabled(root_node)); + ut_assert(!ofnode_is_enabled(node)); + + return 0; +} +DM_TEST(dm_test_ofnode_is_enabled_ot, UT_TESTF_OTHER_FDT); + static int dm_test_ofnode_get_reg(struct unit_test_state *uts) { ofnode node; @@ -303,27 +515,59 @@ static int dm_test_ofnode_get_reg(struct unit_test_state *uts) } DM_TEST(dm_test_ofnode_get_reg, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +static int dm_test_ofnode_get_reg_ot(struct unit_test_state *uts) +{ + oftree otree = get_other_oftree(uts); + ofnode node = oftree_path(otree, "/target"); + fdt_addr_t addr; + + addr = ofnode_get_addr(node); + ut_asserteq(0x8000, addr); + + return 0; +} +DM_TEST(dm_test_ofnode_get_reg_ot, UT_TESTF_OTHER_FDT); + static int dm_test_ofnode_get_path(struct unit_test_state *uts) { const char *path = "/translation-test@8000/noxlatebus@3,300/dev@42"; char buf[64]; ofnode node; - int res; node = ofnode_path(path); ut_assert(ofnode_valid(node)); - res = ofnode_get_path(node, buf, 64); - ut_asserteq(0, res); + ut_assertok(ofnode_get_path(node, buf, sizeof(buf))); ut_asserteq_str(path, buf); - res = ofnode_get_path(node, buf, 32); - ut_asserteq(-ENOSPC, res); + ut_asserteq(-ENOSPC, ofnode_get_path(node, buf, 32)); + + ut_assertok(ofnode_get_path(ofnode_root(), buf, 32)); + ut_asserteq_str("/", buf); return 0; } DM_TEST(dm_test_ofnode_get_path, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +static int dm_test_ofnode_get_path_ot(struct unit_test_state *uts) +{ + oftree otree = get_other_oftree(uts); + const char *path = "/node/subnode"; + ofnode node = oftree_path(otree, path); + char buf[64]; + + ut_assert(ofnode_valid(node)); + + ut_assertok(ofnode_get_path(node, buf, sizeof(buf))); + ut_asserteq_str(path, buf); + + ut_assertok(ofnode_get_path(oftree_root(otree), buf, 32)); + ut_asserteq_str("/", buf); + + return 0; +} +DM_TEST(dm_test_ofnode_get_path_ot, UT_TESTF_OTHER_FDT); + static int dm_test_ofnode_conf(struct unit_test_state *uts) { ut_assert(!ofnode_conf_read_bool("missing")); @@ -483,13 +727,17 @@ DM_TEST(dm_test_ofnode_get_phy, 0); * @uts: Test state * @fdt: Place to write FDT * @size: Maximum size of space for fdt + * @id: id value to add to the tree ('id' property in root node) */ -static int make_ofnode_fdt(struct unit_test_state *uts, void *fdt, int size) +static int make_ofnode_fdt(struct unit_test_state *uts, void *fdt, int size, + int id) { ut_assertok(fdt_create(fdt, size)); ut_assertok(fdt_finish_reservemap(fdt)); ut_assert(fdt_begin_node(fdt, "") >= 0); + ut_assertok(fdt_property_u32(fdt, "id", id)); + ut_assert(fdt_begin_node(fdt, "aliases") >= 0); ut_assertok(fdt_property_string(fdt, "mmc0", "/new-mmc")); ut_assertok(fdt_end_node(fdt)); @@ -505,37 +753,41 @@ static int make_ofnode_fdt(struct unit_test_state *uts, void *fdt, int size) static int dm_test_ofnode_root(struct unit_test_state *uts) { - struct device_node *root = NULL; char fdt[256]; oftree tree; ofnode node; + int ret; /* Check that aliases work on the control FDT */ node = ofnode_get_aliases_node("ethernet3"); ut_assert(ofnode_valid(node)); ut_asserteq_str("sbe5", ofnode_get_name(node)); - ut_assertok(make_ofnode_fdt(uts, fdt, sizeof(fdt))); - if (of_live_active()) { - ut_assertok(unflatten_device_tree(fdt, &root)); - tree.np = root; - } else { - tree.fdt = fdt; - } + ut_assert(!oftree_valid(oftree_null())); + + ut_assertok(make_ofnode_fdt(uts, fdt, sizeof(fdt), 0)); + ret = get_oftree(uts, fdt, &tree); + + /* skip the rest of this test if multiple FDTs are not supported */ + if (ret == -EOVERFLOW) + return 0; + + ut_assertok(ret); + ut_assert(oftree_valid(tree)); /* Make sure they don't work on this new tree */ - node = ofnode_path_root(tree, "mmc0"); + node = oftree_path(tree, "mmc0"); ut_assert(!ofnode_valid(node)); /* It should appear in the new tree */ - node = ofnode_path_root(tree, "/new-mmc"); + node = oftree_path(tree, "/new-mmc"); ut_assert(ofnode_valid(node)); /* ...and not in the control FDT */ - node = ofnode_path_root(oftree_default(), "/new-mmc"); + node = oftree_path(oftree_default(), "/new-mmc"); ut_assert(!ofnode_valid(node)); - free(root); + free_oftree(tree); return 0; } @@ -570,7 +822,8 @@ static int dm_test_ofnode_livetree_writing(struct unit_test_state *uts) ut_asserteq_64(FDT_ADDR_T_NONE, dev_read_addr(dev)); /* reg = 0x42, size = 0x100 */ ut_assertok(ofnode_write_prop(node, "reg", - "\x00\x00\x00\x42\x00\x00\x01\x00", 8)); + "\x00\x00\x00\x42\x00\x00\x01\x00", 8, + false)); ut_asserteq(0x42, dev_read_addr(dev)); /* Test disabling devices */ @@ -584,11 +837,71 @@ static int dm_test_ofnode_livetree_writing(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_ofnode_livetree_writing, - UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_LIVE_OR_FLAT); + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int check_write_prop(struct unit_test_state *uts, ofnode node) +{ + char prop[] = "middle-name"; + char name[10]; + int len; + + strcpy(name, "cecil"); + len = strlen(name) + 1; + ut_assertok(ofnode_write_prop(node, prop, name, len, false)); + ut_asserteq_str(name, ofnode_read_string(node, prop)); + + /* change the underlying value, this should mess up the live tree */ + strcpy(name, "tony"); + if (of_live_active()) { + ut_asserteq_str(name, ofnode_read_string(node, prop)); + } else { + ut_asserteq_str("cecil", ofnode_read_string(node, prop)); + } + + /* try again, this time copying the property */ + strcpy(name, "mary"); + ut_assertok(ofnode_write_prop(node, prop, name, len, true)); + ut_asserteq_str(name, ofnode_read_string(node, prop)); + strcpy(name, "leah"); + + /* both flattree and livetree behave the same */ + ut_asserteq_str("mary", ofnode_read_string(node, prop)); + + return 0; +} + +/* writing the tree with and without copying the property */ +static int dm_test_ofnode_write_copy(struct unit_test_state *uts) +{ + ofnode node; + + node = ofnode_path("/a-test"); + ut_assertok(check_write_prop(uts, node)); + + return 0; +} +DM_TEST(dm_test_ofnode_write_copy, UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_write_copy_ot(struct unit_test_state *uts) +{ + oftree otree = get_other_oftree(uts); + ofnode node, check_node; + + node = oftree_path(otree, "/node"); + ut_assertok(check_write_prop(uts, node)); + + /* make sure the control FDT is not touched */ + check_node = ofnode_path("/node"); + ut_assertnull(ofnode_read_string(check_node, "middle-name")); + + return 0; +} +DM_TEST(dm_test_ofnode_write_copy_ot, UT_TESTF_OTHER_FDT); static int dm_test_ofnode_u32(struct unit_test_state *uts) { ofnode node; + u32 val; node = ofnode_path("/lcd"); ut_assert(ofnode_valid(node)); @@ -597,7 +910,328 @@ static int dm_test_ofnode_u32(struct unit_test_state *uts) ut_asserteq(1367, ofnode_read_u32_default(node, "xres", 123)); ut_assertok(ofnode_write_u32(node, "xres", 1366)); + node = ofnode_path("/backlight"); + ut_assertok(ofnode_read_u32_index(node, "brightness-levels", 0, &val)); + ut_asserteq(0, val); + ut_assertok(ofnode_read_u32_index(node, "brightness-levels", 1, &val)); + ut_asserteq(16, val); + ut_assertok(ofnode_read_u32_index(node, "brightness-levels", 8, &val)); + ut_asserteq(255, val); + ut_asserteq(-EOVERFLOW, + ofnode_read_u32_index(node, "brightness-levels", 9, &val)); + ut_asserteq(-EINVAL, ofnode_read_u32_index(node, "missing", 0, &val)); + + return 0; +} +DM_TEST(dm_test_ofnode_u32, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_u32_array(struct unit_test_state *uts) +{ + ofnode node; + u32 val[10]; + + node = ofnode_path("/a-test"); + ut_assert(ofnode_valid(node)); + ut_assertok(ofnode_read_u32_array(node, "int-value", val, 1)); + ut_asserteq(-EINVAL, ofnode_read_u32_array(node, "missing", val, 1)); + ut_asserteq(-EOVERFLOW, ofnode_read_u32_array(node, "bool-value", val, + 1)); + + memset(val, '\0', sizeof(val)); + ut_assertok(ofnode_read_u32_array(node, "int-array", val + 1, 3)); + ut_asserteq(0, val[0]); + ut_asserteq(5678, val[1]); + ut_asserteq(9123, val[2]); + ut_asserteq(4567, val[3]); + ut_asserteq(0, val[4]); + ut_asserteq(-EOVERFLOW, ofnode_read_u32_array(node, "int-array", val, + 4)); + + return 0; +} +DM_TEST(dm_test_ofnode_u32_array, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_u64(struct unit_test_state *uts) +{ + ofnode node; + u64 val; + + node = ofnode_path("/a-test"); + ut_assert(ofnode_valid(node)); + ut_assertok(ofnode_read_u64(node, "int64-value", &val)); + ut_asserteq_64(0x1111222233334444, val); + ut_asserteq(-EINVAL, ofnode_read_u64(node, "missing", &val)); + + return 0; +} +DM_TEST(dm_test_ofnode_u64, UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_add_subnode(struct unit_test_state *uts) +{ + ofnode node, check, subnode; + char buf[128]; + + node = ofnode_path("/lcd"); + ut_assert(ofnode_valid(node)); + ut_assertok(ofnode_add_subnode(node, "edmund", &subnode)); + check = ofnode_path("/lcd/edmund"); + ut_asserteq(subnode.of_offset, check.of_offset); + ut_assertok(ofnode_get_path(subnode, buf, sizeof(buf))); + ut_asserteq_str("/lcd/edmund", buf); + + if (of_live_active()) { + struct device_node *child; + + ut_assertok(of_add_subnode((void *)ofnode_to_np(node), "edmund", + 2, &child)); + ut_asserteq_str("ed", child->name); + ut_asserteq_str("/lcd/ed", child->full_name); + check = ofnode_path("/lcd/ed"); + ut_asserteq_ptr(child, check.np); + ut_assertok(ofnode_get_path(np_to_ofnode(child), buf, + sizeof(buf))); + ut_asserteq_str("/lcd/ed", buf); + } + + /* An existing node should be returned with -EEXIST */ + ut_asserteq(-EEXIST, ofnode_add_subnode(node, "edmund", &check)); + ut_asserteq(subnode.of_offset, check.of_offset); + + /* add a root node */ + node = ofnode_path("/"); + ut_assert(ofnode_valid(node)); + ut_assertok(ofnode_add_subnode(node, "lcd2", &subnode)); + check = ofnode_path("/lcd2"); + ut_asserteq(subnode.of_offset, check.of_offset); + ut_assertok(ofnode_get_path(subnode, buf, sizeof(buf))); + ut_asserteq_str("/lcd2", buf); + + if (of_live_active()) { + ulong start; + int i; + + /* + * Make sure each of the three malloc()checks in + * of_add_subnode() work + */ + for (i = 0; i < 3; i++) { + malloc_enable_testing(i); + start = ut_check_free(); + ut_asserteq(-ENOMEM, ofnode_add_subnode(node, "anthony", + &check)); + ut_assertok(ut_check_delta(start)); + } + + /* This should pass since we allow 3 allocations */ + malloc_enable_testing(3); + ut_assertok(ofnode_add_subnode(node, "anthony", &check)); + malloc_disable_testing(); + } + + /* write to the empty node */ + ut_assertok(ofnode_write_string(subnode, "example", "text")); + + return 0; +} +DM_TEST(dm_test_ofnode_add_subnode, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_for_each_prop(struct unit_test_state *uts) +{ + ofnode node, subnode; + struct ofprop prop; + int count; + + node = ofnode_path("/buttons"); + count = 0; + + /* we expect "compatible" for each node */ + ofnode_for_each_prop(prop, node) + count++; + ut_asserteq(1, count); + + /* there are two nodes, each with 2 properties */ + ofnode_for_each_subnode(subnode, node) + ofnode_for_each_prop(prop, subnode) + count++; + ut_asserteq(5, count); + + return 0; +} +DM_TEST(dm_test_ofnode_for_each_prop, UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_by_compatible(struct unit_test_state *uts) +{ + const char *compat = "denx,u-boot-fdt-test"; + ofnode node; + int count; + + count = 0; + for (node = ofnode_null(); + node = ofnode_by_compatible(node, compat), ofnode_valid(node);) + count++; + ut_asserteq(11, count); + + return 0; +} +DM_TEST(dm_test_ofnode_by_compatible, UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_by_compatible_ot(struct unit_test_state *uts) +{ + const char *compat = "sandbox-other2"; + oftree otree = get_other_oftree(uts); + ofnode node; + int count; + + count = 0; + for (node = oftree_root(otree); + node = ofnode_by_compatible(node, compat), ofnode_valid(node);) + count++; + ut_asserteq(2, count); + + return 0; +} +DM_TEST(dm_test_ofnode_by_compatible_ot, UT_TESTF_OTHER_FDT); + +static int dm_test_ofnode_find_subnode(struct unit_test_state *uts) +{ + ofnode node, subnode; + + node = ofnode_path("/buttons"); + + subnode = ofnode_find_subnode(node, "btn1"); + ut_assert(ofnode_valid(subnode)); + ut_asserteq_str("btn1", ofnode_get_name(subnode)); + + subnode = ofnode_find_subnode(node, "btn"); + ut_assert(!ofnode_valid(subnode)); + + return 0; +} +DM_TEST(dm_test_ofnode_find_subnode, UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_find_subnode_ot(struct unit_test_state *uts) +{ + oftree otree = get_other_oftree(uts); + ofnode node, subnode; + + node = oftree_path(otree, "/node"); + + subnode = ofnode_find_subnode(node, "subnode"); + ut_assert(ofnode_valid(subnode)); + ut_asserteq_str("subnode", ofnode_get_name(subnode)); + + subnode = ofnode_find_subnode(node, "btn"); + ut_assert(!ofnode_valid(subnode)); + + return 0; +} +DM_TEST(dm_test_ofnode_find_subnode_ot, UT_TESTF_OTHER_FDT); + +static int dm_test_ofnode_get_name(struct unit_test_state *uts) +{ + ofnode node; + + node = ofnode_path("/buttons"); + ut_assert(ofnode_valid(node)); + ut_asserteq_str("buttons", ofnode_get_name(node)); + ut_asserteq_str("", ofnode_get_name(ofnode_root())); + + return 0; +} +DM_TEST(dm_test_ofnode_get_name, UT_TESTF_SCAN_FDT); + +/* try to access more FDTs than is supported */ +static int dm_test_ofnode_too_many(struct unit_test_state *uts) +{ + const int max_trees = CONFIG_IS_ENABLED(OFNODE_MULTI_TREE, + (CONFIG_OFNODE_MULTI_TREE_MAX), (1)); + const int fdt_size = 256; + const int num_trees = max_trees + 1; + char fdt[num_trees][fdt_size]; + int i; + + for (i = 0; i < num_trees; i++) { + oftree tree; + int ret; + + ut_assertok(make_ofnode_fdt(uts, fdt[i], fdt_size, i)); + ret = get_oftree(uts, fdt[i], &tree); + + /* + * With flat tree we have the control FDT using one slot. Live + * tree has no limit since it uses pointers, not integer tree + * IDs + */ + if (of_live_active() || i < max_trees - 1) { + ut_assertok(ret); + } else { + /* + * tree should be invalid when we try to register too + * many trees + */ + ut_asserteq(-EOVERFLOW, ret); + } + } + + return 0; +} +DM_TEST(dm_test_ofnode_too_many, UT_TESTF_SCAN_FDT); + +static int check_copy_props(struct unit_test_state *uts, ofnode src, + ofnode dst) +{ + u32 reg[2], val; + + ut_assertok(ofnode_copy_props(src, dst)); + + ut_assertok(ofnode_read_u32(dst, "ping-expect", &val)); + ut_asserteq(3, val); + + ut_asserteq_str("denx,u-boot-fdt-test", + ofnode_read_string(dst, "compatible")); + + /* check that a property with the same name is overwritten */ + ut_assertok(ofnode_read_u32_array(dst, "reg", reg, ARRAY_SIZE(reg))); + ut_asserteq(3, reg[0]); + ut_asserteq(1, reg[1]); + + /* reset the compatible so the live tree does not change */ + ut_assertok(ofnode_write_string(dst, "compatible", "nothing")); + + return 0; +} + +static int dm_test_ofnode_copy_props(struct unit_test_state *uts) +{ + ofnode src, dst; + + /* + * These nodes are chosen so that the src node is before the destination + * node in the tree. This doesn't matter with livetree, but with + * flattree any attempt to insert a property earlier in the tree will + * mess up the offsets after it. + */ + src = ofnode_path("/b-test"); + dst = ofnode_path("/some-bus"); + + ut_assertok(check_copy_props(uts, src, dst)); + + /* check a property that is in the destination already */ + ut_asserteq_str("mux0", ofnode_read_string(dst, "mux-control-names")); + + return 0; +} +DM_TEST(dm_test_ofnode_copy_props, UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_copy_props_ot(struct unit_test_state *uts) +{ + ofnode src, dst; + oftree otree = get_other_oftree(uts); + + src = ofnode_path("/b-test"); + dst = oftree_path(otree, "/node/subnode2"); + ut_assertok(check_copy_props(uts, src, dst)); + return 0; } -DM_TEST(dm_test_ofnode_u32, - UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_LIVE_OR_FLAT); +DM_TEST(dm_test_ofnode_copy_props_ot, UT_TESTF_SCAN_FDT | UT_TESTF_OTHER_FDT); |