Tegra210: power off all DMA masters before System Suspend entry

This patch puts all the DMA masters in reset before starting the System
Suspend sequence. This helps us make sure that there are no rogue agents
in the system trying to over-write the SC7 Entry Firmware with their own.

Change-Id: I7eb39999d229951e612fbfeb9f86c4efb8f98b5a
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
diff --git a/plat/nvidia/tegra/include/t210/tegra_def.h b/plat/nvidia/tegra/include/t210/tegra_def.h
index 02c5f13..eed443d 100644
--- a/plat/nvidia/tegra/include/t210/tegra_def.h
+++ b/plat/nvidia/tegra/include/t210/tegra_def.h
@@ -41,6 +41,7 @@
  * iRAM memory constants
  ******************************************************************************/
 #define TEGRA_IRAM_BASE			U(0x40000000)
+#define TEGRA_IRAM_A_SIZE		U(0x10000) /* 64KB */
 #define TEGRA_IRAM_SIZE			U(40000) /* 256KB */
 
 /*******************************************************************************
@@ -95,23 +96,55 @@
  * Tegra Clock and Reset Controller constants
  ******************************************************************************/
 #define TEGRA_CAR_RESET_BASE		U(0x60006000)
+#define TEGRA_BOND_OUT_H		U(0x74)
+#define  APB_DMA_LOCK_BIT		(U(1) << 2)
+#define  AHB_DMA_LOCK_BIT		(U(1) << 1)
+#define TEGRA_BOND_OUT_U		U(0x78)
+#define  IRAM_D_LOCK_BIT		(U(1) << 23)
+#define  IRAM_C_LOCK_BIT		(U(1) << 22)
+#define  IRAM_B_LOCK_BIT		(U(1) << 21)
 #define TEGRA_GPU_RESET_REG_OFFSET	U(0x28C)
 #define TEGRA_GPU_RESET_GPU_SET_OFFSET	U(0x290)
 #define  GPU_RESET_BIT			(U(1) << 24)
 #define  GPU_SET_BIT			(U(1) << 24)
+#define TEGRA_RST_DEV_SET_Y		U(0x2a8)
+#define  NVENC_RESET_BIT		(U(1) << 27)
+#define  TSECB_RESET_BIT		(U(1) << 14)
+#define  APE_RESET_BIT			(U(1) << 6)
+#define  NVJPG_RESET_BIT		(U(1) << 3)
+#define  NVDEC_RESET_BIT		(U(1) << 2)
+#define TEGRA_RST_DEV_SET_L		U(0x300)
+#define  HOST1X_RESET_BIT		(U(1) << 28)
+#define  ISP_RESET_BIT			(U(1) << 23)
+#define  USBD_RESET_BIT			(U(1) << 22)
+#define  VI_RESET_BIT			(U(1) << 20)
+#define  SDMMC4_RESET_BIT		(U(1) << 15)
+#define  SDMMC1_RESET_BIT		(U(1) << 14)
+#define  SDMMC2_RESET_BIT		(U(1) << 9)
+#define TEGRA_RST_DEV_SET_H		U(0x308)
+#define  USB2_RESET_BIT			(U(1) << 26)
+#define  APBDMA_RESET_BIT		(U(1) << 2)
+#define  AHBDMA_RESET_BIT		(U(1) << 1)
+#define TEGRA_RST_DEV_SET_U		U(0x310)
+#define  XUSB_DEV_RESET_BIT		(U(1) << 31)
+#define  XUSB_HOST_RESET_BIT		(U(1) << 25)
+#define  TSEC_RESET_BIT			(U(1) << 19)
+#define  PCIE_RESET_BIT			(U(1) << 6)
+#define  SDMMC3_RESET_BIT		(U(1) << 5)
+#define TEGRA_RST_DEVICES_V		U(0x358)
+#define TEGRA_RST_DEVICES_W		U(0x35C)
+#define  ENTROPY_CLK_ENB_BIT		(U(1) << 21)
+#define TEGRA_CLK_OUT_ENB_V		U(0x360)
+#define  SE_CLK_ENB_BIT			(U(1) << 31)
+#define TEGRA_CLK_OUT_ENB_W		U(0x364)
+#define  ENTROPY_RESET_BIT 		(U(1) << 21)
+#define TEGRA_RST_DEV_SET_V		U(0x430)
+#define  SE_RESET_BIT			(U(1) << 31)
+#define  HDA_RESET_BIT			(U(1) << 29)
+#define  SATA_RESET_BIT			(U(1) << 28)
 #define TEGRA_RST_DEV_CLR_V		U(0x434)
 #define TEGRA_CLK_ENB_V			U(0x440)
 
-/* SE Clock Offsets */
-#define TEGRA_RST_DEVICES_V		0x358UL
-#define  SE_RESET_BIT 			(0x1UL << 31)
-#define TEGRA_RST_DEVICES_W		 0x35CUL
-#define  ENTROPY_CLK_ENB_BIT		(0x1UL << 21)
-#define TEGRA_CLK_OUT_ENB_V		0x360UL
-#define  SE_CLK_ENB_BIT			(0x1UL << 31)
-#define TEGRA_CLK_OUT_ENB_W		0x364UL
-#define  ENTROPY_RESET_BIT 		(0x1UL << 21)
-
 /*******************************************************************************
  * Tegra Flow Controller constants
  ******************************************************************************/
diff --git a/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c b/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c
index 4c49386..4e9206f 100644
--- a/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c
+++ b/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c
@@ -21,6 +21,7 @@
 #include <tegra_def.h>
 #include <tegra_private.h>
 #include <tegra_platform.h>
+#include <utils.h>
 
 /*
  * Register used to clear CPU reset signals. Each CPU has two reset
@@ -255,6 +256,74 @@
 	return ret;
 }
 
+static void tegra_reset_all_dma_masters(void)
+{
+	uint32_t val, mask;
+
+	/*
+	 * Reset all possible DMA masters in the system.
+	 */
+	val = GPU_RESET_BIT;
+	mmio_write_32(TEGRA_CAR_RESET_BASE + TEGRA_GPU_RESET_REG_OFFSET, val);
+
+	val = NVENC_RESET_BIT | TSECB_RESET_BIT | APE_RESET_BIT |
+	      NVJPG_RESET_BIT | NVDEC_RESET_BIT;
+	mmio_write_32(TEGRA_CAR_RESET_BASE + TEGRA_RST_DEV_SET_Y, val);
+
+	val = HOST1X_RESET_BIT | ISP_RESET_BIT | USBD_RESET_BIT |
+	      VI_RESET_BIT | SDMMC4_RESET_BIT | SDMMC1_RESET_BIT |
+	      SDMMC2_RESET_BIT;
+	mmio_write_32(TEGRA_CAR_RESET_BASE + TEGRA_RST_DEV_SET_L, val);
+
+	val = USB2_RESET_BIT | APBDMA_RESET_BIT | AHBDMA_RESET_BIT;
+	mmio_write_32(TEGRA_CAR_RESET_BASE + TEGRA_RST_DEV_SET_H, val);
+
+	val = XUSB_DEV_RESET_BIT | XUSB_HOST_RESET_BIT | TSEC_RESET_BIT |
+	      PCIE_RESET_BIT | SDMMC3_RESET_BIT;
+	mmio_write_32(TEGRA_CAR_RESET_BASE + TEGRA_RST_DEV_SET_U, val);
+
+	val = SE_RESET_BIT | HDA_RESET_BIT | SATA_RESET_BIT;
+	mmio_write_32(TEGRA_CAR_RESET_BASE + TEGRA_RST_DEV_SET_V, val);
+
+	/*
+	 * If any of the DMA masters are still alive, assume
+	 * that the system has been compromised and reboot.
+	 */
+	val = mmio_read_32(TEGRA_CAR_RESET_BASE + TEGRA_GPU_RESET_REG_OFFSET);
+	mask = GPU_RESET_BIT;
+	if ((val & mask) != mask)
+		tegra_pmc_system_reset();
+
+	mask = NVENC_RESET_BIT | TSECB_RESET_BIT | APE_RESET_BIT |
+	      NVJPG_RESET_BIT | NVDEC_RESET_BIT;
+	val = mmio_read_32(TEGRA_CAR_RESET_BASE + TEGRA_RST_DEV_SET_Y);
+	if ((val & mask) != mask)
+		tegra_pmc_system_reset();
+
+	mask = HOST1X_RESET_BIT | ISP_RESET_BIT | USBD_RESET_BIT |
+	       VI_RESET_BIT | SDMMC4_RESET_BIT | SDMMC1_RESET_BIT |
+	       SDMMC2_RESET_BIT;
+	val = mmio_read_32(TEGRA_CAR_RESET_BASE + TEGRA_RST_DEV_SET_L);
+	if ((val & mask) != mask)
+		tegra_pmc_system_reset();
+
+	mask = USB2_RESET_BIT | APBDMA_RESET_BIT | AHBDMA_RESET_BIT;
+	val = mmio_read_32(TEGRA_CAR_RESET_BASE + TEGRA_RST_DEV_SET_H);
+	if ((val & mask) != mask)
+		tegra_pmc_system_reset();
+
+	mask = XUSB_DEV_RESET_BIT | XUSB_HOST_RESET_BIT | TSEC_RESET_BIT |
+	       PCIE_RESET_BIT | SDMMC3_RESET_BIT;
+	val = mmio_read_32(TEGRA_CAR_RESET_BASE + TEGRA_RST_DEV_SET_U);
+	if ((val & mask) != mask)
+		tegra_pmc_system_reset();
+
+	val = mmio_read_32(TEGRA_CAR_RESET_BASE + TEGRA_RST_DEV_SET_V);
+	mask = SE_RESET_BIT | HDA_RESET_BIT | SATA_RESET_BIT;
+	if ((val & mask) != mask)
+		tegra_pmc_system_reset();
+}
+
 int tegra_soc_pwr_domain_power_down_wfi(const psci_power_state_t *target_state)
 {
 	u_register_t mpidr = read_mpidr();
@@ -287,6 +356,28 @@
 			/* Power off BPMP before we proceed */
 			tegra_fc_bpmp_off();
 
+			/* bond out IRAM banks B, C and D */
+			mmio_write_32(TEGRA_CAR_RESET_BASE + TEGRA_BOND_OUT_U,
+				IRAM_B_LOCK_BIT | IRAM_C_LOCK_BIT |
+				IRAM_D_LOCK_BIT);
+
+			/* bond out APB/AHB DMAs */
+			mmio_write_32(TEGRA_CAR_RESET_BASE + TEGRA_BOND_OUT_H,
+				APB_DMA_LOCK_BIT | AHB_DMA_LOCK_BIT);
+
+			/* Power off BPMP before we proceed */
+			tegra_fc_bpmp_off();
+
+			/*
+			 * Reset all the hardware blocks that can act as DMA
+			 * masters on the bus.
+			 */
+			tegra_reset_all_dma_masters();
+
+			/* clean up IRAM of any cruft */
+			zeromem((void *)(uintptr_t)TEGRA_IRAM_BASE,
+					TEGRA_IRAM_A_SIZE);
+
 			/* Copy the firmware to BPMP's internal RAM */
 			(void)memcpy((void *)(uintptr_t)TEGRA_IRAM_BASE,
 				(const void *)plat_params->sc7entry_fw_base,
diff --git a/plat/nvidia/tegra/soc/t210/plat_setup.c b/plat/nvidia/tegra/soc/t210/plat_setup.c
index 4fdd5a8..0aeb9ff 100644
--- a/plat/nvidia/tegra/soc/t210/plat_setup.c
+++ b/plat/nvidia/tegra/soc/t210/plat_setup.c
@@ -160,7 +160,7 @@
 	/* memmap TZDRAM area containing the SC7 Entry Firmware */
 	if (plat_params->sc7entry_fw_base && plat_params->sc7entry_fw_size) {
 
-		assert(plat_params->sc7entry_fw_size <= TEGRA_IRAM_SIZE);
+		assert(plat_params->sc7entry_fw_size <= TEGRA_IRAM_A_SIZE);
 
 		/*
 		 * Verify that the SC7 entry firmware resides inside the TZDRAM