diff --git a/arch/arm64/src/common/arm64_arch.h b/arch/arm64/src/common/arm64_arch.h index 4b426c1798b..abf7a472d69 100644 --- a/arch/arm64/src/common/arm64_arch.h +++ b/arch/arm64/src/common/arm64_arch.h @@ -207,12 +207,14 @@ #define ICC_EOIR0_EL1 S3_0_C12_C8_1 #define ICC_EOIR1_EL1 S3_0_C12_C12_1 #define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_DIR_EL1 S3_0_C12_C11_1 /* register constants */ #define ICC_SRE_ELX_SRE_BIT BIT(0) #define ICC_SRE_ELX_DFB_BIT BIT(1) #define ICC_SRE_ELX_DIB_BIT BIT(2) #define ICC_SRE_EL3_EN_BIT BIT(3) +#define ICC_CTLR_EOIMODE_BIT BIT(1) /* ICC SGI macros */ #define SGIR_TGT_MASK (0xffff) diff --git a/arch/arm64/src/common/arm64_gic.h b/arch/arm64/src/common/arm64_gic.h index 4467eaaee2e..43eec799a5a 100644 --- a/arch/arm64/src/common/arm64_gic.h +++ b/arch/arm64/src/common/arm64_gic.h @@ -86,6 +86,8 @@ #define ICENABLER(base, n) (base + GIC_DIST_ICENABLER + (n) * 4) #define ISPENDR(base, n) (base + GIC_DIST_ISPENDR + (n) * 4) #define ICPENDR(base, n) (base + GIC_DIST_ICPENDR + (n) * 4) +#define ISACTIVER(base, n) (base + GIC_DIST_ISACTIVER + (n) * 4) +#define ICACTIVER(base, n) (base + GIC_DIST_ICACTIVER + (n) * 4) #define IPRIORITYR(base, n) (base + GIC_DIST_IPRIORITYR + n) #define ITARGETSR(base, n) (base + GIC_DIST_ITARGETSR + (n) * 4) #define ICFGR(base, n) (base + GIC_DIST_ICFGR + (n) * 4) diff --git a/arch/arm64/src/common/arm64_gicv3.c b/arch/arm64/src/common/arm64_gicv3.c index 51d828ab2bf..321986fe02e 100644 --- a/arch/arm64/src/common/arm64_gicv3.c +++ b/arch/arm64/src/common/arm64_gicv3.c @@ -482,11 +482,13 @@ static void gicv3_rdist_enable(unsigned long rdist) static void gicv3_cpuif_init(void) { - uint32_t icc_sre; + uint64_t icc_sre; + uint64_t icc_ctrl; uint32_t intid; unsigned long base = gic_get_rdist() + GICR_SGI_BASE_OFF; + bool eoi_mode; - /* Disable all sgi ppi */ + /* Disable all SGI and PPI interrupts */ putreg32(BIT64_MASK(GIC_NUM_INTR_PER_REG), ICENABLER(base, 0)); @@ -494,13 +496,61 @@ static void gicv3_cpuif_init(void) gic_wait_rwp(0); - /* Configure all SGIs/PPIs as G1S or G1NS depending on Zephyr - * is run in EL1S or EL1NS respectively. - * All interrupts will be delivered as irq + /* Clear pending and active SGI and PPI interrupts at GIC */ + + putreg32(BIT64_MASK(GIC_NUM_INTR_PER_REG), ICPENDR(base, 0)); + putreg32(BIT64_MASK(GIC_NUM_INTR_PER_REG), ICACTIVER(base, 0)); + + /* Configure all SGIs/PPIs as G1S or G1NS depending on NuttX is run in + * EL1S or EL1NS respectively. All interrupts will be delivered as irq. */ putreg32(IGROUPR_SGI_VAL, IGROUPR(base, 0)); - putreg32(BIT64_MASK(GIC_NUM_INTR_PER_REG), IGROUPMODR(base, 0)); + putreg32(0, IGROUPMODR(base, 0)); + + /* Clear any active IRQs in the CPU interface */ + + icc_ctrl = read_sysreg(ICC_CTLR_EL1); + UP_DSB(); + eoi_mode = (icc_ctrl & ICC_CTLR_EOIMODE_BIT) != 0; + + /* If the SW has crashed / warm-rebooted right after reading the + * ICC_IAR1_EL1, but before acking it in EOIR1, the irq is still + * active, but not any longer readable from the IAR1. + * + * Just ack all IRQs to clear such state. Invalid writes are ignored by + * the architecture. + */ + + for (intid = 0; intid < NR_IRQS; intid++) + { +#ifdef CONFIG_ARM64_DECODEFIQ + write_sysreg(intid, ICC_EOIR0_EL1); +#endif + write_sysreg(intid, ICC_EOIR1_EL1); + + if (eoi_mode) + { + write_sysreg(intid, ICC_DIR_EL1); + } + } + + /* Now, if the SW has crashed / warm rebooted when interrupts are + * activated, but before reading the ICC_IAR1_EL1, disable the + * interrupts normally + */ + + while ((intid = arm64_gic_get_active_irq()) < NR_IRQS) + { +#ifdef CONFIG_ARM64_DECODEFIQ + write_sysreg(intid, ICC_EOIR0_EL1); +#endif + write_sysreg(intid, ICC_EOIR1_EL1); + if (eoi_mode) + { + write_sysreg(intid, ICC_DIR_EL1); + } + } /* Configure default priorities for SGI 0:15 and PPI 0:15. */ @@ -517,7 +567,6 @@ static void gicv3_cpuif_init(void) /* 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); @@ -590,22 +639,23 @@ static void gicv3_dist_init(void) /* Disable interrupt */ - putreg32(BIT64_MASK(GIC_NUM_INTR_PER_REG), - ICENABLER(base, idx)); + putreg32(BIT64_MASK(GIC_NUM_INTR_PER_REG), ICENABLER(base, idx)); - /* Clear pending */ + /* Wait for rwp on GICD */ + + gic_wait_rwp(intid); + + /* Clear pending and active SPIs */ + + putreg32(BIT64_MASK(GIC_NUM_INTR_PER_REG), ICPENDR(base, idx)); + putreg32(BIT64_MASK(GIC_NUM_INTR_PER_REG), ICACTIVER(base, idx)); + + /* Configure groups to default values */ - putreg32(BIT64_MASK(GIC_NUM_INTR_PER_REG), - ICPENDR(base, idx)); putreg32(IGROUPR_VAL, IGROUPR(base, idx)); - putreg32(BIT64_MASK(GIC_NUM_INTR_PER_REG), - IGROUPMODR(base, idx)); + putreg32(0, 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;