zynqmp: pm: Add support for setting suspend-to-RAM mode

Beside standard suspend-to-RAM state, Zynq MPSoC supports
suspend-to-RAM state with additional power savings, called
power-off suspend-to-RAM. If this mode is set, only NODE_EXTERN
must be set as wake source. Standard suspend-to-RAM procedure
is unchanged.

This patch adds support for setting suspend mode from higher
ELs and ensuring that all conditions for power-off suspend mode
are set.

Signed-off-by: Siva Durga Prasad Paladugu <siva.durga.paladugu@xilinx.com>
Signed-off-by: Filip Drazic <filip.drazic@aggios.com>
diff --git a/plat/xilinx/zynqmp/pm_service/pm_client.c b/plat/xilinx/zynqmp/pm_service/pm_client.c
index 9016fd6..874b8a9 100644
--- a/plat/xilinx/zynqmp/pm_service/pm_client.c
+++ b/plat/xilinx/zynqmp/pm_service/pm_client.c
@@ -26,10 +26,15 @@
 #define NUM_GICD_ISENABLER	((IRQ_MAX >> 5) + 1)
 #define UNDEFINED_CPUID		(~0)
 
+#define PM_SUSPEND_MODE_STD		0
+#define PM_SUSPEND_MODE_POWER_OFF	1
+
 DEFINE_BAKERY_LOCK(pm_client_secure_lock);
 
 extern const struct pm_ipi apu_ipi;
 
+static uint32_t suspend_mode = PM_SUSPEND_MODE_STD;
+
 /* Order in pm_procs_all array must match cpu ids */
 static const struct pm_proc pm_procs_all[] = {
 	{
@@ -165,6 +170,19 @@
 	uint8_t pm_wakeup_nodes_set[NODE_MAX];
 	uintptr_t isenabler1 = BASE_GICD_BASE + GICD_ISENABLER + 4;
 
+	/* In case of power-off suspend, only NODE_EXTERN must be set */
+	if (suspend_mode == PM_SUSPEND_MODE_POWER_OFF) {
+		enum pm_ret_status ret;
+
+		ret = pm_set_wakeup_source(NODE_APU, NODE_EXTERN, 1);
+		/**
+		 * If NODE_EXTERN could not be set as wake source, proceed with
+		 * standard suspend (no one will wake the system otherwise)
+		 */
+		if (ret == PM_RET_SUCCESS)
+			return;
+	}
+
 	zeromem(&pm_wakeup_nodes_set, sizeof(pm_wakeup_nodes_set));
 
 	for (reg_num = 0; reg_num < NUM_GICD_ISENABLER; reg_num++) {
@@ -305,3 +323,13 @@
 
 	bakery_lock_release(&pm_client_secure_lock);
 }
+
+enum pm_ret_status pm_set_suspend_mode(uint32_t mode)
+{
+	if ((mode != PM_SUSPEND_MODE_STD) &&
+	    (mode != PM_SUSPEND_MODE_POWER_OFF))
+		return PM_RET_ERROR_ARGS;
+
+	suspend_mode = mode;
+	return PM_RET_SUCCESS;
+}