Add power management support in the SPD

This patch implements a set of handlers in the SPD which are called by
the PSCI runtime service upon receiving a power management
operation. These handlers in turn pass control to the Secure Payload
image if required before returning control to PSCI. This ensures that
the Secure Payload has complete visibility of all power transitions in
the system and can prepare accordingly.

Change-Id: I2d1dba5629b7cf2d53999d39fe807dfcf3f62fe2
diff --git a/services/psci/psci_afflvl_off.c b/services/psci/psci_afflvl_off.c
index 72557aa..24c212f 100644
--- a/services/psci/psci_afflvl_off.c
+++ b/services/psci/psci_afflvl_off.c
@@ -56,9 +56,21 @@
 	psci_set_state(cpu_node, PSCI_STATE_OFF);
 
 	/*
-	 * Generic management: Get the index for clearing any
-	 * lingering re-entry information
+	 * Generic management: Get the index for clearing any lingering re-entry
+	 * information and allow the secure world to switch itself off
+	 */
+
+	/*
+	 * Call the cpu off handler registered by the Secure Payload Dispatcher
+	 * to let it do any bookeeping. Assume that the SPD always reports an
+	 * E_DENIED error if SP refuse to power down
 	 */
+	if (spd_pm.svc_off) {
+		rc = spd_pm.svc_off(0);
+		if (rc)
+			return rc;
+	}
+
 	index = cpu_node->data;
 	memset(&psci_ns_entry_info[index], 0, sizeof(psci_ns_entry_info[index]));
 
diff --git a/services/psci/psci_afflvl_on.c b/services/psci/psci_afflvl_on.c
index d22904c..ee16c73 100644
--- a/services/psci/psci_afflvl_on.c
+++ b/services/psci/psci_afflvl_on.c
@@ -91,6 +91,14 @@
 		return rc;
 
 	/*
+	 * Call the cpu on handler registered by the Secure Payload Dispatcher
+	 * to let it do any bookeeping. If the handler encounters an error, it's
+	 * expected to assert within
+	 */
+	if (spd_pm.svc_on)
+		spd_pm.svc_on(target_cpu);
+
+	/*
 	 * Arch. management: Derive the re-entry information for
 	 * the non-secure world from the non-secure state from
 	 * where this call originated.
@@ -365,6 +373,24 @@
 	bl31_arch_setup();
 
 	/*
+	 * Use the more complex exception vectors to enable SPD
+	 * initialisation. SP_EL3 should point to a 'cpu_context'
+	 * structure which has an exception stack allocated. The
+	 * calling cpu should have set the context already
+	 */
+	assert(cm_get_context(mpidr, NON_SECURE));
+	cm_set_next_eret_context(NON_SECURE);
+	write_vbar_el3((uint64_t) runtime_exceptions);
+
+	/*
+	 * Call the cpu on finish handler registered by the Secure Payload
+	 * Dispatcher to let it do any bookeeping. If the handler encounters an
+	 * error, it's expected to assert within
+	 */
+	if (spd_pm.svc_on_finish)
+		spd_pm.svc_on_finish(0);
+
+	/*
 	 * Generic management: Now we just need to retrieve the
 	 * information that we had stashed away during the cpu_on
 	 * call to set this cpu on its way. First get the index
diff --git a/services/psci/psci_afflvl_suspend.c b/services/psci/psci_afflvl_suspend.c
index 4391580..62d270f 100644
--- a/services/psci/psci_afflvl_suspend.c
+++ b/services/psci/psci_afflvl_suspend.c
@@ -94,6 +94,19 @@
 	/* Sanity check to safeguard against data corruption */
 	assert(cpu_node->level == MPIDR_AFFLVL0);
 
+	/*
+	 * Generic management: Store the re-entry information for the non-secure
+	 * world and allow the secure world to suspend itself
+	 */
+
+	/*
+	 * Call the cpu suspend handler registered by the Secure Payload
+	 * Dispatcher to let it do any bookeeping. If the handler encounters an
+	 * error, it's expected to assert within
+	 */
+	if (spd_pm.svc_suspend)
+		spd_pm.svc_suspend(power_state);
+
 	/* State management: mark this cpu as suspended */
 	psci_set_state(cpu_node, PSCI_STATE_SUSPEND);
 
@@ -395,6 +408,7 @@
 						aff_map_node *cpu_node)
 {
 	unsigned int index, plat_state, state, rc = PSCI_E_SUCCESS;
+	int32_t suspend_level;
 
 	assert(cpu_node->level == MPIDR_AFFLVL0);
 
@@ -430,6 +444,27 @@
 	rc = PSCI_E_SUCCESS;
 
 	/*
+	 * Use the more complex exception vectors to enable SPD
+	 * initialisation. SP_EL3 should point to a 'cpu_context'
+	 * structure which has an exception stack allocated. The
+	 * non-secure context should have been set on this cpu
+	 * prior to suspension.
+	 */
+	assert(cm_get_context(mpidr, NON_SECURE));
+	cm_set_next_eret_context(NON_SECURE);
+	write_vbar_el3((uint64_t) runtime_exceptions);
+
+	/*
+	 * Call the cpu suspend finish handler registered by the Secure Payload
+	 * Dispatcher to let it do any bookeeping. If the handler encounters an
+	 * error, it's expected to assert within
+	 */
+	if (spd_pm.svc_suspend) {
+		suspend_level = psci_get_suspend_afflvl(cpu_node);
+		spd_pm.svc_suspend_finish(suspend_level);
+	}
+
+	/*
 	 * Generic management: Now we just need to retrieve the
 	 * information that we had stashed away during the suspend
 	 * call to set this cpu on its way.
diff --git a/services/psci/psci_common.c b/services/psci/psci_common.c
index 214db78..cacd97e 100644
--- a/services/psci/psci_common.c
+++ b/services/psci/psci_common.c
@@ -40,6 +40,12 @@
 #include <runtime_svc.h>
 #include "debug.h"
 
+/*
+ * Provide a null weak instantiation for SPD power management operations. An SPD
+ * can define its own instance overriding this one
+ */
+const spd_pm_ops __attribute__((weak)) spd_pm = {0};
+
 /*******************************************************************************
  * Arrays that contains information needs to resume a cpu's execution when woken
  * out of suspend or off states. 'psci_ns_einfo_idx' keeps track of the next
diff --git a/services/psci/psci_entry.S b/services/psci/psci_entry.S
index 15e074c..361dfde 100644
--- a/services/psci/psci_entry.S
+++ b/services/psci/psci_entry.S
@@ -109,18 +109,6 @@
 	mov	x0, x19
 	bl	platform_set_stack
 
-	/* ---------------------------------------------
-	 * Now that the context management has been set
-	 * up, enable full runtime exception handling.
-	 * SP_EL3 is pointing to a 'cpu_context'
-	 * structure which has an exception stack
-	 * allocated. Since we're just about to leave
-	 * this EL with ERET, we don't need an ISB here
-	 * ---------------------------------------------
-	 */
-	adr	x0, runtime_exceptions
-	msr	vbar_el3, x0
-
 	zero_callee_saved_regs
 	b	el3_exit
 _panic:
diff --git a/services/psci/psci_main.c b/services/psci/psci_main.c
index 67f189d..ca3a5a0 100644
--- a/services/psci/psci_main.c
+++ b/services/psci/psci_main.c
@@ -178,7 +178,7 @@
 /* Unimplemented */
 unsigned int psci_migrate_info_type(void)
 {
-	return PSCI_TOS_NOT_PRESENT;
+	return PSCI_TOS_NOT_PRESENT_MP;
 }
 
 unsigned long psci_migrate_info_up_cpu(void)
diff --git a/services/psci/psci_private.h b/services/psci/psci_private.h
index 3d7ae74..351cbe8 100644
--- a/services/psci/psci_private.h
+++ b/services/psci/psci_private.h
@@ -96,6 +96,12 @@
 extern afflvl_power_on_finisher psci_afflvl_sus_finish_handlers[];
 
 /*******************************************************************************
+ * Weak declarations to allow PSCI to cope on a system where the Secure Payload
+ * Dispatcher is missing. An SPD will define this structure when present.
+ ******************************************************************************/
+extern const spd_pm_ops spd_pm;
+
+/*******************************************************************************
  * Function prototypes
  ******************************************************************************/
 /* Private exported functions from psci_common.c */