aboutsummaryrefslogtreecommitdiff
path: root/plat/nvidia/tegra/soc/t210
diff options
context:
space:
mode:
authorVarun Wadekar <vwadekar@nvidia.com>2018-02-13 20:31:12 -0800
committerVarun Wadekar <vwadekar@nvidia.com>2019-01-31 08:48:09 -0800
commit7db077f2e3116cc47d9a146f39555559faef42d7 (patch)
tree568d27577e765cc111596ba60011655e8f7e952e /plat/nvidia/tegra/soc/t210
parenta7a63e0ee5db9b53f666ea0d7bf83d95ea04bd14 (diff)
downloadtrusted-firmware-a-7db077f2e3116cc47d9a146f39555559faef42d7.tar.gz
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>
Diffstat (limited to 'plat/nvidia/tegra/soc/t210')
-rw-r--r--plat/nvidia/tegra/soc/t210/plat_psci_handlers.c87
-rw-r--r--plat/nvidia/tegra/soc/t210/platform_t210.mk3
2 files changed, 81 insertions, 9 deletions
diff --git a/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c b/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c
index f52d975d83..0aa36b4ef8 100644
--- a/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c
+++ b/plat/nvidia/tegra/soc/t210/plat_psci_handlers.c
@@ -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
*/
@@ -35,6 +35,7 @@
#define SCLK_BURST_POLICY_DEFAULT 0x10000000
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,
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_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_AFFLVL1] = state_id;
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
- * specified affinity level
+ * specified affinity level.
******************************************************************************/
plat_local_state_t tegra_soc_get_target_pwr_state(unsigned int lvl,
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;
int cpu = plat_my_core_pos();
int core_pos = read_mpidr() & MPIDR_CPU_MASK;
- uint32_t bpmp_reply, data[3];
+ uint32_t bpmp_reply, data[3], val;
int ret;
/* 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 */
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 {
- /* Cluster idle */
+ /* Cluster power-down */
data[0] = (uint32_t)cpu;
data[1] = TEGRA_PM_CC6;
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,
(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)) {
- /* Cluster idle not allowed */
+ /* Cluster power down not allowed */
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_afflvl1 = pwr_domain_state[MPIDR_AFFLVL1];
unsigned int stateid_afflvl0 = pwr_domain_state[MPIDR_AFFLVL0];
+ uint32_t cfg;
int ret = PSCI_E_SUCCESS;
+ uint32_t val;
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);
+ 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 */
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)
{
const plat_params_from_bl2_t *plat_params = bl31_get_plat_params();
+ uint32_t cfg;
uint32_t val;
/* 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
* 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();
+ }
}
/*
diff --git a/plat/nvidia/tegra/soc/t210/platform_t210.mk b/plat/nvidia/tegra/soc/t210/platform_t210.mk
index 59077eb81c..723534a1d7 100644
--- a/plat/nvidia/tegra/soc/t210/platform_t210.mk
+++ b/plat/nvidia/tegra/soc/t210/platform_t210.mk
@@ -51,3 +51,6 @@ A53_DISABLE_NON_TEMPORAL_HINT := 1
ERRATA_A53_826319 := 1
ERRATA_A53_836870 := 1
ERRATA_A53_855873 := 1
+
+# Skip L1 $ flush when powering down Cortex-A57 CPUs
+SKIP_A57_L1_FLUSH_PWR_DWN := 1