blob: 97d3c55365d59c101a0034c9f56ca63a77b5c55e [file] [log] [blame]
/*
* Copyright Meta Platforms, Inc. and its affiliates.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ztest.h>
#include <zephyr/arch/cpu.h>
#include <cmsis_core.h>
#include <zephyr/sys/barrier.h>
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");
}
/**
* @}
*/