Tegra: Add support for fake system suspend

This patch adds support for fake system suspend (SC7).
This is a debug mode, to ensure that a different code path is
executed for cases like pre-silicon development, where a
full-fledged SC7 is not possible in early stages.

This particular patch ensures that, if fake system suspend is
enabled (denoted by tegra_fake_system_suspend variable
having a non-zero value), instead of calling WFI, a request
for a warm reset is made for starting the SC7 exit procedure.

This ensures that the code path of kernel->ATF and back to
kernel is executed without depending on other components
involved in SC7 code path.

Additionally, this patch also adds support for SMC call
from kernel, enabling fake system suspend mode.

Signed-off-by: Vignesh Radhakrishnan <vigneshr@nvidia.com>
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
diff --git a/include/lib/aarch64/arch.h b/include/lib/aarch64/arch.h
index a2c736c..d766490 100644
--- a/include/lib/aarch64/arch.h
+++ b/include/lib/aarch64/arch.h
@@ -419,6 +419,10 @@
 
 #define EC_BITS(x)			(x >> ESR_EC_SHIFT) & ESR_EC_MASK
 
+/* Reset bit inside the Reset management register for EL3 (RMR_EL3) */
+#define RMR_RESET_REQUEST_SHIFT 	0x1u
+#define RMR_WARM_RESET_CPU		(1u << RMR_RESET_REQUEST_SHIFT)
+
 /*******************************************************************************
  * Definitions of register offsets, fields and macros for CPU system
  * instructions.
diff --git a/plat/nvidia/tegra/common/tegra_pm.c b/plat/nvidia/tegra/common/tegra_pm.c
index 5376d52..d632926 100644
--- a/plat/nvidia/tegra/common/tegra_pm.c
+++ b/plat/nvidia/tegra/common/tegra_pm.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -49,6 +49,14 @@
 extern uint64_t tegra_console_base;
 
 /*
+ * tegra_fake_system_suspend acts as a boolean var controlling whether
+ * we are going to take fake system suspend code or normal system suspend code
+ * path. This variable is set inside the sip call handlers,when the kernel
+ * requests a SIP call to set the suspend debug flags.
+ */
+uint8_t tegra_fake_system_suspend;
+
+/*
  * The following platform setup functions are weakly defined. They
  * provide typical implementations that will be overridden by a SoC.
  */
@@ -182,14 +190,31 @@
 __dead2 void tegra_pwr_domain_power_down_wfi(const psci_power_state_t
 					     *target_state)
 {
+	uint8_t pwr_state = target_state->pwr_domain_state[PLAT_MAX_PWR_LVL];
+	uint64_t rmr_el3 = 0;
+
 	/* call the chip's power down handler */
 	tegra_soc_pwr_domain_power_down_wfi(target_state);
 
-	/* enter power down state */
-	wfi();
+	/*
+	 * If we are in fake system suspend mode, ensure we start doing
+	 * procedures that help in looping back towards system suspend exit
+	 * instead of calling WFI by requesting a warm reset.
+	 * Else, just call WFI to enter low power state.
+	 */
+	if ((tegra_fake_system_suspend != 0U) &&
+	    (pwr_state == (uint8_t)PSTATE_ID_SOC_POWERDN)) {
+
+		/* warm reboot */
+		rmr_el3 = read_rmr_el3();
+		write_rmr_el3(rmr_el3 | RMR_WARM_RESET_CPU);
+
+	} else {
+		/* enter power down state */
+		wfi();
+	}
 
 	/* we can never reach here */
-	ERROR("%s: operation not handled.\n", __func__);
 	panic();
 }
 
diff --git a/plat/nvidia/tegra/common/tegra_sip_calls.c b/plat/nvidia/tegra/common/tegra_sip_calls.c
index 4dd4353..b01dcb0 100644
--- a/plat/nvidia/tegra/common/tegra_sip_calls.c
+++ b/plat/nvidia/tegra/common/tegra_sip_calls.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -37,6 +37,7 @@
 #include <memctrl.h>
 #include <runtime_svc.h>
 #include <tegra_private.h>
+#include <tegra_platform.h>
 
 /*******************************************************************************
  * Common Tegra SiP SMCs
@@ -44,8 +45,15 @@
 #define TEGRA_SIP_NEW_VIDEOMEM_REGION		0x82000003
 #define TEGRA_SIP_FIQ_NS_ENTRYPOINT		0x82000005
 #define TEGRA_SIP_FIQ_NS_GET_CONTEXT		0x82000006
+#define TEGRA_SIP_ENABLE_FAKE_SYSTEM_SUSPEND	0xC2000007
 
 /*******************************************************************************
+ * Fake system suspend mode control var
+ ******************************************************************************/
+extern uint8_t tegra_fake_system_suspend;
+
+
+/*******************************************************************************
  * SoC specific SiP handler
  ******************************************************************************/
 #pragma weak plat_sip_handler
@@ -144,6 +152,26 @@
 		SMC_RET0(handle);
 		break;
 
+	case TEGRA_SIP_ENABLE_FAKE_SYSTEM_SUSPEND:
+		/*
+		 * System suspend fake mode is set if we are on VDK and we make
+		 * a debug SIP call. This mode ensures that we excercise debug
+		 * path instead of the regular code path to suit the pre-silicon
+		 * platform needs. These include replacing the call to WFI by
+		 * a warm reset request.
+		 */
+		if (tegra_platform_is_emulation() != 0U) {
+
+			tegra_fake_system_suspend = 1;
+			SMC_RET1(handle, 0);
+		}
+
+		/*
+		 * We return to the external world as if this SIP is not
+		 * implemented in case, we are not running on VDK.
+		 */
+		break;
+
 	default:
 		ERROR("%s: unhandled SMC (0x%x)\n", __func__, smc_fid);
 		break;
diff --git a/plat/nvidia/tegra/include/tegra_private.h b/plat/nvidia/tegra/include/tegra_private.h
index 39006f6..edd1f65 100644
--- a/plat/nvidia/tegra/include/tegra_private.h
+++ b/plat/nvidia/tegra/include/tegra_private.h
@@ -103,6 +103,8 @@
 void tegra_security_setup_videomem(uintptr_t base, uint64_t size);
 
 /* Declarations for tegra_pm.c */
+extern uint8_t tegra_fake_system_suspend;
+
 void tegra_pm_system_suspend_entry(void);
 void tegra_pm_system_suspend_exit(void);
 int tegra_system_suspended(void);