blob: 59a22b1a5d20f087866c9d17f46e20230c493c13 [file] [log] [blame]
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +02001/*
Boyan Karatotev794b0ac2025-06-20 13:13:29 +01002 * Copyright (c) 2018-2025, Arm Limited. All rights reserved.
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +02003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <arch.h>
8#include <arch_helpers.h>
9#include <debug.h>
10#include <events.h>
11#include <irq.h>
12#include <mmio.h>
13#include <plat_topology.h>
14#include <platform.h>
15#include <power_management.h>
16#include <psci.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020017#include <stdlib.h>
18#include <test_helpers.h>
19#include <tftf_lib.h>
20#include <timer.h>
21
22static event_t cpu_ready[PLATFORM_CORE_COUNT];
23
24/* Used to confirm the CPU is woken up by IRQ_WAKE_SGI or Timer IRQ */
25static volatile int requested_irq_received[PLATFORM_CORE_COUNT];
26/* Used to count number of CPUs woken up by IRQ_WAKE_SGI */
27static int multiple_timer_count;
28/* Used to count number of CPUs woken up by Timer IRQ */
29static int timer_switch_count;
30/* Timer step value of a platform */
31static unsigned int timer_step_value;
32/* Used to program the interrupt time */
33static unsigned long long next_int_time;
34/* Lock to prevent concurrently modifying next_int_time */
35static spinlock_t int_timer_access_lock;
36/* Lock to prevent concurrently modifying irq handler data structures */
37static spinlock_t irq_handler_lock;
38
39/* Variable to confirm all cores are inside the testcase */
40static volatile unsigned int all_cores_inside_test;
41
42/*
43 * Used by test cases to confirm if the programmed timer is fired. It also
44 * keeps track of how many timer irq's are received.
45 */
46static int requested_irq_handler(void *data)
47{
48 unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
49 unsigned int irq_id = *(unsigned int *) data;
50
Boyan Karatotev6d144db2025-06-23 15:04:53 +010051 assert(irq_id == tftf_irq_get_my_sgi_num(IRQ_WAKE_SGI) || irq_id == tftf_get_timer_irq());
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020052 assert(requested_irq_received[core_pos] == 0);
53
54 if (irq_id == tftf_get_timer_irq()) {
55 spin_lock(&irq_handler_lock);
56 timer_switch_count++;
57 spin_unlock(&irq_handler_lock);
58 }
59
60 requested_irq_received[core_pos] = 1;
61
62 return 0;
63}
64
65/*
66 * Used by test cases to confirm if the programmed timer is fired. It also
67 * keeps track of how many WAKE_SGI's are received.
68 */
69static int multiple_timer_handler(void *data)
70{
71 unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
72 unsigned int irq_id = *(unsigned int *) data;
73
Boyan Karatotev6d144db2025-06-23 15:04:53 +010074 assert(irq_id == tftf_irq_get_my_sgi_num(IRQ_WAKE_SGI) || irq_id == tftf_get_timer_irq());
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020075 assert(requested_irq_received[core_pos] == 0);
76
Boyan Karatotev6d144db2025-06-23 15:04:53 +010077 if (irq_id == tftf_irq_get_my_sgi_num(IRQ_WAKE_SGI)) {
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020078 spin_lock(&irq_handler_lock);
79 multiple_timer_count++;
80 spin_unlock(&irq_handler_lock);
81 }
82
83 requested_irq_received[core_pos] = 1;
84
85 return 0;
86}
87
88/*
89 * @Test_Aim@ Validates timer interrupt framework and platform timer driver for
90 * generation and routing of interrupt to a powered on core.
91 *
92 * Returns SUCCESS or waits forever in wfi()
93 */
94test_result_t test_timer_framework_interrupt(void)
95{
96 unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
97 int ret;
98
99 /* Initialise common variable across tests */
100 requested_irq_received[core_pos] = 0;
101
102 /* Register timer handler to confirm it received the timer interrupt */
103 ret = tftf_timer_register_handler(requested_irq_handler);
104 if (ret != 0) {
105 tftf_testcase_printf("Failed to register timer handler:0x%x\n", ret);
106 return TEST_RESULT_FAIL;
107 }
108
109 ret = tftf_program_timer(tftf_get_timer_step_value() + 1);
110 if (ret != 0) {
111 tftf_testcase_printf("Failed to program timer:0x%x\n", ret);
112 return TEST_RESULT_FAIL;
113 }
114 wfi();
115
116 while (!requested_irq_received[core_pos])
117 ;
118 ret = tftf_timer_unregister_handler();
119 if (ret != 0) {
120 tftf_testcase_printf("Failed to unregister timer handler:0x%x\n", ret);
121 return TEST_RESULT_SKIPPED;
122 }
123
124 return TEST_RESULT_SUCCESS;
125}
126
127static test_result_t timer_target_power_down_cpu(void)
128{
129 unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
130 unsigned int power_state;
131 unsigned int stateid;
132 int ret;
133 unsigned long timer_delay;
134
135 tftf_send_event(&cpu_ready[core_pos]);
136 /* Initialise common variable across tests */
137 requested_irq_received[core_pos] = 0;
138
139 /* Construct the state-id for power down */
140 ret = tftf_psci_make_composite_state_id(MPIDR_AFFLVL0,
141 PSTATE_TYPE_POWERDOWN, &stateid);
142 if (ret != PSCI_E_SUCCESS) {
143 tftf_testcase_printf("Failed to construct composite state\n");
144 return TEST_RESULT_FAIL;
145 }
146
147 /* Register timer handler to confirm it received the timer interrupt */
148 ret = tftf_timer_register_handler(requested_irq_handler);
149 if (ret != 0) {
150 tftf_testcase_printf("Failed to register timer handler:0x%x\n", ret);
151 return TEST_RESULT_FAIL;
152 }
153
154 /* Wait for all cores to be up */
155 while (!all_cores_inside_test)
156 ;
157
158 power_state = tftf_make_psci_pstate(MPIDR_AFFLVL0,
159 PSTATE_TYPE_POWERDOWN, stateid);
160
161 spin_lock(&int_timer_access_lock);
162 timer_delay = PLAT_SUSPEND_ENTRY_TIME + next_int_time;
163 next_int_time -= 2 * (timer_step_value + PLAT_SUSPEND_ENTRY_EXIT_TIME);
164 spin_unlock(&int_timer_access_lock);
165
166 ret = tftf_program_timer_and_suspend(timer_delay, power_state,
167 NULL, NULL);
168 if (ret != 0) {
169 tftf_testcase_printf(
170 "Failed to program timer or suspend CPU: 0x%x\n", ret);
171 return TEST_RESULT_FAIL;
172 }
173
174 while (!requested_irq_received[core_pos])
175 ;
176 ret = tftf_timer_unregister_handler();
177 if (ret != 0) {
178 tftf_testcase_printf("Failed to unregister timer handler:0x%x\n", ret);
179 return TEST_RESULT_SKIPPED;
180 }
181
182 return TEST_RESULT_SUCCESS;
183}
184
185/*
186 * @Test_Aim@ Validates routing of timer interrupt to the lowest requested
187 * timer interrupt core on power down.
188 *
189 * Power up all the cores and each requests a timer interrupt lesser than
190 * the previous requested core by timer step value. Doing this ensures
191 * at least some cores would be waken by Time IRQ.
192 *
193 * Returns SUCCESS if all cores power up on getting the interrupt.
194 */
195test_result_t test_timer_target_power_down_cpu(void)
196{
197 unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK;
198 unsigned int cpu_mpid, cpu_node;
199 unsigned int core_pos;
200 unsigned int rc;
201 unsigned int valid_cpu_count;
202
203 SKIP_TEST_IF_LESS_THAN_N_CPUS(2);
204
Deepika Bhavnani2cb7c522019-10-14 12:48:04 -0500205 for (unsigned int i = 0; i < PLATFORM_CORE_COUNT; i++) {
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200206 tftf_init_event(&cpu_ready[i]);
Deepika Bhavnani2cb7c522019-10-14 12:48:04 -0500207 requested_irq_received[i] = 0;
208 }
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200209
210 if (!timer_step_value)
211 timer_step_value = tftf_get_timer_step_value();
212
213 timer_switch_count = 0;
214 all_cores_inside_test = 0;
215
216 /*
217 * To be sure none of the CPUs do not fall in an atomic slice,
218 * all CPU's program the timer as close as possible with a time
219 * difference of twice the sum of step value and suspend entry
220 * exit time.
221 */
222 next_int_time = 2 * (timer_step_value + PLAT_SUSPEND_ENTRY_EXIT_TIME) * (PLATFORM_CORE_COUNT + 2);
223
224 /*
225 * Preparation step: Power on all cores.
226 */
227 for_each_cpu(cpu_node) {
228 cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
229 /* Skip lead CPU as it is already on */
230 if (cpu_mpid == lead_mpid)
231 continue;
232
233 rc = tftf_cpu_on(cpu_mpid,
234 (uintptr_t) timer_target_power_down_cpu,
235 0);
236 if (rc != PSCI_E_SUCCESS) {
237 tftf_testcase_printf(
238 "Failed to power on CPU 0x%x (%d)\n",
239 cpu_mpid, rc);
240 return TEST_RESULT_SKIPPED;
241 }
242 }
243
244 /* Wait for all non-lead CPUs to be ready */
245 for_each_cpu(cpu_node) {
246 cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
247 /* Skip lead CPU */
248 if (cpu_mpid == lead_mpid)
249 continue;
250
251 core_pos = platform_get_core_pos(cpu_mpid);
252 tftf_wait_for_event(&cpu_ready[core_pos]);
253 }
254
255 all_cores_inside_test = 1;
256
257 rc = timer_target_power_down_cpu();
258
259 valid_cpu_count = 0;
260 /* Wait for all cores to complete the test */
261 for_each_cpu(cpu_node) {
262 cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
263 core_pos = platform_get_core_pos(cpu_mpid);
264 while (!requested_irq_received[core_pos])
265 ;
266 valid_cpu_count++;
267 }
268
269 if (timer_switch_count != valid_cpu_count) {
270 tftf_testcase_printf("Expected timer switch: %d Actual: %d\n",
271 valid_cpu_count, timer_switch_count);
272 return TEST_RESULT_FAIL;
273 }
274
275 return TEST_RESULT_SUCCESS;
276}
277
278static test_result_t timer_same_interval(void)
279{
280 unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
281 unsigned int power_state;
282 unsigned int stateid;
283 int ret;
284
285 tftf_send_event(&cpu_ready[core_pos]);
286
287 /* Initialise common variable across tests */
288 requested_irq_received[core_pos] = 0;
289
290 /* Construct the state-id for power down */
291 ret = tftf_psci_make_composite_state_id(MPIDR_AFFLVL0,
292 PSTATE_TYPE_POWERDOWN, &stateid);
293 if (ret != PSCI_E_SUCCESS) {
294 tftf_testcase_printf("Failed to construct composite state\n");
295 return TEST_RESULT_FAIL;
296 }
297
298 /* Register timer handler to confirm it received the timer interrupt */
299 ret = tftf_timer_register_handler(multiple_timer_handler);
300 if (ret != 0) {
301 tftf_testcase_printf("Failed to register timer handler:0x%x\n", ret);
302 return TEST_RESULT_FAIL;
303 }
304
305 /* Wait for all cores to be up */
306 while (!all_cores_inside_test)
307 ;
308
309 /*
310 * Lets hope with in Suspend entry time + 10ms, at least some of the CPU's
311 * have same interval
312 */
313 power_state = tftf_make_psci_pstate(MPIDR_AFFLVL0,
314 PSTATE_TYPE_POWERDOWN, stateid);
315 ret = tftf_program_timer_and_suspend(PLAT_SUSPEND_ENTRY_TIME + 10,
316 power_state, NULL, NULL);
317 if (ret != 0) {
318 tftf_testcase_printf(
319 "Failed to program timer or suspend CPU: 0x%x\n", ret);
320 }
321
322 while (!requested_irq_received[core_pos])
323 ;
324 ret = tftf_timer_unregister_handler();
325 if (ret != 0) {
326 tftf_testcase_printf("Failed to unregister timer handler:0x%x\n", ret);
327 return TEST_RESULT_SKIPPED;
328 }
329
330 return TEST_RESULT_SUCCESS;
331}
332
333/*
334 * @Test_Aim@ Validates routing of timer interrupt when multiple cores
335 * requested same time.
336 *
337 * Power up all the cores and each core requests same time.
338 *
339 * Returns SUCCESS if all cores get an interrupt and power up.
340 */
341test_result_t test_timer_target_multiple_same_interval(void)
342{
343 unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK;
344 unsigned int cpu_mpid, cpu_node;
345 unsigned int core_pos;
346 unsigned int rc;
347
348 SKIP_TEST_IF_LESS_THAN_N_CPUS(2);
349
Deepika Bhavnani2cb7c522019-10-14 12:48:04 -0500350 for (unsigned int i = 0; i < PLATFORM_CORE_COUNT; i++) {
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200351 tftf_init_event(&cpu_ready[i]);
Deepika Bhavnani2cb7c522019-10-14 12:48:04 -0500352 requested_irq_received[i] = 0;
353 }
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200354
355 multiple_timer_count = 0;
356 all_cores_inside_test = 0;
357
358 /*
359 * Preparation step: Power on all cores.
360 */
361 for_each_cpu(cpu_node) {
362 cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
363 /* Skip lead CPU as it is already on */
364 if (cpu_mpid == lead_mpid)
365 continue;
366
367 rc = tftf_cpu_on(cpu_mpid,
368 (uintptr_t) timer_same_interval,
369 0);
370 if (rc != PSCI_E_SUCCESS) {
371 tftf_testcase_printf(
372 "Failed to power on CPU 0x%x (%d)\n",
373 cpu_mpid, rc);
374 return TEST_RESULT_SKIPPED;
375 }
376 }
377
378 /* Wait for all non-lead CPUs to be ready */
379 for_each_cpu(cpu_node) {
380 cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
381 /* Skip lead CPU */
382 if (cpu_mpid == lead_mpid)
383 continue;
384
385 core_pos = platform_get_core_pos(cpu_mpid);
386 tftf_wait_for_event(&cpu_ready[core_pos]);
387 }
388
389 core_pos = platform_get_core_pos(lead_mpid);
390 /* Initialise common variable across tests */
391 requested_irq_received[core_pos] = 0;
392
393 all_cores_inside_test = 1;
394
395 rc = timer_same_interval();
396
397 /* Wait for all cores to complete the test */
398 for_each_cpu(cpu_node) {
399 cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
400 core_pos = platform_get_core_pos(cpu_mpid);
401 while (!requested_irq_received[core_pos])
402 ;
403 }
404
405 if (rc != TEST_RESULT_SUCCESS)
406 return rc;
407
408 /* At least 2 CPUs requests should fall in same timer period. */
409 return multiple_timer_count ? TEST_RESULT_SUCCESS : TEST_RESULT_SKIPPED;
410}
411
412static test_result_t do_stress_test(void)
413{
414 unsigned int power_state;
415 unsigned int stateid;
416 unsigned int timer_int_interval;
417 unsigned int verify_cancel;
418 unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
419 unsigned long long end_time;
420 unsigned long long current_time;
421 int ret;
422
423 tftf_send_event(&cpu_ready[core_pos]);
424
Varun Wadekar5904da42020-05-22 10:52:23 -0700425 end_time = read_cntpct_el0() + read_cntfrq_el0() * 10;
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200426
427 /* Construct the state-id for power down */
428 ret = tftf_psci_make_composite_state_id(MPIDR_AFFLVL0,
429 PSTATE_TYPE_POWERDOWN, &stateid);
430 if (ret != PSCI_E_SUCCESS) {
431 tftf_testcase_printf("Failed to construct composite state\n");
432 return TEST_RESULT_FAIL;
433 }
434
435 /* Register a handler to confirm its woken by programmed interrupt */
436 ret = tftf_timer_register_handler(requested_irq_handler);
437 if (ret != 0) {
438 tftf_testcase_printf("Failed to register timer handler:0x%x\n", ret);
439 return TEST_RESULT_FAIL;
440 }
441
442 do {
Varun Wadekar5904da42020-05-22 10:52:23 -0700443 current_time = read_cntpct_el0();
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200444 if (current_time > end_time)
445 break;
446
447 timer_int_interval = 1 + rand() % 5;
448 verify_cancel = rand() % 5;
449
450 requested_irq_received[core_pos] = 0;
451
452 /*
453 * If verify_cancel == 0, cancel the programmed timer. As it can
454 * take values from 0 to 4, we will be cancelling only 20% of
455 * times.
456 */
457 if (!verify_cancel) {
458 ret = tftf_program_timer(PLAT_SUSPEND_ENTRY_TIME +
459 timer_int_interval);
460 if (ret != 0) {
461 tftf_testcase_printf("Failed to program timer: "
462 "0x%x\n", ret);
463 return TEST_RESULT_FAIL;
464 }
465
466 ret = tftf_cancel_timer();
467 if (ret != 0) {
468 tftf_testcase_printf("Failed to cancel timer: "
469 "0x%x\n", ret);
470 return TEST_RESULT_FAIL;
471 }
472 } else {
473 power_state = tftf_make_psci_pstate(
474 MPIDR_AFFLVL0, PSTATE_TYPE_POWERDOWN,
475 stateid);
476
477 ret = tftf_program_timer_and_suspend(
478 PLAT_SUSPEND_ENTRY_TIME + timer_int_interval,
479 power_state, NULL, NULL);
480 if (ret != 0) {
481 tftf_testcase_printf("Failed to program timer "
482 "or suspend: 0x%x\n", ret);
483 return TEST_RESULT_FAIL;
484 }
485
486 if (!requested_irq_received[core_pos]) {
487 /*
488 * Cancel the interrupt as the CPU has been
489 * woken by some other interrupt
490 */
491 ret = tftf_cancel_timer();
492 if (ret != 0) {
493 tftf_testcase_printf("Failed to cancel timer:0x%x\n", ret);
494 return TEST_RESULT_FAIL;
495 }
496 }
497 }
498 } while (1);
499
500 ret = tftf_timer_unregister_handler();
501 if (ret != 0) {
502 tftf_testcase_printf("Failed to unregister timer handler:0x%x\n", ret);
503 return TEST_RESULT_SKIPPED;
504 }
505
506 return TEST_RESULT_SUCCESS;
507}
508
509/*
510 * @Test_Aim@ Stress tests timer framework by requesting combination of
511 * timer requests with SUSPEND and cancel calls.
512 *
513 * Returns SUCCESS if all the cores successfully wakes up from suspend
514 * and returns back to the framework.
515 */
516test_result_t stress_test_timer_framework(void)
517{
518 unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK;
519 unsigned int cpu_mpid, cpu_node;
520 unsigned int core_pos;
521 unsigned int rc;
522
523 for (unsigned int i = 0; i < PLATFORM_CORE_COUNT; i++) {
524 tftf_init_event(&cpu_ready[i]);
525 requested_irq_received[i] = 0;
526 }
527 /*
528 * Preparation step: Power on all cores.
529 */
530 for_each_cpu(cpu_node) {
531 cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
532 /* Skip lead CPU as it is already on */
533 if (cpu_mpid == lead_mpid)
534 continue;
535
536 rc = tftf_cpu_on(cpu_mpid,
537 (uintptr_t) do_stress_test,
538 0);
539 if (rc != PSCI_E_SUCCESS) {
540 tftf_testcase_printf(
541 "Failed to power on CPU 0x%x (%d)\n",
542 cpu_mpid, rc);
543 return TEST_RESULT_SKIPPED;
544 }
545 }
546
547 /* Wait for all non-lead CPUs to be ready */
548 for_each_cpu(cpu_node) {
549 cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
550 /* Skip lead CPU */
551 if (cpu_mpid == lead_mpid)
552 continue;
553
554 core_pos = platform_get_core_pos(cpu_mpid);
555 tftf_wait_for_event(&cpu_ready[core_pos]);
556 }
557
558 return do_stress_test();
559}