Tegra210: support for cluster idle from the CPU
This patch adds support to enter/exit to/from cluster idle power state on Tegra210 platforms that do not load BPMP firmware. The CPU initates the cluster idle sequence on the last standing CPU, by following these steps: Entry ----- * stop other CPUs from waking up * program the PWM pinmux to tristate for OVR PMIC * program the flow controller to enter CC6 state * skip L1 $ flush during cluster power down, as L2 $ is inclusive of L1 $ on Cortex-A57 CPUs Exit ---- * program the PWM pinmux to un-tristate for OVR PMIC * allow other CPUs to wake up This patch also makes sure that cluster idle state entry is not enabled until CL-DVFS is ready. Change-Id: I54cf31bf72b4a09d9bf9d2baaed6ee5a963c7808 Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
This commit is contained in:
parent
a7a63e0ee5
commit
7db077f2e3
|
@ -137,6 +137,8 @@
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
#define TEGRA_MISC_BASE U(0x70000000)
|
#define TEGRA_MISC_BASE U(0x70000000)
|
||||||
#define HARDWARE_REVISION_OFFSET U(0x804)
|
#define HARDWARE_REVISION_OFFSET U(0x804)
|
||||||
|
#define PINMUX_AUX_DVFS_PWM U(0x3184)
|
||||||
|
#define PINMUX_PWM_TRISTATE (U(1) << 4)
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Tegra UART controller base addresses
|
* Tegra UART controller base addresses
|
||||||
|
@ -193,6 +195,17 @@
|
||||||
#define MC_SMMU_PPCS_ASID_0 0x270U
|
#define MC_SMMU_PPCS_ASID_0 0x270U
|
||||||
#define PPCS_SMMU_ENABLE (0x1U << 31)
|
#define PPCS_SMMU_ENABLE (0x1U << 31)
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Tegra CLDVFS constants
|
||||||
|
******************************************************************************/
|
||||||
|
#define TEGRA_CL_DVFS_BASE U(0x70110000)
|
||||||
|
#define DVFS_DFLL_CTRL U(0x00)
|
||||||
|
#define ENABLE_OPEN_LOOP U(1)
|
||||||
|
#define ENABLE_CLOSED_LOOP U(2)
|
||||||
|
#define DVFS_DFLL_OUTPUT_CFG U(0x20)
|
||||||
|
#define DFLL_OUTPUT_CFG_I2C_EN_BIT (U(1) << 30)
|
||||||
|
#define DFLL_OUTPUT_CFG_CLK_EN_BIT (U(1) << 6)
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Tegra SE constants
|
* Tegra SE constants
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
|
* Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-3-Clause
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -35,6 +35,7 @@
|
||||||
#define SCLK_BURST_POLICY_DEFAULT 0x10000000
|
#define SCLK_BURST_POLICY_DEFAULT 0x10000000
|
||||||
|
|
||||||
static int cpu_powergate_mask[PLATFORM_MAX_CPUS_PER_CLUSTER];
|
static int cpu_powergate_mask[PLATFORM_MAX_CPUS_PER_CLUSTER];
|
||||||
|
static bool tegra_bpmp_available = true;
|
||||||
|
|
||||||
int32_t tegra_soc_validate_power_state(unsigned int power_state,
|
int32_t tegra_soc_validate_power_state(unsigned int power_state,
|
||||||
psci_power_state_t *req_state)
|
psci_power_state_t *req_state)
|
||||||
|
@ -53,11 +54,12 @@ int32_t tegra_soc_validate_power_state(unsigned int power_state,
|
||||||
|
|
||||||
case PSTATE_ID_CLUSTER_IDLE:
|
case PSTATE_ID_CLUSTER_IDLE:
|
||||||
case PSTATE_ID_CLUSTER_POWERDN:
|
case PSTATE_ID_CLUSTER_POWERDN:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cluster powerdown/idle request only for afflvl 1
|
* Cluster idle request for afflvl 0
|
||||||
*/
|
*/
|
||||||
req_state->pwr_domain_state[MPIDR_AFFLVL1] = state_id;
|
|
||||||
req_state->pwr_domain_state[MPIDR_AFFLVL0] = PSTATE_ID_CORE_POWERDN;
|
req_state->pwr_domain_state[MPIDR_AFFLVL0] = PSTATE_ID_CORE_POWERDN;
|
||||||
|
req_state->pwr_domain_state[MPIDR_AFFLVL1] = state_id;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -83,7 +85,7 @@ int32_t tegra_soc_validate_power_state(unsigned int power_state,
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Platform handler to calculate the proper target power level at the
|
* Platform handler to calculate the proper target power level at the
|
||||||
* specified affinity level
|
* specified affinity level.
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
plat_local_state_t tegra_soc_get_target_pwr_state(unsigned int lvl,
|
plat_local_state_t tegra_soc_get_target_pwr_state(unsigned int lvl,
|
||||||
const plat_local_state_t *states,
|
const plat_local_state_t *states,
|
||||||
|
@ -92,7 +94,7 @@ plat_local_state_t tegra_soc_get_target_pwr_state(unsigned int lvl,
|
||||||
plat_local_state_t target = PSCI_LOCAL_STATE_RUN;
|
plat_local_state_t target = PSCI_LOCAL_STATE_RUN;
|
||||||
int cpu = plat_my_core_pos();
|
int cpu = plat_my_core_pos();
|
||||||
int core_pos = read_mpidr() & MPIDR_CPU_MASK;
|
int core_pos = read_mpidr() & MPIDR_CPU_MASK;
|
||||||
uint32_t bpmp_reply, data[3];
|
uint32_t bpmp_reply, data[3], val;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* get the power state at this level */
|
/* get the power state at this level */
|
||||||
|
@ -109,9 +111,40 @@ plat_local_state_t tegra_soc_get_target_pwr_state(unsigned int lvl,
|
||||||
|
|
||||||
/* Cluster idle not allowed */
|
/* Cluster idle not allowed */
|
||||||
target = PSCI_LOCAL_STATE_RUN;
|
target = PSCI_LOCAL_STATE_RUN;
|
||||||
|
|
||||||
|
/*******************************************
|
||||||
|
* BPMP is not present, so handle CC6 entry
|
||||||
|
* from the CPU
|
||||||
|
******************************************/
|
||||||
|
|
||||||
|
/* check if cluster idle state has been enabled */
|
||||||
|
val = mmio_read_32(TEGRA_CL_DVFS_BASE + DVFS_DFLL_CTRL);
|
||||||
|
if (val == ENABLE_CLOSED_LOOP) {
|
||||||
|
/*
|
||||||
|
* flag to indicate that BPMP firmware is not
|
||||||
|
* available and the CPU has to handle entry/exit
|
||||||
|
* for all power states
|
||||||
|
*/
|
||||||
|
tegra_bpmp_available = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Acquire the cluster idle lock to stop
|
||||||
|
* other CPUs from powering up.
|
||||||
|
*/
|
||||||
|
tegra_fc_ccplex_pgexit_lock();
|
||||||
|
|
||||||
|
/* Cluster idle only from the last standing CPU */
|
||||||
|
if (tegra_pmc_is_last_on_cpu() && tegra_fc_is_ccx_allowed()) {
|
||||||
|
/* Cluster idle allowed */
|
||||||
|
target = PSTATE_ID_CLUSTER_IDLE;
|
||||||
|
} else {
|
||||||
|
/* release cluster idle lock */
|
||||||
|
tegra_fc_ccplex_pgexit_unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
/* Cluster idle */
|
/* Cluster power-down */
|
||||||
data[0] = (uint32_t)cpu;
|
data[0] = (uint32_t)cpu;
|
||||||
data[1] = TEGRA_PM_CC6;
|
data[1] = TEGRA_PM_CC6;
|
||||||
data[2] = TEGRA_PM_SC1;
|
data[2] = TEGRA_PM_SC1;
|
||||||
|
@ -120,10 +153,10 @@ plat_local_state_t tegra_soc_get_target_pwr_state(unsigned int lvl,
|
||||||
(void *)&bpmp_reply,
|
(void *)&bpmp_reply,
|
||||||
(int)sizeof(bpmp_reply));
|
(int)sizeof(bpmp_reply));
|
||||||
|
|
||||||
/* check if cluster idle entry is allowed */
|
/* check if cluster power down is allowed */
|
||||||
if ((ret != 0L) || (bpmp_reply != BPMP_CCx_ALLOWED)) {
|
if ((ret != 0L) || (bpmp_reply != BPMP_CCx_ALLOWED)) {
|
||||||
|
|
||||||
/* Cluster idle not allowed */
|
/* Cluster power down not allowed */
|
||||||
target = PSCI_LOCAL_STATE_RUN;
|
target = PSCI_LOCAL_STATE_RUN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,7 +209,9 @@ int tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state)
|
||||||
unsigned int stateid_afflvl2 = pwr_domain_state[MPIDR_AFFLVL2];
|
unsigned int stateid_afflvl2 = pwr_domain_state[MPIDR_AFFLVL2];
|
||||||
unsigned int stateid_afflvl1 = pwr_domain_state[MPIDR_AFFLVL1];
|
unsigned int stateid_afflvl1 = pwr_domain_state[MPIDR_AFFLVL1];
|
||||||
unsigned int stateid_afflvl0 = pwr_domain_state[MPIDR_AFFLVL0];
|
unsigned int stateid_afflvl0 = pwr_domain_state[MPIDR_AFFLVL0];
|
||||||
|
uint32_t cfg;
|
||||||
int ret = PSCI_E_SUCCESS;
|
int ret = PSCI_E_SUCCESS;
|
||||||
|
uint32_t val;
|
||||||
|
|
||||||
if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) {
|
if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) {
|
||||||
|
|
||||||
|
@ -197,6 +232,17 @@ int tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state)
|
||||||
|
|
||||||
assert(stateid_afflvl0 == PSTATE_ID_CORE_POWERDN);
|
assert(stateid_afflvl0 == PSTATE_ID_CORE_POWERDN);
|
||||||
|
|
||||||
|
if (!tegra_bpmp_available) {
|
||||||
|
|
||||||
|
/* PWM tristate */
|
||||||
|
cfg = mmio_read_32(TEGRA_CL_DVFS_BASE + DVFS_DFLL_OUTPUT_CFG);
|
||||||
|
if (cfg & DFLL_OUTPUT_CFG_CLK_EN_BIT) {
|
||||||
|
val = mmio_read_32(TEGRA_MISC_BASE + PINMUX_AUX_DVFS_PWM);
|
||||||
|
val |= PINMUX_PWM_TRISTATE;
|
||||||
|
mmio_write_32(TEGRA_MISC_BASE + PINMUX_AUX_DVFS_PWM, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Prepare for cluster idle */
|
/* Prepare for cluster idle */
|
||||||
tegra_fc_cluster_idle(mpidr);
|
tegra_fc_cluster_idle(mpidr);
|
||||||
|
|
||||||
|
@ -245,6 +291,7 @@ int tegra_soc_pwr_domain_power_down_wfi(const psci_power_state_t *target_state)
|
||||||
int tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state)
|
int tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state)
|
||||||
{
|
{
|
||||||
const plat_params_from_bl2_t *plat_params = bl31_get_plat_params();
|
const plat_params_from_bl2_t *plat_params = bl31_get_plat_params();
|
||||||
|
uint32_t cfg;
|
||||||
uint32_t val;
|
uint32_t val;
|
||||||
|
|
||||||
/* platform parameter passed by the previous bootloader */
|
/* platform parameter passed by the previous bootloader */
|
||||||
|
@ -286,7 +333,29 @@ int tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state)
|
||||||
* Restore Boot and Power Management Processor (BPMP) reset
|
* Restore Boot and Power Management Processor (BPMP) reset
|
||||||
* address and reset it.
|
* address and reset it.
|
||||||
*/
|
*/
|
||||||
tegra_fc_reset_bpmp();
|
if (tegra_bpmp_available)
|
||||||
|
tegra_fc_reset_bpmp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if we are exiting cluster idle state
|
||||||
|
*/
|
||||||
|
if (target_state->pwr_domain_state[MPIDR_AFFLVL1] ==
|
||||||
|
PSTATE_ID_CLUSTER_IDLE) {
|
||||||
|
|
||||||
|
if (!tegra_bpmp_available) {
|
||||||
|
|
||||||
|
/* PWM un-tristate */
|
||||||
|
cfg = mmio_read_32(TEGRA_CL_DVFS_BASE + DVFS_DFLL_OUTPUT_CFG);
|
||||||
|
if (cfg & DFLL_OUTPUT_CFG_CLK_EN_BIT) {
|
||||||
|
val = mmio_read_32(TEGRA_MISC_BASE + PINMUX_AUX_DVFS_PWM);
|
||||||
|
val &= ~PINMUX_PWM_TRISTATE;
|
||||||
|
mmio_write_32(TEGRA_MISC_BASE + PINMUX_AUX_DVFS_PWM, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* release cluster idle lock */
|
||||||
|
tegra_fc_ccplex_pgexit_unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -51,3 +51,6 @@ A53_DISABLE_NON_TEMPORAL_HINT := 1
|
||||||
ERRATA_A53_826319 := 1
|
ERRATA_A53_826319 := 1
|
||||||
ERRATA_A53_836870 := 1
|
ERRATA_A53_836870 := 1
|
||||||
ERRATA_A53_855873 := 1
|
ERRATA_A53_855873 := 1
|
||||||
|
|
||||||
|
# Skip L1 $ flush when powering down Cortex-A57 CPUs
|
||||||
|
SKIP_A57_L1_FLUSH_PWR_DWN := 1
|
||||||
|
|
Loading…
Reference in New Issue