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> 15*ffb07b04SMaxim Uvarov #include <drivers/gpio.h> 16301d27d9SRadoslaw Biernacki 17301d27d9SRadoslaw Biernacki #include "qemu_private.h" 18301d27d9SRadoslaw Biernacki 1961cbd41dSAndrew Walbran #define ADP_STOPPED_APPLICATION_EXIT 0x20026 2061cbd41dSAndrew Walbran 21301d27d9SRadoslaw Biernacki /* 22301d27d9SRadoslaw Biernacki * The secure entry point to be used on warm reset. 23301d27d9SRadoslaw Biernacki */ 24301d27d9SRadoslaw Biernacki static unsigned long secure_entrypoint; 25301d27d9SRadoslaw Biernacki 26301d27d9SRadoslaw Biernacki /* Make composite power state parameter till power level 0 */ 27301d27d9SRadoslaw Biernacki #if PSCI_EXTENDED_STATE_ID 28301d27d9SRadoslaw Biernacki 29301d27d9SRadoslaw Biernacki #define qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \ 30301d27d9SRadoslaw Biernacki (((lvl0_state) << PSTATE_ID_SHIFT) | \ 31301d27d9SRadoslaw Biernacki ((type) << PSTATE_TYPE_SHIFT)) 32301d27d9SRadoslaw Biernacki #else 33301d27d9SRadoslaw Biernacki #define qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \ 34301d27d9SRadoslaw Biernacki (((lvl0_state) << PSTATE_ID_SHIFT) | \ 35301d27d9SRadoslaw Biernacki ((pwr_lvl) << PSTATE_PWR_LVL_SHIFT) | \ 36301d27d9SRadoslaw Biernacki ((type) << PSTATE_TYPE_SHIFT)) 37301d27d9SRadoslaw Biernacki #endif /* PSCI_EXTENDED_STATE_ID */ 38301d27d9SRadoslaw Biernacki 39301d27d9SRadoslaw Biernacki 40301d27d9SRadoslaw Biernacki #define qemu_make_pwrstate_lvl1(lvl1_state, lvl0_state, pwr_lvl, type) \ 41301d27d9SRadoslaw Biernacki (((lvl1_state) << PLAT_LOCAL_PSTATE_WIDTH) | \ 42301d27d9SRadoslaw Biernacki qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type)) 43301d27d9SRadoslaw Biernacki 44301d27d9SRadoslaw Biernacki 45301d27d9SRadoslaw Biernacki 46301d27d9SRadoslaw Biernacki /* 47301d27d9SRadoslaw Biernacki * The table storing the valid idle power states. Ensure that the 48301d27d9SRadoslaw Biernacki * array entries are populated in ascending order of state-id to 49301d27d9SRadoslaw Biernacki * enable us to use binary search during power state validation. 50301d27d9SRadoslaw Biernacki * The table must be terminated by a NULL entry. 51301d27d9SRadoslaw Biernacki */ 52301d27d9SRadoslaw Biernacki static const unsigned int qemu_pm_idle_states[] = { 53301d27d9SRadoslaw Biernacki /* State-id - 0x01 */ 54301d27d9SRadoslaw Biernacki qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_RUN, PLAT_LOCAL_STATE_RET, 55301d27d9SRadoslaw Biernacki MPIDR_AFFLVL0, PSTATE_TYPE_STANDBY), 56301d27d9SRadoslaw Biernacki /* State-id - 0x02 */ 57301d27d9SRadoslaw Biernacki qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_RUN, PLAT_LOCAL_STATE_OFF, 58301d27d9SRadoslaw Biernacki MPIDR_AFFLVL0, PSTATE_TYPE_POWERDOWN), 59301d27d9SRadoslaw Biernacki /* State-id - 0x22 */ 60301d27d9SRadoslaw Biernacki qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_OFF, PLAT_LOCAL_STATE_OFF, 61301d27d9SRadoslaw Biernacki MPIDR_AFFLVL1, PSTATE_TYPE_POWERDOWN), 62301d27d9SRadoslaw Biernacki 0, 63301d27d9SRadoslaw Biernacki }; 64301d27d9SRadoslaw Biernacki 65301d27d9SRadoslaw Biernacki /******************************************************************************* 66301d27d9SRadoslaw Biernacki * Platform handler called to check the validity of the power state 67301d27d9SRadoslaw Biernacki * parameter. The power state parameter has to be a composite power state. 68301d27d9SRadoslaw Biernacki ******************************************************************************/ 69301d27d9SRadoslaw Biernacki static int qemu_validate_power_state(unsigned int power_state, 70301d27d9SRadoslaw Biernacki psci_power_state_t *req_state) 71301d27d9SRadoslaw Biernacki { 72301d27d9SRadoslaw Biernacki unsigned int state_id; 73301d27d9SRadoslaw Biernacki int i; 74301d27d9SRadoslaw Biernacki 75301d27d9SRadoslaw Biernacki assert(req_state); 76301d27d9SRadoslaw Biernacki 77301d27d9SRadoslaw Biernacki /* 78301d27d9SRadoslaw Biernacki * Currently we are using a linear search for finding the matching 79301d27d9SRadoslaw Biernacki * entry in the idle power state array. This can be made a binary 80301d27d9SRadoslaw Biernacki * search if the number of entries justify the additional complexity. 81301d27d9SRadoslaw Biernacki */ 82301d27d9SRadoslaw Biernacki for (i = 0; !!qemu_pm_idle_states[i]; i++) { 83301d27d9SRadoslaw Biernacki if (power_state == qemu_pm_idle_states[i]) 84301d27d9SRadoslaw Biernacki break; 85301d27d9SRadoslaw Biernacki } 86301d27d9SRadoslaw Biernacki 87301d27d9SRadoslaw Biernacki /* Return error if entry not found in the idle state array */ 88301d27d9SRadoslaw Biernacki if (!qemu_pm_idle_states[i]) 89301d27d9SRadoslaw Biernacki return PSCI_E_INVALID_PARAMS; 90301d27d9SRadoslaw Biernacki 91301d27d9SRadoslaw Biernacki i = 0; 92301d27d9SRadoslaw Biernacki state_id = psci_get_pstate_id(power_state); 93301d27d9SRadoslaw Biernacki 94301d27d9SRadoslaw Biernacki /* Parse the State ID and populate the state info parameter */ 95301d27d9SRadoslaw Biernacki while (state_id) { 96301d27d9SRadoslaw Biernacki req_state->pwr_domain_state[i++] = state_id & 97301d27d9SRadoslaw Biernacki PLAT_LOCAL_PSTATE_MASK; 98301d27d9SRadoslaw Biernacki state_id >>= PLAT_LOCAL_PSTATE_WIDTH; 99301d27d9SRadoslaw Biernacki } 100301d27d9SRadoslaw Biernacki 101301d27d9SRadoslaw Biernacki return PSCI_E_SUCCESS; 102301d27d9SRadoslaw Biernacki } 103301d27d9SRadoslaw Biernacki 104301d27d9SRadoslaw Biernacki /******************************************************************************* 105301d27d9SRadoslaw Biernacki * Platform handler called to check the validity of the non secure 106301d27d9SRadoslaw Biernacki * entrypoint. 107301d27d9SRadoslaw Biernacki ******************************************************************************/ 108301d27d9SRadoslaw Biernacki static int qemu_validate_ns_entrypoint(uintptr_t entrypoint) 109301d27d9SRadoslaw Biernacki { 110301d27d9SRadoslaw Biernacki /* 111301d27d9SRadoslaw Biernacki * Check if the non secure entrypoint lies within the non 112301d27d9SRadoslaw Biernacki * secure DRAM. 113301d27d9SRadoslaw Biernacki */ 114301d27d9SRadoslaw Biernacki if ((entrypoint >= NS_DRAM0_BASE) && 115301d27d9SRadoslaw Biernacki (entrypoint < (NS_DRAM0_BASE + NS_DRAM0_SIZE))) 116301d27d9SRadoslaw Biernacki return PSCI_E_SUCCESS; 117301d27d9SRadoslaw Biernacki return PSCI_E_INVALID_ADDRESS; 118301d27d9SRadoslaw Biernacki } 119301d27d9SRadoslaw Biernacki 120301d27d9SRadoslaw Biernacki /******************************************************************************* 121301d27d9SRadoslaw Biernacki * Platform handler called when a CPU is about to enter standby. 122301d27d9SRadoslaw Biernacki ******************************************************************************/ 123301d27d9SRadoslaw Biernacki static void qemu_cpu_standby(plat_local_state_t cpu_state) 124301d27d9SRadoslaw Biernacki { 125301d27d9SRadoslaw Biernacki 126301d27d9SRadoslaw Biernacki assert(cpu_state == PLAT_LOCAL_STATE_RET); 127301d27d9SRadoslaw Biernacki 128301d27d9SRadoslaw Biernacki /* 129301d27d9SRadoslaw Biernacki * Enter standby state 130301d27d9SRadoslaw Biernacki * dsb is good practice before using wfi to enter low power states 131301d27d9SRadoslaw Biernacki */ 132301d27d9SRadoslaw Biernacki dsb(); 133301d27d9SRadoslaw Biernacki wfi(); 134301d27d9SRadoslaw Biernacki } 135301d27d9SRadoslaw Biernacki 136301d27d9SRadoslaw Biernacki /******************************************************************************* 137301d27d9SRadoslaw Biernacki * Platform handler called when a power domain is about to be turned on. The 138301d27d9SRadoslaw Biernacki * mpidr determines the CPU to be turned on. 139301d27d9SRadoslaw Biernacki ******************************************************************************/ 140301d27d9SRadoslaw Biernacki static int qemu_pwr_domain_on(u_register_t mpidr) 141301d27d9SRadoslaw Biernacki { 142301d27d9SRadoslaw Biernacki int rc = PSCI_E_SUCCESS; 143301d27d9SRadoslaw Biernacki unsigned pos = plat_core_pos_by_mpidr(mpidr); 144301d27d9SRadoslaw Biernacki uint64_t *hold_base = (uint64_t *)PLAT_QEMU_HOLD_BASE; 145301d27d9SRadoslaw Biernacki 146301d27d9SRadoslaw Biernacki hold_base[pos] = PLAT_QEMU_HOLD_STATE_GO; 147301d27d9SRadoslaw Biernacki sev(); 148301d27d9SRadoslaw Biernacki 149301d27d9SRadoslaw Biernacki return rc; 150301d27d9SRadoslaw Biernacki } 151301d27d9SRadoslaw Biernacki 152301d27d9SRadoslaw Biernacki /******************************************************************************* 153301d27d9SRadoslaw Biernacki * Platform handler called when a power domain is about to be turned off. The 154301d27d9SRadoslaw Biernacki * target_state encodes the power state that each level should transition to. 155301d27d9SRadoslaw Biernacki ******************************************************************************/ 15633e8c569SAndrew Walbran static void qemu_pwr_domain_off(const psci_power_state_t *target_state) 157301d27d9SRadoslaw Biernacki { 15833e8c569SAndrew Walbran qemu_pwr_gic_off(); 15933e8c569SAndrew Walbran } 16033e8c569SAndrew Walbran 16133e8c569SAndrew Walbran void __dead2 plat_secondary_cold_boot_setup(void); 16233e8c569SAndrew Walbran 16333e8c569SAndrew Walbran static void __dead2 16433e8c569SAndrew Walbran qemu_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) 16533e8c569SAndrew Walbran { 16633e8c569SAndrew Walbran disable_mmu_el3(); 16733e8c569SAndrew Walbran plat_secondary_cold_boot_setup(); 168301d27d9SRadoslaw Biernacki } 169301d27d9SRadoslaw Biernacki 170301d27d9SRadoslaw Biernacki /******************************************************************************* 171301d27d9SRadoslaw Biernacki * Platform handler called when a power domain is about to be suspended. The 172301d27d9SRadoslaw Biernacki * target_state encodes the power state that each level should transition to. 173301d27d9SRadoslaw Biernacki ******************************************************************************/ 174301d27d9SRadoslaw Biernacki void qemu_pwr_domain_suspend(const psci_power_state_t *target_state) 175301d27d9SRadoslaw Biernacki { 176301d27d9SRadoslaw Biernacki assert(0); 177301d27d9SRadoslaw Biernacki } 178301d27d9SRadoslaw Biernacki 179301d27d9SRadoslaw Biernacki /******************************************************************************* 180301d27d9SRadoslaw Biernacki * Platform handler called when a power domain has just been powered on after 181301d27d9SRadoslaw Biernacki * being turned off earlier. The target_state encodes the low power state that 182301d27d9SRadoslaw Biernacki * each level has woken up from. 183301d27d9SRadoslaw Biernacki ******************************************************************************/ 184301d27d9SRadoslaw Biernacki void qemu_pwr_domain_on_finish(const psci_power_state_t *target_state) 185301d27d9SRadoslaw Biernacki { 186301d27d9SRadoslaw Biernacki assert(target_state->pwr_domain_state[MPIDR_AFFLVL0] == 187301d27d9SRadoslaw Biernacki PLAT_LOCAL_STATE_OFF); 188301d27d9SRadoslaw Biernacki 189301d27d9SRadoslaw Biernacki qemu_pwr_gic_on_finish(); 190301d27d9SRadoslaw Biernacki } 191301d27d9SRadoslaw Biernacki 192301d27d9SRadoslaw Biernacki /******************************************************************************* 193301d27d9SRadoslaw Biernacki * Platform handler called when a power domain has just been powered on after 194301d27d9SRadoslaw Biernacki * having been suspended earlier. The target_state encodes the low power state 195301d27d9SRadoslaw Biernacki * that each level has woken up from. 196301d27d9SRadoslaw Biernacki ******************************************************************************/ 197301d27d9SRadoslaw Biernacki void qemu_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 198301d27d9SRadoslaw Biernacki { 199301d27d9SRadoslaw Biernacki assert(0); 200301d27d9SRadoslaw Biernacki } 201301d27d9SRadoslaw Biernacki 202301d27d9SRadoslaw Biernacki /******************************************************************************* 203301d27d9SRadoslaw Biernacki * Platform handlers to shutdown/reboot the system 204301d27d9SRadoslaw Biernacki ******************************************************************************/ 205*ffb07b04SMaxim Uvarov 206301d27d9SRadoslaw Biernacki static void __dead2 qemu_system_off(void) 207301d27d9SRadoslaw Biernacki { 208*ffb07b04SMaxim Uvarov #ifdef SECURE_GPIO_BASE 209*ffb07b04SMaxim Uvarov ERROR("QEMU System Power off: with GPIO.\n"); 210*ffb07b04SMaxim Uvarov gpio_set_direction(SECURE_GPIO_POWEROFF, GPIO_DIR_OUT); 211*ffb07b04SMaxim Uvarov gpio_set_value(SECURE_GPIO_POWEROFF, GPIO_LEVEL_HIGH); 212*ffb07b04SMaxim Uvarov gpio_set_value(SECURE_GPIO_POWEROFF, GPIO_LEVEL_LOW); 213*ffb07b04SMaxim Uvarov #else 21461cbd41dSAndrew Walbran semihosting_exit(ADP_STOPPED_APPLICATION_EXIT, 0); 21561cbd41dSAndrew Walbran ERROR("QEMU System Off: semihosting call unexpectedly returned.\n"); 216*ffb07b04SMaxim Uvarov #endif 217301d27d9SRadoslaw Biernacki panic(); 218301d27d9SRadoslaw Biernacki } 219301d27d9SRadoslaw Biernacki 220301d27d9SRadoslaw Biernacki static void __dead2 qemu_system_reset(void) 221301d27d9SRadoslaw Biernacki { 222*ffb07b04SMaxim Uvarov ERROR("QEMU System Reset: with GPIO.\n"); 223*ffb07b04SMaxim Uvarov #ifdef SECURE_GPIO_BASE 224*ffb07b04SMaxim Uvarov gpio_set_direction(SECURE_GPIO_RESET, GPIO_DIR_OUT); 225*ffb07b04SMaxim Uvarov gpio_set_value(SECURE_GPIO_RESET, GPIO_LEVEL_HIGH); 226*ffb07b04SMaxim Uvarov gpio_set_value(SECURE_GPIO_RESET, GPIO_LEVEL_LOW); 227*ffb07b04SMaxim Uvarov #else 228301d27d9SRadoslaw Biernacki ERROR("QEMU System Reset: operation not handled.\n"); 229*ffb07b04SMaxim Uvarov #endif 230301d27d9SRadoslaw Biernacki panic(); 231301d27d9SRadoslaw Biernacki } 232301d27d9SRadoslaw Biernacki 233301d27d9SRadoslaw Biernacki static const plat_psci_ops_t plat_qemu_psci_pm_ops = { 234301d27d9SRadoslaw Biernacki .cpu_standby = qemu_cpu_standby, 235301d27d9SRadoslaw Biernacki .pwr_domain_on = qemu_pwr_domain_on, 236301d27d9SRadoslaw Biernacki .pwr_domain_off = qemu_pwr_domain_off, 23733e8c569SAndrew Walbran .pwr_domain_pwr_down_wfi = qemu_pwr_domain_pwr_down_wfi, 238301d27d9SRadoslaw Biernacki .pwr_domain_suspend = qemu_pwr_domain_suspend, 239301d27d9SRadoslaw Biernacki .pwr_domain_on_finish = qemu_pwr_domain_on_finish, 240301d27d9SRadoslaw Biernacki .pwr_domain_suspend_finish = qemu_pwr_domain_suspend_finish, 241301d27d9SRadoslaw Biernacki .system_off = qemu_system_off, 242301d27d9SRadoslaw Biernacki .system_reset = qemu_system_reset, 243301d27d9SRadoslaw Biernacki .validate_power_state = qemu_validate_power_state, 244301d27d9SRadoslaw Biernacki .validate_ns_entrypoint = qemu_validate_ns_entrypoint 245301d27d9SRadoslaw Biernacki }; 246301d27d9SRadoslaw Biernacki 247301d27d9SRadoslaw Biernacki int plat_setup_psci_ops(uintptr_t sec_entrypoint, 248301d27d9SRadoslaw Biernacki const plat_psci_ops_t **psci_ops) 249301d27d9SRadoslaw Biernacki { 250301d27d9SRadoslaw Biernacki uintptr_t *mailbox = (void *) PLAT_QEMU_TRUSTED_MAILBOX_BASE; 251301d27d9SRadoslaw Biernacki 252301d27d9SRadoslaw Biernacki *mailbox = sec_entrypoint; 253301d27d9SRadoslaw Biernacki secure_entrypoint = (unsigned long) sec_entrypoint; 254301d27d9SRadoslaw Biernacki *psci_ops = &plat_qemu_psci_pm_ops; 255301d27d9SRadoslaw Biernacki 256301d27d9SRadoslaw Biernacki return 0; 257301d27d9SRadoslaw Biernacki } 258