summaryrefslogtreecommitdiff
path: root/arch/arm64/lib/gic.c
blob: 647e987a618a243c560655f3c1a36c4cbf85da12 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/*
 * gic.c - the interrupt controller state a bootloader owns. the
 * kernel programs the gic itself for the running system, but it
 * trusts the state it inherits: on real hardware the secure
 * world configures which interrupts are visible to non-secure,
 * and a bootloader that leaves random enables or secure group
 * bits set hands the kernel a half-configured distributor that
 * can fire before the kernel's irqchip driver is up.
 *
 * this is the gicv2 sequence from the TRM, the same shape
 * u-boot leaves the machine in: distributor off, every
 * interrupt in the non-secure group, all per interrupt enables
 * cleared, pending state cleared, cpu interfaces off. defined
 * state, nothing firing, the kernel starts from zero.
 *
 * GICv1 shows the same register map minus the security
 * extension registers, the writes below are harmless there.
 *
 * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org>
 */

#include <string.h>
#include <stdint.h>
#include <boot.h>
#include <reg.h>

/* distributor registers, offsets from the GICD base */
#define GICD_CTLR		0x000
#define GICD_TYPER		0x004
#define GICD_ISENABLER(n)	(0x100 + (n) * 4)
#define GICD_ICENABLER(n)	(0x180 + (n) * 4)
#define GICD_ICPENDR(n)		(0x280 + (n) * 4)
#define GICD_ICACTIVER(n)	(0x380 + (n) * 4)

/* cpu interface registers, offsets from the GICC base */
#define GICC_CTLR		0x000
#define GICC_PMR		0x004

/* GICD_CTLR bits */
#define GICD_CTLR_ENABLE_GRP1	(1 << 0)
#define GICD_CTLR_ENABLE_GRP0	(1 << 1)

/* GICC_CTLR bits */
#define GICC_CTLR_ENABLE	(1 << 0)

#define GICD_TYPER_ITLINES_MASK	0x1f

/*
 * how many 32-irq lines the distributor carries, TYPER.ITLines
 * holds count of (irqs / 32) - 1, clamped per the spec because
 * the field is 5 bits and caps at 1020 irqs.
 */
static int gicd_irq_lines(uintptr_t gicd)
{
	uint32_t typer = readl(REG32(gicd + GICD_TYPER));

	return ((typer & GICD_TYPER_ITLINES_MASK) + 1);
}

/*
 * leave the gic in the defined state the kernel expects. the
 * addresses come from the devicetree the caller walked, qemu
 * virt carries a gicv2 at 0x08000000 with the cpu interface at
 * +0x10000.
 */
int tb_gic_init(uintptr_t gicd, uintptr_t gicc)
{
	int lines;
	int n;

	if (!gicd || !gicc)
		return -1;

	/* the distributor is off while it is reconfigured */
	writel(0, REG32(gicd + GICD_CTLR));
	writel(0, REG32(gicc + GICC_CTLR));

	lines = gicd_irq_lines(gicd);

	/*
	 * the group routing is deliberately untouched. the group
	 * registers are the secure world's, a non-secure loader's
	 * writes are dropped on hardware that implements the
	 * security extension, and on emulators that accept them
	 * the timer's per cpu interrupts stop reaching the
	 * kernel. group config belongs to the EL3 monitor, this
	 * loader runs without one.
	 */

	/* no per interrupt enables, nothing pending */
	for (n = 0; n < lines; n++) {
		writel(0xffffffff, REG32(gicd + GICD_ICENABLER(n)));
		writel(0xffffffff, REG32(gicd + GICD_ICPENDR(n)));
	}

	/*
	 * the cpu interface stays off with the priority mask at
	 * the lowest priority, the kernel raises it when it
	 * brings its own irq handling up. off is the defined
	 * state, the enable is the kernel's decision to make.
	 */
	writel(0, REG32(gicc + GICC_PMR));

	return 0;
}