/* * Copyright Meta Platforms, Inc. and its affiliates. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include unsigned int sw_irq_number = (unsigned int)(-1); static volatile bool custom_init_called; static volatile bool custom_enable_called; static volatile bool custom_disable_called; static volatile bool custom_set_priority_called; static volatile bool custom_eoi_called; static volatile bool irq_handler_called; /* Define out custom SoC interrupt controller interface methods. * These closely match the normal Cortex-M implementations. */ #define NUM_IRQS_PER_REG 32 #define REG_FROM_IRQ(irq) (irq / NUM_IRQS_PER_REG) #define BIT_FROM_IRQ(irq) (irq % NUM_IRQS_PER_REG) void z_soc_irq_init(void) { int irq = 0; for (; irq < CONFIG_NUM_IRQS; irq++) { NVIC_SetPriority((IRQn_Type)irq, _IRQ_PRIO_OFFSET); } custom_init_called = true; } void z_soc_irq_enable(unsigned int irq) { if (irq == sw_irq_number) { custom_enable_called = true; } NVIC_EnableIRQ((IRQn_Type)irq); } void z_soc_irq_disable(unsigned int irq) { if (irq == sw_irq_number) { custom_disable_called = true; } NVIC_DisableIRQ((IRQn_Type)irq); } int z_soc_irq_is_enabled(unsigned int irq) { return NVIC->ISER[REG_FROM_IRQ(irq)] & BIT(BIT_FROM_IRQ(irq)); } void z_soc_irq_eoi(unsigned int irq) { if (irq == sw_irq_number) { custom_eoi_called = true; } } inline __attribute__((always_inline)) unsigned int z_soc_irq_get_active(void) { return __get_IPSR(); } void z_soc_irq_priority_set(unsigned int irq, unsigned int prio, uint32_t flags) { if (irq == sw_irq_number) { custom_set_priority_called = true; } if (IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) && (flags & IRQ_ZERO_LATENCY)) { prio = _EXC_ZERO_LATENCY_IRQS_PRIO; } else { prio += _IRQ_PRIO_OFFSET; } NVIC_SetPriority((IRQn_Type)irq, prio); } void arm_isr_handler(const void *args) { ARG_UNUSED(args); #if defined(CONFIG_CPU_CORTEX_M) && defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING) /* Clear Floating Point Status and Control Register (FPSCR), * to prevent from having the interrupt line set to pending again, * in case FPU IRQ is selected by the test as "Available IRQ line" */ #if defined(CONFIG_ARMV8_1_M_MAINLINE) /* * For ARMv8.1-M with FPU, the FPSCR[18:16] LTPSIZE field must be set * to 0b100 for "Tail predication not applied" as it's reset value */ __set_FPSCR(4 << FPU_FPDSCR_LTPSIZE_Pos); #else __set_FPSCR(0); #endif #endif /* IRQ numbers are offset by 16 on Cortex-M. */ unsigned int this_irq = z_soc_irq_get_active() - 16; TC_PRINT("Got IRQ: %u\n", this_irq); zassert_equal(this_irq, sw_irq_number, "Unexpected active IRQ\n"); irq_handler_called = true; } /** * @brief Test custom interrupt controller handling with CONFIG_ARM_CUSTOM_INTERRUPT_CONTROLLER. * @addtogroup kernel_interrupt_tests * @ingroup all_tests * @{ */ ZTEST(arm_custom_interrupt, test_arm_interrupt) { zassert_true(custom_init_called, "Custom IRQ init not called\n"); /* Determine an NVIC IRQ line that is not currently in use. */ int i; for (i = CONFIG_NUM_IRQS - 1; i >= 0; i--) { if (NVIC_GetEnableIRQ(i) == 0) { /* * Interrupts configured statically with IRQ_CONNECT(.) * are automatically enabled. NVIC_GetEnableIRQ() * returning false, here, implies that the IRQ line is * either not implemented or it is not enabled, thus, * currently not in use by Zephyr. */ /* Set the NVIC line to pending. */ NVIC_SetPendingIRQ(i); if (NVIC_GetPendingIRQ(i)) { /* If the NVIC line is pending, it is * guaranteed that it is implemented; clear the * line. */ NVIC_ClearPendingIRQ(i); if (!NVIC_GetPendingIRQ(i)) { /* * If the NVIC line can be successfully * un-pended, it is guaranteed that it * can be used for software interrupt * triggering. */ break; } } } } zassert_true(i >= 0, "No available IRQ line to use in the test\n"); TC_PRINT("Available IRQ line: %u\n", i); sw_irq_number = i; zassert_false(custom_set_priority_called, "Custom set priority flag set\n"); arch_irq_connect_dynamic(sw_irq_number, 0 /* highest priority */, arm_isr_handler, NULL, 0); zassert_true(custom_set_priority_called, "Custom set priority not called\n"); NVIC_ClearPendingIRQ(i); zassert_false(arch_irq_is_enabled(sw_irq_number), "SW IRQ already enabled\n"); zassert_false(custom_enable_called, "Custom IRQ enable flag is set\n"); irq_enable(sw_irq_number); zassert_true(custom_enable_called, "Custom IRQ enable not called\n"); zassert_true(arch_irq_is_enabled(sw_irq_number), "SW IRQ is not enabled\n"); for (int j = 1; j <= 3; j++) { custom_eoi_called = false; irq_handler_called = false; custom_set_priority_called = false; /* Set the dynamic IRQ to pending state. */ NVIC_SetPendingIRQ(i); /* * Instruction barriers to make sure the NVIC IRQ is * set to pending state before 'test_flag' is checked. */ barrier_dsync_fence_full(); barrier_isync_fence_full(); /* Returning here implies the thread was not aborted. */ /* Confirm test flag is set by the ISR handler. */ zassert_true(custom_eoi_called, "Custom EOI handler not called\n"); zassert_true(irq_handler_called, "ISR handler not called\n"); } zassert_false(custom_disable_called, "Custom IRQ disable flag is set\n"); irq_disable(sw_irq_number); zassert_true(custom_disable_called, "Custom IRQ disable not called\n"); } /** * @} */