diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 07535a6b7d..0d8ffd179a 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -255,6 +255,9 @@ acpi-test { compatible = "denx,u-boot-acpi-test"; + child { + compatible = "denx,u-boot-acpi-test"; + }; }; acpi-test2 { diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h new file mode 100644 index 0000000000..37a675f101 --- /dev/null +++ b/include/acpi/acpi_device.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Generation of tables for particular device types + * + * Copyright 2019 Google LLC + * Mostly taken from coreboot file of the same name + */ + +#ifndef __ACPI_DEVICE_H +#define __ACPI_DEVICE_H + +struct udevice; + +/* Length of a full path to an ACPI device */ +#define ACPI_PATH_MAX 30 + +/** + * acpi_device_path() - Get the full path to an ACPI device + * + * This gets the full path in the form XXXX.YYYY.ZZZZ where XXXX is the root + * and ZZZZ is the device. All parent devices are added to the path. + * + * @dev: Device to check + * @buf: Buffer to place the path in (should be ACPI_PATH_MAX long) + * @maxlen: Size of buffer (typically ACPI_PATH_MAX) + * @return 0 if OK, -ve on error + */ +int acpi_device_path(const struct udevice *dev, char *buf, int maxlen); + +/** + * acpi_device_scope() - Get the scope of an ACPI device + * + * This gets the scope which is the full path of the parent device, as per + * acpi_device_path(). + * + * @dev: Device to check + * @buf: Buffer to place the path in (should be ACPI_PATH_MAX long) + * @maxlen: Size of buffer (typically ACPI_PATH_MAX) + * @return 0 if OK, -EINVAL if the device has no parent, other -ve on other + * error + */ +int acpi_device_scope(const struct udevice *dev, char *scope, int maxlen); + +#endif diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile index 660491ef71..caae6c01bd 100644 --- a/lib/acpi/Makefile +++ b/lib/acpi/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-y += acpi_device.o obj-y += acpi_table.o diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c new file mode 100644 index 0000000000..3a9424e7ee --- /dev/null +++ b/lib/acpi/acpi_device.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generation of tables for particular device types + * + * Copyright 2019 Google LLC + * Mostly taken from coreboot file of the same name + */ + +#include +#include +#include +#include +#include + +/** + * acpi_device_path_fill() - Find the root device and build a path from there + * + * This recursively reaches back to the root device and progressively adds path + * elements until the device is reached. + * + * @dev: Device to return path of + * @buf: Buffer to hold the path + * @buf_len: Length of buffer + * @cur: Current position in the buffer + * @return new position in buffer after adding @dev, or -ve on error + */ +static int acpi_device_path_fill(const struct udevice *dev, char *buf, + size_t buf_len, int cur) +{ + char name[ACPI_NAME_MAX]; + int next = 0; + int ret; + + ret = acpi_get_name(dev, name); + if (ret) + return ret; + + /* + * Make sure this name segment will fit, including the path segment + * separator and possible NULL terminator, if this is the last segment. + */ + if (cur + strlen(name) + 2 > buf_len) + return -ENOSPC; + + /* Walk up the tree to the root device */ + if (dev_get_parent(dev)) { + next = acpi_device_path_fill(dev_get_parent(dev), buf, buf_len, + cur); + if (next < 0) + return next; + } + + /* Fill in the path from the root device */ + next += snprintf(buf + next, buf_len - next, "%s%s", + dev_get_parent(dev) && *name ? "." : "", name); + + return next; +} + +int acpi_device_path(const struct udevice *dev, char *buf, int maxlen) +{ + int ret; + + ret = acpi_device_path_fill(dev, buf, maxlen, 0); + if (ret < 0) + return ret; + + return 0; +} + +int acpi_device_scope(const struct udevice *dev, char *scope, int maxlen) +{ + int ret; + + if (!dev_get_parent(dev)) + return log_msg_ret("noparent", -EINVAL); + + ret = acpi_device_path_fill(dev_get_parent(dev), scope, maxlen, 0); + if (ret < 0) + return log_msg_ret("fill", ret); + + return 0; +} diff --git a/test/dm/acpi.c b/test/dm/acpi.c index 4c46dd83a6..5b8459311c 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -14,14 +14,27 @@ #include #include #include +#include #include #include #include #include #define ACPI_TEST_DEV_NAME "ABCD" +#define ACPI_TEST_CHILD_NAME "EFGH" #define BUF_SIZE 4096 +/** + * struct testacpi_platdata - Platform data for the test ACPI device + * + * @no_name: true to emit an empty ACPI name from testacpi_get_name() + * @return_error: true to return an error instead of a name + */ +struct testacpi_platdata { + bool return_error; + bool no_name; +}; + static int testacpi_write_tables(const struct udevice *dev, struct acpi_ctx *ctx) { @@ -40,7 +53,18 @@ static int testacpi_write_tables(const struct udevice *dev, static int testacpi_get_name(const struct udevice *dev, char *out_name) { - return acpi_copy_name(out_name, ACPI_TEST_DEV_NAME); + struct testacpi_platdata *plat = dev_get_platdata(dev); + + if (plat->return_error) + return -EINVAL; + if (plat->no_name) { + *out_name = '\0'; + return 0; + } + if (device_get_uclass_id(dev->parent) == UCLASS_TEST_ACPI) + return acpi_copy_name(out_name, ACPI_TEST_CHILD_NAME); + else + return acpi_copy_name(out_name, ACPI_TEST_DEV_NAME); } struct acpi_ops testacpi_ops = { @@ -57,6 +81,8 @@ U_BOOT_DRIVER(testacpi_drv) = { .name = "testacpi_drv", .of_match = testacpi_ids, .id = UCLASS_TEST_ACPI, + .bind = dm_scan_fdt_dev, + .platdata_auto_alloc_size = sizeof(struct testacpi_platdata), ACPI_OPS_PTR(&testacpi_ops) }; @@ -138,6 +164,7 @@ static int dm_test_acpi_write_tables(struct unit_test_state *uts) struct acpi_dmar *dmar; struct acpi_ctx ctx; void *buf; + int i; buf = malloc(BUF_SIZE); ut_assertnonnull(buf); @@ -147,24 +174,26 @@ static int dm_test_acpi_write_tables(struct unit_test_state *uts) ut_assertok(acpi_write_dev_tables(&ctx)); /* - * We should have two dmar tables, one for each "denx,u-boot-acpi-test" - * device + * We should have three dmar tables, one for each + * "denx,u-boot-acpi-test" device */ - ut_asserteq_ptr(dmar + 2, ctx.current); + ut_asserteq_ptr(dmar + 3, ctx.current); ut_asserteq(DMAR_INTR_REMAP, dmar->flags); ut_asserteq(32 - 1, dmar->host_address_width); ut_asserteq(DMAR_INTR_REMAP, dmar[1].flags); ut_asserteq(32 - 1, dmar[1].host_address_width); - /* Check that the pointers were added correctly */ - ut_asserteq(map_to_sysmem(dmar), ctx.rsdt->entry[0]); - ut_asserteq(map_to_sysmem(dmar + 1), ctx.rsdt->entry[1]); - ut_asserteq(0, ctx.rsdt->entry[2]); + ut_asserteq(DMAR_INTR_REMAP, dmar[2].flags); + ut_asserteq(32 - 1, dmar[2].host_address_width); - ut_asserteq(map_to_sysmem(dmar), ctx.xsdt->entry[0]); - ut_asserteq(map_to_sysmem(dmar + 1), ctx.xsdt->entry[1]); - ut_asserteq(0, ctx.xsdt->entry[2]); + /* Check that the pointers were added correctly */ + for (i = 0; i < 3; i++) { + ut_asserteq(map_to_sysmem(dmar + i), ctx.rsdt->entry[i]); + ut_asserteq(map_to_sysmem(dmar + i), ctx.xsdt->entry[i]); + } + ut_asserteq(0, ctx.rsdt->entry[3]); + ut_asserteq(0, ctx.xsdt->entry[3]); return 0; } @@ -268,15 +297,18 @@ static int dm_test_acpi_cmd_list(struct unit_test_state *uts) addr = ALIGN(addr + sizeof(struct acpi_rsdp), 16); ut_assert_nextline("RSDT %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)", addr, sizeof(struct acpi_table_header) + - 2 * sizeof(u32), U_BOOT_BUILD_DATE); + 3 * sizeof(u32), U_BOOT_BUILD_DATE); addr = ALIGN(addr + sizeof(struct acpi_rsdt), 16); ut_assert_nextline("XSDT %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)", addr, sizeof(struct acpi_table_header) + - 2 * sizeof(u64), U_BOOT_BUILD_DATE); + 3 * sizeof(u64), U_BOOT_BUILD_DATE); addr = ALIGN(addr + sizeof(struct acpi_xsdt), 64); ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)", addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE); addr = ALIGN(addr + sizeof(struct acpi_dmar), 16); + ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)", + addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE); + addr = ALIGN(addr + sizeof(struct acpi_dmar), 16); ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)", addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE); ut_assert_console_end(); @@ -315,3 +347,40 @@ static int dm_test_acpi_cmd_dump(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_cmd_dump, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test acpi_device_path() */ +static int dm_test_acpi_device_path(struct unit_test_state *uts) +{ + struct testacpi_platdata *plat; + char buf[ACPI_PATH_MAX]; + struct udevice *dev, *child; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev)); + ut_assertok(acpi_device_path(dev, buf, sizeof(buf))); + ut_asserteq_str("\\_SB." ACPI_TEST_DEV_NAME, buf); + + /* Test running out of space */ + buf[5] = '\0'; + ut_asserteq(-ENOSPC, acpi_device_path(dev, buf, 5)); + ut_asserteq('\0', buf[5]); + + /* Test a three-component name */ + ut_assertok(device_first_child_err(dev, &child)); + ut_assertok(acpi_device_path(child, buf, sizeof(buf))); + ut_asserteq_str("\\_SB." ACPI_TEST_DEV_NAME "." ACPI_TEST_CHILD_NAME, + buf); + + /* Test handling of a device which doesn't produce a name */ + plat = dev_get_platdata(dev); + plat->no_name = true; + ut_assertok(acpi_device_path(child, buf, sizeof(buf))); + ut_asserteq_str("\\_SB." ACPI_TEST_CHILD_NAME, buf); + + /* Test handling of a device which returns an error */ + plat = dev_get_platdata(dev); + plat->return_error = true; + ut_asserteq(-EINVAL, acpi_device_path(child, buf, sizeof(buf))); + + return 0; +} +DM_TEST(dm_test_acpi_device_path, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);