xref: /rk3399_ARM-atf/services/std_svc/lfa/lfa_holding_pen.c (revision e27f8a24cbbc0b0404a36405c14e649e4d4b82b0)
1 /*
2  * Copyright (c) 2025, Arm Limited. All rights reserved.
3  * Copyright (c) 2026, NVIDIA Corporation. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include <string.h>
9 
10 #include <common/debug.h>
11 #include <lib/psci/psci_lib.h>
12 #include <lib/spinlock.h>
13 #include <lib/utils_def.h>
14 #include <plat/common/platform.h>
15 #include <services/lfa_holding_pen.h>
16 
17 #include <platform_def.h>
18 
19 static spinlock_t holding_lock;
20 static spinlock_t activation_lock;
21 static uint32_t activation_count;
22 static enum lfa_retc activation_status;
23 
24 /**
25  * lfa_holding_start - Called by each active CPU to coordinate live activation.
26  *
27  * Note that only CPUs that are active at the time of activation will
28  * participate in CPU rendezvous.
29  *
30  * This function is invoked by each CPU participating in the LFA Activate
31  * process. It increments the shared activation count under `activation_lock`
32  * to track how many CPUs have entered the activation phase.
33  *
34  * The first CPU to enter acquires the `holding_lock`, which ensures
35  * serialization during the wait and activation phases. This lock is
36  * released only after the last CPU completes the activation.
37  *
38  * The function returns `true` only for the last CPU to enter, allowing it
39  * to proceed with performing the live firmware activation. All other CPUs
40  * receive `false` and will wait in `lfa_holding_wait()` until activation
41  * is complete.
42  *
43  * @return `true` for the last CPU, `false` for all others.
44  */
lfa_holding_start(void)45 bool lfa_holding_start(void)
46 {
47 	bool status;
48 	unsigned int no_of_cpus;
49 
50 	spin_lock(&activation_lock);
51 
52 	if (activation_count == 0U) {
53 		/* First CPU locks holding lock */
54 		spin_lock(&holding_lock);
55 	}
56 
57 	activation_count += 1U;
58 
59 	no_of_cpus = psci_num_cpus_running_on_safe(plat_my_core_pos());
60 	status = (activation_count == no_of_cpus);
61 	if (!status) {
62 		VERBOSE("Hold, %d CPU left\n",
63 			 PLATFORM_CORE_COUNT - activation_count);
64 	}
65 
66 	spin_unlock(&activation_lock);
67 
68 	return status;
69 }
70 
71 /**
72  * lfa_holding_wait - CPUs wait until activation is completed by the last CPU.
73  *
74  * All CPUs are serialized using `holding_lock`, which is initially acquired
75  * by the first CPU in `lfa_holding_start()` and only released by the last
76  * CPU through `lfa_holding_release()`. This ensures that no two CPUs enter
77  * the critical section at the same time during the wait phase. Once the
78  * last CPU completes activation, each CPU returns the final activation status,
79  * which was set by the last CPU to complete the activation process.
80  *
81  * @return Activation status set by the last CPU.
82  */
lfa_holding_wait(void)83 enum lfa_retc lfa_holding_wait(void)
84 {
85 	spin_lock(&holding_lock);
86 	spin_unlock(&holding_lock);
87 	return activation_status;
88 }
89 
90 /**
91  * lfa_holding_release - Called by the last CPU to complete activation.
92  *
93  * This function is used by the last participating CPU after it completes
94  * live firmware activation. It updates the shared activation status and
95  * resets the activation count. Finally, it releases the `holding_lock` to
96  * allow other CPUs that were waiting in `lfa_holding_wait()` to proceed.
97  *
98  * @param status Activation status to be shared with other CPUs.
99  */
lfa_holding_release(enum lfa_retc status)100 void lfa_holding_release(enum lfa_retc status)
101 {
102 	activation_count = 0U;
103 	activation_status = status;
104 	spin_unlock(&holding_lock);
105 }
106