diff --git a/docs/porting-guide.rst b/docs/porting-guide.rst index bc309ddfb..1225a9f79 100644 --- a/docs/porting-guide.rst +++ b/docs/porting-guide.rst @@ -2785,6 +2785,22 @@ Perform the platform specific actions to power on a CPU, specified by the ``MPIDR`` (first argument). The generic code expects the platform to return PSCI_E_SUCCESS on success or PSCI_E_INTERN_FAIL for any failure. +plat_psci_ops.pwr_domain_off_early() [optional] +............................................... + +This optional function performs the platform specific actions to check if +powering off the calling CPU and its higher parent power domain levels as +indicated by the ``target_state`` (first argument) is possible or allowed. + +The ``target_state`` encodes the platform coordinated target local power states +for the CPU power domain and its parent power domain levels. + +For this handler, the local power state for the CPU power domain will be a +power down state where as it could be either power down, retention or run state +for the higher power domain levels depending on the result of state +coordination. The generic code expects PSCI_E_DENIED return code if the +platform thinks that CPU_OFF should not proceed on the calling CPU. + plat_psci_ops.pwr_domain_off() .............................. diff --git a/include/lib/psci/psci.h b/include/lib/psci/psci.h index 6d27b7b61..4d7e58e93 100644 --- a/include/lib/psci/psci.h +++ b/include/lib/psci/psci.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2023, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -317,6 +318,7 @@ typedef struct plat_psci_ops { void (*cpu_standby)(plat_local_state_t cpu_state); int (*pwr_domain_on)(u_register_t mpidr); void (*pwr_domain_off)(const psci_power_state_t *target_state); + int (*pwr_domain_off_early)(const psci_power_state_t *target_state); void (*pwr_domain_suspend_pwrdown_early)( const psci_power_state_t *target_state); #if PSCI_OS_INIT_MODE diff --git a/lib/psci/psci_off.c b/lib/psci/psci_off.c index 637adb97e..9f36ac719 100644 --- a/lib/psci/psci_off.c +++ b/lib/psci/psci_off.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2023, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -56,6 +57,19 @@ int psci_do_cpu_off(unsigned int end_pwrlvl) /* Construct the psci_power_state for CPU_OFF */ psci_set_power_off_state(&state_info); + /* + * Call the platform provided early CPU_OFF handler to allow + * platforms to perform any housekeeping activities before + * actually powering the CPU off. PSCI_E_DENIED indicates that + * the CPU off sequence should be aborted at this time. + */ + if (psci_plat_pm_ops->pwr_domain_off_early) { + rc = psci_plat_pm_ops->pwr_domain_off_early(&state_info); + if (rc == PSCI_E_DENIED) { + return rc; + } + } + /* * Get the parent nodes here, this is important to do before we * initiate the power down sequence as after that point the core may diff --git a/plat/nvidia/tegra/common/aarch64/tegra_helpers.S b/plat/nvidia/tegra/common/aarch64/tegra_helpers.S index 6c8c4f018..72ecd54e9 100644 --- a/plat/nvidia/tegra/common/aarch64/tegra_helpers.S +++ b/plat/nvidia/tegra/common/aarch64/tegra_helpers.S @@ -125,15 +125,18 @@ .endm /* ----------------------------------------------------- - * unsigned int plat_is_my_cpu_primary(void); + * bool plat_is_my_cpu_primary(void); * * This function checks if this is the Primary CPU + * + * Registers clobbered: x0, x1 * ----------------------------------------------------- */ func plat_is_my_cpu_primary mrs x0, mpidr_el1 - and x0, x0, #(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK) - cmp x0, #TEGRA_PRIMARY_CPU + adr x1, tegra_primary_cpu_mpid + ldr x1, [x1] + cmp x0, x1 cset x0, eq ret endfunc plat_is_my_cpu_primary @@ -251,6 +254,14 @@ _end: mov x0, x20 adr x18, bl31_entrypoint str x18, [x17] + /* ----------------------------------- + * save the boot CPU MPID value + * ----------------------------------- + */ + mrs x0, mpidr_el1 + adr x1, tegra_primary_cpu_mpid + str x0, [x1] + 1: cpu_init_common ret @@ -426,3 +437,10 @@ tegra_bl31_phys_base: */ tegra_console_base: .quad 0 + + /* -------------------------------------------------- + * MPID value for the boot CPU + * -------------------------------------------------- + */ +tegra_primary_cpu_mpid: + .quad 0 diff --git a/plat/nvidia/tegra/common/tegra_pm.c b/plat/nvidia/tegra/common/tegra_pm.c index ec34a850d..8edb0247e 100644 --- a/plat/nvidia/tegra/common/tegra_pm.c +++ b/plat/nvidia/tegra/common/tegra_pm.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. - * Copyright (c) 2020, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2020-2023, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -89,6 +89,16 @@ static int32_t tegra_pwr_domain_on(u_register_t mpidr) return tegra_soc_pwr_domain_on(mpidr); } +/******************************************************************************* + * Handler called when a power domain is about to be turned off. The + * target_state encodes the power state that each level should transition to. + * Return error if CPU off sequence is not allowed for the current core. + ******************************************************************************/ +static int tegra_pwr_domain_off_early(const psci_power_state_t *target_state) +{ + return tegra_soc_pwr_domain_off_early(target_state); +} + /******************************************************************************* * Handler called when a power domain is about to be turned off. The * target_state encodes the power state that each level should transition to. @@ -268,6 +278,7 @@ static int32_t tegra_validate_ns_entrypoint(uintptr_t entrypoint) static plat_psci_ops_t tegra_plat_psci_ops = { .cpu_standby = tegra_cpu_standby, .pwr_domain_on = tegra_pwr_domain_on, + .pwr_domain_off_early = tegra_pwr_domain_off_early, .pwr_domain_off = tegra_pwr_domain_off, .pwr_domain_suspend_pwrdown_early = tegra_pwr_domain_suspend_pwrdown_early, .pwr_domain_suspend = tegra_pwr_domain_suspend, diff --git a/plat/nvidia/tegra/include/platform_def.h b/plat/nvidia/tegra/include/platform_def.h index 84b3297e0..958a3f972 100644 --- a/plat/nvidia/tegra/include/platform_def.h +++ b/plat/nvidia/tegra/include/platform_def.h @@ -41,8 +41,6 @@ #define PLATFORM_STACK_SIZE U(0x400) #endif -#define TEGRA_PRIMARY_CPU U(0x0) - #define PLAT_MAX_PWR_LVL MPIDR_AFFLVL2 #define PLATFORM_CORE_COUNT (PLATFORM_CLUSTER_COUNT * \ PLATFORM_MAX_CPUS_PER_CLUSTER) diff --git a/plat/nvidia/tegra/include/tegra_private.h b/plat/nvidia/tegra/include/tegra_private.h index cc2ad869c..71bea0845 100644 --- a/plat/nvidia/tegra/include/tegra_private.h +++ b/plat/nvidia/tegra/include/tegra_private.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. - * Copyright (c) 2020, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2020-2023, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -98,6 +98,9 @@ void tegra_fiq_handler_setup(void); int32_t tegra_fiq_get_intr_context(void); void tegra_fiq_set_ns_entrypoint(uint64_t entrypoint); +/* Declarations for tegra_helpers.S */ +bool plat_is_my_cpu_primary(void); + /* Declarations for tegra_security.c */ void tegra_security_setup(void); void tegra_security_setup_videomem(uintptr_t base, uint64_t size); @@ -109,6 +112,7 @@ int32_t tegra_system_suspended(void); int32_t tegra_soc_cpu_standby(plat_local_state_t cpu_state); int32_t tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state); int32_t tegra_soc_pwr_domain_on(u_register_t mpidr); +int32_t tegra_soc_pwr_domain_off_early(const psci_power_state_t *target_state); int32_t tegra_soc_pwr_domain_off(const psci_power_state_t *target_state); int32_t tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state); int32_t tegra_soc_pwr_domain_power_down_wfi(const psci_power_state_t *target_state); diff --git a/plat/nvidia/tegra/soc/t186/plat_psci_handlers.c b/plat/nvidia/tegra/soc/t186/plat_psci_handlers.c index af4182e24..8f88e2887 100644 --- a/plat/nvidia/tegra/soc/t186/plat_psci_handlers.c +++ b/plat/nvidia/tegra/soc/t186/plat_psci_handlers.c @@ -433,6 +433,16 @@ int32_t tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state) return PSCI_E_SUCCESS; } +int32_t tegra_soc_pwr_domain_off_early(const psci_power_state_t *target_state) +{ + /* Do not power off the boot CPU */ + if (plat_is_my_cpu_primary()) { + return PSCI_E_DENIED; + } + + return PSCI_E_SUCCESS; +} + int32_t tegra_soc_pwr_domain_off(const psci_power_state_t *target_state) { uint64_t impl = (read_midr() >> MIDR_IMPL_SHIFT) & (uint64_t)MIDR_IMPL_MASK; diff --git a/plat/nvidia/tegra/soc/t194/plat_psci_handlers.c b/plat/nvidia/tegra/soc/t194/plat_psci_handlers.c index 41a85ee7c..83d815afc 100644 --- a/plat/nvidia/tegra/soc/t194/plat_psci_handlers.c +++ b/plat/nvidia/tegra/soc/t194/plat_psci_handlers.c @@ -463,6 +463,16 @@ int32_t tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state) return PSCI_E_SUCCESS; } +int32_t tegra_soc_pwr_domain_off_early(const psci_power_state_t *target_state) +{ + /* Do not power off the boot CPU */ + if (plat_is_my_cpu_primary()) { + return PSCI_E_DENIED; + } + + return PSCI_E_SUCCESS; +} + int32_t tegra_soc_pwr_domain_off(const psci_power_state_t *target_state) { uint64_t impl = (read_midr() >> MIDR_IMPL_SHIFT) & MIDR_IMPL_MASK; diff --git a/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c b/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c index 7f73ea506..2ec044c40 100644 --- a/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c +++ b/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c @@ -575,6 +575,16 @@ int tegra_soc_pwr_domain_on(u_register_t mpidr) return PSCI_E_SUCCESS; } +int32_t tegra_soc_pwr_domain_off_early(const psci_power_state_t *target_state) +{ + /* Do not power off the boot CPU */ + if (plat_is_my_cpu_primary()) { + return PSCI_E_DENIED; + } + + return PSCI_E_SUCCESS; +} + int tegra_soc_pwr_domain_off(const psci_power_state_t *target_state) { tegra_fc_cpu_off(read_mpidr() & MPIDR_CPU_MASK);