blob: 8ee260c64488ae9621bec5765f4a9a64b1fd6ea8 [file] [log] [blame]
/*
* 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);
}