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