-
Notifications
You must be signed in to change notification settings - Fork 6.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tests: kernel: interrupt: Add testcase for shared interrupts
This commit introduces a new testcase for shared interrupts. Signed-off-by: Laurentiu Mihalcea <[email protected]>
- Loading branch information
1 parent
5a8a285
commit 315465e
Showing
3 changed files
with
317 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,307 @@ | ||
/* | ||
* Copyright 2023 NXP | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include <zephyr/ztest.h> | ||
#include <zephyr/interrupt_util.h> | ||
|
||
#define GIC_IRQ_LINE_1 10 | ||
#define GIC_IRQ_LINE_2 11 | ||
#define GIC_IRQ_PRIORITY 0 | ||
#define TEST_VECTOR_SIZE 10 | ||
#define TEST_INVALID_INDEX -1 | ||
|
||
#define ISR_DEFINE(name) \ | ||
static void name(const void *data) \ | ||
{ \ | ||
int idx = POINTER_TO_INT(data); \ | ||
test_vector[idx] = result_vector[idx]; \ | ||
} \ | ||
|
||
static uint32_t test_vector[TEST_VECTOR_SIZE] = { | ||
}; | ||
|
||
static uint32_t result_vector[TEST_VECTOR_SIZE] = { | ||
0xdeadbeef, | ||
0xcafebabe, | ||
0x1234cafe, | ||
}; | ||
|
||
ISR_DEFINE(test_isr_0); | ||
ISR_DEFINE(test_isr_1); | ||
ISR_DEFINE(test_isr_2); | ||
|
||
/* utility function - checks if client ISR/arg found at index idx matches given | ||
* pair. If passed idx is TEST_INVALID_IDX then this function looks through the | ||
* list of all clients for given ISR/arg pair. | ||
*/ | ||
static bool client_exists_at_index(void (*routine)(const void *arg), void *arg, int irq, int idx) | ||
{ | ||
int i; | ||
struct shared_irq_data *irq_data; | ||
|
||
if (idx == TEST_INVALID_INDEX) { | ||
irq_data = &_shared_irq_table[irq]; | ||
|
||
for (i = 0; i < irq_data->client_num; i++) { | ||
if (irq_data->clients[i].routine == routine && irq_data->clients[i].arg == arg) | ||
return true; | ||
} | ||
} else { | ||
if (_shared_irq_table[irq].client_num <= idx) | ||
return false; | ||
|
||
return _shared_irq_table[irq].clients[idx].routine == routine && | ||
_shared_irq_table[irq].clients[idx].arg == arg; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
static void reset_test_vector(void) | ||
{ | ||
int i; | ||
|
||
for (i = 0; i < TEST_VECTOR_SIZE; i++) | ||
test_vector[i] = 0; | ||
} | ||
|
||
#ifdef CONFIG_GIC | ||
static bool static_shared_irq_test(void) | ||
{ | ||
int i; | ||
|
||
IRQ_CONNECT(GIC_IRQ_LINE_1, GIC_IRQ_PRIORITY, test_isr_0, 0, 0); | ||
IRQ_CONNECT(GIC_IRQ_LINE_1, GIC_IRQ_PRIORITY, test_isr_1, (void *)1, 0); | ||
IRQ_CONNECT(GIC_IRQ_LINE_2, GIC_IRQ_PRIORITY, test_isr_2, (void *)2, 0); | ||
|
||
/* note: the order in which the ISR/arg pairs appear in the list of clients | ||
* depends on gen_isr_tables.py. As such, we don't care about the order ATM. | ||
* | ||
* Instead, the order will be verified in the case of dynamic interrupts since | ||
* the algorithm from shared_irq.c will register clients in the order in which | ||
* arch_irq_connect_dynamic() was called. | ||
*/ | ||
if (!client_exists_at_index(test_isr_0, 0, GIC_IRQ_LINE_1, TEST_INVALID_INDEX)) | ||
return false; | ||
|
||
if (!client_exists_at_index(test_isr_1, (void *)1, GIC_IRQ_LINE_1, TEST_INVALID_INDEX)) | ||
return false; | ||
|
||
/* GIC_IRQ_LINE_2 is not shared so we expect the ISR field from | ||
* _sw_isr_table to remain unmodified. | ||
*/ | ||
if (_sw_isr_table[GIC_IRQ_LINE_2].isr != test_isr_2) | ||
return false; | ||
|
||
/* since GIC_IRQ_LINE_1 is being shared, we expect the ISR field | ||
* from _sw_isr_table to be shared_isr. | ||
*/ | ||
if (_sw_isr_table[GIC_IRQ_LINE_1].isr != shared_isr) | ||
return false; | ||
|
||
/* the number of clients for GIC_IRQ_LINE_1 should be 2, while | ||
* the number of clients for GIC_IRQ_LINE_2 should be 0 (since it's | ||
* not being shared). | ||
*/ | ||
if (_shared_irq_table[GIC_IRQ_LINE_1].client_num != 2) | ||
return false; | ||
if (_shared_irq_table[GIC_IRQ_LINE_2].client_num != 0) | ||
return false; | ||
|
||
irq_enable(GIC_IRQ_LINE_1); | ||
irq_enable(GIC_IRQ_LINE_2); | ||
|
||
trigger_irq(GIC_IRQ_LINE_1); | ||
trigger_irq(GIC_IRQ_LINE_2); | ||
|
||
/* wait 5ms before checking the results */ | ||
k_busy_wait(5000); | ||
|
||
for (i = 0; i < TEST_VECTOR_SIZE; i++) { | ||
if (test_vector[i] != result_vector[i]) | ||
return false; | ||
} | ||
|
||
irq_disable(GIC_IRQ_LINE_1); | ||
irq_disable(GIC_IRQ_LINE_2); | ||
|
||
reset_test_vector(); | ||
|
||
/* pass */ | ||
return true; | ||
} | ||
#endif /* CONFIG_GIC */ | ||
|
||
/* note: this function is used to put _sw_isr_table and _shared_irq_table | ||
* into a known state. | ||
*/ | ||
static bool reset_irq_table_state_test(void) | ||
{ | ||
/* at the end of these statements, _sw_isr_table and _shared_irq_table should | ||
* have the following states: | ||
* | ||
* _sw_isr_table[GIC_IRQ_LINE_1].isr = z_irq_spurious | ||
* _sw_isr_table[GIC_IRQ_LINE_1].arg = NULL | ||
* _sw_isr_table[GIC_IRQ_LINE_2].isr = z_irq_spurious | ||
* _sw_isr_table[GIC_IRQ_LINE_2].arg = NULL | ||
* | ||
* _shared_irq_table[GIC_IRQ_LINE_1].client_num = 0 | ||
* _shared_irq_table[GIC_IRQ_LINE_2].client_num = 0 | ||
*/ | ||
arch_irq_disconnect_dynamic(GIC_IRQ_LINE_1, GIC_IRQ_PRIORITY, test_isr_0, 0, 0); | ||
arch_irq_disconnect_dynamic(GIC_IRQ_LINE_1, GIC_IRQ_PRIORITY, test_isr_1, (void *)1, 0); | ||
arch_irq_disconnect_dynamic(GIC_IRQ_LINE_2, GIC_IRQ_PRIORITY, test_isr_2, (void *)2, 0); | ||
|
||
if (_sw_isr_table[GIC_IRQ_LINE_1].isr != z_irq_spurious) | ||
return false; | ||
if (_sw_isr_table[GIC_IRQ_LINE_1].arg != NULL) | ||
return false; | ||
|
||
if (_sw_isr_table[GIC_IRQ_LINE_2].isr != z_irq_spurious) | ||
return false; | ||
if (_sw_isr_table[GIC_IRQ_LINE_2].arg != NULL) | ||
return false; | ||
|
||
if (_shared_irq_table[GIC_IRQ_LINE_1].client_num != 0) | ||
return false; | ||
if (_shared_irq_table[GIC_IRQ_LINE_2].client_num != 0) | ||
return false; | ||
|
||
/* pass */ | ||
return true; | ||
} | ||
|
||
static bool dynamic_shared_irq_test(void) | ||
{ | ||
int irq_line_1, irq_line_2, irq_priority, i; | ||
|
||
#if defined(CONFIG_GIC) | ||
irq_line_1 = GIC_IRQ_LINE_1; | ||
irq_line_2 = GIC_IRQ_LINE_2; | ||
irq_priority = GIC_IRQ_PRIORITY; | ||
#elif defined(CONFIG_CPU_CORTEX_M) | ||
irq_line_1 = get_available_nvic_line(CONFIG_NUM_IRQS); | ||
irq_line_2 = get_available_nvic_line(irq_line_1); | ||
irq_priority = 1; | ||
#else | ||
#error "Unsupported architecture." | ||
#endif | ||
|
||
arch_irq_connect_dynamic(irq_line_1, irq_priority, test_isr_0, 0, 0); | ||
|
||
if (_sw_isr_table[irq_line_1].isr != test_isr_0) | ||
return false; | ||
if (_sw_isr_table[irq_line_1].arg != NULL) | ||
return false; | ||
if (_shared_irq_table[irq_line_1].client_num != 0) | ||
return false; | ||
|
||
arch_irq_connect_dynamic(irq_line_1, irq_priority, test_isr_1, (void *)1, 0); | ||
|
||
if (_sw_isr_table[irq_line_1].isr != shared_isr) | ||
return false; | ||
if (_sw_isr_table[irq_line_1].arg != &_shared_irq_table[irq_line_1]) | ||
return false; | ||
if (_shared_irq_table[irq_line_1].client_num != 2) | ||
return false; | ||
|
||
/* here it's mandatory that test_isr_0/NULL is the first client, while | ||
* test_isr_1/1 is the second client. | ||
*/ | ||
if (!client_exists_at_index(test_isr_0, 0, irq_line_1, 0)) | ||
return 0; | ||
if (!client_exists_at_index(test_isr_1, (void *)1, irq_line_1, 1)) | ||
return 0; | ||
|
||
arch_irq_connect_dynamic(irq_line_2, irq_priority, test_isr_2, (void *)2, 0); | ||
|
||
if (_sw_isr_table[irq_line_2].isr != test_isr_2) | ||
return false; | ||
if (_sw_isr_table[irq_line_2].arg != (void *)2) | ||
return false; | ||
if (_shared_irq_table[irq_line_2].client_num != 0) | ||
return false; | ||
|
||
irq_enable(irq_line_1); | ||
irq_enable(irq_line_2); | ||
|
||
trigger_irq(irq_line_1); | ||
trigger_irq(irq_line_2); | ||
|
||
/* wait 5ms before checking the results */ | ||
k_busy_wait(5000); | ||
|
||
for (i = 0; i < TEST_VECTOR_SIZE; i++) { | ||
if (test_vector[i] != result_vector[i]) | ||
return false; | ||
} | ||
|
||
irq_disable(irq_line_1); | ||
irq_disable(irq_line_2); | ||
|
||
reset_test_vector(); | ||
|
||
/* remove test_isr_0/NULL pair. After this statement we expect | ||
* irq_line_1 to be unshared. | ||
*/ | ||
arch_irq_disconnect_dynamic(irq_line_1, irq_priority, test_isr_0, 0, 0); | ||
|
||
if (_sw_isr_table[irq_line_1].isr != test_isr_1) | ||
return false; | ||
if (_sw_isr_table[irq_line_1].arg != (void *)1) | ||
return false; | ||
if (_shared_irq_table[irq_line_1].client_num != 0) | ||
return false; | ||
|
||
irq_enable(irq_line_1); | ||
trigger_irq(irq_line_1); | ||
|
||
/* wait 5ms before checking the results */ | ||
k_busy_wait(5000); | ||
|
||
for (i = 0; i < TEST_VECTOR_SIZE; i++) { | ||
if (i == 1) { | ||
if (test_vector[i] != result_vector[i]) | ||
return false; | ||
continue; | ||
} | ||
|
||
if (test_vector[i] != 0) | ||
return false; | ||
} | ||
|
||
irq_disable(irq_line_1); | ||
|
||
reset_test_vector(); | ||
|
||
/* pass */ | ||
return true; | ||
} | ||
|
||
ZTEST(interrupt_feature, test_shared_irq) | ||
{ | ||
/* TODO: make this test work on other architectures as well. | ||
* What are the equivalents of the SGIs on the other architectures? | ||
*/ | ||
#ifdef CONFIG_GIC | ||
zassert_true(static_shared_irq_test(), | ||
"failed static shared irq test."); | ||
#endif /* CONFIG_GIC */ | ||
|
||
#ifdef CONFIG_DYNAMIC_INTERRUPTS | ||
|
||
#ifdef CONFIG_GIC | ||
zassert_true(reset_irq_table_state_test(), | ||
"failed reset table state test"); | ||
#endif /* CONFIG_GIC */ | ||
|
||
zassert_true(dynamic_shared_irq_test(), | ||
"failed dynamic irq test."); | ||
|
||
zassert_true(reset_irq_table_state_test(), | ||
"failed reset table state test"); | ||
#endif | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters