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