Message ID | 20200707131135.v4.9.I6aa22eaeb633cacd7b056f0e7199b5d15b557a84@changeid |
---|---|
State | Superseded |
Headers | show |
Series | dm: Add programmatic generation of ACPI tables (part B) | expand |
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
On Mon, Jul 13, 2020 at 10:47 AM Bin Meng <bmeng.cn at gmail.com> wrote: > > 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 ? > Dropped this comment when applying > > + * > > + * @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> > applied to u-boot-x86, thanks!
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. + * + * @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);