13cf3183fSVarun Wadekar /*
293c78ed2SAntonio Nino Diaz * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
3e44f86efSVarun Wadekar * Copyright (c) 2020, NVIDIA Corporation. All rights reserved.
43cf3183fSVarun Wadekar *
582cb2c1aSdp-arm * SPDX-License-Identifier: BSD-3-Clause
63cf3183fSVarun Wadekar */
73cf3183fSVarun Wadekar
809d40e0eSAntonio Nino Diaz #include <assert.h>
94eed9c84SJeetesh Burman #include <stdbool.h>
1009d40e0eSAntonio Nino Diaz #include <string.h>
1109d40e0eSAntonio Nino Diaz
12b47d97b3SVarun Wadekar #include <arch.h>
13b47d97b3SVarun Wadekar #include <arch_helpers.h>
1409d40e0eSAntonio Nino Diaz #include <common/bl_common.h>
1509d40e0eSAntonio Nino Diaz #include <common/debug.h>
16348619f2SVarun Wadekar #include <context.h>
17b495791bSHarvey Hsieh #include <cortex_a57.h>
18aa1bdc96SVarun Wadekar #include <denver.h>
1909d40e0eSAntonio Nino Diaz #include <lib/el3_runtime/context_mgmt.h>
2009d40e0eSAntonio Nino Diaz #include <lib/psci/psci.h>
2109d40e0eSAntonio Nino Diaz #include <plat/common/platform.h>
2209d40e0eSAntonio Nino Diaz
234eed9c84SJeetesh Burman #include <bpmp_ipc.h>
24b47d97b3SVarun Wadekar #include <mce.h>
25a391d494SPritesh Raithatha #include <memctrl_v2.h>
264eed9c84SJeetesh Burman #include <security_engine.h>
2750402b17SVarun Wadekar #include <smmu.h>
28c7ec0892SVarun Wadekar #include <t18x_ari.h>
29889c07c7SVarun Wadekar #include <tegra186_private.h>
303cf3183fSVarun Wadekar #include <tegra_private.h>
313cf3183fSVarun Wadekar
3293c78ed2SAntonio Nino Diaz extern void memcpy16(void *dest, const void *src, unsigned int length);
337eaf040aSVarun Wadekar
347afd4637SVarun Wadekar /* state id mask */
35214e8464SAnthony Zhou #define TEGRA186_STATE_ID_MASK 0xFU
367afd4637SVarun Wadekar /* constants to get power state's wake time */
37214e8464SAnthony Zhou #define TEGRA186_WAKE_TIME_MASK 0x0FFFFFF0U
38214e8464SAnthony Zhou #define TEGRA186_WAKE_TIME_SHIFT 4U
391b9ab054SVarun Wadekar /* default core wake mask for CPU_SUSPEND */
40214e8464SAnthony Zhou #define TEGRA186_CORE_WAKE_MASK 0x180cU
4150402b17SVarun Wadekar /* context size to save during system suspend */
42214e8464SAnthony Zhou #define TEGRA186_SE_CONTEXT_SIZE 3U
437afd4637SVarun Wadekar
4450402b17SVarun Wadekar static uint32_t se_regs[TEGRA186_SE_CONTEXT_SIZE];
45214e8464SAnthony Zhou static struct tegra_psci_percpu_data {
46214e8464SAnthony Zhou uint32_t wake_time;
47214e8464SAnthony Zhou } __aligned(CACHE_WRITEBACK_GRANULE) tegra_percpu_data[PLATFORM_CORE_COUNT];
487afd4637SVarun Wadekar
tegra_soc_validate_power_state(uint32_t power_state,psci_power_state_t * req_state)49214e8464SAnthony Zhou int32_t tegra_soc_validate_power_state(uint32_t power_state,
50b67a7c7cSVarun Wadekar psci_power_state_t *req_state)
513cf3183fSVarun Wadekar {
52214e8464SAnthony Zhou uint8_t state_id = (uint8_t)psci_get_pstate_id(power_state) & TEGRA186_STATE_ID_MASK;
53214e8464SAnthony Zhou uint32_t cpu = plat_my_core_pos();
54214e8464SAnthony Zhou int32_t ret = PSCI_E_SUCCESS;
55b67a7c7cSVarun Wadekar
565ea1fe56SKrishna Sitaraman /* save the core wake time (in TSC ticks)*/
57214e8464SAnthony Zhou tegra_percpu_data[cpu].wake_time = (power_state & TEGRA186_WAKE_TIME_MASK)
585ea1fe56SKrishna Sitaraman << TEGRA186_WAKE_TIME_SHIFT;
597afd4637SVarun Wadekar
60322b00fcSMustafa Yigit Bilgen /*
61322b00fcSMustafa Yigit Bilgen * Clean percpu_data[cpu] to DRAM. This needs to be done to ensure that
62322b00fcSMustafa Yigit Bilgen * the correct value is read in tegra_soc_pwr_domain_suspend(), which
63322b00fcSMustafa Yigit Bilgen * is called with caches disabled. It is possible to read a stale value
64322b00fcSMustafa Yigit Bilgen * from DRAM in that function, because the L2 cache is not flushed
65322b00fcSMustafa Yigit Bilgen * unless the cluster is entering CC6/CC7.
66322b00fcSMustafa Yigit Bilgen */
67214e8464SAnthony Zhou clean_dcache_range((uint64_t)&tegra_percpu_data[cpu],
68214e8464SAnthony Zhou sizeof(tegra_percpu_data[cpu]));
69322b00fcSMustafa Yigit Bilgen
707afd4637SVarun Wadekar /* Sanity check the requested state id */
717afd4637SVarun Wadekar switch (state_id) {
727afd4637SVarun Wadekar case PSTATE_ID_CORE_IDLE:
737afd4637SVarun Wadekar case PSTATE_ID_CORE_POWERDN:
74f3a20c32SVarun Wadekar
7526c22a5eSVarun Wadekar if (psci_get_pstate_type(power_state) != PSTATE_TYPE_POWERDOWN) {
7626c22a5eSVarun Wadekar ret = PSCI_E_INVALID_PARAMS;
7726c22a5eSVarun Wadekar break;
7826c22a5eSVarun Wadekar }
7926c22a5eSVarun Wadekar
80f3a20c32SVarun Wadekar /* Core powerdown request */
817afd4637SVarun Wadekar req_state->pwr_domain_state[MPIDR_AFFLVL0] = state_id;
82f3a20c32SVarun Wadekar req_state->pwr_domain_state[MPIDR_AFFLVL1] = state_id;
83b67a7c7cSVarun Wadekar
847afd4637SVarun Wadekar break;
857afd4637SVarun Wadekar
867afd4637SVarun Wadekar default:
877afd4637SVarun Wadekar ERROR("%s: unsupported state id (%d)\n", __func__, state_id);
88214e8464SAnthony Zhou ret = PSCI_E_INVALID_PARAMS;
89214e8464SAnthony Zhou break;
907afd4637SVarun Wadekar }
917afd4637SVarun Wadekar
92214e8464SAnthony Zhou return ret;
937afd4637SVarun Wadekar }
947afd4637SVarun Wadekar
tegra_soc_cpu_standby(plat_local_state_t cpu_state)95e44f86efSVarun Wadekar int32_t tegra_soc_cpu_standby(plat_local_state_t cpu_state)
96e44f86efSVarun Wadekar {
97e44f86efSVarun Wadekar (void)cpu_state;
98e44f86efSVarun Wadekar return PSCI_E_SUCCESS;
99e44f86efSVarun Wadekar }
100e44f86efSVarun Wadekar
tegra_soc_pwr_domain_suspend(const psci_power_state_t * target_state)101214e8464SAnthony Zhou int32_t tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state)
1027afd4637SVarun Wadekar {
1037afd4637SVarun Wadekar const plat_local_state_t *pwr_domain_state;
104214e8464SAnthony Zhou uint8_t stateid_afflvl0, stateid_afflvl2;
105214e8464SAnthony Zhou uint32_t cpu = plat_my_core_pos();
106214e8464SAnthony Zhou const plat_params_from_bl2_t *params_from_bl2 = bl31_get_plat_params();
107f3a20c32SVarun Wadekar mce_cstate_info_t cstate_info = { 0 };
108a391d494SPritesh Raithatha uint64_t mc_ctx_base;
10950402b17SVarun Wadekar uint32_t val;
11050402b17SVarun Wadekar
1117afd4637SVarun Wadekar /* get the state ID */
1127afd4637SVarun Wadekar pwr_domain_state = target_state->pwr_domain_state;
1137afd4637SVarun Wadekar stateid_afflvl0 = pwr_domain_state[MPIDR_AFFLVL0] &
1147afd4637SVarun Wadekar TEGRA186_STATE_ID_MASK;
11550402b17SVarun Wadekar stateid_afflvl2 = pwr_domain_state[PLAT_MAX_PWR_LVL] &
11650402b17SVarun Wadekar TEGRA186_STATE_ID_MASK;
1177afd4637SVarun Wadekar
118f3a20c32SVarun Wadekar if ((stateid_afflvl0 == PSTATE_ID_CORE_IDLE) ||
119f3a20c32SVarun Wadekar (stateid_afflvl0 == PSTATE_ID_CORE_POWERDN)) {
1207afd4637SVarun Wadekar
121f3a20c32SVarun Wadekar /* Enter CPU idle/powerdown */
122f3a20c32SVarun Wadekar val = (stateid_afflvl0 == PSTATE_ID_CORE_IDLE) ?
123aa64c5fbSAnthony Zhou (uint32_t)TEGRA_ARI_CORE_C6 : (uint32_t)TEGRA_ARI_CORE_C7;
124214e8464SAnthony Zhou (void)mce_command_handler((uint64_t)MCE_CMD_ENTER_CSTATE, (uint64_t)val,
125214e8464SAnthony Zhou tegra_percpu_data[cpu].wake_time, 0U);
1267afd4637SVarun Wadekar
12750402b17SVarun Wadekar } else if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) {
12850402b17SVarun Wadekar
12950402b17SVarun Wadekar /* save SE registers */
13050402b17SVarun Wadekar se_regs[0] = mmio_read_32(TEGRA_SE0_BASE +
13150402b17SVarun Wadekar SE_MUTEX_WATCHDOG_NS_LIMIT);
13250402b17SVarun Wadekar se_regs[1] = mmio_read_32(TEGRA_RNG1_BASE +
13350402b17SVarun Wadekar RNG_MUTEX_WATCHDOG_NS_LIMIT);
13450402b17SVarun Wadekar se_regs[2] = mmio_read_32(TEGRA_PKA1_BASE +
13550402b17SVarun Wadekar PKA_MUTEX_WATCHDOG_NS_LIMIT);
13650402b17SVarun Wadekar
13750402b17SVarun Wadekar /* save 'Secure Boot' Processor Feature Config Register */
13850402b17SVarun Wadekar val = mmio_read_32(TEGRA_MISC_BASE + MISCREG_PFCFG);
139601a8e54SSteven Kao mmio_write_32(TEGRA_SCRATCH_BASE + SCRATCH_SECURE_BOOTP_FCFG, val);
14050402b17SVarun Wadekar
141a391d494SPritesh Raithatha /* save MC context to TZDRAM */
1422139c9c8SVarun Wadekar mc_ctx_base = params_from_bl2->tzdram_base;
143a391d494SPritesh Raithatha tegra_mc_save_context((uintptr_t)mc_ctx_base);
14450402b17SVarun Wadekar
14550402b17SVarun Wadekar /* Prepare for system suspend */
146aa64c5fbSAnthony Zhou cstate_info.cluster = (uint32_t)TEGRA_ARI_CLUSTER_CC7;
147aa64c5fbSAnthony Zhou cstate_info.system = (uint32_t)TEGRA_ARI_SYSTEM_SC7;
148f3a20c32SVarun Wadekar cstate_info.system_state_force = 1;
149f3a20c32SVarun Wadekar cstate_info.update_wake_mask = 1;
150f3a20c32SVarun Wadekar mce_update_cstate_info(&cstate_info);
151539c62d7SVarun Wadekar
15250f38a4aSVarun Wadekar /* Loop until system suspend is allowed */
15350f38a4aSVarun Wadekar do {
154214e8464SAnthony Zhou val = (uint32_t)mce_command_handler(
155214e8464SAnthony Zhou (uint64_t)MCE_CMD_IS_SC7_ALLOWED,
156aa64c5fbSAnthony Zhou (uint64_t)TEGRA_ARI_CORE_C7,
15750f38a4aSVarun Wadekar MCE_CORE_SLEEP_TIME_INFINITE,
158214e8464SAnthony Zhou 0U);
159214e8464SAnthony Zhou } while (val == 0U);
16050f38a4aSVarun Wadekar
16168c7de6fSVarun Wadekar /* Instruct the MCE to enter system suspend state */
162214e8464SAnthony Zhou (void)mce_command_handler((uint64_t)MCE_CMD_ENTER_CSTATE,
163aa64c5fbSAnthony Zhou (uint64_t)TEGRA_ARI_CORE_C7, MCE_CORE_SLEEP_TIME_INFINITE, 0U);
164539c62d7SVarun Wadekar
165214e8464SAnthony Zhou } else {
166214e8464SAnthony Zhou ; /* do nothing */
1673cf3183fSVarun Wadekar }
1683cf3183fSVarun Wadekar
1693cf3183fSVarun Wadekar return PSCI_E_SUCCESS;
1703cf3183fSVarun Wadekar }
171b47d97b3SVarun Wadekar
172f3a20c32SVarun Wadekar /*******************************************************************************
1734e1830a9SVarun Wadekar * Helper function to check if this is the last ON CPU in the cluster
174f3a20c32SVarun Wadekar ******************************************************************************/
tegra_last_cpu_in_cluster(const plat_local_state_t * states,uint32_t ncpu)1754e1830a9SVarun Wadekar static bool tegra_last_cpu_in_cluster(const plat_local_state_t *states,
176214e8464SAnthony Zhou uint32_t ncpu)
177f3a20c32SVarun Wadekar {
1784e1830a9SVarun Wadekar plat_local_state_t target;
1794e1830a9SVarun Wadekar bool last_on_cpu = true;
1804e1830a9SVarun Wadekar uint32_t num_cpus = ncpu, pos = 0;
1814e1830a9SVarun Wadekar
1824e1830a9SVarun Wadekar do {
1834e1830a9SVarun Wadekar target = states[pos];
1844e1830a9SVarun Wadekar if (target != PLAT_MAX_OFF_STATE) {
1854e1830a9SVarun Wadekar last_on_cpu = false;
1864e1830a9SVarun Wadekar }
1874e1830a9SVarun Wadekar --num_cpus;
1884e1830a9SVarun Wadekar pos++;
1894e1830a9SVarun Wadekar } while (num_cpus != 0U);
1904e1830a9SVarun Wadekar
1914e1830a9SVarun Wadekar return last_on_cpu;
1924e1830a9SVarun Wadekar }
1934e1830a9SVarun Wadekar
1944e1830a9SVarun Wadekar /*******************************************************************************
1954e1830a9SVarun Wadekar * Helper function to get target power state for the cluster
1964e1830a9SVarun Wadekar ******************************************************************************/
tegra_get_afflvl1_pwr_state(const plat_local_state_t * states,uint32_t ncpu)1974e1830a9SVarun Wadekar static plat_local_state_t tegra_get_afflvl1_pwr_state(const plat_local_state_t *states,
1984e1830a9SVarun Wadekar uint32_t ncpu)
1994e1830a9SVarun Wadekar {
2004e1830a9SVarun Wadekar uint32_t core_pos = (uint32_t)read_mpidr() & (uint32_t)MPIDR_CPU_MASK;
2014e1830a9SVarun Wadekar uint32_t cpu = plat_my_core_pos();
2024e1830a9SVarun Wadekar int32_t ret;
2034e1830a9SVarun Wadekar plat_local_state_t target = states[core_pos];
204f3a20c32SVarun Wadekar mce_cstate_info_t cstate_info = { 0 };
205f3a20c32SVarun Wadekar
206f3a20c32SVarun Wadekar /* CPU suspend */
2074e1830a9SVarun Wadekar if (target == PSTATE_ID_CORE_POWERDN) {
208f3a20c32SVarun Wadekar /* Program default wake mask */
209f3a20c32SVarun Wadekar cstate_info.wake_mask = TEGRA186_CORE_WAKE_MASK;
210f3a20c32SVarun Wadekar cstate_info.update_wake_mask = 1;
211f3a20c32SVarun Wadekar mce_update_cstate_info(&cstate_info);
212f3a20c32SVarun Wadekar
213f3a20c32SVarun Wadekar /* Check if CCx state is allowed. */
214214e8464SAnthony Zhou ret = mce_command_handler((uint64_t)MCE_CMD_IS_CCX_ALLOWED,
2154e1830a9SVarun Wadekar (uint64_t)TEGRA_ARI_CORE_C7,
2164e1830a9SVarun Wadekar tegra_percpu_data[cpu].wake_time,
217214e8464SAnthony Zhou 0U);
2184e1830a9SVarun Wadekar if (ret == 0) {
2194e1830a9SVarun Wadekar target = PSCI_LOCAL_STATE_RUN;
220214e8464SAnthony Zhou }
221f3a20c32SVarun Wadekar }
222f3a20c32SVarun Wadekar
223f3a20c32SVarun Wadekar /* CPU off */
2244e1830a9SVarun Wadekar if (target == PLAT_MAX_OFF_STATE) {
225f3a20c32SVarun Wadekar /* Enable cluster powerdn from last CPU in the cluster */
2264e1830a9SVarun Wadekar if (tegra_last_cpu_in_cluster(states, ncpu)) {
227f3a20c32SVarun Wadekar /* Enable CC7 state and turn off wake mask */
2284e1830a9SVarun Wadekar cstate_info.cluster = (uint32_t)TEGRA_ARI_CLUSTER_CC7;
229f3a20c32SVarun Wadekar cstate_info.update_wake_mask = 1;
230f3a20c32SVarun Wadekar mce_update_cstate_info(&cstate_info);
231f3a20c32SVarun Wadekar
232f3a20c32SVarun Wadekar /* Check if CCx state is allowed. */
233214e8464SAnthony Zhou ret = mce_command_handler((uint64_t)MCE_CMD_IS_CCX_ALLOWED,
2344e1830a9SVarun Wadekar (uint64_t)TEGRA_ARI_CORE_C7,
235f3a20c32SVarun Wadekar MCE_CORE_SLEEP_TIME_INFINITE,
236214e8464SAnthony Zhou 0U);
2374e1830a9SVarun Wadekar if (ret == 0) {
2384e1830a9SVarun Wadekar target = PSCI_LOCAL_STATE_RUN;
239214e8464SAnthony Zhou }
240f3a20c32SVarun Wadekar
241f3a20c32SVarun Wadekar } else {
242f3a20c32SVarun Wadekar
243f3a20c32SVarun Wadekar /* Turn off wake_mask */
244f3a20c32SVarun Wadekar cstate_info.update_wake_mask = 1;
245f3a20c32SVarun Wadekar mce_update_cstate_info(&cstate_info);
2464e1830a9SVarun Wadekar target = PSCI_LOCAL_STATE_RUN;
247f3a20c32SVarun Wadekar }
248f3a20c32SVarun Wadekar }
249f3a20c32SVarun Wadekar
2504e1830a9SVarun Wadekar return target;
2514e1830a9SVarun Wadekar }
2524e1830a9SVarun Wadekar
2534e1830a9SVarun Wadekar /*******************************************************************************
2544e1830a9SVarun Wadekar * Platform handler to calculate the proper target power level at the
2554e1830a9SVarun Wadekar * specified affinity level
2564e1830a9SVarun Wadekar ******************************************************************************/
tegra_soc_get_target_pwr_state(uint32_t lvl,const plat_local_state_t * states,uint32_t ncpu)257aa64c5fbSAnthony Zhou plat_local_state_t tegra_soc_get_target_pwr_state(uint32_t lvl,
2584e1830a9SVarun Wadekar const plat_local_state_t *states,
2594e1830a9SVarun Wadekar uint32_t ncpu)
2604e1830a9SVarun Wadekar {
2614e1830a9SVarun Wadekar plat_local_state_t target = PSCI_LOCAL_STATE_RUN;
262aa64c5fbSAnthony Zhou uint32_t cpu = plat_my_core_pos();
2634e1830a9SVarun Wadekar
264f3a20c32SVarun Wadekar /* System Suspend */
2654e1830a9SVarun Wadekar if ((lvl == (uint32_t)MPIDR_AFFLVL2) &&
2664e1830a9SVarun Wadekar (states[cpu] == PSTATE_ID_SOC_POWERDN)) {
2674e1830a9SVarun Wadekar target = PSTATE_ID_SOC_POWERDN;
268f3a20c32SVarun Wadekar }
269f3a20c32SVarun Wadekar
2704e1830a9SVarun Wadekar /* CPU off, CPU suspend */
2714e1830a9SVarun Wadekar if (lvl == (uint32_t)MPIDR_AFFLVL1) {
2724e1830a9SVarun Wadekar target = tegra_get_afflvl1_pwr_state(states, ncpu);
2734e1830a9SVarun Wadekar }
2744e1830a9SVarun Wadekar
2754e1830a9SVarun Wadekar /* target cluster/system state */
2764e1830a9SVarun Wadekar return target;
277214e8464SAnthony Zhou }
278214e8464SAnthony Zhou
tegra_soc_pwr_domain_power_down_wfi(const psci_power_state_t * target_state)279214e8464SAnthony Zhou int32_t tegra_soc_pwr_domain_power_down_wfi(const psci_power_state_t *target_state)
28068c7de6fSVarun Wadekar {
28168c7de6fSVarun Wadekar const plat_local_state_t *pwr_domain_state =
28268c7de6fSVarun Wadekar target_state->pwr_domain_state;
283214e8464SAnthony Zhou const plat_params_from_bl2_t *params_from_bl2 = bl31_get_plat_params();
284214e8464SAnthony Zhou uint8_t stateid_afflvl2 = pwr_domain_state[PLAT_MAX_PWR_LVL] &
28568c7de6fSVarun Wadekar TEGRA186_STATE_ID_MASK;
28683f3f536SSteven Kao uint64_t val;
2874eed9c84SJeetesh Burman uint64_t src_len_in_bytes = (uint64_t)(((uintptr_t)(&__BL31_END__) -
2884eed9c84SJeetesh Burman (uintptr_t)BL31_BASE));
2894eed9c84SJeetesh Burman int32_t ret;
29068c7de6fSVarun Wadekar
29168c7de6fSVarun Wadekar if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) {
2924eed9c84SJeetesh Burman val = params_from_bl2->tzdram_base +
2932139c9c8SVarun Wadekar tegra186_get_mc_ctx_size();
2944eed9c84SJeetesh Burman
2954eed9c84SJeetesh Burman /* Initialise communication channel with BPMP */
2964eed9c84SJeetesh Burman assert(tegra_bpmp_ipc_init() == 0);
2974eed9c84SJeetesh Burman
2984eed9c84SJeetesh Burman /* Enable SE clock */
299e9044480SVarun Wadekar ret = tegra_bpmp_ipc_enable_clock(TEGRA186_CLK_SE);
3004eed9c84SJeetesh Burman if (ret != 0) {
3014eed9c84SJeetesh Burman ERROR("Failed to enable clock\n");
3024eed9c84SJeetesh Burman return ret;
3034eed9c84SJeetesh Burman }
3044eed9c84SJeetesh Burman
3054eed9c84SJeetesh Burman /*
3064eed9c84SJeetesh Burman * Generate/save SHA256 of ATF during SC7 entry
3074eed9c84SJeetesh Burman */
3084eed9c84SJeetesh Burman if (tegra_se_save_sha256_hash(BL31_BASE,
3094eed9c84SJeetesh Burman (uint32_t)src_len_in_bytes) != 0) {
3104eed9c84SJeetesh Burman ERROR("Hash calculation failed. Reboot\n");
3114eed9c84SJeetesh Burman (void)tegra_soc_prepare_system_reset();
3124eed9c84SJeetesh Burman }
3134eed9c84SJeetesh Burman
31468c7de6fSVarun Wadekar /*
31568c7de6fSVarun Wadekar * The TZRAM loses power when we enter system suspend. To
31668c7de6fSVarun Wadekar * allow graceful exit from system suspend, we need to copy
31768c7de6fSVarun Wadekar * BL3-1 over to TZDRAM.
31868c7de6fSVarun Wadekar */
31968c7de6fSVarun Wadekar val = params_from_bl2->tzdram_base +
3202139c9c8SVarun Wadekar tegra186_get_mc_ctx_size();
32168c7de6fSVarun Wadekar memcpy16((void *)(uintptr_t)val, (void *)(uintptr_t)BL31_BASE,
322b8c7e54dSVarun Wadekar (uintptr_t)BL31_END - (uintptr_t)BL31_BASE);
3234eed9c84SJeetesh Burman
3242139c9c8SVarun Wadekar /*
3252139c9c8SVarun Wadekar * Save code base and size; this would be used by SC7-RF to
3262139c9c8SVarun Wadekar * verify binary
3272139c9c8SVarun Wadekar */
3282139c9c8SVarun Wadekar mmio_write_32(TEGRA_SCRATCH_BASE + SECURE_SCRATCH_RSV68_LO,
3292139c9c8SVarun Wadekar (uint32_t)val);
3302139c9c8SVarun Wadekar mmio_write_32(TEGRA_SCRATCH_BASE + SECURE_SCRATCH_RSV0_HI,
3312139c9c8SVarun Wadekar (uint32_t)src_len_in_bytes);
3322139c9c8SVarun Wadekar
333e9044480SVarun Wadekar ret = tegra_bpmp_ipc_disable_clock(TEGRA186_CLK_SE);
3344eed9c84SJeetesh Burman if (ret != 0) {
3354eed9c84SJeetesh Burman ERROR("Failed to disable clock\n");
3364eed9c84SJeetesh Burman return ret;
3374eed9c84SJeetesh Burman }
33868c7de6fSVarun Wadekar }
33968c7de6fSVarun Wadekar
34068c7de6fSVarun Wadekar return PSCI_E_SUCCESS;
34168c7de6fSVarun Wadekar }
34268c7de6fSVarun Wadekar
tegra_soc_pwr_domain_suspend_pwrdown_early(const psci_power_state_t * target_state)343e44f86efSVarun Wadekar int32_t tegra_soc_pwr_domain_suspend_pwrdown_early(const psci_power_state_t *target_state)
344e44f86efSVarun Wadekar {
345e44f86efSVarun Wadekar return PSCI_E_NOT_SUPPORTED;
346e44f86efSVarun Wadekar }
347e44f86efSVarun Wadekar
tegra_soc_pwr_domain_on(u_register_t mpidr)348214e8464SAnthony Zhou int32_t tegra_soc_pwr_domain_on(u_register_t mpidr)
349b47d97b3SVarun Wadekar {
350214e8464SAnthony Zhou int32_t ret = PSCI_E_SUCCESS;
3519e7a2436SAnthony Zhou uint64_t target_cpu = mpidr & MPIDR_CPU_MASK;
3529e7a2436SAnthony Zhou uint64_t target_cluster = (mpidr & MPIDR_CLUSTER_MASK) >>
3539e7a2436SAnthony Zhou MPIDR_AFFINITY_BITS;
354b47d97b3SVarun Wadekar
355b6d1757bSVarun Wadekar if (target_cluster > ((uint32_t)PLATFORM_CLUSTER_COUNT - 1U)) {
356214e8464SAnthony Zhou
357b47d97b3SVarun Wadekar ERROR("%s: unsupported CPU (0x%lx)\n", __func__, mpidr);
358214e8464SAnthony Zhou ret = PSCI_E_NOT_PRESENT;
359b47d97b3SVarun Wadekar
360214e8464SAnthony Zhou } else {
361b47d97b3SVarun Wadekar /* construct the target CPU # */
362b47d97b3SVarun Wadekar target_cpu |= (target_cluster << 2);
363b47d97b3SVarun Wadekar
364214e8464SAnthony Zhou (void)mce_command_handler((uint64_t)MCE_CMD_ONLINE_CORE, target_cpu, 0U, 0U);
365b47d97b3SVarun Wadekar }
366b47d97b3SVarun Wadekar
367214e8464SAnthony Zhou return ret;
368214e8464SAnthony Zhou }
369214e8464SAnthony Zhou
tegra_soc_pwr_domain_on_finish(const psci_power_state_t * target_state)370214e8464SAnthony Zhou int32_t tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state)
37150402b17SVarun Wadekar {
372214e8464SAnthony Zhou uint8_t stateid_afflvl2 = target_state->pwr_domain_state[PLAT_MAX_PWR_LVL];
373214e8464SAnthony Zhou uint8_t stateid_afflvl0 = target_state->pwr_domain_state[MPIDR_AFFLVL0];
374f3a20c32SVarun Wadekar mce_cstate_info_t cstate_info = { 0 };
375b495791bSHarvey Hsieh uint64_t impl, val;
376b495791bSHarvey Hsieh const plat_params_from_bl2_t *plat_params = bl31_get_plat_params();
377b495791bSHarvey Hsieh
3789e7a2436SAnthony Zhou impl = (read_midr() >> MIDR_IMPL_SHIFT) & MIDR_IMPL_MASK;
379b495791bSHarvey Hsieh
380b495791bSHarvey Hsieh /*
381b495791bSHarvey Hsieh * Enable ECC and Parity Protection for Cortex-A57 CPUs (Tegra186
382b495791bSHarvey Hsieh * A02p and beyond).
383b495791bSHarvey Hsieh */
3849e7a2436SAnthony Zhou if ((plat_params->l2_ecc_parity_prot_dis != 1) && (impl != DENVER_IMPL)) {
385b495791bSHarvey Hsieh
386b495791bSHarvey Hsieh val = read_l2ctlr_el1();
387214e8464SAnthony Zhou val |= CORTEX_A57_L2_ECC_PARITY_PROTECTION_BIT;
388b495791bSHarvey Hsieh write_l2ctlr_el1(val);
389b495791bSHarvey Hsieh }
39050402b17SVarun Wadekar
39150402b17SVarun Wadekar /*
392b8de8473SVarun Wadekar * Reset power state info for CPUs when onlining, we set
393b8de8473SVarun Wadekar * deepest power when offlining a core but that may not be
394b8de8473SVarun Wadekar * requested by non-secure sw which controls idle states. It
395b8de8473SVarun Wadekar * will re-init this info from non-secure software when the
396b8de8473SVarun Wadekar * core come online.
397b46ac6dcSVarun Wadekar */
398b8de8473SVarun Wadekar if (stateid_afflvl0 == PLAT_MAX_OFF_STATE) {
399b8de8473SVarun Wadekar
400aa64c5fbSAnthony Zhou cstate_info.cluster = (uint32_t)TEGRA_ARI_CLUSTER_CC1;
401f3a20c32SVarun Wadekar cstate_info.update_wake_mask = 1;
402f3a20c32SVarun Wadekar mce_update_cstate_info(&cstate_info);
403b8de8473SVarun Wadekar }
404b46ac6dcSVarun Wadekar
405b46ac6dcSVarun Wadekar /*
40650402b17SVarun Wadekar * Check if we are exiting from deep sleep and restore SE
40750402b17SVarun Wadekar * context if we are.
40850402b17SVarun Wadekar */
409b8de8473SVarun Wadekar if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) {
410b8de8473SVarun Wadekar
41150402b17SVarun Wadekar mmio_write_32(TEGRA_SE0_BASE + SE_MUTEX_WATCHDOG_NS_LIMIT,
41250402b17SVarun Wadekar se_regs[0]);
41350402b17SVarun Wadekar mmio_write_32(TEGRA_RNG1_BASE + RNG_MUTEX_WATCHDOG_NS_LIMIT,
41450402b17SVarun Wadekar se_regs[1]);
41550402b17SVarun Wadekar mmio_write_32(TEGRA_PKA1_BASE + PKA_MUTEX_WATCHDOG_NS_LIMIT,
41650402b17SVarun Wadekar se_regs[2]);
41750402b17SVarun Wadekar
41850402b17SVarun Wadekar /* Init SMMU */
41950402b17SVarun Wadekar tegra_smmu_init();
42068c7de6fSVarun Wadekar
42168c7de6fSVarun Wadekar /*
422f3a20c32SVarun Wadekar * Reset power state info for the last core doing SC7
423f3a20c32SVarun Wadekar * entry and exit, we set deepest power state as CC7
424f3a20c32SVarun Wadekar * and SC7 for SC7 entry which may not be requested by
425f3a20c32SVarun Wadekar * non-secure SW which controls idle states.
42668c7de6fSVarun Wadekar */
427aa64c5fbSAnthony Zhou cstate_info.cluster = (uint32_t)TEGRA_ARI_CLUSTER_CC7;
428aa64c5fbSAnthony Zhou cstate_info.system = (uint32_t)TEGRA_ARI_SYSTEM_SC1;
429f3a20c32SVarun Wadekar cstate_info.update_wake_mask = 1;
430f3a20c32SVarun Wadekar mce_update_cstate_info(&cstate_info);
43150402b17SVarun Wadekar }
43250402b17SVarun Wadekar
43350402b17SVarun Wadekar return PSCI_E_SUCCESS;
43450402b17SVarun Wadekar }
43550402b17SVarun Wadekar
tegra_soc_pwr_domain_off_early(const psci_power_state_t * target_state)436*96d07af4SVarun Wadekar int32_t tegra_soc_pwr_domain_off_early(const psci_power_state_t *target_state)
437*96d07af4SVarun Wadekar {
438*96d07af4SVarun Wadekar /* Do not power off the boot CPU */
439*96d07af4SVarun Wadekar if (plat_is_my_cpu_primary()) {
440*96d07af4SVarun Wadekar return PSCI_E_DENIED;
441*96d07af4SVarun Wadekar }
442*96d07af4SVarun Wadekar
443*96d07af4SVarun Wadekar return PSCI_E_SUCCESS;
444*96d07af4SVarun Wadekar }
445*96d07af4SVarun Wadekar
tegra_soc_pwr_domain_off(const psci_power_state_t * target_state)446214e8464SAnthony Zhou int32_t tegra_soc_pwr_domain_off(const psci_power_state_t *target_state)
447b47d97b3SVarun Wadekar {
448214e8464SAnthony Zhou uint64_t impl = (read_midr() >> MIDR_IMPL_SHIFT) & (uint64_t)MIDR_IMPL_MASK;
449214e8464SAnthony Zhou
450214e8464SAnthony Zhou (void)target_state;
451348619f2SVarun Wadekar
4521f586a71SVarun Wadekar /* Disable Denver's DCO operations */
453214e8464SAnthony Zhou if (impl == DENVER_IMPL) {
4541f586a71SVarun Wadekar denver_disable_dco();
455214e8464SAnthony Zhou }
4561f586a71SVarun Wadekar
457b47d97b3SVarun Wadekar /* Turn off CPU */
458aa64c5fbSAnthony Zhou (void)mce_command_handler((uint64_t)MCE_CMD_ENTER_CSTATE,
459aa64c5fbSAnthony Zhou (uint64_t)TEGRA_ARI_CORE_C7, MCE_CORE_SLEEP_TIME_INFINITE, 0U);
460f3a20c32SVarun Wadekar
461f3a20c32SVarun Wadekar return PSCI_E_SUCCESS;
462b47d97b3SVarun Wadekar }
463c7ec0892SVarun Wadekar
tegra_soc_prepare_system_off(void)464c7ec0892SVarun Wadekar __dead2 void tegra_soc_prepare_system_off(void)
465c7ec0892SVarun Wadekar {
4667eaf040aSVarun Wadekar /* power off the entire system */
467aa64c5fbSAnthony Zhou mce_enter_ccplex_state((uint32_t)TEGRA_ARI_MISC_CCPLEX_SHUTDOWN_POWER_OFF);
4687eaf040aSVarun Wadekar
4697eaf040aSVarun Wadekar wfi();
4707eaf040aSVarun Wadekar
4717eaf040aSVarun Wadekar /* wait for the system to power down */
4727eaf040aSVarun Wadekar for (;;) {
4737eaf040aSVarun Wadekar ;
4747eaf040aSVarun Wadekar }
475c7ec0892SVarun Wadekar }
476b6ea86b1SVarun Wadekar
tegra_soc_prepare_system_reset(void)477214e8464SAnthony Zhou int32_t tegra_soc_prepare_system_reset(void)
478b6ea86b1SVarun Wadekar {
479aa64c5fbSAnthony Zhou mce_enter_ccplex_state((uint32_t)TEGRA_ARI_MISC_CCPLEX_SHUTDOWN_REBOOT);
480b6ea86b1SVarun Wadekar
481b6ea86b1SVarun Wadekar return PSCI_E_SUCCESS;
482b6ea86b1SVarun Wadekar }
483