1 /*
2 * Copyright (c) 2025 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/tc_util.h>
8 #include <zephyr/ztest.h>
9 #include <zephyr/kernel.h>
10
11 #if (CONFIG_MP_MAX_NUM_CPUS == 1)
12 #error "This test must have at least CONFIG_MP_MAX_NUM_CPUS=2 CPUs"
13 #endif
14
15 /* structs */
16
17 struct test_ipi_work {
18 struct k_ipi_work work;
19
20 volatile unsigned int cpu_bit;
21 };
22
23 /* forward declarations */
24
25 static void timer_func(struct k_timer *tmr);
26
27 /* locals */
28
29 static struct test_ipi_work test_item;
30 static K_SEM_DEFINE(timer_sem, 0, 1);
31 static K_TIMER_DEFINE(timer, timer_func, NULL);
32 static uint32_t timer_target_cpu;
33
34 /**
35 * This function is called when the IPI work item is executed on a CPU.
36 * It sets the CPU ID in the test_item structure to indicate which CPU
37 * executed the work item.
38 *
39 * @param item Pointer to the IPI work item to be executed
40 */
test_function(struct k_ipi_work * item)41 static void test_function(struct k_ipi_work *item)
42 {
43 struct test_ipi_work *my_work;
44 unsigned int cpu;
45
46 cpu = arch_curr_cpu()->id;
47
48 my_work = CONTAINER_OF(item, struct test_ipi_work, work);
49 my_work->cpu_bit = BIT(cpu);
50 }
51
52 /**
53 * Timer function that is called to initiate the IPI work from
54 * an ISR and to wait for the work item to complete.
55 */
timer_func(struct k_timer * tmr)56 static void timer_func(struct k_timer *tmr)
57 {
58 ARG_UNUSED(tmr);
59
60 timer_target_cpu = _current_cpu->id == 0 ? BIT(1) : BIT(0);
61
62 /* Add the work item to the IPI queue, signal and wait */
63
64 k_ipi_work_add(&test_item.work, timer_target_cpu, test_function);
65 k_ipi_work_signal();
66 while (k_ipi_work_wait(&test_item.work, K_NO_WAIT) == -EAGAIN) {
67 }
68
69 /* Wake the thread waiting for the work item to complete */
70 k_sem_give(&timer_sem);
71
72 }
73
74 /**
75 * This test covers the simplest working cases of IPI work
76 * item execution and waiting. It adds a single IPI work item
77 * to another CPU's queue, signals it and waits for it to complete.
78 * Waiting covers two scenarios
79 * 1. From thread level
80 * 2. From a k_timer (ISR).
81 */
ZTEST(ipi_work,test_ipi_work_simple)82 ZTEST(ipi_work, test_ipi_work_simple)
83 {
84 unsigned int key;
85 unsigned int cpu_id;
86 unsigned int target_cpu_mask;
87 int status;
88
89 k_ipi_work_init(&test_item.work);
90
91 /*
92 * Issue the IPI work item from thread level. The current thread will
93 * pend while waiting for work completion. Interrupts are locked to
94 * ensure that the current thread does not change CPUs while setting
95 * up the IPI work item.
96 */
97
98 TC_PRINT("Thread level IPI\n");
99
100 key = arch_irq_lock();
101 cpu_id = arch_curr_cpu()->id;
102 target_cpu_mask = _current_cpu->id == 0 ? BIT(1) : BIT(0);
103
104 test_item.cpu_bit = 0xFFFFFFFFU;
105 k_ipi_work_add(&test_item.work, target_cpu_mask, test_function);
106 k_ipi_work_signal();
107 arch_irq_unlock(key);
108
109 /* Wait for the work item to complete */
110
111 status = k_ipi_work_wait(&test_item.work, K_FOREVER);
112 zassert_equal(status, 0, "k_ipi_work_wait failed: %d", status);
113
114 zassert_equal(test_item.cpu_bit, target_cpu_mask,
115 "Work item was not executed on the expected CPU");
116
117 /*
118 * Issue the IPI work item from a k_timer (ISR). The k_timer will spin
119 * while waiting for the IPI work item to complete.
120 */
121
122 TC_PRINT("ISR level IPI\n");
123
124 test_item.cpu_bit = 0xFFFFFFFFU;
125 k_timer_start(&timer, K_TICKS(2), K_NO_WAIT);
126 status = k_sem_take(&timer_sem, K_SECONDS(10));
127
128 zassert_equal(status, 0, "k_sem_take failed: %d", status);
129 zassert_equal(test_item.cpu_bit, timer_target_cpu,
130 "Work item was not executed on the expected CPU");
131 }
132
133 ZTEST_SUITE(ipi_work, NULL, NULL, NULL, NULL, NULL);
134