diff options
author | Simon Glass <sjg@chromium.org> | 2020-07-07 13:11:46 -0600 |
---|---|---|
committer | Bin Meng <bmeng.cn@gmail.com> | 2020-07-17 14:32:24 +0800 |
commit | a9e0a077df5509c3d4b49e745c746c38a4f9a7a1 (patch) | |
tree | 0a1d0d7b22275a0bd7bae704fadcf16a3133e81a /lib/acpi | |
parent | 7fb8da4ce1b22c8f077e6d4fe6e0d88e3fd9206d (diff) |
acpi: Support generation of GPIO descriptor
Add a function to write a GPIO descriptor to the generated ACPI code.
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Wolfgang Wallner <wolfgang.wallner@br-automation.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
[bmeng: Drop comment about the type always being ACPI_GPIO_TYPE_IO]
Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Diffstat (limited to 'lib/acpi')
-rw-r--r-- | lib/acpi/acpi_device.c | 151 |
1 files changed, 151 insertions, 0 deletions
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; } |