Tegra: bpmp: suspend/resume handlers

This patch adds suspend and resume handlers for the BPMP
interface. Mark the interface as "suspended" before entering
System Suspend and verify that BPMP is alive on exit.

Change-Id: I74ccbc86125079b46d06360fc4c7e8a5acfbdfb2
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
diff --git a/plat/nvidia/tegra/common/drivers/bpmp/bpmp.c b/plat/nvidia/tegra/common/drivers/bpmp/bpmp.c
index 9a41a9b..31c7d80 100644
--- a/plat/nvidia/tegra/common/drivers/bpmp/bpmp.c
+++ b/plat/nvidia/tegra/common/drivers/bpmp/bpmp.c
@@ -16,7 +16,7 @@
 #include <string.h>
 #include <tegra_def.h>
 
-#define BPMP_TIMEOUT	2
+#define BPMP_TIMEOUT	500 /* 500ms */
 
 static uint32_t channel_base[NR_CHANNELS];
 static uint32_t bpmp_init_state = BPMP_INIT_PENDING;
@@ -115,57 +115,109 @@
 
 int tegra_bpmp_init(void)
 {
-	uint32_t val, base;
+	uint32_t val, base, timeout = BPMP_TIMEOUT;
 	unsigned int ch;
 	int ret = 0;
 
-	if (bpmp_init_state != BPMP_INIT_COMPLETE) {
+	if (bpmp_init_state == BPMP_INIT_PENDING) {
 
 		/* check if the bpmp processor is alive. */
-		val = mmio_read_32(TEGRA_RES_SEMA_BASE + STA_OFFSET);
-		if (val != SIGN_OF_LIFE) {
-			return -ENOTSUP;
-		}
+		do {
+			val = mmio_read_32(TEGRA_RES_SEMA_BASE + STA_OFFSET);
+			if (val != SIGN_OF_LIFE) {
+				mdelay(1);
+				timeout--;
+			}
 
-		/* check if clock for the atomics block is enabled */
-		val = mmio_read_32(TEGRA_CAR_RESET_BASE + TEGRA_CLK_ENB_V);
-		if ((val & CAR_ENABLE_ATOMICS) == 0) {
-			ERROR("Clock to the atomics block is disabled\n");
-		}
+		} while ((val != SIGN_OF_LIFE) && (timeout > 0U));
 
-		/* check if the atomics block is out of reset */
-		val = mmio_read_32(TEGRA_CAR_RESET_BASE + TEGRA_RST_DEV_CLR_V);
-		if ((val & CAR_ENABLE_ATOMICS) == CAR_ENABLE_ATOMICS) {
-			ERROR("Reset to the atomics block is asserted\n");
-		}
+		if (val == SIGN_OF_LIFE) {
 
-		/* base address to get the result from Atomics */
-		base = TEGRA_ATOMICS_BASE + RESULT0_REG_OFFSET;
+			/* check if clock for the atomics block is enabled */
+			val = mmio_read_32(TEGRA_CAR_RESET_BASE + TEGRA_CLK_ENB_V);
+			if ((val & CAR_ENABLE_ATOMICS) == 0) {
+				ERROR("Clock to the atomics block is disabled\n");
+			}
 
-		/* channel area is setup by BPMP before signaling handshake */
-		for (ch = 0; ch < NR_CHANNELS; ch++) {
+			/* check if the atomics block is out of reset */
+			val = mmio_read_32(TEGRA_CAR_RESET_BASE + TEGRA_RST_DEV_CLR_V);
+			if ((val & CAR_ENABLE_ATOMICS) == CAR_ENABLE_ATOMICS) {
+				ERROR("Reset to the atomics block is asserted\n");
+			}
 
-			/* issue command to get the channel base address */
-			mmio_write_32(base, (ch << TRIGGER_ID_SHIFT) |
-				      ATOMIC_CMD_GET);
+			/* base address to get the result from Atomics */
+			base = TEGRA_ATOMICS_BASE + RESULT0_REG_OFFSET;
 
-			/* get the base address for the channel */
-			channel_base[ch] = mmio_read_32(base);
+			/* channel area is setup by BPMP before signaling handshake */
+			for (ch = 0; ch < NR_CHANNELS; ch++) {
 
-			/* increment result register offset */
-			base += 4U;
-		}
+				/* issue command to get the channel base address */
+				mmio_write_32(base, (ch << TRIGGER_ID_SHIFT) |
+					      ATOMIC_CMD_GET);
 
-		/* mark state as "initialized" */
-		bpmp_init_state = BPMP_INIT_COMPLETE;
+				/* get the base address for the channel */
+				channel_base[ch] = mmio_read_32(base);
 
-		/* the channel values have to be visible across all cpus */
-		flush_dcache_range((uint64_t)channel_base, sizeof(channel_base));
-		flush_dcache_range((uint64_t)&bpmp_init_state,
-				   sizeof(bpmp_init_state));
+				/* increment result register offset */
+				base += 4U;
+			}
+
+			/* mark state as "initialized" */
+			bpmp_init_state = BPMP_INIT_COMPLETE;
+
+			/* the channel values have to be visible across all cpus */
+			flush_dcache_range((uint64_t)channel_base,
+					   sizeof(channel_base));
+			flush_dcache_range((uint64_t)&bpmp_init_state,
+					   sizeof(bpmp_init_state));
+
+			INFO("%s: done\n", __func__);
 
-		INFO("%s: done\n", __func__);
+		} else {
+			ERROR("BPMP not powered on\n");
+			ret = -ETIMEDOUT;
+		}
 	}
 
 	return ret;
 }
+
+void tegra_bpmp_suspend(void)
+{
+	/* freeze the interface */
+	bpmp_init_state = BPMP_SUSPEND_ENTRY;
+	flush_dcache_range((uint64_t)&bpmp_init_state, sizeof(bpmp_init_state));
+}
+
+void tegra_bpmp_resume(void)
+{
+	uint32_t val, timeout = 0;
+
+	if (bpmp_init_state == BPMP_SUSPEND_ENTRY) {
+
+		/* check if the bpmp processor is alive. */
+		do {
+
+			val = mmio_read_32(TEGRA_RES_SEMA_BASE + STA_OFFSET);
+			if (val != SIGN_OF_LIFE) {
+				mdelay(1);
+				timeout++;
+			}
+
+		} while ((val != SIGN_OF_LIFE) && (timeout < BPMP_TIMEOUT));
+
+		if (val == SIGN_OF_LIFE) {
+
+			INFO("%s: BPMP took %d ms to resume\n", __func__, timeout);
+
+			/* mark state as "initialized" */
+			bpmp_init_state = BPMP_INIT_COMPLETE;
+
+			/* state has to be visible across all cpus */
+			flush_dcache_range((uint64_t)&bpmp_init_state,
+					   sizeof(bpmp_init_state));
+		} else {
+			ERROR("BPMP not powered on\n");
+		}
+	}
+}
diff --git a/plat/nvidia/tegra/include/drivers/bpmp.h b/plat/nvidia/tegra/include/drivers/bpmp.h
index 03da6f6..0046f6c 100644
--- a/plat/nvidia/tegra/include/drivers/bpmp.h
+++ b/plat/nvidia/tegra/include/drivers/bpmp.h
@@ -29,6 +29,7 @@
 /* flags to indicate bpmp driver's state */
 #define BPMP_INIT_COMPLETE	0xBEEFF00DU
 #define BPMP_INIT_PENDING	0xDEADBEEFU
+#define BPMP_SUSPEND_ENTRY	0xF00DCAFEU
 
 /* requests serviced by the bpmp */
 #define MRQ_PING		0
@@ -107,6 +108,16 @@
 int tegra_bpmp_init(void);
 
 /**
+ * Function to suspend the interface with the bpmp
+ */
+void tegra_bpmp_suspend(void);
+
+/**
+ * Function to resume the interface with the bpmp
+ */
+void tegra_bpmp_resume(void);
+
+/**
  * Handler to send a MRQ_* command to the bpmp
  */
 int32_t tegra_bpmp_send_receive_atomic(int mrq, const void *ob_data, int ob_sz,