xref: /rk3399_ARM-atf/plat/qemu/qemu_sbsa/sbsa_pm.c (revision db5fe4f4934208ac8f8ae9283df2fbac6066e24e)
12fb5ed47SGraeme Gregory /*
22fb5ed47SGraeme Gregory  * Copyright (c) 2020, Nuvia Inc
32fb5ed47SGraeme Gregory  * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved.
42fb5ed47SGraeme Gregory  *
52fb5ed47SGraeme Gregory  * SPDX-License-Identifier: BSD-3-Clause
62fb5ed47SGraeme Gregory  */
72fb5ed47SGraeme Gregory 
82fb5ed47SGraeme Gregory 
92fb5ed47SGraeme Gregory #include <arch_helpers.h>
102fb5ed47SGraeme Gregory #include <assert.h>
112fb5ed47SGraeme Gregory #include <lib/mmio.h>
122fb5ed47SGraeme Gregory #include <lib/psci/psci.h>
132fb5ed47SGraeme Gregory #include <plat/common/platform.h>
142fb5ed47SGraeme Gregory 
152fb5ed47SGraeme Gregory #include <platform_def.h>
162fb5ed47SGraeme Gregory #include "sbsa_private.h"
172fb5ed47SGraeme Gregory 
182fb5ed47SGraeme Gregory #define ADP_STOPPED_APPLICATION_EXIT 0x20026
192fb5ed47SGraeme Gregory 
202fb5ed47SGraeme Gregory /*
212fb5ed47SGraeme Gregory  * Define offset and commands for the fake EC device
222fb5ed47SGraeme Gregory  */
232fb5ed47SGraeme Gregory #define SBSA_SECURE_EC_OFFSET 0x50000000
242fb5ed47SGraeme Gregory 
252fb5ed47SGraeme Gregory #define SBSA_SECURE_EC_CMD_SHUTDOWN 0x01
262fb5ed47SGraeme Gregory #define SBSA_SECURE_EC_CMD_REBOOT   0x02
272fb5ed47SGraeme Gregory 
282fb5ed47SGraeme Gregory /*
292fb5ed47SGraeme Gregory  * The secure entry point to be used on warm reset.
302fb5ed47SGraeme Gregory  */
312fb5ed47SGraeme Gregory static unsigned long secure_entrypoint;
322fb5ed47SGraeme Gregory 
332fb5ed47SGraeme Gregory /* Make composite power state parameter till power level 0 */
342fb5ed47SGraeme Gregory #if PSCI_EXTENDED_STATE_ID
352fb5ed47SGraeme Gregory 
362fb5ed47SGraeme Gregory #define qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \
372fb5ed47SGraeme Gregory 		(((lvl0_state) << PSTATE_ID_SHIFT) | \
382fb5ed47SGraeme Gregory 		 ((type) << PSTATE_TYPE_SHIFT))
392fb5ed47SGraeme Gregory #else
402fb5ed47SGraeme Gregory #define qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \
412fb5ed47SGraeme Gregory 		(((lvl0_state) << PSTATE_ID_SHIFT) | \
422fb5ed47SGraeme Gregory 		 ((pwr_lvl) << PSTATE_PWR_LVL_SHIFT) | \
432fb5ed47SGraeme Gregory 		 ((type) << PSTATE_TYPE_SHIFT))
442fb5ed47SGraeme Gregory #endif /* PSCI_EXTENDED_STATE_ID */
452fb5ed47SGraeme Gregory 
462fb5ed47SGraeme Gregory 
472fb5ed47SGraeme Gregory #define qemu_make_pwrstate_lvl1(lvl1_state, lvl0_state, pwr_lvl, type) \
482fb5ed47SGraeme Gregory 		(((lvl1_state) << PLAT_LOCAL_PSTATE_WIDTH) | \
492fb5ed47SGraeme Gregory 		 qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type))
502fb5ed47SGraeme Gregory 
512fb5ed47SGraeme Gregory 
522fb5ed47SGraeme Gregory 
532fb5ed47SGraeme Gregory /*
542fb5ed47SGraeme Gregory  *  The table storing the valid idle power states. Ensure that the
552fb5ed47SGraeme Gregory  *  array entries are populated in ascending order of state-id to
562fb5ed47SGraeme Gregory  *  enable us to use binary search during power state validation.
572fb5ed47SGraeme Gregory  *  The table must be terminated by a NULL entry.
582fb5ed47SGraeme Gregory  */
592fb5ed47SGraeme Gregory static const unsigned int qemu_pm_idle_states[] = {
602fb5ed47SGraeme Gregory 	/* State-id - 0x01 */
612fb5ed47SGraeme Gregory 	qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_RUN, PLAT_LOCAL_STATE_RET,
622fb5ed47SGraeme Gregory 				MPIDR_AFFLVL0, PSTATE_TYPE_STANDBY),
632fb5ed47SGraeme Gregory 	/* State-id - 0x02 */
642fb5ed47SGraeme Gregory 	qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_RUN, PLAT_LOCAL_STATE_OFF,
652fb5ed47SGraeme Gregory 				MPIDR_AFFLVL0, PSTATE_TYPE_POWERDOWN),
662fb5ed47SGraeme Gregory 	/* State-id - 0x22 */
672fb5ed47SGraeme Gregory 	qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_OFF, PLAT_LOCAL_STATE_OFF,
682fb5ed47SGraeme Gregory 				MPIDR_AFFLVL1, PSTATE_TYPE_POWERDOWN),
692fb5ed47SGraeme Gregory 	0
702fb5ed47SGraeme Gregory };
712fb5ed47SGraeme Gregory 
722fb5ed47SGraeme Gregory /*******************************************************************************
732fb5ed47SGraeme Gregory  * Platform handler called to check the validity of the power state
742fb5ed47SGraeme Gregory  * parameter. The power state parameter has to be a composite power state.
752fb5ed47SGraeme Gregory  ******************************************************************************/
762fb5ed47SGraeme Gregory static int qemu_validate_power_state(unsigned int power_state,
772fb5ed47SGraeme Gregory 				psci_power_state_t *req_state)
782fb5ed47SGraeme Gregory {
792fb5ed47SGraeme Gregory 	unsigned int state_id;
802fb5ed47SGraeme Gregory 	unsigned int i;
812fb5ed47SGraeme Gregory 
822fb5ed47SGraeme Gregory 	assert(req_state != NULL);
832fb5ed47SGraeme Gregory 
842fb5ed47SGraeme Gregory 	/*
852fb5ed47SGraeme Gregory 	 *  Currently we are using a linear search for finding the matching
862fb5ed47SGraeme Gregory 	 *  entry in the idle power state array. This can be made a binary
872fb5ed47SGraeme Gregory 	 *  search if the number of entries justifies the additional complexity.
882fb5ed47SGraeme Gregory 	 */
892fb5ed47SGraeme Gregory 	for (i = 0U; qemu_pm_idle_states[i] != 0U; i++) {
902fb5ed47SGraeme Gregory 		if (power_state == qemu_pm_idle_states[i]) {
912fb5ed47SGraeme Gregory 			break;
922fb5ed47SGraeme Gregory 		}
932fb5ed47SGraeme Gregory 	}
942fb5ed47SGraeme Gregory 
952fb5ed47SGraeme Gregory 	/* Return error if entry not found in the idle state array */
962fb5ed47SGraeme Gregory 	if (qemu_pm_idle_states[i] == 0U) {
972fb5ed47SGraeme Gregory 		return PSCI_E_INVALID_PARAMS;
982fb5ed47SGraeme Gregory 	}
992fb5ed47SGraeme Gregory 
1002fb5ed47SGraeme Gregory 	i = 0U;
1012fb5ed47SGraeme Gregory 	state_id = psci_get_pstate_id(power_state);
1022fb5ed47SGraeme Gregory 
1032fb5ed47SGraeme Gregory 	/* Parse the State ID and populate the state info parameter */
1042fb5ed47SGraeme Gregory 	while (state_id != 0U) {
1052fb5ed47SGraeme Gregory 		req_state->pwr_domain_state[i++] = state_id &
1062fb5ed47SGraeme Gregory 						PLAT_LOCAL_PSTATE_MASK;
1072fb5ed47SGraeme Gregory 		state_id >>= PLAT_LOCAL_PSTATE_WIDTH;
1082fb5ed47SGraeme Gregory 	}
1092fb5ed47SGraeme Gregory 
1102fb5ed47SGraeme Gregory 	return PSCI_E_SUCCESS;
1112fb5ed47SGraeme Gregory }
1122fb5ed47SGraeme Gregory 
1132fb5ed47SGraeme Gregory /*******************************************************************************
1142fb5ed47SGraeme Gregory  * Platform handler called when a CPU is about to enter standby.
1152fb5ed47SGraeme Gregory  ******************************************************************************/
1162fb5ed47SGraeme Gregory static void qemu_cpu_standby(plat_local_state_t cpu_state)
1172fb5ed47SGraeme Gregory {
1182fb5ed47SGraeme Gregory 
1192fb5ed47SGraeme Gregory 	assert(cpu_state == PLAT_LOCAL_STATE_RET);
1202fb5ed47SGraeme Gregory 
1212fb5ed47SGraeme Gregory 	/*
1222fb5ed47SGraeme Gregory 	 * Enter standby state
1232fb5ed47SGraeme Gregory 	 * dsb is good practice before using wfi to enter low power states
1242fb5ed47SGraeme Gregory 	 */
1252fb5ed47SGraeme Gregory 	dsb();
1262fb5ed47SGraeme Gregory 	wfi();
1272fb5ed47SGraeme Gregory }
1282fb5ed47SGraeme Gregory 
1292fb5ed47SGraeme Gregory /*******************************************************************************
1302fb5ed47SGraeme Gregory  * Platform handler called when a power domain is about to be turned on. The
1312fb5ed47SGraeme Gregory  * mpidr determines the CPU to be turned on.
1322fb5ed47SGraeme Gregory  ******************************************************************************/
1332fb5ed47SGraeme Gregory static int qemu_pwr_domain_on(u_register_t mpidr)
1342fb5ed47SGraeme Gregory {
1352fb5ed47SGraeme Gregory 	int pos = plat_core_pos_by_mpidr(mpidr);
1362fb5ed47SGraeme Gregory 	uint64_t *hold_base = (uint64_t *)PLAT_QEMU_HOLD_BASE;
1372fb5ed47SGraeme Gregory 
1382fb5ed47SGraeme Gregory 	if (pos < 0) {
1392fb5ed47SGraeme Gregory 		return PSCI_E_INVALID_PARAMS;
1402fb5ed47SGraeme Gregory 	}
1412fb5ed47SGraeme Gregory 
1422fb5ed47SGraeme Gregory 	hold_base[pos] = PLAT_QEMU_HOLD_STATE_GO;
1432fb5ed47SGraeme Gregory 	dsb();
1442fb5ed47SGraeme Gregory 	sev();
1452fb5ed47SGraeme Gregory 
1462fb5ed47SGraeme Gregory 	return PSCI_E_SUCCESS;
1472fb5ed47SGraeme Gregory }
1482fb5ed47SGraeme Gregory 
1492fb5ed47SGraeme Gregory /*******************************************************************************
1502fb5ed47SGraeme Gregory  * Platform handler called when a power domain is about to be turned off. The
1512fb5ed47SGraeme Gregory  * target_state encodes the power state that each level should transition to.
1522fb5ed47SGraeme Gregory  ******************************************************************************/
1532fb5ed47SGraeme Gregory static void qemu_pwr_domain_off(const psci_power_state_t *target_state)
1542fb5ed47SGraeme Gregory {
1552fb5ed47SGraeme Gregory 	qemu_pwr_gic_off();
1562fb5ed47SGraeme Gregory }
1572fb5ed47SGraeme Gregory 
1582fb5ed47SGraeme Gregory void __dead2 plat_secondary_cold_boot_setup(void);
1592fb5ed47SGraeme Gregory 
1602fb5ed47SGraeme Gregory static void __dead2
1612fb5ed47SGraeme Gregory qemu_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state)
1622fb5ed47SGraeme Gregory {
1632fb5ed47SGraeme Gregory 	disable_mmu_el3();
1642fb5ed47SGraeme Gregory 	plat_secondary_cold_boot_setup();
1652fb5ed47SGraeme Gregory }
1662fb5ed47SGraeme Gregory 
1672fb5ed47SGraeme Gregory /*******************************************************************************
1682fb5ed47SGraeme Gregory  * Platform handler called when a power domain is about to be suspended. The
1692fb5ed47SGraeme Gregory  * target_state encodes the power state that each level should transition to.
1702fb5ed47SGraeme Gregory  ******************************************************************************/
1712fb5ed47SGraeme Gregory void qemu_pwr_domain_suspend(const psci_power_state_t *target_state)
1722fb5ed47SGraeme Gregory {
1732fb5ed47SGraeme Gregory 	assert(false);
1742fb5ed47SGraeme Gregory }
1752fb5ed47SGraeme Gregory 
1762fb5ed47SGraeme Gregory /*******************************************************************************
1772fb5ed47SGraeme Gregory  * Platform handler called when a power domain has just been powered on after
1782fb5ed47SGraeme Gregory  * being turned off earlier. The target_state encodes the low power state that
1792fb5ed47SGraeme Gregory  * each level has woken up from.
1802fb5ed47SGraeme Gregory  ******************************************************************************/
1812fb5ed47SGraeme Gregory void qemu_pwr_domain_on_finish(const psci_power_state_t *target_state)
1822fb5ed47SGraeme Gregory {
1832fb5ed47SGraeme Gregory 	assert(target_state->pwr_domain_state[MPIDR_AFFLVL0] ==
1842fb5ed47SGraeme Gregory 					PLAT_LOCAL_STATE_OFF);
1852fb5ed47SGraeme Gregory 
1862fb5ed47SGraeme Gregory 	qemu_pwr_gic_on_finish();
1872fb5ed47SGraeme Gregory }
1882fb5ed47SGraeme Gregory 
1892fb5ed47SGraeme Gregory /*******************************************************************************
1902fb5ed47SGraeme Gregory  * Platform handler called when a power domain has just been powered on after
1912fb5ed47SGraeme Gregory  * having been suspended earlier. The target_state encodes the low power state
1922fb5ed47SGraeme Gregory  * that each level has woken up from.
1932fb5ed47SGraeme Gregory  ******************************************************************************/
1942fb5ed47SGraeme Gregory void qemu_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
1952fb5ed47SGraeme Gregory {
1962fb5ed47SGraeme Gregory 	assert(false);
1972fb5ed47SGraeme Gregory }
1982fb5ed47SGraeme Gregory 
1992fb5ed47SGraeme Gregory /*******************************************************************************
2002fb5ed47SGraeme Gregory  * Platform handlers to shutdown/reboot the system
2012fb5ed47SGraeme Gregory  ******************************************************************************/
2022fb5ed47SGraeme Gregory static void __dead2 qemu_system_off(void)
2032fb5ed47SGraeme Gregory {
2042fb5ed47SGraeme Gregory 	mmio_write_32(SBSA_SECURE_EC_OFFSET, SBSA_SECURE_EC_CMD_SHUTDOWN);
2052fb5ed47SGraeme Gregory 	panic();
2062fb5ed47SGraeme Gregory }
2072fb5ed47SGraeme Gregory 
2082fb5ed47SGraeme Gregory static void __dead2 qemu_system_reset(void)
2092fb5ed47SGraeme Gregory {
2102fb5ed47SGraeme Gregory 	mmio_write_32(SBSA_SECURE_EC_OFFSET, SBSA_SECURE_EC_CMD_REBOOT);
2112fb5ed47SGraeme Gregory 	panic();
2122fb5ed47SGraeme Gregory }
2132fb5ed47SGraeme Gregory 
2142fb5ed47SGraeme Gregory static const plat_psci_ops_t plat_qemu_psci_pm_ops = {
2152fb5ed47SGraeme Gregory 	.cpu_standby = qemu_cpu_standby,
2162fb5ed47SGraeme Gregory 	.pwr_domain_on = qemu_pwr_domain_on,
2172fb5ed47SGraeme Gregory 	.pwr_domain_off = qemu_pwr_domain_off,
218*db5fe4f4SBoyan Karatotev 	.pwr_domain_pwr_down = qemu_pwr_domain_pwr_down_wfi,
2192fb5ed47SGraeme Gregory 	.pwr_domain_suspend = qemu_pwr_domain_suspend,
2202fb5ed47SGraeme Gregory 	.pwr_domain_on_finish = qemu_pwr_domain_on_finish,
2212fb5ed47SGraeme Gregory 	.pwr_domain_suspend_finish = qemu_pwr_domain_suspend_finish,
2222fb5ed47SGraeme Gregory 	.system_off = qemu_system_off,
2232fb5ed47SGraeme Gregory 	.system_reset = qemu_system_reset,
2242fb5ed47SGraeme Gregory 	.validate_power_state = qemu_validate_power_state
2252fb5ed47SGraeme Gregory };
2262fb5ed47SGraeme Gregory 
2272fb5ed47SGraeme Gregory int plat_setup_psci_ops(uintptr_t sec_entrypoint,
2282fb5ed47SGraeme Gregory 			const plat_psci_ops_t **psci_ops)
2292fb5ed47SGraeme Gregory {
2302fb5ed47SGraeme Gregory 	uintptr_t *mailbox = (uintptr_t *)PLAT_QEMU_TRUSTED_MAILBOX_BASE;
2312fb5ed47SGraeme Gregory 
2322fb5ed47SGraeme Gregory 	*mailbox = sec_entrypoint;
2332fb5ed47SGraeme Gregory 	secure_entrypoint = (unsigned long)sec_entrypoint;
2342fb5ed47SGraeme Gregory 	*psci_ops = &plat_qemu_psci_pm_ops;
2352fb5ed47SGraeme Gregory 
2362fb5ed47SGraeme Gregory 	return 0;
2372fb5ed47SGraeme Gregory }
238