diff --git a/plat/nvidia/tegra/soc/t186/plat_psci_handlers.c b/plat/nvidia/tegra/soc/t186/plat_psci_handlers.c index 1a77c5bf2d1983e5a073933456cae3d2fd722f64..20c0f4c12151ae77043444a9e3ffce0cc06b1961 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 8b340a19075a0709a282c82b187610eb4f093e37..fabab0185ddc665ba1308fa5e2f8ce11f5eb086a 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; }