blob: 4563bcb1d76c0f4ea3c6ba99853d8c765b31dc6d [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>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +02009#include <assert.h>
10#include <debug.h>
Antonio Nino Diaz09a00ef2019-01-11 13:12:58 +000011#include <drivers/arm/arm_gic.h>
12#include <drivers/arm/gic_v2.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020013#include <events.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020014#include <irq.h>
15#include <plat_topology.h>
16#include <platform.h>
17#include <platform_def.h>
18#include <power_management.h>
19#include <psci.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020020#include <test_helpers.h>
21#include <tftf.h>
22#include <tftf_lib.h>
23#include <timer.h>
24
25#define SUSPEND_TIME_3_SECS 3000
26#define SUSPEND_TIME_10_SECS 10000
27#define TEST_ITERATION_COUNT 0x5
28
Antonio Nino Diaz855b0b22018-11-23 13:19:33 +000029/* TODO: Remove assumption that affinity levels always map to power levels. */
30#define MPIDR_CLUSTER_ID(mpid) MPIDR_AFF_ID(mpid, 1)
31#define MPIDR_CPU_ID(mpid) MPIDR_AFF_ID(mpid, 0)
32
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020033/* Helper macro to verify if system suspend API is supported */
34#define is_psci_sys_susp64_supported() \
35 (tftf_get_psci_feature_info(SMC_PSCI_SYSTEM_SUSPEND64) != \
36 PSCI_E_NOT_SUPPORTED)
37
38static unsigned int deepest_power_state;
39static unsigned int test_target_node = PWR_DOMAIN_INIT;
40static event_t cpu_ready[PLATFORM_CORE_COUNT];
41static event_t sgi_received[PLATFORM_CORE_COUNT];
42static event_t waitq[PLATFORM_CORE_COUNT];
43
44static volatile int wakeup_irq_rcvd[PLATFORM_CORE_COUNT];
45static volatile unsigned int sgi_handled[PLATFORM_CORE_COUNT];
Boyan Karatotev794b0ac2025-06-20 13:13:29 +010046static unsigned int sgi_data;
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020047static volatile int cpu_ref_count;
48
Ambroise Vincentee3e7cd2019-07-03 16:44:49 +010049extern unsigned long __TEXT_START__;
50#define TFTF_RO_START (unsigned long)(&__TEXT_START__)
51extern unsigned long __RODATA_END__;
52#define TFTF_RO_END (unsigned long)(&__RODATA_END__)
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020053
54static int suspend_wakeup_handler(void *data)
55{
56 unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
57
58 assert(wakeup_irq_rcvd[core_pos] == 0);
59 wakeup_irq_rcvd[core_pos] = 1;
60
61 return 0;
62}
63
64static int sgi_handler(void *data)
65{
66 unsigned int mpid = read_mpidr_el1() & MPID_MASK;
67 unsigned int core_pos = platform_get_core_pos(mpid);
68
Boyan Karatotev794b0ac2025-06-20 13:13:29 +010069 sgi_data = *(unsigned int *) data;
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020070 sgi_handled[core_pos] = 1;
71 return 0;
72}
73
74/*
75 * Iterate over all cores and issue system suspend
76 * After returning from suspend, ensure that the core which entered
77 * suspend resumed from suspend.
78 */
79static test_result_t sys_suspend_from_all_cores(void)
80{
81 unsigned long long my_mpid = read_mpidr_el1() & MPID_MASK, target_mpid;
82 unsigned int core_pos = platform_get_core_pos(my_mpid);
83 int ret;
84 int psci_ret;
85
86 /* Increment the count of CPUs in the test */
87 cpu_ref_count++;
88 dsbsy();
89
90 while (!is_sys_suspend_state_ready())
91 ;
92
93 wakeup_irq_rcvd[core_pos] = 0;
94
95 /* Register timer handler */
96 tftf_timer_register_handler(suspend_wakeup_handler);
97
98 /* Program timer to fire after delay */
99 ret = tftf_program_timer_and_sys_suspend(PLAT_SUSPEND_ENTRY_TIME,
100 NULL, NULL);
101
102 /* Wait until the IRQ wake interrupt is received */
103 while (!wakeup_irq_rcvd[core_pos])
104 ;
105
106 if (ret) {
107 tftf_testcase_printf("Failed to program timer or suspend "
108 "system from core %x\n", core_pos);
109 return TEST_RESULT_FAIL;
110 }
111
112 /* Unregister time handler */
113 tftf_timer_unregister_handler();
114 tftf_cancel_timer();
115
116 /* Done with the suspend test. Decrement count */
117 cpu_ref_count--;
118 dsbsy();
119
120 test_target_node = tftf_topology_next_cpu(test_target_node);
121 if (test_target_node != PWR_DOMAIN_INIT) {
122 target_mpid = tftf_get_mpidr_from_node(test_target_node);
123 psci_ret = tftf_cpu_on(target_mpid,
124 (uintptr_t) sys_suspend_from_all_cores,
125 0);
126 if (psci_ret != PSCI_E_SUCCESS) {
127 tftf_testcase_printf("Failed to power on CPU 0x%x (%d) \n",
128 (unsigned int)target_mpid, psci_ret);
129 return TEST_RESULT_FAIL;
130 }
131
132 /*
133 * Wait for the target CPU to enter the test. The TFTF framework
134 * requires more than one CPU to be in the test to detect that the
135 * test has not finished.
136 */
137 while (!cpu_ref_count)
138 ;
139 }
140
141 return TEST_RESULT_SUCCESS;
142}
143
144/*
145 * @Test_Aim@ Functionality test : Issue system suspend from all cores
146 * sequentially. This test ensures that system suspend can be issued
147 * from all cores and right core is resumed from system suspend.
148 */
149test_result_t test_system_suspend_from_all_cores(void)
150{
151 unsigned long long target_mpid, my_mpid;
152 int psci_ret;
153
154 test_target_node = PWR_DOMAIN_INIT;
155 my_mpid = read_mpidr_el1() & MPID_MASK;
156
157 if (!is_psci_sys_susp64_supported()) {
158 tftf_testcase_printf("System suspend is not supported "
159 "by the EL3 firmware\n");
160 return TEST_RESULT_SKIPPED;
161 }
162
163 SKIP_TEST_IF_LESS_THAN_N_CPUS(2);
164
165 for (unsigned int i = 0; i < PLATFORM_CORE_COUNT; ++i)
166 tftf_init_event(&cpu_ready[i]);
167
168 test_target_node = tftf_topology_next_cpu(test_target_node);
169 assert(test_target_node != PWR_DOMAIN_INIT);
170
171 target_mpid = tftf_get_mpidr_from_node(test_target_node);
172 if (target_mpid == my_mpid)
173 return sys_suspend_from_all_cores();
174
175 psci_ret = tftf_cpu_on(target_mpid,
176 (uintptr_t) sys_suspend_from_all_cores,
177 0);
178 if (psci_ret != PSCI_E_SUCCESS) {
179 tftf_testcase_printf("Failed to power on CPU 0x%x (%d) \n",
180 (unsigned int)target_mpid, psci_ret);
181 return TEST_RESULT_FAIL;
182 }
183
184 /*
185 * Wait for the target CPU to enter the test. The TFTF framework
186 * requires more than one CPU to be in the test to detect that the
187 * test has not finished.
188 */
189 while (!cpu_ref_count)
190 ;
191
192 return TEST_RESULT_SUCCESS;
193}
194
195/*
196 * Helper function to issue SYSTEM SUSPEND SMC with custom parameters.
197 */
198int sys_suspend_helper(uintptr_t entry_point_address,
199 u_register_t context_id)
200{
201 smc_args args = {
202 SMC_PSCI_SYSTEM_SUSPEND,
203 (uintptr_t)entry_point_address,
204 (u_register_t)context_id
205 };
206 smc_ret_values ret_vals;
207
208 ret_vals = tftf_smc(&args);
209
210 return ret_vals.ret0;
211}
212
213/*
214 * Function to issue system suspend with invalid entry-point on all cores
215 * sequentially.
216 */
217static test_result_t invalid_entrypoint_for_sys_suspend(void)
218{
219 unsigned long long target_mpid;
220 int psci_ret;
221
222 /* Increment the count of CPUs in the test */
223 cpu_ref_count++;
224 dsbsy();
225
226 while (!is_sys_suspend_state_ready())
227 ;
228
229 psci_ret = sys_suspend_helper((uintptr_t) 0x1, 0);
230 if (psci_ret != PSCI_E_INVALID_ADDRESS) {
231 tftf_testcase_printf("Test failed with invalid entry addr %x\n",
232 psci_ret);
233 return TEST_RESULT_FAIL;
234 }
235
236 /* Done with the suspend test. Decrement count */
237 cpu_ref_count--;
238 dsbsy();
239
240 test_target_node = tftf_topology_next_cpu(test_target_node);
241 if (test_target_node != PWR_DOMAIN_INIT) {
242 target_mpid = tftf_get_mpidr_from_node(test_target_node);
243 psci_ret = tftf_cpu_on(target_mpid,
244 (uintptr_t) invalid_entrypoint_for_sys_suspend,
245 0);
246 if (psci_ret != PSCI_E_SUCCESS) {
247 tftf_testcase_printf("Failed to power on CPU 0x%x (%d) \n",
248 (unsigned int)target_mpid, psci_ret);
249 return TEST_RESULT_FAIL;
250 }
251
252 /*
253 * Wait for the target CPU to enter the test. The TFTF framework
254 * requires more than one CPU to be in the test to detect that the
255 * test has not finished.
256 */
257 while (!cpu_ref_count)
258 ;
259 }
260
261 return TEST_RESULT_SUCCESS;
262}
263
264/*
265 * @Test_Aim@ API test: Issue system suspend with invalid entrypoint on all
266 * cores. It should return error.
267 */
268test_result_t test_system_suspend_invalid_entrypoint(void)
269{
270 unsigned long long target_mpid, my_mpid;
271 int psci_ret;
272
273 test_target_node = PWR_DOMAIN_INIT;
274 my_mpid = read_mpidr_el1() & MPID_MASK;
275
276 if (!is_psci_sys_susp64_supported()) {
277 tftf_testcase_printf("System suspend is not supported "
278 "by the EL3 firmware\n");
279 return TEST_RESULT_SKIPPED;
280 }
281
282 SKIP_TEST_IF_LESS_THAN_N_CPUS(2);
283
284 for (unsigned int i = 0; i < PLATFORM_CORE_COUNT; ++i)
285 tftf_init_event(&cpu_ready[i]);
286
287 test_target_node = tftf_topology_next_cpu(test_target_node);
288 assert(test_target_node != PWR_DOMAIN_INIT);
289
290 target_mpid = tftf_get_mpidr_from_node(test_target_node);
291 if (target_mpid == my_mpid)
292 return invalid_entrypoint_for_sys_suspend();
293
294 psci_ret = tftf_cpu_on(target_mpid,
295 (uintptr_t) invalid_entrypoint_for_sys_suspend,
296 0);
297 if (psci_ret != PSCI_E_SUCCESS) {
298 tftf_testcase_printf("Failed to power on CPU 0x%x (%d) \n",
299 (unsigned int)target_mpid, psci_ret);
300 return TEST_RESULT_FAIL;
301 }
302
303 /*
304 * Wait for the target CPU to enter the test. The TFTF framework
305 * requires more than one CPU to be in the test to detect that the
306 * test has not finished.
307 */
308 while (!cpu_ref_count)
309 ;
310
311 return TEST_RESULT_SUCCESS;
312}
313
314/*
315 * Function to test Non lead CPU response to SGIs after multiple invocations
316 * of system suspend.
317 */
318static test_result_t non_lead_cpu_sgi_test(void)
319{
320 unsigned int mpid = read_mpidr_el1();
321 unsigned int core_pos = platform_get_core_pos(mpid);
322 const unsigned int sgi_id = IRQ_NS_SGI_0;
323 int sgi_ret;
324
325 /* Register the local IRQ handler for the SGI */
Boyan Karatotev6d144db2025-06-23 15:04:53 +0100326 sgi_ret = tftf_irq_register_handler_sgi(sgi_id, sgi_handler);
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200327 if (sgi_ret != 0) {
328 tftf_testcase_printf("Failed to register IRQ %u (%d)",
329 sgi_id, sgi_ret);
330 return TEST_RESULT_FAIL;
331 }
332 /* Enable SGI */
Boyan Karatotev6d144db2025-06-23 15:04:53 +0100333 tftf_irq_enable_sgi(sgi_id, GIC_HIGHEST_NS_PRIORITY);
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200334
335 /* Signal to the lead CPU that we are ready to receive SGI */
336 tftf_send_event(&cpu_ready[core_pos]);
337
338 /* Wait for SGI */
339 while (sgi_handled[core_pos] == 0)
340 ;
341 /* Send event to indicate reception of SGI */
342 tftf_send_event(&sgi_received[core_pos]);
343
344 /* Unregister SGI handler */
Boyan Karatotev6d144db2025-06-23 15:04:53 +0100345 tftf_irq_disable_sgi(sgi_id);
346 tftf_irq_unregister_handler_sgi(sgi_id);
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200347 return TEST_RESULT_SUCCESS;
348}
349
350/*
351 * @Test_Aim@ Functionality test: Issue system suspend multiple times with
352 * all non-lead cores as OFF. This test ensures invoking system suspend
353 * multiple times on lead core does not have any issue.
354 * Steps:
355 * - Register timer wakeup event and issue system suspend multiple
356 * times. Ensure that the system suspend succeeds.
357 * - Turn on all the non lead CPU and send SGIs to them to ensure that
358 * all the non lead CPU are responsive.
359 */
360test_result_t test_psci_sys_susp_multiple_iteration(void)
361{
362 unsigned int target_mpid, target_node;
363 unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK;
364 unsigned int core_pos = platform_get_core_pos(lead_mpid);
365 const unsigned int sgi_id = IRQ_NS_SGI_0;
366 int psci_ret;
367 int timer_ret;
368
369 if (!is_psci_sys_susp64_supported()) {
370 tftf_testcase_printf("System suspend is not supported "
371 "by the EL3 firmware\n");
372 return TEST_RESULT_SKIPPED;
373 }
374
375 for (unsigned int i = 0; i < PLATFORM_CORE_COUNT; ++i) {
376 tftf_init_event(&cpu_ready[i]);
377 tftf_init_event(&sgi_received[i]);
378 }
379
380 /* Register timer handler */
381 tftf_timer_register_handler(suspend_wakeup_handler);
382
383 for (unsigned int i = 0; i < TEST_ITERATION_COUNT; i++) {
384 wakeup_irq_rcvd[core_pos] = 0;
385
386 /*
387 * Program the wakeup timer, this will serve as the wake-up event
388 * to come out of suspend state, and issue system suspend
389 */
390 tftf_program_timer_and_sys_suspend(
391 PLAT_SUSPEND_ENTRY_TIME, &timer_ret, &psci_ret);
392
393 while (!wakeup_irq_rcvd[core_pos])
394 ;
395
396 if (psci_ret != PSCI_E_SUCCESS) {
397 tftf_testcase_printf("System suspend failed with return value %i\n",
398 psci_ret);
399 return TEST_RESULT_FAIL;
400 }
401 if (timer_ret) {
402 tftf_testcase_printf("Timer programming failed with return value %i\n",
403 timer_ret);
404 return TEST_RESULT_FAIL;
405 }
406 }
407
408 tftf_cancel_timer();
409 /* Unregister timer handler */
410 tftf_timer_unregister_handler();
411
412 /* Turn on all cores after test to ensure all cores boot up*/
413 for_each_cpu(target_node) {
414 target_mpid = tftf_get_mpidr_from_node(target_node);
415 core_pos = platform_get_core_pos(target_mpid);
416
417 if (target_mpid == lead_mpid)
418 continue;
419
420 psci_ret = tftf_cpu_on(target_mpid,
421 (uintptr_t) non_lead_cpu_sgi_test,
422 0);
423 if (psci_ret != PSCI_E_SUCCESS) {
424 tftf_testcase_printf(
425 "Failed to power on CPU 0x%x (%d)\n",
426 target_mpid, psci_ret);
427 return TEST_RESULT_FAIL;
428 }
429 tftf_wait_for_event(&cpu_ready[core_pos]);
430 }
431
432 /* Send SGI to all non lead CPUs and ensure that they receive it */
433 for_each_cpu(target_node) {
434 target_mpid = tftf_get_mpidr_from_node(target_node);
435 /* Skip lead CPU */
436 if (target_mpid == lead_mpid)
437 continue;
438
439 core_pos = platform_get_core_pos(target_mpid);
440 tftf_send_sgi(sgi_id, core_pos);
441 tftf_wait_for_event(&sgi_received[core_pos]);
442 }
443
444 return TEST_RESULT_SUCCESS;
445}
446
447/*
448 * @Test_Aim@ Functionality test : Issue system suspend with pending
449 * SGI on calling core. System suspend call should return prior to the
450 * programmed wake-up interval.
451 * Steps:
452 * - Mask the interrupts on lead CPU and send SGI to current CPU
453 * - Configure a wake-up timer and issue SYSTEM SUSPEND
454 * - Unmask the interrupt and verify that current CPU has woken
455 * prior to the wake-up timer firing.
456 */
457test_result_t test_psci_sys_susp_pending_irq(void)
458{
459 unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
460 const unsigned int sgi_id = IRQ_NS_SGI_0;
Boyan Karatotev6d144db2025-06-23 15:04:53 +0100461 const unsigned int sgi_irq = tftf_irq_get_my_sgi_num(sgi_id);
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200462 int sgi_ret;
463 int psci_ret;
464 test_result_t ret = TEST_RESULT_SUCCESS;
465
466 if (!is_psci_sys_susp64_supported()) {
467 tftf_testcase_printf("System suspend is not supported "
468 "by the EL3 firmware\n");
469 return TEST_RESULT_SKIPPED;
470 }
471
472 /* Initialize variables */
473 sgi_handled[core_pos] = 0;
474 wakeup_irq_rcvd[core_pos] = 0;
475
476 /* Register the local IRQ handler for the SGI */
Boyan Karatotev6d144db2025-06-23 15:04:53 +0100477 sgi_ret = tftf_irq_register_handler_sgi(sgi_id, sgi_handler);
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200478 if (sgi_ret != 0) {
479 tftf_testcase_printf("Failed to register IRQ %u (%d)",
480 sgi_id, sgi_ret);
481 return TEST_RESULT_FAIL;
482 }
483
484 /* Register for timer interrupt */
485 tftf_timer_register_handler(suspend_wakeup_handler);
486
487 /*
488 * Program the MB timer, for 3 secs to fire timer interrupt if
489 * system enters suspend state with pending IRQ
490 */
491 tftf_program_timer(SUSPEND_TIME_3_SECS);
492
Boyan Karatotev6d144db2025-06-23 15:04:53 +0100493 tftf_irq_enable_sgi(sgi_id, GIC_HIGHEST_NS_PRIORITY);
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200494 disable_irq();
495
496 /* Send the SGI to the lead CPU */
497 tftf_send_sgi(sgi_id, core_pos);
498
499 /* Check if system enters suspend state with pending IRQ or not */
500 psci_ret = tftf_system_suspend();
501
502 /* Un-mask the interrupt */
503 enable_irq();
504
505 /*
506 * If the wake-up timer has fired, then the pending interrupt did
507 * not have any effect on the SYSTEM SUSPEND which means the
508 * test case failed.
509 *
510 */
511 if (wakeup_irq_rcvd[core_pos]) {
512 tftf_testcase_printf("Timer irq received\n");
513 ret = TEST_RESULT_FAIL;
514 }
515
516 /* Wait for the SGI to be handled */
517 while (sgi_handled[core_pos] == 0)
518 ;
519
520 /* Verify the sgi data received by the SGI handler */
Boyan Karatotev6d144db2025-06-23 15:04:53 +0100521 if (sgi_data != sgi_irq) {
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200522 tftf_testcase_printf("Wrong IRQ ID, expected %u, got %u\n",
Boyan Karatotev6d144db2025-06-23 15:04:53 +0100523 sgi_irq, sgi_data);
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200524 ret = TEST_RESULT_FAIL;
525 }
526
527 if (psci_ret != PSCI_E_SUCCESS)
528 ret = TEST_RESULT_FAIL;
529
530 /* Unregister timer handler */
531 tftf_timer_unregister_handler();
532 tftf_cancel_timer();
533
534 /* Unregister SGI handler */
Boyan Karatotev6d144db2025-06-23 15:04:53 +0100535 tftf_irq_disable_sgi(sgi_id);
536 tftf_irq_unregister_handler_sgi(sgi_id);
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200537
538 return ret;
539}
540
541/* Helper function to calculate checksum of a given DRAM area */
542unsigned long check_data_integrity(unsigned int *addr, unsigned int size)
543{
544 unsigned int chksum = 0;
545 unsigned int i;
546
547 for (i = 0; i < (size/sizeof(unsigned int)); i++)
548 chksum += *(addr + i);
549 return chksum;
550}
551
552/*
553 * @Test_Aim@ Functionality Test: Ensure that RAM contents are preserved on
554 * resume from system suspend
555 * Steps:
556 * - Write a known pattern to the DRAM and calculate the hash.
557 * - Configure wake-up timer and issue SYSTEM SUSPEND on lead CPU.
558 * - Recalculate the hash of the DRAM and compare it with the previous
559 * value. Both the hash values should match.
560 *
561 */
562test_result_t test_psci_sys_susp_validate_ram(void)
563{
564 unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
565 unsigned long prev_hash_val = 0;
566 unsigned long present_hash_val = 0;
567 int psci_ret;
568 int timer_ret;
569
570 test_result_t ret = TEST_RESULT_SUCCESS;
571
572 if (!is_psci_sys_susp64_supported()) {
573 tftf_testcase_printf("System suspend is not supported "
574 "by the EL3 firmware\n");
575 return TEST_RESULT_SKIPPED;
576 }
577
578 wakeup_irq_rcvd[core_pos] = 0;
579
580 /* Check hash on known region of RAM before putting into suspend */
581 prev_hash_val = check_data_integrity((unsigned int *)TFTF_RO_START,
582 TFTF_RO_END - TFTF_RO_START);
583
584 tftf_timer_register_handler(suspend_wakeup_handler);
585
586 /*
587 * Program timer to fire interrupt after timer expires and issue
588 * system suspend
589 */
590 tftf_program_timer_and_sys_suspend(SUSPEND_TIME_10_SECS,
591 &timer_ret, &psci_ret);
592
593 while (!wakeup_irq_rcvd[core_pos])
594 ;
595 if (psci_ret == PSCI_E_SUCCESS) {
596 /*
597 * Check hash on known region of RAM after returning
598 * from suspend
599 */
600 present_hash_val = check_data_integrity(
601 (unsigned int *)TFTF_RO_START,
602 TFTF_RO_END - TFTF_RO_START);
603 if (present_hash_val != prev_hash_val) {
604 tftf_testcase_printf("ERROR: RAM data not retained \n");
605 ret = TEST_RESULT_FAIL;
606 }
607 } else {
608 tftf_testcase_printf("Failed: system suspend to RAM \n");
609 ret = TEST_RESULT_FAIL;
610 }
611
612 if (timer_ret) {
613 tftf_testcase_printf("Failed: timer programming \n");
614 ret = TEST_RESULT_FAIL;
615 }
616
617 /* Unregister timer handler */
618 tftf_timer_unregister_handler();
619 tftf_cancel_timer();
620
621 return ret;
622}
623
624/* Helper function to get deepest power state */
625static unsigned int get_deepest_power_state(void)
626{
627 unsigned int test_suspend_type;
628 unsigned int suspend_state_id;
629 unsigned int power_level;
630 unsigned int power_state = 0;
631 unsigned int pstate_id_idx[PLAT_MAX_PWR_LEVEL + 1];
632 int ret;
633
634 INIT_PWR_LEVEL_INDEX(pstate_id_idx);
635 do {
636 tftf_set_next_state_id_idx(PLAT_MAX_PWR_LEVEL, pstate_id_idx);
637
638 if (pstate_id_idx[0] == PWR_STATE_INIT_INDEX)
639 break;
640
641 ret = tftf_get_pstate_vars(&power_level,
642 &test_suspend_type,
643 &suspend_state_id,
644 pstate_id_idx);
645 if (ret)
646 continue;
647
648 power_state = tftf_make_psci_pstate(power_level,
649 test_suspend_type,
650 suspend_state_id);
651
652 } while (1);
653
654 return power_state;
655}
656
657/*
658 * Suspend non-lead cores
659 */
660static test_result_t suspend_non_lead_cpu(void)
661{
662 unsigned long long mpid = read_mpidr_el1();
663 unsigned int core_pos = platform_get_core_pos(mpid);
664 int ret;
665
Boyan Karatotev6d144db2025-06-23 15:04:53 +0100666 tftf_irq_enable_sgi(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200667
668 /* Tell the lead CPU that the calling CPU is about to suspend itself */
669 tftf_send_event(&cpu_ready[core_pos]);
670
671 ret = tftf_cpu_suspend(deepest_power_state);
Boyan Karatotev6d144db2025-06-23 15:04:53 +0100672 tftf_irq_disable_sgi(IRQ_NS_SGI_0);
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200673
674 if (ret) {
675 ERROR(" CPU suspend failed with error %x\n", ret);
676 return TEST_RESULT_FAIL;
677 }
678
679 return TEST_RESULT_SUCCESS;
680}
681
682/*
683 * @Test_Aim@ API Test: Issue system suspend on a core while other
684 * cores are in suspend. This test ensures that system suspend will
685 * not be successful if cores other than core issuing suspend are not
686 * in OFF state.
687 * Steps:
688 * - Turn on non lead CPUs and suspend it to the deepest suspend
689 * power state.
690 * - Issue SYSTEM SUSPEND on primary CPU. The API should return
691 * error.
692 *
693 */
694test_result_t test_psci_sys_susp_with_cores_in_suspend(void)
695{
696 unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
697 unsigned int target_mpid, target_node;
698 unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK;
699 int psci_ret;
700 int timer_ret;
701 test_result_t ret = TEST_RESULT_SUCCESS;
702
703 if (!is_psci_sys_susp64_supported()) {
704 tftf_testcase_printf("System suspend is not supported "
705 "by the EL3 firmware\n");
706 return TEST_RESULT_SKIPPED;
707 }
708
709 SKIP_TEST_IF_LESS_THAN_N_CLUSTERS(2);
710
711 for (unsigned int j = 0; j < PLATFORM_CORE_COUNT; j++)
712 tftf_init_event(&cpu_ready[j]);
713
714 wakeup_irq_rcvd[core_pos] = 0;
715 deepest_power_state = get_deepest_power_state();
716
717 /* Suspend all cores other than lead core */
718 for_each_cpu(target_node) {
719 target_mpid = tftf_get_mpidr_from_node(target_node);
720
721 if (target_mpid == lead_mpid)
722 continue;
723
724 /* Turn on the non lead CPU and suspend it. */
725 psci_ret = tftf_cpu_on(target_mpid,
726 (uintptr_t) suspend_non_lead_cpu,
727 0);
728 if (psci_ret != PSCI_E_SUCCESS) {
729 tftf_testcase_printf(
730 "Failed to power on CPU 0x%x (%d)\n",
731 (unsigned int)target_mpid, psci_ret);
732 return TEST_RESULT_FAIL;
733 }
734 }
735
736 /* Wait for all non-lead CPUs to be ready */
737 for_each_cpu(target_node) {
738 target_mpid = tftf_get_mpidr_from_node(target_node);
739 /* Skip lead CPU */
740 if (target_mpid == lead_mpid)
741 continue;
742
743 core_pos = platform_get_core_pos(target_mpid);
744 tftf_wait_for_event(&cpu_ready[core_pos]);
745 }
746
747 /* Wait for 10 ms to ensure all the secondaries have suspended */
748 waitms(10);
749
750 /*
751 * Register and program timer, then issue a system suspend
752 * when other cores are in suspend state
753 */
754 tftf_timer_register_handler(suspend_wakeup_handler);
755 tftf_program_timer_and_sys_suspend(
756 PLAT_SUSPEND_ENTRY_TIME, &timer_ret, &psci_ret);
757
758 /* Wake all non-lead CPUs */
759 for_each_cpu(target_node) {
760 target_mpid = tftf_get_mpidr_from_node(target_node);
761 /* Skip lead CPU */
762 if (target_mpid == lead_mpid)
763 continue;
764
765 core_pos = platform_get_core_pos(target_mpid);
766 tftf_send_sgi(IRQ_NS_SGI_0, core_pos);
767 }
768
769 /* Check return from value from system suspend API */
770 if (psci_ret != PSCI_E_DENIED) {
771 tftf_testcase_printf("Entered suspend with cores in suspend\n");
772 ret = TEST_RESULT_FAIL;
773 }
774 if (timer_ret) {
775 tftf_testcase_printf("Failed to program the timer\n");
776 ret = TEST_RESULT_FAIL;
777 }
778 /* Unregister and cancel timer */
779 tftf_timer_unregister_handler();
780 tftf_cancel_timer();
781
782 return ret;
783}
784
785/*
786 * This functions holds the CPU till a `waitq` event is received.
787 */
788static test_result_t cpu_waitq(void)
789{
790 unsigned int mpid = read_mpidr_el1();
791 unsigned int core_pos = platform_get_core_pos(mpid);
792
793 tftf_send_event(&cpu_ready[core_pos]);
794
795 /* Wait for event from primary cpu */
796 tftf_wait_for_event(&waitq[core_pos]);
797 return TEST_RESULT_SUCCESS;
798}
799
800/*
801 * @Test_Aim@ API TEST: Ensure that system suspend will not be successful
802 * if cores other than core issuing suspend are in running state
803 *
804 * Steps :
805 * - Turn on multiple cores on the non lead cluster
806 * - Issue SYSTEM SUSPEND. The API should return error.
807 */
808test_result_t test_psci_sys_susp_with_cores_on(void)
809{
810 unsigned int lead_cluster = MPIDR_CLUSTER_ID(read_mpidr_el1());
811 unsigned int core_pos;
812 unsigned int target_mpid, target_node;
813 int psci_ret;
814 int timer_ret;
815 test_result_t ret = TEST_RESULT_SUCCESS;
816
817 if (!is_psci_sys_susp64_supported()) {
818 tftf_testcase_printf("System suspend is not supported "
819 "by the EL3 firmware\n");
820 return TEST_RESULT_SKIPPED;
821 }
822
823 SKIP_TEST_IF_LESS_THAN_N_CLUSTERS(2);
824
825 for (unsigned int j = 0; j < PLATFORM_CORE_COUNT; j++) {
826 tftf_init_event(&waitq[j]);
827 tftf_init_event(&cpu_ready[j]);
828 wakeup_irq_rcvd[j] = 0;
829 }
830
831 /* Turn on cores in non-lead cluster */
832 for_each_cpu(target_node) {
833 target_mpid = tftf_get_mpidr_from_node(target_node);
834
835 if (MPIDR_CLUSTER_ID(target_mpid) == lead_cluster)
836 continue;
837
838 psci_ret = tftf_cpu_on(target_mpid,
839 (uintptr_t) cpu_waitq,
840 0);
841 if (psci_ret != PSCI_E_SUCCESS) {
842 tftf_testcase_printf("Failed to power "
843 "on CPU 0x%x (%d)\n",
844 (unsigned int)target_mpid, psci_ret);
845 return TEST_RESULT_FAIL;
846 }
847
848 core_pos = platform_get_core_pos(target_mpid);
849 /* Ensure that the core has booted */
850 tftf_wait_for_event(&cpu_ready[core_pos]);
851 }
852
853 /* Register timer handler */
854 tftf_timer_register_handler(suspend_wakeup_handler);
855
856 /*
857 * Program timer to fire after delay and issue system suspend with
858 * other cores in ON state
859 */
860 tftf_program_timer_and_sys_suspend(PLAT_SUSPEND_ENTRY_TIME,
861 &timer_ret, &psci_ret);
862
863 /* Send event to CPUs waiting for `waitq` event. */
864 for_each_cpu(target_node) {
865 target_mpid = tftf_get_mpidr_from_node(target_node);
866
867 /* Skip lead cluster */
868 if (MPIDR_CLUSTER_ID(target_mpid) == lead_cluster)
869 continue;
870
871 core_pos = platform_get_core_pos(target_mpid);
872 tftf_send_event(&waitq[core_pos]);
873 }
874
875 /* Check return value from system suspend API */
876 if (psci_ret != PSCI_E_DENIED) {
877 tftf_testcase_printf("Test failed when suspending with return "
878 "value: %x \n", psci_ret);
879 ret = TEST_RESULT_FAIL;
880 }
881 if (timer_ret) {
882 tftf_testcase_printf("Test failed with return value when "
883 "programming the timer: %x \n", timer_ret);
884 ret = TEST_RESULT_FAIL;
885 }
886 tftf_timer_unregister_handler();
887 tftf_cancel_timer();
888 return ret;
889}