aboutsummaryrefslogtreecommitdiff
path: root/lib/acpi/acpi_device.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/acpi/acpi_device.c')
-rw-r--r--lib/acpi/acpi_device.c151
1 files changed, 151 insertions, 0 deletions
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 <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;
}