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 ******************************************************************************/
qemu_validate_power_state(unsigned int power_state,psci_power_state_t * req_state)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 ******************************************************************************/
qemu_cpu_standby(plat_local_state_t cpu_state)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 ******************************************************************************/
qemu_pwr_domain_on(u_register_t mpidr)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 ******************************************************************************/
qemu_pwr_domain_off(const psci_power_state_t * target_state)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
qemu_pwr_domain_pwr_down_wfi(const psci_power_state_t * target_state)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 ******************************************************************************/
qemu_pwr_domain_suspend(const psci_power_state_t * target_state)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 ******************************************************************************/
qemu_pwr_domain_on_finish(const psci_power_state_t * target_state)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 ******************************************************************************/
qemu_pwr_domain_suspend_finish(const psci_power_state_t * target_state)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 ******************************************************************************/
qemu_system_off(void)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
qemu_system_reset(void)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
plat_setup_psci_ops(uintptr_t sec_entrypoint,const plat_psci_ops_t ** psci_ops)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