xref: /rk3399_ARM-atf/plat/nvidia/tegra/soc/t186/plat_psci_handlers.c (revision f50107d3df52204af137c8e6e1eb9b768ab34da9)
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