/* * 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 */ #include #include #include #include /* distributor registers, offsets from the GICD base */ #define GICD_CTLR 0x000 #define GICD_TYPER 0x004 #define GICD_IGROUPR(n) (0x080 + (n) * 4) #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); /* * every interrupt in group 1, the non-secure group. the * kernel does not see group 0 interrupts on non-secure * hardware, and a bootloader that leaves any line in the * secure group strands it. */ for (n = 0; n < lines; n++) writel(0xffffffff, REG32(gicd + GICD_IGROUPR(n))); /* 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; }