diff options
author | Varun Wadekar <vwadekar@nvidia.com> | 2018-02-13 20:31:12 -0800 |
---|---|---|
committer | Varun Wadekar <vwadekar@nvidia.com> | 2019-01-31 08:48:09 -0800 |
commit | 7db077f2e3116cc47d9a146f39555559faef42d7 (patch) | |
tree | 568d27577e765cc111596ba60011655e8f7e952e /plat/nvidia/tegra/soc/t210 | |
parent | a7a63e0ee5db9b53f666ea0d7bf83d95ea04bd14 (diff) | |
download | trusted-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.c | 87 | ||||
-rw-r--r-- | plat/nvidia/tegra/soc/t210/platform_t210.mk | 3 |
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 |