mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-14 00:47:26 +00:00
acpi: Add a function to get a device path and scope
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>
This commit is contained in:
parent
4b724a1377
commit
1361a53c1a
5 changed files with 213 additions and 13 deletions
|
@ -255,6 +255,9 @@
|
||||||
|
|
||||||
acpi-test {
|
acpi-test {
|
||||||
compatible = "denx,u-boot-acpi-test";
|
compatible = "denx,u-boot-acpi-test";
|
||||||
|
child {
|
||||||
|
compatible = "denx,u-boot-acpi-test";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
acpi-test2 {
|
acpi-test2 {
|
||||||
|
|
44
include/acpi/acpi_device.h
Normal file
44
include/acpi/acpi_device.h
Normal file
|
@ -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
|
|
@ -1,4 +1,5 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0+
|
# SPDX-License-Identifier: GPL-2.0+
|
||||||
#
|
#
|
||||||
|
|
||||||
|
obj-y += acpi_device.o
|
||||||
obj-y += acpi_table.o
|
obj-y += acpi_table.o
|
||||||
|
|
83
lib/acpi/acpi_device.c
Normal file
83
lib/acpi/acpi_device.c
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -14,14 +14,27 @@
|
||||||
#include <version.h>
|
#include <version.h>
|
||||||
#include <tables_csum.h>
|
#include <tables_csum.h>
|
||||||
#include <version.h>
|
#include <version.h>
|
||||||
|
#include <acpi/acpi_device.h>
|
||||||
#include <acpi/acpi_table.h>
|
#include <acpi/acpi_table.h>
|
||||||
#include <dm/acpi.h>
|
#include <dm/acpi.h>
|
||||||
#include <dm/test.h>
|
#include <dm/test.h>
|
||||||
#include <test/ut.h>
|
#include <test/ut.h>
|
||||||
|
|
||||||
#define ACPI_TEST_DEV_NAME "ABCD"
|
#define ACPI_TEST_DEV_NAME "ABCD"
|
||||||
|
#define ACPI_TEST_CHILD_NAME "EFGH"
|
||||||
#define BUF_SIZE 4096
|
#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,
|
static int testacpi_write_tables(const struct udevice *dev,
|
||||||
struct acpi_ctx *ctx)
|
struct acpi_ctx *ctx)
|
||||||
{
|
{
|
||||||
|
@ -40,6 +53,17 @@ static int testacpi_write_tables(const struct udevice *dev,
|
||||||
|
|
||||||
static int testacpi_get_name(const struct udevice *dev, char *out_name)
|
static int testacpi_get_name(const struct udevice *dev, char *out_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);
|
return acpi_copy_name(out_name, ACPI_TEST_DEV_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +81,8 @@ U_BOOT_DRIVER(testacpi_drv) = {
|
||||||
.name = "testacpi_drv",
|
.name = "testacpi_drv",
|
||||||
.of_match = testacpi_ids,
|
.of_match = testacpi_ids,
|
||||||
.id = UCLASS_TEST_ACPI,
|
.id = UCLASS_TEST_ACPI,
|
||||||
|
.bind = dm_scan_fdt_dev,
|
||||||
|
.platdata_auto_alloc_size = sizeof(struct testacpi_platdata),
|
||||||
ACPI_OPS_PTR(&testacpi_ops)
|
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_dmar *dmar;
|
||||||
struct acpi_ctx ctx;
|
struct acpi_ctx ctx;
|
||||||
void *buf;
|
void *buf;
|
||||||
|
int i;
|
||||||
|
|
||||||
buf = malloc(BUF_SIZE);
|
buf = malloc(BUF_SIZE);
|
||||||
ut_assertnonnull(buf);
|
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));
|
ut_assertok(acpi_write_dev_tables(&ctx));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We should have two dmar tables, one for each "denx,u-boot-acpi-test"
|
* We should have three dmar tables, one for each
|
||||||
* device
|
* "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(DMAR_INTR_REMAP, dmar->flags);
|
||||||
ut_asserteq(32 - 1, dmar->host_address_width);
|
ut_asserteq(32 - 1, dmar->host_address_width);
|
||||||
|
|
||||||
ut_asserteq(DMAR_INTR_REMAP, dmar[1].flags);
|
ut_asserteq(DMAR_INTR_REMAP, dmar[1].flags);
|
||||||
ut_asserteq(32 - 1, dmar[1].host_address_width);
|
ut_asserteq(32 - 1, dmar[1].host_address_width);
|
||||||
|
|
||||||
/* Check that the pointers were added correctly */
|
ut_asserteq(DMAR_INTR_REMAP, dmar[2].flags);
|
||||||
ut_asserteq(map_to_sysmem(dmar), ctx.rsdt->entry[0]);
|
ut_asserteq(32 - 1, dmar[2].host_address_width);
|
||||||
ut_asserteq(map_to_sysmem(dmar + 1), ctx.rsdt->entry[1]);
|
|
||||||
ut_asserteq(0, ctx.rsdt->entry[2]);
|
|
||||||
|
|
||||||
ut_asserteq(map_to_sysmem(dmar), ctx.xsdt->entry[0]);
|
/* Check that the pointers were added correctly */
|
||||||
ut_asserteq(map_to_sysmem(dmar + 1), ctx.xsdt->entry[1]);
|
for (i = 0; i < 3; i++) {
|
||||||
ut_asserteq(0, ctx.xsdt->entry[2]);
|
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;
|
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);
|
addr = ALIGN(addr + sizeof(struct acpi_rsdp), 16);
|
||||||
ut_assert_nextline("RSDT %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
|
ut_assert_nextline("RSDT %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
|
||||||
addr, sizeof(struct acpi_table_header) +
|
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);
|
addr = ALIGN(addr + sizeof(struct acpi_rsdt), 16);
|
||||||
ut_assert_nextline("XSDT %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
|
ut_assert_nextline("XSDT %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
|
||||||
addr, sizeof(struct acpi_table_header) +
|
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);
|
addr = ALIGN(addr + sizeof(struct acpi_xsdt), 64);
|
||||||
ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
|
ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
|
||||||
addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE);
|
addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE);
|
||||||
addr = ALIGN(addr + sizeof(struct acpi_dmar), 16);
|
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)",
|
ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
|
||||||
addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE);
|
addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE);
|
||||||
ut_assert_console_end();
|
ut_assert_console_end();
|
||||||
|
@ -315,3 +347,40 @@ static int dm_test_acpi_cmd_dump(struct unit_test_state *uts)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
DM_TEST(dm_test_acpi_cmd_dump, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|
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);
|
||||||
|
|
Loading…
Reference in a new issue