diff mbox series

[v4,09/35] acpi: Support generation of GPIO descriptor

Message ID 20200707131135.v4.9.I6aa22eaeb633cacd7b056f0e7199b5d15b557a84@changeid
State Superseded
Headers show
Series dm: Add programmatic generation of ACPI tables (part B) | expand

Commit Message

Simon Glass July 7, 2020, 7:11 p.m. UTC
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(+)

Comments

Bin Meng July 13, 2020, 2:47 a.m. UTC | #1
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
Bin Meng July 13, 2020, 4:22 a.m. UTC | #2
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 mbox series

Patch

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);