xref: /haiku/src/system/kernel/arch/arm/arch_int_gicv2.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
1 /*
2  * Copyright 2021 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 #include <int.h>
6 #include <interrupt_controller.h>
7 #include <kernel.h>
8 #include <vm/vm.h>
9 
10 #include "arch_int_gicv2.h"
11 #include "gicv2_regs.h"
12 
13 #define GIC_SPI_IRQ_START	32
14 
15 
16 GICv2InterruptController::GICv2InterruptController(uint32_t gicd_addr, uint32_t gicc_addr)
17 : InterruptController()
18 {
19 	area_id gicd_area = vm_map_physical_memory(B_SYSTEM_TEAM, "intc-gicv2-gicd",
20 		(void**)&fGicdRegs, B_ANY_KERNEL_ADDRESS, GICD_REG_SIZE,
21 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
22 		gicd_addr ? gicd_addr : GICD_REG_START, false);
23 	if (gicd_area < 0) {
24 		panic("not able to map the memory area for gicd\n");
25 	}
26 
27 	area_id gicc_area = vm_map_physical_memory(B_SYSTEM_TEAM, "intc-gicv2-gicc",
28 		(void**)&fGiccRegs, B_ANY_KERNEL_ADDRESS, GICC_REG_SIZE,
29 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
30 		gicc_addr ? gicc_addr : GICC_REG_START, false);
31 	if (gicc_area < 0) {
32 		panic("not able to map the memory area for gicc\n");
33 	}
34 
35 	// disable GICD
36 	fGicdRegs[GICD_REG_CTLR] = 0;
37 
38 	// disable GICC
39 	fGiccRegs[GICC_REG_CTLR] = 0;
40 
41 	// TODO: disable all interrupts
42 	fGicdRegs[GICD_REG_ICENABLER] = 0xffffffff;
43 	fGicdRegs[GICD_REG_ICENABLER+1] = 0xffffffff;
44 
45 	// set PMR and BPR
46 	fGiccRegs[GICC_REG_PMR] = 0xff;
47 	fGiccRegs[GICC_REG_BPR] = 0x07;
48 
49 	// enable GICC
50 	fGiccRegs[GICC_REG_CTLR] = 0x03;
51 
52 	// enable GICD
53 	fGicdRegs[GICD_REG_CTLR] = 0x03;
54 }
55 
56 
57 void GICv2InterruptController::EnableInterrupt(int irq)
58 {
59 	irq += GIC_SPI_IRQ_START;
60 
61 	uint32_t ena_reg = GICD_REG_ISENABLER + irq / 32;
62 	uint32_t ena_val = 1 << (irq % 32);
63 	fGicdRegs[ena_reg] = ena_val;
64 
65 	uint32_t prio_reg = GICD_REG_IPRIORITYR + irq / 4;
66 	uint32_t prio_val = fGicdRegs[prio_reg];
67 	prio_val |= 0x80 << (irq % 4 * 8);
68 	fGicdRegs[prio_reg] = prio_val;
69 }
70 
71 
72 void GICv2InterruptController::DisableInterrupt(int irq)
73 {
74 	irq += GIC_SPI_IRQ_START;
75 	fGicdRegs[GICD_REG_ICENABLER + irq / 32] = 1 << (irq % 32);
76 }
77 
78 
79 void GICv2InterruptController::HandleInterrupt()
80 {
81 	uint32_t iar = fGiccRegs[GICC_REG_IAR];
82 	uint32_t irqnr = iar & 0x3FF;
83 	if ((irqnr == 1022) || (irqnr == 1023)) {
84 		dprintf("spurious interrupt\n");
85 	} else {
86 		int_io_interrupt_handler(irqnr-GIC_SPI_IRQ_START, true);
87 	}
88 
89 	fGiccRegs[GICC_REG_EOIR] = iar;
90 }
91