1*2fb5ed47SGraeme Gregory /* 2*2fb5ed47SGraeme Gregory * Copyright (c) 2020, Nuvia Inc 3*2fb5ed47SGraeme Gregory * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. 4*2fb5ed47SGraeme Gregory * 5*2fb5ed47SGraeme Gregory * SPDX-License-Identifier: BSD-3-Clause 6*2fb5ed47SGraeme Gregory */ 7*2fb5ed47SGraeme Gregory 8*2fb5ed47SGraeme Gregory 9*2fb5ed47SGraeme Gregory #include <arch_helpers.h> 10*2fb5ed47SGraeme Gregory #include <assert.h> 11*2fb5ed47SGraeme Gregory #include <lib/mmio.h> 12*2fb5ed47SGraeme Gregory #include <lib/psci/psci.h> 13*2fb5ed47SGraeme Gregory #include <plat/common/platform.h> 14*2fb5ed47SGraeme Gregory 15*2fb5ed47SGraeme Gregory #include <platform_def.h> 16*2fb5ed47SGraeme Gregory #include "sbsa_private.h" 17*2fb5ed47SGraeme Gregory 18*2fb5ed47SGraeme Gregory #define ADP_STOPPED_APPLICATION_EXIT 0x20026 19*2fb5ed47SGraeme Gregory 20*2fb5ed47SGraeme Gregory /* 21*2fb5ed47SGraeme Gregory * Define offset and commands for the fake EC device 22*2fb5ed47SGraeme Gregory */ 23*2fb5ed47SGraeme Gregory #define SBSA_SECURE_EC_OFFSET 0x50000000 24*2fb5ed47SGraeme Gregory 25*2fb5ed47SGraeme Gregory #define SBSA_SECURE_EC_CMD_SHUTDOWN 0x01 26*2fb5ed47SGraeme Gregory #define SBSA_SECURE_EC_CMD_REBOOT 0x02 27*2fb5ed47SGraeme Gregory 28*2fb5ed47SGraeme Gregory /* 29*2fb5ed47SGraeme Gregory * The secure entry point to be used on warm reset. 30*2fb5ed47SGraeme Gregory */ 31*2fb5ed47SGraeme Gregory static unsigned long secure_entrypoint; 32*2fb5ed47SGraeme Gregory 33*2fb5ed47SGraeme Gregory /* Make composite power state parameter till power level 0 */ 34*2fb5ed47SGraeme Gregory #if PSCI_EXTENDED_STATE_ID 35*2fb5ed47SGraeme Gregory 36*2fb5ed47SGraeme Gregory #define qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \ 37*2fb5ed47SGraeme Gregory (((lvl0_state) << PSTATE_ID_SHIFT) | \ 38*2fb5ed47SGraeme Gregory ((type) << PSTATE_TYPE_SHIFT)) 39*2fb5ed47SGraeme Gregory #else 40*2fb5ed47SGraeme Gregory #define qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \ 41*2fb5ed47SGraeme Gregory (((lvl0_state) << PSTATE_ID_SHIFT) | \ 42*2fb5ed47SGraeme Gregory ((pwr_lvl) << PSTATE_PWR_LVL_SHIFT) | \ 43*2fb5ed47SGraeme Gregory ((type) << PSTATE_TYPE_SHIFT)) 44*2fb5ed47SGraeme Gregory #endif /* PSCI_EXTENDED_STATE_ID */ 45*2fb5ed47SGraeme Gregory 46*2fb5ed47SGraeme Gregory 47*2fb5ed47SGraeme Gregory #define qemu_make_pwrstate_lvl1(lvl1_state, lvl0_state, pwr_lvl, type) \ 48*2fb5ed47SGraeme Gregory (((lvl1_state) << PLAT_LOCAL_PSTATE_WIDTH) | \ 49*2fb5ed47SGraeme Gregory qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type)) 50*2fb5ed47SGraeme Gregory 51*2fb5ed47SGraeme Gregory 52*2fb5ed47SGraeme Gregory 53*2fb5ed47SGraeme Gregory /* 54*2fb5ed47SGraeme Gregory * The table storing the valid idle power states. Ensure that the 55*2fb5ed47SGraeme Gregory * array entries are populated in ascending order of state-id to 56*2fb5ed47SGraeme Gregory * enable us to use binary search during power state validation. 57*2fb5ed47SGraeme Gregory * The table must be terminated by a NULL entry. 58*2fb5ed47SGraeme Gregory */ 59*2fb5ed47SGraeme Gregory static const unsigned int qemu_pm_idle_states[] = { 60*2fb5ed47SGraeme Gregory /* State-id - 0x01 */ 61*2fb5ed47SGraeme Gregory qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_RUN, PLAT_LOCAL_STATE_RET, 62*2fb5ed47SGraeme Gregory MPIDR_AFFLVL0, PSTATE_TYPE_STANDBY), 63*2fb5ed47SGraeme Gregory /* State-id - 0x02 */ 64*2fb5ed47SGraeme Gregory qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_RUN, PLAT_LOCAL_STATE_OFF, 65*2fb5ed47SGraeme Gregory MPIDR_AFFLVL0, PSTATE_TYPE_POWERDOWN), 66*2fb5ed47SGraeme Gregory /* State-id - 0x22 */ 67*2fb5ed47SGraeme Gregory qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_OFF, PLAT_LOCAL_STATE_OFF, 68*2fb5ed47SGraeme Gregory MPIDR_AFFLVL1, PSTATE_TYPE_POWERDOWN), 69*2fb5ed47SGraeme Gregory 0 70*2fb5ed47SGraeme Gregory }; 71*2fb5ed47SGraeme Gregory 72*2fb5ed47SGraeme Gregory /******************************************************************************* 73*2fb5ed47SGraeme Gregory * Platform handler called to check the validity of the power state 74*2fb5ed47SGraeme Gregory * parameter. The power state parameter has to be a composite power state. 75*2fb5ed47SGraeme Gregory ******************************************************************************/ 76*2fb5ed47SGraeme Gregory static int qemu_validate_power_state(unsigned int power_state, 77*2fb5ed47SGraeme Gregory psci_power_state_t *req_state) 78*2fb5ed47SGraeme Gregory { 79*2fb5ed47SGraeme Gregory unsigned int state_id; 80*2fb5ed47SGraeme Gregory unsigned int i; 81*2fb5ed47SGraeme Gregory 82*2fb5ed47SGraeme Gregory assert(req_state != NULL); 83*2fb5ed47SGraeme Gregory 84*2fb5ed47SGraeme Gregory /* 85*2fb5ed47SGraeme Gregory * Currently we are using a linear search for finding the matching 86*2fb5ed47SGraeme Gregory * entry in the idle power state array. This can be made a binary 87*2fb5ed47SGraeme Gregory * search if the number of entries justifies the additional complexity. 88*2fb5ed47SGraeme Gregory */ 89*2fb5ed47SGraeme Gregory for (i = 0U; qemu_pm_idle_states[i] != 0U; i++) { 90*2fb5ed47SGraeme Gregory if (power_state == qemu_pm_idle_states[i]) { 91*2fb5ed47SGraeme Gregory break; 92*2fb5ed47SGraeme Gregory } 93*2fb5ed47SGraeme Gregory } 94*2fb5ed47SGraeme Gregory 95*2fb5ed47SGraeme Gregory /* Return error if entry not found in the idle state array */ 96*2fb5ed47SGraeme Gregory if (qemu_pm_idle_states[i] == 0U) { 97*2fb5ed47SGraeme Gregory return PSCI_E_INVALID_PARAMS; 98*2fb5ed47SGraeme Gregory } 99*2fb5ed47SGraeme Gregory 100*2fb5ed47SGraeme Gregory i = 0U; 101*2fb5ed47SGraeme Gregory state_id = psci_get_pstate_id(power_state); 102*2fb5ed47SGraeme Gregory 103*2fb5ed47SGraeme Gregory /* Parse the State ID and populate the state info parameter */ 104*2fb5ed47SGraeme Gregory while (state_id != 0U) { 105*2fb5ed47SGraeme Gregory req_state->pwr_domain_state[i++] = state_id & 106*2fb5ed47SGraeme Gregory PLAT_LOCAL_PSTATE_MASK; 107*2fb5ed47SGraeme Gregory state_id >>= PLAT_LOCAL_PSTATE_WIDTH; 108*2fb5ed47SGraeme Gregory } 109*2fb5ed47SGraeme Gregory 110*2fb5ed47SGraeme Gregory return PSCI_E_SUCCESS; 111*2fb5ed47SGraeme Gregory } 112*2fb5ed47SGraeme Gregory 113*2fb5ed47SGraeme Gregory /******************************************************************************* 114*2fb5ed47SGraeme Gregory * Platform handler called when a CPU is about to enter standby. 115*2fb5ed47SGraeme Gregory ******************************************************************************/ 116*2fb5ed47SGraeme Gregory static void qemu_cpu_standby(plat_local_state_t cpu_state) 117*2fb5ed47SGraeme Gregory { 118*2fb5ed47SGraeme Gregory 119*2fb5ed47SGraeme Gregory assert(cpu_state == PLAT_LOCAL_STATE_RET); 120*2fb5ed47SGraeme Gregory 121*2fb5ed47SGraeme Gregory /* 122*2fb5ed47SGraeme Gregory * Enter standby state 123*2fb5ed47SGraeme Gregory * dsb is good practice before using wfi to enter low power states 124*2fb5ed47SGraeme Gregory */ 125*2fb5ed47SGraeme Gregory dsb(); 126*2fb5ed47SGraeme Gregory wfi(); 127*2fb5ed47SGraeme Gregory } 128*2fb5ed47SGraeme Gregory 129*2fb5ed47SGraeme Gregory /******************************************************************************* 130*2fb5ed47SGraeme Gregory * Platform handler called when a power domain is about to be turned on. The 131*2fb5ed47SGraeme Gregory * mpidr determines the CPU to be turned on. 132*2fb5ed47SGraeme Gregory ******************************************************************************/ 133*2fb5ed47SGraeme Gregory static int qemu_pwr_domain_on(u_register_t mpidr) 134*2fb5ed47SGraeme Gregory { 135*2fb5ed47SGraeme Gregory int pos = plat_core_pos_by_mpidr(mpidr); 136*2fb5ed47SGraeme Gregory uint64_t *hold_base = (uint64_t *)PLAT_QEMU_HOLD_BASE; 137*2fb5ed47SGraeme Gregory 138*2fb5ed47SGraeme Gregory if (pos < 0) { 139*2fb5ed47SGraeme Gregory return PSCI_E_INVALID_PARAMS; 140*2fb5ed47SGraeme Gregory } 141*2fb5ed47SGraeme Gregory 142*2fb5ed47SGraeme Gregory hold_base[pos] = PLAT_QEMU_HOLD_STATE_GO; 143*2fb5ed47SGraeme Gregory dsb(); 144*2fb5ed47SGraeme Gregory sev(); 145*2fb5ed47SGraeme Gregory 146*2fb5ed47SGraeme Gregory return PSCI_E_SUCCESS; 147*2fb5ed47SGraeme Gregory } 148*2fb5ed47SGraeme Gregory 149*2fb5ed47SGraeme Gregory /******************************************************************************* 150*2fb5ed47SGraeme Gregory * Platform handler called when a power domain is about to be turned off. The 151*2fb5ed47SGraeme Gregory * target_state encodes the power state that each level should transition to. 152*2fb5ed47SGraeme Gregory ******************************************************************************/ 153*2fb5ed47SGraeme Gregory static void qemu_pwr_domain_off(const psci_power_state_t *target_state) 154*2fb5ed47SGraeme Gregory { 155*2fb5ed47SGraeme Gregory qemu_pwr_gic_off(); 156*2fb5ed47SGraeme Gregory } 157*2fb5ed47SGraeme Gregory 158*2fb5ed47SGraeme Gregory void __dead2 plat_secondary_cold_boot_setup(void); 159*2fb5ed47SGraeme Gregory 160*2fb5ed47SGraeme Gregory static void __dead2 161*2fb5ed47SGraeme Gregory qemu_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) 162*2fb5ed47SGraeme Gregory { 163*2fb5ed47SGraeme Gregory disable_mmu_el3(); 164*2fb5ed47SGraeme Gregory plat_secondary_cold_boot_setup(); 165*2fb5ed47SGraeme Gregory } 166*2fb5ed47SGraeme Gregory 167*2fb5ed47SGraeme Gregory /******************************************************************************* 168*2fb5ed47SGraeme Gregory * Platform handler called when a power domain is about to be suspended. The 169*2fb5ed47SGraeme Gregory * target_state encodes the power state that each level should transition to. 170*2fb5ed47SGraeme Gregory ******************************************************************************/ 171*2fb5ed47SGraeme Gregory void qemu_pwr_domain_suspend(const psci_power_state_t *target_state) 172*2fb5ed47SGraeme Gregory { 173*2fb5ed47SGraeme Gregory assert(false); 174*2fb5ed47SGraeme Gregory } 175*2fb5ed47SGraeme Gregory 176*2fb5ed47SGraeme Gregory /******************************************************************************* 177*2fb5ed47SGraeme Gregory * Platform handler called when a power domain has just been powered on after 178*2fb5ed47SGraeme Gregory * being turned off earlier. The target_state encodes the low power state that 179*2fb5ed47SGraeme Gregory * each level has woken up from. 180*2fb5ed47SGraeme Gregory ******************************************************************************/ 181*2fb5ed47SGraeme Gregory void qemu_pwr_domain_on_finish(const psci_power_state_t *target_state) 182*2fb5ed47SGraeme Gregory { 183*2fb5ed47SGraeme Gregory assert(target_state->pwr_domain_state[MPIDR_AFFLVL0] == 184*2fb5ed47SGraeme Gregory PLAT_LOCAL_STATE_OFF); 185*2fb5ed47SGraeme Gregory 186*2fb5ed47SGraeme Gregory qemu_pwr_gic_on_finish(); 187*2fb5ed47SGraeme Gregory } 188*2fb5ed47SGraeme Gregory 189*2fb5ed47SGraeme Gregory /******************************************************************************* 190*2fb5ed47SGraeme Gregory * Platform handler called when a power domain has just been powered on after 191*2fb5ed47SGraeme Gregory * having been suspended earlier. The target_state encodes the low power state 192*2fb5ed47SGraeme Gregory * that each level has woken up from. 193*2fb5ed47SGraeme Gregory ******************************************************************************/ 194*2fb5ed47SGraeme Gregory void qemu_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 195*2fb5ed47SGraeme Gregory { 196*2fb5ed47SGraeme Gregory assert(false); 197*2fb5ed47SGraeme Gregory } 198*2fb5ed47SGraeme Gregory 199*2fb5ed47SGraeme Gregory /******************************************************************************* 200*2fb5ed47SGraeme Gregory * Platform handlers to shutdown/reboot the system 201*2fb5ed47SGraeme Gregory ******************************************************************************/ 202*2fb5ed47SGraeme Gregory static void __dead2 qemu_system_off(void) 203*2fb5ed47SGraeme Gregory { 204*2fb5ed47SGraeme Gregory mmio_write_32(SBSA_SECURE_EC_OFFSET, SBSA_SECURE_EC_CMD_SHUTDOWN); 205*2fb5ed47SGraeme Gregory panic(); 206*2fb5ed47SGraeme Gregory } 207*2fb5ed47SGraeme Gregory 208*2fb5ed47SGraeme Gregory static void __dead2 qemu_system_reset(void) 209*2fb5ed47SGraeme Gregory { 210*2fb5ed47SGraeme Gregory mmio_write_32(SBSA_SECURE_EC_OFFSET, SBSA_SECURE_EC_CMD_REBOOT); 211*2fb5ed47SGraeme Gregory panic(); 212*2fb5ed47SGraeme Gregory } 213*2fb5ed47SGraeme Gregory 214*2fb5ed47SGraeme Gregory static const plat_psci_ops_t plat_qemu_psci_pm_ops = { 215*2fb5ed47SGraeme Gregory .cpu_standby = qemu_cpu_standby, 216*2fb5ed47SGraeme Gregory .pwr_domain_on = qemu_pwr_domain_on, 217*2fb5ed47SGraeme Gregory .pwr_domain_off = qemu_pwr_domain_off, 218*2fb5ed47SGraeme Gregory .pwr_domain_pwr_down_wfi = qemu_pwr_domain_pwr_down_wfi, 219*2fb5ed47SGraeme Gregory .pwr_domain_suspend = qemu_pwr_domain_suspend, 220*2fb5ed47SGraeme Gregory .pwr_domain_on_finish = qemu_pwr_domain_on_finish, 221*2fb5ed47SGraeme Gregory .pwr_domain_suspend_finish = qemu_pwr_domain_suspend_finish, 222*2fb5ed47SGraeme Gregory .system_off = qemu_system_off, 223*2fb5ed47SGraeme Gregory .system_reset = qemu_system_reset, 224*2fb5ed47SGraeme Gregory .validate_power_state = qemu_validate_power_state 225*2fb5ed47SGraeme Gregory }; 226*2fb5ed47SGraeme Gregory 227*2fb5ed47SGraeme Gregory int plat_setup_psci_ops(uintptr_t sec_entrypoint, 228*2fb5ed47SGraeme Gregory const plat_psci_ops_t **psci_ops) 229*2fb5ed47SGraeme Gregory { 230*2fb5ed47SGraeme Gregory uintptr_t *mailbox = (uintptr_t *)PLAT_QEMU_TRUSTED_MAILBOX_BASE; 231*2fb5ed47SGraeme Gregory 232*2fb5ed47SGraeme Gregory *mailbox = sec_entrypoint; 233*2fb5ed47SGraeme Gregory secure_entrypoint = (unsigned long)sec_entrypoint; 234*2fb5ed47SGraeme Gregory *psci_ops = &plat_qemu_psci_pm_ops; 235*2fb5ed47SGraeme Gregory 236*2fb5ed47SGraeme Gregory return 0; 237*2fb5ed47SGraeme Gregory } 238