From 7afd46375318bc912f6bb41362685371a003bb11 Mon Sep 17 00:00:00 2001 From: Varun Wadekar Date: Mon, 18 Jan 2016 19:03:19 -0800 Subject: [PATCH] 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 --- plat/nvidia/tegra/include/t186/tegra_def.h | 18 ++++- .../t186/drivers/mce/aarch64/nvg_helpers.S | 1 - .../tegra/soc/t186/plat_psci_handlers.c | 66 ++++++++++++++++--- 3 files changed, 72 insertions(+), 13 deletions(-) diff --git a/plat/nvidia/tegra/include/t186/tegra_def.h b/plat/nvidia/tegra/include/t186/tegra_def.h index bd2c5db9b..3983a6b03 100644 --- a/plat/nvidia/tegra/include/t186/tegra_def.h +++ b/plat/nvidia/tegra/include/t186/tegra_def.h @@ -34,10 +34,22 @@ #include /******************************************************************************* - * 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_SOC_POWERDN 1 +#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 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 e1398ccee..b6e4b31ab 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 aa4291a65..413e3f820 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 #include +/* 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; - /* power domain in standby state */ - req_state->pwr_domain_state[pwr_lvl] = PLAT_MAX_RET_STATE; + 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); + + } else { + ERROR("%s: Unknown state id\n", __func__); + return PSCI_E_NOT_SUPPORTED; } return PSCI_E_SUCCESS;