aboutsummaryrefslogtreecommitdiff
path: root/plat/nvidia/tegra/soc/t186
diff options
context:
space:
mode:
Diffstat (limited to 'plat/nvidia/tegra/soc/t186')
-rw-r--r--plat/nvidia/tegra/soc/t186/plat_psci_handlers.c53
-rw-r--r--plat/nvidia/tegra/soc/t186/plat_sip_calls.c30
2 files changed, 82 insertions, 1 deletions
diff --git a/plat/nvidia/tegra/soc/t186/plat_psci_handlers.c b/plat/nvidia/tegra/soc/t186/plat_psci_handlers.c
index 1a77c5bf..20c0f4c1 100644
--- a/plat/nvidia/tegra/soc/t186/plat_psci_handlers.c
+++ b/plat/nvidia/tegra/soc/t186/plat_psci_handlers.c
@@ -41,6 +41,8 @@
#include <t18x_ari.h>
#include <tegra_private.h>
+extern void prepare_cpu_pwr_dwn(void);
+
/* state id mask */
#define TEGRA186_STATE_ID_MASK 0xF
/* constants to get power state's wake time */
@@ -49,6 +51,9 @@
static unsigned int wake_time[PLATFORM_CORE_COUNT];
+/* System power down state */
+uint32_t tegra186_system_powerdn_state = TEGRA_ARI_MISC_CCPLEX_SHUTDOWN_POWER_OFF;
+
int32_t tegra_soc_validate_power_state(unsigned int power_state,
psci_power_state_t *req_state)
{
@@ -162,7 +167,53 @@ int tegra_soc_pwr_domain_off(const psci_power_state_t *target_state)
__dead2 void tegra_soc_prepare_system_off(void)
{
- mce_enter_ccplex_state(TEGRA_ARI_MISC_CCPLEX_SHUTDOWN_POWER_OFF);
+ cpu_context_t *ctx = cm_get_context(NON_SECURE);
+ gp_regs_t *gp_regs = get_gpregs_ctx(ctx);
+ uint32_t val;
+
+ if (tegra186_system_powerdn_state == TEGRA_ARI_MISC_CCPLEX_SHUTDOWN_POWER_OFF) {
+
+ /* power off the entire system */
+ mce_enter_ccplex_state(tegra186_system_powerdn_state);
+
+ } else if (tegra186_system_powerdn_state == TEGRA_ARI_SYSTEM_SC8) {
+
+ /* loop until other CPUs power down */
+ do {
+ val = mce_command_handler(MCE_CMD_IS_SC7_ALLOWED,
+ TEGRA_ARI_CORE_C7,
+ MCE_CORE_SLEEP_TIME_INFINITE,
+ 0);
+ } while (val == 0);
+
+ /* Prepare for quasi power down */
+ write_ctx_reg(gp_regs, CTX_GPREG_X4, 1);
+ write_ctx_reg(gp_regs, CTX_GPREG_X5, 0);
+ write_ctx_reg(gp_regs, CTX_GPREG_X6, 1);
+ (void)mce_command_handler(MCE_CMD_UPDATE_CSTATE_INFO,
+ TEGRA_ARI_CLUSTER_CC7, 0, TEGRA_ARI_SYSTEM_SC8);
+
+ /* Enter quasi power down state */
+ (void)mce_command_handler(MCE_CMD_ENTER_CSTATE,
+ TEGRA_ARI_CORE_C7, MCE_CORE_SLEEP_TIME_INFINITE, 0);
+
+ /* disable GICC */
+ tegra_gic_cpuif_deactivate();
+
+ /* power down core */
+ prepare_cpu_pwr_dwn();
+
+ } else {
+ ERROR("%s: unsupported power down state (%d)\n", __func__,
+ tegra186_system_powerdn_state);
+ }
+
+ wfi();
+
+ /* wait for the system to power down */
+ for (;;) {
+ ;
+ }
}
int tegra_soc_prepare_system_reset(void)
diff --git a/plat/nvidia/tegra/soc/t186/plat_sip_calls.c b/plat/nvidia/tegra/soc/t186/plat_sip_calls.c
index 8b340a19..fabab018 100644
--- a/plat/nvidia/tegra/soc/t186/plat_sip_calls.c
+++ b/plat/nvidia/tegra/soc/t186/plat_sip_calls.c
@@ -41,10 +41,13 @@
#include <t18x_ari.h>
#include <tegra_private.h>
+extern uint32_t tegra186_system_powerdn_state;
+
/*******************************************************************************
* Tegra186 SiP SMCs
******************************************************************************/
#define TEGRA_SIP_NEW_VIDEOMEM_REGION 0x82000003
+#define TEGRA_SIP_SYSTEM_SHUTDOWN_STATE 0x82FFFE01
#define TEGRA_SIP_MCE_CMD_ENTER_CSTATE 0x82FFFF00
#define TEGRA_SIP_MCE_CMD_UPDATE_CSTATE_INFO 0x82FFFF01
#define TEGRA_SIP_MCE_CMD_UPDATE_CROSSOVER_TIME 0x82FFFF02
@@ -133,6 +136,33 @@ int plat_sip_handler(uint32_t smc_fid,
return 0;
+ case TEGRA_SIP_SYSTEM_SHUTDOWN_STATE:
+
+ /* clean up the high bits */
+ x1 = (uint32_t)x1;
+
+ /*
+ * SC8 is a special Tegra186 system state where the CPUs and
+ * DRAM are powered down but the other subsystem is still
+ * alive.
+ */
+ if ((x1 == TEGRA_ARI_SYSTEM_SC8) ||
+ (x1 == TEGRA_ARI_MISC_CCPLEX_SHUTDOWN_POWER_OFF)) {
+
+ tegra186_system_powerdn_state = x1;
+ flush_dcache_range(
+ (uintptr_t)&tegra186_system_powerdn_state,
+ sizeof(tegra186_system_powerdn_state));
+
+ } else {
+
+ ERROR("%s: unhandled powerdn state (%d)\n", __func__,
+ (uint32_t)x1);
+ return -ENOTSUP;
+ }
+
+ return 0;
+
default:
break;
}