From patchwork Sun Jun 14 02:54:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242234 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:54:49 -0600 Subject: [PATCH v3 01/35] dm: core: Add an ACPI name for the root node In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.1.I3fc26919ac73d55741c6ee8098cc0d6df70bb23c@changeid> This always has a fixed ACPI name so add it as a driver function. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- (no changes since v1) Changes in v1: - Capitalise ACPI_OPS_PTR drivers/core/root.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/core/root.c b/drivers/core/root.c index 7d257ea887..14ae8a953b 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -373,10 +374,22 @@ int dm_init_and_scan(bool pre_reloc_only) return 0; } +#ifdef CONFIG_ACPIGEN +static int root_acpi_get_name(const struct udevice *dev, char *out_name) +{ + return acpi_copy_name(out_name, "\\_SB"); +} + +struct acpi_ops root_acpi_ops = { + .get_name = root_acpi_get_name, +}; +#endif + /* This is the root driver - all drivers are children of this */ U_BOOT_DRIVER(root_driver) = { .name = "root_driver", .id = UCLASS_ROOT, + ACPI_OPS_PTR(&root_acpi_ops) }; /* This is the root uclass */ From patchwork Sun Jun 14 02:54:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242241 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:54:50 -0600 Subject: [PATCH v3 02/35] acpi: Add a function to get a device path and scope In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.2.Ia1c0ba090f86174165e99209155eb4d8352fbf72@changeid> Add a function to build up the ACPI path for a device and another for its scope. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- Changes in v3: - s/NUL/NULL/ for the NUL character, since that is in more common use Changes in v1: - Split into more patches for review - Add tests - Rebase on top of common.h series arch/sandbox/dts/test.dts | 3 ++ include/acpi/acpi_device.h | 44 ++++++++++++++++++ lib/acpi/Makefile | 1 + lib/acpi/acpi_device.c | 83 +++++++++++++++++++++++++++++++++ test/dm/acpi.c | 95 ++++++++++++++++++++++++++++++++------ 5 files changed, 213 insertions(+), 13 deletions(-) create mode 100644 include/acpi/acpi_device.h create mode 100644 lib/acpi/acpi_device.c diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 5ce5e28476..bad0b4f1a8 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); From patchwork Sun Jun 14 02:54:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242236 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:54:51 -0600 Subject: [PATCH v3 03/35] acpi: Add a way to check device status In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.3.I9e30a0969f5b740da80c19958fb1336dff8c09d9@changeid> At present U-Boot does not support the different ACPI status values, but it is best to put this logic in a central place. Add a function to get the device status. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- (no changes since v1) include/acpi/acpi_device.h | 28 ++++++++++++++++++++++++++++ lib/acpi/acpi_device.c | 5 +++++ test/dm/acpi.c | 12 ++++++++++++ 3 files changed, 45 insertions(+) diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index 37a675f101..09c227489a 100644 --- a/include/acpi/acpi_device.h +++ b/include/acpi/acpi_device.h @@ -9,11 +9,28 @@ #ifndef __ACPI_DEVICE_H #define __ACPI_DEVICE_H +#include + struct udevice; /* Length of a full path to an ACPI device */ #define ACPI_PATH_MAX 30 +/* Values that can be returned for ACPI device _STA method */ +enum acpi_dev_status { + ACPI_DSTATUS_PRESENT = BIT(0), + ACPI_DSTATUS_ENABLED = BIT(1), + ACPI_DSTATUS_SHOW_IN_UI = BIT(2), + ACPI_DSTATUS_OK = BIT(3), + ACPI_DSTATUS_HAS_BATTERY = BIT(4), + + ACPI_DSTATUS_ALL_OFF = 0, + ACPI_DSTATUS_HIDDEN_ON = ACPI_DSTATUS_PRESENT | ACPI_DSTATUS_ENABLED | + ACPI_DSTATUS_OK, + ACPI_DSTATUS_ALL_ON = ACPI_DSTATUS_HIDDEN_ON | + ACPI_DSTATUS_SHOW_IN_UI, +}; + /** * acpi_device_path() - Get the full path to an ACPI device * @@ -41,4 +58,15 @@ int acpi_device_path(const struct udevice *dev, char *buf, int maxlen); */ int acpi_device_scope(const struct udevice *dev, char *scope, int maxlen); +/** + * acpi_device_status() - Get the status of a device + * + * This currently just returns ACPI_DSTATUS_ALL_ON. It does not support + * inactive or hidden devices. + * + * @dev: Device to check + * @return device status, as ACPI_DSTATUS_... + */ +enum acpi_dev_status acpi_device_status(const struct udevice *dev); + #endif diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c index 3a9424e7ee..60f4fd8cd5 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -81,3 +81,8 @@ int acpi_device_scope(const struct udevice *dev, char *scope, int maxlen) return 0; } + +enum acpi_dev_status acpi_device_status(const struct udevice *dev) +{ + return ACPI_DSTATUS_ALL_ON; +} diff --git a/test/dm/acpi.c b/test/dm/acpi.c index 5b8459311c..07b0daaab0 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -384,3 +384,15 @@ static int dm_test_acpi_device_path(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_device_path, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test acpi_device_status() */ +static int dm_test_acpi_device_status(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev)); + ut_asserteq(ACPI_DSTATUS_ALL_ON, acpi_device_status(dev)); + + return 0; +} +DM_TEST(dm_test_acpi_device_status, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Sun Jun 14 02:54:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242237 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:54:52 -0600 Subject: [PATCH v3 04/35] irq: Add a method to convert an interrupt to ACPI In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200614025523.40183-2-sjg@chromium.org> When generating ACPI tables we need to convert IRQs in U-Boot to the ACPI structures required by ACPI. This is a SoC-specific conversion and cannot be handled by generic code, so add a new IRQ method to do the conversion. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- (no changes since v1) Changes in v1: - Fix 'the an' typo - Move header definitions into this patch drivers/misc/irq-uclass.c | 18 ++++++++++-- drivers/misc/irq_sandbox.c | 16 +++++++++++ include/acpi/acpi_device.h | 59 ++++++++++++++++++++++++++++++++++++++ include/irq.h | 43 +++++++++++++++++++++++++++ test/dm/irq.c | 22 ++++++++++++++ 5 files changed, 156 insertions(+), 2 deletions(-) diff --git a/drivers/misc/irq-uclass.c b/drivers/misc/irq-uclass.c index 16dc0be75c..98bc79eaba 100644 --- a/drivers/misc/irq-uclass.c +++ b/drivers/misc/irq-uclass.c @@ -154,8 +154,6 @@ int irq_request(struct udevice *dev, struct irq *irq) const struct irq_ops *ops; log_debug("(dev=%p, irq=%p)\n", dev, irq); - if (!irq) - return 0; ops = irq_get_ops(dev); irq->dev = dev; @@ -177,6 +175,22 @@ int irq_first_device_type(enum irq_dev_t type, struct udevice **devp) return 0; } +#if CONFIG_IS_ENABLED(ACPIGEN) +int irq_get_acpi(const struct irq *irq, struct acpi_irq *acpi_irq) +{ + struct irq_ops *ops; + + if (!irq_is_valid(irq)) + return -EINVAL; + + ops = irq_get_ops(irq->dev); + if (!ops->get_acpi) + return -ENOSYS; + + return ops->get_acpi(irq, acpi_irq); +} +#endif + UCLASS_DRIVER(irq) = { .id = UCLASS_IRQ, .name = "irq", diff --git a/drivers/misc/irq_sandbox.c b/drivers/misc/irq_sandbox.c index 54bc47c8d8..a2511b32fc 100644 --- a/drivers/misc/irq_sandbox.c +++ b/drivers/misc/irq_sandbox.c @@ -8,6 +8,7 @@ #include #include #include +#include #include /** @@ -73,6 +74,18 @@ static int sandbox_irq_of_xlate(struct irq *irq, return 0; } +static __maybe_unused int sandbox_get_acpi(const struct irq *irq, + struct acpi_irq *acpi_irq) +{ + acpi_irq->pin = irq->id; + acpi_irq->mode = ACPI_IRQ_LEVEL_TRIGGERED; + acpi_irq->polarity = ACPI_IRQ_ACTIVE_HIGH; + acpi_irq->shared = ACPI_IRQ_SHARED; + acpi_irq->wake = ACPI_IRQ_WAKE; + + return 0; +} + static const struct irq_ops sandbox_irq_ops = { .route_pmc_gpio_gpe = sandbox_route_pmc_gpio_gpe, .set_polarity = sandbox_set_polarity, @@ -80,6 +93,9 @@ static const struct irq_ops sandbox_irq_ops = { .restore_polarities = sandbox_restore_polarities, .read_and_clear = sandbox_irq_read_and_clear, .of_xlate = sandbox_irq_of_xlate, +#if CONFIG_IS_ENABLED(ACPIGEN) + .get_acpi = sandbox_get_acpi, +#endif }; static const struct udevice_id sandbox_irq_ids[] = { diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index 09c227489a..24895de0da 100644 --- a/include/acpi/acpi_device.h +++ b/include/acpi/acpi_device.h @@ -13,6 +13,12 @@ struct udevice; +/* ACPI descriptor values for common descriptors: SERIAL_BUS means I2C */ +#define ACPI_DESCRIPTOR_LARGE BIT(7) +#define ACPI_DESCRIPTOR_INTERRUPT (ACPI_DESCRIPTOR_LARGE | 9) +#define ACPI_DESCRIPTOR_GPIO (ACPI_DESCRIPTOR_LARGE | 12) +#define ACPI_DESCRIPTOR_SERIAL_BUS (ACPI_DESCRIPTOR_LARGE | 14) + /* Length of a full path to an ACPI device */ #define ACPI_PATH_MAX 30 @@ -31,6 +37,59 @@ enum acpi_dev_status { ACPI_DSTATUS_SHOW_IN_UI, }; +/** enum acpi_irq_mode - edge/level trigger mode */ +enum acpi_irq_mode { + ACPI_IRQ_EDGE_TRIGGERED, + ACPI_IRQ_LEVEL_TRIGGERED, +}; + +/** + * enum acpi_irq_polarity - polarity of interrupt + * + * @ACPI_IRQ_ACTIVE_LOW - for ACPI_IRQ_EDGE_TRIGGERED this means falling edge + * @ACPI_IRQ_ACTIVE_HIGH - for ACPI_IRQ_EDGE_TRIGGERED this means rising edge + * @ACPI_IRQ_ACTIVE_BOTH - not meaningful for ACPI_IRQ_EDGE_TRIGGERED + */ +enum acpi_irq_polarity { + ACPI_IRQ_ACTIVE_LOW, + ACPI_IRQ_ACTIVE_HIGH, + ACPI_IRQ_ACTIVE_BOTH, +}; + +/** + * enum acpi_irq_shared - whether interrupt is shared or not + * + * @ACPI_IRQ_EXCLUSIVE: only this device uses the interrupt + * @ACPI_IRQ_SHARED: other devices may use this interrupt + */ +enum acpi_irq_shared { + ACPI_IRQ_EXCLUSIVE, + ACPI_IRQ_SHARED, +}; + +/** enum acpi_irq_wake - indicates whether this interrupt can wake the device */ +enum acpi_irq_wake { + ACPI_IRQ_NO_WAKE, + ACPI_IRQ_WAKE, +}; + +/** + * struct acpi_irq - representation of an ACPI interrupt + * + * @pin: ACPI pin that is monitored for the interrupt + * @mode: Edge/level triggering + * @polarity: Interrupt polarity + * @shared: Whether interrupt is shared or not + * @wake: Whether interrupt can wake the device from sleep + */ +struct acpi_irq { + unsigned int pin; + enum acpi_irq_mode mode; + enum acpi_irq_polarity polarity; + enum acpi_irq_shared shared; + enum acpi_irq_wake wake; +}; + /** * acpi_device_path() - Get the full path to an ACPI device * diff --git a/include/irq.h b/include/irq.h index b71afe9bee..8527e4dd79 100644 --- a/include/irq.h +++ b/include/irq.h @@ -8,6 +8,9 @@ #ifndef __irq_H #define __irq_H +struct acpi_irq; +struct ofnode_phandle_args; + /* * Interrupt controller types available. You can find a particular one with * irq_first_device_type() @@ -24,10 +27,12 @@ enum irq_dev_t { * * @dev: IRQ device that handles this irq * @id: ID to identify this irq with the device + * @flags: Flags associated with this interrupt (IRQ_TYPE_...) */ struct irq { struct udevice *dev; ulong id; + ulong flags; }; /** @@ -119,10 +124,36 @@ struct irq_ops { * @return 0 if OK, or a negative error code. */ int (*free)(struct irq *irq); + +#if CONFIG_IS_ENABLED(ACPIGEN) + /** + * get_acpi() - Get the ACPI info for an irq + * + * This converts a irq to an ACPI structure for adding to the ACPI + * tables. + * + * @irq: irq to convert + * @acpi_irq: Output ACPI interrupt information + * @return ACPI pin number or -ve on error + */ + int (*get_acpi)(const struct irq *irq, struct acpi_irq *acpi_irq); +#endif }; #define irq_get_ops(dev) ((struct irq_ops *)(dev)->driver->ops) +/** + * irq_is_valid() - Check if an IRQ is valid + * + * @irq: IRQ description containing device and ID, e.g. previously + * returned by irq_get_by_index() + * @return true if valid, false if not + */ +static inline bool irq_is_valid(const struct irq *irq) +{ + return irq->dev != NULL; +} + /** * irq_route_pmc_gpio_gpe() - Get the GPIO for an event * @@ -223,4 +254,16 @@ int irq_free(struct irq *irq); */ int irq_first_device_type(enum irq_dev_t type, struct udevice **devp); +/** + * irq_get_acpi() - Get the ACPI info for an irq + * + * This converts a irq to an ACPI structure for adding to the ACPI + * tables. + * + * @irq: irq to convert + * @acpi_irq: Output ACPI interrupt information + * @return ACPI pin number or -ve on error + */ +int irq_get_acpi(const struct irq *irq, struct acpi_irq *acpi_irq); + #endif diff --git a/test/dm/irq.c b/test/dm/irq.c index 192d80d7e1..50e505e657 100644 --- a/test/dm/irq.c +++ b/test/dm/irq.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -75,3 +76,24 @@ static int dm_test_request(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_request, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test of irq_get_acpi() */ +static int dm_test_irq_get_acpi(struct unit_test_state *uts) +{ + struct acpi_irq airq; + struct udevice *dev; + struct irq irq; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev)); + ut_assertok(irq_get_by_index(dev, 0, &irq)); + + ut_assertok(irq_get_acpi(&irq, &airq)); + ut_asserteq(3, airq.pin); + ut_asserteq(ACPI_IRQ_LEVEL_TRIGGERED, airq.mode); + ut_asserteq(ACPI_IRQ_ACTIVE_HIGH, airq.polarity); + ut_asserteq(ACPI_IRQ_SHARED, airq.shared); + ut_asserteq(ACPI_IRQ_WAKE, airq.wake); + + return 0; +} +DM_TEST(dm_test_irq_get_acpi, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Sun Jun 14 02:54:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242239 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:54:53 -0600 Subject: [PATCH v3 05/35] acpi: Support generation of ACPI code In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.5.I8117fef4dff2607848f038cf14f6616f83997480@changeid> Add a new file to handle generating ACPI code programatically. This is used when information must be dynamically added to the tables, e.g. the SSDT. Initial support is just for writing simple values. Also add a 'base' value so that the table can be freed. This likely doesn't happen in normal code, but is nice to do in tests. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- (no changes since v2) Changes in v2: - Update to add a new 'base' field to struct acpi_ctx - Free the memory allocated to the table and context include/acpi/acpigen.h | 49 ++++++++++++++++++++++++++++++ include/dm/acpi.h | 2 ++ lib/acpi/Makefile | 1 + lib/acpi/acpi_table.c | 1 + lib/acpi/acpigen.c | 38 +++++++++++++++++++++++ test/dm/Makefile | 1 + test/dm/acpigen.c | 69 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 161 insertions(+) create mode 100644 include/acpi/acpigen.h create mode 100644 lib/acpi/acpigen.c create mode 100644 test/dm/acpigen.c diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h new file mode 100644 index 0000000000..8809cdb4e1 --- /dev/null +++ b/include/acpi/acpigen.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Core ACPI (Advanced Configuration and Power Interface) support + * + * Copyright 2019 Google LLC + * + * Modified from coreboot file acpigen.h + */ + +#ifndef __ACPI_ACPIGEN_H +#define __ACPI_ACPIGEN_H + +#include + +struct acpi_ctx; + +/** + * acpigen_get_current() - Get the current ACPI code output pointer + * + * @ctx: ACPI context pointer + * @return output pointer + */ +u8 *acpigen_get_current(struct acpi_ctx *ctx); + +/** + * acpigen_emit_byte() - Emit a byte to the ACPI code + * + * @ctx: ACPI context pointer + * @data: Value to output + */ +void acpigen_emit_byte(struct acpi_ctx *ctx, uint data); + +/** + * acpigen_emit_word() - Emit a 16-bit word to the ACPI code + * + * @ctx: ACPI context pointer + * @data: Value to output + */ +void acpigen_emit_word(struct acpi_ctx *ctx, uint data); + +/** + * acpigen_emit_dword() - Emit a 32-bit 'double word' to the ACPI code + * + * @ctx: ACPI context pointer + * @data: Value to output + */ +void acpigen_emit_dword(struct acpi_ctx *ctx, uint data); + +#endif diff --git a/include/dm/acpi.h b/include/dm/acpi.h index 7563a4c60a..696b1a96a0 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -29,6 +29,7 @@ * * This contains a few useful pieces of information used when writing * + * @base: Base address of ACPI tables * @current: Current address for writing * @rsdp: Pointer to the Root System Description Pointer, typically used when * adding a new table. The RSDP holds pointers to the RSDT and XSDT. @@ -36,6 +37,7 @@ * @xsdt: Pointer to the Extended System Description Table */ struct acpi_ctx { + void *base; void *current; struct acpi_rsdp *rsdp; struct acpi_rsdt *rsdt; diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile index caae6c01bd..85a1f774ad 100644 --- a/lib/acpi/Makefile +++ b/lib/acpi/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-y += acpigen.o obj-y += acpi_device.o obj-y += acpi_table.o diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index 431776666e..17d5258438 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -238,6 +238,7 @@ static void acpi_write_xsdt(struct acpi_xsdt *xsdt) void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start) { ctx->current = start; + ctx->current = start; /* Align ACPI tables to 16 byte */ acpi_align(ctx); diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c new file mode 100644 index 0000000000..59bd3af0b7 --- /dev/null +++ b/lib/acpi/acpigen.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generation of ACPI (Advanced Configuration and Power Interface) tables + * + * Copyright 2019 Google LLC + * Mostly taken from coreboot + */ + +#define LOG_CATEGORY LOGC_ACPI + +#include +#include +#include +#include + +u8 *acpigen_get_current(struct acpi_ctx *ctx) +{ + return ctx->current; +} + +void acpigen_emit_byte(struct acpi_ctx *ctx, uint data) +{ + *(u8 *)ctx->current++ = data; +} + +void acpigen_emit_word(struct acpi_ctx *ctx, uint data) +{ + acpigen_emit_byte(ctx, data & 0xff); + acpigen_emit_byte(ctx, (data >> 8) & 0xff); +} + +void acpigen_emit_dword(struct acpi_ctx *ctx, uint data) +{ + acpigen_emit_byte(ctx, data & 0xff); + acpigen_emit_byte(ctx, (data >> 8) & 0xff); + acpigen_emit_byte(ctx, (data >> 16) & 0xff); + acpigen_emit_byte(ctx, (data >> 24) & 0xff); +} diff --git a/test/dm/Makefile b/test/dm/Makefile index 6c18fd04ce..e3e0cccf01 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_UT_DM) += test-uclass.o obj-$(CONFIG_UT_DM) += core.o ifneq ($(CONFIG_SANDBOX),) obj-$(CONFIG_ACPIGEN) += acpi.o +obj-$(CONFIG_ACPIGEN) += acpigen.o obj-$(CONFIG_SOUND) += audio.o obj-$(CONFIG_BLK) += blk.o obj-$(CONFIG_BOARD) += board.o diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c new file mode 100644 index 0000000000..c353408021 --- /dev/null +++ b/test/dm/acpigen.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for ACPI code generation + * + * Copyright 2019 Google LLC + * Written by Simon Glass + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static int alloc_context(struct acpi_ctx **ctxp) +{ + struct acpi_ctx *ctx; + + *ctxp = NULL; + ctx = malloc(sizeof(*ctx)); + if (!ctx) + return -ENOMEM; + ctx->base = malloc(150); + if (!ctx->base) { + free(ctx); + return -ENOMEM; + } + ctx->current = ctx->base; + *ctxp = ctx; + + return 0; +} + +static void free_context(struct acpi_ctx **ctxp) +{ + free((*ctxp)->base); + free(*ctxp); + *ctxp = NULL; +} + +/* Test emitting simple types and acpigen_get_current() */ +static int dm_test_acpi_emit_simple(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_emit_byte(ctx, 0x23); + ut_asserteq(1, acpigen_get_current(ctx) - ptr); + ut_asserteq(0x23, *(u8 *)ptr); + + acpigen_emit_word(ctx, 0x1234); + ut_asserteq(3, acpigen_get_current(ctx) - ptr); + ut_asserteq(0x1234, get_unaligned((u16 *)(ptr + 1))); + + acpigen_emit_dword(ctx, 0x87654321); + ut_asserteq(7, acpigen_get_current(ctx) - ptr); + ut_asserteq(0x87654321, get_unaligned((u32 *)(ptr + 3))); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_emit_simple, 0); From patchwork Sun Jun 14 02:54:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242238 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:54:54 -0600 Subject: [PATCH v3 06/35] acpi: Support generation of interrupt descriptor In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200614025523.40183-3-sjg@chromium.org> Add a function to write an interrupt descriptor to the generated ACPI code. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- (no changes since v1) include/acpi/acpi_device.h | 15 +++++ lib/acpi/acpi_device.c | 118 +++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 31 ++++++++++ 3 files changed, 164 insertions(+) diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index 24895de0da..4f87cd003a 100644 --- a/include/acpi/acpi_device.h +++ b/include/acpi/acpi_device.h @@ -11,6 +11,8 @@ #include +struct acpi_ctx; +struct irq; struct udevice; /* ACPI descriptor values for common descriptors: SERIAL_BUS means I2C */ @@ -128,4 +130,17 @@ int acpi_device_scope(const struct udevice *dev, char *scope, int maxlen); */ enum acpi_dev_status acpi_device_status(const struct udevice *dev); +/** + * acpi_device_write_interrupt_irq() - Write an interrupt descriptor + * + * This writes an ACPI interrupt descriptor for the given interrupt, converting + * fields as needed. + * + * @ctx: ACPI context pointer + * @req_irq: Interrupt to output + * @return 0 if OK, -ve on error + */ +int acpi_device_write_interrupt_irq(struct acpi_ctx *ctx, + const struct irq *req_irq); + #endif diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c index 60f4fd8cd5..94f37b8d73 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -8,8 +8,10 @@ #include #include +#include #include #include +#include #include /** @@ -86,3 +88,119 @@ enum acpi_dev_status acpi_device_status(const struct udevice *dev) { return ACPI_DSTATUS_ALL_ON; } + +/** + * acpi_device_write_zero_len() - Write a placeholder word value + * + * @return pointer to the zero word (for fixing up later) + */ +static void *acpi_device_write_zero_len(struct acpi_ctx *ctx) +{ + u8 *p = acpigen_get_current(ctx); + + acpigen_emit_word(ctx, 0); + + return p; +} + +/** + * acpi_device_fill_from_len() - Fill in a length value + * + * This calculated the number of bytes since the provided @start and writes it + * to @ptr, which was previous returned by acpi_device_write_zero_len(). + * + * @ptr: Word to update + * @start: Start address to count from to calculated the length + */ +static void acpi_device_fill_from_len(struct acpi_ctx *ctx, char *ptr, + u8 *start) +{ + u16 len = acpigen_get_current(ctx) - start; + + ptr[0] = len & 0xff; + ptr[1] = (len >> 8) & 0xff; +} + +/** + * acpi_device_fill_len() - Fill in a length value, excluding the length itself + * + * Fill in the length field with the value calculated from after the 16bit + * field to acpigen current. This is useful since the length value does not + * include the length field itself. + * + * This calls acpi_device_fill_from_len() passing @ptr + 2 as @start + * + * @ptr: Word to update. + */ +static void acpi_device_fill_len(struct acpi_ctx *ctx, void *ptr) +{ + acpi_device_fill_from_len(ctx, ptr, ptr + sizeof(u16)); +} + +/* ACPI 6.3 section 6.4.3.6: Extended Interrupt Descriptor */ +static int acpi_device_write_interrupt(struct acpi_ctx *ctx, + const struct acpi_irq *irq) +{ + void *desc_length; + u8 flags; + + if (!irq->pin) + return -ENOENT; + + /* This is supported by GpioInt() but not Interrupt() */ + if (irq->polarity == ACPI_IRQ_ACTIVE_BOTH) + return -EINVAL; + + /* Byte 0: Descriptor Type */ + acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_INTERRUPT); + + /* Byte 1-2: Length (filled in later) */ + desc_length = acpi_device_write_zero_len(ctx); + + /* + * Byte 3: Flags + * [7:5]: Reserved + * [4]: Wake (0=NO_WAKE 1=WAKE) + * [3]: Sharing (0=EXCLUSIVE 1=SHARED) + * [2]: Polarity (0=HIGH 1=LOW) + * [1]: Mode (0=LEVEL 1=EDGE) + * [0]: Resource (0=PRODUCER 1=CONSUMER) + */ + flags = BIT(0); /* ResourceConsumer */ + if (irq->mode == ACPI_IRQ_EDGE_TRIGGERED) + flags |= BIT(1); + if (irq->polarity == ACPI_IRQ_ACTIVE_LOW) + flags |= BIT(2); + if (irq->shared == ACPI_IRQ_SHARED) + flags |= BIT(3); + if (irq->wake == ACPI_IRQ_WAKE) + flags |= BIT(4); + acpigen_emit_byte(ctx, flags); + + /* Byte 4: Interrupt Table Entry Count */ + acpigen_emit_byte(ctx, 1); + + /* Byte 5-8: Interrupt Number */ + acpigen_emit_dword(ctx, irq->pin); + + /* Fill in Descriptor Length (account for len word) */ + acpi_device_fill_len(ctx, desc_length); + + return 0; +} + +int acpi_device_write_interrupt_irq(struct acpi_ctx *ctx, + const struct irq *req_irq) +{ + struct acpi_irq irq; + int ret; + + ret = irq_get_acpi(req_irq, &irq); + if (ret) + return log_msg_ret("get", ret); + ret = acpi_device_write_interrupt(ctx, &irq); + if (ret) + return log_msg_ret("write", ret); + + return 0; +} diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index c353408021..4ee5c9cfe2 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -8,8 +8,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -67,3 +69,32 @@ static int dm_test_acpi_emit_simple(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_emit_simple, 0); + +/* Test emitting an interrupt descriptor */ +static int dm_test_acpi_interrupt(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct udevice *dev; + struct irq irq; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev)); + ut_assertok(irq_get_by_index(dev, 0, &irq)); + + ut_assertok(acpi_device_write_interrupt_irq(ctx, &irq)); + ut_asserteq(9, acpigen_get_current(ctx) - ptr); + ut_asserteq(ACPI_DESCRIPTOR_INTERRUPT, ptr[0]); + ut_asserteq(6, get_unaligned((u16 *)(ptr + 1))); + ut_asserteq(0x19, ptr[3]); + ut_asserteq(1, ptr[4]); + ut_asserteq(3, get_unaligned((u32 *)(ptr + 5))); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_interrupt, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Sun Jun 14 02:54:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242244 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:54:55 -0600 Subject: [PATCH v3 07/35] gpio: Add a method to convert a GPIO to ACPI In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.7.I29adaf758a9ea81f51ffbecf89b53983968a0d2a@changeid> When generating ACPI tables we need to convert GPIOs in U-Boot to the ACPI structures required by ACPI. This is a SoC-specific conversion and cannot be handled by generic code, so add a new GPIO method to do the conversion. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- (no changes since v1) Changes in v1: - Update sandbox driver slightly for testing drivers/gpio/gpio-uclass.c | 21 +++++++++ drivers/gpio/sandbox.c | 86 ++++++++++++++++++++++++++++++++++++ include/acpi/acpi_device.h | 90 ++++++++++++++++++++++++++++++++++++++ include/asm-generic/gpio.h | 27 ++++++++++++ test/dm/gpio.c | 62 ++++++++++++++++++++++++++ test/dm/irq.c | 1 + 6 files changed, 287 insertions(+) diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 9eeab22eef..e2f4d7e02c 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -809,6 +810,26 @@ int gpio_get_status(struct udevice *dev, int offset, char *buf, int buffsize) return 0; } +#if CONFIG_IS_ENABLED(ACPIGEN) +int gpio_get_acpi(const struct gpio_desc *desc, struct acpi_gpio *gpio) +{ + struct dm_gpio_ops *ops; + + if (!dm_gpio_is_valid(desc)) { + /* Indicate that the GPIO is not valid */ + gpio->pin_count = 0; + gpio->pins[0] = 0; + return -EINVAL; + } + + ops = gpio_get_ops(desc->dev); + if (!ops->get_acpi) + return -ENOSYS; + + return ops->get_acpi(desc, gpio); +} +#endif + int gpio_claim_vector(const int *gpio_num_array, const char *fmt) { int i, ret; diff --git a/drivers/gpio/sandbox.c b/drivers/gpio/sandbox.c index 98b7fa4bb3..6cd2a1455a 100644 --- a/drivers/gpio/sandbox.c +++ b/drivers/gpio/sandbox.c @@ -8,7 +8,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -197,6 +199,72 @@ static int sb_gpio_get_dir_flags(struct udevice *dev, unsigned int offset, return 0; } +#if CONFIG_IS_ENABLED(ACPIGEN) +static int sb_gpio_get_acpi(const struct gpio_desc *desc, + struct acpi_gpio *gpio) +{ + int ret; + + /* All of these values are just used for testing */ + if (desc->flags & GPIOD_ACTIVE_LOW) { + gpio->pin_count = 1; + gpio->pins[0] = desc->offset; + gpio->pin0_addr = 0x80012 + desc->offset; + gpio->type = ACPI_GPIO_TYPE_INTERRUPT; + gpio->pull = ACPI_GPIO_PULL_DOWN; + ret = acpi_device_scope(desc->dev, gpio->resource, + sizeof(gpio->resource)); + if (ret) + return log_ret(ret); + gpio->interrupt_debounce_timeout = 4321; + memset(&gpio->irq, '\0', sizeof(gpio->irq)); + + /* We use the GpioInt part */ + gpio->irq.pin = desc->offset; + gpio->irq.polarity = ACPI_IRQ_ACTIVE_BOTH; + gpio->irq.shared = ACPI_IRQ_SHARED; + gpio->irq.wake = ACPI_IRQ_WAKE; + + /* The GpioIo part is not used */ + gpio->output_drive_strength = 0; + gpio->io_shared = false; + gpio->io_restrict = 0; + gpio->polarity = ACPI_GPIO_ACTIVE_LOW; + } else { + gpio->pin_count = 1; + gpio->pins[0] = desc->offset; + gpio->pin0_addr = 0xc00dc + desc->offset; + gpio->type = ACPI_GPIO_TYPE_IO; + gpio->pull = ACPI_GPIO_PULL_UP; + ret = acpi_device_scope(desc->dev, gpio->resource, + sizeof(gpio->resource)); + if (ret) + return log_ret(ret); + gpio->interrupt_debounce_timeout = 0; + + /* The GpioInt part is not used */ + memset(&gpio->irq, '\0', sizeof(gpio->irq)); + + /* We use the GpioIo part */ + gpio->output_drive_strength = 1234; + gpio->io_shared = true; + gpio->io_restrict = ACPI_GPIO_IO_RESTRICT_INPUT; + gpio->polarity = 0; + } + + return 0; +} + +static int sb_gpio_get_name(const struct udevice *dev, char *out_name) +{ + return acpi_copy_name(out_name, "GPIO"); +} + +struct acpi_ops gpio_sandbox_acpi_ops = { + .get_name = sb_gpio_get_name, +}; +#endif /* ACPIGEN */ + static const struct dm_gpio_ops gpio_sandbox_ops = { .direction_input = sb_gpio_direction_input, .direction_output = sb_gpio_direction_output, @@ -206,6 +274,9 @@ static const struct dm_gpio_ops gpio_sandbox_ops = { .xlate = sb_gpio_xlate, .set_dir_flags = sb_gpio_set_dir_flags, .get_dir_flags = sb_gpio_get_dir_flags, +#if CONFIG_IS_ENABLED(ACPIGEN) + .get_acpi = sb_gpio_get_acpi, +#endif }; static int sandbox_gpio_ofdata_to_platdata(struct udevice *dev) @@ -252,6 +323,7 @@ U_BOOT_DRIVER(gpio_sandbox) = { .probe = gpio_sandbox_probe, .remove = gpio_sandbox_remove, .ops = &gpio_sandbox_ops, + ACPI_OPS_PTR(&gpio_sandbox_acpi_ops) }; /* pincontrol: used only to check GPIO pin configuration (pinmux command) */ @@ -419,6 +491,13 @@ static int sb_pinctrl_get_pin_muxing(struct udevice *dev, return 0; } +#if CONFIG_IS_ENABLED(ACPIGEN) +static int sb_pinctrl_get_name(const struct udevice *dev, char *out_name) +{ + return acpi_copy_name(out_name, "PINC"); +} +#endif + static int sandbox_pinctrl_probe(struct udevice *dev) { struct sb_pinctrl_priv *priv = dev_get_priv(dev); @@ -434,6 +513,12 @@ static struct pinctrl_ops sandbox_pinctrl_gpio_ops = { .get_pin_muxing = sb_pinctrl_get_pin_muxing, }; +#if CONFIG_IS_ENABLED(ACPIGEN) +struct acpi_ops pinctrl_sandbox_acpi_ops = { + .get_name = sb_pinctrl_get_name, +}; +#endif + static const struct udevice_id sandbox_pinctrl_gpio_match[] = { { .compatible = "sandbox,pinctrl-gpio" }, { /* sentinel */ } @@ -447,4 +532,5 @@ U_BOOT_DRIVER(sandbox_pinctrl_gpio) = { .bind = dm_scan_fdt_dev, .probe = sandbox_pinctrl_probe, .priv_auto_alloc_size = sizeof(struct sb_pinctrl_priv), + ACPI_OPS_PTR(&pinctrl_sandbox_acpi_ops) }; diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index 4f87cd003a..cb9166aeae 100644 --- a/include/acpi/acpi_device.h +++ b/include/acpi/acpi_device.h @@ -92,6 +92,96 @@ struct acpi_irq { enum acpi_irq_wake wake; }; +/** + * enum acpi_gpio_type - type of the descriptor + * + * @ACPI_GPIO_TYPE_INTERRUPT: GpioInterrupt + * @ACPI_GPIO_TYPE_IO: GpioIo + */ +enum acpi_gpio_type { + ACPI_GPIO_TYPE_INTERRUPT, + ACPI_GPIO_TYPE_IO, +}; + +/** + * enum acpi_gpio_pull - pull direction + * + * @ACPI_GPIO_PULL_DEFAULT: Use default value for pin + * @ACPI_GPIO_PULL_UP: Pull up + * @ACPI_GPIO_PULL_DOWN: Pull down + * @ACPI_GPIO_PULL_NONE: No pullup/pulldown + */ +enum acpi_gpio_pull { + ACPI_GPIO_PULL_DEFAULT, + ACPI_GPIO_PULL_UP, + ACPI_GPIO_PULL_DOWN, + ACPI_GPIO_PULL_NONE, +}; + +/** + * enum acpi_gpio_io_restrict - controls input/output of pin + * + * @ACPI_GPIO_IO_RESTRICT_NONE: no restrictions + * @ACPI_GPIO_IO_RESTRICT_INPUT: input only (no output) + * @ACPI_GPIO_IO_RESTRICT_OUTPUT: output only (no input) + * @ACPI_GPIO_IO_RESTRICT_PRESERVE: preserve settings when driver not active + */ +enum acpi_gpio_io_restrict { + ACPI_GPIO_IO_RESTRICT_NONE, + ACPI_GPIO_IO_RESTRICT_INPUT, + ACPI_GPIO_IO_RESTRICT_OUTPUT, + ACPI_GPIO_IO_RESTRICT_PRESERVE, +}; + +/** enum acpi_gpio_polarity - controls the GPIO polarity */ +enum acpi_gpio_polarity { + ACPI_GPIO_ACTIVE_HIGH = 0, + ACPI_GPIO_ACTIVE_LOW = 1, +}; + +#define ACPI_GPIO_REVISION_ID 1 +#define ACPI_GPIO_MAX_PINS 2 + +/** + * struct acpi_gpio - representation of an ACPI GPIO + * + * @pin_count: Number of pins represented + * @pins: List of pins + * @pin0_addr: Address in memory of the control registers for pin 0. This is + * used when generating ACPI tables + * @type: GPIO type + * @pull: Pullup/pulldown setting + * @resource: Resource name for this GPIO controller + * For GpioInt: + * @interrupt_debounce_timeout: Debounce timeout in units of 10us + * @irq: Interrupt + * + * For GpioIo: + * @output_drive_strength: Drive strength in units of 10uA + * @io_shared; true if GPIO is shared + * @io_restrict: I/O restriction setting + * @polarity: GPIO polarity + */ +struct acpi_gpio { + int pin_count; + u16 pins[ACPI_GPIO_MAX_PINS]; + ulong pin0_addr; + + enum acpi_gpio_type type; + enum acpi_gpio_pull pull; + char resource[ACPI_PATH_MAX]; + + /* GpioInt */ + u16 interrupt_debounce_timeout; + struct acpi_irq irq; + + /* GpioIo */ + u16 output_drive_strength; + bool io_shared; + enum acpi_gpio_io_restrict io_restrict; + enum acpi_gpio_polarity polarity; +}; + /** * acpi_device_path() - Get the full path to an ACPI device * diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index e16c2f31d9..a57dd2665c 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -10,6 +10,7 @@ #include #include +struct acpi_gpio; struct ofnode_phandle_args; /* @@ -329,6 +330,20 @@ struct dm_gpio_ops { */ int (*get_dir_flags)(struct udevice *dev, unsigned int offset, ulong *flags); + +#if CONFIG_IS_ENABLED(ACPIGEN) + /** + * get_acpi() - Get the ACPI info for a GPIO + * + * This converts a GPIO to an ACPI structure for adding to the ACPI + * tables. + * + * @desc: GPIO description to convert + * @gpio: Output ACPI GPIO information + * @return ACPI pin number or -ve on error + */ + int (*get_acpi)(const struct gpio_desc *desc, struct acpi_gpio *gpio); +#endif }; /** @@ -674,4 +689,16 @@ int dm_gpio_get_dir_flags(struct gpio_desc *desc, ulong *flags); */ int gpio_get_number(const struct gpio_desc *desc); +/** + * gpio_get_acpi() - Get the ACPI pin for a GPIO + * + * This converts a GPIO to an ACPI pin number for adding to the ACPI + * tables. If the GPIO is invalid, the pin_count and pins[0] are set to 0 + * + * @desc: GPIO description to convert + * @gpio: Output ACPI GPIO information + * @return ACPI pin number or -ve on error + */ +int gpio_get_acpi(const struct gpio_desc *desc, struct acpi_gpio *gpio); + #endif /* _ASM_GENERIC_GPIO_H_ */ diff --git a/test/dm/gpio.c b/test/dm/gpio.c index b5ee4e4f87..843ba2c894 100644 --- a/test/dm/gpio.c +++ b/test/dm/gpio.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -385,3 +386,64 @@ static int dm_test_gpio_get_dir_flags(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_gpio_get_dir_flags, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test of gpio_get_acpi() */ +static int dm_test_gpio_get_acpi(struct unit_test_state *uts) +{ + struct acpi_gpio agpio; + struct udevice *dev; + struct gpio_desc desc; + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0)); + + /* See sb_gpio_get_acpi() */ + ut_assertok(gpio_get_acpi(&desc, &agpio)); + ut_asserteq(1, agpio.pin_count); + ut_asserteq(4, agpio.pins[0]); + ut_asserteq(ACPI_GPIO_TYPE_IO, agpio.type); + ut_asserteq(ACPI_GPIO_PULL_UP, agpio.pull); + ut_asserteq_str("\\_SB.PINC", agpio.resource); + ut_asserteq(0, agpio.interrupt_debounce_timeout); + ut_asserteq(0, agpio.irq.pin); + ut_asserteq(1234, agpio.output_drive_strength); + ut_asserteq(true, agpio.io_shared); + ut_asserteq(ACPI_GPIO_IO_RESTRICT_INPUT, agpio.io_restrict); + ut_asserteq(ACPI_GPIO_ACTIVE_HIGH, agpio.polarity); + + return 0; +} +DM_TEST(dm_test_gpio_get_acpi, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test of gpio_get_acpi() with an interrupt GPIO */ +static int dm_test_gpio_get_acpi_irq(struct unit_test_state *uts) +{ + struct acpi_gpio agpio; + struct udevice *dev; + struct gpio_desc desc; + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &desc, 0)); + + /* See sb_gpio_get_acpi() */ + ut_assertok(gpio_get_acpi(&desc, &agpio)); + ut_asserteq(1, agpio.pin_count); + ut_asserteq(6, agpio.pins[0]); + ut_asserteq(ACPI_GPIO_TYPE_INTERRUPT, agpio.type); + ut_asserteq(ACPI_GPIO_PULL_DOWN, agpio.pull); + ut_asserteq_str("\\_SB.PINC", agpio.resource); + ut_asserteq(4321, agpio.interrupt_debounce_timeout); + ut_asserteq(6, agpio.irq.pin); + ut_asserteq(ACPI_IRQ_ACTIVE_BOTH, agpio.irq.polarity); + ut_asserteq(ACPI_IRQ_SHARED, agpio.irq.shared); + ut_asserteq(true, agpio.irq.wake); + ut_asserteq(0, agpio.output_drive_strength); + ut_asserteq(false, agpio.io_shared); + ut_asserteq(0, agpio.io_restrict); + ut_asserteq(ACPI_GPIO_ACTIVE_LOW, agpio.polarity); + + return 0; +} +DM_TEST(dm_test_gpio_get_acpi_irq, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/irq.c b/test/dm/irq.c index 50e505e657..51bae31b0f 100644 --- a/test/dm/irq.c +++ b/test/dm/irq.c @@ -87,6 +87,7 @@ static int dm_test_irq_get_acpi(struct unit_test_state *uts) ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev)); ut_assertok(irq_get_by_index(dev, 0, &irq)); + /* see sandbox_get_acpi() */ ut_assertok(irq_get_acpi(&irq, &airq)); ut_asserteq(3, airq.pin); ut_asserteq(ACPI_IRQ_LEVEL_TRIGGERED, airq.mode); From patchwork Sun Jun 14 02:54:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242240 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:54:56 -0600 Subject: [PATCH v3 08/35] acpi: Support string output In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.8.I16b67cc18d7b82670005ce5aabd2f162f60a50a6@changeid> Add support for output of strings and streams of bytes. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- (no changes since v1) include/acpi/acpigen.h | 19 +++++++++++++++++++ lib/acpi/acpigen.c | 14 ++++++++++++++ test/dm/acpigen.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 8809cdb4e1..7365cce738 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -46,4 +46,23 @@ void acpigen_emit_word(struct acpi_ctx *ctx, uint data); */ void acpigen_emit_dword(struct acpi_ctx *ctx, uint data); +/** + * acpigen_emit_stream() - Emit a stream of bytes + * + * @ctx: ACPI context pointer + * @data: Data to output + * @size: Size of data in bytes + */ +void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size); + +/** + * acpigen_emit_string() - Emit a string + * + * Emit a string with a nul terminator + * + * @ctx: ACPI context pointer + * @str: String to output, or NULL for an empty string + */ +void acpigen_emit_string(struct acpi_ctx *ctx, const char *str); + #endif diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index 59bd3af0b7..1223f0d1c4 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -36,3 +36,17 @@ void acpigen_emit_dword(struct acpi_ctx *ctx, uint data) acpigen_emit_byte(ctx, (data >> 16) & 0xff); acpigen_emit_byte(ctx, (data >> 24) & 0xff); } + +void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size) +{ + int i; + + for (i = 0; i < size; i++) + acpigen_emit_byte(ctx, data[i]); +} + +void acpigen_emit_string(struct acpi_ctx *ctx, const char *str) +{ + acpigen_emit_stream(ctx, str, str ? strlen(str) : 0); + acpigen_emit_byte(ctx, '\0'); /* NUL */ +} diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 4ee5c9cfe2..fac9886f2e 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -17,6 +17,9 @@ #include #include +#define TEST_STRING "frogmore" +#define TEST_STREAM2 "\xfa\xde" + static int alloc_context(struct acpi_ctx **ctxp) { struct acpi_ctx *ctx; @@ -70,6 +73,45 @@ static int dm_test_acpi_emit_simple(struct unit_test_state *uts) } DM_TEST(dm_test_acpi_emit_simple, 0); +/* Test emitting a stream */ +static int dm_test_acpi_emit_stream(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_emit_stream(ctx, TEST_STREAM2, 2); + ut_asserteq(2, acpigen_get_current(ctx) - ptr); + ut_asserteq((u8)TEST_STREAM2[0], ptr[0]); + ut_asserteq((u8)TEST_STREAM2[1], ptr[1]); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_emit_stream, 0); + +/* Test emitting a string */ +static int dm_test_acpi_emit_string(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_emit_string(ctx, TEST_STRING); + ut_asserteq(sizeof(TEST_STRING), acpigen_get_current(ctx) - ptr); + ut_asserteq_str(TEST_STRING, (char *)ptr); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_emit_string, 0); + /* Test emitting an interrupt descriptor */ static int dm_test_acpi_interrupt(struct unit_test_state *uts) { From patchwork Sun Jun 14 02:54:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242243 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:54:57 -0600 Subject: [PATCH v3 09/35] acpi: Support generation of GPIO descriptor In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.9.I6aa22eaeb633cacd7b056f0e7199b5d15b557a84@changeid> Add a function to write a GPIO descriptor to the generated ACPI code. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- (no changes since v1) include/acpi/acpi_device.h | 22 ++++++ lib/acpi/acpi_device.c | 151 +++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 89 ++++++++++++++++++++++ 3 files changed, 262 insertions(+) diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index cb9166aeae..5f229d79cf 100644 --- a/include/acpi/acpi_device.h +++ b/include/acpi/acpi_device.h @@ -12,6 +12,7 @@ #include struct acpi_ctx; +struct gpio_desc; struct irq; struct udevice; @@ -233,4 +234,25 @@ enum acpi_dev_status acpi_device_status(const struct udevice *dev); int acpi_device_write_interrupt_irq(struct acpi_ctx *ctx, const struct irq *req_irq); +/** + * acpi_device_write_gpio() - Write GpioIo() or GpioInt() descriptor + * + * @gpio: GPIO information to write + * @return 0 if OK, -ve on error + */ +int acpi_device_write_gpio(struct acpi_ctx *ctx, const struct acpi_gpio *gpio); + +/** + * acpi_device_write_gpio_desc() - Write a GPIO to ACPI + * + * This creates a GPIO descriptor for a GPIO, including information ACPI needs + * to use it. The type is always ACPI_GPIO_TYPE_IO. + * + * @ctx: ACPI context pointer + * @desc: GPIO to write + * @return 0 if OK, -ve on error + */ +int acpi_device_write_gpio_desc(struct acpi_ctx *ctx, + const struct gpio_desc *desc); + #endif diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c index 94f37b8d73..1cc7dd8b37 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -12,6 +12,7 @@ #include #include #include +#include #include /** @@ -204,3 +205,153 @@ int acpi_device_write_interrupt_irq(struct acpi_ctx *ctx, return 0; } + +/* ACPI 6.3 section 6.4.3.8.1 - GPIO Interrupt or I/O */ +int acpi_device_write_gpio(struct acpi_ctx *ctx, const struct acpi_gpio *gpio) +{ + void *start, *desc_length; + void *pin_table_offset, *vendor_data_offset, *resource_offset; + u16 flags = 0; + int pin; + + if (gpio->type > ACPI_GPIO_TYPE_IO) + return -EINVAL; + + start = acpigen_get_current(ctx); + + /* Byte 0: Descriptor Type */ + acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_GPIO); + + /* Byte 1-2: Length (fill in later) */ + desc_length = acpi_device_write_zero_len(ctx); + + /* Byte 3: Revision ID */ + acpigen_emit_byte(ctx, ACPI_GPIO_REVISION_ID); + + /* Byte 4: GpioIo or GpioInt */ + acpigen_emit_byte(ctx, gpio->type); + + /* + * Byte 5-6: General Flags + * [15:1]: 0 => Reserved + * [0]: 1 => ResourceConsumer + */ + acpigen_emit_word(ctx, 1 << 0); + + switch (gpio->type) { + case ACPI_GPIO_TYPE_INTERRUPT: + /* + * Byte 7-8: GPIO Interrupt Flags + * [15:5]: 0 => Reserved + * [4]: Wake (0=NO_WAKE 1=WAKE) + * [3]: Sharing (0=EXCLUSIVE 1=SHARED) + * [2:1]: Polarity (0=HIGH 1=LOW 2=BOTH) + * [0]: Mode (0=LEVEL 1=EDGE) + */ + if (gpio->irq.mode == ACPI_IRQ_EDGE_TRIGGERED) + flags |= 1 << 0; + if (gpio->irq.shared == ACPI_IRQ_SHARED) + flags |= 1 << 3; + if (gpio->irq.wake == ACPI_IRQ_WAKE) + flags |= 1 << 4; + + switch (gpio->irq.polarity) { + case ACPI_IRQ_ACTIVE_HIGH: + flags |= 0 << 1; + break; + case ACPI_IRQ_ACTIVE_LOW: + flags |= 1 << 1; + break; + case ACPI_IRQ_ACTIVE_BOTH: + flags |= 2 << 1; + break; + } + break; + + case ACPI_GPIO_TYPE_IO: + /* + * Byte 7-8: GPIO IO Flags + * [15:4]: 0 => Reserved + * [3]: Sharing (0=EXCLUSIVE 1=SHARED) + * [2]: 0 => Reserved + * [1:0]: IO Restriction + * 0 => IoRestrictionNone + * 1 => IoRestrictionInputOnly + * 2 => IoRestrictionOutputOnly + * 3 => IoRestrictionNoneAndPreserve + */ + flags |= gpio->io_restrict & 3; + if (gpio->io_shared) + flags |= 1 << 3; + break; + } + acpigen_emit_word(ctx, flags); + + /* + * Byte 9: Pin Configuration + * 0x01 => Default (no configuration applied) + * 0x02 => Pull-up + * 0x03 => Pull-down + * 0x04-0x7F => Reserved + * 0x80-0xff => Vendor defined + */ + acpigen_emit_byte(ctx, gpio->pull); + + /* Byte 10-11: Output Drive Strength in 1/100 mA */ + acpigen_emit_word(ctx, gpio->output_drive_strength); + + /* Byte 12-13: Debounce Timeout in 1/100 ms */ + acpigen_emit_word(ctx, gpio->interrupt_debounce_timeout); + + /* Byte 14-15: Pin Table Offset, relative to start */ + pin_table_offset = acpi_device_write_zero_len(ctx); + + /* Byte 16: Reserved */ + acpigen_emit_byte(ctx, 0); + + /* Byte 17-18: Resource Source Name Offset, relative to start */ + resource_offset = acpi_device_write_zero_len(ctx); + + /* Byte 19-20: Vendor Data Offset, relative to start */ + vendor_data_offset = acpi_device_write_zero_len(ctx); + + /* Byte 21-22: Vendor Data Length */ + acpigen_emit_word(ctx, 0); + + /* Fill in Pin Table Offset */ + acpi_device_fill_from_len(ctx, pin_table_offset, start); + + /* Pin Table, one word for each pin */ + for (pin = 0; pin < gpio->pin_count; pin++) + acpigen_emit_word(ctx, gpio->pins[pin]); + + /* Fill in Resource Source Name Offset */ + acpi_device_fill_from_len(ctx, resource_offset, start); + + /* Resource Source Name String */ + acpigen_emit_string(ctx, gpio->resource); + + /* Fill in Vendor Data Offset */ + acpi_device_fill_from_len(ctx, vendor_data_offset, start); + + /* Fill in GPIO Descriptor Length (account for len word) */ + acpi_device_fill_len(ctx, desc_length); + + return 0; +} + +int acpi_device_write_gpio_desc(struct acpi_ctx *ctx, + const struct gpio_desc *desc) +{ + struct acpi_gpio gpio; + int ret; + + ret = gpio_get_acpi(desc, &gpio); + if (ret) + return log_msg_ret("desc", ret); + ret = acpi_device_write_gpio(ctx, &gpio); + if (ret) + return log_msg_ret("gpio", ret); + + return 0; +} diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index fac9886f2e..cc7d51c460 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -140,3 +141,91 @@ static int dm_test_acpi_interrupt(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_interrupt, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test emitting a GPIO descriptor */ +static int dm_test_acpi_gpio(struct unit_test_state *uts) +{ + struct gpio_desc desc; + struct acpi_ctx *ctx; + struct udevice *dev; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0)); + + ut_assertok(acpi_device_write_gpio_desc(ctx, &desc)); + ut_asserteq(35, acpigen_get_current(ctx) - ptr); + ut_asserteq(ACPI_DESCRIPTOR_GPIO, ptr[0]); + ut_asserteq(32, get_unaligned((u16 *)(ptr + 1))); + ut_asserteq(ACPI_GPIO_REVISION_ID, ptr[3]); + ut_asserteq(ACPI_GPIO_TYPE_IO, ptr[4]); + ut_asserteq(1, get_unaligned((u16 *)(ptr + 5))); + ut_asserteq(9, get_unaligned((u16 *)(ptr + 7))); + ut_asserteq(ACPI_GPIO_PULL_UP, ptr[9]); + ut_asserteq(1234, get_unaligned((u16 *)(ptr + 10))); + ut_asserteq(0, get_unaligned((u16 *)(ptr + 12))); + ut_asserteq(23, get_unaligned((u16 *)(ptr + 14))); + ut_asserteq(0, ptr[16]); + ut_asserteq(25, get_unaligned((u16 *)(ptr + 17))); + ut_asserteq(35, get_unaligned((u16 *)(ptr + 19))); + ut_asserteq(0, get_unaligned((u16 *)(ptr + 21))); + + /* pin0 */ + ut_asserteq(4, get_unaligned((u16 *)(ptr + 23))); + + ut_asserteq_str("\\_SB.PINC", (char *)ptr + 25); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_gpio, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test emitting a GPIO descriptor with an interrupt */ +static int dm_test_acpi_gpio_irq(struct unit_test_state *uts) +{ + struct gpio_desc desc; + struct acpi_ctx *ctx; + struct udevice *dev; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &desc, 0)); + + ut_assertok(acpi_device_write_gpio_desc(ctx, &desc)); + ut_asserteq(35, acpigen_get_current(ctx) - ptr); + ut_asserteq(ACPI_DESCRIPTOR_GPIO, ptr[0]); + ut_asserteq(32, get_unaligned((u16 *)(ptr + 1))); + ut_asserteq(ACPI_GPIO_REVISION_ID, ptr[3]); + ut_asserteq(ACPI_GPIO_TYPE_INTERRUPT, ptr[4]); + ut_asserteq(1, get_unaligned((u16 *)(ptr + 5))); + ut_asserteq(29, get_unaligned((u16 *)(ptr + 7))); + ut_asserteq(ACPI_GPIO_PULL_DOWN, ptr[9]); + ut_asserteq(0, get_unaligned((u16 *)(ptr + 10))); + ut_asserteq(4321, get_unaligned((u16 *)(ptr + 12))); + ut_asserteq(23, get_unaligned((u16 *)(ptr + 14))); + ut_asserteq(0, ptr[16]); + ut_asserteq(25, get_unaligned((u16 *)(ptr + 17))); + ut_asserteq(35, get_unaligned((u16 *)(ptr + 19))); + ut_asserteq(0, get_unaligned((u16 *)(ptr + 21))); + + /* pin0 */ + ut_asserteq(6, get_unaligned((u16 *)(ptr + 23))); + + ut_asserteq_str("\\_SB.PINC", (char *)ptr + 25); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_gpio_irq, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Sun Jun 14 02:54:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242242 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:54:58 -0600 Subject: [PATCH v3 10/35] acpi: Support generation of a GPIO/irq for a device In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.10.I9acfc65b118312ed0c6884f00ccea3def3cb3b32@changeid> Some devices use interrupts but some use GPIOs. Since these are fully specified in the device tree we can automatically produce the correct ACPI descriptor for a device. Add a function to handle this. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- Changes in v3: - Fix 'an GPIO is found' typo include/acpi/acpi_device.h | 15 ++++++++++++++ lib/acpi/acpi_device.c | 26 ++++++++++++++++++++++++ test/dm/acpigen.c | 41 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index 5f229d79cf..863bf7cf47 100644 --- a/include/acpi/acpi_device.h +++ b/include/acpi/acpi_device.h @@ -255,4 +255,19 @@ int acpi_device_write_gpio(struct acpi_ctx *ctx, const struct acpi_gpio *gpio); int acpi_device_write_gpio_desc(struct acpi_ctx *ctx, const struct gpio_desc *desc); +/** + * acpi_device_write_interrupt_or_gpio() - Write interrupt or GPIO to ACPI + * + * This reads an interrupt from the device tree "interrupts-extended" property, + * if available. If not it reads the first GPIO with the name @prop. + * + * If an interrupt is found, an ACPI interrupt descriptor is written to the ACPI + * output. If not, but if a GPIO is found, a GPIO descriptor is written. + * + * @return 0 if OK, -ve if neither an interrupt nor a GPIO could be found, or + * some other error occurred + */ +int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx, + struct udevice *dev, const char *prop); + #endif diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c index 1cc7dd8b37..bf16e8d9dd 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -355,3 +355,29 @@ int acpi_device_write_gpio_desc(struct acpi_ctx *ctx, return 0; } + +int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx, + struct udevice *dev, const char *prop) +{ + struct irq req_irq; + int ret; + + ret = irq_get_by_index(dev, 0, &req_irq); + if (!ret) { + ret = acpi_device_write_interrupt_irq(ctx, &req_irq); + if (ret) + return log_msg_ret("irq", ret); + } else { + struct gpio_desc req_gpio; + + ret = gpio_request_by_name(dev, prop, 0, &req_gpio, + GPIOD_IS_IN); + if (ret) + return log_msg_ret("no gpio", ret); + ret = acpi_device_write_gpio_desc(ctx, &req_gpio); + if (ret) + return log_msg_ret("gpio", ret); + } + + return 0; +} diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index cc7d51c460..bd96c2cd57 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #define TEST_STRING "frogmore" @@ -229,3 +230,43 @@ static int dm_test_acpi_gpio_irq(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_gpio_irq, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test emitting either a GPIO or interrupt descriptor */ +static int dm_test_acpi_interrupt_or_gpio(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct udevice *dev; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + /* This should produce an interrupt, even though it also has a GPIO */ + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(acpi_device_write_interrupt_or_gpio(ctx, dev, + "test2-gpios")); + ut_asserteq(ACPI_DESCRIPTOR_INTERRUPT, ptr[0]); + + /* This has no interrupt so should produce a GPIO */ + ptr = ctx->current; + ut_assertok(uclass_find_first_device(UCLASS_PANEL_BACKLIGHT, &dev)); + ut_assertok(acpi_device_write_interrupt_or_gpio(ctx, dev, + "enable-gpios")); + ut_asserteq(ACPI_DESCRIPTOR_GPIO, ptr[0]); + + /* This one has neither */ + ptr = acpigen_get_current(ctx); + ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 3, &dev)); + ut_asserteq_str("b-test", dev->name); + ut_asserteq(-ENOENT, + acpi_device_write_interrupt_or_gpio(ctx, dev, + "enable-gpios")); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_interrupt_or_gpio, + DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Sun Jun 14 02:54:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242245 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:54:59 -0600 Subject: [PATCH v3 11/35] acpi: Support generation of I2C descriptor In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.11.I9a92f5e4d3361484afce57b3afff40a3119a6103@changeid> Add a function to write a GPIO descriptor to the generated ACPI code. Reviewed-by: Wolfgang Wallner Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- Changes in v3: - Update comment in acpi_device_set_i2c() to talk about scope parameter Changes in v2: - Fix memset of I2C descriptor drivers/i2c/sandbox_i2c.c | 11 ++++ drivers/rtc/sandbox_rtc.c | 13 +++++ include/acpi/acpi_device.h | 36 +++++++++++++ lib/acpi/acpi_device.c | 107 +++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 32 +++++++++++ 5 files changed, 199 insertions(+) diff --git a/drivers/i2c/sandbox_i2c.c b/drivers/i2c/sandbox_i2c.c index f4ae2397a0..125026da90 100644 --- a/drivers/i2c/sandbox_i2c.c +++ b/drivers/i2c/sandbox_i2c.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -83,6 +84,15 @@ static int sandbox_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, return ops->xfer(emul, msg, nmsgs); } +static int sandbox_i2c_get_name(const struct udevice *dev, char *out_name) +{ + return acpi_copy_name(out_name, "SI2C"); +} + +struct acpi_ops sandbox_i2c_acpi_ops = { + .get_name = sandbox_i2c_get_name, +}; + static const struct dm_i2c_ops sandbox_i2c_ops = { .xfer = sandbox_i2c_xfer, }; @@ -98,4 +108,5 @@ U_BOOT_DRIVER(i2c_sandbox) = { .of_match = sandbox_i2c_ids, .ops = &sandbox_i2c_ops, .priv_auto_alloc_size = sizeof(struct sandbox_i2c_priv), + ACPI_OPS_PTR(&sandbox_i2c_acpi_ops) }; diff --git a/drivers/rtc/sandbox_rtc.c b/drivers/rtc/sandbox_rtc.c index b08d758a74..f2906c3397 100644 --- a/drivers/rtc/sandbox_rtc.c +++ b/drivers/rtc/sandbox_rtc.c @@ -9,6 +9,7 @@ #include #include #include +#include #define REG_COUNT 0x80 @@ -84,6 +85,17 @@ static int sandbox_rtc_write8(struct udevice *dev, unsigned int reg, int val) return dm_i2c_reg_write(dev, reg, val); } +#if CONFIG_IS_ENABLED(ACPIGEN) +static int sandbox_rtc_get_name(const struct udevice *dev, char *out_name) +{ + return acpi_copy_name(out_name, "RTCC"); +} + +struct acpi_ops sandbox_rtc_acpi_ops = { + .get_name = sandbox_rtc_get_name, +}; +#endif + static const struct rtc_ops sandbox_rtc_ops = { .get = sandbox_rtc_get, .set = sandbox_rtc_set, @@ -102,4 +114,5 @@ U_BOOT_DRIVER(rtc_sandbox) = { .id = UCLASS_RTC, .of_match = sandbox_rtc_ids, .ops = &sandbox_rtc_ops, + ACPI_OPS_PTR(&sandbox_rtc_acpi_ops) }; diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index 863bf7cf47..f738b03d58 100644 --- a/include/acpi/acpi_device.h +++ b/include/acpi/acpi_device.h @@ -9,6 +9,7 @@ #ifndef __ACPI_DEVICE_H #define __ACPI_DEVICE_H +#include #include struct acpi_ctx; @@ -183,6 +184,29 @@ struct acpi_gpio { enum acpi_gpio_polarity polarity; }; +/* ACPI Descriptors for Serial Bus interfaces */ +#define ACPI_SERIAL_BUS_TYPE_I2C 1 +#define ACPI_SERIAL_BUS_TYPE_SPI 2 +#define ACPI_I2C_SERIAL_BUS_REVISION_ID 1 /* TODO: upgrade to 2 */ +#define ACPI_I2C_TYPE_SPECIFIC_REVISION_ID 1 +#define ACPI_SPI_SERIAL_BUS_REVISION_ID 1 +#define ACPI_SPI_TYPE_SPECIFIC_REVISION_ID 1 + +/** + * struct acpi_i2c - representation of an ACPI I2C device + * + * @address: 7-bit or 10-bit I2C address + * @mode_10bit: Which address size is used + * @speed: Bus speed in Hz + * @resource: Resource name for the I2C controller + */ +struct acpi_i2c { + u16 address; + enum i2c_address_mode mode_10bit; + enum i2c_speed_rate speed; + const char *resource; +}; + /** * acpi_device_path() - Get the full path to an ACPI device * @@ -270,4 +294,16 @@ int acpi_device_write_gpio_desc(struct acpi_ctx *ctx, int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx, struct udevice *dev, const char *prop); +/** + * acpi_device_write_i2c_dev() - Write an I2C device to ACPI + * + * This creates a I2cSerialBus descriptor for an I2C device, including + * information ACPI needs to use it. + * + * @ctx: ACPI context pointer + * @dev: I2C device to write + * @return 0 if OK, -ve on error + */ +int acpi_device_write_i2c_dev(struct acpi_ctx *ctx, const struct udevice *dev); + #endif diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c index bf16e8d9dd..40d1a82d27 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -381,3 +381,110 @@ int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx, return 0; } + +/* ACPI 6.3 section 6.4.3.8.2.1 - I2cSerialBus() */ +static void acpi_device_write_i2c(struct acpi_ctx *ctx, + const struct acpi_i2c *i2c) +{ + void *desc_length, *type_length; + + /* Byte 0: Descriptor Type */ + acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_SERIAL_BUS); + + /* Byte 1+2: Length (filled in later) */ + desc_length = acpi_device_write_zero_len(ctx); + + /* Byte 3: Revision ID */ + acpigen_emit_byte(ctx, ACPI_I2C_SERIAL_BUS_REVISION_ID); + + /* Byte 4: Resource Source Index is Reserved */ + acpigen_emit_byte(ctx, 0); + + /* Byte 5: Serial Bus Type is I2C */ + acpigen_emit_byte(ctx, ACPI_SERIAL_BUS_TYPE_I2C); + + /* + * Byte 6: Flags + * [7:2]: 0 => Reserved + * [1]: 1 => ResourceConsumer + * [0]: 0 => ControllerInitiated + */ + acpigen_emit_byte(ctx, 1 << 1); + + /* + * Byte 7-8: Type Specific Flags + * [15:1]: 0 => Reserved + * [0]: 0 => 7bit, 1 => 10bit + */ + acpigen_emit_word(ctx, i2c->mode_10bit); + + /* Byte 9: Type Specific Revision ID */ + acpigen_emit_byte(ctx, ACPI_I2C_TYPE_SPECIFIC_REVISION_ID); + + /* Byte 10-11: I2C Type Data Length */ + type_length = acpi_device_write_zero_len(ctx); + + /* Byte 12-15: I2C Bus Speed */ + acpigen_emit_dword(ctx, i2c->speed); + + /* Byte 16-17: I2C Slave Address */ + acpigen_emit_word(ctx, i2c->address); + + /* Fill in Type Data Length */ + acpi_device_fill_len(ctx, type_length); + + /* Byte 18+: ResourceSource */ + acpigen_emit_string(ctx, i2c->resource); + + /* Fill in I2C Descriptor Length */ + acpi_device_fill_len(ctx, desc_length); +} + +/** + * acpi_device_set_i2c() - Set up an ACPI I2C struct from a device + * + * The value of @scope is not copied, but only referenced. This implies the + * caller has to ensure it stays valid for the lifetime of @i2c. + * + * @dev: I2C device to convert + * @i2c: Place to put the new structure + * @scope: Scope of the I2C device (this is the controller path) + * @return 0 (always) + */ +static int acpi_device_set_i2c(const struct udevice *dev, struct acpi_i2c *i2c, + const char *scope) +{ + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); + struct udevice *bus = dev_get_parent(dev); + + memset(i2c, '\0', sizeof(*i2c)); + i2c->address = chip->chip_addr; + i2c->mode_10bit = 0; + + /* + * i2c_bus->speed_hz is set if this device is probed, but if not we + * must use the device tree + */ + i2c->speed = dev_read_u32_default(bus, "clock-frequency", + I2C_SPEED_STANDARD_RATE); + i2c->resource = scope; + + return 0; +} + +int acpi_device_write_i2c_dev(struct acpi_ctx *ctx, const struct udevice *dev) +{ + char scope[ACPI_PATH_MAX]; + struct acpi_i2c i2c; + int ret; + + ret = acpi_device_scope(dev, scope, sizeof(scope)); + if (ret) + return log_msg_ret("scope", ret); + ret = acpi_device_set_i2c(dev, &i2c, scope); + if (ret) + return log_msg_ret("set", ret); + acpi_device_write_i2c(ctx, &i2c); + + return 0; +} diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index bd96c2cd57..53842be55a 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -270,3 +270,35 @@ static int dm_test_acpi_interrupt_or_gpio(struct unit_test_state *uts) } DM_TEST(dm_test_acpi_interrupt_or_gpio, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test emitting an I2C descriptor */ +static int dm_test_acpi_i2c(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct udevice *dev; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev)); + ut_assertok(acpi_device_write_i2c_dev(ctx, dev)); + ut_asserteq(28, acpigen_get_current(ctx) - ptr); + ut_asserteq(ACPI_DESCRIPTOR_SERIAL_BUS, ptr[0]); + ut_asserteq(25, get_unaligned((u16 *)(ptr + 1))); + ut_asserteq(ACPI_I2C_SERIAL_BUS_REVISION_ID, ptr[3]); + ut_asserteq(0, ptr[4]); + ut_asserteq(ACPI_SERIAL_BUS_TYPE_I2C, ptr[5]); + ut_asserteq(0, get_unaligned((u16 *)(ptr + 7))); + ut_asserteq(ACPI_I2C_TYPE_SPECIFIC_REVISION_ID, ptr[9]); + ut_asserteq(6, get_unaligned((u16 *)(ptr + 10))); + ut_asserteq(100000, get_unaligned((u32 *)(ptr + 12))); + ut_asserteq(0x43, get_unaligned((u16 *)(ptr + 16))); + ut_asserteq_str("\\_SB.SI2C", (char *)ptr + 18); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_i2c, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Sun Jun 14 02:55:00 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242246 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:00 -0600 Subject: [PATCH v3 12/35] acpi: Support generation of SPI descriptor In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.12.I533d36d6b9131850d53b716ea555a4e494fa14df@changeid> Add a function to write a SPI descriptor to the generated ACPI code. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- Changes in v3: - Make acpi_device_write_spi() static - Add an extra comment about scope to acpi_device_set_spi() - Use BIT() in a few places - Resist the temptation to go to >80 characters Changes in v2: - Fix memset of SPI descriptor drivers/spi/sandbox_spi.c | 11 ++++ include/acpi/acpi_device.h | 36 +++++++++++ include/spi.h | 4 +- lib/acpi/acpi_device.c | 124 +++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 36 +++++++++++ 5 files changed, 209 insertions(+), 2 deletions(-) diff --git a/drivers/spi/sandbox_spi.c b/drivers/spi/sandbox_spi.c index b0a46c8868..4264acc953 100644 --- a/drivers/spi/sandbox_spi.c +++ b/drivers/spi/sandbox_spi.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #ifndef CONFIG_SPI_IDLE_VAL @@ -133,6 +134,15 @@ static int sandbox_spi_get_mmap(struct udevice *dev, ulong *map_basep, return 0; } +static int sandbox_spi_get_name(const struct udevice *dev, char *out_name) +{ + return acpi_copy_name(out_name, "SSPI"); +} + +struct acpi_ops sandbox_spi_acpi_ops = { + .get_name = sandbox_spi_get_name, +}; + static const struct dm_spi_ops sandbox_spi_ops = { .xfer = sandbox_spi_xfer, .set_speed = sandbox_spi_set_speed, @@ -151,4 +161,5 @@ U_BOOT_DRIVER(spi_sandbox) = { .id = UCLASS_SPI, .of_match = sandbox_spi_ids, .ops = &sandbox_spi_ops, + ACPI_OPS_PTR(&sandbox_spi_acpi_ops) }; diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index f738b03d58..c0c96183e4 100644 --- a/include/acpi/acpi_device.h +++ b/include/acpi/acpi_device.h @@ -10,6 +10,7 @@ #define __ACPI_DEVICE_H #include +#include #include struct acpi_ctx; @@ -207,6 +208,29 @@ struct acpi_i2c { const char *resource; }; +/** + * struct acpi_spi - representation of an ACPI SPI device + * + * @device_select: Chip select used by this device (typically 0) + * @device_select_polarity: Polarity for the device + * @wire_mode: Number of wires used for SPI + * @speed: Bus speed in Hz + * @data_bit_length: Word length for SPI (typically 8) + * @clock_phase: Clock phase to capture data + * @clock_polarity: Bus polarity + * @resource: Resource name for the SPI controller + */ +struct acpi_spi { + u16 device_select; + enum spi_polarity device_select_polarity; + enum spi_wire_mode wire_mode; + unsigned int speed; + u8 data_bit_length; + enum spi_clock_phase clock_phase; + enum spi_polarity clock_polarity; + const char *resource; +}; + /** * acpi_device_path() - Get the full path to an ACPI device * @@ -306,4 +330,16 @@ int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx, */ int acpi_device_write_i2c_dev(struct acpi_ctx *ctx, const struct udevice *dev); +/** + * acpi_device_write_spi_dev() - Write a SPI device to ACPI + * + * This writes a serial bus descriptor for the SPI device so that ACPI can use + * it + * + * @ctx: ACPI context pointer + * @dev: SPI device to write + * @return 0 if OK, -ve on error + */ +int acpi_device_write_spi_dev(struct acpi_ctx *ctx, const struct udevice *dev); + #endif diff --git a/include/spi.h b/include/spi.h index 5cc6d6e008..f34533f54e 100644 --- a/include/spi.h +++ b/include/spi.h @@ -13,8 +13,8 @@ #include /* SPI mode flags */ -#define SPI_CPHA BIT(0) /* clock phase */ -#define SPI_CPOL BIT(1) /* clock polarity */ +#define SPI_CPHA BIT(0) /* clock phase (1 = SPI_CLOCK_PHASE_SECOND) */ +#define SPI_CPOL BIT(1) /* clock polarity (1 = SPI_POLARITY_HIGH) */ #define SPI_MODE_0 (0|0) /* (original MicroWire) */ #define SPI_MODE_1 (0|SPI_CPHA) #define SPI_MODE_2 (SPI_CPOL|0) diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c index 40d1a82d27..38101c8b96 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -488,3 +488,127 @@ int acpi_device_write_i2c_dev(struct acpi_ctx *ctx, const struct udevice *dev) return 0; } + +#ifdef CONFIG_SPI +/* ACPI 6.1 section 6.4.3.8.2.2 - SpiSerialBus() */ +static void acpi_device_write_spi(struct acpi_ctx *ctx, const struct acpi_spi *spi) +{ + void *desc_length, *type_length; + u16 flags = 0; + + /* Byte 0: Descriptor Type */ + acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_SERIAL_BUS); + + /* Byte 1+2: Length (filled in later) */ + desc_length = acpi_device_write_zero_len(ctx); + + /* Byte 3: Revision ID */ + acpigen_emit_byte(ctx, ACPI_SPI_SERIAL_BUS_REVISION_ID); + + /* Byte 4: Resource Source Index is Reserved */ + acpigen_emit_byte(ctx, 0); + + /* Byte 5: Serial Bus Type is SPI */ + acpigen_emit_byte(ctx, ACPI_SERIAL_BUS_TYPE_SPI); + + /* + * Byte 6: Flags + * [7:2]: 0 => Reserved + * [1]: 1 => ResourceConsumer + * [0]: 0 => ControllerInitiated + */ + acpigen_emit_byte(ctx, BIT(1)); + + /* + * Byte 7-8: Type Specific Flags + * [15:2]: 0 => Reserveda + * [1]: 0 => ActiveLow, 1 => ActiveHigh + * [0]: 0 => FourWire, 1 => ThreeWire + */ + if (spi->wire_mode == SPI_3_WIRE_MODE) + flags |= BIT(0); + if (spi->device_select_polarity == SPI_POLARITY_HIGH) + flags |= BIT(1); + acpigen_emit_word(ctx, flags); + + /* Byte 9: Type Specific Revision ID */ + acpigen_emit_byte(ctx, ACPI_SPI_TYPE_SPECIFIC_REVISION_ID); + + /* Byte 10-11: SPI Type Data Length */ + type_length = acpi_device_write_zero_len(ctx); + + /* Byte 12-15: Connection Speed */ + acpigen_emit_dword(ctx, spi->speed); + + /* Byte 16: Data Bit Length */ + acpigen_emit_byte(ctx, spi->data_bit_length); + + /* Byte 17: Clock Phase */ + acpigen_emit_byte(ctx, spi->clock_phase); + + /* Byte 18: Clock Polarity */ + acpigen_emit_byte(ctx, spi->clock_polarity); + + /* Byte 19-20: Device Selection */ + acpigen_emit_word(ctx, spi->device_select); + + /* Fill in Type Data Length */ + acpi_device_fill_len(ctx, type_length); + + /* Byte 21+: ResourceSource String */ + acpigen_emit_string(ctx, spi->resource); + + /* Fill in SPI Descriptor Length */ + acpi_device_fill_len(ctx, desc_length); +} + +/** + * acpi_device_set_spi() - Set up an ACPI SPI struct from a device + * + * The value of @scope is not copied, but only referenced. This implies the + * caller has to ensure it stays valid for the lifetime of @spi. + * + * @dev: SPI device to convert + * @spi: Place to put the new structure + * @scope: Scope of the SPI device (this is the controller path) + * @return 0 (always) + */ +static int acpi_device_set_spi(const struct udevice *dev, struct acpi_spi *spi, + const char *scope) +{ + struct dm_spi_slave_platdata *plat; + struct spi_slave *slave = dev_get_parent_priv(dev); + + plat = dev_get_parent_platdata(slave->dev); + memset(spi, '\0', sizeof(*spi)); + spi->device_select = plat->cs; + spi->device_select_polarity = SPI_POLARITY_LOW; + spi->wire_mode = SPI_4_WIRE_MODE; + spi->speed = plat->max_hz; + spi->data_bit_length = slave->wordlen; + spi->clock_phase = plat->mode & SPI_CPHA ? + SPI_CLOCK_PHASE_SECOND : SPI_CLOCK_PHASE_FIRST; + spi->clock_polarity = plat->mode & SPI_CPOL ? + SPI_POLARITY_HIGH : SPI_POLARITY_LOW; + spi->resource = scope; + + return 0; +} + +int acpi_device_write_spi_dev(struct acpi_ctx *ctx, const struct udevice *dev) +{ + char scope[ACPI_PATH_MAX]; + struct acpi_spi spi; + int ret; + + ret = acpi_device_scope(dev, scope, sizeof(scope)); + if (ret) + return log_msg_ret("scope", ret); + ret = acpi_device_set_spi(dev, &spi, scope); + if (ret) + return log_msg_ret("set", ret); + acpi_device_write_spi(ctx, &spi); + + return 0; +} +#endif /* CONFIG_SPI */ diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 53842be55a..571510722d 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -302,3 +302,39 @@ static int dm_test_acpi_i2c(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_i2c, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test emitting a SPI descriptor */ +static int dm_test_acpi_spi(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct udevice *dev; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + ut_assertok(uclass_first_device_err(UCLASS_SPI_FLASH, &dev)); + ut_assertok(acpi_device_write_spi_dev(ctx, dev)); + ut_asserteq(31, acpigen_get_current(ctx) - ptr); + ut_asserteq(ACPI_DESCRIPTOR_SERIAL_BUS, ptr[0]); + ut_asserteq(28, get_unaligned((u16 *)(ptr + 1))); + ut_asserteq(ACPI_SPI_SERIAL_BUS_REVISION_ID, ptr[3]); + ut_asserteq(0, ptr[4]); + ut_asserteq(ACPI_SERIAL_BUS_TYPE_SPI, ptr[5]); + ut_asserteq(2, ptr[6]); + ut_asserteq(0, get_unaligned((u16 *)(ptr + 7))); + ut_asserteq(ACPI_SPI_TYPE_SPECIFIC_REVISION_ID, ptr[9]); + ut_asserteq(9, get_unaligned((u16 *)(ptr + 10))); + ut_asserteq(40000000, get_unaligned((u32 *)(ptr + 12))); + ut_asserteq(8, ptr[16]); + ut_asserteq(0, ptr[17]); + ut_asserteq(0, ptr[18]); + ut_asserteq(0, get_unaligned((u16 *)(ptr + 19))); + ut_asserteq_str("\\_SB.SSPI", (char *)ptr + 21); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_spi, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Sun Jun 14 02:55:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242248 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:01 -0600 Subject: [PATCH v3 13/35] acpigen: Support writing a length In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.13.If6772cfcfc5b88c3cbfb77bb384676e74ddcd985@changeid> It is convenient to write a length value for preceding a block of data. Of course the length is not known or is hard to calculate a priori. So add a way to mark the start on a stack, so the length can be updated when known. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- Changes in v3: - Add a reference to the ACPI spec - Add a define for the 0x80 constant - Move the function comments into this patch include/acpi/acpigen.h | 38 +++++++++++++++++++++++++ include/dm/acpi.h | 7 +++++ lib/acpi/acpigen.c | 33 ++++++++++++++++++++++ test/dm/acpigen.c | 64 ++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 140 insertions(+), 2 deletions(-) diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 7365cce738..12cd1bd578 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -14,6 +14,9 @@ struct acpi_ctx; +/* Top 4 bits of the value used to indicate a three-byte length value */ +#define ACPI_PKG_LEN_3_BYTES 0x80 + /** * acpigen_get_current() - Get the current ACPI code output pointer * @@ -65,4 +68,39 @@ void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size); */ void acpigen_emit_string(struct acpi_ctx *ctx, const char *str); +/** + * acpigen_write_len_f() - Write a 'forward' length placeholder + * + * This adds space for a length value in the ACPI stream and pushes the current + * position (before the length) on the stack. After calling this you can write + * some data and then call acpigen_pop_len() to update the length value. + * + * Usage: + * + * acpigen_write_len_f() ------\ + * acpigen_write...() | + * acpigen_write...() | + * acpigen_write_len_f() --\ | + * acpigen_write...() | | + * acpigen_write...() | | + * acpigen_pop_len() ------/ | + * acpigen_write...() | + * acpigen_pop_len() ----------/ + * + * See ACPI 6.3 section 20.2.4 Package Length Encoding + * + * @ctx: ACPI context pointer + */ +void acpigen_write_len_f(struct acpi_ctx *ctx); + +/** + * acpigen_pop_len() - Update the previously stacked length placeholder + * + * Call this after the data for the block gas been written. It updates the + * top length value in the stack and pops it off. + * + * @ctx: ACPI context pointer + */ +void acpigen_pop_len(struct acpi_ctx *ctx); + #endif diff --git a/include/dm/acpi.h b/include/dm/acpi.h index 696b1a96a0..f27ca64507 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -22,6 +22,9 @@ /* Length of an ACPI name string including nul terminator */ #define ACPI_NAME_MAX (ACPI_NAME_LEN + 1) +/* Number of nested objects supported */ +#define ACPIGEN_LENSTACK_SIZE 10 + #if !defined(__ACPI__) /** @@ -35,6 +38,8 @@ * adding a new table. The RSDP holds pointers to the RSDT and XSDT. * @rsdt: Pointer to the Root System Description Table * @xsdt: Pointer to the Extended System Description Table + * @len_stack: Stack of 'length' words to fix up later + * @ltop: Points to current top of stack (0 = empty) */ struct acpi_ctx { void *base; @@ -42,6 +47,8 @@ struct acpi_ctx { struct acpi_rsdp *rsdp; struct acpi_rsdt *rsdt; struct acpi_xsdt *xsdt; + char *len_stack[ACPIGEN_LENSTACK_SIZE]; + int ltop; }; /** diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index 1223f0d1c4..2c2b604d80 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -37,6 +38,38 @@ void acpigen_emit_dword(struct acpi_ctx *ctx, uint data) acpigen_emit_byte(ctx, (data >> 24) & 0xff); } +/* + * Maximum length for an ACPI object generated by this code, + * + * If you need to change this, change acpigen_write_len_f(ctx) and + * acpigen_pop_len(ctx) + */ +#define ACPIGEN_MAXLEN 0xfffff + +void acpigen_write_len_f(struct acpi_ctx *ctx) +{ + assert(ctx->ltop < (ACPIGEN_LENSTACK_SIZE - 1)); + ctx->len_stack[ctx->ltop++] = ctx->current; + acpigen_emit_byte(ctx, 0); + acpigen_emit_byte(ctx, 0); + acpigen_emit_byte(ctx, 0); +} + +void acpigen_pop_len(struct acpi_ctx *ctx) +{ + int len; + char *p; + + assert(ctx->ltop > 0); + p = ctx->len_stack[--ctx->ltop]; + len = ctx->current - (void *)p; + assert(len <= ACPIGEN_MAXLEN); + /* generate store length for 0xfffff max */ + p[0] = ACPI_PKG_LEN_3_BYTES | (len & 0xf); + p[1] = len >> 4 & 0xff; + p[2] = len >> 12 & 0xff; +} + void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size) { int i; diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 571510722d..71f70ebf71 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -22,7 +22,7 @@ #define TEST_STRING "frogmore" #define TEST_STREAM2 "\xfa\xde" -static int alloc_context(struct acpi_ctx **ctxp) +static int alloc_context_size(struct acpi_ctx **ctxp, int size) { struct acpi_ctx *ctx; @@ -30,17 +30,23 @@ static int alloc_context(struct acpi_ctx **ctxp) ctx = malloc(sizeof(*ctx)); if (!ctx) return -ENOMEM; - ctx->base = malloc(150); + ctx->base = malloc(size); if (!ctx->base) { free(ctx); return -ENOMEM; } + ctx->ltop = 0; ctx->current = ctx->base; *ctxp = ctx; return 0; } +static int alloc_context(struct acpi_ctx **ctxp) +{ + return alloc_context_size(ctxp, 150); +} + static void free_context(struct acpi_ctx **ctxp) { free((*ctxp)->base); @@ -338,3 +344,57 @@ static int dm_test_acpi_spi(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_spi, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/** + * get_length() - decode a three-byte length field + * + * @ptr: Length encoded as per ACPI + * @return decoded length, or -EINVAL on error + */ +static int get_length(u8 *ptr) +{ + if (!(*ptr & 0x80)) + return -EINVAL; + + return (*ptr & 0xf) | ptr[1] << 4 | ptr[2] << 12; +} + +/* Test emitting a length */ +static int dm_test_acpi_len(struct unit_test_state *uts) +{ + const int size = 0xc0000; + struct acpi_ctx *ctx; + u8 *ptr; + int i; + + ut_assertok(alloc_context_size(&ctx, size)); + + ptr = acpigen_get_current(ctx); + + /* Write a byte and a 3-byte length */ + acpigen_write_len_f(ctx); + acpigen_emit_byte(ctx, 0x23); + acpigen_pop_len(ctx); + ut_asserteq(1 + 3, get_length(ptr)); + + /* Write 200 bytes so we need two length bytes */ + ptr = ctx->current; + acpigen_write_len_f(ctx); + for (i = 0; i < 200; i++) + acpigen_emit_byte(ctx, 0x23); + acpigen_pop_len(ctx); + ut_asserteq(200 + 3, get_length(ptr)); + + /* Write 40KB so we need three length bytes */ + ptr = ctx->current; + acpigen_write_len_f(ctx); + for (i = 0; i < 40000; i++) + acpigen_emit_byte(ctx, 0x23); + acpigen_pop_len(ctx); + ut_asserteq(40000 + 3, get_length(ptr)); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_len, 0); From patchwork Sun Jun 14 02:55:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242247 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:02 -0600 Subject: [PATCH v3 14/35] acpigen: Support writing a package In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200614025523.40183-4-sjg@chromium.org> A package collects together several elements. Add an easy way of writing a package header and updating its length later. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- Changes in v3: - Fix 'easy of testing' typo - Move two function comments into the previous patch include/acpi/acpigen.h | 32 ++++++++++++++++++++++++++++++++ lib/acpi/acpigen.c | 12 ++++++++++++ test/dm/acpigen.c | 27 +++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 12cd1bd578..d51cb7d76e 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -17,6 +17,11 @@ struct acpi_ctx; /* Top 4 bits of the value used to indicate a three-byte length value */ #define ACPI_PKG_LEN_3_BYTES 0x80 +/* ACPI Op/Prefix codes */ +enum { + PACKAGE_OP = 0x12, +}; + /** * acpigen_get_current() - Get the current ACPI code output pointer * @@ -103,4 +108,31 @@ void acpigen_write_len_f(struct acpi_ctx *ctx); */ void acpigen_pop_len(struct acpi_ctx *ctx); +/** + * acpigen_write_package() - Start writing a package + * + * A package collects together a number of elements in the ACPI code. To write + * a package use: + * + * acpigen_write_package(ctx, 3); + * ...write things + * acpigen_pop_len() + * + * If you don't know the number of elements in advance, acpigen_write_package() + * returns a pointer to the value so you can update it later: + * + * char *num_elements = acpigen_write_package(ctx, 0); + * ...write things + * *num_elements += 1; + * ...write things + * *num_elements += 1; + * acpigen_pop_len() + * + * @ctx: ACPI context pointer + * @nr_el: Number of elements (0 if not known) + * @returns pointer to the number of elements, which can be updated by the + * caller if needed + */ +char *acpigen_write_package(struct acpi_ctx *ctx, int nr_el); + #endif diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index 2c2b604d80..d1d0cca330 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -70,6 +70,18 @@ void acpigen_pop_len(struct acpi_ctx *ctx) p[2] = len >> 12 & 0xff; } +char *acpigen_write_package(struct acpi_ctx *ctx, int nr_el) +{ + char *p; + + acpigen_emit_byte(ctx, PACKAGE_OP); + acpigen_write_len_f(ctx); + p = ctx->current; + acpigen_emit_byte(ctx, nr_el); + + return p; +} + void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size) { int i; diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 71f70ebf71..8c06f7144a 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -398,3 +398,30 @@ static int dm_test_acpi_len(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_len, 0); + +/* Test emitting a package */ +static int dm_test_acpi_package(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + char *num_elements; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + num_elements = acpigen_write_package(ctx, 3); + ut_asserteq_ptr(num_elements, ptr + 4); + + /* For ease of testing, just emit a byte, not valid package contents */ + acpigen_emit_byte(ctx, 0x23); + acpigen_pop_len(ctx); + ut_asserteq(PACKAGE_OP, ptr[0]); + ut_asserteq(5, get_length(ptr + 1)); + ut_asserteq(3, ptr[4]); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_package, 0); From patchwork Sun Jun 14 02:55:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242249 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:03 -0600 Subject: [PATCH v3 15/35] acpi: Support writing an integer In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200614025523.40183-5-sjg@chromium.org> ACPI supports storing integers in various ways. Add a function to handle this. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- (no changes since v1) include/acpi/acpigen.h | 17 ++++++++++++++ lib/acpi/acpigen.c | 51 ++++++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 46 +++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index d51cb7d76e..b17a9ef14c 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -19,6 +19,12 @@ struct acpi_ctx; /* ACPI Op/Prefix codes */ enum { + ZERO_OP = 0x00, + ONE_OP = 0x01, + BYTE_PREFIX = 0x0a, + WORD_PREFIX = 0x0b, + DWORD_PREFIX = 0x0c, + QWORD_PREFIX = 0x0e, PACKAGE_OP = 0x12, }; @@ -135,4 +141,15 @@ void acpigen_pop_len(struct acpi_ctx *ctx); */ char *acpigen_write_package(struct acpi_ctx *ctx, int nr_el); +/** + * acpigen_write_integer() - Write an integer + * + * This writes an operation (BYTE_OP, WORD_OP, DWORD_OP, QWORD_OP depending on + * the integer size) and an integer value. Note that WORD means 16 bits in ACPI. + * + * @ctx: ACPI context pointer + * @data: Integer to write + */ +void acpigen_write_integer(struct acpi_ctx *ctx, u64 data); + #endif diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index d1d0cca330..c8c8f07897 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -82,6 +82,57 @@ char *acpigen_write_package(struct acpi_ctx *ctx, int nr_el) return p; } +void acpigen_write_byte(struct acpi_ctx *ctx, unsigned int data) +{ + acpigen_emit_byte(ctx, BYTE_PREFIX); + acpigen_emit_byte(ctx, data & 0xff); +} + +void acpigen_write_word(struct acpi_ctx *ctx, unsigned int data) +{ + acpigen_emit_byte(ctx, WORD_PREFIX); + acpigen_emit_word(ctx, data); +} + +void acpigen_write_dword(struct acpi_ctx *ctx, unsigned int data) +{ + acpigen_emit_byte(ctx, DWORD_PREFIX); + acpigen_emit_dword(ctx, data); +} + +void acpigen_write_qword(struct acpi_ctx *ctx, u64 data) +{ + acpigen_emit_byte(ctx, QWORD_PREFIX); + acpigen_emit_dword(ctx, data & 0xffffffff); + acpigen_emit_dword(ctx, (data >> 32) & 0xffffffff); +} + +void acpigen_write_zero(struct acpi_ctx *ctx) +{ + acpigen_emit_byte(ctx, ZERO_OP); +} + +void acpigen_write_one(struct acpi_ctx *ctx) +{ + acpigen_emit_byte(ctx, ONE_OP); +} + +void acpigen_write_integer(struct acpi_ctx *ctx, u64 data) +{ + if (data == 0) + acpigen_write_zero(ctx); + else if (data == 1) + acpigen_write_one(ctx); + else if (data <= 0xff) + acpigen_write_byte(ctx, (unsigned char)data); + else if (data <= 0xffff) + acpigen_write_word(ctx, (unsigned int)data); + else if (data <= 0xffffffff) + acpigen_write_dword(ctx, (unsigned int)data); + else + acpigen_write_qword(ctx, data); +} + void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size) { int i; diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 8c06f7144a..730f5713ec 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -22,6 +22,11 @@ #define TEST_STRING "frogmore" #define TEST_STREAM2 "\xfa\xde" +#define TEST_INT8 0x7d +#define TEST_INT16 0x2345 +#define TEST_INT32 0x12345678 +#define TEST_INT64 0x4567890123456 + static int alloc_context_size(struct acpi_ctx **ctxp, int size) { struct acpi_ctx *ctx; @@ -425,3 +430,44 @@ static int dm_test_acpi_package(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_package, 0); + +/* Test emitting an integer */ +static int dm_test_acpi_integer(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + acpigen_write_integer(ctx, 0); + acpigen_write_integer(ctx, 1); + acpigen_write_integer(ctx, TEST_INT8); + acpigen_write_integer(ctx, TEST_INT16); + acpigen_write_integer(ctx, TEST_INT32); + acpigen_write_integer(ctx, TEST_INT64); + + ut_asserteq(6 + 1 + 2 + 4 + 8, acpigen_get_current(ctx) - ptr); + + ut_asserteq(ZERO_OP, ptr[0]); + + ut_asserteq(ONE_OP, ptr[1]); + + ut_asserteq(BYTE_PREFIX, ptr[2]); + ut_asserteq(TEST_INT8, ptr[3]); + + ut_asserteq(WORD_PREFIX, ptr[4]); + ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 5))); + + ut_asserteq(DWORD_PREFIX, ptr[7]); + ut_asserteq(TEST_INT32, get_unaligned((u32 *)(ptr + 8))); + + ut_asserteq(QWORD_PREFIX, ptr[12]); + ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 13))); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_integer, 0); From patchwork Sun Jun 14 02:55:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242250 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:04 -0600 Subject: [PATCH v3 16/35] acpi: Support writing a string In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200614025523.40183-6-sjg@chromium.org> ACPI supports storing a simple nul-terminated string. Add support for this. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- (no changes since v1) include/acpi/acpigen.h | 10 ++++++++++ lib/acpi/acpigen.c | 6 ++++++ test/dm/acpigen.c | 32 ++++++++++++++++++++++++++++++-- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index b17a9ef14c..cf7f6173db 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -24,6 +24,7 @@ enum { BYTE_PREFIX = 0x0a, WORD_PREFIX = 0x0b, DWORD_PREFIX = 0x0c, + STRING_PREFIX = 0x0d, QWORD_PREFIX = 0x0e, PACKAGE_OP = 0x12, }; @@ -152,4 +153,13 @@ char *acpigen_write_package(struct acpi_ctx *ctx, int nr_el); */ void acpigen_write_integer(struct acpi_ctx *ctx, u64 data); +/** + * acpigen_write_string() - Write a string + * + * This writes a STRING_PREFIX followed by a nul-terminated string + * + * @ctx: ACPI context pointer + * @str: String to write + */ +void acpigen_write_string(struct acpi_ctx *ctx, const char *str); #endif diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index c8c8f07897..dcf84afbe6 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -146,3 +146,9 @@ void acpigen_emit_string(struct acpi_ctx *ctx, const char *str) acpigen_emit_stream(ctx, str, str ? strlen(str) : 0); acpigen_emit_byte(ctx, '\0'); /* NUL */ } + +void acpigen_write_string(struct acpi_ctx *ctx, const char *str) +{ + acpigen_emit_byte(ctx, STRING_PREFIX); + acpigen_emit_string(ctx, str); +} diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 730f5713ec..836cb4f75c 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -20,6 +20,7 @@ #include #define TEST_STRING "frogmore" +#define TEST_STRING2 "ranch" #define TEST_STREAM2 "\xfa\xde" #define TEST_INT8 0x7d @@ -404,7 +405,7 @@ static int dm_test_acpi_len(struct unit_test_state *uts) } DM_TEST(dm_test_acpi_len, 0); -/* Test emitting a package */ +/* Test writing a package */ static int dm_test_acpi_package(struct unit_test_state *uts) { struct acpi_ctx *ctx; @@ -431,7 +432,7 @@ static int dm_test_acpi_package(struct unit_test_state *uts) } DM_TEST(dm_test_acpi_package, 0); -/* Test emitting an integer */ +/* Test writing an integer */ static int dm_test_acpi_integer(struct unit_test_state *uts) { struct acpi_ctx *ctx; @@ -471,3 +472,30 @@ static int dm_test_acpi_integer(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_integer, 0); + +/* Test writing a string */ +static int dm_test_acpi_string(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + acpigen_write_string(ctx, TEST_STRING); + acpigen_write_string(ctx, TEST_STRING2); + + ut_asserteq(2 + sizeof(TEST_STRING) + sizeof(TEST_STRING2), + acpigen_get_current(ctx) - ptr); + ut_asserteq(STRING_PREFIX, ptr[0]); + ut_asserteq_str(TEST_STRING, (char *)ptr + 1); + ptr += 1 + sizeof(TEST_STRING); + ut_asserteq(STRING_PREFIX, ptr[0]); + ut_asserteq_str(TEST_STRING2, (char *)ptr + 1); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_string, 0); From patchwork Sun Jun 14 02:55:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242251 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:05 -0600 Subject: [PATCH v3 17/35] acpi: Support writing a name In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200614025523.40183-7-sjg@chromium.org> ACPI supports storing names which are made up of multiple path components. Several special cases are supported. Add a function to emit a name. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- (no changes since v1) include/acpi/acpigen.h | 25 +++++++++++ include/test/ut.h | 17 ++++++++ lib/acpi/acpigen.c | 96 ++++++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 93 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 231 insertions(+) diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index cf7f6173db..4d3202c922 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -21,12 +21,15 @@ struct acpi_ctx; enum { ZERO_OP = 0x00, ONE_OP = 0x01, + NAME_OP = 0x08, BYTE_PREFIX = 0x0a, WORD_PREFIX = 0x0b, DWORD_PREFIX = 0x0c, STRING_PREFIX = 0x0d, QWORD_PREFIX = 0x0e, PACKAGE_OP = 0x12, + DUAL_NAME_PREFIX = 0x2e, + MULTI_NAME_PREFIX = 0x2f, }; /** @@ -162,4 +165,26 @@ void acpigen_write_integer(struct acpi_ctx *ctx, u64 data); * @str: String to write */ void acpigen_write_string(struct acpi_ctx *ctx, const char *str); + +/** + * acpigen_emit_namestring() - Emit an ACPI name + * + * This writes out an ACPI name or path in the required special format. It does + * not add the NAME_OP prefix. + * + * @ctx: ACPI context pointer + * @namepath: Name / path to emit + */ +void acpigen_emit_namestring(struct acpi_ctx *ctx, const char *namepath); + +/** + * acpigen_write_name() - Write out an ACPI name + * + * This writes out an ACPI name or path in the required special format with a + * NAME_OP prefix. + * + * @ctx: ACPI context pointer + * @namepath: Name / path to emit + */ +void acpigen_write_name(struct acpi_ctx *ctx, const char *namepath); #endif diff --git a/include/test/ut.h b/include/test/ut.h index 7ddd6e8872..99bbb1230c 100644 --- a/include/test/ut.h +++ b/include/test/ut.h @@ -134,6 +134,23 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); } \ } +/* + * Assert that two string expressions are equal, up to length of the + * first + */ +#define ut_asserteq_strn(expr1, expr2) { \ + const char *_val1 = (expr1), *_val2 = (expr2); \ + int _len = strlen(_val1); \ + \ + if (memcmp(_val1, _val2, _len)) { \ + ut_failf(uts, __FILE__, __LINE__, __func__, \ + #expr1 " = " #expr2, \ + "Expected \"%.*s\", got \"%.*s\"", \ + _len, _val1, _len, _val2); \ + return CMD_RET_FAILURE; \ + } \ +} + /* Assert that two memory areas are equal */ #define ut_asserteq_mem(expr1, expr2, len) { \ const u8 *_val1 = (u8 *)(expr1), *_val2 = (u8 *)(expr2); \ diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index dcf84afbe6..55d7afc633 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -152,3 +152,99 @@ void acpigen_write_string(struct acpi_ctx *ctx, const char *str) acpigen_emit_byte(ctx, STRING_PREFIX); acpigen_emit_string(ctx, str); } + +/* + * The naming conventions for ACPI namespace names are a bit tricky as + * each element has to be 4 chars wide ("All names are a fixed 32 bits.") + * and "By convention, when an ASL compiler pads a name shorter than 4 + * characters, it is done so with trailing underscores ('_')". + * + * Check sections 5.3, 20.2.2 and 20.4 of ACPI spec 6.3 for details. + */ +static void acpigen_emit_simple_namestring(struct acpi_ctx *ctx, + const char *name) +{ + const char *ptr; + int i; + + for (i = 0, ptr = name; i < 4; i++) { + if (!*ptr || *ptr == '.') + acpigen_emit_byte(ctx, '_'); + else + acpigen_emit_byte(ctx, *ptr++); + } +} + +static void acpigen_emit_double_namestring(struct acpi_ctx *ctx, + const char *name, int dotpos) +{ + acpigen_emit_byte(ctx, DUAL_NAME_PREFIX); + acpigen_emit_simple_namestring(ctx, name); + acpigen_emit_simple_namestring(ctx, &name[dotpos + 1]); +} + +static void acpigen_emit_multi_namestring(struct acpi_ctx *ctx, + const char *name) +{ + unsigned char *pathlen; + int count = 0; + + acpigen_emit_byte(ctx, MULTI_NAME_PREFIX); + pathlen = ctx->current; + acpigen_emit_byte(ctx, 0); + + while (*name) { + acpigen_emit_simple_namestring(ctx, name); + /* find end or next entity */ + while (*name != '.' && *name) + name++; + /* forward to next */ + if (*name == '.') + name++; + count++; + } + + *pathlen = count; +} + +void acpigen_emit_namestring(struct acpi_ctx *ctx, const char *namepath) +{ + int dotcount; + int dotpos; + int i; + + /* We can start with a '\' */ + if (*namepath == '\\') { + acpigen_emit_byte(ctx, '\\'); + namepath++; + } + + /* And there can be any number of '^' */ + while (*namepath == '^') { + acpigen_emit_byte(ctx, '^'); + namepath++; + } + + for (i = 0, dotcount = 0; namepath[i]; i++) { + if (namepath[i] == '.') { + dotcount++; + dotpos = i; + } + } + + /* If we have only \\ or only ^* then we need to add a nul name */ + if (!*namepath) + acpigen_emit_byte(ctx, ZERO_OP); + else if (dotcount == 0) + acpigen_emit_simple_namestring(ctx, namepath); + else if (dotcount == 1) + acpigen_emit_double_namestring(ctx, namepath, dotpos); + else + acpigen_emit_multi_namestring(ctx, namepath); +} + +void acpigen_write_name(struct acpi_ctx *ctx, const char *namepath) +{ + acpigen_emit_byte(ctx, NAME_OP); + acpigen_emit_namestring(ctx, namepath); +} diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 836cb4f75c..c151f7a76b 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -499,3 +499,96 @@ static int dm_test_acpi_string(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_string, 0); + +/* Test writing a name */ +static int dm_test_acpi_name(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + /* + * The names here are made up for testing the various cases. The + * grammar is in the ACPI spec 6.3 section 19.2.2 + */ + acpigen_write_name(ctx, "\\_SB"); + acpigen_write_name(ctx, "\\_SB.I2C0"); + acpigen_write_name(ctx, "\\_SB.I2C0.TPM2"); + acpigen_write_name(ctx, "\\_SB.I2C0.TPM2.LONG"); + acpigen_write_name(ctx, "^^^^SPI0.FLAS"); + acpigen_write_name(ctx, "NN"); + acpigen_write_name(ctx, "^AB.CD.D.EFG"); + acpigen_write_name(ctx, "^^^^"); + acpigen_write_name(ctx, "\\"); + acpigen_write_name(ctx, "\\ABCD"); + + ut_asserteq(107, acpigen_get_current(ctx) - ptr); + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq_strn("\\_SB_", (char *)ptr + 1); + ptr += 6; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq('\\', ptr[1]); + ut_asserteq(DUAL_NAME_PREFIX, ptr[2]); + ut_asserteq_strn("_SB_I2C0", (char *)ptr + 3); + ptr += 11; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq('\\', ptr[1]); + ut_asserteq(MULTI_NAME_PREFIX, ptr[2]); + ut_asserteq(3, ptr[3]); + ut_asserteq_strn("_SB_I2C0TPM2", (char *)ptr + 4); + ptr += 16; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq('\\', ptr[1]); + ut_asserteq(MULTI_NAME_PREFIX, ptr[2]); + ut_asserteq(4, ptr[3]); + ut_asserteq_strn("_SB_I2C0TPM2LONG", (char *)ptr + 4); + ptr += 20; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq('^', ptr[1]); + ut_asserteq('^', ptr[2]); + ut_asserteq('^', ptr[3]); + ut_asserteq('^', ptr[4]); + ut_asserteq(DUAL_NAME_PREFIX, ptr[5]); + ut_asserteq_strn("SPI0FLAS", (char *)ptr + 6); + ptr += 14; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq_strn("NN__", (char *)ptr + 1); + ptr += 5; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq('^', ptr[1]); + ut_asserteq(MULTI_NAME_PREFIX, ptr[2]); + ut_asserteq(4, ptr[3]); + ut_asserteq_strn("AB__CD__D___EFG_", (char *)ptr + 4); + ptr += 20; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq('^', ptr[1]); + ut_asserteq('^', ptr[2]); + ut_asserteq('^', ptr[3]); + ut_asserteq('^', ptr[4]); + ut_asserteq(ZERO_OP, ptr[5]); + ptr += 6; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq('\\', ptr[1]); + ut_asserteq(ZERO_OP, ptr[2]); + ptr += 3; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq_strn("\\ABCD", (char *)ptr + 1); + ptr += 5; + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_name, 0); From patchwork Sun Jun 14 02:55:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242252 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:06 -0600 Subject: [PATCH v3 18/35] acpi: Support writing a UUID In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200614025523.40183-8-sjg@chromium.org> ACPI supports writing a UUID in a special format. Add a function to handle this. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- (no changes since v1) include/acpi/acpigen.h | 13 +++++++++++++ lib/acpi/acpigen.c | 38 ++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 4d3202c922..f45a19714b 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -27,6 +27,7 @@ enum { DWORD_PREFIX = 0x0c, STRING_PREFIX = 0x0d, QWORD_PREFIX = 0x0e, + BUFFER_OP = 0x11, PACKAGE_OP = 0x12, DUAL_NAME_PREFIX = 0x2e, MULTI_NAME_PREFIX = 0x2f, @@ -187,4 +188,16 @@ void acpigen_emit_namestring(struct acpi_ctx *ctx, const char *namepath); * @namepath: Name / path to emit */ void acpigen_write_name(struct acpi_ctx *ctx, const char *namepath); + +/** + * acpigen_write_uuid() - Write a UUID + * + * This writes out a UUID in the format used by ACPI, with a BUFFER_OP prefix. + * + * @ctx: ACPI context pointer + * @uuid: UUID to write in the form aabbccdd-eeff-gghh-iijj-kkllmmnnoopp + * @return 0 if OK, -EINVAL if the format is incorrect + */ +int acpigen_write_uuid(struct acpi_ctx *ctx, const char *uuid); + #endif diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index 55d7afc633..0f08d7b8a8 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -248,3 +249,40 @@ void acpigen_write_name(struct acpi_ctx *ctx, const char *namepath) acpigen_emit_byte(ctx, NAME_OP); acpigen_emit_namestring(ctx, namepath); } + +/* + * ToUUID(uuid) + * + * ACPI 6.3 Section 19.6.142 table 19-438 defines a special output order for the + * bytes that make up a UUID Buffer object: + * + * UUID byte order for input to this function: + * aabbccdd-eeff-gghh-iijj-kkllmmnnoopp + * + * UUID byte order output by this function: + * ddccbbaa-ffee-hhgg-iijj-kkllmmnnoopp + */ +int acpigen_write_uuid(struct acpi_ctx *ctx, const char *uuid) +{ + u8 buf[UUID_BIN_LEN]; + int ret; + + /* Parse UUID string into bytes */ + ret = uuid_str_to_bin(uuid, buf, UUID_STR_FORMAT_GUID); + if (ret) + return log_msg_ret("bad hex", -EINVAL); + + /* BufferOp */ + acpigen_emit_byte(ctx, BUFFER_OP); + acpigen_write_len_f(ctx); + + /* Buffer length in bytes */ + acpigen_write_word(ctx, UUID_BIN_LEN); + + /* Output UUID in expected order */ + acpigen_emit_stream(ctx, (char *)buf, UUID_BIN_LEN); + + acpigen_pop_len(ctx); + + return 0; +} diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index c151f7a76b..4f514eb9cc 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -592,3 +592,36 @@ static int dm_test_acpi_name(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_name, 0); + +/* Test writing a UUID */ +static int dm_test_acpi_uuid(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + ut_assertok(acpigen_write_uuid(ctx, + "dbb8e3e6-5886-4ba6-8795-1319f52a966b")); + ut_asserteq(23, acpigen_get_current(ctx) - ptr); + ut_asserteq(BUFFER_OP, ptr[0]); + ut_asserteq(22, get_length(ptr + 1)); + ut_asserteq(0xdbb8e3e6, get_unaligned((u32 *)(ptr + 7))); + ut_asserteq(0x5886, get_unaligned((u16 *)(ptr + 11))); + ut_asserteq(0x4ba6, get_unaligned((u16 *)(ptr + 13))); + ut_asserteq(0x9587, get_unaligned((u16 *)(ptr + 15))); + ut_asserteq(0x2af51913, get_unaligned((u32 *)(ptr + 17))); + ut_asserteq(0x6b96, get_unaligned((u16 *)(ptr + 21))); + + /* Try a bad UUID */ + ut_asserteq(-EINVAL, + acpigen_write_uuid(ctx, + "dbb8e3e6-5886-4ba6x8795-1319f52a966b")); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_uuid, 0); From patchwork Sun Jun 14 02:55:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242256 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:07 -0600 Subject: [PATCH v3 19/35] acpi: Support writing Device Properties objects via _DSD In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200614025523.40183-9-sjg@chromium.org> More complex device properties can be provided to drivers via a device-specific data (_DSD) object. To create this we need to build it up in a separate data structure and then generate the ACPI code, due to its recursive nature. Add an implementation of this. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- Changes in v3: - Allow the name parameter to be NULL - Add error checking to acpi_dp_add_integer_array() - Fix 'acpi_device.v' typo - Drop unused ACPI_CPU_STRING include/acpi/acpi_dp.h | 216 ++++++++++++++++++++++ include/acpi/acpigen.h | 1 + lib/acpi/Makefile | 1 + lib/acpi/acpi_dp.c | 323 ++++++++++++++++++++++++++++++++ test/dm/Makefile | 1 + test/dm/acpi_dp.c | 405 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 947 insertions(+) create mode 100644 include/acpi/acpi_dp.h create mode 100644 lib/acpi/acpi_dp.c create mode 100644 test/dm/acpi_dp.c diff --git a/include/acpi/acpi_dp.h b/include/acpi/acpi_dp.h new file mode 100644 index 0000000000..3fd048e111 --- /dev/null +++ b/include/acpi/acpi_dp.h @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Device properties, a temporary data structure for adding to ACPI code + * + * Copyright 2019 Google LLC + * Mostly taken from coreboot file acpi_device.h + */ + +#ifndef __ACPI_DP_H +#define __ACPI_DP_H + +struct acpi_ctx; + +/* + * Writing Device Properties objects via _DSD + * + * This is described in ACPI 6.3 section 6.2.5 + * + * This provides a structure to handle nested device-specific data which ends + * up in a _DSD table. + * + * https://www.kernel.org/doc/html/latest/firmware-guide/acpi/DSD-properties-rules.html + * https://uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf + * https://uefi.org/sites/default/files/resources/_DSD-hierarchical-data-extension-UUID-v1.1.pdf + * + * The Device Property Hierarchy can be multiple levels deep with multiple + * children possible in each level. In order to support this flexibility + * the device property hierarchy must be built up before being written out. + * + * For example: + * + * // Child table with string and integer + * struct acpi_dp *child = acpi_dp_new_table("CHLD"); + * acpi_dp_add_string(child, "childstring", "CHILD"); + * acpi_dp_add_integer(child, "childint", 100); + * + * // _DSD table with integer and gpio and child pointer + * struct acpi_dp *dsd = acpi_dp_new_table("_DSD"); + * acpi_dp_add_integer(dsd, "number1", 1); + * acpi_dp_add_gpio(dsd, "gpio", "\_SB.PCI0.GPIO", 0, 0, 1); + * acpi_dp_add_child(dsd, "child", child); + * + * // Write entries into SSDT and clean up resources + * acpi_dp_write(dsd); + * + * Name(_DSD, Package() { + * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") + * Package() { + * Package() { "gpio", Package() { \_SB.PCI0.GPIO, 0, 0, 0 } } + * Package() { "number1", 1 } + * } + * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b") + * Package() { + * Package() { "child", CHLD } + * } + * } + * Name(CHLD, Package() { + * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") + * Package() { + * Package() { "childstring", "CHILD" } + * Package() { "childint", 100 } + * } + * } + */ + +#define ACPI_DP_UUID "daffd814-6eba-4d8c-8a91-bc9bbf4aa301" +#define ACPI_DP_CHILD_UUID "dbb8e3e6-5886-4ba6-8795-1319f52a966b" + +/** + * enum acpi_dp_type - types of device property objects + * + * These refer to the types defined by struct acpi_dp below + * + * @ACPI_DP_TYPE_UNKNOWN: Unknown / do not use + * @ACPI_DP_TYPE_INTEGER: Integer value (u64) in @integer + * @ACPI_DP_TYPE_STRING: String value in @string + * @ACPI_DP_TYPE_REFERENCE: Reference to another object, with value in @string + * @ACPI_DP_TYPE_TABLE: Type for a top-level table which may have children + * @ACPI_DP_TYPE_ARRAY: Array of items with first item in @array and following + * items linked from that item's @next + * @ACPI_DP_TYPE_CHILD: Child object, with siblings in that child's @next + */ +enum acpi_dp_type { + ACPI_DP_TYPE_UNKNOWN, + ACPI_DP_TYPE_INTEGER, + ACPI_DP_TYPE_STRING, + ACPI_DP_TYPE_REFERENCE, + ACPI_DP_TYPE_TABLE, + ACPI_DP_TYPE_ARRAY, + ACPI_DP_TYPE_CHILD, +}; + +/** + * struct acpi_dp - ACPI device properties + * + * @type: Table type + * @name: Name of object, typically _DSD but could be CHLD for a child object. + * This can be NULL if there is no name + * @next: Next object in list (next array element or next sibling) + * @child: Pointer to first child, if @type == ACPI_DP_TYPE_CHILD, else NULL + * @array: First array element, if @type == ACPI_DP_TYPE_ARRAY, else NULL + * @integer: Integer value of the property, if @type == ACPI_DP_TYPE_INTEGER + * @string: String value of the property, if @type == ACPI_DP_TYPE_STRING; + * child name if @type == ACPI_DP_TYPE_CHILD; + * reference name if @type == ACPI_DP_TYPE_REFERENCE; + */ +struct acpi_dp { + enum acpi_dp_type type; + const char *name; + struct acpi_dp *next; + union { + struct acpi_dp *child; + struct acpi_dp *array; + }; + union { + u64 integer; + const char *string; + }; +}; + +/** + * acpi_dp_new_table() - Start a new Device Property table + * + * @ref: ACPI reference (e.g. "_DSD") + * @return pointer to table, or NULL if out of memory + */ +struct acpi_dp *acpi_dp_new_table(const char *ref); + +/** + * acpi_dp_add_integer() - Add integer Device Property + * + * A new node is added to the end of the property list of @dp + * + * @dp: Table to add this property to + * @name: Name of property, or NULL for none + * @value: Integer value + * @return pointer to new node, or NULL if out of memory + */ +struct acpi_dp *acpi_dp_add_integer(struct acpi_dp *dp, const char *name, + u64 value); + +/** + * acpi_dp_add_string() - Add string Device Property + * + * A new node is added to the end of the property list of @dp + * + * @dp: Table to add this property to + * @name: Name of property, or NULL for none + * @string: String value + * @return pointer to new node, or NULL if out of memory + */ +struct acpi_dp *acpi_dp_add_string(struct acpi_dp *dp, const char *name, + const char *string); + +/** + * acpi_dp_add_reference() - Add reference Device Property + * + * A new node is added to the end of the property list of @dp + * + * @dp: Table to add this property to + * @name: Name of property, or NULL for none + * @reference: Reference value + * @return pointer to new node, or NULL if out of memory + */ +struct acpi_dp *acpi_dp_add_reference(struct acpi_dp *dp, const char *name, + const char *reference); + +/** + * acpi_dp_add_array() - Add array Device Property + * + * A new node is added to the end of the property list of @dp, with the array + * attached to that. + * + * @dp: Table to add this property to + * @name: Name of property, or NULL for none + * @return pointer to new node, or NULL if out of memory + */ +struct acpi_dp *acpi_dp_add_array(struct acpi_dp *dp, struct acpi_dp *array); + +/** + * acpi_dp_add_integer_array() - Add an array of integers + * + * A new node is added to the end of the property list of @dp, with the array + * attached to that. Each element of the array becomes a new node. + * + * @dp: Table to add this property to + * @name: Name of property, or NULL for none + * @return pointer to new array node, or NULL if out of memory + */ +struct acpi_dp *acpi_dp_add_integer_array(struct acpi_dp *dp, const char *name, + u64 *array, int len); + +/** + * acpi_dp_add_child() - Add a child table of Device Properties + * + * A new node is added as a child of @dp + * + * @dp: Table to add this child to + * @name: Name of child, or NULL for none + * @child: Child node to add + * @return pointer to new array node, or NULL if out of memory + */ +struct acpi_dp *acpi_dp_add_child(struct acpi_dp *dp, const char *name, + struct acpi_dp *child); + +/** + * acpi_dp_write() - Write Device Property hierarchy and clean up resources + * + * This writes the table using acpigen and then frees it + * + * @table: Table to write + * @return 0 if OK, -ve on error + */ +int acpi_dp_write(struct acpi_ctx *ctx, struct acpi_dp *table); + +#endif diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index f45a19714b..40cd72504a 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -31,6 +31,7 @@ enum { PACKAGE_OP = 0x12, DUAL_NAME_PREFIX = 0x2e, MULTI_NAME_PREFIX = 0x2f, + ROOT_PREFIX = 0x5c, }; /** diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile index 85a1f774ad..5c2f793701 100644 --- a/lib/acpi/Makefile +++ b/lib/acpi/Makefile @@ -3,4 +3,5 @@ obj-y += acpigen.o obj-y += acpi_device.o +obj-y += acpi_dp.o obj-y += acpi_table.o diff --git a/lib/acpi/acpi_dp.c b/lib/acpi/acpi_dp.c new file mode 100644 index 0000000000..ebbc5d5538 --- /dev/null +++ b/lib/acpi/acpi_dp.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generation of tables for particular device types + * + * Copyright 2019 Google LLC + * Mostly taken from coreboot file acpi_device.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static void acpi_dp_write_array(struct acpi_ctx *ctx, + const struct acpi_dp *array); + +static void acpi_dp_write_value(struct acpi_ctx *ctx, + const struct acpi_dp *prop) +{ + switch (prop->type) { + case ACPI_DP_TYPE_INTEGER: + acpigen_write_integer(ctx, prop->integer); + break; + case ACPI_DP_TYPE_STRING: + case ACPI_DP_TYPE_CHILD: + acpigen_write_string(ctx, prop->string); + break; + case ACPI_DP_TYPE_REFERENCE: + acpigen_emit_namestring(ctx, prop->string); + break; + case ACPI_DP_TYPE_ARRAY: + acpi_dp_write_array(ctx, prop->array); + break; + default: + break; + } +} + +/* Package (2) { "prop->name", VALUE } */ +static void acpi_dp_write_property(struct acpi_ctx *ctx, + const struct acpi_dp *prop) +{ + acpigen_write_package(ctx, 2); + acpigen_write_string(ctx, prop->name); + acpi_dp_write_value(ctx, prop); + acpigen_pop_len(ctx); +} + +/* Write array of Device Properties */ +static void acpi_dp_write_array(struct acpi_ctx *ctx, + const struct acpi_dp *array) +{ + const struct acpi_dp *dp; + char *pkg_count; + + /* Package element count determined as it is populated */ + pkg_count = acpigen_write_package(ctx, 0); + + /* + * Only acpi_dp of type DP_TYPE_TABLE is allowed to be an array. + * DP_TYPE_TABLE does not have a value to be written. Thus, start + * the loop from next type in the array. + */ + for (dp = array->next; dp; dp = dp->next) { + acpi_dp_write_value(ctx, dp); + (*pkg_count)++; + } + + acpigen_pop_len(ctx); +} + +static void acpi_dp_free(struct acpi_dp *dp) +{ + assert(dp); + while (dp) { + struct acpi_dp *p = dp->next; + + switch (dp->type) { + case ACPI_DP_TYPE_CHILD: + acpi_dp_free(dp->child); + break; + case ACPI_DP_TYPE_ARRAY: + acpi_dp_free(dp->array); + break; + default: + break; + } + + free(dp); + dp = p; + } +} + +int acpi_dp_write_(struct acpi_ctx *ctx, struct acpi_dp *table) +{ + struct acpi_dp *dp, *prop; + char *dp_count, *prop_count = NULL; + int child_count = 0; + int ret; + + assert(table); + if (table->type != ACPI_DP_TYPE_TABLE) + return 0; + + /* Name (name) */ + acpigen_write_name(ctx, table->name); + + /* Device Property list starts with the next entry */ + prop = table->next; + + /* Package (DP), default to assuming no properties or children */ + dp_count = acpigen_write_package(ctx, 0); + + /* Print base properties */ + for (dp = prop; dp; dp = dp->next) { + if (dp->type == ACPI_DP_TYPE_CHILD) { + child_count++; + } else { + /* + * The UUID and package is only added when + * we come across the first property. This + * is to avoid creating a zero-length package + * in situations where there are only children. + */ + if (!prop_count) { + *dp_count += 2; + /* ToUUID (ACPI_DP_UUID) */ + ret = acpigen_write_uuid(ctx, ACPI_DP_UUID); + if (ret) + return log_msg_ret("touuid", ret); + /* + * Package (PROP), element count determined as + * it is populated + */ + prop_count = acpigen_write_package(ctx, 0); + } + (*prop_count)++; + acpi_dp_write_property(ctx, dp); + } + } + + if (prop_count) { + /* Package (PROP) length, if a package was written */ + acpigen_pop_len(ctx); + } + + if (child_count) { + /* Update DP package count to 2 or 4 */ + *dp_count += 2; + /* ToUUID (ACPI_DP_CHILD_UUID) */ + ret = acpigen_write_uuid(ctx, ACPI_DP_CHILD_UUID); + if (ret) + return log_msg_ret("child uuid", ret); + + /* Print child pointer properties */ + acpigen_write_package(ctx, child_count); + + for (dp = prop; dp; dp = dp->next) + if (dp->type == ACPI_DP_TYPE_CHILD) + acpi_dp_write_property(ctx, dp); + /* Package (CHILD) length */ + acpigen_pop_len(ctx); + } + + /* Package (DP) length */ + acpigen_pop_len(ctx); + + /* Recursively parse children into separate tables */ + for (dp = prop; dp; dp = dp->next) { + if (dp->type == ACPI_DP_TYPE_CHILD) { + ret = acpi_dp_write_(ctx, dp->child); + if (ret) + return log_msg_ret("dp child", ret); + } + } + + return 0; +} + +int acpi_dp_write(struct acpi_ctx *ctx, struct acpi_dp *table) +{ + int ret; + + ret = acpi_dp_write_(ctx, table); + + /* Clean up */ + acpi_dp_free(table); + + if (ret) + return log_msg_ret("write", ret); + + return 0; +} + +static struct acpi_dp *acpi_dp_new(struct acpi_dp *dp, enum acpi_dp_type type, + const char *name) +{ + struct acpi_dp *new; + + new = malloc(sizeof(struct acpi_dp)); + if (!new) + return NULL; + + memset(new, '\0', sizeof(*new)); + new->type = type; + new->name = name; + + if (dp) { + /* Add to end of property list */ + while (dp->next) + dp = dp->next; + dp->next = new; + } + + return new; +} + +struct acpi_dp *acpi_dp_new_table(const char *name) +{ + return acpi_dp_new(NULL, ACPI_DP_TYPE_TABLE, name); +} + +struct acpi_dp *acpi_dp_add_integer(struct acpi_dp *dp, const char *name, + u64 value) +{ + struct acpi_dp *new; + + assert(dp); + new = acpi_dp_new(dp, ACPI_DP_TYPE_INTEGER, name); + + if (new) + new->integer = value; + + return new; +} + +struct acpi_dp *acpi_dp_add_string(struct acpi_dp *dp, const char *name, + const char *string) +{ + struct acpi_dp *new; + + assert(dp); + new = acpi_dp_new(dp, ACPI_DP_TYPE_STRING, name); + if (new) + new->string = string; + + return new; +} + +struct acpi_dp *acpi_dp_add_reference(struct acpi_dp *dp, const char *name, + const char *reference) +{ + struct acpi_dp *new; + + assert(dp); + new = acpi_dp_new(dp, ACPI_DP_TYPE_REFERENCE, name); + if (new) + new->string = reference; + + return new; +} + +struct acpi_dp *acpi_dp_add_child(struct acpi_dp *dp, const char *name, + struct acpi_dp *child) +{ + struct acpi_dp *new; + + assert(dp); + if (child->type != ACPI_DP_TYPE_TABLE) + return NULL; + + new = acpi_dp_new(dp, ACPI_DP_TYPE_CHILD, name); + if (new) { + new->child = child; + new->string = child->name; + } + + return new; +} + +struct acpi_dp *acpi_dp_add_array(struct acpi_dp *dp, struct acpi_dp *array) +{ + struct acpi_dp *new; + + assert(dp); + assert(array); + if (array->type != ACPI_DP_TYPE_TABLE) + return NULL; + + new = acpi_dp_new(dp, ACPI_DP_TYPE_ARRAY, array->name); + if (new) + new->array = array; + + return new; +} + +struct acpi_dp *acpi_dp_add_integer_array(struct acpi_dp *dp, const char *name, + u64 *array, int len) +{ + struct acpi_dp *dp_array; + int i; + + assert(dp); + if (len <= 0) + return NULL; + + dp_array = acpi_dp_new_table(name); + if (!dp_array) + return NULL; + + for (i = 0; i < len; i++) + if (!acpi_dp_add_integer(dp_array, NULL, array[i])) + break; + + if (!acpi_dp_add_array(dp, dp_array)) + return NULL; + + return dp_array; +} diff --git a/test/dm/Makefile b/test/dm/Makefile index e3e0cccf01..eb62faa0e3 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_UT_DM) += core.o ifneq ($(CONFIG_SANDBOX),) obj-$(CONFIG_ACPIGEN) += acpi.o obj-$(CONFIG_ACPIGEN) += acpigen.o +obj-$(CONFIG_ACPIGEN) += acpi_dp.o obj-$(CONFIG_SOUND) += audio.o obj-$(CONFIG_BLK) += blk.o obj-$(CONFIG_BOARD) += board.o diff --git a/test/dm/acpi_dp.c b/test/dm/acpi_dp.c new file mode 100644 index 0000000000..c11394786f --- /dev/null +++ b/test/dm/acpi_dp.c @@ -0,0 +1,405 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for ACPI code generation via a device-property table + * + * Copyright 2019 Google LLC + * Written by Simon Glass + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TEST_INT8 0x7d +#define TEST_INT16 0x2345 +#define TEST_INT32 0x12345678 +#define TEST_INT64 0x4567890123456 +#define TEST_STR "testing acpi strings" +#define TEST_REF "\\SB.I2C0.TPM2" +#define EXPECT_REF "SB__I2C0TPM2" + +static int alloc_context(struct acpi_ctx **ctxp) +{ + struct acpi_ctx *ctx; + + *ctxp = NULL; + ctx = malloc(sizeof(*ctx)); + if (!ctx) + return -ENOMEM; + memset(ctx, '\0', sizeof(*ctx)); + ctx->current = malloc(500); + if (!ctx->current) + return -ENOMEM; + *ctxp = ctx; + + return 0; +} + +static void free_context(struct acpi_ctx **ctxp) +{ + free(*ctxp); + *ctxp = NULL; +} + +/** + * get_length() - decode a three-byte length field + * + * @ptr: Length encoded as per ACPI + * @return decoded length, or -EINVAL on error + */ +static int get_length(u8 *ptr) +{ + if (!(*ptr & 0x80)) + return -EINVAL; + + return (*ptr & 0xf) | ptr[1] << 4 | ptr[2] << 12; +} + +/* Test emitting an empty table */ +static int dm_test_acpi_dp_new_table(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct acpi_dp *dp; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(10, acpigen_get_current(ctx) - ptr); + ut_asserteq(NAME_OP, *(u8 *)ptr); + ut_asserteq_strn("FRED", (char *)ptr + 1); + ut_asserteq(PACKAGE_OP, ptr[5]); + ut_asserteq(4, get_length(ptr + 6)); + ut_asserteq(0, ptr[9]); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_dp_new_table, 0); + +/* Test emitting an integer */ +static int dm_test_acpi_dp_int(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + char uuid[UUID_STR_LEN + 1]; + struct acpi_dp *dp; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT32)); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(54, acpigen_get_current(ctx) - ptr); + ut_asserteq(NAME_OP, *(u8 *)ptr); + ut_asserteq_strn("FRED", (char *)ptr + 1); + ut_asserteq(PACKAGE_OP, ptr[5]); + ut_asserteq(48, get_length(ptr + 6)); + ut_asserteq(2, ptr[9]); + + /* UUID */ + ut_asserteq(BUFFER_OP, ptr[10]); + ut_asserteq(22, get_length(ptr + 11)); + ut_asserteq(WORD_PREFIX, ptr[14]); + ut_asserteq(16, get_unaligned((u16 *)(ptr + 15))); + uuid_bin_to_str(ptr + 17, uuid, 1); + ut_asserteq_str(ACPI_DP_UUID, uuid); + + /* Container package */ + ut_asserteq(PACKAGE_OP, ptr[33]); + ut_asserteq(20, get_length(ptr + 34)); + ut_asserteq(1, ptr[37]); + + /* Package with name and (integer) value */ + ut_asserteq(PACKAGE_OP, ptr[38]); + ut_asserteq(15, get_length(ptr + 39)); + ut_asserteq(2, ptr[42]); + ut_asserteq(STRING_PREFIX, ptr[43]); + ut_asserteq_str("MARY", (char *)ptr + 44); + + ut_asserteq(DWORD_PREFIX, ptr[49]); + ut_asserteq(TEST_INT32, get_unaligned((u32 *)(ptr + 50))); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_dp_int, 0); + +/* Test emitting a 64-bit integer */ +static int dm_test_acpi_dp_int64(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct acpi_dp *dp; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT64)); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(58, acpigen_get_current(ctx) - ptr); + + ut_asserteq(QWORD_PREFIX, ptr[49]); + ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 50))); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_dp_int64, 0); + +/* Test emitting a 16-bit integer */ +static int dm_test_acpi_dp_int16(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct acpi_dp *dp; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT16)); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(52, acpigen_get_current(ctx) - ptr); + + ut_asserteq(WORD_PREFIX, ptr[49]); + ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 50))); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_dp_int16, 0); + +/* Test emitting a 8-bit integer */ +static int dm_test_acpi_dp_int8(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct acpi_dp *dp; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT8)); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(51, acpigen_get_current(ctx) - ptr); + + ut_asserteq(BYTE_PREFIX, ptr[49]); + ut_asserteq(TEST_INT8, ptr[50]); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_dp_int8, 0); + +/* Test emitting multiple values */ +static int dm_test_acpi_dp_multiple(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct acpi_dp *dp; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + ut_assertnonnull(acpi_dp_add_integer(dp, "int16", TEST_INT16)); + ut_assertnonnull(acpi_dp_add_string(dp, "str", TEST_STR)); + ut_assertnonnull(acpi_dp_add_reference(dp, "ref", TEST_REF)); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(110, acpigen_get_current(ctx) - ptr); + + ut_asserteq(WORD_PREFIX, ptr[0x32]); + ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 0x33))); + ut_asserteq(STRING_PREFIX, ptr[0x3f]); + ut_asserteq_str(TEST_STR, (char *)ptr + 0x40); + ut_asserteq(ROOT_PREFIX, ptr[0x5f]); + ut_asserteq(MULTI_NAME_PREFIX, ptr[0x60]); + ut_asserteq(3, ptr[0x61]); + ut_asserteq_strn(EXPECT_REF, (char *)ptr + 0x62); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_dp_multiple, 0); + +/* Test emitting an array */ +static int dm_test_acpi_dp_array(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct acpi_dp *dp; + u64 speed[4]; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + speed[0] = TEST_INT8; + speed[1] = TEST_INT16; + speed[2] = TEST_INT32; + speed[3] = TEST_INT64; + ut_assertnonnull(acpi_dp_add_integer_array(dp, "speeds", speed, + ARRAY_SIZE(speed))); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(75, acpigen_get_current(ctx) - ptr); + + ut_asserteq(BYTE_PREFIX, ptr[0x38]); + ut_asserteq(TEST_INT8, ptr[0x39]); + + ut_asserteq(WORD_PREFIX, ptr[0x3a]); + ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 0x3b))); + + ut_asserteq(DWORD_PREFIX, ptr[0x3d]); + ut_asserteq(TEST_INT32, get_unaligned((u32 *)(ptr + 0x3e))); + + ut_asserteq(QWORD_PREFIX, ptr[0x42]); + ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 0x43))); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_dp_array, 0); + +/* Test emitting a child */ +static int dm_test_acpi_dp_child(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct acpi_dp *dp, *child1, *child2; + char uuid[UUID_STR_LEN + 1]; + u8 *ptr, *pptr; + int i; + + ut_assertok(alloc_context(&ctx)); + + child1 = acpi_dp_new_table("child"); + ut_assertnonnull(child1); + ut_assertnonnull(acpi_dp_add_integer(child1, "height", TEST_INT16)); + + child2 = acpi_dp_new_table("child"); + ut_assertnonnull(child2); + ut_assertnonnull(acpi_dp_add_integer(child2, "age", TEST_INT8)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + + ut_assertnonnull(acpi_dp_add_child(dp, "anna", child1)); + ut_assertnonnull(acpi_dp_add_child(dp, "john", child2)); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(178, acpigen_get_current(ctx) - ptr); + + /* UUID for child extension using Hierarchical Data Extension UUID */ + ut_asserteq(BUFFER_OP, ptr[10]); + ut_asserteq(22, get_length(ptr + 11)); + ut_asserteq(WORD_PREFIX, ptr[14]); + ut_asserteq(16, get_unaligned((u16 *)(ptr + 15))); + uuid_bin_to_str(ptr + 17, uuid, 1); + ut_asserteq_str(ACPI_DP_CHILD_UUID, uuid); + + /* Package with two children */ + ut_asserteq(PACKAGE_OP, ptr[0x21]); + ut_asserteq(0x28, get_length(ptr + 0x22)); + ut_asserteq(2, ptr[0x25]); + + /* First we expect the two children as string/value */ + pptr = ptr + 0x26; + for (i = 0; i < 2; i++) { + ut_asserteq(PACKAGE_OP, pptr[0]); + ut_asserteq(0x11, get_length(pptr + 1)); + ut_asserteq(2, pptr[4]); + ut_asserteq(STRING_PREFIX, pptr[5]); + ut_asserteq_str(i ? "john" : "anna", (char *)pptr + 6); + ut_asserteq(STRING_PREFIX, pptr[11]); + ut_asserteq_str("child", (char *)pptr + 12); + pptr += 0x12; + } + + /* Write the two children */ + ut_asserteq(0x4a, pptr - ptr); + for (i = 0; i < 2; i++) { + const char *prop = i ? "age" : "height"; + const int datalen = i ? 1 : 2; + int len = strlen(prop) + 1; + + ut_asserteq(NAME_OP, pptr[0]); + ut_asserteq_strn("chil", (char *)pptr + 1); + ut_asserteq(PACKAGE_OP, pptr[5]); + ut_asserteq(0x27 + len + datalen, get_length(pptr + 6)); + ut_asserteq(2, pptr[9]); + + /* UUID */ + ut_asserteq(BUFFER_OP, pptr[10]); + ut_asserteq(22, get_length(pptr + 11)); + ut_asserteq(WORD_PREFIX, pptr[14]); + ut_asserteq(16, get_unaligned((u16 *)(pptr + 15))); + uuid_bin_to_str(pptr + 17, uuid, 1); + ut_asserteq_str(ACPI_DP_UUID, uuid); + pptr += 33; + + /* Containing package */ + ut_asserteq(i ? 0xa1 : 0x6b, pptr - ptr); + ut_asserteq(PACKAGE_OP, pptr[0]); + ut_asserteq(0xb + len + datalen, get_length(pptr + 1)); + ut_asserteq(1, pptr[4]); + + /* Package containing the property-name string and the value */ + pptr += 5; + ut_asserteq(i ? 0xa6 : 0x70, pptr - ptr); + ut_asserteq(PACKAGE_OP, pptr[0]); + ut_asserteq(6 + len + datalen, get_length(pptr + 1)); + ut_asserteq(2, pptr[4]); + + ut_asserteq(STRING_PREFIX, pptr[5]); + ut_asserteq_str(i ? "age" : "height", (char *)pptr + 6); + pptr += 6 + len; + if (i) { + ut_asserteq(BYTE_PREFIX, pptr[0]); + ut_asserteq(TEST_INT8, pptr[1]); + } else { + ut_asserteq(WORD_PREFIX, pptr[0]); + ut_asserteq(TEST_INT16, + get_unaligned((u16 *)(pptr + 1))); + } + pptr += 1 + datalen; + } + ut_asserteq(178, pptr - ptr); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_dp_child, 0); From patchwork Sun Jun 14 02:55:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242254 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:08 -0600 Subject: [PATCH v3 20/35] acpi: Support writing a GPIO In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.20.I768a728895dcce8d3bf6303837dd3b35cf9b28e5@changeid> Allowing writing out a reference to a GPIO within the ACPI output. This can be used by ACPI code to access a GPIO at runtime. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- Changes in v3: - Use an enum for the GPIO priority - Add error checking include/acpi/acpi_dp.h | 20 ++++++++++++++++++++ lib/acpi/acpi_dp.c | 23 +++++++++++++++++++++++ test/dm/acpi_dp.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/include/acpi/acpi_dp.h b/include/acpi/acpi_dp.h index 3fd048e111..efbec3d152 100644 --- a/include/acpi/acpi_dp.h +++ b/include/acpi/acpi_dp.h @@ -11,6 +11,8 @@ struct acpi_ctx; +#include + /* * Writing Device Properties objects via _DSD * @@ -203,6 +205,24 @@ struct acpi_dp *acpi_dp_add_integer_array(struct acpi_dp *dp, const char *name, struct acpi_dp *acpi_dp_add_child(struct acpi_dp *dp, const char *name, struct acpi_dp *child); +/** + * acpi_dp_add_gpio() - Add a GPIO to a list of Device Properties + * + * A new node is added to the end of the property list of @dp, with the + * GPIO properties added to the the new node + * + * @dp: Table to add this property to + * @name: Name of property + * @ref: Reference to device with a _CRS containing GpioIO or GpioInt + * @index: Index of the GPIO resource in _CRS starting from zero + * @pin: Pin in the GPIO resource, typically zero + * @polarity: GPIO polarity. Note that ACPI_IRQ_ACTIVE_BOTH is not supported + * @return pointer to new node, or NULL if out of memory + */ +struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name, + const char *ref, int index, int pin, + enum acpi_irq_polarity polarity); + /** * acpi_dp_write() - Write Device Property hierarchy and clean up resources * diff --git a/lib/acpi/acpi_dp.c b/lib/acpi/acpi_dp.c index ebbc5d5538..4ff857a451 100644 --- a/lib/acpi/acpi_dp.c +++ b/lib/acpi/acpi_dp.c @@ -321,3 +321,26 @@ struct acpi_dp *acpi_dp_add_integer_array(struct acpi_dp *dp, const char *name, return dp_array; } + +struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name, + const char *ref, int index, int pin, + enum acpi_irq_polarity polarity) +{ + struct acpi_dp *gpio; + + assert(dp); + gpio = acpi_dp_new_table(name); + if (!gpio) + return NULL; + + if (!acpi_dp_add_reference(gpio, NULL, ref) || + !acpi_dp_add_integer(gpio, NULL, index) || + !acpi_dp_add_integer(gpio, NULL, pin) || + !acpi_dp_add_integer(gpio, NULL, polarity == ACPI_IRQ_ACTIVE_LOW)) + return NULL; + + if (!acpi_dp_add_array(dp, gpio)) + return NULL; + + return gpio; +} diff --git a/test/dm/acpi_dp.c b/test/dm/acpi_dp.c index c11394786f..24615c384f 100644 --- a/test/dm/acpi_dp.c +++ b/test/dm/acpi_dp.c @@ -403,3 +403,43 @@ static int dm_test_acpi_dp_child(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_dp_child, 0); + +/* Test emitting a GPIO */ +static int dm_test_acpi_dp_gpio(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct acpi_dp *dp; + u8 *ptr, *pptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + + /* Try a few different parameters */ + ut_assertnonnull(acpi_dp_add_gpio(dp, "reset", TEST_REF, 0x23, 0x24, + ACPI_IRQ_ACTIVE_HIGH)); + ut_assertnonnull(acpi_dp_add_gpio(dp, "allow", TEST_REF, 0, 0, + ACPI_IRQ_ACTIVE_LOW)); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(0x6e, acpigen_get_current(ctx) - ptr); + + pptr = ptr + 0x2c; //0x3a; + ut_asserteq_str("reset", (char *)pptr); + ut_asserteq_strn(EXPECT_REF, (char *)pptr + 0xe); + ut_asserteq(0x23, pptr[0x1b]); + ut_asserteq(0x24, pptr[0x1d]); + ut_asserteq(ZERO_OP, pptr[0x1e]); + + pptr = ptr + 0x51; + ut_asserteq_str("allow", (char *)pptr); + ut_asserteq_strn(EXPECT_REF, (char *)pptr + 0xe); + ut_asserteq(ZERO_OP, pptr[0x1a]); + ut_asserteq(ZERO_OP, pptr[0x1b]); + ut_asserteq(ONE_OP, pptr[0x1c]); + + return 0; +} +DM_TEST(dm_test_acpi_dp_gpio, 0); From patchwork Sun Jun 14 02:55:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242253 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:09 -0600 Subject: [PATCH v3 21/35] acpi: Support copying properties from device tree to ACPI In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200614025523.40183-10-sjg@chromium.org> Some drivers in Linux support both device tree and ACPI. U-Boot itself uses Linux device-tree bindings for its own configuration but does not use ACPI. It is convenient to copy these values over to the device tree for passing to linux. Add some convenience functions to help with this. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- Changes in v3: - Fix incorrect function names and arguments in commments - Add error checking for acpi_dp_add...() functions arch/sandbox/dts/test.dts | 1 + include/acpi/acpi_dp.h | 51 +++++++++++++++++++++++++++++ lib/acpi/acpi_dp.c | 56 ++++++++++++++++++++++++++++++++ test/dm/acpi_dp.c | 67 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index bad0b4f1a8..173778b09c 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -111,6 +111,7 @@ uint-value = <(-1234)>; int64-value = /bits/ 64 <0x1111222233334444>; int-array = <5678 9123 4567>; + str-value = "test string"; interrupts-extended = <&irq 3 0>; }; diff --git a/include/acpi/acpi_dp.h b/include/acpi/acpi_dp.h index efbec3d152..a90abcb102 100644 --- a/include/acpi/acpi_dp.h +++ b/include/acpi/acpi_dp.h @@ -228,9 +228,60 @@ struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name, * * This writes the table using acpigen and then frees it * + * @ctx: ACPI context * @table: Table to write * @return 0 if OK, -ve on error */ int acpi_dp_write(struct acpi_ctx *ctx, struct acpi_dp *table); +/** + * acpi_dp_ofnode_copy_int() - Copy a property from device tree to DP + * + * This copies an integer property from the device tree to the ACPI DP table. + * + * @node: Node to copy from + * @dp: DP to copy to + * @prop: Property name to copy + * @return 0 if OK, -ve on error + */ +int acpi_dp_ofnode_copy_int(ofnode node, struct acpi_dp *dp, const char *prop); + +/** + * acpi_dp_ofnode_copy_str() - Copy a property from device tree to DP + * + * This copies a string property from the device tree to the ACPI DP table. + * + * @node: Node to copy from + * @dp: DP to copy to + * @prop: Property name to copy + * @return 0 if OK, -ve on error + */ +int acpi_dp_ofnode_copy_str(ofnode node, struct acpi_dp *dp, const char *prop); + +/** + * acpi_dp_dev_copy_int() - Copy a property from device tree to DP + * + * This copies an integer property from the device tree to the ACPI DP table. + * + * @dev: Device to copy from + * @dp: DP to copy to + * @prop: Property name to copy + * @return 0 if OK, -ve on error + */ +int acpi_dp_dev_copy_int(const struct udevice *dev, struct acpi_dp *dp, + const char *prop); + +/** + * acpi_dp_dev_copy_str() - Copy a property from device tree to DP + * + * This copies a string property from the device tree to the ACPI DP table. + * + * @dev: Device to copy from + * @dp: DP to copy to + * @prop: Property name to copy + * @return 0 if OK, -ve on error + */ +int acpi_dp_dev_copy_str(const struct udevice *dev, struct acpi_dp *dp, + const char *prop); + #endif diff --git a/lib/acpi/acpi_dp.c b/lib/acpi/acpi_dp.c index 4ff857a451..e85f077cc2 100644 --- a/lib/acpi/acpi_dp.c +++ b/lib/acpi/acpi_dp.c @@ -344,3 +344,59 @@ struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name, return gpio; } + +int acpi_dp_ofnode_copy_int(ofnode node, struct acpi_dp *dp, const char *prop) +{ + int ret; + u32 val = 0; + + ret = ofnode_read_u32(node, prop, &val); + if (ret) + return ret; + if (!acpi_dp_add_integer(dp, prop, val)) + return log_ret(-ENOMEM); + + return 0; +} + +int acpi_dp_ofnode_copy_str(ofnode node, struct acpi_dp *dp, const char *prop) +{ + const char *val; + + val = ofnode_read_string(node, prop); + if (!val) + return -EINVAL; + if (!acpi_dp_add_string(dp, prop, val)) + return log_ret(-ENOMEM); + + return 0; +} + +int acpi_dp_dev_copy_int(const struct udevice *dev, struct acpi_dp *dp, + const char *prop) +{ + int ret; + u32 val = 0; + + ret = dev_read_u32(dev, prop, &val); + if (ret) + return ret; + if (!acpi_dp_add_integer(dp, prop, val)) + return log_ret(-ENOMEM); + + return ret; +} + +int acpi_dp_dev_copy_str(const struct udevice *dev, struct acpi_dp *dp, + const char *prop) +{ + const char *val; + + val = dev_read_string(dev, prop); + if (!val) + return -EINVAL; + if (!acpi_dp_add_string(dp, prop, val)) + return log_ret(-ENOMEM); + + return 0; +} diff --git a/test/dm/acpi_dp.c b/test/dm/acpi_dp.c index 24615c384f..4432645a3e 100644 --- a/test/dm/acpi_dp.c +++ b/test/dm/acpi_dp.c @@ -443,3 +443,70 @@ static int dm_test_acpi_dp_gpio(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_dp_gpio, 0); + +/* Test copying info from the device tree to ACPI tables */ +static int dm_test_acpi_dp_copy(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct udevice *dev; + struct acpi_dp *dp; + ofnode node; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + + ut_assertok(acpi_dp_dev_copy_int(dev, dp, "int-value")); + ut_asserteq(-EINVAL, acpi_dp_dev_copy_int(dev, dp, "missing-value")); + ut_assertok(acpi_dp_dev_copy_int(dev, dp, "uint-value")); + + ut_assertok(acpi_dp_dev_copy_str(dev, dp, "str-value")); + ut_asserteq(-EINVAL, acpi_dp_dev_copy_str(dev, dp, "missing-value")); + + node = ofnode_path("/chosen"); + ut_assert(ofnode_valid(node)); + ut_assertok(acpi_dp_ofnode_copy_int(node, dp, "int-values")); + ut_asserteq(-EINVAL, + acpi_dp_ofnode_copy_int(node, dp, "missing-value")); + + ut_assertok(acpi_dp_ofnode_copy_str(node, dp, "setting")); + ut_asserteq(-EINVAL, + acpi_dp_ofnode_copy_str(node, dp, "missing-value")); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(0x9d, acpigen_get_current(ctx) - ptr); + + ut_asserteq(STRING_PREFIX, ptr[0x2b]); + ut_asserteq_str("int-value", (char *)ptr + 0x2c); + ut_asserteq(WORD_PREFIX, ptr[0x36]); + ut_asserteq(1234, get_unaligned((u16 *)(ptr + 0x37))); + + ut_asserteq(STRING_PREFIX, ptr[0x3e]); + ut_asserteq_str("uint-value", (char *)ptr + 0x3f); + ut_asserteq(DWORD_PREFIX, ptr[0x4a]); + ut_asserteq(-1234, get_unaligned((u32 *)(ptr + 0x4b))); + + ut_asserteq(STRING_PREFIX, ptr[0x54]); + ut_asserteq_str("str-value", (char *)ptr + 0x55); + ut_asserteq(STRING_PREFIX, ptr[0x5f]); + ut_asserteq_str("test string", (char *)ptr + 0x60); + + ut_asserteq(STRING_PREFIX, ptr[0x71]); + ut_asserteq_str("int-values", (char *)ptr + 0x72); + ut_asserteq(WORD_PREFIX, ptr[0x7d]); + ut_asserteq(0x1937, get_unaligned((u16 *)(ptr + 0x7e))); + + ut_asserteq(STRING_PREFIX, ptr[0x85]); + ut_asserteq_str("setting", (char *)ptr + 0x86); + ut_asserteq(STRING_PREFIX, ptr[0x8e]); + ut_asserteq_str("sunrise ohoka", (char *)(ptr + 0x8f)); + + return 0; +} +DM_TEST(dm_test_acpi_dp_copy, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Sun Jun 14 02:55:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242258 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:10 -0600 Subject: [PATCH v3 22/35] acpi: Add support for various misc ACPI opcodes In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200614025523.40183-11-sjg@chromium.org> Add more functions to handle some miscellaneous ACPI opcodes. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- Changes in v3: - Fix function name in comment for acpigen_write_not() - Use #defines for the mask values include/acpi/acpigen.h | 117 +++++++++++++++++++++++++++++++++++++++++ lib/acpi/acpigen.c | 84 +++++++++++++++++++++++++++++ test/dm/acpigen.c | 75 ++++++++++++++++++++++++++ 3 files changed, 276 insertions(+) diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 40cd72504a..d640324986 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -17,6 +17,9 @@ struct acpi_ctx; /* Top 4 bits of the value used to indicate a three-byte length value */ #define ACPI_PKG_LEN_3_BYTES 0x80 +#define ACPI_METHOD_NARGS_MASK 0x7 +#define ACPI_METHOD_SERIALIZED_MASK BIT(3) + /* ACPI Op/Prefix codes */ enum { ZERO_OP = 0x00, @@ -29,9 +32,26 @@ enum { QWORD_PREFIX = 0x0e, BUFFER_OP = 0x11, PACKAGE_OP = 0x12, + METHOD_OP = 0x14, + SLEEP_OP = 0x22, DUAL_NAME_PREFIX = 0x2e, MULTI_NAME_PREFIX = 0x2f, + DEBUG_OP = 0x31, + EXT_OP_PREFIX = 0x5b, ROOT_PREFIX = 0x5c, + LOCAL0_OP = 0x60, + LOCAL1_OP = 0x61, + LOCAL2_OP = 0x62, + LOCAL3_OP = 0x63, + LOCAL4_OP = 0x64, + LOCAL5_OP = 0x65, + LOCAL6_OP = 0x66, + LOCAL7_OP = 0x67, + STORE_OP = 0x70, + AND_OP = 0x7b, + OR_OP = 0x7d, + NOT_OP = 0x80, + RETURN_OP = 0xa4, }; /** @@ -201,4 +221,101 @@ void acpigen_write_name(struct acpi_ctx *ctx, const char *namepath); */ int acpigen_write_uuid(struct acpi_ctx *ctx, const char *uuid); +/** + * acpigen_emit_ext_op() - Emit an extended op with the EXT_OP_PREFIX prefix + * + * @ctx: ACPI context pointer + * @op: Operation code (e.g. SLEEP_OP) + */ +void acpigen_emit_ext_op(struct acpi_ctx *ctx, uint op); + +/** + * acpigen_write_method() - Write a method header + * + * @ctx: ACPI context pointer + * @name: Method name (4 characters) + * @nargs: Number of method arguments (0 if none) + */ +void acpigen_write_method(struct acpi_ctx *ctx, const char *name, int nargs); + +/** + * acpigen_write_method_serialized() - Write a method header + * + * This sets the 'serialized' flag so that the method is thread-safe + * + * @ctx: ACPI context pointer + * @name: Method name (4 characters) + * @nargs: Number of method arguments (0 if none) + */ +void acpigen_write_method_serialized(struct acpi_ctx *ctx, const char *name, + int nargs); + +/** + * acpigen_write_sta() - Write a _STA method + * + * @ctx: ACPI context pointer + * @status: Status value to return + */ +void acpigen_write_sta(struct acpi_ctx *ctx, uint status); + +/** + * acpigen_write_sleep() - Write a sleep operation + * + * @ctx: ACPI context pointer + * @sleep_ms: Number of milliseconds to sleep for + */ +void acpigen_write_sleep(struct acpi_ctx *ctx, u64 sleep_ms); + +/** + * acpigen_write_store() - Write a store operation + * + * @ctx: ACPI context pointer + */ +void acpigen_write_store(struct acpi_ctx *ctx); + +/** + * acpigen_write_debug_string() - Write a debug string + * + * This writes a debug operation with an associated string + * + * @ctx: ACPI context pointer + * @str: String to write + */ +void acpigen_write_debug_string(struct acpi_ctx *ctx, const char *str); + +/** + * acpigen_write_or() - Write a bitwise OR operation + * + * res = arg1 | arg2 + * + * @ctx: ACPI context pointer + * @arg1: ACPI opcode for operand 1 (e.g. LOCAL0_OP) + * @arg2: ACPI opcode for operand 2 (e.g. LOCAL1_OP) + * @res: ACPI opcode for result (e.g. LOCAL2_OP) + */ +void acpigen_write_or(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res); + +/** + * acpigen_write_and() - Write a bitwise AND operation + * + * res = arg1 & arg2 + * + * @ctx: ACPI context pointer + * @arg1: ACPI opcode for operand 1 (e.g. LOCAL0_OP) + * @arg2: ACPI opcode for operand 2 (e.g. LOCAL1_OP) + * @res: ACPI opcode for result (e.g. LOCAL2_OP) + */ +void acpigen_write_and(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res); + +/** + * acpigen_write_not() - Write a bitwise NOT operation + * + * res = ~arg1 + * + * @ctx: ACPI context pointer + * @arg: ACPI opcode for operand (e.g. LOCAL0_OP) + * @res: ACPI opcode for result (e.g. LOCAL2_OP) + */ +void acpigen_write_not(struct acpi_ctx *ctx, u8 arg, u8 res); + #endif diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index 0f08d7b8a8..81ecad774c 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -71,6 +71,12 @@ void acpigen_pop_len(struct acpi_ctx *ctx) p[2] = len >> 12 & 0xff; } +void acpigen_emit_ext_op(struct acpi_ctx *ctx, uint op) +{ + acpigen_emit_byte(ctx, EXT_OP_PREFIX); + acpigen_emit_byte(ctx, op); +} + char *acpigen_write_package(struct acpi_ctx *ctx, int nr_el) { char *p; @@ -250,6 +256,38 @@ void acpigen_write_name(struct acpi_ctx *ctx, const char *namepath) acpigen_emit_namestring(ctx, namepath); } +static void acpigen_write_method_(struct acpi_ctx *ctx, const char *name, + uint flags) +{ + acpigen_emit_byte(ctx, METHOD_OP); + acpigen_write_len_f(ctx); + acpigen_emit_namestring(ctx, name); + acpigen_emit_byte(ctx, flags); +} + +/* Method (name, nargs, NotSerialized) */ +void acpigen_write_method(struct acpi_ctx *ctx, const char *name, int nargs) +{ + acpigen_write_method_(ctx, name, nargs & ACPI_METHOD_NARGS_MASK); +} + +/* Method (name, nargs, Serialized) */ +void acpigen_write_method_serialized(struct acpi_ctx *ctx, const char *name, + int nargs) +{ + acpigen_write_method_(ctx, name, (nargs & ACPI_METHOD_NARGS_MASK) | + ACPI_METHOD_SERIALIZED_MASK); +} + +void acpigen_write_sta(struct acpi_ctx *ctx, uint status) +{ + /* Method (_STA, 0, NotSerialized) { Return (status) } */ + acpigen_write_method(ctx, "_STA", 0); + acpigen_emit_byte(ctx, RETURN_OP); + acpigen_write_byte(ctx, status); + acpigen_pop_len(ctx); +} + /* * ToUUID(uuid) * @@ -286,3 +324,49 @@ int acpigen_write_uuid(struct acpi_ctx *ctx, const char *uuid) return 0; } + +/* Sleep (ms) */ +void acpigen_write_sleep(struct acpi_ctx *ctx, u64 sleep_ms) +{ + acpigen_emit_ext_op(ctx, SLEEP_OP); + acpigen_write_integer(ctx, sleep_ms); +} + +void acpigen_write_store(struct acpi_ctx *ctx) +{ + acpigen_emit_byte(ctx, STORE_OP); +} + +/* Or (arg1, arg2, res) */ +void acpigen_write_or(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res) +{ + acpigen_emit_byte(ctx, OR_OP); + acpigen_emit_byte(ctx, arg1); + acpigen_emit_byte(ctx, arg2); + acpigen_emit_byte(ctx, res); +} + +/* And (arg1, arg2, res) */ +void acpigen_write_and(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res) +{ + acpigen_emit_byte(ctx, AND_OP); + acpigen_emit_byte(ctx, arg1); + acpigen_emit_byte(ctx, arg2); + acpigen_emit_byte(ctx, res); +} + +/* Not (arg, res) */ +void acpigen_write_not(struct acpi_ctx *ctx, u8 arg, u8 res) +{ + acpigen_emit_byte(ctx, NOT_OP); + acpigen_emit_byte(ctx, arg); + acpigen_emit_byte(ctx, res); +} + +/* Store (str, DEBUG) */ +void acpigen_write_debug_string(struct acpi_ctx *ctx, const char *str) +{ + acpigen_write_store(ctx); + acpigen_write_string(ctx, str); + acpigen_emit_ext_op(ctx, DEBUG_OP); +} diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 4f514eb9cc..86c65fbb82 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -625,3 +625,78 @@ static int dm_test_acpi_uuid(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_uuid, 0); + +/* Test writing misc ACPI codes */ +static int dm_test_acpi_misc(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + const int flags = 3; + const int nargs = 4; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_sleep(ctx, TEST_INT64); + ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 3))); + ptr += 11; + + acpigen_write_store(ctx); + ut_asserteq(STORE_OP, *ptr); + ptr++; + + acpigen_write_debug_string(ctx, TEST_STRING); + ut_asserteq_str(TEST_STRING, (char *)ptr + 2); + ptr += 2 + sizeof(TEST_STRING); + ut_asserteq(EXT_OP_PREFIX, ptr[0]); + ut_asserteq(DEBUG_OP, ptr[1]); + ptr += 2; + + acpigen_write_sta(ctx, flags); + ut_asserteq(METHOD_OP, ptr[0]); + ut_asserteq(11, get_length(ptr + 1)); + ut_asserteq_strn("_STA", (char *)ptr + 4); + ut_asserteq(0, ptr[8]); + ut_asserteq(RETURN_OP, ptr[9]); + ut_asserteq(BYTE_PREFIX, ptr[10]); + ut_asserteq(flags, ptr[11]); + ptr += 12; + + acpigen_write_sleep(ctx, TEST_INT16); + ut_asserteq(SLEEP_OP, ptr[1]); + ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 3))); + ptr += 5; + + acpigen_write_method_serialized(ctx, "FRED", nargs); + ut_asserteq(METHOD_OP, ptr[0]); + ut_asserteq_strn("FRED", (char *)ptr + 4); + ut_asserteq(1 << 3 | nargs, ptr[8]); + ut_asserteq(1, ctx->ltop); /* method is unfinished */ + + ptr += 9; + acpigen_write_or(ctx, LOCAL0_OP, LOCAL1_OP, LOCAL2_OP); + acpigen_write_and(ctx, LOCAL3_OP, LOCAL4_OP, LOCAL5_OP); + acpigen_write_not(ctx, LOCAL6_OP, LOCAL7_OP); + ut_asserteq(OR_OP, ptr[0]); + ut_asserteq(LOCAL0_OP, ptr[1]); + ut_asserteq(LOCAL1_OP, ptr[2]); + ut_asserteq(LOCAL2_OP, ptr[3]); + + ptr += 4; + ut_asserteq(AND_OP, ptr[0]); + ut_asserteq(LOCAL3_OP, ptr[1]); + ut_asserteq(LOCAL4_OP, ptr[2]); + ut_asserteq(LOCAL5_OP, ptr[3]); + + ptr += 4; + ut_asserteq(NOT_OP, ptr[0]); + ut_asserteq(LOCAL6_OP, ptr[1]); + ut_asserteq(LOCAL7_OP, ptr[2]); + ptr += 3; + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_misc, 0); From patchwork Sun Jun 14 02:55:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242255 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:11 -0600 Subject: [PATCH v3 23/35] acpi: Add support for writing a Power Resource In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200614025523.40183-12-sjg@chromium.org> These are used in ACPI to disable power to various pats of the system when in sleep. Add a way to create a power resource, with the caller finishing off the details. Reviewed-by: Wolfgang Wallner Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- (no changes since v1) include/acpi/acpigen.h | 22 ++++++++++++++++++++++ lib/acpi/acpigen.c | 22 ++++++++++++++++++++++ test/dm/acpigen.c | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index d640324986..d7a7f2f538 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -51,6 +51,7 @@ enum { AND_OP = 0x7b, OR_OP = 0x7d, NOT_OP = 0x80, + POWER_RES_OP = 0x84, RETURN_OP = 0xa4, }; @@ -318,4 +319,25 @@ void acpigen_write_and(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res); */ void acpigen_write_not(struct acpi_ctx *ctx, u8 arg, u8 res); +/** + * acpigen_write_power_res() - Write a power resource + * + * Name (_PRx, Package(One) { name }) + * ... + * PowerResource (name, level, order) + * + * The caller should fill in the rest of the power resource and then call + * acpigen_pop_len() to close it off + * + * @ctx: ACPI context pointer + * @name: Name of power resource (e.g. "PRIC") + * @level: Deepest sleep level that this resource must be kept on (0=S0, 3=S3) + * @order: Order that this must be enabled/disabled (e.g. 0) + * @dev_stats: List of states to define, e.g. {"_PR0", "_PR3"} + * @dev_states_count: Number of dev states + */ +void acpigen_write_power_res(struct acpi_ctx *ctx, const char *name, uint level, + uint order, const char *const dev_states[], + size_t dev_states_count); + #endif diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index 81ecad774c..5df6ebe334 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -325,6 +325,28 @@ int acpigen_write_uuid(struct acpi_ctx *ctx, const char *uuid) return 0; } +void acpigen_write_power_res(struct acpi_ctx *ctx, const char *name, uint level, + uint order, const char *const dev_states[], + size_t dev_states_count) +{ + size_t i; + + for (i = 0; i < dev_states_count; i++) { + acpigen_write_name(ctx, dev_states[i]); + acpigen_write_package(ctx, 1); + acpigen_emit_simple_namestring(ctx, name); + acpigen_pop_len(ctx); /* Package */ + } + + acpigen_emit_ext_op(ctx, POWER_RES_OP); + + acpigen_write_len_f(ctx); + + acpigen_emit_simple_namestring(ctx, name); + acpigen_emit_byte(ctx, level); + acpigen_emit_word(ctx, order); +} + /* Sleep (ms) */ void acpigen_write_sleep(struct acpi_ctx *ctx, u64 sleep_ms) { diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 86c65fbb82..e66508932b 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -700,3 +700,44 @@ static int dm_test_acpi_misc(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_misc, 0); + +/* Test writing an ACPI power resource */ +static int dm_test_acpi_power_res(struct unit_test_state *uts) +{ + const char *const states[] = { "_PR0", "_PR3" }; + const char *name = "PRIC"; + const int level = 3; + const int order = 2; + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + /* PowerResource (PRIC, 0, 0) */ + acpigen_write_power_res(ctx, name, level, order, states, + ARRAY_SIZE(states)); + ut_asserteq(0x28, acpigen_get_current(ctx) - ptr); + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq_strn(states[0], (char *)ptr + 1); + ut_asserteq(8, get_length(ptr + 6)); + ut_asserteq_strn(name, (char *)ptr + 0xa); + + ut_asserteq_strn(states[1], (char *)ptr + 0xf); + ut_asserteq(8, get_length(ptr + 0x14)); + ut_asserteq_strn(name, (char *)ptr + 0x18); + + ut_asserteq(POWER_RES_OP, ptr[0x1d]); + ut_asserteq_strn(name, (char *)ptr + 0x21); + ut_asserteq(level, ptr[0x25]); + ut_asserteq(order, get_unaligned((u16 *)(ptr + 0x26))); + + /* The length is not set - caller must use acpigen_pop_len() */ + ut_asserteq(1, ctx->ltop); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_power_res, 0); From patchwork Sun Jun 14 02:55:12 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242257 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:12 -0600 Subject: [PATCH v3 24/35] acpi: Add support for writing a GPIO power sequence In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200614025523.40183-13-sjg@chromium.org> Power to some devices is controlled by GPIOs. Add a way to generate ACPI code to enable and disable a GPIO so that this can be handled within an ACPI method. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- (no changes since v1) include/acpi/acpigen.h | 12 +++++++ lib/acpi/acpigen.c | 80 ++++++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 54 ++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+) diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index d7a7f2f538..d68dca5dde 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -13,6 +13,7 @@ #include struct acpi_ctx; +struct acpi_gpio; /* Top 4 bits of the value used to indicate a three-byte length value */ #define ACPI_PKG_LEN_3_BYTES 0x80 @@ -340,4 +341,15 @@ void acpigen_write_power_res(struct acpi_ctx *ctx, const char *name, uint level, uint order, const char *const dev_states[], size_t dev_states_count); +/* + * Helper functions for enabling/disabling Tx GPIOs based on the GPIO + * polarity. These functions end up calling acpigen_soc_{set,clear}_tx_gpio to + * make callbacks into SoC acpigen code. + * + * Returns 0 on success and -1 on error. + */ +int acpigen_set_enable_tx_gpio(struct acpi_ctx *ctx, u32 tx_state_val, + const char *dw0_name, struct acpi_gpio *gpio, + bool enable); + #endif diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index 5df6ebe334..e0af36f775 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -13,6 +13,7 @@ #include #include #include +#include #include u8 *acpigen_get_current(struct acpi_ctx *ctx) @@ -392,3 +393,82 @@ void acpigen_write_debug_string(struct acpi_ctx *ctx, const char *str) acpigen_write_string(ctx, str); acpigen_emit_ext_op(ctx, DEBUG_OP); } + +/** + * acpigen_get_dw0_in_local5() - Generate code to put dw0 cfg0 in local5 + * + * @ctx: ACPI context pointer + * @dw0_name: Name to use (e.g. "\\_SB.GPC0") + * @addr: GPIO pin configuration register address + * + * Store (\_SB.GPC0 (addr), Local5) + * \_SB.GPC0 is used to read cfg0 value from dw0. It is typically defined in + * the board's gpiolib.asl + */ +static void acpigen_get_dw0_in_local5(struct acpi_ctx *ctx, + const char *dw0_name, ulong addr) +{ + acpigen_write_store(ctx); + acpigen_emit_namestring(ctx, dw0_name); + acpigen_write_integer(ctx, addr); + acpigen_emit_byte(ctx, LOCAL5_OP); +} + +/** + * acpigen_set_gpio_val() - Set value of TX GPIO to on/off + * + * @ctx: ACPI context pointer + * @dw0_name: Name to use (e.g. "\\_SB.GPC0") + * @gpio_num: GPIO number to adjust + * @vaL: true to set on, false to set off + */ +static int acpigen_set_gpio_val(struct acpi_ctx *ctx, u32 tx_state_val, + const char *dw0_name, struct acpi_gpio *gpio, + bool val) +{ + acpigen_get_dw0_in_local5(ctx, dw0_name, gpio->pin0_addr); + + if (val) { + /* Or (Local5, PAD_CFG0_TX_STATE, Local5) */ + acpigen_write_or(ctx, LOCAL5_OP, tx_state_val, LOCAL5_OP); + } else { + /* Not (PAD_CFG0_TX_STATE, Local6) */ + acpigen_write_not(ctx, tx_state_val, LOCAL6_OP); + + /* And (Local5, Local6, Local5) */ + acpigen_write_and(ctx, LOCAL5_OP, LOCAL6_OP, LOCAL5_OP); + } + + /* + * \_SB.SPC0 (addr, Local5) + * \_SB.SPC0 is used to write cfg0 value in dw0. It is defined in + * gpiolib.asl. + */ + acpigen_emit_namestring(ctx, dw0_name); + acpigen_write_integer(ctx, gpio->pin0_addr); + acpigen_emit_byte(ctx, LOCAL5_OP); + + return 0; +} + +/* + * Helper functions for enabling/disabling Tx GPIOs based on the GPIO + * polarity. These functions end up calling acpigen_{set,clear}_tx_gpio to + * make callbacks into SoC acpigen code. + * + * Returns 0 on success and -1 on error. + */ +int acpigen_set_enable_tx_gpio(struct acpi_ctx *ctx, u32 tx_state_val, + const char *dw0_name, struct acpi_gpio *gpio, + bool enable) +{ + bool set; + int ret; + + set = gpio->polarity == ACPI_GPIO_ACTIVE_HIGH ? enable : !enable; + ret = acpigen_set_gpio_val(ctx, tx_state_val, dw0_name, gpio, set); + if (ret) + return log_msg_ret("call", ret); + + return 0; +} diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index e66508932b..cbd0007365 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -741,3 +741,57 @@ static int dm_test_acpi_power_res(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_power_res, 0); + +/* Test writing ACPI code to toggle a GPIO */ +static int dm_test_acpi_gpio_toggle(struct unit_test_state *uts) +{ + const uint addr = 0x80012; + const int txbit = BIT(2); + struct gpio_desc desc; + struct acpi_gpio gpio; + struct acpi_ctx *ctx; + struct udevice *dev; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &desc, 0)); + ut_assertok(gpio_get_acpi(&desc, &gpio)); + + /* Spot-check the results - see sb_gpio_get_acpi() */ + ptr = acpigen_get_current(ctx); + acpigen_set_enable_tx_gpio(ctx, txbit, "\\_SB.GPC0", &gpio, true); + acpigen_set_enable_tx_gpio(ctx, txbit, "\\_SB.GPC0", &gpio, false); + + /* Since this GPIO is active low, we expect it to be cleared here */ + ut_asserteq(STORE_OP, ptr[0]); + ut_asserteq_strn("_SB_GPC0", (char *)ptr + 3); + ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0xc))); + ut_asserteq(LOCAL5_OP, ptr[0x10]); + + ut_asserteq(NOT_OP, ptr[0x11]); + ut_asserteq(txbit, ptr[0x12]); + ut_asserteq(AND_OP, ptr[0x14]); + ut_asserteq_strn("_SB_GPC0", (char *)ptr + 0x1a); + ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0x23))); + ut_asserteq(LOCAL5_OP, ptr[0x27]); + + /* Now the second one, which should be set */ + ut_asserteq_strn("_SB_GPC0", (char *)ptr + 0x2b); + ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0x34))); + ut_asserteq(LOCAL5_OP, ptr[0x38]); + + ut_asserteq(OR_OP, ptr[0x39]); + ut_asserteq(txbit, ptr[0x3b]); + ut_asserteq_strn("_SB_GPC0", (char *)ptr + 0x3f); + ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0x48))); + ut_asserteq(LOCAL5_OP, ptr[0x4c]); + ut_asserteq(0x4d, acpigen_get_current(ctx) - ptr); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_gpio_toggle, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Sun Jun 14 02:55:13 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242259 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:13 -0600 Subject: [PATCH v3 25/35] acpi: Add support for a generic power sequence In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200614025523.40183-14-sjg@chromium.org> Add a way for devices to enable and disable themselves using ACPI code that updates GPIOs. This takes several timing parameters and supports enable, reset and stop. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- (no changes since v1) include/acpi/acpi_device.h | 41 ++++++++++++++++ lib/acpi/acpi_device.c | 99 ++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 68 ++++++++++++++++++++++++++ 3 files changed, 208 insertions(+) diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index c0c96183e4..5019a79528 100644 --- a/include/acpi/acpi_device.h +++ b/include/acpi/acpi_device.h @@ -342,4 +342,45 @@ int acpi_device_write_i2c_dev(struct acpi_ctx *ctx, const struct udevice *dev); */ int acpi_device_write_spi_dev(struct acpi_ctx *ctx, const struct udevice *dev); +/** + * acpi_device_add_power_res() - Add a basic PowerResource block for a device + * + * This includes GPIOs to control enable, reset and stop operation of the + * device. Each GPIO is optional, but at least one must be provided. + * This can be applied to any device that has power control, so is fairly + * generic. + * + * Reset - Put the device into / take the device out of reset. + * Enable - Enable / disable power to device. + * Stop - Stop / start operation of device. + * + * @ctx: ACPI context pointer + * @tx_state_val: Mask to use to toggle the TX state on the GPIO pin, e,g. + * PAD_CFG0_TX_STATE + * @dw0_name: Name to use for access to dw0, e.g. "\\_SB.GPC0" + * @reset_gpio: GPIO used to take device out of reset or to put it into reset + * @reset_delay_ms: Delay to be inserted after device is taken out of reset + * (_ON method delay) + * @reset_off_delay_ms: Delay to be inserted after device is put into reset + * (_OFF method delay) + * @enable_gpio: GPIO used to enable device + * @enable_delay_ms: Delay to be inserted after device is enabled + * @enable_off_delay_ms: Delay to be inserted after device is disabled + * (_OFF method delay) + * @stop_gpio: GPIO used to stop operation of device + * @stop_delay_ms: Delay to be inserted after disabling stop (_ON method delay) + * @stop_off_delay_ms: Delay to be inserted after enabling stop. + * (_OFF method delay) + * + * @return 0 if OK, -ve if at least one GPIO is not provided + */ +int acpi_device_add_power_res(struct acpi_ctx *ctx, u32 tx_state_val, + const char *dw0_name, + const struct gpio_desc *reset_gpio, + uint reset_delay_ms, uint reset_off_delay_ms, + const struct gpio_desc *enable_gpio, + uint enable_delay_ms, uint enable_off_delay_ms, + const struct gpio_desc *stop_gpio, + uint stop_delay_ms, uint stop_off_delay_ms); + #endif diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c index 38101c8b96..912369498a 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -382,6 +382,105 @@ int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx, return 0; } +/* PowerResource() with Enable and/or Reset control */ +int acpi_device_add_power_res(struct acpi_ctx *ctx, u32 tx_state_val, + const char *dw0_name, + const struct gpio_desc *reset_gpio, + uint reset_delay_ms, uint reset_off_delay_ms, + const struct gpio_desc *enable_gpio, + uint enable_delay_ms, uint enable_off_delay_ms, + const struct gpio_desc *stop_gpio, + uint stop_delay_ms, uint stop_off_delay_ms) +{ + static const char *const power_res_dev_states[] = { "_PR0", "_PR3" }; + struct acpi_gpio reset, enable, stop; + bool has_reset, has_enable, has_stop; + int ret; + + gpio_get_acpi(reset_gpio, &reset); + gpio_get_acpi(enable_gpio, &enable); + gpio_get_acpi(stop_gpio, &stop); + has_reset = reset.pins[0]; + has_enable = enable.pins[0]; + has_stop = stop.pins[0]; + + if (!has_reset && !has_enable && !has_stop) + return -EINVAL; + + /* PowerResource (PRIC, 0, 0) */ + acpigen_write_power_res(ctx, "PRIC", 0, 0, power_res_dev_states, + ARRAY_SIZE(power_res_dev_states)); + + /* Method (_STA, 0, NotSerialized) { Return (0x1) } */ + acpigen_write_sta(ctx, 0x1); + + /* Method (_ON, 0, Serialized) */ + acpigen_write_method_serialized(ctx, "_ON", 0); + if (reset_gpio) { + ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_name, + &reset, true); + if (ret) + return log_msg_ret("reset1", ret); + } + if (has_enable) { + ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_name, + &enable, true); + if (ret) + return log_msg_ret("enable1", ret); + if (enable_delay_ms) + acpigen_write_sleep(ctx, enable_delay_ms); + } + if (has_reset) { + ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_name, + &reset, false); + if (ret) + return log_msg_ret("reset2", ret); + if (reset_delay_ms) + acpigen_write_sleep(ctx, reset_delay_ms); + } + if (has_stop) { + ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_name, + &stop, false); + if (ret) + return log_msg_ret("stop1", ret); + if (stop_delay_ms) + acpigen_write_sleep(ctx, stop_delay_ms); + } + acpigen_pop_len(ctx); /* _ON method */ + + /* Method (_OFF, 0, Serialized) */ + acpigen_write_method_serialized(ctx, "_OFF", 0); + if (has_stop) { + ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_name, + &stop, true); + if (ret) + return log_msg_ret("stop2", ret); + if (stop_off_delay_ms) + acpigen_write_sleep(ctx, stop_off_delay_ms); + } + if (has_reset) { + ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_name, + &reset, true); + if (ret) + return log_msg_ret("reset3", ret); + if (reset_off_delay_ms) + acpigen_write_sleep(ctx, reset_off_delay_ms); + } + if (has_enable) { + ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_name, + &enable, false); + if (ret) + return log_msg_ret("enable2", ret); + if (enable_off_delay_ms) + acpigen_write_sleep(ctx, enable_off_delay_ms); + } + acpigen_pop_len(ctx); /* _OFF method */ + + acpigen_pop_len(ctx); /* PowerResource PRIC */ + + return 0; +} + /* ACPI 6.3 section 6.4.3.8.2.1 - I2cSerialBus() */ static void acpi_device_write_i2c(struct acpi_ctx *ctx, const struct acpi_i2c *i2c) diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index cbd0007365..3999825953 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -795,3 +795,71 @@ static int dm_test_acpi_gpio_toggle(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_gpio_toggle, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test writing ACPI code to output power-sequence info */ +static int dm_test_acpi_power_seq(struct unit_test_state *uts) +{ + struct gpio_desc reset, enable, stop; + const uint addr = 0xc00dc, addr_act_low = 0x80012; + const int txbit = BIT(2); + struct acpi_ctx *ctx; + struct udevice *dev; + u8 *ptr; + + ut_assertok(alloc_context_size(&ctx, 400)); + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(gpio_request_by_name(dev, "test2-gpios", 0, &reset, 0)); + ut_assertok(gpio_request_by_name(dev, "test2-gpios", 1, &enable, 0)); + ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &stop, 0)); + ptr = acpigen_get_current(ctx); + + ut_assertok(acpi_device_add_power_res(ctx, txbit, "\\_SB.GPC0", + &reset, 2, 3, &enable, 4, 5, + &stop, 6, 7)); + ut_asserteq(0x16a, acpigen_get_current(ctx) - ptr); + ut_asserteq_strn("PRIC", (char *)ptr + 0x18); + + /* First the 'ON' sequence - spot check */ + ut_asserteq_strn("_ON_", (char *)ptr + 0x38); + + /* reset set */ + ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x49))); + ut_asserteq(OR_OP, ptr[0x4e]); + + /* enable set */ + ut_asserteq(addr + enable.offset, get_unaligned((u32 *)(ptr + 0x6e))); + ut_asserteq(OR_OP, ptr[0x73]); + + /* reset clear */ + ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x97))); + ut_asserteq(NOT_OP, ptr[0x9c]); + + /* stop set (disable, active low) */ + ut_asserteq(addr_act_low + stop.offset, + get_unaligned((u32 *)(ptr + 0xc3))); + ut_asserteq(OR_OP, ptr[0xc8]); + + /* Now the 'OFF' sequence */ + ut_asserteq_strn("_OFF", (char *)ptr + 0xe4); + + /* stop clear (enable, active low) */ + ut_asserteq(addr_act_low + stop.offset, + get_unaligned((u32 *)(ptr + 0xf5))); + ut_asserteq(NOT_OP, ptr[0xfa]); + + /* reset clear */ + ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x121))); + ut_asserteq(OR_OP, ptr[0x126]); + + /* enable clear */ + ut_asserteq(addr + enable.offset, get_unaligned((u32 *)(ptr + 0x14a))); + ut_asserteq(NOT_OP, ptr[0x14f]); + + free_context(&ctx); + + return 0; +} + +DM_TEST(dm_test_acpi_power_seq, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Sun Jun 14 02:55:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242260 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:14 -0600 Subject: [PATCH v3 26/35] acpi: Add support for SSDT generation In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200614025523.40183-15-sjg@chromium.org> Some devices need to generate code for the Secondary System Descriptor Table (SSDT). Add a method to handle this. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- Changes in v3: - Fix 'THe' typo Changes in v1: - Switch parameter order of _acpi_fill_ssdt() and make it static - Fix 'sentinal' and 'METHOD_FILL_SDDT' typos arch/sandbox/dts/test.dts | 2 ++ drivers/core/acpi.c | 14 +++++++++++++ include/dm/acpi.h | 23 +++++++++++++++++++++ test/dm/acpi.c | 42 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 173778b09c..30394946f8 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -256,6 +256,7 @@ acpi-test { compatible = "denx,u-boot-acpi-test"; + acpi-ssdt-test-data = "ab"; child { compatible = "denx,u-boot-acpi-test"; }; @@ -263,6 +264,7 @@ acpi-test2 { compatible = "denx,u-boot-acpi-test"; + acpi-ssdt-test-data = "cd"; }; clocks { diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index 8ae61575dd..4497b5cb2f 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -18,6 +18,7 @@ /* Type of method to call */ enum method_t { METHOD_WRITE_TABLES, + METHOD_FILL_SSDT, }; /* Prototype for all methods */ @@ -51,6 +52,8 @@ acpi_method acpi_get_method(struct udevice *dev, enum method_t method) switch (method) { case METHOD_WRITE_TABLES: return aops->write_tables; + case METHOD_FILL_SSDT: + return aops->fill_ssdt; } } @@ -84,6 +87,17 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent, return 0; } +int acpi_fill_ssdt(struct acpi_ctx *ctx) +{ + int ret; + + log_debug("Writing SSDT tables\n"); + ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_SSDT); + log_debug("Writing SSDT finished, err=%d\n", ret); + + return ret; +} + int acpi_write_dev_tables(struct acpi_ctx *ctx) { int ret; diff --git a/include/dm/acpi.h b/include/dm/acpi.h index f27ca64507..eda23f97a6 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -74,6 +74,19 @@ struct acpi_ops { * @return 0 if OK, -ve on error */ int (*write_tables)(const struct udevice *dev, struct acpi_ctx *ctx); + + /** + * fill_ssdt() - Generate SSDT code for a device + * + * This is called to create the SSDT code. The method should write out + * whatever ACPI code is needed by this device. It will end up in the + * SSDT table. + * + * @dev: Device to write + * @ctx: ACPI context to use + * @return 0 if OK, -ve on error + */ + int (*fill_ssdt)(const struct udevice *dev, struct acpi_ctx *ctx); }; #define device_get_acpi_ops(dev) ((dev)->driver->acpi_ops) @@ -118,6 +131,16 @@ int acpi_copy_name(char *out_name, const char *name); */ int acpi_write_dev_tables(struct acpi_ctx *ctx); +/** + * acpi_fill_ssdt() - Generate ACPI tables for SSDT + * + * This is called to create the SSDT code for all devices. + * + * @ctx: ACPI context to use + * @return 0 if OK, -ve on error + */ +int acpi_fill_ssdt(struct acpi_ctx *ctx); + #endif /* __ACPI__ */ #endif diff --git a/test/dm/acpi.c b/test/dm/acpi.c index 07b0daaab0..d46d1fbe66 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -67,9 +68,23 @@ static int testacpi_get_name(const struct udevice *dev, char *out_name) return acpi_copy_name(out_name, ACPI_TEST_DEV_NAME); } +static int testacpi_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx) +{ + const char *data; + + data = dev_read_string(dev, "acpi-ssdt-test-data"); + if (data) { + while (*data) + acpigen_emit_byte(ctx, *data++); + } + + return 0; +} + struct acpi_ops testacpi_ops = { .get_name = testacpi_get_name, .write_tables = testacpi_write_tables, + .fill_ssdt = testacpi_fill_ssdt, }; static const struct udevice_id testacpi_ids[] = { @@ -396,3 +411,30 @@ static int dm_test_acpi_device_status(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_device_status, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test acpi_fill_ssdt() */ +static int dm_test_acpi_fill_ssdt(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + u8 *buf; + + buf = malloc(BUF_SIZE); + ut_assertnonnull(buf); + + ctx.current = buf; + buf[4] = 'z'; /* sentinel */ + ut_assertok(acpi_fill_ssdt(&ctx)); + + /* These values come from acpi-test's acpi-ssdt-test-data property */ + ut_asserteq('a', buf[0]); + ut_asserteq('b', buf[1]); + + /* These values come from acpi-test2's acpi-ssdt-test-data property */ + ut_asserteq('c', buf[2]); + ut_asserteq('d', buf[3]); + + ut_asserteq('z', buf[4]); + + return 0; +} +DM_TEST(dm_test_acpi_fill_ssdt, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Sun Jun 14 02:55:15 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242262 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:15 -0600 Subject: [PATCH v3 27/35] x86: acpi: Move MADT down a bit In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.27.Ib6a3a2f7b429bfd6a43f7bea0543862a1a2eced0@changeid> Put this table before MCFG so that it matches the order that coreboot uses when passing tables to Linux. This is a cosmetic change since the order of the tables does not otherwise matter. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- (no changes since v1) Changes in v1: - Correct the commit subject arch/x86/lib/acpi_table.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 6985ef4ba5..e96acf08d4 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -418,18 +418,18 @@ ulong write_acpi_tables(ulong start_addr) acpi_create_fadt(fadt, facs, dsdt); acpi_add_table(ctx, fadt); - debug("ACPI: * MADT\n"); - madt = ctx->current; - acpi_create_madt(madt); - acpi_inc_align(ctx, madt->header.length); - acpi_add_table(ctx, madt); - debug("ACPI: * MCFG\n"); mcfg = ctx->current; acpi_create_mcfg(mcfg); acpi_inc_align(ctx, mcfg->header.length); acpi_add_table(ctx, mcfg); + debug("ACPI: * MADT\n"); + madt = ctx->current; + acpi_create_madt(madt); + acpi_inc_align(ctx, madt->header.length); + acpi_add_table(ctx, madt); + debug("ACPI: * CSRT\n"); csrt = ctx->current; acpi_create_csrt(csrt); From patchwork Sun Jun 14 02:55:16 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242261 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:16 -0600 Subject: [PATCH v3 28/35] acpi: Record the items added to SSDT In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.28.I1f119e7a847da3e0b2b1791e6f48acaeaf25a223@changeid> It is useful to be able to control the order of data written to the SSDT so that we can compare the output against known-good kernel dumps. Add code to record each item that is added along with the device that added it. That allows us to reorder things later if needed. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- Changes in v3: - Rename MAX_ITEMS to MAX_ACPI_ITEMS Changes in v1: - Generalise the ACPI function recursion with acpi_recurse_method() drivers/core/acpi.c | 83 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index 4497b5cb2f..df3d7ba417 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -9,12 +9,21 @@ #define LOG_CATEOGRY LOGC_ACPI #include +#include #include #include #include #include #include +#define MAX_ACPI_ITEMS 100 + +/* Type of table that we collected */ +enum gen_type_t { + TYPE_NONE, + TYPE_SSDT, +}; + /* Type of method to call */ enum method_t { METHOD_WRITE_TABLES, @@ -24,6 +33,25 @@ enum method_t { /* Prototype for all methods */ typedef int (*acpi_method)(const struct udevice *dev, struct acpi_ctx *ctx); +/** + * struct acpi_item - Holds info about ACPI data generated by a driver method + * + * @dev: Device that generated this data + * @type: Table type it refers to + * @buf: Buffer containing the data + * @size: Size of the data in bytes + */ +struct acpi_item { + struct udevice *dev; + enum gen_type_t type; + char *buf; + int size; +}; + +/* List of ACPI items collected */ +static struct acpi_item acpi_item[MAX_ACPI_ITEMS]; +static int item_count; + int acpi_copy_name(char *out_name, const char *name) { strncpy(out_name, name, ACPI_NAME_LEN); @@ -43,6 +71,43 @@ int acpi_get_name(const struct udevice *dev, char *out_name) return -ENOSYS; } +/** + * acpi_add_item() - Add a new item to the list of data collected + * + * @ctx: ACPI context + * @dev: Device that generated the data + * @type: Table type it refers to + * @start: The start of the data (the end is obtained from ctx->current) + * @return 0 if OK, -ENOSPC if too many items, -ENOMEM if out of memory + */ +static int acpi_add_item(struct acpi_ctx *ctx, struct udevice *dev, + enum gen_type_t type, void *start) +{ + struct acpi_item *item; + void *end = ctx->current; + + if (item_count == MAX_ACPI_ITEMS) { + log_err("Too many items\n"); + return log_msg_ret("mem", -ENOSPC); + } + + item = &acpi_item[item_count]; + item->dev = dev; + item->type = type; + item->size = end - start; + if (!item->size) + return 0; + item->buf = malloc(item->size); + if (!item->buf) + return log_msg_ret("mem", -ENOMEM); + memcpy(item->buf, start, item->size); + item_count++; + log_debug("* %s: Added type %d, %p, size %x\n", dev->name, type, start, + item->size); + + return 0; +} + acpi_method acpi_get_method(struct udevice *dev, enum method_t method) { struct acpi_ops *aops; @@ -61,7 +126,7 @@ acpi_method acpi_get_method(struct udevice *dev, enum method_t method) } int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent, - enum method_t method) + enum method_t method, enum gen_type_t type) { struct udevice *dev; acpi_method func; @@ -69,6 +134,8 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent, func = acpi_get_method(parent, method); if (func) { + void *start = ctx->current; + log_debug("\n"); log_debug("- %s %p\n", parent->name, func); ret = device_ofdata_to_platdata(parent); @@ -77,9 +144,16 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent, ret = func(parent, ctx); if (ret) return log_msg_ret("func", ret); + + /* Add the item to the internal list */ + if (type != TYPE_NONE) { + ret = acpi_add_item(ctx, parent, type, start); + if (ret) + return log_msg_ret("add", ret); + } } device_foreach_child(dev, parent) { - ret = acpi_recurse_method(ctx, dev, method); + ret = acpi_recurse_method(ctx, dev, method, type); if (ret) return log_msg_ret("recurse", ret); } @@ -92,7 +166,7 @@ int acpi_fill_ssdt(struct acpi_ctx *ctx) int ret; log_debug("Writing SSDT tables\n"); - ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_SSDT); + ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_SSDT, TYPE_SSDT); log_debug("Writing SSDT finished, err=%d\n", ret); return ret; @@ -103,7 +177,8 @@ int acpi_write_dev_tables(struct acpi_ctx *ctx) int ret; log_debug("Writing device tables\n"); - ret = acpi_recurse_method(ctx, dm_root(), METHOD_WRITE_TABLES); + ret = acpi_recurse_method(ctx, dm_root(), METHOD_WRITE_TABLES, + TYPE_NONE); log_debug("Writing finished, err=%d\n", ret); return ret; From patchwork Sun Jun 14 02:55:17 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242264 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:17 -0600 Subject: [PATCH v3 29/35] acpi: Support ordering SSDT data by device In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.29.I91d8e58921d27a54f6ecb58ecd5b72d141238f63@changeid> Add a /chosen property to control the order in which the data appears in the SSDT. This allows matching up U-Boot's output from a dump of the known-good data obtained from within Linux. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- Changes in v3: - Make find_item() static and rename to find_acpi_item() - Rename build_type() and add a comment Changes in v1: - Generalise the ACPI function recursion with acpi_recurse_method() arch/sandbox/dts/test.dts | 5 +- doc/device-tree-bindings/chosen.txt | 9 ++++ drivers/core/acpi.c | 75 +++++++++++++++++++++++++++++ test/dm/acpi.c | 15 +++--- 4 files changed, 96 insertions(+), 8 deletions(-) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 30394946f8..993082763d 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -254,7 +254,7 @@ compatible = "denx,u-boot-devres-test"; }; - acpi-test { + acpi_test1: acpi-test { compatible = "denx,u-boot-acpi-test"; acpi-ssdt-test-data = "ab"; child { @@ -262,7 +262,7 @@ }; }; - acpi-test2 { + acpi_test2: acpi-test2 { compatible = "denx,u-boot-acpi-test"; acpi-ssdt-test-data = "cd"; }; @@ -895,6 +895,7 @@ setting = "sunrise ohoka"; other-node = "/some-bus/c-test at 5"; int-values = <0x1937 72993>; + u-boot,acpi-ssdt-order = <&acpi_test2 &acpi_test1>; chosen-test { compatible = "denx,u-boot-fdt-test"; reg = <9 1>; diff --git a/doc/device-tree-bindings/chosen.txt b/doc/device-tree-bindings/chosen.txt index 395c9501e3..d4dfc05847 100644 --- a/doc/device-tree-bindings/chosen.txt +++ b/doc/device-tree-bindings/chosen.txt @@ -134,3 +134,12 @@ Example phandlepart = <&mmc 1>; }; }; + +u-boot,acpi-ssdt-order +---------------------- + +This provides the ordering to use when writing device data to the ACPI SSDT +(Secondary System Descriptor Table). Each cell is a phandle pointer to a device +node to add. The ACPI information is written in this order. + +If the ordering does not include all nodes, an error is generated. diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index df3d7ba417..4719a5c4e4 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -108,6 +108,76 @@ static int acpi_add_item(struct acpi_ctx *ctx, struct udevice *dev, return 0; } +static struct acpi_item *find_acpi_item(const char *devname) +{ + int i; + + for (i = 0; i < item_count; i++) { + struct acpi_item *item = &acpi_item[i]; + + if (!strcmp(devname, item->dev->name)) + return item; + } + + return NULL; +} + +/** + * sort_acpi_item_type - Sort the ACPI items into the desired order + * + * This looks up the ordering in the device tree and then adds each item one by + * one into the supplied buffer + * + * @ctx: ACPI context + * @start: Start position to put the sorted items. The items will follow each + * other in sorted order + * @type: Type of items to sort + * @return 0 if OK, -ve on error + */ +static int sort_acpi_item_type(struct acpi_ctx *ctx, void *start, + enum gen_type_t type) +{ + const u32 *order; + int size; + int count; + void *ptr; + void *end = ctx->current; + + ptr = start; + order = ofnode_read_chosen_prop("u-boot,acpi-ssdt-order", &size); + if (!order) { + log_warning("Failed to find ordering, leaving as is\n"); + return 0; + } + + count = size / sizeof(u32); + while (count--) { + struct acpi_item *item; + const char *name; + ofnode node; + + node = ofnode_get_by_phandle(fdt32_to_cpu(*order++)); + name = ofnode_get_name(node); + item = find_acpi_item(name); + if (!item) { + log_err("Failed to find item '%s'\n", name); + return log_msg_ret("find", -ENOENT); + } + if (item->type == type) { + log_debug(" - add %s\n", item->dev->name); + memcpy(ptr, item->buf, item->size); + ptr += item->size; + } + } + + if (ptr != end) { + log_warning("*** Missing bytes: ptr=%p, end=%p\n", ptr, end); + return -ENXIO; + } + + return 0; +} + acpi_method acpi_get_method(struct udevice *dev, enum method_t method) { struct acpi_ops *aops; @@ -163,11 +233,16 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent, int acpi_fill_ssdt(struct acpi_ctx *ctx) { + void *start = ctx->current; int ret; log_debug("Writing SSDT tables\n"); + item_count = 0; ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_SSDT, TYPE_SSDT); log_debug("Writing SSDT finished, err=%d\n", ret); + ret = sort_acpi_item_type(ctx, start, TYPE_SSDT); + if (ret) + return log_msg_ret("build", ret); return ret; } diff --git a/test/dm/acpi.c b/test/dm/acpi.c index d46d1fbe66..4e1d401e0d 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -425,13 +425,16 @@ static int dm_test_acpi_fill_ssdt(struct unit_test_state *uts) buf[4] = 'z'; /* sentinel */ ut_assertok(acpi_fill_ssdt(&ctx)); - /* These values come from acpi-test's acpi-ssdt-test-data property */ - ut_asserteq('a', buf[0]); - ut_asserteq('b', buf[1]); + /* + * These values come from acpi-test2's acpi-ssdt-test-data property. + * This device comes first because of u-boot,acpi-ssdt-order + */ + ut_asserteq('c', buf[0]); + ut_asserteq('d', buf[1]); - /* These values come from acpi-test2's acpi-ssdt-test-data property */ - ut_asserteq('c', buf[2]); - ut_asserteq('d', buf[3]); + /* These values come from acpi-test's acpi-ssdt-test-data property */ + ut_asserteq('a', buf[2]); + ut_asserteq('b', buf[3]); ut_asserteq('z', buf[4]); From patchwork Sun Jun 14 02:55:18 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242263 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:18 -0600 Subject: [PATCH v3 30/35] x86: Allow devices to write an SSDT In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.30.Id6fc7367886ec2e2d9f2ddf423ad32720e14e5e5@changeid> Call the new core function to write the SSDT. This is made up of fragments generated by devices that have the fill_ssdt() method. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- Changes in v3: Drop coreboot_acpi_ids enum Changes in v1: - Use OEM_TABLE_ID instead of ACPI_TABLE_CREATOR - Update ACPI_DSTATUS enum - Drop writing of coreboot tables arch/x86/lib/acpi_table.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index e96acf08d4..4658d88351 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -354,6 +355,25 @@ static void acpi_create_spcr(struct acpi_spcr *spcr) header->checksum = table_compute_checksum((void *)spcr, header->length); } +void acpi_create_ssdt(struct acpi_ctx *ctx, struct acpi_table_header *ssdt, + const char *oem_table_id) +{ + memset((void *)ssdt, '\0', sizeof(struct acpi_table_header)); + + acpi_fill_header(ssdt, "SSDT"); + ssdt->revision = acpi_get_table_revision(ACPITAB_SSDT); + ssdt->aslc_revision = 1; + ssdt->length = sizeof(struct acpi_table_header); + + acpi_inc(ctx, sizeof(struct acpi_table_header)); + + acpi_fill_ssdt(ctx); + + /* (Re)calculate length and checksum. */ + ssdt->length = ctx->current - (void *)ssdt; + ssdt->checksum = table_compute_checksum((void *)ssdt, ssdt->length); +} + /* * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c */ @@ -363,6 +383,7 @@ ulong write_acpi_tables(ulong start_addr) struct acpi_facs *facs; struct acpi_table_header *dsdt; struct acpi_fadt *fadt; + struct acpi_table_header *ssdt; struct acpi_mcfg *mcfg; struct acpi_madt *madt; struct acpi_csrt *csrt; @@ -418,6 +439,14 @@ ulong write_acpi_tables(ulong start_addr) acpi_create_fadt(fadt, facs, dsdt); acpi_add_table(ctx, fadt); + debug("ACPI: * SSDT\n"); + ssdt = (struct acpi_table_header *)ctx->current; + acpi_create_ssdt(ctx, ssdt, OEM_TABLE_ID); + if (ssdt->length > sizeof(struct acpi_table_header)) { + acpi_inc_align(ctx, ssdt->length); + acpi_add_table(ctx, ssdt); + } + debug("ACPI: * MCFG\n"); mcfg = ctx->current; acpi_create_mcfg(mcfg); From patchwork Sun Jun 14 02:55:19 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242265 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:19 -0600 Subject: [PATCH v3 31/35] acpi: Add support for DSDT generation In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200614025523.40183-16-sjg@chromium.org> Some devices need to inject extra code into the Differentiated System Descriptor Table (DSDT). Add a method to handle this. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- Changes in v3: - Fix 'THe' typo - Rename build_type() to sort_acpi_item_type() Changes in v1: - Generalise the ACPI function recursion with acpi_recurse_method() arch/sandbox/dts/test.dts | 2 ++ drivers/core/acpi.c | 25 +++++++++++++++++++++- include/dm/acpi.h | 23 ++++++++++++++++++++ test/dm/acpi.c | 44 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 1 deletion(-) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 993082763d..caf935cfdf 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -257,6 +257,7 @@ acpi_test1: acpi-test { compatible = "denx,u-boot-acpi-test"; acpi-ssdt-test-data = "ab"; + acpi-dsdt-test-data = "hi"; child { compatible = "denx,u-boot-acpi-test"; }; @@ -265,6 +266,7 @@ acpi_test2: acpi-test2 { compatible = "denx,u-boot-acpi-test"; acpi-ssdt-test-data = "cd"; + acpi-dsdt-test-data = "jk"; }; clocks { diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index 4719a5c4e4..c1fc364550 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -22,12 +22,14 @@ enum gen_type_t { TYPE_NONE, TYPE_SSDT, + TYPE_DSDT, }; /* Type of method to call */ enum method_t { METHOD_WRITE_TABLES, METHOD_FILL_SSDT, + METHOD_INJECT_DSDT, }; /* Prototype for all methods */ @@ -144,7 +146,9 @@ static int sort_acpi_item_type(struct acpi_ctx *ctx, void *start, void *end = ctx->current; ptr = start; - order = ofnode_read_chosen_prop("u-boot,acpi-ssdt-order", &size); + order = ofnode_read_chosen_prop(type == TYPE_DSDT ? + "u-boot,acpi-dsdt-order" : + "u-boot,acpi-ssdt-order", &size); if (!order) { log_warning("Failed to find ordering, leaving as is\n"); return 0; @@ -189,6 +193,8 @@ acpi_method acpi_get_method(struct udevice *dev, enum method_t method) return aops->write_tables; case METHOD_FILL_SSDT: return aops->fill_ssdt; + case METHOD_INJECT_DSDT: + return aops->inject_dsdt; } } @@ -247,6 +253,23 @@ int acpi_fill_ssdt(struct acpi_ctx *ctx) return ret; } +int acpi_inject_dsdt(struct acpi_ctx *ctx) +{ + void *start = ctx->current; + int ret; + + log_debug("Writing DSDT tables\n"); + item_count = 0; + ret = acpi_recurse_method(ctx, dm_root(), METHOD_INJECT_DSDT, + TYPE_DSDT); + log_debug("Writing DSDT finished, err=%d\n", ret); + ret = sort_acpi_item_type(ctx, start, TYPE_DSDT); + if (ret) + return log_msg_ret("build", ret); + + return ret; +} + int acpi_write_dev_tables(struct acpi_ctx *ctx) { int ret; diff --git a/include/dm/acpi.h b/include/dm/acpi.h index eda23f97a6..18bce2baf6 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -87,6 +87,19 @@ struct acpi_ops { * @return 0 if OK, -ve on error */ int (*fill_ssdt)(const struct udevice *dev, struct acpi_ctx *ctx); + + /** + * inject_dsdt() - Generate DSDT code for a device + * + * This is called to create the DSDT code. The method should write out + * whatever ACPI code is needed by this device. It will end up in the + * DSDT table. + * + * @dev: Device to write + * @ctx: ACPI context to use + * @return 0 if OK, -ve on error + */ + int (*inject_dsdt)(const struct udevice *dev, struct acpi_ctx *ctx); }; #define device_get_acpi_ops(dev) ((dev)->driver->acpi_ops) @@ -141,6 +154,16 @@ int acpi_write_dev_tables(struct acpi_ctx *ctx); */ int acpi_fill_ssdt(struct acpi_ctx *ctx); +/** + * acpi_inject_dsdt() - Generate ACPI tables for DSDT + * + * This is called to create the DSDT code for all devices. + * + * @ctx: ACPI context to use + * @return 0 if OK, -ve on error + */ +int acpi_inject_dsdt(struct acpi_ctx *ctx); + #endif /* __ACPI__ */ #endif diff --git a/test/dm/acpi.c b/test/dm/acpi.c index 4e1d401e0d..1abde65c8c 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -81,10 +81,24 @@ static int testacpi_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx) return 0; } +static int testacpi_inject_dsdt(const struct udevice *dev, struct acpi_ctx *ctx) +{ + const char *data; + + data = dev_read_string(dev, "acpi-dsdt-test-data"); + if (data) { + while (*data) + acpigen_emit_byte(ctx, *data++); + } + + return 0; +} + struct acpi_ops testacpi_ops = { .get_name = testacpi_get_name, .write_tables = testacpi_write_tables, .fill_ssdt = testacpi_fill_ssdt, + .inject_dsdt = testacpi_inject_dsdt, }; static const struct udevice_id testacpi_ids[] = { @@ -441,3 +455,33 @@ static int dm_test_acpi_fill_ssdt(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_fill_ssdt, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test acpi_inject_dsdt() */ +static int dm_test_acpi_inject_dsdt(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + u8 *buf; + + buf = malloc(BUF_SIZE); + ut_assertnonnull(buf); + + ctx.current = buf; + buf[4] = 'z'; /* sentinel */ + ut_assertok(acpi_inject_dsdt(&ctx)); + + /* + * These values come from acpi-test's acpi-dsdt-test-data property. + * There is no u-boot,acpi-dsdt-order so device-tree order is used. + */ + ut_asserteq('h', buf[0]); + ut_asserteq('i', buf[1]); + + /* These values come from acpi-test's acpi-dsdt-test-data property */ + ut_asserteq('j', buf[2]); + ut_asserteq('k', buf[3]); + + ut_asserteq('z', buf[4]); + + return 0; +} +DM_TEST(dm_test_acpi_inject_dsdt, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Sun Jun 14 02:55:20 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242266 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:20 -0600 Subject: [PATCH v3 32/35] x86: Allow devices to write to DSDT In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.32.I8b14735c2286701cc6be7d36b85bbad8ca58babd@changeid> Call the new core function to inject ASL programmatically into the DSDT. This is made up of fragments generated by devices that have the inject_dsdt() method. The normal, compiled ASL file is added after this. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- Changes in v3: - Refactor the code to remove the extra memcpy() - Recalculate the DSDT checksum only once - Add a comment as to why the checksum byte is set to 0 arch/x86/lib/acpi_table.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 4658d88351..eeacfe9b06 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -406,11 +406,20 @@ ulong write_acpi_tables(ulong start_addr) debug("ACPI: * DSDT\n"); dsdt = ctx->current; + + /* Put the table header first */ memcpy(dsdt, &AmlCode, sizeof(struct acpi_table_header)); acpi_inc(ctx, sizeof(struct acpi_table_header)); + + /* If the table is not empty, allow devices to inject things */ + if (dsdt->length >= sizeof(struct acpi_table_header)) + acpi_inject_dsdt(ctx); + + /* Copy in the AML code itself if any (after the header) */ memcpy(ctx->current, (char *)&AmlCode + sizeof(struct acpi_table_header), dsdt->length - sizeof(struct acpi_table_header)); + acpi_inc_align(ctx, dsdt->length - sizeof(struct acpi_table_header)); /* Pack GNVS into the ACPI table area */ @@ -425,7 +434,12 @@ ulong write_acpi_tables(ulong start_addr) } } - /* Update DSDT checksum since we patched the GNVS address */ + /* + * Recalculate the length and update the DSDT checksum since we patched + * the GNVS address. Set the checksum to zero since it is part of the + * region being checksummed. + */ + dsdt->length = ctx->current - (void *)dsdt; dsdt->checksum = 0; dsdt->checksum = table_compute_checksum((void *)dsdt, dsdt->length); From patchwork Sun Jun 14 02:55:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242267 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:21 -0600 Subject: [PATCH v3 33/35] pci: Avoid a crash in device_is_on_pci_bus() In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200614025523.40183-17-sjg@chromium.org> This function cannot currently be called on the root node. Add a check for this as well as a test. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- (no changes since v1) include/dm/device.h | 2 +- test/dm/pci.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/include/dm/device.h b/include/dm/device.h index 975eec5d0e..6a41ee5500 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -742,7 +742,7 @@ int dev_enable_by_path(const char *path); */ static inline bool device_is_on_pci_bus(const struct udevice *dev) { - return device_get_uclass_id(dev->parent) == UCLASS_PCI; + return dev->parent && device_get_uclass_id(dev->parent) == UCLASS_PCI; } /** diff --git a/test/dm/pci.c b/test/dm/pci.c index fb93e4c78a..39e82b3699 100644 --- a/test/dm/pci.c +++ b/test/dm/pci.c @@ -339,3 +339,17 @@ static int dm_test_pci_addr_live(struct unit_test_state *uts) } DM_TEST(dm_test_pci_addr_live, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT | DM_TESTF_LIVE_TREE); + +/* Test device_is_on_pci_bus() */ +static int dm_test_pci_on_bus(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &dev)); + ut_asserteq(true, device_is_on_pci_bus(dev)); + ut_asserteq(false, device_is_on_pci_bus(dev_get_parent(dev))); + ut_asserteq(true, device_is_on_pci_bus(dev)); + + return 0; +} +DM_TEST(dm_test_pci_on_bus, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Sun Jun 14 02:55:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242269 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:22 -0600 Subject: [PATCH v3 34/35] dm: acpi: Enhance acpi_get_name() In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.34.I1a77e678d7c6973796cc651ac6d858e1122bb7ae@changeid> For many device types it is possible to figure out the name just by looking at its uclass or parent. Add a function to handle this, since it allows us to cover the vast majority of cases automatically. However it is sometimes impossible to figure out an ACPI name for a device just by looking at its uclass. For example a touch device may have a vendor-specific name. Add a new "acpi,name" property to allow a custom name to be created. With this new feature we can drop the get_name() methods in the sandbox I2C and SPI drivers. They were only added for testing purposes. Update the tests to use the new values. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- Changes in v3: - Fix 'of' typo Changes in v1: - Use acpi,ddn instead of acpi,desc - Rename to acpi_device_infer_name() - Update newly created sandbox tests arch/sandbox/dts/test.dts | 1 + doc/device-tree-bindings/device.txt | 13 ++++ drivers/core/acpi.c | 13 +++- drivers/i2c/sandbox_i2c.c | 10 --- drivers/spi/sandbox_spi.c | 10 --- include/acpi/acpi_device.h | 15 ++++ lib/acpi/acpi_device.c | 106 ++++++++++++++++++++++++++++ test/dm/acpi.c | 42 ++++++++++- test/dm/acpigen.c | 4 +- 9 files changed, 189 insertions(+), 25 deletions(-) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index caf935cfdf..5d4d3daa28 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -113,6 +113,7 @@ int-array = <5678 9123 4567>; str-value = "test string"; interrupts-extended = <&irq 3 0>; + acpi,name = "GHIJ"; }; junk { diff --git a/doc/device-tree-bindings/device.txt b/doc/device-tree-bindings/device.txt index 27bd3978d9..7140339623 100644 --- a/doc/device-tree-bindings/device.txt +++ b/doc/device-tree-bindings/device.txt @@ -17,6 +17,8 @@ the acpi,compatible property. System) Device Name) - acpi,hid : Contains the string to use as the HID (Hardware ID) identifier _HID + - acpi,name : Provides the ACPI name for a device, which is a string consisting + of four alphanumeric character (upper case) - acpi,uid : _UID value for device - linux,probed : Tells U-Boot to add 'linux,probed' to the ACPI tables so that Linux will only load the driver if the device can be detected (e.g. on I2C @@ -34,3 +36,14 @@ elan_touchscreen: elan-touchscreen at 10 { interrupts-extended = <&acpi_gpe GPIO_21_IRQ IRQ_TYPE_EDGE_FALLING>; linux,probed; }; + +pcie-a0 at 14,0 { + reg = <0x0000a000 0 0 0 0>; + acpi,name = "RP01"; + wifi: wifi { + compatible = "intel,generic-wifi"; + acpi,ddn = "Intel WiFi"; + acpi,name = "WF00"; + interrupts-extended = <&acpi_gpe 0x3c 0>; + }; +}; diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index c1fc364550..acc2e3f7e9 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -9,9 +9,10 @@ #define LOG_CATEOGRY LOGC_ACPI #include -#include #include #include +#include +#include #include #include #include @@ -65,12 +66,20 @@ int acpi_copy_name(char *out_name, const char *name) int acpi_get_name(const struct udevice *dev, char *out_name) { struct acpi_ops *aops; + const char *name; + int ret; aops = device_get_acpi_ops(dev); if (aops && aops->get_name) return aops->get_name(dev, out_name); + name = dev_read_string(dev, "acpi,name"); + if (name) + return acpi_copy_name(out_name, name); + ret = acpi_device_infer_name(dev, out_name); + if (ret) + return log_msg_ret("dev", ret); - return -ENOSYS; + return 0; } /** diff --git a/drivers/i2c/sandbox_i2c.c b/drivers/i2c/sandbox_i2c.c index 125026da90..57b1c60fde 100644 --- a/drivers/i2c/sandbox_i2c.c +++ b/drivers/i2c/sandbox_i2c.c @@ -84,15 +84,6 @@ static int sandbox_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, return ops->xfer(emul, msg, nmsgs); } -static int sandbox_i2c_get_name(const struct udevice *dev, char *out_name) -{ - return acpi_copy_name(out_name, "SI2C"); -} - -struct acpi_ops sandbox_i2c_acpi_ops = { - .get_name = sandbox_i2c_get_name, -}; - static const struct dm_i2c_ops sandbox_i2c_ops = { .xfer = sandbox_i2c_xfer, }; @@ -108,5 +99,4 @@ U_BOOT_DRIVER(i2c_sandbox) = { .of_match = sandbox_i2c_ids, .ops = &sandbox_i2c_ops, .priv_auto_alloc_size = sizeof(struct sandbox_i2c_priv), - ACPI_OPS_PTR(&sandbox_i2c_acpi_ops) }; diff --git a/drivers/spi/sandbox_spi.c b/drivers/spi/sandbox_spi.c index 4264acc953..5aeb6bdf43 100644 --- a/drivers/spi/sandbox_spi.c +++ b/drivers/spi/sandbox_spi.c @@ -134,15 +134,6 @@ static int sandbox_spi_get_mmap(struct udevice *dev, ulong *map_basep, return 0; } -static int sandbox_spi_get_name(const struct udevice *dev, char *out_name) -{ - return acpi_copy_name(out_name, "SSPI"); -} - -struct acpi_ops sandbox_spi_acpi_ops = { - .get_name = sandbox_spi_get_name, -}; - static const struct dm_spi_ops sandbox_spi_ops = { .xfer = sandbox_spi_xfer, .set_speed = sandbox_spi_set_speed, @@ -161,5 +152,4 @@ U_BOOT_DRIVER(spi_sandbox) = { .id = UCLASS_SPI, .of_match = sandbox_spi_ids, .ops = &sandbox_spi_ops, - ACPI_OPS_PTR(&sandbox_spi_acpi_ops) }; diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index 5019a79528..d076b452b5 100644 --- a/include/acpi/acpi_device.h +++ b/include/acpi/acpi_device.h @@ -383,4 +383,19 @@ int acpi_device_add_power_res(struct acpi_ctx *ctx, u32 tx_state_val, const struct gpio_desc *stop_gpio, uint stop_delay_ms, uint stop_off_delay_ms); +/** + * acpi_device_infer_name() - Infer the name from its uclass or parent + * + * Many ACPI devices have a standard name that can be inferred from the uclass + * they are in, or the uclass of their parent. These rules are implemented in + * this function. It attempts to produce a name for a device based on these + * rules. + * + * @dev: Device to check + * @out_name: Place to put the name (must hold ACPI_NAME_MAX bytes) + * @return 0 if a name was found, -ENOENT if not found, -ENXIO if the device + * sequence number could not be determined + */ +int acpi_device_infer_name(const struct udevice *dev, char *out_name); + #endif diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c index 912369498a..c6560e37bc 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include #include @@ -711,3 +713,107 @@ int acpi_device_write_spi_dev(struct acpi_ctx *ctx, const struct udevice *dev) return 0; } #endif /* CONFIG_SPI */ + +static const char *acpi_name_from_id(enum uclass_id id) +{ + switch (id) { + case UCLASS_USB_HUB: + /* Root Hub */ + return "RHUB"; + /* DSDT: acpi/northbridge.asl */ + case UCLASS_NORTHBRIDGE: + return "MCHC"; + /* DSDT: acpi/lpc.asl */ + case UCLASS_LPC: + return "LPCB"; + /* DSDT: acpi/xhci.asl */ + case UCLASS_USB: + return "XHCI"; + case UCLASS_PWM: + return "PWM"; + default: + return NULL; + } +} + +static int acpi_check_seq(const struct udevice *dev) +{ + if (dev->req_seq == -1) { + log_warning("Device '%s' has no seq\n", dev->name); + return log_msg_ret("no seq", -ENXIO); + } + + return dev->req_seq; +} + +/* If you change this function, add test cases to dm_test_acpi_get_name() */ +int acpi_device_infer_name(const struct udevice *dev, char *out_name) +{ + enum uclass_id parent_id = UCLASS_INVALID; + enum uclass_id id; + const char *name = NULL; + + id = device_get_uclass_id(dev); + if (dev_get_parent(dev)) + parent_id = device_get_uclass_id(dev_get_parent(dev)); + + if (id == UCLASS_SOUND) + name = "HDAS"; + else if (id == UCLASS_PCI) + name = "PCI0"; + else if (device_is_on_pci_bus(dev)) + name = acpi_name_from_id(id); + if (!name) { + switch (parent_id) { + case UCLASS_USB: { + struct usb_device *udev = dev_get_parent_priv(dev); + + sprintf(out_name, udev->speed >= USB_SPEED_SUPER ? + "HS%02d" : "FS%02d", + udev->portnr); + name = out_name; + break; + } + default: + break; + } + } + if (!name) { + int num; + + switch (id) { + /* DSDT: acpi/lpss.asl */ + case UCLASS_SERIAL: + num = acpi_check_seq(dev); + if (num < 0) + return num; + sprintf(out_name, "URT%d", num); + name = out_name; + break; + case UCLASS_I2C: + num = acpi_check_seq(dev); + if (num < 0) + return num; + sprintf(out_name, "I2C%d", num); + name = out_name; + break; + case UCLASS_SPI: + num = acpi_check_seq(dev); + if (num < 0) + return num; + sprintf(out_name, "SPI%d", num); + name = out_name; + break; + default: + break; + } + } + if (!name) { + log_warning("No name for device '%s'\n", dev->name); + return -ENOENT; + } + if (name != out_name) + acpi_copy_name(out_name, name); + + return 0; +} diff --git a/test/dm/acpi.c b/test/dm/acpi.c index 1abde65c8c..69ca0902aa 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -124,12 +124,52 @@ UCLASS_DRIVER(testacpi) = { static int dm_test_acpi_get_name(struct unit_test_state *uts) { char name[ACPI_NAME_MAX]; - struct udevice *dev; + struct udevice *dev, *dev2, *i2c, *spi, *serial, *timer, *sound; + struct udevice *pci, *root; + /* Test getting the name from the driver */ ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev)); ut_assertok(acpi_get_name(dev, name)); ut_asserteq_str(ACPI_TEST_DEV_NAME, name); + /* Test getting the name from the device tree */ + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test", + &dev2)); + ut_assertok(acpi_get_name(dev2, name)); + ut_asserteq_str("GHIJ", name); + + /* Test getting the name from acpi_device_get_name() */ + ut_assertok(uclass_first_device(UCLASS_I2C, &i2c)); + ut_assertok(acpi_get_name(i2c, name)); + ut_asserteq_str("I2C0", name); + + ut_assertok(uclass_first_device(UCLASS_SPI, &spi)); + ut_assertok(acpi_get_name(spi, name)); + ut_asserteq_str("SPI0", name); + + /* The uart has no sequence number, so this should fail */ + ut_assertok(uclass_first_device(UCLASS_SERIAL, &serial)); + ut_asserteq(-ENXIO, acpi_get_name(serial, name)); + + /* ACPI doesn't know about the timer */ + ut_assertok(uclass_first_device(UCLASS_TIMER, &timer)); + ut_asserteq(-ENOENT, acpi_get_name(timer, name)); + + /* May as well test the rest of the cases */ + ut_assertok(uclass_first_device(UCLASS_SOUND, &sound)); + ut_assertok(acpi_get_name(sound, name)); + ut_asserteq_str("HDAS", name); + + ut_assertok(uclass_first_device(UCLASS_PCI, &pci)); + ut_assertok(acpi_get_name(pci, name)); + ut_asserteq_str("PCI0", name); + + ut_assertok(uclass_first_device(UCLASS_ROOT, &root)); + ut_assertok(acpi_get_name(root, name)); + ut_asserteq_str("\\_SB", name); + + /* Note that we don't have tests for acpi_name_from_id() */ + return 0; } DM_TEST(dm_test_acpi_get_name, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 3999825953..ea2033a27c 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -307,7 +307,7 @@ static int dm_test_acpi_i2c(struct unit_test_state *uts) ut_asserteq(6, get_unaligned((u16 *)(ptr + 10))); ut_asserteq(100000, get_unaligned((u32 *)(ptr + 12))); ut_asserteq(0x43, get_unaligned((u16 *)(ptr + 16))); - ut_asserteq_str("\\_SB.SI2C", (char *)ptr + 18); + ut_asserteq_str("\\_SB.I2C0", (char *)ptr + 18); free_context(&ctx); @@ -343,7 +343,7 @@ static int dm_test_acpi_spi(struct unit_test_state *uts) ut_asserteq(0, ptr[17]); ut_asserteq(0, ptr[18]); ut_asserteq(0, get_unaligned((u16 *)(ptr + 19))); - ut_asserteq_str("\\_SB.SSPI", (char *)ptr + 21); + ut_asserteq_str("\\_SB.SPI0", (char *)ptr + 21); free_context(&ctx); From patchwork Sun Jun 14 02:55:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 242268 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sat, 13 Jun 2020 20:55:23 -0600 Subject: [PATCH v3 35/35] acpi: Add an acpi command to list/dump generated ACPI items In-Reply-To: <20200614025523.40183-1-sjg@chromium.org> References: <20200614025523.40183-1-sjg@chromium.org> Message-ID: <20200613205459.v3.35.I5894252b8fe4522e409528c497d882226f34ab9b@changeid> Add a command that shows the individual blocks of data generated by each device, effectively splitting the full table into its component parts. This can be helpful for debugging. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- Changes in v3: - Update acpi_dump_items() to take an enum - Update commit message and subject for clarity cmd/acpi.c | 15 +++++++++++++-- drivers/core/acpi.c | 16 ++++++++++++++++ include/dm/acpi.h | 16 ++++++++++++++++ test/dm/acpi.c | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 2 deletions(-) diff --git a/cmd/acpi.c b/cmd/acpi.c index e9a9161a91..085a3a650d 100644 --- a/cmd/acpi.c +++ b/cmd/acpi.c @@ -153,6 +153,17 @@ static int do_acpi_list(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_acpi_items(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + bool dump_contents; + + dump_contents = argc >= 2 && !strcmp("-d", argv[1]); + acpi_dump_items(dump_contents ? ACPI_DUMP_CONTENTS : ACPI_DUMP_LIST); + + return 0; +} + static int do_acpi_dump(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -160,8 +171,6 @@ static int do_acpi_dump(struct cmd_tbl *cmdtp, int flag, int argc, char sig[ACPI_NAME_LEN]; int ret; - if (argc < 2) - return CMD_RET_USAGE; name = argv[1]; if (strlen(name) != ACPI_NAME_LEN) { printf("Table name '%s' must be four characters\n", name); @@ -179,8 +188,10 @@ static int do_acpi_dump(struct cmd_tbl *cmdtp, int flag, int argc, static char acpi_help_text[] = "list - list ACPI tables\n" + "acpi items [-d] - List/dump each piece of ACPI data from devices\n" "acpi dump - Dump ACPI table"; U_BOOT_CMD_WITH_SUBCMDS(acpi, "ACPI tables", acpi_help_text, U_BOOT_SUBCMD_MKENT(list, 1, 1, do_acpi_list), + U_BOOT_SUBCMD_MKENT(items, 2, 1, do_acpi_items), U_BOOT_SUBCMD_MKENT(dump, 2, 1, do_acpi_dump)); diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index acc2e3f7e9..fd58c76087 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -119,6 +119,22 @@ static int acpi_add_item(struct acpi_ctx *ctx, struct udevice *dev, return 0; } +void acpi_dump_items(enum acpi_dump_option option) +{ + int i; + + for (i = 0; i < item_count; i++) { + struct acpi_item *item = &acpi_item[i]; + + printf("dev '%s', type %d, size %x\n", item->dev->name, + item->type, item->size); + if (option == ACPI_DUMP_CONTENTS) { + print_buffer(0, item->buf, 1, item->size, 0); + printf("\n"); + } + } +} + static struct acpi_item *find_acpi_item(const char *devname) { int i; diff --git a/include/dm/acpi.h b/include/dm/acpi.h index 18bce2baf6..28d9275ccc 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -27,6 +27,12 @@ #if !defined(__ACPI__) +/** enum acpi_dump_option - selects what ACPI information to dump */ +enum acpi_dump_option { + ACPI_DUMP_LIST, /* Just the list of items */ + ACPI_DUMP_CONTENTS, /* Include the binary contents also */ +}; + /** * struct acpi_ctx - Context used for writing ACPI tables * @@ -164,6 +170,16 @@ int acpi_fill_ssdt(struct acpi_ctx *ctx); */ int acpi_inject_dsdt(struct acpi_ctx *ctx); +/** + * acpi_dump_items() - Dump out the collected ACPI items + * + * This lists the ACPI DSDT and SSDT items generated by the various U-Boot + * drivers. + * + * @option: Sets what should be dumpyed + */ +void acpi_dump_items(enum acpi_dump_option option); + #endif /* __ACPI__ */ #endif diff --git a/test/dm/acpi.c b/test/dm/acpi.c index 69ca0902aa..7768f9514c 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -525,3 +525,42 @@ static int dm_test_acpi_inject_dsdt(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_inject_dsdt, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test 'acpi items' command */ +static int dm_test_acpi_cmd_items(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + void *buf; + + buf = malloc(BUF_SIZE); + ut_assertnonnull(buf); + + ctx.current = buf; + ut_assertok(acpi_fill_ssdt(&ctx)); + console_record_reset(); + run_command("acpi items", 0); + ut_assert_nextline("dev 'acpi-test', type 1, size 2"); + ut_assert_nextline("dev 'acpi-test2', type 1, size 2"); + ut_assert_console_end(); + + ctx.current = buf; + ut_assertok(acpi_inject_dsdt(&ctx)); + console_record_reset(); + run_command("acpi items", 0); + ut_assert_nextline("dev 'acpi-test', type 2, size 2"); + ut_assert_nextline("dev 'acpi-test2', type 2, size 2"); + ut_assert_console_end(); + + console_record_reset(); + run_command("acpi items -d", 0); + ut_assert_nextline("dev 'acpi-test', type 2, size 2"); + ut_assert_nextlines_are_dump(2); + ut_assert_nextline("%s", ""); + ut_assert_nextline("dev 'acpi-test2', type 2, size 2"); + ut_assert_nextlines_are_dump(2); + ut_assert_nextline("%s", ""); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_acpi_cmd_items, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);