feat(lfa): add LFA holding pen logic

Add LFA holding logic to be used by the LFA activate SMC call to manage
CPU rendezvous. All CPUs are expected to invoke the LFA activate call
for the rendezvous, until then, they will remain on a holding lock.
When the final CPU calls LFA activate, it will release the holding
lock after completing the activation process on that CPU, allowing
the activation process on secondary CPUs to proceed.

Signed-off-by: Manish V Badarkhe <Manish.Badarkhe@arm.com>
Change-Id: Iff9e40dd87420245fe5844e286d0685c1f0db289
diff --git a/include/services/lfa_holding_pen.h b/include/services/lfa_holding_pen.h
new file mode 100644
index 0000000..9420747
--- /dev/null
+++ b/include/services/lfa_holding_pen.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef LFA_HOLDING_PEN_H
+#define LFA_HOLDING_PEN_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <services/lfa_svc.h>
+
+bool lfa_holding_start(void);
+enum lfa_retc lfa_holding_wait(void);
+void lfa_holding_release(enum lfa_retc status);
+
+#endif
diff --git a/services/std_svc/lfa/lfa.mk b/services/std_svc/lfa/lfa.mk
index 2dc072d..056b537 100644
--- a/services/std_svc/lfa/lfa.mk
+++ b/services/std_svc/lfa/lfa.mk
@@ -6,7 +6,8 @@
 
 LFA_SOURCES	+=	$(addprefix services/std_svc/lfa/, \
 			  lfa_main.c \
-			  bl31_lfa.c)
+			  bl31_lfa.c \
+			  lfa_holding_pen.c)
 
 ifeq (${ENABLE_RME}, 1)
 LFA_SOURCES	+=	services/std_svc/rmmd/rmmd_rmm_lfa.c
diff --git a/services/std_svc/lfa/lfa_holding_pen.c b/services/std_svc/lfa/lfa_holding_pen.c
new file mode 100644
index 0000000..8ee260c
--- /dev/null
+++ b/services/std_svc/lfa/lfa_holding_pen.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <string.h>
+
+#include <common/debug.h>
+#include <lib/psci/psci_lib.h>
+#include <lib/spinlock.h>
+#include <lib/utils_def.h>
+#include <plat/common/platform.h>
+#include <services/lfa_holding_pen.h>
+
+#include <platform_def.h>
+
+static spinlock_t holding_lock;
+static spinlock_t activation_lock;
+static uint32_t activation_count;
+static enum lfa_retc activation_status;
+
+/**
+ * lfa_holding_start - Called by each active CPU to coordinate live activation.
+ *
+ * Note that only CPUs that are active at the time of activation will
+ * participate in CPU rendezvous.
+ *
+ * This function is invoked by each CPU participating in the LFA Activate
+ * process. It increments the shared activation count under `activation_lock`
+ * to track how many CPUs have entered the activation phase.
+ *
+ * The first CPU to enter acquires the `holding_lock`, which ensures
+ * serialization during the wait and activation phases. This lock is
+ * released only after the last CPU completes the activation.
+ *
+ * The function returns `true` only for the last CPU to enter, allowing it
+ * to proceed with performing the live firmware activation. All other CPUs
+ * receive `false` and will wait in `lfa_holding_wait()` until activation
+ * is complete.
+ *
+ * @return `true` for the last CPU, `false` for all others.
+ */
+bool lfa_holding_start(void)
+{
+	bool status;
+	unsigned int no_of_cpus;
+
+	spin_lock(&activation_lock);
+
+	if (activation_count == 0U) {
+		/* First CPU locks holding lock */
+		spin_lock(&holding_lock);
+	}
+
+	activation_count += 1U;
+
+	no_of_cpus = psci_num_cpus_running_on_safe(plat_my_core_pos());
+	status = (activation_count == no_of_cpus);
+	if (!status) {
+		VERBOSE("Hold, %d CPU left\n",
+			 PLATFORM_CORE_COUNT - activation_count);
+	}
+
+	spin_unlock(&activation_lock);
+
+	return status;
+}
+
+/**
+ * lfa_holding_wait - CPUs wait until activation is completed by the last CPU.
+ *
+ * All CPUs are serialized using `holding_lock`, which is initially acquired
+ * by the first CPU in `lfa_holding_start()` and only released by the last
+ * CPU through `lfa_holding_release()`. This ensures that no two CPUs enter
+ * the critical section at the same time during the wait phase. Once the
+ * last CPU completes activation, each CPU decrements the activation count
+ * and returns the final activation status,  which was set by the last CPU
+ * to complete the activation process.
+ *
+ * @return Activation status set by the last CPU.
+ */
+enum lfa_retc lfa_holding_wait(void)
+{
+	spin_lock(&holding_lock);
+	activation_count -= 1U;
+	spin_unlock(&holding_lock);
+	return activation_status;
+}
+
+/**
+ * lfa_holding_release - Called by the last CPU to complete activation.
+ *
+ * This function is used by the last participating CPU after it completes
+ * live firmware activation. It updates the shared activation status and
+ * resets the activation count. Finally, it releases the `holding_lock` to
+ * allow other CPUs that were waiting in `lfa_holding_wait()` to proceed.
+ *
+ * @param status Activation status to be shared with other CPUs.
+ */
+void lfa_holding_release(enum lfa_retc status)
+{
+	activation_count = 0U;
+	activation_status = status;
+	spin_unlock(&holding_lock);
+}