1301d27d9SRadoslaw Biernacki /* 2301d27d9SRadoslaw Biernacki * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. 3301d27d9SRadoslaw Biernacki * 4301d27d9SRadoslaw Biernacki * SPDX-License-Identifier: BSD-3-Clause 5301d27d9SRadoslaw Biernacki */ 6301d27d9SRadoslaw Biernacki 7301d27d9SRadoslaw Biernacki #include <assert.h> 8301d27d9SRadoslaw Biernacki #include <platform_def.h> 9301d27d9SRadoslaw Biernacki 10301d27d9SRadoslaw Biernacki #include <arch_helpers.h> 11301d27d9SRadoslaw Biernacki #include <common/debug.h> 12301d27d9SRadoslaw Biernacki #include <lib/psci/psci.h> 1361cbd41dSAndrew Walbran #include <lib/semihosting.h> 14301d27d9SRadoslaw Biernacki #include <plat/common/platform.h> 15301d27d9SRadoslaw Biernacki 16301d27d9SRadoslaw Biernacki #include "qemu_private.h" 17301d27d9SRadoslaw Biernacki 1861cbd41dSAndrew Walbran #define ADP_STOPPED_APPLICATION_EXIT 0x20026 1961cbd41dSAndrew Walbran 20301d27d9SRadoslaw Biernacki /* 21301d27d9SRadoslaw Biernacki * The secure entry point to be used on warm reset. 22301d27d9SRadoslaw Biernacki */ 23301d27d9SRadoslaw Biernacki static unsigned long secure_entrypoint; 24301d27d9SRadoslaw Biernacki 25301d27d9SRadoslaw Biernacki /* Make composite power state parameter till power level 0 */ 26301d27d9SRadoslaw Biernacki #if PSCI_EXTENDED_STATE_ID 27301d27d9SRadoslaw Biernacki 28301d27d9SRadoslaw Biernacki #define qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \ 29301d27d9SRadoslaw Biernacki (((lvl0_state) << PSTATE_ID_SHIFT) | \ 30301d27d9SRadoslaw Biernacki ((type) << PSTATE_TYPE_SHIFT)) 31301d27d9SRadoslaw Biernacki #else 32301d27d9SRadoslaw Biernacki #define qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \ 33301d27d9SRadoslaw Biernacki (((lvl0_state) << PSTATE_ID_SHIFT) | \ 34301d27d9SRadoslaw Biernacki ((pwr_lvl) << PSTATE_PWR_LVL_SHIFT) | \ 35301d27d9SRadoslaw Biernacki ((type) << PSTATE_TYPE_SHIFT)) 36301d27d9SRadoslaw Biernacki #endif /* PSCI_EXTENDED_STATE_ID */ 37301d27d9SRadoslaw Biernacki 38301d27d9SRadoslaw Biernacki 39301d27d9SRadoslaw Biernacki #define qemu_make_pwrstate_lvl1(lvl1_state, lvl0_state, pwr_lvl, type) \ 40301d27d9SRadoslaw Biernacki (((lvl1_state) << PLAT_LOCAL_PSTATE_WIDTH) | \ 41301d27d9SRadoslaw Biernacki qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type)) 42301d27d9SRadoslaw Biernacki 43301d27d9SRadoslaw Biernacki 44301d27d9SRadoslaw Biernacki 45301d27d9SRadoslaw Biernacki /* 46301d27d9SRadoslaw Biernacki * The table storing the valid idle power states. Ensure that the 47301d27d9SRadoslaw Biernacki * array entries are populated in ascending order of state-id to 48301d27d9SRadoslaw Biernacki * enable us to use binary search during power state validation. 49301d27d9SRadoslaw Biernacki * The table must be terminated by a NULL entry. 50301d27d9SRadoslaw Biernacki */ 51301d27d9SRadoslaw Biernacki static const unsigned int qemu_pm_idle_states[] = { 52301d27d9SRadoslaw Biernacki /* State-id - 0x01 */ 53301d27d9SRadoslaw Biernacki qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_RUN, PLAT_LOCAL_STATE_RET, 54301d27d9SRadoslaw Biernacki MPIDR_AFFLVL0, PSTATE_TYPE_STANDBY), 55301d27d9SRadoslaw Biernacki /* State-id - 0x02 */ 56301d27d9SRadoslaw Biernacki qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_RUN, PLAT_LOCAL_STATE_OFF, 57301d27d9SRadoslaw Biernacki MPIDR_AFFLVL0, PSTATE_TYPE_POWERDOWN), 58301d27d9SRadoslaw Biernacki /* State-id - 0x22 */ 59301d27d9SRadoslaw Biernacki qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_OFF, PLAT_LOCAL_STATE_OFF, 60301d27d9SRadoslaw Biernacki MPIDR_AFFLVL1, PSTATE_TYPE_POWERDOWN), 61301d27d9SRadoslaw Biernacki 0, 62301d27d9SRadoslaw Biernacki }; 63301d27d9SRadoslaw Biernacki 64301d27d9SRadoslaw Biernacki /******************************************************************************* 65301d27d9SRadoslaw Biernacki * Platform handler called to check the validity of the power state 66301d27d9SRadoslaw Biernacki * parameter. The power state parameter has to be a composite power state. 67301d27d9SRadoslaw Biernacki ******************************************************************************/ 68301d27d9SRadoslaw Biernacki static int qemu_validate_power_state(unsigned int power_state, 69301d27d9SRadoslaw Biernacki psci_power_state_t *req_state) 70301d27d9SRadoslaw Biernacki { 71301d27d9SRadoslaw Biernacki unsigned int state_id; 72301d27d9SRadoslaw Biernacki int i; 73301d27d9SRadoslaw Biernacki 74301d27d9SRadoslaw Biernacki assert(req_state); 75301d27d9SRadoslaw Biernacki 76301d27d9SRadoslaw Biernacki /* 77301d27d9SRadoslaw Biernacki * Currently we are using a linear search for finding the matching 78301d27d9SRadoslaw Biernacki * entry in the idle power state array. This can be made a binary 79301d27d9SRadoslaw Biernacki * search if the number of entries justify the additional complexity. 80301d27d9SRadoslaw Biernacki */ 81301d27d9SRadoslaw Biernacki for (i = 0; !!qemu_pm_idle_states[i]; i++) { 82301d27d9SRadoslaw Biernacki if (power_state == qemu_pm_idle_states[i]) 83301d27d9SRadoslaw Biernacki break; 84301d27d9SRadoslaw Biernacki } 85301d27d9SRadoslaw Biernacki 86301d27d9SRadoslaw Biernacki /* Return error if entry not found in the idle state array */ 87301d27d9SRadoslaw Biernacki if (!qemu_pm_idle_states[i]) 88301d27d9SRadoslaw Biernacki return PSCI_E_INVALID_PARAMS; 89301d27d9SRadoslaw Biernacki 90301d27d9SRadoslaw Biernacki i = 0; 91301d27d9SRadoslaw Biernacki state_id = psci_get_pstate_id(power_state); 92301d27d9SRadoslaw Biernacki 93301d27d9SRadoslaw Biernacki /* Parse the State ID and populate the state info parameter */ 94301d27d9SRadoslaw Biernacki while (state_id) { 95301d27d9SRadoslaw Biernacki req_state->pwr_domain_state[i++] = state_id & 96301d27d9SRadoslaw Biernacki PLAT_LOCAL_PSTATE_MASK; 97301d27d9SRadoslaw Biernacki state_id >>= PLAT_LOCAL_PSTATE_WIDTH; 98301d27d9SRadoslaw Biernacki } 99301d27d9SRadoslaw Biernacki 100301d27d9SRadoslaw Biernacki return PSCI_E_SUCCESS; 101301d27d9SRadoslaw Biernacki } 102301d27d9SRadoslaw Biernacki 103301d27d9SRadoslaw Biernacki /******************************************************************************* 104301d27d9SRadoslaw Biernacki * Platform handler called to check the validity of the non secure 105301d27d9SRadoslaw Biernacki * entrypoint. 106301d27d9SRadoslaw Biernacki ******************************************************************************/ 107301d27d9SRadoslaw Biernacki static int qemu_validate_ns_entrypoint(uintptr_t entrypoint) 108301d27d9SRadoslaw Biernacki { 109301d27d9SRadoslaw Biernacki /* 110301d27d9SRadoslaw Biernacki * Check if the non secure entrypoint lies within the non 111301d27d9SRadoslaw Biernacki * secure DRAM. 112301d27d9SRadoslaw Biernacki */ 113301d27d9SRadoslaw Biernacki if ((entrypoint >= NS_DRAM0_BASE) && 114301d27d9SRadoslaw Biernacki (entrypoint < (NS_DRAM0_BASE + NS_DRAM0_SIZE))) 115301d27d9SRadoslaw Biernacki return PSCI_E_SUCCESS; 116301d27d9SRadoslaw Biernacki return PSCI_E_INVALID_ADDRESS; 117301d27d9SRadoslaw Biernacki } 118301d27d9SRadoslaw Biernacki 119301d27d9SRadoslaw Biernacki /******************************************************************************* 120301d27d9SRadoslaw Biernacki * Platform handler called when a CPU is about to enter standby. 121301d27d9SRadoslaw Biernacki ******************************************************************************/ 122301d27d9SRadoslaw Biernacki static void qemu_cpu_standby(plat_local_state_t cpu_state) 123301d27d9SRadoslaw Biernacki { 124301d27d9SRadoslaw Biernacki 125301d27d9SRadoslaw Biernacki assert(cpu_state == PLAT_LOCAL_STATE_RET); 126301d27d9SRadoslaw Biernacki 127301d27d9SRadoslaw Biernacki /* 128301d27d9SRadoslaw Biernacki * Enter standby state 129301d27d9SRadoslaw Biernacki * dsb is good practice before using wfi to enter low power states 130301d27d9SRadoslaw Biernacki */ 131301d27d9SRadoslaw Biernacki dsb(); 132301d27d9SRadoslaw Biernacki wfi(); 133301d27d9SRadoslaw Biernacki } 134301d27d9SRadoslaw Biernacki 135301d27d9SRadoslaw Biernacki /******************************************************************************* 136301d27d9SRadoslaw Biernacki * Platform handler called when a power domain is about to be turned on. The 137301d27d9SRadoslaw Biernacki * mpidr determines the CPU to be turned on. 138301d27d9SRadoslaw Biernacki ******************************************************************************/ 139301d27d9SRadoslaw Biernacki static int qemu_pwr_domain_on(u_register_t mpidr) 140301d27d9SRadoslaw Biernacki { 141301d27d9SRadoslaw Biernacki int rc = PSCI_E_SUCCESS; 142301d27d9SRadoslaw Biernacki unsigned pos = plat_core_pos_by_mpidr(mpidr); 143301d27d9SRadoslaw Biernacki uint64_t *hold_base = (uint64_t *)PLAT_QEMU_HOLD_BASE; 144301d27d9SRadoslaw Biernacki 145301d27d9SRadoslaw Biernacki hold_base[pos] = PLAT_QEMU_HOLD_STATE_GO; 146301d27d9SRadoslaw Biernacki sev(); 147301d27d9SRadoslaw Biernacki 148301d27d9SRadoslaw Biernacki return rc; 149301d27d9SRadoslaw Biernacki } 150301d27d9SRadoslaw Biernacki 151301d27d9SRadoslaw Biernacki /******************************************************************************* 152301d27d9SRadoslaw Biernacki * Platform handler called when a power domain is about to be turned off. The 153301d27d9SRadoslaw Biernacki * target_state encodes the power state that each level should transition to. 154301d27d9SRadoslaw Biernacki ******************************************************************************/ 155*33e8c569SAndrew Walbran static void qemu_pwr_domain_off(const psci_power_state_t *target_state) 156301d27d9SRadoslaw Biernacki { 157*33e8c569SAndrew Walbran qemu_pwr_gic_off(); 158*33e8c569SAndrew Walbran } 159*33e8c569SAndrew Walbran 160*33e8c569SAndrew Walbran void __dead2 plat_secondary_cold_boot_setup(void); 161*33e8c569SAndrew Walbran 162*33e8c569SAndrew Walbran static void __dead2 163*33e8c569SAndrew Walbran qemu_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) 164*33e8c569SAndrew Walbran { 165*33e8c569SAndrew Walbran disable_mmu_el3(); 166*33e8c569SAndrew Walbran plat_secondary_cold_boot_setup(); 167301d27d9SRadoslaw Biernacki } 168301d27d9SRadoslaw Biernacki 169301d27d9SRadoslaw Biernacki /******************************************************************************* 170301d27d9SRadoslaw Biernacki * Platform handler called when a power domain is about to be suspended. The 171301d27d9SRadoslaw Biernacki * target_state encodes the power state that each level should transition to. 172301d27d9SRadoslaw Biernacki ******************************************************************************/ 173301d27d9SRadoslaw Biernacki void qemu_pwr_domain_suspend(const psci_power_state_t *target_state) 174301d27d9SRadoslaw Biernacki { 175301d27d9SRadoslaw Biernacki assert(0); 176301d27d9SRadoslaw Biernacki } 177301d27d9SRadoslaw Biernacki 178301d27d9SRadoslaw Biernacki /******************************************************************************* 179301d27d9SRadoslaw Biernacki * Platform handler called when a power domain has just been powered on after 180301d27d9SRadoslaw Biernacki * being turned off earlier. The target_state encodes the low power state that 181301d27d9SRadoslaw Biernacki * each level has woken up from. 182301d27d9SRadoslaw Biernacki ******************************************************************************/ 183301d27d9SRadoslaw Biernacki void qemu_pwr_domain_on_finish(const psci_power_state_t *target_state) 184301d27d9SRadoslaw Biernacki { 185301d27d9SRadoslaw Biernacki assert(target_state->pwr_domain_state[MPIDR_AFFLVL0] == 186301d27d9SRadoslaw Biernacki PLAT_LOCAL_STATE_OFF); 187301d27d9SRadoslaw Biernacki 188301d27d9SRadoslaw Biernacki qemu_pwr_gic_on_finish(); 189301d27d9SRadoslaw Biernacki } 190301d27d9SRadoslaw Biernacki 191301d27d9SRadoslaw Biernacki /******************************************************************************* 192301d27d9SRadoslaw Biernacki * Platform handler called when a power domain has just been powered on after 193301d27d9SRadoslaw Biernacki * having been suspended earlier. The target_state encodes the low power state 194301d27d9SRadoslaw Biernacki * that each level has woken up from. 195301d27d9SRadoslaw Biernacki ******************************************************************************/ 196301d27d9SRadoslaw Biernacki void qemu_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 197301d27d9SRadoslaw Biernacki { 198301d27d9SRadoslaw Biernacki assert(0); 199301d27d9SRadoslaw Biernacki } 200301d27d9SRadoslaw Biernacki 201301d27d9SRadoslaw Biernacki /******************************************************************************* 202301d27d9SRadoslaw Biernacki * Platform handlers to shutdown/reboot the system 203301d27d9SRadoslaw Biernacki ******************************************************************************/ 204301d27d9SRadoslaw Biernacki static void __dead2 qemu_system_off(void) 205301d27d9SRadoslaw Biernacki { 20661cbd41dSAndrew Walbran semihosting_exit(ADP_STOPPED_APPLICATION_EXIT, 0); 20761cbd41dSAndrew Walbran ERROR("QEMU System Off: semihosting call unexpectedly returned.\n"); 208301d27d9SRadoslaw Biernacki panic(); 209301d27d9SRadoslaw Biernacki } 210301d27d9SRadoslaw Biernacki 211301d27d9SRadoslaw Biernacki static void __dead2 qemu_system_reset(void) 212301d27d9SRadoslaw Biernacki { 213301d27d9SRadoslaw Biernacki ERROR("QEMU System Reset: operation not handled.\n"); 214301d27d9SRadoslaw Biernacki panic(); 215301d27d9SRadoslaw Biernacki } 216301d27d9SRadoslaw Biernacki 217301d27d9SRadoslaw Biernacki static const plat_psci_ops_t plat_qemu_psci_pm_ops = { 218301d27d9SRadoslaw Biernacki .cpu_standby = qemu_cpu_standby, 219301d27d9SRadoslaw Biernacki .pwr_domain_on = qemu_pwr_domain_on, 220301d27d9SRadoslaw Biernacki .pwr_domain_off = qemu_pwr_domain_off, 221*33e8c569SAndrew Walbran .pwr_domain_pwr_down_wfi = qemu_pwr_domain_pwr_down_wfi, 222301d27d9SRadoslaw Biernacki .pwr_domain_suspend = qemu_pwr_domain_suspend, 223301d27d9SRadoslaw Biernacki .pwr_domain_on_finish = qemu_pwr_domain_on_finish, 224301d27d9SRadoslaw Biernacki .pwr_domain_suspend_finish = qemu_pwr_domain_suspend_finish, 225301d27d9SRadoslaw Biernacki .system_off = qemu_system_off, 226301d27d9SRadoslaw Biernacki .system_reset = qemu_system_reset, 227301d27d9SRadoslaw Biernacki .validate_power_state = qemu_validate_power_state, 228301d27d9SRadoslaw Biernacki .validate_ns_entrypoint = qemu_validate_ns_entrypoint 229301d27d9SRadoslaw Biernacki }; 230301d27d9SRadoslaw Biernacki 231301d27d9SRadoslaw Biernacki int plat_setup_psci_ops(uintptr_t sec_entrypoint, 232301d27d9SRadoslaw Biernacki const plat_psci_ops_t **psci_ops) 233301d27d9SRadoslaw Biernacki { 234301d27d9SRadoslaw Biernacki uintptr_t *mailbox = (void *) PLAT_QEMU_TRUSTED_MAILBOX_BASE; 235301d27d9SRadoslaw Biernacki 236301d27d9SRadoslaw Biernacki *mailbox = sec_entrypoint; 237301d27d9SRadoslaw Biernacki secure_entrypoint = (unsigned long) sec_entrypoint; 238301d27d9SRadoslaw Biernacki *psci_ops = &plat_qemu_psci_pm_ops; 239301d27d9SRadoslaw Biernacki 240301d27d9SRadoslaw Biernacki return 0; 241301d27d9SRadoslaw Biernacki } 242