mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-08-27 05:15:22 +00:00
Add basic driver for GIC V3 interrupt controller. This implementation supports - distributor, re-distributor and cpu interface initialization - configuration and handling of SPI, PPI and SGI. - V2 Legacy mode is not supported and uses system interface. Current implementation supports GIC secure state only. All interrupts are routed to Secure EL1 as 'irq' by configuring them as Group1 Secure. TODO: - MPIDR based affinity routing setting. - percpu redistributor probe - message based SPI and SGI generation api - EL1NS support. Legacy mode support. - LPI/ITS is not supported. Signed-off-by: Sandeep Tripathy <sandeep.tripathy@broadcom.com>
255 lines
6.1 KiB
C
255 lines
6.1 KiB
C
/*
|
|
* Copyright 2020 Broadcom
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <sw_isr_table.h>
|
|
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
|
#include <drivers/interrupt_controller/gic.h>
|
|
#include "intc_gic_common_priv.h"
|
|
#include "intc_gicv3_priv.h"
|
|
|
|
/* Redistributor base addresses for each core */
|
|
mem_addr_t gic_rdists[GIC_NUM_CPU_IF];
|
|
|
|
/*
|
|
* Wait for register write pending
|
|
* TODO: add timed wait
|
|
*/
|
|
static int gic_wait_rwp(u32_t intid)
|
|
{
|
|
u32_t rwp_mask;
|
|
mem_addr_t base;
|
|
|
|
if (intid < GIC_SPI_INT_BASE) {
|
|
base = (GIC_GET_RDIST(GET_CPUID) + GICR_CTLR);
|
|
rwp_mask = BIT(GICR_CTLR_RWP);
|
|
} else {
|
|
base = GICD_CTLR;
|
|
rwp_mask = BIT(GICD_CTLR_RWP);
|
|
}
|
|
|
|
while (sys_read32(base) & rwp_mask)
|
|
;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void arm_gic_irq_set_priority(unsigned int intid,
|
|
unsigned int prio, u32_t flags)
|
|
{
|
|
u32_t mask = BIT(intid & (GIC_NUM_INTR_PER_REG - 1));
|
|
u32_t idx = intid / GIC_NUM_INTR_PER_REG;
|
|
u32_t shift;
|
|
u32_t val;
|
|
mem_addr_t base = GET_DIST_BASE(intid);
|
|
|
|
/* Disable the interrupt */
|
|
sys_write32(mask, ICENABLER(base, idx));
|
|
gic_wait_rwp(intid);
|
|
|
|
/* PRIORITYR registers provide byte access */
|
|
sys_write8(prio & GIC_PRI_MASK, IPRIORITYR(base, intid));
|
|
|
|
/* Interrupt type config */
|
|
idx = intid / GIC_NUM_CFG_PER_REG;
|
|
shift = (intid & (GIC_NUM_CFG_PER_REG - 1)) * 2;
|
|
|
|
val = sys_read32(ICFGR(base, idx));
|
|
val &= ~(GICD_ICFGR_MASK << shift);
|
|
if (flags & IRQ_TYPE_EDGE) {
|
|
val |= (GICD_ICFGR_TYPE << shift);
|
|
}
|
|
sys_write32(val, ICFGR(base, idx));
|
|
}
|
|
|
|
void arm_gic_irq_enable(unsigned int intid)
|
|
{
|
|
u32_t mask = BIT(intid & (GIC_NUM_INTR_PER_REG - 1));
|
|
u32_t idx = intid / GIC_NUM_INTR_PER_REG;
|
|
|
|
sys_write32(mask, ISENABLER(GET_DIST_BASE(intid), idx));
|
|
}
|
|
|
|
void arm_gic_irq_disable(unsigned int intid)
|
|
{
|
|
u32_t mask = BIT(intid & (GIC_NUM_INTR_PER_REG - 1));
|
|
u32_t idx = intid / GIC_NUM_INTR_PER_REG;
|
|
|
|
sys_write32(mask, ICENABLER(GET_DIST_BASE(intid), idx));
|
|
/* poll to ensure write is complete */
|
|
gic_wait_rwp(intid);
|
|
}
|
|
|
|
bool arm_gic_irq_is_enabled(unsigned int intid)
|
|
{
|
|
u32_t mask = BIT(intid & (GIC_NUM_INTR_PER_REG - 1));
|
|
u32_t idx = intid / GIC_NUM_INTR_PER_REG;
|
|
u32_t val;
|
|
|
|
val = sys_read32(ISENABLER(GET_DIST_BASE(intid), idx));
|
|
|
|
return (val & mask) != 0;
|
|
}
|
|
|
|
unsigned int arm_gic_get_active(void)
|
|
{
|
|
int intid;
|
|
|
|
/* (Pending -> Active / AP) or (AP -> AP) */
|
|
intid = read_sysreg(ICC_IAR1_EL1);
|
|
|
|
return intid;
|
|
}
|
|
|
|
void arm_gic_eoi(unsigned int intid)
|
|
{
|
|
/* (AP -> Pending) Or (Active -> Inactive) or (AP to AP) nested case */
|
|
write_sysreg(intid, ICC_EOIR1_EL1);
|
|
}
|
|
|
|
/*
|
|
* Wake up GIC redistributor.
|
|
* clear ProcessorSleep and wait till ChildAsleep is cleared.
|
|
* ProcessSleep to be cleared only when ChildAsleep is set
|
|
* Check if redistributor is not powered already.
|
|
*/
|
|
static void gicv3_rdist_enable(mem_addr_t rdist)
|
|
{
|
|
if (!(sys_read32(rdist + GICR_WAKER) & BIT(GICR_WAKER_CA)))
|
|
return;
|
|
|
|
sys_clear_bit(rdist + GICR_WAKER, GICR_WAKER_PS);
|
|
while (sys_read32(rdist + GICR_WAKER) & BIT(GICR_WAKER_CA))
|
|
;
|
|
}
|
|
|
|
/*
|
|
* Initialize the cpu interface. This should be called by each core.
|
|
*/
|
|
static void gicv3_cpuif_init(void)
|
|
{
|
|
u32_t icc_sre;
|
|
u32_t intid;
|
|
|
|
mem_addr_t base = gic_rdists[GET_CPUID] + GICR_SGI_BASE_OFF;
|
|
|
|
/* Disable all sgi ppi */
|
|
sys_write32(BIT_MASK(GIC_NUM_INTR_PER_REG), ICENABLER(base, 0));
|
|
/* Any sgi/ppi intid ie. 0-31 will select GICR_CTRL */
|
|
gic_wait_rwp(0);
|
|
|
|
/* Clear pending */
|
|
sys_write32(BIT_MASK(GIC_NUM_INTR_PER_REG), ICPENDR(base, 0));
|
|
|
|
/* Configure all SGIs/PPIs as G1S.
|
|
* TODO: G1S or G1NS dependending on Zephyr is run in EL1S
|
|
* or EL1NS respectively.
|
|
* All interrupts will be delivered as irq
|
|
*/
|
|
sys_write32(0, IGROUPR(base, 0));
|
|
sys_write32(BIT_MASK(GIC_NUM_INTR_PER_REG), IGROUPMODR(base, 0));
|
|
|
|
/*
|
|
* Configure default priorities for SGI 0:15 and PPI 0:15.
|
|
*/
|
|
for (intid = 0; intid < GIC_SPI_INT_BASE;
|
|
intid += GIC_NUM_PRI_PER_REG) {
|
|
sys_write32(GIC_INT_DEF_PRI_X4, IPRIORITYR(base, intid));
|
|
}
|
|
|
|
/* Configure PPIs as level triggered */
|
|
sys_write32(0, ICFGR(base, 1));
|
|
|
|
/*
|
|
* Check if system interface can be enabled.
|
|
* 'icc_sre_el3' needs to be configured at 'EL3'
|
|
* to allow access to 'icc_sre_el1' at 'EL1'
|
|
* eg: z_arch_el3_plat_init can be used by platform.
|
|
*/
|
|
icc_sre = read_sysreg(ICC_SRE_EL1);
|
|
|
|
if (!(icc_sre & ICC_SRE_ELx_SRE)) {
|
|
icc_sre = (icc_sre | ICC_SRE_ELx_SRE |
|
|
ICC_SRE_ELx_DIB | ICC_SRE_ELx_DFB);
|
|
write_sysreg(icc_sre, ICC_SRE_EL1);
|
|
icc_sre = read_sysreg(ICC_SRE_EL1);
|
|
|
|
assert(icc_sre & ICC_SRE_ELx_SRE);
|
|
}
|
|
|
|
write_sysreg(GIC_IDLE_PRIO, ICC_PMR_EL1);
|
|
|
|
/* Allow group1 interrupts */
|
|
write_sysreg(1, ICC_IGRPEN1_EL1);
|
|
}
|
|
|
|
/*
|
|
* TODO: Consider Zephyr in EL1NS.
|
|
*/
|
|
static void gicv3_dist_init(void)
|
|
{
|
|
unsigned int num_ints;
|
|
unsigned int intid;
|
|
unsigned int idx;
|
|
mem_addr_t base = GIC_DIST_BASE;
|
|
|
|
num_ints = sys_read32(GICD_TYPER);
|
|
num_ints &= GICD_TYPER_ITLINESNUM_MASK;
|
|
num_ints = (num_ints + 1) << 5;
|
|
|
|
/*
|
|
* Default configuration of all SPIs
|
|
*/
|
|
for (intid = GIC_SPI_INT_BASE; intid < num_ints;
|
|
intid += GIC_NUM_INTR_PER_REG) {
|
|
idx = intid / GIC_NUM_INTR_PER_REG;
|
|
/* Disable interrupt */
|
|
sys_write32(BIT_MASK(GIC_NUM_INTR_PER_REG),
|
|
ICENABLER(base, idx));
|
|
/* Clear pending */
|
|
sys_write32(BIT_MASK(GIC_NUM_INTR_PER_REG),
|
|
ICPENDR(base, idx));
|
|
/* All SPIs are G1S and owned by Zephyr */
|
|
sys_write32(0, IGROUPR(base, idx));
|
|
sys_write32(BIT_MASK(GIC_NUM_INTR_PER_REG),
|
|
IGROUPMODR(base, idx));
|
|
|
|
}
|
|
/* wait for rwp on GICD */
|
|
gic_wait_rwp(GIC_SPI_INT_BASE);
|
|
|
|
/* Configure default priorities for all SPIs. */
|
|
for (intid = GIC_SPI_INT_BASE; intid < num_ints;
|
|
intid += GIC_NUM_PRI_PER_REG) {
|
|
sys_write32(GIC_INT_DEF_PRI_X4, IPRIORITYR(base, intid));
|
|
}
|
|
|
|
/* Configure all SPIs as active low, level triggered by default */
|
|
for (intid = GIC_SPI_INT_BASE; intid < num_ints;
|
|
intid += GIC_NUM_CFG_PER_REG) {
|
|
idx = intid / GIC_NUM_CFG_PER_REG;
|
|
sys_write32(0, ICFGR(base, idx));
|
|
}
|
|
|
|
/* enable Group 1 secure interrupts */
|
|
sys_set_bit(GICD_CTLR, GICD_CTLR_ENABLE_G1S);
|
|
}
|
|
|
|
/* TODO: add arm_gic_secondary_init() for multicore support */
|
|
int arm_gic_init(void)
|
|
{
|
|
gicv3_dist_init();
|
|
|
|
/* Fixme: populate each redistributor */
|
|
gic_rdists[0] = GIC_RDIST_BASE;
|
|
|
|
gicv3_rdist_enable(GIC_GET_RDIST(GET_CPUID));
|
|
|
|
gicv3_cpuif_init();
|
|
|
|
return 0;
|
|
}
|