test(psci): add tests for OS-initiated mode

Change-Id: I33e135f659aea600f71e053ac3db57eb0172e22b
Signed-off-by: Wing Li <wingers@google.com>
diff --git a/include/lib/tftf_lib.h b/include/lib/tftf_lib.h
index f9d1a6e..d265bb9 100644
--- a/include/lib/tftf_lib.h
+++ b/include/lib/tftf_lib.h
@@ -47,6 +47,7 @@
 			 uintptr_t entry_point_address,
 			 u_register_t context_id);
 int32_t tftf_psci_cpu_off(void);
+int32_t tftf_psci_set_suspend_mode(uint32_t mode);
 int32_t tftf_psci_affinity_info(u_register_t target_affinity,
 				uint32_t lowest_affinity_level);
 int32_t tftf_psci_node_hw_state(u_register_t target_cpu, uint32_t power_level);
diff --git a/include/plat/common/plat_topology.h b/include/plat/common/plat_topology.h
index 0ca5eff..fbae878 100644
--- a/include/plat/common/plat_topology.h
+++ b/include/plat/common/plat_topology.h
@@ -86,6 +86,8 @@
 	unsigned char is_present;
 } tftf_pwr_domain_node_t;
 
+extern tftf_pwr_domain_node_t tftf_pd_nodes[PLATFORM_NUM_AFFS];
+
 /*
  * Detect and store the platform topology so that test cases can query it later.
  */
diff --git a/include/runtime_services/psci.h b/include/runtime_services/psci.h
index f93ee40..caa74a9 100644
--- a/include/runtime_services/psci.h
+++ b/include/runtime_services/psci.h
@@ -208,6 +208,12 @@
 #define PSCI_E_INVALID_ADDRESS	-9
 
 /*******************************************************************************
+ * PSCI suspend mode related constants.
+ ******************************************************************************/
+#define PSCI_PLAT_COORD		0x0
+#define PSCI_OS_INIT		0x1
+
+/*******************************************************************************
  * PSCI affinity state related constants.
  ******************************************************************************/
 #define PSCI_STATE_ON		0x0
diff --git a/lib/psci/psci.c b/lib/psci/psci.c
index 857b01e..aace092 100644
--- a/lib/psci/psci.c
+++ b/lib/psci/psci.c
@@ -81,6 +81,17 @@
 	return ret_vals.ret0;
 }
 
+int32_t tftf_psci_set_suspend_mode(uint32_t mode)
+{
+	smc_args args = {
+		SMC_PSCI_SET_SUSPEND_MODE,
+		mode
+	};
+	smc_ret_values ret_vals;
+
+	ret_vals = tftf_smc(&args);
+	return ret_vals.ret0;
+}
 
 u_register_t tftf_psci_stat_residency(u_register_t target_cpu,
 		uint32_t power_state)
@@ -185,6 +196,8 @@
 			ret = PSCI_E_INVALID_PARAMS;
 		}
 	}
+	*state_id |= psci_make_local_state_id(PLAT_MAX_PWR_LEVEL + 1,
+					      affinity_level);
 
 	return ret;
 }
diff --git a/plat/arm/corstone1000/tests_to_skip.txt b/plat/arm/corstone1000/tests_to_skip.txt
index fdab230..a928249 100644
--- a/plat/arm/corstone1000/tests_to_skip.txt
+++ b/plat/arm/corstone1000/tests_to_skip.txt
@@ -12,4 +12,5 @@
 Timer framework Validation/Verify the timer interrupt generation
 CPU Hotplug/CPU hotplug
 PSCI CPU Suspend
+PSCI CPU Suspend in OSI mode
 PSCI STAT/for valid composite state CPU suspend
diff --git a/plat/arm/juno/juno32_tests_to_skip.txt b/plat/arm/juno/juno32_tests_to_skip.txt
index 078e363..83d342e 100644
--- a/plat/arm/juno/juno32_tests_to_skip.txt
+++ b/plat/arm/juno/juno32_tests_to_skip.txt
@@ -4,6 +4,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 #
 
+# OS-initiated mode is not supported on AArch32 Juno.
+PSCI CPU Suspend in OSI mode
+
 # System suspend is not supported on AArch32 Juno.
 PSCI System Suspend Validation
 PSCI STAT/Stats test cases after system suspend
diff --git a/plat/arm/juno/juno64_tests_to_skip.txt b/plat/arm/juno/juno64_tests_to_skip.txt
index 53c7e7f..636b0df 100644
--- a/plat/arm/juno/juno64_tests_to_skip.txt
+++ b/plat/arm/juno/juno64_tests_to_skip.txt
@@ -6,3 +6,6 @@
 
 # The multicore spurious interrupt test is known to cause problems on Juno
 IRQ support in TSP/Multicore spurious interrupt test
+
+# OS-initiated mode is not supported on Juno
+PSCI CPU Suspend in OSI mode
diff --git a/plat/arm/n1sdp/tests_to_skip.txt b/plat/arm/n1sdp/tests_to_skip.txt
index 21417e0..b6e87bf 100644
--- a/plat/arm/n1sdp/tests_to_skip.txt
+++ b/plat/arm/n1sdp/tests_to_skip.txt
@@ -7,6 +7,9 @@
 # Disable SMMUv3 tests
 SMMUv3 tests
 
+# OS-initiated mode is not supported on N1SDP
+PSCI CPU Suspend in OSI mode
+
 # PSCI is enabled but not tested
 PSCI STAT/Stats test cases after system suspend
 PSCI System Suspend Validation
diff --git a/plat/arm/rdinfra/rdn1edge/tests_to_skip.txt b/plat/arm/rdinfra/rdn1edge/tests_to_skip.txt
index 7fda40b..95360bc 100644
--- a/plat/arm/rdinfra/rdn1edge/tests_to_skip.txt
+++ b/plat/arm/rdinfra/rdn1edge/tests_to_skip.txt
@@ -4,6 +4,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 #
 
+# OS-initiated mode is not supported on RD-N1Edge
+PSCI CPU Suspend in OSI mode
+
 # System suspend is not supported as there are no wakeup sources in RD-N1Edge FVP
 PSCI STAT/Stats test cases after system suspend
 PSCI System Suspend Validation
diff --git a/plat/arm/rdinfra/rdn2/tests_to_skip.txt b/plat/arm/rdinfra/rdn2/tests_to_skip.txt
index 370f2b7..b8a433d 100644
--- a/plat/arm/rdinfra/rdn2/tests_to_skip.txt
+++ b/plat/arm/rdinfra/rdn2/tests_to_skip.txt
@@ -4,6 +4,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 #
 
+# OS-initiated mode is not supported on RD-N2
+PSCI CPU Suspend in OSI mode
+
 # System suspend is not supported as there are no wakeup sources in RD-N2 FVP
 PSCI STAT/Stats test cases after system suspend
 PSCI System Suspend Validation
diff --git a/plat/arm/rdinfra/rdv1/tests_to_skip.txt b/plat/arm/rdinfra/rdv1/tests_to_skip.txt
index 25e3414..9b3ff5f 100644
--- a/plat/arm/rdinfra/rdv1/tests_to_skip.txt
+++ b/plat/arm/rdinfra/rdv1/tests_to_skip.txt
@@ -4,6 +4,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 #
 
+# OS-initiated mode is not supported on RD-V1
+PSCI CPU Suspend in OSI mode
+
 # System suspend is not supported as there are no wakeup sources in RD-V1 FVP
 PSCI STAT/Stats test cases after system suspend
 PSCI System Suspend Validation
diff --git a/plat/arm/sgi/sgi575/tests_to_skip.txt b/plat/arm/sgi/sgi575/tests_to_skip.txt
index 5f132f5..1af24d8 100644
--- a/plat/arm/sgi/sgi575/tests_to_skip.txt
+++ b/plat/arm/sgi/sgi575/tests_to_skip.txt
@@ -4,6 +4,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 #
 
+# OS-initiated mode is not supported on SGI-575
+PSCI CPU Suspend in OSI mode
+
 # System suspend is not supported as there are no wakeup sources in SGI-575 FVP
 PSCI STAT/Stats test cases after system suspend
 PSCI System Suspend Validation
diff --git a/plat/arm/tc0/tests_to_skip.txt b/plat/arm/tc0/tests_to_skip.txt
index 5830c14..762bfa0 100644
--- a/plat/arm/tc0/tests_to_skip.txt
+++ b/plat/arm/tc0/tests_to_skip.txt
@@ -7,6 +7,9 @@
 # Disable SMMUv3 tests
 SMMUv3 tests
 
+# OS-initiated mode is not supported
+PSCI CPU Suspend in OSI mode
+
 # PSCI is enabled but not tested
 PSCI STAT/Stats test cases after system suspend
 PSCI System Suspend Validation
diff --git a/plat/common/plat_topology.c b/plat/common/plat_topology.c
index 5ff7a31..a9b9828 100644
--- a/plat/common/plat_topology.c
+++ b/plat/common/plat_topology.c
@@ -184,11 +184,14 @@
 static void update_pwrlvl_limits(void)
 {
 	int cpu_id, j, is_present;
-	unsigned int nodes_idx[PLATFORM_MAX_AFFLVL] = {-1};
+	unsigned int nodes_idx[PLATFORM_MAX_AFFLVL];
 	unsigned int temp_index[PLATFORM_MAX_AFFLVL];
 
 	unsigned int cpu_node_offset = tftf_pwr_domain_start_idx[0];
 
+	for (j = 0; j < PLATFORM_MAX_AFFLVL; j++)
+		nodes_idx[j] = -1;
+
 	for (cpu_id = 0; cpu_id < PLATFORM_CORE_COUNT; cpu_id++) {
 		get_parent_pwr_domain_nodes(cpu_id + cpu_node_offset,
 						PLATFORM_MAX_AFFLVL,
diff --git a/plat/nvidia/tegra186/tests_to_skip.txt b/plat/nvidia/tegra186/tests_to_skip.txt
index 1a18495..7086208 100644
--- a/plat/nvidia/tegra186/tests_to_skip.txt
+++ b/plat/nvidia/tegra186/tests_to_skip.txt
@@ -15,6 +15,9 @@
 PSCI CPU Suspend/CPU suspend to standby at level 2
 PSCI System Suspend Validation/Suspend system with cores in suspend
 
+# Tegra186 platforms do not support OS-initiated mode
+PSCI CPU Suspend in OSI mode
+
 # Tegra186 platforms are facing problems with system suspend
 PSCI System Suspend Validation
 
diff --git a/plat/nvidia/tegra194/tests_to_skip.txt b/plat/nvidia/tegra194/tests_to_skip.txt
index f1be76e..14eb0dd 100644
--- a/plat/nvidia/tegra194/tests_to_skip.txt
+++ b/plat/nvidia/tegra194/tests_to_skip.txt
@@ -15,6 +15,9 @@
 PSCI CPU Suspend/CPU suspend to standby at level 2
 PSCI System Suspend Validation/Suspend system with cores in suspend
 
+# Tegra194 platforms do not support OS-initiated mode
+PSCI CPU Suspend in OSI mode
+
 # Tegra194 platforms enter system suspend only from the boot core
 PSCI System Suspend Validation/system suspend from all cores
 
diff --git a/plat/nvidia/tegra210/tests_to_skip.txt b/plat/nvidia/tegra210/tests_to_skip.txt
index 62fb958..6c68967 100644
--- a/plat/nvidia/tegra210/tests_to_skip.txt
+++ b/plat/nvidia/tegra210/tests_to_skip.txt
@@ -15,6 +15,9 @@
 PSCI CPU Suspend/CPU suspend to powerdown at level 1
 PSCI CPU Suspend/CPU suspend to powerdown at level 2
 
+# Tegra186 platforms do not support OS-initiated mode
+PSCI CPU Suspend in OSI mode
+
 # Tegra210 platforms enter system suspend only from the boot core
 PSCI System Suspend Validation
 
diff --git a/tftf/tests/runtime_services/standard_service/psci/api_tests/cpu_suspend/test_suspend.c b/tftf/tests/runtime_services/standard_service/psci/api_tests/cpu_suspend/test_suspend.c
index 9e9998c..7da63ca 100644
--- a/tftf/tests/runtime_services/standard_service/psci/api_tests/cpu_suspend/test_suspend.c
+++ b/tftf/tests/runtime_services/standard_service/psci/api_tests/cpu_suspend/test_suspend.c
@@ -20,13 +20,15 @@
 #include <timer.h>
 
 /*
- * Desired affinity level and state type (standby or powerdown) for the next
- * CPU_SUSPEND operation. We need these shared variables because there is no way
- * to pass arguments to non-lead CPUs...
+ * Desired affinity level, state type (standby or powerdown), and entry time for
+ * each CPU in the next CPU_SUSPEND operation. We need these shared variables
+ * because there is no way to pass arguments to non-lead CPUs...
  */
-static unsigned int test_aff_level;
-static unsigned int test_suspend_type;
+static unsigned int test_aff_level[PLATFORM_CORE_COUNT];
+static unsigned int test_suspend_type[PLATFORM_CORE_COUNT];
+static unsigned int test_suspend_entry_time[PLATFORM_CORE_COUNT];
 
+static event_t cpu_booted[PLATFORM_CORE_COUNT];
 static event_t cpu_ready[PLATFORM_CORE_COUNT];
 
 /*
@@ -53,6 +55,38 @@
 	return 0;
 }
 
+static test_result_t test_init(unsigned int aff_level,
+			       unsigned int suspend_type)
+{
+	if (aff_level > MPIDR_MAX_AFFLVL)
+		return TEST_RESULT_SKIPPED;
+
+	assert((suspend_type == PSTATE_TYPE_POWERDOWN) ||
+	       (suspend_type == PSTATE_TYPE_STANDBY));
+
+	for (unsigned int i = 0; i < PLATFORM_CORE_COUNT; ++i) {
+		/* Export these variables for the non-lead CPUs */
+		test_aff_level[i] = aff_level;
+		test_suspend_type[i] = suspend_type;
+		test_suspend_entry_time[i] =
+			PLAT_SUSPEND_ENTRY_TIME * PLATFORM_CORE_COUNT;
+
+		/*
+		 * All testcases in this file use the same arrays so it needs to
+		 * be re-initialised each time.
+		 */
+		tftf_init_event(&cpu_booted[i]);
+		tftf_init_event(&cpu_ready[i]);
+		tftf_init_event(&event_received_wake_irq[i]);
+		requested_irq_received[i] = 0;
+	}
+
+	/* Ensure the above writes are seen before any read */
+	dmbsy();
+
+	return TEST_RESULT_SUCCESS;
+}
+
 /*
  * Suspend the calling (non-lead) CPU.
  * 1) Program a wake-up event to come out of suspend state
@@ -64,21 +98,26 @@
 {
 	unsigned int mpid = read_mpidr_el1();
 	unsigned int core_pos = platform_get_core_pos(mpid);
+	unsigned int aff_level = test_aff_level[core_pos];
+	unsigned int suspend_type = test_suspend_type[core_pos];
 	uint32_t power_state, stateid;
 	int rc, expected_return_val;
 	u_register_t flags;
 
 	tftf_timer_register_handler(requested_irq_handler);
 
-	/* Tell the lead CPU that the calling CPU is about to suspend itself */
-	tftf_send_event(&cpu_ready[core_pos]);
+	/* Signal to the lead CPU that the calling CPU has entered the test */
+	tftf_send_event(&cpu_booted[core_pos]);
+
+	/* Wait for signal from the lead CPU before suspending itself */
+	tftf_wait_for_event(&cpu_ready[core_pos]);
 
 	/* IRQs need to be disabled prior to programming the timer */
 	/* Preserve DAIF flags*/
 	flags = read_daif();
 	disable_irq();
 
-	rc = tftf_program_timer(PLAT_SUSPEND_ENTRY_TIME);
+	rc = tftf_program_timer(test_suspend_entry_time[core_pos]);
 	if (rc != 0) {
 		/* Restore previous DAIF flags */
 		write_daif(flags);
@@ -87,15 +126,14 @@
 		return TEST_RESULT_FAIL;
 	}
 
-	expected_return_val = tftf_psci_make_composite_state_id(test_aff_level,
-				   test_suspend_type, &stateid);
+	expected_return_val = tftf_psci_make_composite_state_id(aff_level,
+								suspend_type,
+								&stateid);
 
 	/*
 	 * Suspend the calling CPU to the desired affinity level and power state
 	 */
-	power_state =  tftf_make_psci_pstate(test_aff_level,
-					     test_suspend_type,
-					     stateid);
+	power_state = tftf_make_psci_pstate(aff_level, suspend_type, stateid);
 	rc = tftf_cpu_suspend(power_state);
 
 	/* Restore previous DAIF flags */
@@ -126,38 +164,17 @@
  *
  * The test is skipped if an error occurs during the bring-up of non-lead CPUs.
  */
-static test_result_t test_psci_suspend(unsigned int aff_level,
-				     unsigned int suspend_type)
+static test_result_t test_psci_suspend(void)
 {
 	unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK;
 	unsigned int target_mpid, target_node;
 	unsigned int core_pos;
+	unsigned int aff_level, suspend_type;
 	uint32_t power_state, stateid;
 	int rc, expected_return_val;
+	int aff_info;
 	u_register_t flags;
 
-	if (aff_level > MPIDR_MAX_AFFLVL)
-		return TEST_RESULT_SKIPPED;
-
-	assert((suspend_type == PSTATE_TYPE_POWERDOWN) ||
-	       (suspend_type == PSTATE_TYPE_STANDBY));
-
-	/* Export these variables for the non-lead CPUs */
-	test_aff_level = aff_level;
-	test_suspend_type = suspend_type;
-
-	/*
-	 * All testcases in this file use the same cpu_ready[] array so it needs
-	 * to be re-initialised each time.
-	 */
-	for (unsigned int i = 0; i < PLATFORM_CORE_COUNT; ++i) {
-		tftf_init_event(&cpu_ready[i]);
-		tftf_init_event(&event_received_wake_irq[i]);
-		requested_irq_received[i] = 0;
-	}
-	/* Ensure the above writes are seen before any read */
-	dmbsy();
-
 	/*
 	 * Preparation step: Power on all cores.
 	 */
@@ -168,8 +185,8 @@
 			continue;
 
 		rc = tftf_cpu_on(target_mpid,
-				(uintptr_t) suspend_non_lead_cpu,
-				0);
+				 (uintptr_t) suspend_non_lead_cpu,
+				 0);
 		if (rc != PSCI_E_SUCCESS) {
 			tftf_testcase_printf(
 				"Failed to power on CPU 0x%x (%d)\n",
@@ -178,7 +195,7 @@
 		}
 	}
 
-	/* Wait for all non-lead CPUs to be ready */
+	/* Wait for all non-lead CPUs to enter the test */
 	for_each_cpu(target_node) {
 		target_mpid = tftf_get_mpidr_from_node(target_node);
 		/* Skip lead CPU */
@@ -186,7 +203,19 @@
 			continue;
 
 		core_pos = platform_get_core_pos(target_mpid);
-		tftf_wait_for_event(&cpu_ready[core_pos]);
+		tftf_wait_for_event(&cpu_booted[core_pos]);
+	}
+
+	/* Signal to each non-lead CPU to suspend itself */
+	for_each_cpu(target_node) {
+		target_mpid = tftf_get_mpidr_from_node(target_node);
+		/* Skip lead CPU */
+		if (target_mpid == lead_mpid)
+			continue;
+
+		core_pos = platform_get_core_pos(target_mpid);
+		tftf_send_event(&cpu_ready[core_pos]);
+		waitms(PLAT_SUSPEND_ENTRY_TIME);
 	}
 
 	/* IRQs need to be disabled prior to programming the timer */
@@ -198,7 +227,7 @@
 	 * Program the timer, this will serve as the
 	 * wake-up event to come out of suspend state.
 	 */
-	rc = tftf_program_timer(PLAT_SUSPEND_ENTRY_TIME);
+	rc = tftf_program_timer(PLAT_SUSPEND_ENTRY_TIME * PLATFORM_CORE_COUNT);
 	if (rc) {
 		/* Restore previous DAIF flags */
 		write_daif(flags);
@@ -207,16 +236,18 @@
 		return TEST_RESULT_FAIL;
 	}
 
-	expected_return_val = tftf_psci_make_composite_state_id(test_aff_level,
-				   test_suspend_type, &stateid);
+	core_pos = platform_get_core_pos(lead_mpid);
+	aff_level = test_aff_level[core_pos];
+	suspend_type = test_suspend_type[core_pos];
+	expected_return_val = tftf_psci_make_composite_state_id(aff_level,
+								suspend_type,
+								&stateid);
 
 	/*
 	 * Suspend the calling CPU to the desired affinity level and power state
 	 */
-	power_state = tftf_make_psci_pstate(test_aff_level,
-					    test_suspend_type,
-					    stateid);
-	if (test_aff_level >= PSTATE_AFF_LVL_2)
+	power_state = tftf_make_psci_pstate(aff_level, suspend_type, stateid);
+	if (aff_level >= PSTATE_AFF_LVL_2)
 		rc = tftf_cpu_suspend_save_sys_ctx(power_state);
 	else
 		rc = tftf_cpu_suspend(power_state);
@@ -246,6 +277,19 @@
 		tftf_wait_for_event(&event_received_wake_irq[core_pos]);
 	}
 
+	/* Wait for all non-lead CPUs to power down */
+	for_each_cpu(target_node) {
+		target_mpid = tftf_get_mpidr_from_node(target_node);
+		/* Skip lead CPU */
+		if (target_mpid == lead_mpid)
+			continue;
+
+		do {
+			aff_info = tftf_psci_affinity_info(target_mpid,
+							   MPIDR_AFFLVL0);
+		} while (aff_info != PSCI_STATE_OFF);
+	}
+
 	if (rc == expected_return_val)
 		return TEST_RESULT_SUCCESS;
 
@@ -255,11 +299,27 @@
 }
 
 /*
+ * @Test_Aim@ Suspend to the specified suspend type targeted at the specified
+ * affinity level
+ */
+static test_result_t test_psci_suspend_level(unsigned int aff_level,
+					     unsigned int suspend_type)
+{
+	int rc;
+
+	rc = test_init(aff_level, suspend_type);
+	if (rc != TEST_RESULT_SUCCESS)
+		return rc;
+
+	return test_psci_suspend();
+}
+
+/*
  * @Test_Aim@ Suspend to powerdown state targeted at affinity level 0
  */
 test_result_t test_psci_suspend_powerdown_level0(void)
 {
-	return test_psci_suspend(PSTATE_AFF_LVL_0, PSTATE_TYPE_POWERDOWN);
+	return test_psci_suspend_level(PSTATE_AFF_LVL_0, PSTATE_TYPE_POWERDOWN);
 }
 
 /*
@@ -267,7 +327,7 @@
  */
 test_result_t test_psci_suspend_standby_level0(void)
 {
-	return test_psci_suspend(PSTATE_AFF_LVL_0, PSTATE_TYPE_STANDBY);
+	return test_psci_suspend_level(PSTATE_AFF_LVL_0, PSTATE_TYPE_STANDBY);
 }
 
 /*
@@ -275,7 +335,7 @@
  */
 test_result_t test_psci_suspend_powerdown_level1(void)
 {
-	return test_psci_suspend(PSTATE_AFF_LVL_1, PSTATE_TYPE_POWERDOWN);
+	return test_psci_suspend_level(PSTATE_AFF_LVL_1, PSTATE_TYPE_POWERDOWN);
 }
 
 /*
@@ -283,7 +343,7 @@
  */
 test_result_t test_psci_suspend_standby_level1(void)
 {
-	return test_psci_suspend(PSTATE_AFF_LVL_1, PSTATE_TYPE_STANDBY);
+	return test_psci_suspend_level(PSTATE_AFF_LVL_1, PSTATE_TYPE_STANDBY);
 }
 
 /*
@@ -291,7 +351,7 @@
  */
 test_result_t test_psci_suspend_powerdown_level2(void)
 {
-	return test_psci_suspend(PSTATE_AFF_LVL_2, PSTATE_TYPE_POWERDOWN);
+	return test_psci_suspend_level(PSTATE_AFF_LVL_2, PSTATE_TYPE_POWERDOWN);
 }
 
 /*
@@ -299,7 +359,7 @@
  */
 test_result_t test_psci_suspend_standby_level2(void)
 {
-	return test_psci_suspend(PSTATE_AFF_LVL_2, PSTATE_TYPE_STANDBY);
+	return test_psci_suspend_level(PSTATE_AFF_LVL_2, PSTATE_TYPE_STANDBY);
 }
 
 /*
@@ -307,7 +367,7 @@
  */
 test_result_t test_psci_suspend_powerdown_level3(void)
 {
-	return test_psci_suspend(PSTATE_AFF_LVL_3, PSTATE_TYPE_POWERDOWN);
+	return test_psci_suspend_level(PSTATE_AFF_LVL_3, PSTATE_TYPE_POWERDOWN);
 }
 
 /*
@@ -315,5 +375,317 @@
  */
 test_result_t test_psci_suspend_standby_level3(void)
 {
-	return test_psci_suspend(PSTATE_AFF_LVL_3, PSTATE_TYPE_STANDBY);
+	return test_psci_suspend_level(PSTATE_AFF_LVL_3, PSTATE_TYPE_STANDBY);
+}
+
+/*
+ * @Test_Aim@ Suspend to the specified suspend type targeted at affinity level 0
+ * in OS-initiated mode
+ */
+static test_result_t test_psci_suspend_level0_osi(unsigned int suspend_type)
+{
+	int err, rc;
+
+	err = tftf_psci_set_suspend_mode(PSCI_OS_INIT);
+	if (err != PSCI_E_SUCCESS)
+		return TEST_RESULT_FAIL;
+
+	rc = test_psci_suspend_level(PSTATE_AFF_LVL_0, suspend_type);
+
+	err = tftf_psci_set_suspend_mode(PSCI_PLAT_COORD);
+	if (err != PSCI_E_SUCCESS)
+		return TEST_RESULT_FAIL;
+
+	return rc;
+}
+
+/*
+ * @Test_Aim@ Suspend to powerdown state targeted at affinity level 0 in
+ * OS-initiated mode
+ */
+test_result_t test_psci_suspend_powerdown_level0_osi(void)
+{
+	return test_psci_suspend_level0_osi(PSTATE_TYPE_POWERDOWN);
+}
+
+/*
+ * @Test_Aim@ Suspend to standby state targeted at affinity level 0 in
+ * OS-initiated mode
+ */
+test_result_t test_psci_suspend_standby_level0_osi(void)
+{
+	return test_psci_suspend_level0_osi(PSTATE_TYPE_STANDBY);
+}
+
+/*
+ * @Test_Aim@ Suspend to the specified suspend type targeted at affinity level 1
+ * in OS-initiated mode
+ */
+static test_result_t test_psci_suspend_level1_osi(unsigned int suspend_type)
+{
+	unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK;
+	unsigned int lead_lvl_1_node =
+		tftf_get_parent_node_from_mpidr(lead_mpid, PSTATE_AFF_LVL_1);
+	unsigned int target_mpid, target_node, lvl_1_node, lvl_1_end_node;
+	unsigned int core_pos;
+	tftf_pwr_domain_node_t pd_node;
+	int err, rc;
+
+	err = test_init(PSTATE_AFF_LVL_1, suspend_type);
+	if (err != TEST_RESULT_SUCCESS)
+		return err;
+
+	err = tftf_psci_set_suspend_mode(PSCI_OS_INIT);
+	if (err != PSCI_E_SUCCESS)
+		return TEST_RESULT_FAIL;
+
+	for_each_power_domain_idx(lvl_1_node, PSTATE_AFF_LVL_1) {
+		pd_node = tftf_pd_nodes[lvl_1_node];
+		lvl_1_end_node = pd_node.cpu_start_node + pd_node.ncpus - 1;
+
+		for_each_cpu_in_power_domain(target_node, lvl_1_node) {
+			target_mpid = tftf_get_mpidr_from_node(target_node);
+			/* Skip lead CPU as it is already on */
+			if (target_mpid == lead_mpid)
+				continue;
+
+			core_pos = platform_get_core_pos(target_mpid);
+			if (target_node == lvl_1_end_node &&
+			    lvl_1_node != lead_lvl_1_node) {
+				test_aff_level[core_pos] = PSTATE_AFF_LVL_1;
+			} else {
+				test_aff_level[core_pos] = PSTATE_AFF_LVL_0;
+			}
+		}
+	}
+
+	rc = test_psci_suspend();
+
+	err = tftf_psci_set_suspend_mode(PSCI_PLAT_COORD);
+	if (err != PSCI_E_SUCCESS)
+		return TEST_RESULT_FAIL;
+
+	return rc;
+}
+
+/*
+ * @Test_Aim@ Suspend to powerdown state targeted at affinity level 1 in
+ * OS-initiated mode
+ */
+test_result_t test_psci_suspend_powerdown_level1_osi(void)
+{
+	return test_psci_suspend_level1_osi(PSTATE_TYPE_POWERDOWN);
+}
+
+/*
+ * @Test_Aim@ Suspend to standby state targeted at affinity level 1 in
+ * OS-initiated mode
+ */
+test_result_t test_psci_suspend_standby_level1_osi(void)
+{
+	return test_psci_suspend_level1_osi(PSTATE_TYPE_STANDBY);
+}
+
+/*
+ * @Test_Aim@ Suspend to the specified suspend type targeted at affinity level 2
+ * in OS-initiated mode
+ */
+static test_result_t test_psci_suspend_level2_osi(unsigned int suspend_type)
+{
+	unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK;
+	unsigned int lead_lvl_1_node =
+		tftf_get_parent_node_from_mpidr(lead_mpid, PSTATE_AFF_LVL_1);
+	unsigned int lead_lvl_2_node =
+		tftf_get_parent_node_from_mpidr(lead_mpid, PSTATE_AFF_LVL_2);
+	unsigned int target_mpid, target_node;
+	unsigned int lvl_1_node, lvl_2_node;
+	unsigned int lvl_1_end_node, lvl_2_end_node;
+	unsigned int core_pos;
+	tftf_pwr_domain_node_t lvl_1_pd_node, lvl_2_pd_node;
+	int err, rc;
+
+	err = test_init(PSTATE_AFF_LVL_2, suspend_type);
+	if (err != TEST_RESULT_SUCCESS)
+		return err;
+
+	err = tftf_psci_set_suspend_mode(PSCI_OS_INIT);
+	if (err != PSCI_E_SUCCESS)
+		return TEST_RESULT_FAIL;
+
+	for_each_power_domain_idx(lvl_2_node, PSTATE_AFF_LVL_2) {
+		lvl_2_pd_node = tftf_pd_nodes[lvl_2_node];
+		lvl_2_end_node =
+			lvl_2_pd_node.cpu_start_node + lvl_2_pd_node.ncpus - 1;
+
+		for_each_power_domain_idx(lvl_1_node, PSTATE_AFF_LVL_1) {
+			lvl_1_pd_node = tftf_pd_nodes[lvl_1_node];
+			if (lvl_1_pd_node.parent_node != lvl_2_node)
+				continue;
+
+			lvl_1_end_node =
+				lvl_1_pd_node.cpu_start_node +
+				lvl_1_pd_node.ncpus - 1;
+
+			for_each_cpu_in_power_domain(target_node, lvl_1_node) {
+				target_mpid =
+					tftf_get_mpidr_from_node(target_node);
+				/* Skip lead CPU as it is already on */
+				if (target_mpid == lead_mpid)
+					continue;
+
+				core_pos = platform_get_core_pos(target_mpid);
+				if (target_node == lvl_1_end_node &&
+				    target_node == lvl_2_end_node &&
+				    lvl_2_node != lead_lvl_2_node) {
+					test_aff_level[core_pos] =
+							PSTATE_AFF_LVL_2;
+				} else if (target_node == lvl_1_end_node &&
+					   lvl_1_node != lead_lvl_1_node) {
+					test_aff_level[core_pos] =
+							PSTATE_AFF_LVL_1;
+				} else {
+					test_aff_level[core_pos] =
+							PSTATE_AFF_LVL_0;
+				}
+			}
+		}
+
+	}
+
+	rc = test_psci_suspend();
+
+	err = tftf_psci_set_suspend_mode(PSCI_PLAT_COORD);
+	if (err != PSCI_E_SUCCESS)
+		return TEST_RESULT_FAIL;
+
+	return rc;
+}
+
+/*
+ * @Test_Aim@ Suspend to powerdown state targeted at affinity level 2 in
+ * OS-initiated mode
+ */
+test_result_t test_psci_suspend_powerdown_level2_osi(void)
+{
+	return test_psci_suspend_level2_osi(PSTATE_TYPE_POWERDOWN);
+}
+
+/*
+ * @Test_Aim@ Suspend to standby state targeted at affinity level 2 in
+ * OS-initiated mode
+ */
+test_result_t test_psci_suspend_standby_level2_osi(void)
+{
+	return test_psci_suspend_level2_osi(PSTATE_TYPE_STANDBY);
+}
+
+/*
+ * @Test_Aim@ Suspend to the specified suspend type targeted at affinity level 3
+ * in OS-initiated mode
+ */
+static test_result_t test_psci_suspend_level3_osi(unsigned int suspend_type)
+{
+	unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK;
+	unsigned int lead_lvl_1_node =
+		tftf_get_parent_node_from_mpidr(lead_mpid, PSTATE_AFF_LVL_1);
+	unsigned int lead_lvl_2_node =
+		tftf_get_parent_node_from_mpidr(lead_mpid, PSTATE_AFF_LVL_2);
+	unsigned int lead_lvl_3_node =
+		tftf_get_parent_node_from_mpidr(lead_mpid, PSTATE_AFF_LVL_3);
+	unsigned int target_mpid, target_node;
+	unsigned int lvl_1_node, lvl_2_node, lvl_3_node;
+	unsigned int lvl_1_end_node, lvl_2_end_node, lvl_3_end_node;
+	unsigned int core_pos;
+	tftf_pwr_domain_node_t lvl_1_pd_node, lvl_2_pd_node, lvl_3_pd_node;
+	int err, rc;
+
+	err = test_init(PSTATE_AFF_LVL_3, PSTATE_TYPE_POWERDOWN);
+	if (err != TEST_RESULT_SUCCESS)
+		return err;
+
+	err = tftf_psci_set_suspend_mode(PSCI_OS_INIT);
+	if (err != PSCI_E_SUCCESS)
+		return TEST_RESULT_FAIL;
+
+	for_each_power_domain_idx(lvl_3_node, PSTATE_AFF_LVL_3) {
+		lvl_3_pd_node = tftf_pd_nodes[lvl_3_node];
+		lvl_3_end_node =
+			lvl_3_pd_node.cpu_start_node + lvl_3_pd_node.ncpus - 1;
+
+		for_each_power_domain_idx(lvl_2_node, PSTATE_AFF_LVL_2) {
+			lvl_2_pd_node = tftf_pd_nodes[lvl_2_node];
+			if (lvl_2_pd_node.parent_node != lvl_3_node)
+				continue;
+
+			lvl_2_end_node =
+				lvl_2_pd_node.cpu_start_node + lvl_2_pd_node.ncpus - 1;
+
+			for_each_power_domain_idx(lvl_1_node, PSTATE_AFF_LVL_1) {
+				lvl_1_pd_node = tftf_pd_nodes[lvl_1_node];
+				if (lvl_1_pd_node.parent_node != lvl_2_node)
+					continue;
+
+				lvl_1_end_node =
+					lvl_1_pd_node.cpu_start_node +
+					lvl_1_pd_node.ncpus - 1;
+
+				for_each_cpu_in_power_domain(target_node, lvl_1_node) {
+					target_mpid =
+						tftf_get_mpidr_from_node(target_node);
+					/* Skip lead CPU as it is already on */
+					if (target_mpid == lead_mpid)
+						continue;
+
+					core_pos = platform_get_core_pos(target_mpid);
+					if (target_node == lvl_1_end_node &&
+					    target_node == lvl_2_end_node &&
+					    target_node == lvl_3_end_node &&
+					    lvl_3_node != lead_lvl_3_node) {
+						test_aff_level[core_pos] =
+								PSTATE_AFF_LVL_3;
+					}
+					if (target_node == lvl_1_end_node &&
+					    target_node == lvl_2_end_node &&
+					    lvl_2_node != lead_lvl_2_node) {
+						test_aff_level[core_pos] =
+								PSTATE_AFF_LVL_2;
+					} else if (target_node == lvl_1_end_node &&
+						   lvl_1_node != lead_lvl_1_node) {
+						test_aff_level[core_pos] =
+								PSTATE_AFF_LVL_1;
+					} else {
+						test_aff_level[core_pos] =
+								PSTATE_AFF_LVL_0;
+					}
+				}
+			}
+
+		}
+	}
+
+	rc = test_psci_suspend();
+
+	err = tftf_psci_set_suspend_mode(PSCI_PLAT_COORD);
+	if (err != PSCI_E_SUCCESS)
+		return TEST_RESULT_FAIL;
+
+	return rc;
+}
+
+/*
+ * @Test_Aim@ Suspend to powerdown state targeted at affinity level 3 in
+ * OS-initiated mode
+ */
+test_result_t test_psci_suspend_powerdown_level3_osi(void)
+{
+	return test_psci_suspend_level3_osi(PSTATE_TYPE_POWERDOWN);
+}
+
+/*
+ * @Test_Aim@ Suspend to standby state targeted at affinity level 3 in
+ * OS-initiated mode
+ */
+test_result_t test_psci_suspend_standby_level3_osi(void)
+{
+	return test_psci_suspend_level3_osi(PSTATE_TYPE_STANDBY);
 }
diff --git a/tftf/tests/tests-psci.xml b/tftf/tests/tests-psci.xml
index e2be557..e9c612b 100644
--- a/tftf/tests/tests-psci.xml
+++ b/tftf/tests/tests-psci.xml
@@ -51,6 +51,18 @@
     <testcase name="CPU suspend to standby at level 3" function="test_psci_suspend_standby_level3" />
   </testsuite>
 
+  <testsuite name="PSCI CPU Suspend in OSI mode" description="Test PSCI CPU Suspend support in OSI mode">
+    <testcase name="CPU suspend to powerdown at level 0 in OSI mode" function="test_psci_suspend_powerdown_level0_osi" />
+    <testcase name="CPU suspend to powerdown at level 1 in OSI mode" function="test_psci_suspend_powerdown_level1_osi" />
+    <testcase name="CPU suspend to powerdown at level 2 in OSI mode" function="test_psci_suspend_powerdown_level2_osi" />
+    <testcase name="CPU suspend to powerdown at level 3 in OSI mode" function="test_psci_suspend_powerdown_level3_osi" />
+
+    <testcase name="CPU suspend to standby at level 0 in OSI mode" function="test_psci_suspend_standby_level0_osi" />
+    <testcase name="CPU suspend to standby at level 1 in OSI mode" function="test_psci_suspend_standby_level1_osi" />
+    <testcase name="CPU suspend to standby at level 2 in OSI mode" function="test_psci_suspend_standby_level2_osi" />
+    <testcase name="CPU suspend to standby at level 3 in OSI mode" function="test_psci_suspend_standby_level3_osi" />
+  </testsuite>
+
   <testsuite name="PSCI STAT" description="Test PSCI STAT support Core level">
     <testcase name="for valid composite state CPU suspend" function="test_psci_stat_all_power_states" />
     <testcase name="Stats test cases for CPU OFF" function="test_psci_stats_cpu_off" />