Unifying External Interrupt System (#51)
[lunaix-os.git] / lunaix-os / arch / x86 / hal / apic.c
index ddc2e31ef2471557122b8b6ddb4ab11d0b7b68bd..8e9a01367b7feef10654ffc2ae554ef55be3b12a 100644 (file)
 
 #include "asm/x86.h"
 #include "asm/x86_cpu.h"
 
 #include "asm/x86.h"
 #include "asm/x86_cpu.h"
-
 #include "asm/soc/apic.h"
 #include "asm/soc/apic.h"
+
 #include <asm/hart.h>
 
 #include <lunaix/mm/mmio.h>
 #include <lunaix/spike.h>
 #include <lunaix/syslog.h>
 #include <asm/hart.h>
 
 #include <lunaix/mm/mmio.h>
 #include <lunaix/spike.h>
 #include <lunaix/syslog.h>
+#include <lunaix/device.h>
+
+#include <hal/irq.h>
+#include <hal/acpi/acpi.h>
 
 #include "pic.h"
 
 LOG_MODULE("APIC")
 
 
 #include "pic.h"
 
 LOG_MODULE("APIC")
 
+#define IOAPIC_IOREGSEL 0x00
+#define IOAPIC_IOWIN 0x10
+#define IOAPIC_IOREDTBL_BASE 0x10
+
+#define IOAPIC_REG_ID 0x00
+#define IOAPIC_REG_VER 0x01
+#define IOAPIC_REG_ARB 0x02
+
+#define IOAPIC_DELMOD_FIXED 0b000
+#define IOAPIC_DELMOD_LPRIO 0b001
+#define IOAPIC_DELMOD_NMI 0b100
+
+#define IOAPIC_MASKED (1 << 16)
+#define IOAPIC_TRIG_LEVEL (1 << 15)
+#define IOAPIC_INTPOL_L (1 << 13)
+#define IOAPIC_DESTMOD_LOGIC (1 << 11)
+
+#define IOAPIC_BASE_VADDR 0x2000
+
+#define IOAPIC_REG_SEL *((volatile u32_t*)(_ioapic_base + IOAPIC_IOREGSEL))
+#define IOAPIC_REG_WIN *((volatile u32_t*)(_ioapic_base + IOAPIC_IOWIN))
+
+
+#define LVT_ENTRY_LINT0(vector) (LVT_DELIVERY_FIXED | vector)
+
+// Pin LINT#1 is configured for relaying NMI, but we masked it here as I think
+//  it is too early for that
+// LINT#1 *must* be edge trigged (Intel manual vol3. 10-14)
+#define LVT_ENTRY_LINT1 (LVT_DELIVERY_NMI | LVT_MASKED | LVT_TRIGGER_EDGE)
+#define LVT_ENTRY_ERROR(vector) (LVT_DELIVERY_FIXED | vector)
+
+
+static volatile ptr_t _ioapic_base;
 static volatile ptr_t _apic_base;
 
 void
 static volatile ptr_t _apic_base;
 
 void
-apic_setup_lvts();
+apic_write_reg(unsigned int reg, unsigned int val)
+{
+    *(unsigned int*)(_apic_base + reg) = val;
+}
 
 void
 
 void
+apic_ack_interrupt(irq_t irq)
+{
+    *(unsigned int*)(_apic_base + APIC_EOI) = 0;
+}
+
+unsigned int
+apic_read_reg(unsigned int reg)
+{
+    return *(unsigned int*)(_apic_base + (reg));
+}
+
+
+static void
+apic_setup_lvts()
+{
+    apic_write_reg(APIC_LVT_LINT0, LVT_ENTRY_LINT0(APIC_LINT0_IV));
+    apic_write_reg(APIC_LVT_LINT1, LVT_ENTRY_LINT1);
+    apic_write_reg(APIC_LVT_ERROR, LVT_ENTRY_ERROR(APIC_ERROR_IV));
+}
+
+static void
 apic_init()
 {
     // ensure that external interrupt is disabled
 apic_init()
 {
     // ensure that external interrupt is disabled
@@ -85,40 +146,109 @@ apic_init()
     apic_write_reg(APIC_SPIVR, spiv);
 }
 
     apic_write_reg(APIC_SPIVR, spiv);
 }
 
-#define LVT_ENTRY_LINT0(vector) (LVT_DELIVERY_FIXED | vector)
+static void
+ioapic_init()
+{
+    acpi_context* acpi_ctx = acpi_get_context();
 
 
-// Pin LINT#1 is configured for relaying NMI, but we masked it here as I think
-//  it is too early for that
-// LINT#1 *must* be edge trigged (Intel manual vol3. 10-14)
-#define LVT_ENTRY_LINT1 (LVT_DELIVERY_NMI | LVT_MASKED | LVT_TRIGGER_EDGE)
-#define LVT_ENTRY_ERROR(vector) (LVT_DELIVERY_FIXED | vector)
+    _ioapic_base =
+        ioremap(acpi_ctx->madt.ioapic->ioapic_addr & ~0xfff, 4096);
+}
 
 
-void
-apic_setup_lvts()
+static void
+ioapic_write(u8_t sel, u32_t val)
 {
 {
-    apic_write_reg(APIC_LVT_LINT0, LVT_ENTRY_LINT0(APIC_LINT0_IV));
-    apic_write_reg(APIC_LVT_LINT1, LVT_ENTRY_LINT1);
-    apic_write_reg(APIC_LVT_ERROR, LVT_ENTRY_ERROR(APIC_ERROR_IV));
+    IOAPIC_REG_SEL = sel;
+    IOAPIC_REG_WIN = val;
 }
 
 }
 
-void
-apic_on_eoi(struct x86_intc* intc_ctx, cpu_t cpu, int iv)
+static u32_t
+ioapic_read(u8_t sel)
+{
+    IOAPIC_REG_SEL = sel;
+    return IOAPIC_REG_WIN;
+}
+
+static void
+__ioapic_install_line(struct irq_domain *domain, irq_t irq)
+{
+    struct irq_line_wire *line;
+    u8_t reg_sel;
+    u32_t ioapic_fg;
+
+    line = irq->line;
+    reg_sel = IOAPIC_IOREDTBL_BASE + line->domain_mapped * 2;
+    ioapic_fg = IOAPIC_DELMOD_FIXED;
+
+    // Write low 32 bits
+    ioapic_write(reg_sel, (irq->vector | ioapic_fg) & 0x1FFFF);
+
+    // Write high 32 bits
+    ioapic_write(reg_sel + 1, 0);
+}
+
+static int
+__ioapic_translate_irq(struct irq_domain *domain, irq_t irq, void *irq_extra)
 {
 {
-    // for all external interrupts except the spurious interrupt
-    //  this is required by Intel Manual Vol.3A, section 10.8.1 & 10.8.5
-    if (iv >= IV_EX_BEGIN && iv != APIC_SPIV_IV) {
-        *(unsigned int*)(_apic_base + APIC_EOI) = 0;
+    struct irq_line_wire *line;
+
+    if (irq->type == IRQ_LINE) {
+        line = irq->line;
+        line->domain_mapped = acpi_gsimap(line->domain_local);
     }
     }
+
+    return 0;
 }
 
 }
 
-unsigned int
-apic_read_reg(unsigned int reg)
+static int
+__ioapic_install_irq(struct irq_domain *domain, irq_t irq)
 {
 {
-    return *(unsigned int*)(_apic_base + (reg));
+    if (irq->vector == IRQ_VECTOR_UNSET) {
+        irq->vector = btrie_map(&domain->irq_map, IV_EX_BEGIN, IV_EX_END, irq);
+    }
+    else {
+        irq_record(domain, irq);
+    }
+
+    if (irq->type == IRQ_MESSAGE) {
+        irq->msi->wr_addr = __APIC_BASE_PADDR;
+    }
+    else {
+        __ioapic_install_line(domain, irq);
+    }
+
+    return 0;
 }
 
 }
 
-void
-apic_write_reg(unsigned int reg, unsigned int val)
+
+static struct irq_domain_ops apic_domain_ops = {
+    .map_irq = __ioapic_translate_irq,
+    .install_irq = __ioapic_install_irq
+};
+
+static int
+apic_device_create(struct device_def* def, morph_t* morph)
 {
 {
-    *(unsigned int*)(_apic_base + reg) = val;
-}
\ No newline at end of file
+    int err;
+    struct device* dev;
+    struct irq_domain* domain;
+
+    apic_init();
+    ioapic_init();
+
+    dev = device_allocsys(NULL, NULL);
+    domain = irq_create_domain(dev, &apic_domain_ops);
+
+    irq_set_default_domain(domain);
+    register_device(dev, &def->class, "apic");
+    irq_attach_domain(NULL, domain);
+    
+    return 0;
+}
+
+static struct device_def apic_devdef = {
+    def_device_class(INTEL, CFG, INTC),
+    def_device_name("x86 APIC"),
+    def_on_create(apic_device_create)
+};
+EXPORT_DEVICE(x86_apic, &apic_devdef, load_sysconf);
\ No newline at end of file