]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
acpi: Add a function to get a device path and scope
authorSimon Glass <sjg@chromium.org>
Tue, 7 Jul 2020 19:11:39 +0000 (13:11 -0600)
committerBin Meng <bmeng.cn@gmail.com>
Fri, 17 Jul 2020 06:32:24 +0000 (14:32 +0800)
Add a function to build up the ACPI path for a device and another for its
scope.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Wolfgang Wallner <wolfgang.wallner@br-automation.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
arch/sandbox/dts/test.dts
include/acpi/acpi_device.h [new file with mode: 0644]
lib/acpi/Makefile
lib/acpi/acpi_device.c [new file with mode: 0644]
test/dm/acpi.c

index 07535a6b7d3457ebb2850ff18851a7d80853f009..0d8ffd179a868197817dc77a67f047f77018d3a4 100644 (file)
 
        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 (file)
index 0000000..37a675f
--- /dev/null
@@ -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
index 660491ef711c64115573d1cb1950b89b0274fb12..caae6c01bd91284919d53ec1062d9b9717c78bb5 100644 (file)
@@ -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 (file)
index 0000000..3a9424e
--- /dev/null
@@ -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 <common.h>
+#include <dm.h>
+#include <log.h>
+#include <acpi/acpi_device.h>
+#include <dm/acpi.h>
+
+/**
+ * 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;
+}
index 4c46dd83a695bbc0d5b5afd312d7af9ad798eeb8..5b8459311cb1ac3011ccc93534d60281bb832e64 100644 (file)
 #include <version.h>
 #include <tables_csum.h>
 #include <version.h>
+#include <acpi/acpi_device.h>
 #include <acpi/acpi_table.h>
 #include <dm/acpi.h>
 #include <dm/test.h>
 #include <test/ut.h>
 
 #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);