[PATCH v4 09/35] acpi: Support generation of GPIO descriptor
Bin Meng
bmeng.cn at gmail.com
Mon Jul 13 04:47:11 CEST 2020
Hi Simon,
On Wed, Jul 8, 2020 at 3:12 AM Simon Glass <sjg at chromium.org> wrote:
>
> Add a function to write a GPIO descriptor to the generated ACPI code.
>
> Signed-off-by: Simon Glass <sjg at chromium.org>
> Reviewed-by: Wolfgang Wallner <wolfgang.wallner at br-automation.com>
> ---
>
> Changes in v4:
> - Drop comment about the type always being ACPI_GPIO_TYPE_IO
> - Rename the length-writing functions to indicate they are for large resources
> - Update functions to return a GPIO pin number
>
> include/acpi/acpi_device.h | 22 ++++++
> lib/acpi/acpi_device.c | 151 +++++++++++++++++++++++++++++++++++++
> test/dm/acpigen.c | 91 ++++++++++++++++++++++
> 3 files changed, 264 insertions(+)
>
> diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h
> index 69b90968a8..18063e977f 100644
> --- a/include/acpi/acpi_device.h
> +++ b/include/acpi/acpi_device.h
> @@ -12,6 +12,7 @@
> #include <linux/bitops.h>
>
> 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. The type is always ACPI_GPIO_TYPE_IO.
This comment is not dropped ?
> + *
> + * @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 d854a45cbc..bbe1cfc57a 100644
> --- a/lib/acpi/acpi_device.c
> +++ b/lib/acpi/acpi_device.c
> @@ -12,6 +12,7 @@
> #include <log.h>
> #include <acpi/acpi_device.h>
> #include <acpi/acpigen.h>
> +#include <asm-generic/gpio.h>
> #include <dm/acpi.h>
>
> /**
> @@ -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 26d1b76db4..d15273d6bf 100644
> --- a/test/dm/acpigen.c
> +++ b/test/dm/acpigen.c
> @@ -12,6 +12,7 @@
> #include <malloc.h>
> #include <acpi/acpigen.h>
> #include <acpi/acpi_device.h>
> +#include <asm/gpio.h>
> #include <asm/unaligned.h>
> #include <dm/acpi.h>
> #include <dm/test.h>
> @@ -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);
> --
Reviewed-by: Bin Meng <bmeng.cn at gmail.com>
Regards,
Bin
More information about the U-Boot
mailing list