From e1ddf67cb3982b5c49c1165da87917a82a285783 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 9 Jul 2020 18:43:14 -0600 Subject: timer: Allow delays with a 32-bit microsecond timer The current get_timer_us() uses 64-bit arithmetic on 32-bit machines. When implementing microsecond-level timeouts, 32-bits is plenty. Add a new function that uses an unsigned long. On 64-bit machines this is still 64-bit, but this doesn't introduce a penalty. On 32-bit machines it is more efficient. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- include/time.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/time.h b/include/time.h index e99f9c80127..3f00e687135 100644 --- a/include/time.h +++ b/include/time.h @@ -17,6 +17,17 @@ unsigned long get_timer(unsigned long base); unsigned long timer_get_us(void); uint64_t get_timer_us(uint64_t base); +/** + * get_timer_us_long() - Get the number of elapsed microseconds + * + * This uses 32-bit arithmetic on 32-bit machines, which is enough to handle + * delays of over an hour. For 64-bit machines it uses a 64-bit value. + * + *@base: Base time to consider + *@return elapsed time since @base + */ +unsigned long get_timer_us_long(unsigned long base); + /* * timer_test_add_offset() * -- cgit v1.3.1 From 1361a53c1ae1f0534825e12ed41fb44aefd2c224 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:39 -0600 Subject: acpi: Add a function to get a device path and scope Add a function to build up the ACPI path for a device and another for its scope. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- 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 (limited to 'include') diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 07535a6b7d3..0d8ffd179a8 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 00000000000..37a675f101d --- /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 660491ef711..caae6c01bd9 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 00000000000..3a9424e7ee5 --- /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 4c46dd83a69..5b8459311cb 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); -- cgit v1.3.1 From 2715b3623c08bf1ad2dfe6076ad86c24e3138016 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:40 -0600 Subject: acpi: Add a way to check device status 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 --- include/acpi/acpi_device.h | 28 ++++++++++++++++++++++++++++ lib/acpi/acpi_device.c | 5 +++++ test/dm/acpi.c | 12 ++++++++++++ 3 files changed, 45 insertions(+) (limited to 'include') diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index 37a675f101d..09c227489af 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 3a9424e7ee5..60f4fd8cd52 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 5b8459311cb..07b0daaab0d 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); -- cgit v1.3.1 From f4955137f5f15e615376cf38559414a9b53e3d55 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:41 -0600 Subject: irq: Add a method to convert an interrupt to ACPI 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 --- 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 | 23 ++++++++++++++++++ 5 files changed, 157 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/misc/irq-uclass.c b/drivers/misc/irq-uclass.c index ec70866cc3e..8727a33dd91 100644 --- a/drivers/misc/irq-uclass.c +++ b/drivers/misc/irq-uclass.c @@ -152,8 +152,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; @@ -175,6 +173,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 54bc47c8d8a..a2511b32fcd 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 09c227489af..24895de0dad 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 b71afe9bee9..8527e4dd797 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 192d80d7e10..51bae31b0f8 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,25 @@ 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)); + + /* see sandbox_get_acpi() */ + 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); -- cgit v1.3.1 From 61cc93396a54c1c3fcace092c83def70f3843c2a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:42 -0600 Subject: acpi: Support generation of ACPI code 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 Reviewed-by: Bin Meng --- include/acpi/acpigen.h | 49 ++++++++++++++++++++++++++++++++++ include/dm/acpi.h | 2 ++ lib/acpi/Makefile | 1 + lib/acpi/acpi_table.c | 1 + lib/acpi/acpigen.c | 39 +++++++++++++++++++++++++++ test/dm/Makefile | 1 + test/dm/acpigen.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 165 insertions(+) create mode 100644 include/acpi/acpigen.h create mode 100644 lib/acpi/acpigen.c create mode 100644 test/dm/acpigen.c (limited to 'include') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h new file mode 100644 index 00000000000..8809cdb4e12 --- /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 7563a4c60a7..696b1a96a09 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 caae6c01bd9..85a1f774ad4 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 431776666e2..acc55e7fad6 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -237,6 +237,7 @@ static void acpi_write_xsdt(struct acpi_xsdt *xsdt) void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start) { + ctx->base = start; ctx->current = start; /* Align ACPI tables to 16 byte */ diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c new file mode 100644 index 00000000000..a42ae264943 --- /dev/null +++ b/lib/acpi/acpigen.c @@ -0,0 +1,39 @@ +// 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) +{ + /* Output the value in little-endian format */ + 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 0d1c66fa1e6..5641070ea11 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 00000000000..9ff9b685325 --- /dev/null +++ b/test/dm/acpigen.c @@ -0,0 +1,72 @@ +// 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 + +/* Maximum size of the ACPI context needed for most tests */ +#define ACPI_CONTEXT_SIZE 150 + +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(ACPI_CONTEXT_SIZE); + 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); -- cgit v1.3.1 From ff715c6f4f7a3181fcc6a45907bb8bf0c8c6f08f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:43 -0600 Subject: acpi: Support generation of interrupt descriptor Add a function to write an interrupt descriptor to the generated ACPI code. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- include/acpi/acpi_device.h | 15 ++++++ lib/acpi/acpi_device.c | 119 +++++++++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 32 ++++++++++++ 3 files changed, 166 insertions(+) (limited to 'include') diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index 24895de0dad..e72fede54ce 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 IRQ pin number 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 60f4fd8cd52..d854a45cbc6 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,120 @@ enum acpi_dev_status acpi_device_status(const struct udevice *dev) { return ACPI_DSTATUS_ALL_ON; } + +/** + * largeres_write_len_f() - Write a placeholder word value + * + * Write a forward length for a large resource (2 bytes) + * + * @return pointer to the zero word (for fixing up later) + */ +static void *largeres_write_len_f(struct acpi_ctx *ctx) +{ + u8 *p = acpigen_get_current(ctx); + + acpigen_emit_word(ctx, 0); + + return p; +} + +/** + * largeres_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 largeres_write_len_f(). + * + * @ptr: Word to update + * @start: Start address to count from to calculated the length + */ +static void largeres_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; +} + +/** + * largeres_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_largeres_fill_len() passing @ptr + 2 as @start + * + * @ptr: Word to update. + */ +static void largeres_fill_len(struct acpi_ctx *ctx, void *ptr) +{ + largeres_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 = largeres_write_len_f(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) */ + largeres_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 9ff9b685325..a4adfbfdf83 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -8,8 +8,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -70,3 +72,33 @@ 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)); + + /* See a-test, property interrupts-extended in the device tree */ + ut_asserteq(3, 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); -- cgit v1.3.1 From 2912686c08c33aff5269512de962dffb35fbee7c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:44 -0600 Subject: gpio: Add a method to convert a GPIO to ACPI 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 Reviewed-by: Bin Meng --- drivers/gpio/gpio-uclass.c | 22 ++++++++++++ drivers/gpio/sandbox.c | 77 +++++++++++++++++++++++++++++++++++++++ include/acpi/acpi_device.h | 90 ++++++++++++++++++++++++++++++++++++++++++++++ include/asm-generic/gpio.h | 27 ++++++++++++++ test/dm/gpio.c | 62 ++++++++++++++++++++++++++++++++ 5 files changed, 278 insertions(+) (limited to 'include') diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index ab17fa8a5d8..9c53299b6a3 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -855,6 +856,27 @@ 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; + + memset(gpio, '\0', sizeof(*gpio)); + 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 b9a1d65acc4..c2f80472b83 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,63 @@ 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; + + /* Note that gpio_get_acpi() zeroes *gpio before calling here */ + gpio->pin_count = 1; + gpio->pins[0] = desc->offset; + ret = acpi_device_scope(desc->dev, gpio->resource, + sizeof(gpio->resource)); + if (ret) + return log_ret(ret); + + /* All of these values are just used for testing */ + if (desc->flags & GPIOD_ACTIVE_LOW) { + gpio->pin0_addr = 0x80012 + desc->offset; + gpio->type = ACPI_GPIO_TYPE_INTERRUPT; + gpio->pull = ACPI_GPIO_PULL_DOWN; + gpio->interrupt_debounce_timeout = 4321; + + /* 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 only used for testing */ + gpio->polarity = ACPI_GPIO_ACTIVE_LOW; + } else { + gpio->pin0_addr = 0xc00dc + desc->offset; + gpio->type = ACPI_GPIO_TYPE_IO; + gpio->pull = ACPI_GPIO_PULL_UP; + gpio->interrupt_debounce_timeout = 0; + + /* The GpioInt part is not used */ + + /* 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 +265,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 +314,7 @@ U_BOOT_DRIVER(sandbox_gpio) = { .probe = gpio_sandbox_probe, .remove = gpio_sandbox_remove, .ops = &gpio_sandbox_ops, + ACPI_OPS_PTR(&gpio_sandbox_acpi_ops) }; U_BOOT_DRIVER_ALIAS(sandbox_gpio, sandbox_gpio_alias) @@ -421,6 +484,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); @@ -436,6 +506,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 */ } @@ -449,4 +525,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 e72fede54ce..69b90968a8a 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 e16c2f31d9d..a57dd2665ce 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 e3be57b602b..29701389fcd 100644 --- a/test/dm/gpio.c +++ b/test/dm/gpio.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -417,3 +418,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); -- cgit v1.3.1 From 7fb8da4ce1b22c8f077e6d4fe6e0d88e3fd9206d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:45 -0600 Subject: acpi: Support string output Add support for output of strings and streams of bytes. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- include/acpi/acpigen.h | 19 +++++++++++++++++++ lib/acpi/acpigen.c | 14 ++++++++++++++ test/dm/acpigen.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) (limited to 'include') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 8809cdb4e12..9c1faf7bf2b 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 null 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 a42ae264943..671f1d0eea8 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -37,3 +37,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'); +} diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index a4adfbfdf83..26d1b76db48 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -20,6 +20,9 @@ /* Maximum size of the ACPI context needed for most tests */ #define ACPI_CONTEXT_SIZE 150 +#define TEST_STRING "frogmore" +#define TEST_STREAM2 "\xfa\xde" + static int alloc_context(struct acpi_ctx **ctxp) { struct acpi_ctx *ctx; @@ -73,6 +76,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) { -- cgit v1.3.1 From a9e0a077df5509c3d4b49e745c746c38a4f9a7a1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:46 -0600 Subject: acpi: Support generation of GPIO descriptor Add a function to write a GPIO descriptor to the generated ACPI code. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng [bmeng: Drop comment about the type always being ACPI_GPIO_TYPE_IO] Signed-off-by: Bin Meng --- include/acpi/acpi_device.h | 22 +++++++ lib/acpi/acpi_device.c | 151 +++++++++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 91 +++++++++++++++++++++++++++ 3 files changed, 264 insertions(+) (limited to 'include') diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index 69b90968a8a..002e8356448 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 GPIO pin number of first GPIO 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. + * + * @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 d854a45cbc6..bbe1cfc57ad 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -12,6 +12,7 @@ #include #include #include +#include #include /** @@ -203,5 +204,155 @@ int acpi_device_write_interrupt_irq(struct acpi_ctx *ctx, if (ret) return log_msg_ret("write", ret); + return irq.pin; +} + +/* 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 = largeres_write_len_f(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 = largeres_write_len_f(ctx); + + /* Byte 16: Reserved */ + acpigen_emit_byte(ctx, 0); + + /* Byte 17-18: Resource Source Name Offset, relative to start */ + resource_offset = largeres_write_len_f(ctx); + + /* Byte 19-20: Vendor Data Offset, relative to start */ + vendor_data_offset = largeres_write_len_f(ctx); + + /* Byte 21-22: Vendor Data Length */ + acpigen_emit_word(ctx, 0); + + /* Fill in Pin Table Offset */ + largeres_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 */ + largeres_fill_from_len(ctx, resource_offset, start); + + /* Resource Source Name String */ + acpigen_emit_string(ctx, gpio->resource); + + /* Fill in Vendor Data Offset */ + largeres_fill_from_len(ctx, vendor_data_offset, start); + + /* Fill in GPIO Descriptor Length (account for len word) */ + largeres_fill_len(ctx, desc_length); + + return gpio->pins[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 < 0) + return log_msg_ret("gpio", ret); + return 0; } diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 26d1b76db48..d15273d6bff 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -144,3 +145,93 @@ 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)); + + /* This should write GPIO pin 4 (see device tree test.dts ) */ + ut_asserteq(4, 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)); + + /* This should write GPIO pin 6 (see device tree test.dts ) */ + ut_asserteq(6, 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); -- cgit v1.3.1 From 4ebc940b39b6a43de9d1fa74653321cd6fdb4d3a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:47 -0600 Subject: acpi: Support generation of a GPIO/irq for a device 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 --- include/acpi/acpi_device.h | 15 +++++++++++++++ lib/acpi/acpi_device.c | 31 ++++++++++++++++++++++++++++++- test/dm/acpigen.c | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index 002e8356448..67a242eb75d 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 irq or GPIO pin number 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 bbe1cfc57ad..c93af2e5837 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -354,5 +354,34 @@ int acpi_device_write_gpio_desc(struct acpi_ctx *ctx, if (ret < 0) return log_msg_ret("gpio", ret); - return 0; + return ret; +} + +int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx, + struct udevice *dev, const char *prop) +{ + struct irq req_irq; + int pin; + int ret; + + ret = irq_get_by_index(dev, 0, &req_irq); + if (!ret) { + ret = acpi_device_write_interrupt_irq(ctx, &req_irq); + if (ret < 0) + return log_msg_ret("irq", ret); + pin = 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 < 0) + return log_msg_ret("gpio", ret); + pin = ret; + } + + return pin; } diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index d15273d6bff..7d81652295e 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -16,6 +16,7 @@ #include #include #include +#include #include /* Maximum size of the ACPI context needed for most tests */ @@ -235,3 +236,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_asserteq(3, 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_asserteq(1, 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); -- cgit v1.3.1 From 31e1787ec15e4a498e940e9ba24b625ca059a3b2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:48 -0600 Subject: acpi: Support generation of I2C descriptor Add a function to write a GPIO descriptor to the generated ACPI code. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- drivers/i2c/sandbox_i2c.c | 11 +++++ drivers/rtc/sandbox_rtc.c | 13 ++++++ include/acpi/acpi_device.h | 33 ++++++++++++++ lib/acpi/acpi_device.c | 107 +++++++++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 32 ++++++++++++++ 5 files changed, 196 insertions(+) (limited to 'include') diff --git a/drivers/i2c/sandbox_i2c.c b/drivers/i2c/sandbox_i2c.c index f4ae2397a03..125026da908 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 77065e49c76..852770a49cf 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 @@ -67,6 +68,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, @@ -85,4 +97,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 67a242eb75d..7a65dadbb2f 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,26 @@ struct acpi_gpio { enum acpi_gpio_polarity polarity; }; +/* ACPI Descriptors for Serial Bus interfaces */ +#define ACPI_SERIAL_BUS_TYPE_I2C 1 +#define ACPI_I2C_SERIAL_BUS_REVISION_ID 1 /* TODO: upgrade to 2 */ +#define ACPI_I2C_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 +291,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 I2C address of device 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 c93af2e5837..c0d5a9acaed 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -385,3 +385,110 @@ int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx, return pin; } + +/* 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 = largeres_write_len_f(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 = largeres_write_len_f(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 */ + largeres_fill_len(ctx, type_length); + + /* Byte 18+: ResourceSource */ + acpigen_emit_string(ctx, i2c->resource); + + /* Fill in I2C Descriptor Length */ + largeres_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 chip address of device + */ +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 i2c->address; +} + +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 < 0) + return log_msg_ret("set", ret); + acpi_device_write_i2c(ctx, &i2c); + + return ret; +} diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 7d81652295e..c210ceb404c 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -276,3 +276,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_asserteq(0x43, 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.I2C0", (char *)ptr + 18); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_i2c, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- cgit v1.3.1 From 70e5e67a4dc7cee0c69eaf9f5cc07b201a59cb59 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:49 -0600 Subject: acpi: Support generation of SPI descriptor 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 --- drivers/spi/sandbox_spi.c | 11 ++++ include/acpi/acpi_device.h | 39 ++++++++++++++ include/spi.h | 4 +- lib/acpi/acpi_device.c | 124 +++++++++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 36 +++++++++++++ 5 files changed, 212 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/spi/sandbox_spi.c b/drivers/spi/sandbox_spi.c index 570ae285f2a..77797bf096d 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(sandbox_spi) = { .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 7a65dadbb2f..62b1295201c 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; @@ -186,8 +187,11 @@ struct acpi_gpio { /* 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 @@ -204,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 * @@ -303,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 a37900b2fd9..98ba9e796d5 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 c0d5a9acaed..b628fa13377 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -492,3 +492,127 @@ int acpi_device_write_i2c_dev(struct acpi_ctx *ctx, const struct udevice *dev) return ret; } + +#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 = largeres_write_len_f(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 = largeres_write_len_f(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 */ + largeres_fill_len(ctx, type_length); + + /* Byte 21+: ResourceSource String */ + acpigen_emit_string(ctx, spi->resource); + + /* Fill in SPI Descriptor Length */ + largeres_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 c210ceb404c..f3d9915500a 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -308,3 +308,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.SPI0", (char *)ptr + 21); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_spi, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- cgit v1.3.1 From 7e148f2ed365c89f50701ed45acd6e36138de447 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:50 -0600 Subject: acpigen: Support writing a length 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 Reviewed-by: Bin Meng --- include/acpi/acpigen.h | 41 ++++++++++++++++++++++++++++++++ include/dm/acpi.h | 9 ++++++- lib/acpi/acpigen.c | 33 ++++++++++++++++++++++++++ test/dm/acpigen.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 144 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 9c1faf7bf2b..18409b613c6 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,42 @@ 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 + * + * This implementation always uses a 3-byte packet length for simplicity. It + * could be adjusted to support other lengths. + * + * @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 has 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 696b1a96a09..dfda88e4931 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -16,12 +16,15 @@ #define ACPI_OPS_PTR(_ptr) #endif -/* Length of an ACPI name string, excluding nul terminator */ +/* Length of an ACPI name string, excluding null terminator */ #define ACPI_NAME_LEN 4 /* 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 671f1d0eea8..11fc38419af 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -38,6 +39,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 f3d9915500a..c7568470934 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -25,7 +25,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; @@ -33,17 +33,23 @@ static int alloc_context(struct acpi_ctx **ctxp) ctx = malloc(sizeof(*ctx)); if (!ctx) return -ENOMEM; - ctx->base = malloc(ACPI_CONTEXT_SIZE); + 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, ACPI_CONTEXT_SIZE); +} + static void free_context(struct acpi_ctx **ctxp) { free((*ctxp)->base); @@ -344,3 +350,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); -- cgit v1.3.1 From 03967ce2e5e4332edd1e023360707a1086f42242 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:51 -0600 Subject: acpigen: Support writing a package 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 --- include/acpi/acpigen.h | 32 ++++++++++++++++++++++++++++++++ lib/acpi/acpigen.c | 12 ++++++++++++ test/dm/acpigen.c | 27 +++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) (limited to 'include') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 18409b613c6..c1a6bc263ca 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 * @@ -106,4 +111,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 11fc38419af..9043c2bc723 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -71,6 +71,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 c7568470934..aaffc6a4cf5 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -404,3 +404,30 @@ static int dm_test_acpi_len(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_len, 0); + +/* Test writing 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); -- cgit v1.3.1 From 83b2bd5a74a4a07a738ba039afc3028d8df2f97d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:52 -0600 Subject: acpi: Support writing an integer 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 --- include/acpi/acpigen.h | 17 +++++++++++++++++ lib/acpi/acpigen.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 46 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) (limited to 'include') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index c1a6bc263ca..9e52ec589f5 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, }; @@ -138,4 +144,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 9043c2bc723..27d4eab7a60 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -83,6 +83,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 aaffc6a4cf5..187001d2fcb 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -25,6 +25,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; @@ -431,3 +436,44 @@ static int dm_test_acpi_package(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_package, 0); + +/* Test writing 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); -- cgit v1.3.1 From 3df33bda5c92a1a8b46b83ac9521fcf48a2fe50f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:53 -0600 Subject: acpi: Support writing a string ACPI supports storing a simple null-terminated string. Add support for this. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- include/acpi/acpigen.h | 10 ++++++++++ lib/acpi/acpigen.c | 6 ++++++ test/dm/acpigen.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) (limited to 'include') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 9e52ec589f5..14b96203a9d 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, }; @@ -155,4 +156,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 null-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 27d4eab7a60..b254ef6c9a9 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -147,3 +147,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'); } + +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 187001d2fcb..c360f55dd3d 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -23,6 +23,7 @@ #define ACPI_CONTEXT_SIZE 150 #define TEST_STRING "frogmore" +#define TEST_STRING2 "ranch" #define TEST_STREAM2 "\xfa\xde" #define TEST_INT8 0x7d @@ -477,3 +478,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); -- cgit v1.3.1 From 7aed90d44c07ff4ea2f05e7a7b167040fe99f64f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:54 -0600 Subject: acpi: Support writing a name 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 --- 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(+) (limited to 'include') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 14b96203a9d..24aec7fad6f 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, }; /** @@ -165,4 +168,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 7ddd6e88724..99bbb1230c7 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 b254ef6c9a9..0a6c1e37c5a 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -153,3 +153,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 null 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 c360f55dd3d..adfcf887655 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -505,3 +505,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); -- cgit v1.3.1 From 29df845204e6b67583491b3c9883432c3a74d923 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:55 -0600 Subject: acpi: Support writing a UUID 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 --- include/acpi/acpigen.h | 13 +++++++++++++ lib/acpi/acpigen.c | 38 ++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) (limited to 'include') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 24aec7fad6f..31fa20a806f 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, @@ -190,4 +191,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 0a6c1e37c5a..3e8374e65e3 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -249,3 +250,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 adfcf887655..1cdbcf21311 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -598,3 +598,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); -- cgit v1.3.1 From 0e5a0a00d6e44dc0c7e1466ceb3e452b43ceeb1b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:56 -0600 Subject: acpi: Support writing Device Properties objects via _DSD 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 Reviewed-by: Bin Meng --- include/acpi/acpi_dp.h | 217 ++++++++++++++++++++++++++++ include/acpi/acpigen.h | 1 + lib/acpi/Makefile | 1 + lib/acpi/acpi_dp.c | 323 +++++++++++++++++++++++++++++++++++++++++ test/dm/Makefile | 1 + test/dm/acpi.h | 29 ++++ test/dm/acpi_dp.c | 385 +++++++++++++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 39 +++-- 8 files changed, 974 insertions(+), 22 deletions(-) create mode 100644 include/acpi/acpi_dp.h create mode 100644 lib/acpi/acpi_dp.c create mode 100644 test/dm/acpi.h create mode 100644 test/dm/acpi_dp.c (limited to 'include') diff --git a/include/acpi/acpi_dp.h b/include/acpi/acpi_dp.h new file mode 100644 index 00000000000..a82330856a5 --- /dev/null +++ b/include/acpi/acpi_dp.h @@ -0,0 +1,217 @@ +/* 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 child 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 + * + * @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); + +#endif diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 31fa20a806f..b10226f8f8d 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 85a1f774ad4..5c2f793701f 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 00000000000..32528e1ec42 --- /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; + } +} + +static int acpi_dp_write_internal(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_internal(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_internal(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 5641070ea11..b03c96da062 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.h b/test/dm/acpi.h new file mode 100644 index 00000000000..885dff85d3d --- /dev/null +++ b/test/dm/acpi.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Common functions for ACPI tests + * + * Copyright 2020 Google LLC + * Written by Simon Glass + */ + +#ifndef __TEST_DM_ACPI_H +#define __TEST_DM_ACPI_H + +/** + * acpi_test_alloc_context_size() - Allocate an ACPI context of a given size + * + * @ctxp: Returns allocated context + * @size: Size to allocate in bytes + * @return 0 if OK, -ENOMEM if out of memory + */ +int acpi_test_alloc_context_size(struct acpi_ctx **ctxp, int size); + +/** + * acpi_test_get_length() - decode a three-byte length field + * + * @ptr: Length encoded as per ACPI + * @return decoded length, or -EINVAL on error + */ +int acpi_test_get_length(u8 *ptr); + +#endif /*__TEST_DM_ACPI_H */ diff --git a/test/dm/acpi_dp.c b/test/dm/acpi_dp.c new file mode 100644 index 00000000000..28696aaff6b --- /dev/null +++ b/test/dm/acpi_dp.c @@ -0,0 +1,385 @@ +// 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 +#include "acpi.h" + +/* Maximum size of the ACPI context needed for most tests */ +#define ACPI_CONTEXT_SIZE 500 + +#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) +{ + return acpi_test_alloc_context_size(ctxp, ACPI_CONTEXT_SIZE); + + return 0; +} + +static void free_context(struct acpi_ctx **ctxp) +{ + free(*ctxp); + *ctxp = NULL; +} + +/* 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, acpi_test_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, acpi_test_get_length(ptr + 6)); + ut_asserteq(2, ptr[9]); + + /* UUID */ + ut_asserteq(BUFFER_OP, ptr[10]); + ut_asserteq(22, acpi_test_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, acpi_test_get_length(ptr + 34)); + ut_asserteq(1, ptr[37]); + + /* Package with name and (integer) value */ + ut_asserteq(PACKAGE_OP, ptr[38]); + ut_asserteq(15, acpi_test_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, acpi_test_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, acpi_test_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, acpi_test_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, acpi_test_get_length(pptr + 6)); + ut_asserteq(2, pptr[9]); + + /* UUID */ + ut_asserteq(BUFFER_OP, pptr[10]); + ut_asserteq(22, acpi_test_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, acpi_test_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, acpi_test_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); diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 1cdbcf21311..a488db8b3e8 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -18,6 +18,7 @@ #include #include #include +#include "acpi.h" /* Maximum size of the ACPI context needed for most tests */ #define ACPI_CONTEXT_SIZE 150 @@ -31,7 +32,7 @@ #define TEST_INT32 0x12345678 #define TEST_INT64 0x4567890123456 -static int alloc_context_size(struct acpi_ctx **ctxp, int size) +int acpi_test_alloc_context_size(struct acpi_ctx **ctxp, int size) { struct acpi_ctx *ctx; @@ -51,9 +52,17 @@ static int alloc_context_size(struct acpi_ctx **ctxp, int size) return 0; } +int acpi_test_get_length(u8 *ptr) +{ + if (!(*ptr & 0x80)) + return -EINVAL; + + return (*ptr & 0xf) | ptr[1] << 4 | ptr[2] << 12; +} + static int alloc_context(struct acpi_ctx **ctxp) { - return alloc_context_size(ctxp, ACPI_CONTEXT_SIZE); + return acpi_test_alloc_context_size(ctxp, ACPI_CONTEXT_SIZE); } static void free_context(struct acpi_ctx **ctxp) @@ -357,20 +366,6 @@ static int dm_test_acpi_spi(struct unit_test_state *uts) } 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) { @@ -379,7 +374,7 @@ static int dm_test_acpi_len(struct unit_test_state *uts) u8 *ptr; int i; - ut_assertok(alloc_context_size(&ctx, size)); + ut_assertok(acpi_test_alloc_context_size(&ctx, size)); ptr = acpigen_get_current(ctx); @@ -387,7 +382,7 @@ static int dm_test_acpi_len(struct unit_test_state *uts) acpigen_write_len_f(ctx); acpigen_emit_byte(ctx, 0x23); acpigen_pop_len(ctx); - ut_asserteq(1 + 3, get_length(ptr)); + ut_asserteq(1 + 3, acpi_test_get_length(ptr)); /* Write 200 bytes so we need two length bytes */ ptr = ctx->current; @@ -395,7 +390,7 @@ static int dm_test_acpi_len(struct unit_test_state *uts) for (i = 0; i < 200; i++) acpigen_emit_byte(ctx, 0x23); acpigen_pop_len(ctx); - ut_asserteq(200 + 3, get_length(ptr)); + ut_asserteq(200 + 3, acpi_test_get_length(ptr)); /* Write 40KB so we need three length bytes */ ptr = ctx->current; @@ -403,7 +398,7 @@ static int dm_test_acpi_len(struct unit_test_state *uts) for (i = 0; i < 40000; i++) acpigen_emit_byte(ctx, 0x23); acpigen_pop_len(ctx); - ut_asserteq(40000 + 3, get_length(ptr)); + ut_asserteq(40000 + 3, acpi_test_get_length(ptr)); free_context(&ctx); @@ -429,7 +424,7 @@ static int dm_test_acpi_package(struct unit_test_state *uts) acpigen_emit_byte(ctx, 0x23); acpigen_pop_len(ctx); ut_asserteq(PACKAGE_OP, ptr[0]); - ut_asserteq(5, get_length(ptr + 1)); + ut_asserteq(5, acpi_test_get_length(ptr + 1)); ut_asserteq(3, ptr[4]); free_context(&ctx); @@ -613,7 +608,7 @@ static int dm_test_acpi_uuid(struct unit_test_state *uts) "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(22, acpi_test_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))); -- cgit v1.3.1 From 235723466628c32e30685363377e416696318e84 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:57 -0600 Subject: acpi: Support writing a GPIO 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 --- include/acpi/acpi_dp.h | 20 ++++++++++++++++++++ lib/acpi/acpi_dp.c | 23 +++++++++++++++++++++++ test/dm/acpi_dp.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) (limited to 'include') diff --git a/include/acpi/acpi_dp.h b/include/acpi/acpi_dp.h index a82330856a5..b89e0b8c493 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 32528e1ec42..e8de5651c83 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 28696aaff6b..8b812260b19 100644 --- a/test/dm/acpi_dp.c +++ b/test/dm/acpi_dp.c @@ -383,3 +383,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); -- cgit v1.3.1 From 06679000493a07eb643287d1adfa4ffe6c3fc87c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:58 -0600 Subject: acpi: Support copying properties from device tree to ACPI 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 ACPI DP table 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 --- arch/sandbox/dts/test.dts | 1 + include/acpi/acpi_dp.h | 50 +++++++++++++++++++++++++++++++++++ lib/acpi/acpi_dp.c | 56 +++++++++++++++++++++++++++++++++++++++ test/dm/acpi_dp.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 174 insertions(+) (limited to 'include') diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 0d8ffd179a8..f0d8f12d401 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 b89e0b8c493..0b514bce59c 100644 --- a/include/acpi/acpi_dp.h +++ b/include/acpi/acpi_dp.h @@ -234,4 +234,54 @@ struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name, */ 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 e8de5651c83..579cab47715 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 8b812260b19..93604b87e18 100644 --- a/test/dm/acpi_dp.c +++ b/test/dm/acpi_dp.c @@ -423,3 +423,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); -- cgit v1.3.1 From 9c70e7e556339ce9fa864782445f7927fafc5c03 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:11:59 -0600 Subject: acpi: Add support for various misc ACPI opcodes Add more functions to handle some miscellaneous ACPI opcodes. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- include/acpi/acpigen.h | 117 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/acpi/acpigen.c | 86 ++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 75 +++++++++++++++++++++++++++++++ 3 files changed, 278 insertions(+) (limited to 'include') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index b10226f8f8d..2dd806aa273 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, }; /** @@ -204,4 +224,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 3e8374e65e3..4fd29ccb81e 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -72,6 +72,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; @@ -251,6 +257,40 @@ void acpigen_write_name(struct acpi_ctx *ctx, const char *namepath) acpigen_emit_namestring(ctx, namepath); } +static void acpigen_write_method_internal(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_internal(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_internal(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) * @@ -287,3 +327,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 a488db8b3e8..63744454539 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -626,3 +626,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, acpi_test_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); -- cgit v1.3.1 From f9189d5ada8d48932463dc1d56ea4d44c050ebbd Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:12:00 -0600 Subject: acpi: Add support for writing a Power Resource 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. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- include/acpi/acpigen.h | 22 ++++++++++++++++++++++ lib/acpi/acpigen.c | 22 ++++++++++++++++++++++ test/dm/acpigen.c | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) (limited to 'include') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 2dd806aa273..2c07a562082 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, }; @@ -321,4 +322,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 4fd29ccb81e..70d734245bb 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -328,6 +328,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 63744454539..df062a210c7 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -701,3 +701,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, acpi_test_get_length(ptr + 6)); + ut_asserteq_strn(name, (char *)ptr + 0xa); + + ut_asserteq_strn(states[1], (char *)ptr + 0xf); + ut_asserteq(8, acpi_test_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); -- cgit v1.3.1 From f8054dd8ba533be0e2ebd7965b95446a1f573953 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:12:01 -0600 Subject: acpi: Add support for writing a GPIO power sequence 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 Reviewed-by: Bin Meng --- include/acpi/acpigen.h | 56 +++++++++++++++++++++++++++++++++ lib/acpi/acpigen.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 64 +++++++++++++++++++++++++++++++++++++ 3 files changed, 205 insertions(+) (limited to 'include') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 2c07a562082..d06d2c0c731 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 @@ -343,4 +344,59 @@ 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); +/** + * acpigen_set_enable_tx_gpio() - Emit ACPI code to enable/disable a GPIO + * + * This emits code to either enable to disable a Tx GPIO. It takes account of + * the GPIO polarity. + * + * The code needs access to the DW0 register for the pad being used. This is + * provided by gpio->pin0_addr and ACPI methods must be defined for the board + * which can read and write the pad's DW0 register given this address: + * @dw0_read: takes a single argument, the DW0 address + * returns the DW0 value + * @dw0:write: takes two arguments, the DW0 address and the value to write + * no return value + * + * Example code (-- means comment): + * + * -- Get Pad Configuration DW0 register value + * Method (GPC0, 0x1, Serialized) + * { + * -- Arg0 - GPIO DW0 address + * Store (Arg0, Local0) + * OperationRegion (PDW0, SystemMemory, Local0, 4) + * Field (PDW0, AnyAcc, NoLock, Preserve) { + * TEMP, 32 + * } + * Return (TEMP) + * } + * + * -- Set Pad Configuration DW0 register value + * Method (SPC0, 0x2, Serialized) + * { + * -- Arg0 - GPIO DW0 address + * -- Arg1 - Value for DW0 register + * Store (Arg0, Local0) + * OperationRegion (PDW0, SystemMemory, Local0, 4) + * Field (PDW0, AnyAcc, NoLock, Preserve) { + * TEMP,32 + * } + * Store (Arg1, TEMP) + * } + * + * + * @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_read: Method name to use to read dw0, e.g. "\\_SB.GPC0" + * @dw0_write: Method name to use to read dw0, e.g. "\\_SB.SPC0" + * @gpio: GPIO to change + * @enable: true to enable GPIO, false to disable + * Returns 0 on success, -ve on error. + */ +int acpigen_set_enable_tx_gpio(struct acpi_ctx *ctx, u32 tx_state_val, + const char *dw0_read, const char *dw0_write, + struct acpi_gpio *gpio, bool enable); + #endif diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index 70d734245bb..45216c1f9d6 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) @@ -395,3 +396,87 @@ 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 + * + * 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 + * + * The value needs to be stored in a local variable so that it can be used in + * expressions in the ACPI code. + * + * @ctx: ACPI context pointer + * @dw0_read: Name to use to read dw0, e.g. "\\_SB.GPC0" + * @addr: GPIO pin configuration register address + * + */ +static void acpigen_get_dw0_in_local5(struct acpi_ctx *ctx, + const char *dw0_read, ulong addr) +{ + acpigen_write_store(ctx); + acpigen_emit_namestring(ctx, dw0_read); + acpigen_write_integer(ctx, addr); + acpigen_emit_byte(ctx, LOCAL5_OP); +} + +/** + * acpigen_set_gpio_val() - Emit code to set value of TX GPIO to on/off + * + * @ctx: ACPI context pointer + * @dw0_read: Method name to use to read dw0, e.g. "\\_SB.GPC0" + * @dw0_write: Method name to use to read dw0, e.g. "\\_SB.SPC0" + * @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_read, const char *dw0_write, + struct acpi_gpio *gpio, bool val) +{ + acpigen_get_dw0_in_local5(ctx, dw0_read, gpio->pin0_addr); + + /* Store (0x40, Local0) */ + acpigen_write_store(ctx); + acpigen_write_integer(ctx, tx_state_val); + acpigen_emit_byte(ctx, LOCAL0_OP); + + if (val) { + /* Or (Local5, PAD_CFG0_TX_STATE, Local5) */ + acpigen_write_or(ctx, LOCAL5_OP, LOCAL0_OP, LOCAL5_OP); + } else { + /* Not (PAD_CFG0_TX_STATE, Local6) */ + acpigen_write_not(ctx, LOCAL0_OP, 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_write); + acpigen_write_integer(ctx, gpio->pin0_addr); + acpigen_emit_byte(ctx, LOCAL5_OP); + + return 0; +} + +int acpigen_set_enable_tx_gpio(struct acpi_ctx *ctx, u32 tx_state_val, + const char *dw0_read, const char *dw0_write, + 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_read, dw0_write, 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 df062a210c7..9b699fc26a3 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -742,3 +742,67 @@ 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", "\\_SB.SPC0", + &gpio, true); + acpigen_set_enable_tx_gpio(ctx, txbit, "\\_SB.GPC0", "\\_SB.SPC0", + &gpio, false); + + /* Since this GPIO is active low, we expect it to be cleared here */ + ut_asserteq(STORE_OP, *ptr); + 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(STORE_OP, ptr[0x11]); + ut_asserteq(BYTE_PREFIX, ptr[0x12]); + ut_asserteq(txbit, ptr[0x13]); + ut_asserteq(LOCAL0_OP, ptr[0x14]); + + ut_asserteq(NOT_OP, ptr[0x15]); + ut_asserteq(LOCAL0_OP, ptr[0x16]); + ut_asserteq(LOCAL6_OP, ptr[0x17]); + ut_asserteq(AND_OP, ptr[0x18]); + ut_asserteq_strn("_SB_SPC0", (char *)ptr + 0x1e); + ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0x27))); + ut_asserteq(LOCAL5_OP, ptr[0x2b]); + + /* Now the second one, which should be set */ + ut_asserteq_strn("_SB_GPC0", (char *)ptr + 0x2f); + ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0x38))); + ut_asserteq(LOCAL5_OP, ptr[0x3c]); + + ut_asserteq(STORE_OP, ptr[0x3d]); + + ut_asserteq(OR_OP, ptr[0x41]); + ut_asserteq(LOCAL0_OP, ptr[0x43]); + ut_asserteq_strn("_SB_SPC0", (char *)ptr + 0x47); + ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0x50))); + ut_asserteq(LOCAL5_OP, ptr[0x54]); + ut_asserteq(0x55, 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); -- cgit v1.3.1 From 740630ba73768667a2f87326f2a237d373a5093d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:12:02 -0600 Subject: acpi: Add support for a generic power sequence 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 Reviewed-by: Bin Meng --- include/acpi/acpi_device.h | 42 ++++++++++++++++++++ lib/acpi/acpi_device.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 68 +++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) (limited to 'include') diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index 62b1295201c..e7db7bf5ad6 100644 --- a/include/acpi/acpi_device.h +++ b/include/acpi/acpi_device.h @@ -342,4 +342,46 @@ 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_read: Name to use to read dw0, e.g. "\\_SB.GPC0" + * @dw0_write: Name to use to read dw0, e.g. "\\_SB.SPC0" + * @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_read, const char *dw0_write, + 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 b628fa13377..c66cafcfee8 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -386,6 +386,105 @@ int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx, return pin; } +/* PowerResource() with Enable and/or Reset control */ +int acpi_device_add_power_res(struct acpi_ctx *ctx, u32 tx_state_val, + const char *dw0_read, const char *dw0_write, + 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_read, + dw0_write, &reset, true); + if (ret) + return log_msg_ret("reset1", ret); + } + if (has_enable) { + ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read, + dw0_write, &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_read, + dw0_write, &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_read, + dw0_write, &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_read, + dw0_write, &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_read, + dw0_write, &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_read, + dw0_write, &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 9b699fc26a3..9e7a928b249 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -806,3 +806,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(acpi_test_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", + "\\_SB.SPC0", &reset, 2, 3, + &enable, 4, 5, &stop, 6, 7)); + ut_asserteq(0x186, 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[0x52]); + + /* enable set */ + ut_asserteq(addr + enable.offset, get_unaligned((u32 *)(ptr + 0x72))); + ut_asserteq(OR_OP, ptr[0x7b]); + + /* reset clear */ + ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x9f))); + ut_asserteq(NOT_OP, ptr[0xa8]); + + /* stop set (disable, active low) */ + ut_asserteq(addr_act_low + stop.offset, + get_unaligned((u32 *)(ptr + 0xcf))); + ut_asserteq(OR_OP, ptr[0xd8]); + + /* Now the 'OFF' sequence */ + ut_asserteq_strn("_OFF", (char *)ptr + 0xf4); + + /* stop clear (enable, active low) */ + ut_asserteq(addr_act_low + stop.offset, + get_unaligned((u32 *)(ptr + 0x105))); + ut_asserteq(NOT_OP, ptr[0x10e]); + + /* reset clear */ + ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x135))); + ut_asserteq(OR_OP, ptr[0x13e]); + + /* enable clear */ + ut_asserteq(addr + enable.offset, get_unaligned((u32 *)(ptr + 0x162))); + ut_asserteq(NOT_OP, ptr[0x16b]); + + free_context(&ctx); + + return 0; +} + +DM_TEST(dm_test_acpi_power_seq, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- cgit v1.3.1 From b5183172f031603c5d861c34389f88a3c493cfd7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:12:03 -0600 Subject: acpi: Add support for SSDT generation 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 --- 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(+) (limited to 'include') diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index f0d8f12d401..5b41edd8d3b 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 8ae61575dd3..4497b5cb2f6 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 dfda88e4931..e956e49680e 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 07b0daaab0d..d46d1fbe66f 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); -- cgit v1.3.1 From 01694589af589212d6f2a6241821896ef9e334d3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:12:08 -0600 Subject: acpi: Add support for DSDT generation 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 Reviewed-by: Bin Meng [bmeng: correct one typo in inject_dsdt() comments] Signed-off-by: Bin Meng --- arch/sandbox/dts/test.dts | 2 ++ drivers/core/acpi.c | 25 ++++++++++++++++++++++++- include/dm/acpi.h | 30 ++++++++++++++++++++++++++++++ test/dm/acpi.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index c79ea349324..12101aaf799 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 a9b7fc1d9a0..7b32694ad43 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; @@ -198,6 +202,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; } } @@ -256,6 +262,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 e956e49680e..fceb1ae95c2 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -82,11 +82,31 @@ struct acpi_ops { * whatever ACPI code is needed by this device. It will end up in the * SSDT table. * + * Note that this is called 'fill' because the entire contents of the + * SSDT is build by calling this method on all devices. + * * @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); + + /** + * 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. + * + * Note that this is called 'inject' because the output of calling this + * method on all devices is injected into the DSDT, the bulk of which + * is written in .asl files for the board. + * + * @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 +161,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 4e1d401e0d6..1abde65c8c1 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); -- cgit v1.3.1 From 20349781a3ca833c67126888ddfce7c1517c772e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:12:10 -0600 Subject: pci: Avoid a crash in device_is_on_pci_bus() 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 --- include/dm/device.h | 2 +- test/dm/pci.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/dm/device.h b/include/dm/device.h index f5738a0cee9..953706cf525 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -764,7 +764,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 fb93e4c78ae..39e82b3699f 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); -- cgit v1.3.1 From fefac0b0643b14e72c356cf05dabcbe7512c4709 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:12:11 -0600 Subject: dm: acpi: Enhance acpi_get_name() 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 Reviewed-by: Bin Meng --- 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 | 18 ++++++ lib/acpi/acpi_device.c | 106 ++++++++++++++++++++++++++++++++++++ test/dm/acpi.c | 42 +++++++++++++- 8 files changed, 190 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 12101aaf799..3744a466030 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 27bd3978d98..7140339623b 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@10 { interrupts-extended = <&acpi_gpe GPIO_21_IRQ IRQ_TYPE_EDGE_FALLING>; linux,probed; }; + +pcie-a0@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 7b32694ad43..076fb4f1b47 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 125026da908..57b1c60fde6 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 77797bf096d..755f1768614 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(sandbox_spi) = { .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 e7db7bf5ad6..5d94a88c02d 100644 --- a/include/acpi/acpi_device.h +++ b/include/acpi/acpi_device.h @@ -384,4 +384,22 @@ 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. + * + * NOTE: This currently supports only x86 devices. Feel free to enhance it for + * other architectures as needed. + * + * @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 c66cafcfee8..3c75b6d9629 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 @@ -715,3 +717,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: + /* This only supports USB3.0 controllers at present */ + 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 1abde65c8c1..69ca0902aab 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); -- cgit v1.3.1 From a4f8208919a4458ebe93d46d43a7cb0a13f7a0d8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 13:12:12 -0600 Subject: acpi: Add an acpi command to list/dump generated ACPI items 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 --- 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(-) (limited to 'include') diff --git a/cmd/acpi.c b/cmd/acpi.c index e9a9161a913..085a3a650d1 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 076fb4f1b47..b566f4f1864 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 fceb1ae95c2..aa1071ae354 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 * @@ -171,6 +177,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 69ca0902aab..7768f9514cf 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); -- cgit v1.3.1 From db6fb7d152bf39d2781b61d89458900660544b91 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 21:32:02 -0600 Subject: binman: Allow setting the ROM offset On x86 the SPI ROM can be memory-mapped, at least most of it. Add a way to tell binman the offset from a ROM address to a RAM address. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- include/binman.h | 8 ++++++++ lib/binman.c | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) (limited to 'include') diff --git a/include/binman.h b/include/binman.h index b462dc85429..baf49f7876b 100644 --- a/include/binman.h +++ b/include/binman.h @@ -20,6 +20,14 @@ struct binman_entry { u32 size; }; +/** + * binman_set_rom_offset() - Set the ROM memory-map offset + * + * @rom_offset: Offset from an image_pos to the memory-mapped address. This + * tells binman that ROM image_pos x can be addressed at rom_offset + x + */ +void binman_set_rom_offset(int rom_offset); + /** * binman_entry_find() - Find a binman symbol * diff --git a/lib/binman.c b/lib/binman.c index fd7de24bd23..dc3a880882e 100644 --- a/lib/binman.c +++ b/lib/binman.c @@ -12,10 +12,21 @@ #include #include +/** + * struct binman_info - Information needed by the binman library + * + * @image: Node describing the image we are running from + * @rom_offset: Offset from an image_pos to the memory-mapped address, or + * ROM_OFFSET_NONE if the ROM is not memory-mapped. Can be positive or + * negative + */ struct binman_info { ofnode image; + int rom_offset; }; +#define ROM_OFFSET_NONE (-1) + static struct binman_info *binman; int binman_entry_find(const char *name, struct binman_entry *entry) @@ -37,6 +48,11 @@ int binman_entry_find(const char *name, struct binman_entry *entry) return 0; } +void binman_set_rom_offset(int rom_offset) +{ + binman->rom_offset = rom_offset; +} + int binman_init(void) { binman = malloc(sizeof(struct binman_info)); @@ -45,6 +61,7 @@ int binman_init(void) binman->image = ofnode_path("/binman"); if (!ofnode_valid(binman->image)) return log_msg_ret("binman node", -EINVAL); + binman->rom_offset = ROM_OFFSET_NONE; return 0; } -- cgit v1.3.1 From 8f9877df95ae0068ce14a962bd72c22026c1d2e8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 21:32:04 -0600 Subject: binman: Add way to locate an entry in memory Add support for accessing an entry's contents in memory-mapped SPI flash. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- include/binman.h | 22 ++++++++++++++++++++++ lib/binman.c | 23 +++++++++++++++++++++++ 2 files changed, 45 insertions(+) (limited to 'include') diff --git a/include/binman.h b/include/binman.h index baf49f7876b..e0b92075e27 100644 --- a/include/binman.h +++ b/include/binman.h @@ -9,6 +9,8 @@ #ifndef _BINMAN_H_ #define _BINMAN_H_ +#include + /** *struct binman_entry - information about a binman entry * @@ -20,6 +22,18 @@ struct binman_entry { u32 size; }; +/** + * binman_entry_map() - Look up the address of an entry in memory + * + * @parent: Parent binman node + * @name: Name of entry + * @bufp: Returns a pointer to the entry + * @sizep: Returns the size of the entry + * @return 0 on success, -EPERM if the ROM offset is not set, -ENOENT if the + * entry cannot be found, other error code other error + */ +int binman_entry_map(ofnode parent, const char *name, void **bufp, int *sizep); + /** * binman_set_rom_offset() - Set the ROM memory-map offset * @@ -41,6 +55,14 @@ void binman_set_rom_offset(int rom_offset); */ int binman_entry_find(const char *name, struct binman_entry *entry); +/** + * binman_section_find_node() - Find a binman node + * + * @name: Name of node to look for + * @return Node that was found, ofnode_null() if not found + */ +ofnode binman_section_find_node(const char *name); + /** * binman_init() - Set up the binman symbol information * diff --git a/lib/binman.c b/lib/binman.c index 9098a1dffa1..7a8ad62c4a8 100644 --- a/lib/binman.c +++ b/lib/binman.c @@ -11,6 +11,7 @@ #include #include #include +#include /** * struct binman_info - Information needed by the binman library @@ -55,6 +56,28 @@ int binman_entry_find(const char *name, struct binman_entry *entry) return binman_entry_find_internal(binman->image, name, entry); } +int binman_entry_map(ofnode parent, const char *name, void **bufp, int *sizep) +{ + struct binman_entry entry; + int ret; + + if (binman->rom_offset == ROM_OFFSET_NONE) + return -EPERM; + ret = binman_entry_find_internal(parent, name, &entry); + if (ret) + return log_msg_ret("entry", ret); + if (sizep) + *sizep = entry.size; + *bufp = map_sysmem(entry.image_pos + binman->rom_offset, entry.size); + + return 0; +} + +ofnode binman_section_find_node(const char *name) +{ + return ofnode_find_subnode(binman->image, name); +} + void binman_set_rom_offset(int rom_offset) { binman->rom_offset = rom_offset; -- cgit v1.3.1 From f18589576cb87e76c20046b335124a8af6feb6ac Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 21:32:07 -0600 Subject: dm: core: Add a way of overriding the ACPI device path Some devices such as GPIO need to override the normal path that would be generated by driver model. Add a device-tree property for this. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Reviewed-by: Wolfgang Wallner --- doc/device-tree-bindings/device.txt | 23 +++++++++++++++++++++++ drivers/core/acpi.c | 19 +++++++++++++++++++ include/dm/acpi.h | 13 +++++++++++++ 3 files changed, 55 insertions(+) (limited to 'include') diff --git a/doc/device-tree-bindings/device.txt b/doc/device-tree-bindings/device.txt index 7140339623b..2a5736c5981 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,path : Specifies the full ACPI path for a device. This overrides the + normal path built from the driver-model hierarchy - 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 @@ -47,3 +49,24 @@ pcie-a0@14,0 { interrupts-extended = <&acpi_gpe 0x3c 0>; }; }; + +p2sb: p2sb@d,0 { + u-boot,dm-pre-reloc; + reg = <0x02006810 0 0 0 0>; + compatible = "intel,apl-p2sb"; + early-regs = ; + pci,no-autoconfig; + + n { + compatible = "intel,apl-pinctrl"; + u-boot,dm-pre-reloc; + intel,p2sb-port-id = ; + acpi,path = "\\_SB.GPO0"; + gpio_n: gpio-n { + compatible = "intel,gpio"; + u-boot,dm-pre-reloc; + gpio-controller; + #gpio-cells = <2>; + linux-name = "INT3452:00"; + }; + }; diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index b566f4f1864..ae692585625 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -82,6 +82,25 @@ int acpi_get_name(const struct udevice *dev, char *out_name) return 0; } +int acpi_get_path(const struct udevice *dev, char *out_path, int maxlen) +{ + const char *path; + int ret; + + path = dev_read_string(dev, "acpi,path"); + if (path) { + if (strlen(path) >= maxlen) + return -E2BIG; + strcpy(out_path, path); + return 0; + } + ret = acpi_device_path(dev, out_path, maxlen); + if (ret) + return log_msg_ret("dev", ret); + + return 0; +} + /** * acpi_add_item() - Add a new item to the list of data collected * diff --git a/include/dm/acpi.h b/include/dm/acpi.h index aa1071ae354..a58722de737 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -187,6 +187,19 @@ int acpi_inject_dsdt(struct acpi_ctx *ctx); */ void acpi_dump_items(enum acpi_dump_option option); +/** + * acpi_get_path() - Get the full ACPI path for a device + * + * This checks for any override in the device tree and calls acpi_device_path() + * if not + * + * @dev: Device to check + * @out_path: 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_get_path(const struct udevice *dev, char *out_path, int maxlen); + #endif /* __ACPI__ */ #endif -- cgit v1.3.1 From b4e843341816395ef8a9d48793322617f9a50f9f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 21:32:08 -0600 Subject: dm: acpi: Add support for the NHLT table The Intel Non-High-Definition-Audio Link Table (NHLT) table describes the audio codecs and connections in a system. Various devices can contribute information to produce the table. Add core support for this, based on a structure which is built up through calls to the driver. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- drivers/core/acpi.c | 15 +++++++++++++++ include/dm/acpi.h | 26 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) (limited to 'include') diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index ae692585625..cdbc2c5cf5b 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -31,6 +31,7 @@ enum method_t { METHOD_WRITE_TABLES, METHOD_FILL_SSDT, METHOD_INJECT_DSDT, + METHOD_SETUP_NHLT, }; /* Prototype for all methods */ @@ -248,6 +249,8 @@ acpi_method acpi_get_method(struct udevice *dev, enum method_t method) return aops->fill_ssdt; case METHOD_INJECT_DSDT: return aops->inject_dsdt; + case METHOD_SETUP_NHLT: + return aops->setup_nhlt; } } @@ -334,3 +337,15 @@ int acpi_write_dev_tables(struct acpi_ctx *ctx) return ret; } + +int acpi_setup_nhlt(struct acpi_ctx *ctx, struct nhlt *nhlt) +{ + int ret; + + log_debug("Setup NHLT\n"); + ctx->nhlt = nhlt; + ret = acpi_recurse_method(ctx, dm_root(), METHOD_SETUP_NHLT, TYPE_NONE); + log_debug("Setup finished, err=%d\n", ret); + + return ret; +} diff --git a/include/dm/acpi.h b/include/dm/acpi.h index a58722de737..e8b0336f6d8 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -27,6 +27,8 @@ #if !defined(__ACPI__) +struct nhlt; + /** enum acpi_dump_option - selects what ACPI information to dump */ enum acpi_dump_option { ACPI_DUMP_LIST, /* Just the list of items */ @@ -44,6 +46,9 @@ enum acpi_dump_option { * 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 + * @nhlt: Intel Non-High-Definition-Audio Link Table (NHLT) pointer, used to + * build up information that audio codecs need to provide in the NHLT ACPI + * table * @len_stack: Stack of 'length' words to fix up later * @ltop: Points to current top of stack (0 = empty) */ @@ -53,6 +58,7 @@ struct acpi_ctx { struct acpi_rsdp *rsdp; struct acpi_rsdt *rsdt; struct acpi_xsdt *xsdt; + struct nhlt *nhlt; char *len_stack[ACPIGEN_LENSTACK_SIZE]; int ltop; }; @@ -113,6 +119,15 @@ struct acpi_ops { * @return 0 if OK, -ve on error */ int (*inject_dsdt)(const struct udevice *dev, struct acpi_ctx *ctx); + + /** + * setup_nhlt() - Set up audio information for this device + * + * The method can add information to ctx->nhlt if it likes + * + * @return 0 if OK, -ENODATA if nothing to add, -ve on error + */ + int (*setup_nhlt)(const struct udevice *dev, struct acpi_ctx *ctx); }; #define device_get_acpi_ops(dev) ((dev)->driver->acpi_ops) @@ -177,6 +192,17 @@ int acpi_fill_ssdt(struct acpi_ctx *ctx); */ int acpi_inject_dsdt(struct acpi_ctx *ctx); +/** + * acpi_setup_nhlt() - Set up audio information + * + * This is called to set up the nhlt information for all devices. + * + * @ctx: ACPI context to use + * @nhlt: Pointer to nhlt information to add to + * @return 0 if OK, -ve on error + */ +int acpi_setup_nhlt(struct acpi_ctx *ctx, struct nhlt *nhlt); + /** * acpi_dump_items() - Dump out the collected ACPI items * -- cgit v1.3.1 From fea9651084e72fe94aefa0b828854ef5ce2835b4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 21:32:09 -0600 Subject: acpi: Export functions to write sized values At present only acpigen_write_integer() is exported for use by other code. But in some cases it is useful to call the specific function depending on the size of the value. Export these functions and add a test. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng [bmeng: Fix the "new blank line at EOF" warning] Signed-off-by: Bin Meng --- include/acpi/acpigen.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index d06d2c0c731..c6644bc2b23 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -172,6 +172,52 @@ void acpigen_pop_len(struct acpi_ctx *ctx); */ char *acpigen_write_package(struct acpi_ctx *ctx, int nr_el); +/** + * acpigen_write_byte() - Write a byte + * + * @ctx: ACPI context pointer + * @data: Value to write + */ +void acpigen_write_byte(struct acpi_ctx *ctx, unsigned int data); + +/** + * acpigen_write_word() - Write a word + * + * @ctx: ACPI context pointer + * @data: Value to write + */ +void acpigen_write_word(struct acpi_ctx *ctx, unsigned int data); + +/** + * acpigen_write_dword() - Write a dword + * + * @ctx: ACPI context pointer + * @data: Value to write + */ +void acpigen_write_dword(struct acpi_ctx *ctx, unsigned int data); + +/** + * acpigen_write_qword() - Write a qword + * + * @ctx: ACPI context pointer + * @data: Value to write + */ +void acpigen_write_qword(struct acpi_ctx *ctx, u64 data); + +/** + * acpigen_write_zero() - Write zero + * + * @ctx: ACPI context pointer + */ +void acpigen_write_zero(struct acpi_ctx *ctx); + +/** + * acpigen_write_one() - Write one + * + * @ctx: ACPI context pointer + */ +void acpigen_write_one(struct acpi_ctx *ctx); + /** * acpigen_write_integer() - Write an integer * diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 9e7a928b249..f25be938a47 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -872,5 +872,47 @@ static int dm_test_acpi_power_seq(struct unit_test_state *uts) return 0; } - DM_TEST(dm_test_acpi_power_seq, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test writing values */ +static int dm_test_acpi_write_values(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + ptr = acpigen_get_current(ctx); + + acpigen_write_zero(ctx); + acpigen_write_one(ctx); + acpigen_write_byte(ctx, TEST_INT8); + acpigen_write_word(ctx, TEST_INT16); + acpigen_write_dword(ctx, TEST_INT32); + acpigen_write_qword(ctx, TEST_INT64); + + ut_asserteq(ZERO_OP, *ptr++); + + ut_asserteq(ONE_OP, *ptr++); + + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(TEST_INT8, *ptr++); + + ut_asserteq(WORD_PREFIX, *ptr++); + ut_asserteq(TEST_INT16, get_unaligned((u16 *)ptr)); + ptr += 2; + + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(TEST_INT32, get_unaligned((u32 *)ptr)); + ptr += 4; + + ut_asserteq(QWORD_PREFIX, *ptr++); + ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)ptr)); + ptr += 8; + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_values, 0); -- cgit v1.3.1 From 82659cc91014df7dfcc55474c1657d1c9b6fe957 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 21:32:10 -0600 Subject: acpi: Support generation of a scope Add a function to write a scope to the generated ACPI code. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng [bmeng: Fix build failures on Sandbox] Signed-off-by: Bin Meng --- include/acpi/acpigen.h | 9 +++++++++ lib/acpi/acpigen.c | 7 +++++++ test/dm/acpi.c | 3 +-- test/dm/acpi.h | 3 +++ test/dm/acpigen.c | 31 +++++++++++++++++++++++++++++++ 5 files changed, 51 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index c6644bc2b23..4a606125de0 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -31,6 +31,7 @@ enum { DWORD_PREFIX = 0x0c, STRING_PREFIX = 0x0d, QWORD_PREFIX = 0x0e, + SCOPE_OP = 0x10, BUFFER_OP = 0x11, PACKAGE_OP = 0x12, METHOD_OP = 0x14, @@ -261,6 +262,14 @@ void acpigen_emit_namestring(struct acpi_ctx *ctx, const char *namepath); */ void acpigen_write_name(struct acpi_ctx *ctx, const char *namepath); +/** + * acpigen_write_scope() - Write a scope + * + * @ctx: ACPI context pointer + * @scope: Scope to write (e.g. "\\_SB.ABCD") + */ +void acpigen_write_scope(struct acpi_ctx *ctx, const char *scope); + /** * acpigen_write_uuid() - Write a UUID * diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index 45216c1f9d6..1e0a489d7b9 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -258,6 +258,13 @@ void acpigen_write_name(struct acpi_ctx *ctx, const char *namepath) acpigen_emit_namestring(ctx, namepath); } +void acpigen_write_scope(struct acpi_ctx *ctx, const char *scope) +{ + acpigen_emit_byte(ctx, SCOPE_OP); + acpigen_write_len_f(ctx); + acpigen_emit_namestring(ctx, scope); +} + static void acpigen_write_method_internal(struct acpi_ctx *ctx, const char *name, uint flags) { diff --git a/test/dm/acpi.c b/test/dm/acpi.c index 7768f9514cf..b94c4ba4d13 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -20,9 +20,8 @@ #include #include #include +#include "acpi.h" -#define ACPI_TEST_DEV_NAME "ABCD" -#define ACPI_TEST_CHILD_NAME "EFGH" #define BUF_SIZE 4096 /** diff --git a/test/dm/acpi.h b/test/dm/acpi.h index 885dff85d3d..535db56b51e 100644 --- a/test/dm/acpi.h +++ b/test/dm/acpi.h @@ -9,6 +9,9 @@ #ifndef __TEST_DM_ACPI_H #define __TEST_DM_ACPI_H +#define ACPI_TEST_DEV_NAME "ABCD" +#define ACPI_TEST_CHILD_NAME "EFGH" + /** * acpi_test_alloc_context_size() - Allocate an ACPI context of a given size * diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index f25be938a47..46792d8e89f 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -916,3 +916,34 @@ static int dm_test_acpi_write_values(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_write_values, 0); + +/* Test writing a scope */ +static int dm_test_acpi_scope(struct unit_test_state *uts) +{ + char buf[ACPI_PATH_MAX]; + 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_TEST_ACPI, &dev)); + ut_assertok(acpi_device_path(dev, buf, sizeof(buf))); + acpigen_write_scope(ctx, buf); + acpigen_pop_len(ctx); + + ut_asserteq(SCOPE_OP, *ptr++); + ut_asserteq(13, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(ROOT_PREFIX, *ptr++); + ut_asserteq(DUAL_NAME_PREFIX, *ptr++); + ut_asserteq_strn("_SB_" ACPI_TEST_DEV_NAME, (char *)ptr); + ptr += 8; + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_scope, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- cgit v1.3.1 From d7d631df2d4f32710dbecb020e375b9c0d986225 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 21:32:11 -0600 Subject: acpi: Support generation of a generic register Allow writing out a generic register. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Reviewed-by: Wolfgang Wallner [bmeng: Fix build failures on Sandbox] Signed-off-by: Bin Meng --- include/acpi/acpi_device.h | 1 + include/acpi/acpigen.h | 28 ++++++++++++++++++ lib/acpi/acpigen.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 46 ++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+) (limited to 'include') diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index 5d94a88c02d..11461e168d3 100644 --- a/include/acpi/acpi_device.h +++ b/include/acpi/acpi_device.h @@ -20,6 +20,7 @@ struct udevice; /* ACPI descriptor values for common descriptors: SERIAL_BUS means I2C */ #define ACPI_DESCRIPTOR_LARGE BIT(7) +#define ACPI_DESCRIPTOR_REGISTER (ACPI_DESCRIPTOR_LARGE | 2) #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) diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 4a606125de0..1f37c9c31cd 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -13,6 +13,7 @@ #include struct acpi_ctx; +struct acpi_gen_regaddr; struct acpi_gpio; /* Top 4 bits of the value used to indicate a three-byte length value */ @@ -21,6 +22,8 @@ struct acpi_gpio; #define ACPI_METHOD_NARGS_MASK 0x7 #define ACPI_METHOD_SERIALIZED_MASK BIT(3) +#define ACPI_END_TAG 0x79 + /* ACPI Op/Prefix codes */ enum { ZERO_OP = 0x00, @@ -318,6 +321,31 @@ void acpigen_write_method_serialized(struct acpi_ctx *ctx, const char *name, */ void acpigen_write_sta(struct acpi_ctx *ctx, uint status); +/** + * acpigen_write_resourcetemplate_header() - Write a ResourceTemplate header + * + * @ctx: ACPI context pointer + */ +void acpigen_write_resourcetemplate_header(struct acpi_ctx *ctx); + +/** + * acpigen_write_resourcetemplate_footer() - Write a ResourceTemplate footer + * + * @ctx: ACPI context pointer + */ +void acpigen_write_resourcetemplate_footer(struct acpi_ctx *ctx); + +/** + * acpigen_write_register_resource() - Write a register resource + * + * This writes a header, the address information and a footer + * + * @ctx: ACPI context pointer + * @addr: Address to write + */ +void acpigen_write_register_resource(struct acpi_ctx *ctx, + const struct acpi_gen_regaddr *addr); + /** * acpigen_write_sleep() - Write a sleep operation * diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index 1e0a489d7b9..45691b79610 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -14,6 +14,7 @@ #include #include #include +#include #include u8 *acpigen_get_current(struct acpi_ctx *ctx) @@ -299,6 +300,76 @@ void acpigen_write_sta(struct acpi_ctx *ctx, uint status) acpigen_pop_len(ctx); } +static void acpigen_write_register(struct acpi_ctx *ctx, + const struct acpi_gen_regaddr *addr) +{ + /* See ACPI v6.3 section 6.4.3.7: Generic Register Descriptor */ + acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_REGISTER); + acpigen_emit_byte(ctx, 0x0c); /* Register Length 7:0 */ + acpigen_emit_byte(ctx, 0x00); /* Register Length 15:8 */ + acpigen_emit_byte(ctx, addr->space_id); + acpigen_emit_byte(ctx, addr->bit_width); + acpigen_emit_byte(ctx, addr->bit_offset); + acpigen_emit_byte(ctx, addr->access_size); + acpigen_emit_dword(ctx, addr->addrl); + acpigen_emit_dword(ctx, addr->addrh); +} + +void acpigen_write_resourcetemplate_header(struct acpi_ctx *ctx) +{ + /* + * A ResourceTemplate() is a Buffer() with a + * (Byte|Word|DWord) containing the length, followed by one or more + * resource items, terminated by the end tag. + * (small item 0xf, len 1) + */ + acpigen_emit_byte(ctx, BUFFER_OP); + acpigen_write_len_f(ctx); + acpigen_emit_byte(ctx, WORD_PREFIX); + ctx->len_stack[ctx->ltop++] = ctx->current; + + /* + * Add two dummy bytes for the ACPI word (keep aligned with the + * calculation in acpigen_write_resourcetemplate_footer() below) + */ + acpigen_emit_byte(ctx, 0x00); + acpigen_emit_byte(ctx, 0x00); +} + +void acpigen_write_resourcetemplate_footer(struct acpi_ctx *ctx) +{ + char *p = ctx->len_stack[--ctx->ltop]; + int len; + /* + * See ACPI v6.3 section 6.4.2.9: End Tag + * 0x79 + * 0x00 is treated as a good checksum according to the spec + * and is what iasl generates. + */ + acpigen_emit_byte(ctx, ACPI_END_TAG); + acpigen_emit_byte(ctx, 0x00); + + /* + * Start counting past the 2-bytes length added in + * acpigen_write_resourcetemplate_header() above + */ + len = (char *)ctx->current - (p + 2); + + /* patch len word */ + p[0] = len & 0xff; + p[1] = (len >> 8) & 0xff; + + acpigen_pop_len(ctx); +} + +void acpigen_write_register_resource(struct acpi_ctx *ctx, + const struct acpi_gen_regaddr *addr) +{ + acpigen_write_resourcetemplate_header(ctx); + acpigen_write_register(ctx, addr); + acpigen_write_resourcetemplate_footer(ctx); +} + /* * ToUUID(uuid) * diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 46792d8e89f..822641afb01 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -947,3 +948,48 @@ static int dm_test_acpi_scope(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_scope, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test writing a resource template */ +static int dm_test_acpi_resource_template(struct unit_test_state *uts) +{ + struct acpi_gen_regaddr addr; + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + ptr = acpigen_get_current(ctx); + + addr.space_id = ACPI_ADDRESS_SPACE_EC; + addr.bit_width = 32; + addr.bit_offset = 8; + addr.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS; + addr.addrl = TEST_INT64 & 0xffffffff; + addr.addrh = TEST_INT64 >> 32; + acpigen_write_register_resource(ctx, &addr); + + ut_asserteq(BUFFER_OP, *ptr++); + ut_asserteq(0x17, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(WORD_PREFIX, *ptr++); + ut_asserteq(0x11, get_unaligned((u16 *)ptr)); + ptr += 2; + ut_asserteq(ACPI_DESCRIPTOR_REGISTER, *ptr++); + ut_asserteq(0xc, *ptr++); + ut_asserteq(0, *ptr++); + ut_asserteq(ACPI_ADDRESS_SPACE_EC, *ptr++); + ut_asserteq(32, *ptr++); + ut_asserteq(8, *ptr++); + ut_asserteq(ACPI_ACCESS_SIZE_DWORD_ACCESS, *ptr++); + ut_asserteq(TEST_INT64 & 0xffffffff, get_unaligned((u32 *)ptr)); + ptr += 4; + ut_asserteq(TEST_INT64 >> 32, get_unaligned((u32 *)ptr)); + ptr += 4; + ut_asserteq(ACPI_END_TAG, *ptr++); + ut_asserteq(0x00, *ptr++); + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_resource_template, 0); -- cgit v1.3.1 From 70303d24809599e14f97f8ef08364ac6589410f4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 21:32:13 -0600 Subject: x86: Add bindings for NHLT Add devicetree bindings for the Intel Non-High-Definition-Audio Link Table (NHLT). Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- include/dt-bindings/sound/nhlt.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 include/dt-bindings/sound/nhlt.h (limited to 'include') diff --git a/include/dt-bindings/sound/nhlt.h b/include/dt-bindings/sound/nhlt.h new file mode 100644 index 00000000000..dad69c24b4b --- /dev/null +++ b/include/dt-bindings/sound/nhlt.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2019 Google LLC + */ + +#ifndef _DT_BINDINGS_SOUND_NHLT_H +#define _DT_BINDINGS_SOUND_NHLT_H + +/* See Table 2-1. NHLT Endpoint Descriptor in the NHLT Specification (0.8.1) */ +#define NHLT_VID 0x8086 +#define NHLT_DID_DMIC 0xae20 +#define NHLT_DID_BT 0xae30 +#define NHLT_DID_SSP 0xae34 + +/* Hardware links available to use for codecs */ +#define AUDIO_LINK_SSP0 0 +#define AUDIO_LINK_SSP1 1 +#define AUDIO_LINK_SSP2 2 +#define AUDIO_LINK_SSP3 3 +#define AUDIO_LINK_SSP4 4 +#define AUDIO_LINK_SSP5 5 +#define AUDIO_LINK_DMIC 6 + +#endif /* _DT_BINDINGS_SOUND_NHLT_H */ -- cgit v1.3.1 From 91c2f9c32ed76269b9786a6c506233e9b91bdfce Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 21:32:14 -0600 Subject: acpi: Support generation of a device Allow writing an ACPI device to the generated ACPI code. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Reviewed-by: Wolfgang Wallner [bmeng: Fix build failures on Sandbox] Signed-off-by: Bin Meng --- include/acpi/acpigen.h | 9 +++++++++ lib/acpi/acpigen.c | 7 +++++++ test/dm/acpigen.c | 27 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) (limited to 'include') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 1f37c9c31cd..59d7c2ff6f3 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -56,6 +56,7 @@ enum { AND_OP = 0x7b, OR_OP = 0x7d, NOT_OP = 0x80, + DEVICE_OP = 0x82, POWER_RES_OP = 0x84, RETURN_OP = 0xa4, }; @@ -313,6 +314,14 @@ void acpigen_write_method(struct acpi_ctx *ctx, const char *name, int nargs); void acpigen_write_method_serialized(struct acpi_ctx *ctx, const char *name, int nargs); +/** + * acpigen_write_device() - Write an ACPI device + * + * @ctx: ACPI context pointer + * @name: Device name to write + */ +void acpigen_write_device(struct acpi_ctx *ctx, const char *name); + /** * acpigen_write_sta() - Write a _STA method * diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index 45691b79610..a66601a1383 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -291,6 +291,13 @@ void acpigen_write_method_serialized(struct acpi_ctx *ctx, const char *name, ACPI_METHOD_SERIALIZED_MASK); } +void acpigen_write_device(struct acpi_ctx *ctx, const char *name) +{ + acpigen_emit_ext_op(ctx, DEVICE_OP); + acpigen_write_len_f(ctx); + acpigen_emit_namestring(ctx, name); +} + void acpigen_write_sta(struct acpi_ctx *ctx, uint status) { /* Method (_STA, 0, NotSerialized) { Return (status) } */ diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 822641afb01..cea1a62b950 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -993,3 +993,30 @@ static int dm_test_acpi_resource_template(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_resource_template, 0); + +/* Test writing a device */ +static int dm_test_acpi_device(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + ptr = acpigen_get_current(ctx); + + acpigen_write_device(ctx, "\\_SB." ACPI_TEST_DEV_NAME); + acpigen_pop_len(ctx); + + ut_asserteq(EXT_OP_PREFIX, *ptr++); + ut_asserteq(DEVICE_OP, *ptr++); + ut_asserteq(0xd, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(ROOT_PREFIX, *ptr++); + ut_asserteq(DUAL_NAME_PREFIX, *ptr++); + ptr += 8; + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_device, 0); -- cgit v1.3.1 From bb6772c3ffb0b2df22fd22cfe8d17276823af06d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 21:32:15 -0600 Subject: acpi: Support writing named values Allow writing named integers and strings to the generated ACPI code. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Reviewed-by: Wolfgang Wallner [bmeng: Fix the "new blank line at EOF" warning] Signed-off-by: Bin Meng --- include/acpi/acpigen.h | 72 ++++++++++++++++++++++++++++++++++++++++++++++ lib/acpi/acpigen.c | 49 ++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+) (limited to 'include') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 59d7c2ff6f3..228ac9c404b 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -234,6 +234,78 @@ void acpigen_write_one(struct acpi_ctx *ctx); */ void acpigen_write_integer(struct acpi_ctx *ctx, u64 data); +/** + * acpigen_write_name_zero() - Write a named zero value + * + * @ctx: ACPI context pointer + * @name: Name of the value + */ +void acpigen_write_name_zero(struct acpi_ctx *ctx, const char *name); + +/** + * acpigen_write_name_one() - Write a named one value + * + * @ctx: ACPI context pointer + * @name: Name of the value + */ +void acpigen_write_name_one(struct acpi_ctx *ctx, const char *name); + +/** + * acpigen_write_name_byte() - Write a named byte value + * + * @ctx: ACPI context pointer + * @name: Name of the value + * @val: Value to write + */ +void acpigen_write_name_byte(struct acpi_ctx *ctx, const char *name, uint val); + +/** + * acpigen_write_name_word() - Write a named word value + * + * @ctx: ACPI context pointer + * @name: Name of the value + * @val: Value to write + */ +void acpigen_write_name_word(struct acpi_ctx *ctx, const char *name, uint val); + +/** + * acpigen_write_name_dword() - Write a named dword value + * + * @ctx: ACPI context pointer + * @name: Name of the value + * @val: Value to write + */ +void acpigen_write_name_dword(struct acpi_ctx *ctx, const char *name, uint val); + +/** + * acpigen_write_name_qword() - Write a named qword value + * + * @ctx: ACPI context pointer + * @name: Name of the value + * @val: Value to write + */ +void acpigen_write_name_qword(struct acpi_ctx *ctx, const char *name, u64 val); + +/** + * acpigen_write_name_integer() - Write a named integer value + * + * @ctx: ACPI context pointer + * @name: Name of the value + * @val: Value to write + */ +void acpigen_write_name_integer(struct acpi_ctx *ctx, const char *name, + u64 val); + +/** + * acpigen_write_name_string() - Write a named string value + * + * @ctx: ACPI context pointer + * @name: Name of the value + * @string: String to write + */ +void acpigen_write_name_string(struct acpi_ctx *ctx, const char *name, + const char *string); + /** * acpigen_write_string() - Write a string * diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index a66601a1383..c609ef4daa4 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -143,6 +143,55 @@ void acpigen_write_integer(struct acpi_ctx *ctx, u64 data) acpigen_write_qword(ctx, data); } +void acpigen_write_name_zero(struct acpi_ctx *ctx, const char *name) +{ + acpigen_write_name(ctx, name); + acpigen_write_zero(ctx); +} + +void acpigen_write_name_one(struct acpi_ctx *ctx, const char *name) +{ + acpigen_write_name(ctx, name); + acpigen_write_one(ctx); +} + +void acpigen_write_name_byte(struct acpi_ctx *ctx, const char *name, uint val) +{ + acpigen_write_name(ctx, name); + acpigen_write_byte(ctx, val); +} + +void acpigen_write_name_word(struct acpi_ctx *ctx, const char *name, uint val) +{ + acpigen_write_name(ctx, name); + acpigen_write_word(ctx, val); +} + +void acpigen_write_name_dword(struct acpi_ctx *ctx, const char *name, uint val) +{ + acpigen_write_name(ctx, name); + acpigen_write_dword(ctx, val); +} + +void acpigen_write_name_qword(struct acpi_ctx *ctx, const char *name, u64 val) +{ + acpigen_write_name(ctx, name); + acpigen_write_qword(ctx, val); +} + +void acpigen_write_name_integer(struct acpi_ctx *ctx, const char *name, u64 val) +{ + acpigen_write_name(ctx, name); + acpigen_write_integer(ctx, val); +} + +void acpigen_write_name_string(struct acpi_ctx *ctx, const char *name, + const char *string) +{ + acpigen_write_name(ctx, name); + acpigen_write_string(ctx, string); +} + 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 cea1a62b950..14a758d08a3 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -1020,3 +1020,80 @@ static int dm_test_acpi_device(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_device, 0); + +/* Test writing named values */ +static int dm_test_acpi_write_name(struct unit_test_state *uts) +{ + const char *name = "\\_SB." ACPI_TEST_DEV_NAME; + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + ptr = acpigen_get_current(ctx); + + acpigen_write_name_zero(ctx, name); + acpigen_write_name_one(ctx, name); + acpigen_write_name_byte(ctx, name, TEST_INT8); + acpigen_write_name_word(ctx, name, TEST_INT16); + acpigen_write_name_dword(ctx, name, TEST_INT32); + acpigen_write_name_qword(ctx, name, TEST_INT64); + acpigen_write_name_integer(ctx, name, TEST_INT64 + 1); + acpigen_write_name_string(ctx, name, "baldrick"); + acpigen_write_name_string(ctx, name, NULL); + + ut_asserteq(NAME_OP, *ptr++); + ut_asserteq_strn("\\._SB_ABCD", (char *)ptr); + ptr += 10; + ut_asserteq(ZERO_OP, *ptr++); + + ut_asserteq(NAME_OP, *ptr++); + ptr += 10; + ut_asserteq(ONE_OP, *ptr++); + + ut_asserteq(NAME_OP, *ptr++); + ptr += 10; + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(TEST_INT8, *ptr++); + + ut_asserteq(NAME_OP, *ptr++); + ptr += 10; + ut_asserteq(WORD_PREFIX, *ptr++); + ut_asserteq(TEST_INT16, get_unaligned((u16 *)ptr)); + ptr += 2; + + ut_asserteq(NAME_OP, *ptr++); + ptr += 10; + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(TEST_INT32, get_unaligned((u32 *)ptr)); + ptr += 4; + + ut_asserteq(NAME_OP, *ptr++); + ptr += 10; + ut_asserteq(QWORD_PREFIX, *ptr++); + ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)ptr)); + ptr += 8; + + ut_asserteq(NAME_OP, *ptr++); + ptr += 10; + ut_asserteq(QWORD_PREFIX, *ptr++); + ut_asserteq_64(TEST_INT64 + 1, get_unaligned((u64 *)ptr)); + ptr += 8; + + ut_asserteq(NAME_OP, *ptr++); + ptr += 10; + ut_asserteq(STRING_PREFIX, *ptr++); + ut_asserteq_str("baldrick", (char *)ptr) + ptr += 9; + + ut_asserteq(NAME_OP, *ptr++); + ptr += 10; + ut_asserteq(STRING_PREFIX, *ptr++); + ut_asserteq('\0', *ptr++); + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_name, 0); -- cgit v1.3.1 From 4916f4586eab7941f38a89e4dacd103af1c4105f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 21:32:19 -0600 Subject: x86: pinctrl: Add a way to get the pinctrl reg address At present we can query the offset of a pinctrl register within the p2sb. For ACPI we need to get the actual address of the register. Add a function to handle this and rename the old one to more accurately reflect its purpose. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Reviewed-by: Wolfgang Wallner --- arch/x86/include/asm/intel_pinctrl.h | 16 ++++++++++++++-- drivers/gpio/intel_gpio.c | 15 +++++++++++---- drivers/misc/p2sb-uclass.c | 16 ++++++++-------- drivers/pinctrl/intel/pinctrl.c | 11 +++++++++-- include/p2sb.h | 9 +++++++++ 5 files changed, 51 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/intel_pinctrl.h b/arch/x86/include/asm/intel_pinctrl.h index e2524b089d4..b5564b9ee11 100644 --- a/arch/x86/include/asm/intel_pinctrl.h +++ b/arch/x86/include/asm/intel_pinctrl.h @@ -263,11 +263,23 @@ int pinctrl_read_pads(struct udevice *dev, ofnode node, const char *prop, int pinctrl_count_pads(struct udevice *dev, u32 *pads, int size); /** - * intel_pinctrl_get_config_reg_addr() - Get address of the pin config registers + * intel_pinctrl_get_config_reg_offset() - Get offset of pin config registers * + * This works out the register offset of a pin within the p2sb region. + * + * @dev: Pinctrl device + * @offset: GPIO offset within this device + * @return register offset of first register within the GPIO p2sb region + */ +u32 intel_pinctrl_get_config_reg_offset(struct udevice *dev, uint offset); + +/** + * intel_pinctrl_get_config_reg_addr() - Get address of pin config registers + * + * This works out the absolute address of the registers for a pin * @dev: Pinctrl device * @offset: GPIO offset within this device - * @return register offset within the GPIO p2sb region + * @return register address of first register within the GPIO p2sb region */ u32 intel_pinctrl_get_config_reg_addr(struct udevice *dev, uint offset); diff --git a/drivers/gpio/intel_gpio.c b/drivers/gpio/intel_gpio.c index 711fea1b587..b4d5be97dad 100644 --- a/drivers/gpio/intel_gpio.c +++ b/drivers/gpio/intel_gpio.c @@ -24,7 +24,9 @@ static int intel_gpio_direction_input(struct udevice *dev, uint offset) { struct udevice *pinctrl = dev_get_parent(dev); - uint config_offset = intel_pinctrl_get_config_reg_addr(pinctrl, offset); + uint config_offset; + + config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset); pcr_clrsetbits32(pinctrl, config_offset, PAD_CFG0_MODE_MASK | PAD_CFG0_TX_STATE | @@ -38,7 +40,9 @@ static int intel_gpio_direction_output(struct udevice *dev, uint offset, int value) { struct udevice *pinctrl = dev_get_parent(dev); - uint config_offset = intel_pinctrl_get_config_reg_addr(pinctrl, offset); + uint config_offset; + + config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset); pcr_clrsetbits32(pinctrl, config_offset, PAD_CFG0_MODE_MASK | PAD_CFG0_RX_STATE | @@ -68,10 +72,13 @@ static int intel_gpio_get_value(struct udevice *dev, uint offset) return 0; } -static int intel_gpio_set_value(struct udevice *dev, unsigned offset, int value) +static int intel_gpio_set_value(struct udevice *dev, unsigned int offset, + int value) { struct udevice *pinctrl = dev_get_parent(dev); - uint config_offset = intel_pinctrl_get_config_reg_addr(pinctrl, offset); + uint config_offset; + + config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset); pcr_clrsetbits32(pinctrl, config_offset, PAD_CFG0_TX_STATE, value ? PAD_CFG0_TX_STATE : 0); diff --git a/drivers/misc/p2sb-uclass.c b/drivers/misc/p2sb-uclass.c index 06b1e8d9ad7..d5fe12ebd8d 100644 --- a/drivers/misc/p2sb-uclass.c +++ b/drivers/misc/p2sb-uclass.c @@ -18,7 +18,7 @@ #define PCR_COMMON_IOSF_1_0 1 -static void *_pcr_reg_address(struct udevice *dev, uint offset) +void *pcr_reg_address(struct udevice *dev, uint offset) { struct p2sb_child_platdata *pplat = dev_get_parent_platdata(dev); struct udevice *p2sb = dev_get_parent(dev); @@ -55,7 +55,7 @@ uint pcr_read32(struct udevice *dev, uint offset) /* Ensure the PCR offset is correctly aligned */ assert(IS_ALIGNED(offset, sizeof(uint32_t))); - ptr = _pcr_reg_address(dev, offset); + ptr = pcr_reg_address(dev, offset); val = readl(ptr); unmap_sysmem(ptr); @@ -67,7 +67,7 @@ uint pcr_read16(struct udevice *dev, uint offset) /* Ensure the PCR offset is correctly aligned */ check_pcr_offset_align(offset, sizeof(uint16_t)); - return readw(_pcr_reg_address(dev, offset)); + return readw(pcr_reg_address(dev, offset)); } uint pcr_read8(struct udevice *dev, uint offset) @@ -75,7 +75,7 @@ uint pcr_read8(struct udevice *dev, uint offset) /* Ensure the PCR offset is correctly aligned */ check_pcr_offset_align(offset, sizeof(uint8_t)); - return readb(_pcr_reg_address(dev, offset)); + return readb(pcr_reg_address(dev, offset)); } /* @@ -86,7 +86,7 @@ uint pcr_read8(struct udevice *dev, uint offset) */ static void write_completion(struct udevice *dev, uint offset) { - readl(_pcr_reg_address(dev, ALIGN_DOWN(offset, sizeof(uint32_t)))); + readl(pcr_reg_address(dev, ALIGN_DOWN(offset, sizeof(uint32_t)))); } void pcr_write32(struct udevice *dev, uint offset, uint indata) @@ -94,7 +94,7 @@ void pcr_write32(struct udevice *dev, uint offset, uint indata) /* Ensure the PCR offset is correctly aligned */ assert(IS_ALIGNED(offset, sizeof(indata))); - writel(indata, _pcr_reg_address(dev, offset)); + writel(indata, pcr_reg_address(dev, offset)); /* Ensure the writes complete */ write_completion(dev, offset); } @@ -104,7 +104,7 @@ void pcr_write16(struct udevice *dev, uint offset, uint indata) /* Ensure the PCR offset is correctly aligned */ check_pcr_offset_align(offset, sizeof(uint16_t)); - writew(indata, _pcr_reg_address(dev, offset)); + writew(indata, pcr_reg_address(dev, offset)); /* Ensure the writes complete */ write_completion(dev, offset); } @@ -114,7 +114,7 @@ void pcr_write8(struct udevice *dev, uint offset, uint indata) /* Ensure the PCR offset is correctly aligned */ check_pcr_offset_align(offset, sizeof(uint8_t)); - writeb(indata, _pcr_reg_address(dev, offset)); + writeb(indata, pcr_reg_address(dev, offset)); /* Ensure the writes complete */ write_completion(dev, offset); } diff --git a/drivers/pinctrl/intel/pinctrl.c b/drivers/pinctrl/intel/pinctrl.c index ba8206350ea..bf3989bf327 100644 --- a/drivers/pinctrl/intel/pinctrl.c +++ b/drivers/pinctrl/intel/pinctrl.c @@ -394,7 +394,7 @@ static int pinctrl_configure_pad(struct udevice *dev, return 0; } -u32 intel_pinctrl_get_config_reg_addr(struct udevice *dev, uint offset) +u32 intel_pinctrl_get_config_reg_offset(struct udevice *dev, uint offset) { struct intel_pinctrl_priv *priv = dev_get_priv(dev); const struct pad_community *comm = priv->comm; @@ -407,9 +407,16 @@ u32 intel_pinctrl_get_config_reg_addr(struct udevice *dev, uint offset) return config_offset; } +u32 intel_pinctrl_get_config_reg_addr(struct udevice *dev, uint offset) +{ + uint config_offset = intel_pinctrl_get_config_reg_offset(dev, offset); + + return (u32)(ulong)pcr_reg_address(dev, config_offset); +} + u32 intel_pinctrl_get_config_reg(struct udevice *dev, uint offset) { - uint config_offset = intel_pinctrl_get_config_reg_addr(dev, offset); + uint config_offset = intel_pinctrl_get_config_reg_offset(dev, offset); return pcr_read32(dev, config_offset); } diff --git a/include/p2sb.h b/include/p2sb.h index 60c7f70773c..74eb08b7ff8 100644 --- a/include/p2sb.h +++ b/include/p2sb.h @@ -132,4 +132,13 @@ int p2sb_set_port_id(struct udevice *dev, int portid); */ int p2sb_get_port_id(struct udevice *dev); +/** + * pcr_reg_address() Convert an offset in p2sb space to an absolute address + * + * @dev: Child device (whose parent is UCLASS_P2SB) + * @offset: Offset within that child's address space + * @return pointer to that offset within the child's address space + */ +void *pcr_reg_address(struct udevice *dev, uint offset); + #endif -- cgit v1.3.1 From 6d349e2e435340bf27c8f567f7607eaaba2ee77c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 21:32:30 -0600 Subject: p2sb: Add a method to hide the bus The P2SB bus needs to be hidden in some cases so that it does not get auto-configured by Linux. Add a method for this. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Reviewed-by: Wolfgang Wallner Tested-by: Wolfgang Wallner --- drivers/misc/p2sb-uclass.c | 10 ++++++++++ include/p2sb.h | 25 ++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/misc/p2sb-uclass.c b/drivers/misc/p2sb-uclass.c index d5fe12ebd8d..b5219df46be 100644 --- a/drivers/misc/p2sb-uclass.c +++ b/drivers/misc/p2sb-uclass.c @@ -18,6 +18,16 @@ #define PCR_COMMON_IOSF_1_0 1 +int p2sb_set_hide(struct udevice *dev, bool hide) +{ + struct p2sb_ops *ops = p2sb_get_ops(dev); + + if (!ops->set_hide) + return -ENOSYS; + + return ops->set_hide(dev, hide); +} + void *pcr_reg_address(struct udevice *dev, uint offset) { struct p2sb_child_platdata *pplat = dev_get_parent_platdata(dev); diff --git a/include/p2sb.h b/include/p2sb.h index 74eb08b7ff8..93e1155dca6 100644 --- a/include/p2sb.h +++ b/include/p2sb.h @@ -31,13 +31,36 @@ struct p2sb_uc_priv { }; /** - * struct p2sb_ops - Operations for the P2SB (none at present) + * struct p2sb_ops - Operations for the P2SB */ struct p2sb_ops { + /** + * set_hide() - Set/clear the 'hide' bit on the p2sb + * + * This device can be hidden from the PCI bus if needed. This method + * can be called before the p2sb is probed. + * + * @dev: P2SB device + * @hide: true to hide the device, false to show it + * @return 0 if OK, -ve on error + */ + int (*set_hide)(struct udevice *dev, bool hide); }; #define p2sb_get_ops(dev) ((struct p2sb_ops *)(dev)->driver->ops) +/** + * p2sb_set_hide() - Set/clear the 'hide' bit on the p2sb + * + * This device can be hidden from the PCI bus if needed. This method + * can be called before the p2sb is probed. + * + * @dev: P2SB device + * @hide: true to hide the device, false to show it + * @return 0 if OK, -ve on error + */ +int p2sb_set_hide(struct udevice *dev, bool hide); + /** * pcr_read32/16/8() - Read from a PCR device * -- cgit v1.3.1 From 11e27ae92b1b7596a2ad5755be119c14e68895ac Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 21:32:33 -0600 Subject: pmc: Move common registers to the header file These registers need to be accesses from ACPI code, so move them to the header file. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Reviewed-by: Bin Meng --- drivers/power/acpi_pmc/acpi-pmc-uclass.c | 9 --------- include/power/acpi_pmc.h | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/power/acpi_pmc/acpi-pmc-uclass.c b/drivers/power/acpi_pmc/acpi-pmc-uclass.c index 1c79f835c66..828963d8a09 100644 --- a/drivers/power/acpi_pmc/acpi-pmc-uclass.c +++ b/drivers/power/acpi_pmc/acpi-pmc-uclass.c @@ -15,15 +15,6 @@ #include #include -enum { - PM1_STS = 0x00, - PM1_EN = 0x02, - PM1_CNT = 0x04, - - GPE0_STS = 0x20, - GPE0_EN = 0x30, -}; - struct tco_regs { u32 tco_rld; u32 tco_sts; diff --git a/include/power/acpi_pmc.h b/include/power/acpi_pmc.h index 1f50c23f5f8..5fbf7451369 100644 --- a/include/power/acpi_pmc.h +++ b/include/power/acpi_pmc.h @@ -6,10 +6,22 @@ #ifndef __ACPI_PMC_H #define __ACPI_PMC_H +#ifndef __ACPI__ + enum { GPE0_REG_MAX = 4, }; +enum { + PM1_STS = 0x00, + PM1_EN = 0x02, + PM1_CNT = 0x04, + PM1_TMR = 0x08, + + GPE0_STS = 0x20, + GPE0_EN = 0x30, +}; + /** * struct acpi_pmc_upriv - holds common data for the x86 PMC * @@ -182,4 +194,6 @@ void pmc_dump_info(struct udevice *dev); */ int pmc_gpe_init(struct udevice *dev); +#endif /* !__ACPI__ */ + #endif -- cgit v1.3.1 From a8c2789c09681660bfdbda489e504d12b0ab74b5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 7 Jul 2020 21:32:34 -0600 Subject: x86: irq: Support flags for acpi_gpe This binding currently has a flags cell but it is not used. Make use of it to create ACPI tables for interrupts. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Reviewed-by: Wolfgang Wallner --- arch/x86/cpu/acpi_gpe.c | 26 ++++++++++++++++++++++ include/dt-bindings/interrupt-controller/x86-irq.h | 14 ++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 include/dt-bindings/interrupt-controller/x86-irq.h (limited to 'include') diff --git a/arch/x86/cpu/acpi_gpe.c b/arch/x86/cpu/acpi_gpe.c index 8aa2009bd6a..70badb15a3b 100644 --- a/arch/x86/cpu/acpi_gpe.c +++ b/arch/x86/cpu/acpi_gpe.c @@ -8,7 +8,10 @@ #include #include #include +#include #include +#include +#include /** * struct acpi_gpe_priv - private driver information @@ -62,13 +65,36 @@ static int acpi_gpe_ofdata_to_platdata(struct udevice *dev) static int acpi_gpe_of_xlate(struct irq *irq, struct ofnode_phandle_args *args) { irq->id = args->args[0]; + irq->flags = args->args[1]; return 0; } +#if CONFIG_IS_ENABLED(ACPIGEN) +static int acpi_gpe_get_acpi(const struct irq *irq, struct acpi_irq *acpi_irq) +{ + memset(acpi_irq, '\0', sizeof(*acpi_irq)); + acpi_irq->pin = irq->id; + acpi_irq->mode = irq->flags & IRQ_TYPE_EDGE_BOTH ? + ACPI_IRQ_EDGE_TRIGGERED : ACPI_IRQ_LEVEL_TRIGGERED; + acpi_irq->polarity = irq->flags & + (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW) ? + ACPI_IRQ_ACTIVE_LOW : ACPI_IRQ_ACTIVE_HIGH; + acpi_irq->shared = irq->flags & X86_IRQ_TYPE_SHARED ? + ACPI_IRQ_SHARED : ACPI_IRQ_EXCLUSIVE; + acpi_irq->wake = irq->flags & X86_IRQ_TYPE_WAKE ? ACPI_IRQ_WAKE : + ACPI_IRQ_NO_WAKE; + + return 0; +} +#endif + static const struct irq_ops acpi_gpe_ops = { .read_and_clear = acpi_gpe_read_and_clear, .of_xlate = acpi_gpe_of_xlate, +#if CONFIG_IS_ENABLED(ACPIGEN) + .get_acpi = acpi_gpe_get_acpi, +#endif }; static const struct udevice_id acpi_gpe_ids[] = { diff --git a/include/dt-bindings/interrupt-controller/x86-irq.h b/include/dt-bindings/interrupt-controller/x86-irq.h new file mode 100644 index 00000000000..9e0b4612e13 --- /dev/null +++ b/include/dt-bindings/interrupt-controller/x86-irq.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2019 Google LLC + * + * This provides additional flags used by x86. + */ + +#ifndef _DT_BINDINGS_INTERRUPT_CONTROLLER_X86_IRQ_H +#define _DT_BINDINGS_INTERRUPT_CONTROLLER_X86_IRQ_H + +#define X86_IRQ_TYPE_SHARED (1 << 4) +#define X86_IRQ_TYPE_WAKE (1 << 5) + +#endif -- cgit v1.3.1