Tegra186: support for C6/C7 CPU_SUSPEND states

This patch adds support for the C6 and C7 CPU_SUSPEND states. C6 is
an idle state while C7 is a powerdown state.

The MCE block takes care of the entry/exit to/from these core power
states and hence we call the corresponding MCE handler to process
these requests. The NS driver passes the tentative time that the
core is expected to stay in this state as part of the power_state
parameter, which we store in a per-cpu array and pass it to the
MCE block.

Change-Id: I152acb11ab93d91fb866da2129b1795843dfa39b
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
diff --git a/plat/nvidia/tegra/include/t186/tegra_def.h b/plat/nvidia/tegra/include/t186/tegra_def.h
index bd2c5db..3983a6b 100644
--- a/plat/nvidia/tegra/include/t186/tegra_def.h
+++ b/plat/nvidia/tegra/include/t186/tegra_def.h
@@ -34,10 +34,22 @@
 #include <platform_def.h>
 
 /*******************************************************************************
- * This value is used by the PSCI implementation during the `SYSTEM_SUSPEND`
- * call as the `state-id` field in the 'power state' parameter.
+ * These values are used by the PSCI implementation during the `CPU_SUSPEND`
+ * and `SYSTEM_SUSPEND` calls as the `state-id` field in the 'power state'
+ * parameter.
+ ******************************************************************************/
+#define PSTATE_ID_CORE_IDLE		6
+#define PSTATE_ID_CORE_POWERDN		7
+#define PSTATE_ID_SOC_POWERDN		2
+
+/*******************************************************************************
+ * Platform power states (used by PSCI framework)
+ *
+ * - PLAT_MAX_RET_STATE should be less than lowest PSTATE_ID
+ * - PLAT_MAX_OFF_STATE should be greater than the highest PSTATE_ID
  ******************************************************************************/
-#define PSTATE_ID_SOC_POWERDN		1
+#define PLAT_MAX_RET_STATE		1
+#define PLAT_MAX_OFF_STATE		8
 
 /*******************************************************************************
  * Implementation defined ACTLR_EL3 bit definitions
diff --git a/plat/nvidia/tegra/soc/t186/drivers/mce/aarch64/nvg_helpers.S b/plat/nvidia/tegra/soc/t186/drivers/mce/aarch64/nvg_helpers.S
index e1398cc..b6e4b31 100644
--- a/plat/nvidia/tegra/soc/t186/drivers/mce/aarch64/nvg_helpers.S
+++ b/plat/nvidia/tegra/soc/t186/drivers/mce/aarch64/nvg_helpers.S
@@ -39,7 +39,6 @@
 func nvg_set_request_data
 	msr	s3_0_c15_c1_2, x0
 	msr	s3_0_c15_c1_3, x1
-	isb
 	ret
 endfunc nvg_set_request_data
 
diff --git a/plat/nvidia/tegra/soc/t186/plat_psci_handlers.c b/plat/nvidia/tegra/soc/t186/plat_psci_handlers.c
index aa4291a..413e3f8 100644
--- a/plat/nvidia/tegra/soc/t186/plat_psci_handlers.c
+++ b/plat/nvidia/tegra/soc/t186/plat_psci_handlers.c
@@ -40,22 +40,70 @@
 #include <t18x_ari.h>
 #include <tegra_private.h>
 
+/* state id mask */
+#define TEGRA186_STATE_ID_MASK		0xF
+/* constants to get power state's wake time */
+#define TEGRA186_WAKE_TIME_MASK		0xFFFFFF
+#define TEGRA186_WAKE_TIME_SHIFT	4
+
+/* per cpu wake time value */
+static unsigned int wake_time[PLATFORM_CORE_COUNT];
+
 int32_t tegra_soc_validate_power_state(unsigned int power_state,
 					psci_power_state_t *req_state)
 {
-	int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
+	int state_id = psci_get_pstate_id(power_state) & TEGRA186_STATE_ID_MASK;
+	int cpu = read_mpidr() & MPIDR_CPU_MASK;
 
-	/* Sanity check the requested afflvl */
-	if (psci_get_pstate_type(power_state) == PSTATE_TYPE_STANDBY) {
+	/* get the wake time value */
+	wake_time[cpu] = (power_state & TEGRA186_WAKE_TIME_MASK) >>
+			  TEGRA186_WAKE_TIME_SHIFT;
+
+	/* Sanity check the requested state id */
+	switch (state_id) {
+	case PSTATE_ID_CORE_IDLE:
+	case PSTATE_ID_CORE_POWERDN:
 		/*
-		 * It's possible to enter standby only on affinity level 0 i.e.
-		 * a cpu on Tegra. Ignore any other affinity level.
+		 * Core powerdown request only for afflvl 0
 		 */
-		if (pwr_lvl != MPIDR_AFFLVL0)
-			return PSCI_E_INVALID_PARAMS;
+		req_state->pwr_domain_state[MPIDR_AFFLVL0] = state_id;
+
+		break;
+
+	default:
+		ERROR("%s: unsupported state id (%d)\n", __func__, state_id);
+		return PSCI_E_INVALID_PARAMS;
+	}
+
+	return PSCI_E_SUCCESS;
+}
+
+int tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state)
+{
+	const plat_local_state_t *pwr_domain_state;
+	unsigned int stateid_afflvl0;
+	int cpu = read_mpidr() & MPIDR_CPU_MASK;
+
+	/* get the state ID */
+	pwr_domain_state = target_state->pwr_domain_state;
+	stateid_afflvl0 = pwr_domain_state[MPIDR_AFFLVL0] &
+		TEGRA186_STATE_ID_MASK;
+
+	if (stateid_afflvl0 == PSTATE_ID_CORE_IDLE) {
+
+		/* Prepare for cpu idle */
+		(void)mce_command_handler(MCE_CMD_ENTER_CSTATE,
+			TEGRA_ARI_CORE_C6, wake_time[cpu], 0);
+
+	} else if (stateid_afflvl0 == PSTATE_ID_CORE_POWERDN) {
+
+		/* Prepare for cpu powerdn */
+		(void)mce_command_handler(MCE_CMD_ENTER_CSTATE,
+			TEGRA_ARI_CORE_C7, wake_time[cpu], 0);
 
-		/* power domain in standby state */
-		req_state->pwr_domain_state[pwr_lvl] = PLAT_MAX_RET_STATE;
+	} else {
+		ERROR("%s: Unknown state id\n", __func__);
+		return PSCI_E_NOT_SUPPORTED;
 	}
 
 	return PSCI_E_SUCCESS;