diff --git a/src/arch/x86/acpi_device.c b/src/arch/x86/acpi_device.c index b3861a2392..b607834349 100644 --- a/src/arch/x86/acpi_device.c +++ b/src/arch/x86/acpi_device.c @@ -20,6 +20,32 @@ #include #include +/* Write empty word value and return pointer to it */ +static void *acpi_device_write_zero_len(void) +{ + char *p = acpigen_get_current(); + acpigen_emit_word(0); + return p; +} + +/* Fill in length value from start to current at specified location */ +static void acpi_device_fill_from_len(char *ptr, char *start) +{ + uint16_t len = acpigen_get_current() - start; + ptr[0] = len & 0xff; + ptr[1] = (len >> 8) & 0xff; +} + +/* + * Fill in the length field with the value calculated from after + * the 16bit field to acpigen current as this length value does + * not include the length field itself. + */ +static void acpi_device_fill_len(void *ptr) +{ + acpi_device_fill_from_len(ptr, ptr + sizeof(uint16_t)); +} + /* Locate and return the ACPI name for this device */ const char *acpi_device_name(struct device *dev) { @@ -109,3 +135,52 @@ const char *acpi_device_path_join(struct device *dev, const char *name) return buf; } + +/* ACPI 6.1 section 6.4.3.6: Extended Interrupt Descriptor */ +void acpi_device_write_interrupt(const struct acpi_irq *irq) +{ + void *desc_length; + uint8_t flags; + + if (!irq || !irq->pin) + return; + + /* This is supported by GpioInt() but not Interrupt() */ + if (irq->polarity == IRQ_ACTIVE_BOTH) + return; + + /* Byte 0: Descriptor Type */ + acpigen_emit_byte(ACPI_DESCRIPTOR_INTERRUPT); + + /* Byte 1-2: Length (filled in later) */ + desc_length = acpi_device_write_zero_len(); + + /* + * Byte 3: Flags + * [7:5]: Reserved + * [4]: Wake (0=NO_WAKE 1=WAKE) + * [3]: Sharing (0=EXCLUSIVE 1=SHARED) + * [2]: Polarity (0=HIGH 1=LOW) + * [1]: Mode (0=LEVEL 1=EDGE) + * [0]: Resource (0=PRODUCER 1=CONSUMER) + */ + flags = 1 << 0; /* ResourceConsumer */ + if (irq->mode == IRQ_EDGE_TRIGGERED) + flags |= 1 << 1; + if (irq->polarity == IRQ_ACTIVE_LOW) + flags |= 1 << 2; + if (irq->shared == IRQ_SHARED) + flags |= 1 << 3; + if (irq->wake == IRQ_WAKE) + flags |= 1 << 4; + acpigen_emit_byte(flags); + + /* Byte 4: Interrupt Table Entry Count */ + acpigen_emit_byte(1); + + /* Byte 5-8: Interrupt Number */ + acpigen_emit_dword(irq->pin); + + /* Fill in Descriptor Length (account for len word) */ + acpi_device_fill_len(desc_length); +} diff --git a/src/arch/x86/include/arch/acpi_device.h b/src/arch/x86/include/arch/acpi_device.h index 2cad9b5d21..60f7e00606 100644 --- a/src/arch/x86/include/arch/acpi_device.h +++ b/src/arch/x86/include/arch/acpi_device.h @@ -16,10 +16,70 @@ #ifndef __ACPI_DEVICE_H #define __ACPI_DEVICE_H +#define ACPI_DESCRIPTOR_LARGE (1 << 7) +#define ACPI_DESCRIPTOR_INTERRUPT (ACPI_DESCRIPTOR_LARGE | 9) + struct device; const char *acpi_device_name(struct device *dev); const char *acpi_device_path(struct device *dev); const char *acpi_device_scope(struct device *dev); const char *acpi_device_path_join(struct device *dev, const char *name); +/* + * ACPI Descriptor for extended Interrupt() + */ + +enum irq_mode { + IRQ_EDGE_TRIGGERED, + IRQ_LEVEL_TRIGGERED +}; + +enum irq_polarity { + IRQ_ACTIVE_LOW, + IRQ_ACTIVE_HIGH, + IRQ_ACTIVE_BOTH +}; + +enum irq_shared { + IRQ_EXCLUSIVE, + IRQ_SHARED +}; + +enum irq_wake { + IRQ_NO_WAKE, + IRQ_WAKE +}; + +struct acpi_irq { + unsigned int pin; + enum irq_mode mode; + enum irq_polarity polarity; + enum irq_shared shared; + enum irq_wake wake; +}; + +#define IRQ_EDGE_LOW(x) { \ + .pin = (x), \ + .mode = IRQ_EDGE_TRIGGERED, \ + .polarity = IRQ_ACTIVE_LOW, \ + .shared = IRQ_EXCLUSIVE, \ + .wake = IRQ_NO_WAKE } + +#define IRQ_EDGE_HIGH(x) { \ + .pin = (x), \ + .mode = IRQ_EDGE_TRIGGERED, \ + .polarity = IRQ_ACTIVE_HIGH, \ + .shared = IRQ_EXCLUSIVE, \ + .wake = IRQ_NO_WAKE } + +#define IRQ_LEVEL_LOW(x) { \ + .pin = (x), \ + .mode = IRQ_LEVEL_TRIGGERED, \ + .polarity = IRQ_ACTIVE_LOW, \ + .shared = IRQ_SHARED, \ + .wake = IRQ_NO_WAKE } + +/* Write extended Interrupt() descriptor to SSDT AML output */ +void acpi_device_write_interrupt(const struct acpi_irq *irq); + #endif