summaryrefslogtreecommitdiff
path: root/src/arch/x86
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/x86')
-rw-r--r--src/arch/x86/acpi_device.c75
-rw-r--r--src/arch/x86/include/arch/acpi_device.h60
2 files changed, 135 insertions, 0 deletions
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 <device/device.h>
#include <device/path.h>
+/* 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