Merge changes I9d06e0ee,I6980e84f into integration

* changes:
  feat(tegra): implement 'pwr_domain_off_early' handler
  feat(psci): introduce 'pwr_domain_off_early' hook
diff --git a/docs/porting-guide.rst b/docs/porting-guide.rst
index bc309dd..1225a9f 100644
--- a/docs/porting-guide.rst
+++ b/docs/porting-guide.rst
@@ -2785,6 +2785,22 @@
 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 6d27b7b..4d7e58e 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 @@
 	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 637adb9..9f36ac7 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
  */
@@ -57,6 +58,19 @@
 	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
 	 * have exited coherency and its cache may be disabled, any access to
diff --git a/plat/nvidia/tegra/common/aarch64/tegra_helpers.S b/plat/nvidia/tegra/common/aarch64/tegra_helpers.S
index 6c8c4f0..72ecd54 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 @@
 	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_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 ec34a85..8edb024 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
  */
@@ -92,6 +92,16 @@
 /*******************************************************************************
  * 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.
  ******************************************************************************/
 static void tegra_pwr_domain_off(const psci_power_state_t *target_state)
 {
@@ -268,6 +278,7 @@
 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 84b3297..958a3f9 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 cc2ad86..71bea08 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 @@
 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_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 af4182e..8f88e28 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 @@
 	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 41a85ee..83d815a 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 @@
 	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 7f73ea5..2ec044c 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 @@
 	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);