xilinx: versal: Implement set wakeup source for client

Add support to set wakeup source for APU while suspending.

Signed-off-by: Tejas Patel <tejas.patel@xilinx.com>
Signed-off-by: Rajan Vaja <rajan.vaja@xilinx.com>
Signed-off-by: Jolly Shah <jolly.shah@xilinx.com>
Change-Id: I4809fd83a710def8144fdad74990c45e62b8fdf3
diff --git a/plat/xilinx/versal/pm_service/pm_client.c b/plat/xilinx/versal/pm_service/pm_client.c
index b234e1b..5b47838 100644
--- a/plat/xilinx/versal/pm_service/pm_client.c
+++ b/plat/xilinx/versal/pm_service/pm_client.c
@@ -9,16 +9,22 @@
  * for getting information about and changing state of the APU.
  */
 
+#include <assert.h>
 #include <plat_ipi.h>
 #include <platform_def.h>
 #include <versal_def.h>
 #include <lib/bakery_lock.h>
 #include <lib/mmio.h>
+#include <lib/utils.h>
 #include <drivers/arm/gicv3.h>
+#include <drivers/arm/gic_common.h>
 #include <plat/common/platform.h>
+#include "pm_api_sys.h"
 #include "pm_client.h"
 
 #define UNDEFINED_CPUID		(~0)
+#define IRQ_MAX		142
+#define NUM_GICD_ISENABLER	((IRQ_MAX >> 5) + 1)
 
 DEFINE_BAKERY_LOCK(pm_client_secure_lock);
 
@@ -44,6 +50,111 @@
 
 const struct pm_proc *primary_proc = &pm_procs_all[0];
 
+/* Interrupt to PM node index map */
+static enum pm_device_node_idx irq_node_map[IRQ_MAX + 1] = {
+	[13] = XPM_NODEIDX_DEV_GPIO,
+	[14] = XPM_NODEIDX_DEV_I2C_0,
+	[15] = XPM_NODEIDX_DEV_I2C_1,
+	[16] = XPM_NODEIDX_DEV_SPI_0,
+	[17] = XPM_NODEIDX_DEV_SPI_1,
+	[18] = XPM_NODEIDX_DEV_UART_0,
+	[19] = XPM_NODEIDX_DEV_UART_1,
+	[20] = XPM_NODEIDX_DEV_CAN_FD_0,
+	[21] = XPM_NODEIDX_DEV_CAN_FD_1,
+	[22] = XPM_NODEIDX_DEV_USB_0,
+	[23] = XPM_NODEIDX_DEV_USB_0,
+	[24] = XPM_NODEIDX_DEV_USB_0,
+	[25] = XPM_NODEIDX_DEV_USB_0,
+	[26] = XPM_NODEIDX_DEV_USB_0,
+	[37] = XPM_NODEIDX_DEV_TTC_0,
+	[38] = XPM_NODEIDX_DEV_TTC_0,
+	[39] = XPM_NODEIDX_DEV_TTC_0,
+	[40] = XPM_NODEIDX_DEV_TTC_1,
+	[41] = XPM_NODEIDX_DEV_TTC_1,
+	[42] = XPM_NODEIDX_DEV_TTC_1,
+	[43] = XPM_NODEIDX_DEV_TTC_2,
+	[44] = XPM_NODEIDX_DEV_TTC_2,
+	[45] = XPM_NODEIDX_DEV_TTC_2,
+	[46] = XPM_NODEIDX_DEV_TTC_3,
+	[47] = XPM_NODEIDX_DEV_TTC_3,
+	[48] = XPM_NODEIDX_DEV_TTC_3,
+	[56] = XPM_NODEIDX_DEV_GEM_0,
+	[57] = XPM_NODEIDX_DEV_GEM_0,
+	[58] = XPM_NODEIDX_DEV_GEM_1,
+	[59] = XPM_NODEIDX_DEV_GEM_1,
+	[60] = XPM_NODEIDX_DEV_ADMA_0,
+	[61] = XPM_NODEIDX_DEV_ADMA_1,
+	[62] = XPM_NODEIDX_DEV_ADMA_2,
+	[63] = XPM_NODEIDX_DEV_ADMA_3,
+	[64] = XPM_NODEIDX_DEV_ADMA_4,
+	[65] = XPM_NODEIDX_DEV_ADMA_5,
+	[66] = XPM_NODEIDX_DEV_ADMA_6,
+	[67] = XPM_NODEIDX_DEV_ADMA_7,
+	[74] = XPM_NODEIDX_DEV_USB_0,
+	[126] = XPM_NODEIDX_DEV_SDIO_0,
+	[127] = XPM_NODEIDX_DEV_SDIO_0,
+	[128] = XPM_NODEIDX_DEV_SDIO_1,
+	[129] = XPM_NODEIDX_DEV_SDIO_1,
+	[142] = XPM_NODEIDX_DEV_RTC,
+};
+
+/**
+ * irq_to_pm_node_idx - Get PM node index corresponding to the interrupt number
+ * @irq:	Interrupt number
+ *
+ * Return:	PM node index corresponding to the specified interrupt
+ */
+static enum pm_device_node_idx irq_to_pm_node_idx(unsigned int irq)
+{
+	assert(irq <= IRQ_MAX);
+	return irq_node_map[irq];
+}
+
+/**
+ * pm_client_set_wakeup_sources - Set all devices with enabled interrupts as
+ *				  wake sources in the LibPM.
+ */
+static void pm_client_set_wakeup_sources(void)
+{
+	uint32_t reg_num;
+	uint32_t device_id;
+	uint8_t pm_wakeup_nodes_set[XPM_NODEIDX_DEV_MAX];
+	uintptr_t isenabler1 = PLAT_VERSAL_GICD_BASE + GICD_ISENABLER + 4;
+
+	zeromem(&pm_wakeup_nodes_set, sizeof(pm_wakeup_nodes_set));
+
+	for (reg_num = 0; reg_num < NUM_GICD_ISENABLER; reg_num++) {
+		uint32_t base_irq = reg_num << ISENABLER_SHIFT;
+		uint32_t reg = mmio_read_32(isenabler1 + (reg_num << 2));
+
+		if (!reg)
+			continue;
+
+		while (reg) {
+			enum pm_device_node_idx node_idx;
+			uint32_t idx, ret, irq, lowest_set = reg & (-reg);
+
+			idx = __builtin_ctz(lowest_set);
+			irq = base_irq + idx;
+
+			if (irq > IRQ_MAX)
+				break;
+
+			node_idx = irq_to_pm_node_idx(irq);
+			reg &= ~lowest_set;
+
+			if ((node_idx != XPM_NODEIDX_DEV_MIN) &&
+			    (!pm_wakeup_nodes_set[node_idx])) {
+				/* Get device ID from node index */
+				device_id = PERIPH_DEVID(node_idx);
+				ret = pm_set_wakeup_source(XPM_DEVID_ACPU_0,
+							   device_id, 1);
+				pm_wakeup_nodes_set[node_idx] = !ret;
+			}
+		}
+	}
+}
+
 /**
  * pm_client_suspend() - Client-specific suspend actions
  *
@@ -55,6 +166,9 @@
 {
 	bakery_lock_get(&pm_client_secure_lock);
 
+	if (state == PM_STATE_SUSPEND_TO_RAM)
+		pm_client_set_wakeup_sources();
+
 	/* Set powerdown request */
 	mmio_write_32(FPD_APU_PWRCTL, mmio_read_32(FPD_APU_PWRCTL) |
 		      proc->pwrdn_mask);