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> 15ffb07b04SMaxim 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 when a CPU is about to enter standby. 106301d27d9SRadoslaw Biernacki ******************************************************************************/ 107301d27d9SRadoslaw Biernacki static void qemu_cpu_standby(plat_local_state_t cpu_state) 108301d27d9SRadoslaw Biernacki { 109301d27d9SRadoslaw Biernacki 110301d27d9SRadoslaw Biernacki assert(cpu_state == PLAT_LOCAL_STATE_RET); 111301d27d9SRadoslaw Biernacki 112301d27d9SRadoslaw Biernacki /* 113301d27d9SRadoslaw Biernacki * Enter standby state 114301d27d9SRadoslaw Biernacki * dsb is good practice before using wfi to enter low power states 115301d27d9SRadoslaw Biernacki */ 116301d27d9SRadoslaw Biernacki dsb(); 117301d27d9SRadoslaw Biernacki wfi(); 118301d27d9SRadoslaw Biernacki } 119301d27d9SRadoslaw Biernacki 120301d27d9SRadoslaw Biernacki /******************************************************************************* 121301d27d9SRadoslaw Biernacki * Platform handler called when a power domain is about to be turned on. The 122301d27d9SRadoslaw Biernacki * mpidr determines the CPU to be turned on. 123301d27d9SRadoslaw Biernacki ******************************************************************************/ 124301d27d9SRadoslaw Biernacki static int qemu_pwr_domain_on(u_register_t mpidr) 125301d27d9SRadoslaw Biernacki { 126301d27d9SRadoslaw Biernacki int rc = PSCI_E_SUCCESS; 127301d27d9SRadoslaw Biernacki unsigned pos = plat_core_pos_by_mpidr(mpidr); 128301d27d9SRadoslaw Biernacki uint64_t *hold_base = (uint64_t *)PLAT_QEMU_HOLD_BASE; 129301d27d9SRadoslaw Biernacki 130301d27d9SRadoslaw Biernacki hold_base[pos] = PLAT_QEMU_HOLD_STATE_GO; 131301d27d9SRadoslaw Biernacki sev(); 132301d27d9SRadoslaw Biernacki 133301d27d9SRadoslaw Biernacki return rc; 134301d27d9SRadoslaw Biernacki } 135301d27d9SRadoslaw Biernacki 136301d27d9SRadoslaw Biernacki /******************************************************************************* 137301d27d9SRadoslaw Biernacki * Platform handler called when a power domain is about to be turned off. The 138301d27d9SRadoslaw Biernacki * target_state encodes the power state that each level should transition to. 139301d27d9SRadoslaw Biernacki ******************************************************************************/ 14033e8c569SAndrew Walbran static void qemu_pwr_domain_off(const psci_power_state_t *target_state) 141301d27d9SRadoslaw Biernacki { 14233e8c569SAndrew Walbran qemu_pwr_gic_off(); 14333e8c569SAndrew Walbran } 14433e8c569SAndrew Walbran 14533e8c569SAndrew Walbran void __dead2 plat_secondary_cold_boot_setup(void); 14633e8c569SAndrew Walbran 14733e8c569SAndrew Walbran static void __dead2 14833e8c569SAndrew Walbran qemu_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) 14933e8c569SAndrew Walbran { 15033e8c569SAndrew Walbran disable_mmu_el3(); 15133e8c569SAndrew Walbran plat_secondary_cold_boot_setup(); 152301d27d9SRadoslaw Biernacki } 153301d27d9SRadoslaw Biernacki 154301d27d9SRadoslaw Biernacki /******************************************************************************* 155301d27d9SRadoslaw Biernacki * Platform handler called when a power domain is about to be suspended. The 156301d27d9SRadoslaw Biernacki * target_state encodes the power state that each level should transition to. 157301d27d9SRadoslaw Biernacki ******************************************************************************/ 158301d27d9SRadoslaw Biernacki void qemu_pwr_domain_suspend(const psci_power_state_t *target_state) 159301d27d9SRadoslaw Biernacki { 160301d27d9SRadoslaw Biernacki assert(0); 161301d27d9SRadoslaw Biernacki } 162301d27d9SRadoslaw Biernacki 163301d27d9SRadoslaw Biernacki /******************************************************************************* 164301d27d9SRadoslaw Biernacki * Platform handler called when a power domain has just been powered on after 165301d27d9SRadoslaw Biernacki * being turned off earlier. The target_state encodes the low power state that 166301d27d9SRadoslaw Biernacki * each level has woken up from. 167301d27d9SRadoslaw Biernacki ******************************************************************************/ 168301d27d9SRadoslaw Biernacki void qemu_pwr_domain_on_finish(const psci_power_state_t *target_state) 169301d27d9SRadoslaw Biernacki { 170301d27d9SRadoslaw Biernacki assert(target_state->pwr_domain_state[MPIDR_AFFLVL0] == 171301d27d9SRadoslaw Biernacki PLAT_LOCAL_STATE_OFF); 172301d27d9SRadoslaw Biernacki 173301d27d9SRadoslaw Biernacki qemu_pwr_gic_on_finish(); 174301d27d9SRadoslaw Biernacki } 175301d27d9SRadoslaw Biernacki 176301d27d9SRadoslaw Biernacki /******************************************************************************* 177301d27d9SRadoslaw Biernacki * Platform handler called when a power domain has just been powered on after 178301d27d9SRadoslaw Biernacki * having been suspended earlier. The target_state encodes the low power state 179301d27d9SRadoslaw Biernacki * that each level has woken up from. 180301d27d9SRadoslaw Biernacki ******************************************************************************/ 181301d27d9SRadoslaw Biernacki void qemu_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 182301d27d9SRadoslaw Biernacki { 183301d27d9SRadoslaw Biernacki assert(0); 184301d27d9SRadoslaw Biernacki } 185301d27d9SRadoslaw Biernacki 186301d27d9SRadoslaw Biernacki /******************************************************************************* 187301d27d9SRadoslaw Biernacki * Platform handlers to shutdown/reboot the system 188301d27d9SRadoslaw Biernacki ******************************************************************************/ 189ffb07b04SMaxim Uvarov 190301d27d9SRadoslaw Biernacki static void __dead2 qemu_system_off(void) 191301d27d9SRadoslaw Biernacki { 192ffb07b04SMaxim Uvarov #ifdef SECURE_GPIO_BASE 193ffb07b04SMaxim Uvarov ERROR("QEMU System Power off: with GPIO.\n"); 194ffb07b04SMaxim Uvarov gpio_set_direction(SECURE_GPIO_POWEROFF, GPIO_DIR_OUT); 195ffb07b04SMaxim Uvarov gpio_set_value(SECURE_GPIO_POWEROFF, GPIO_LEVEL_LOW); 196bd2ad12eSMaxim Uvarov gpio_set_value(SECURE_GPIO_POWEROFF, GPIO_LEVEL_HIGH); 197ffb07b04SMaxim Uvarov #else 19861cbd41dSAndrew Walbran semihosting_exit(ADP_STOPPED_APPLICATION_EXIT, 0); 19961cbd41dSAndrew Walbran ERROR("QEMU System Off: semihosting call unexpectedly returned.\n"); 200ffb07b04SMaxim Uvarov #endif 201301d27d9SRadoslaw Biernacki panic(); 202301d27d9SRadoslaw Biernacki } 203301d27d9SRadoslaw Biernacki 204301d27d9SRadoslaw Biernacki static void __dead2 qemu_system_reset(void) 205301d27d9SRadoslaw Biernacki { 206ffb07b04SMaxim Uvarov ERROR("QEMU System Reset: with GPIO.\n"); 207ffb07b04SMaxim Uvarov #ifdef SECURE_GPIO_BASE 208ffb07b04SMaxim Uvarov gpio_set_direction(SECURE_GPIO_RESET, GPIO_DIR_OUT); 209ffb07b04SMaxim Uvarov gpio_set_value(SECURE_GPIO_RESET, GPIO_LEVEL_LOW); 210bd2ad12eSMaxim Uvarov gpio_set_value(SECURE_GPIO_RESET, GPIO_LEVEL_HIGH); 211ffb07b04SMaxim Uvarov #else 212301d27d9SRadoslaw Biernacki ERROR("QEMU System Reset: operation not handled.\n"); 213ffb07b04SMaxim Uvarov #endif 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*db5fe4f4SBoyan Karatotev .pwr_domain_pwr_down = 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 }; 229301d27d9SRadoslaw Biernacki 230301d27d9SRadoslaw Biernacki int plat_setup_psci_ops(uintptr_t sec_entrypoint, 231301d27d9SRadoslaw Biernacki const plat_psci_ops_t **psci_ops) 232301d27d9SRadoslaw Biernacki { 233301d27d9SRadoslaw Biernacki uintptr_t *mailbox = (void *) PLAT_QEMU_TRUSTED_MAILBOX_BASE; 234301d27d9SRadoslaw Biernacki 235301d27d9SRadoslaw Biernacki *mailbox = sec_entrypoint; 236301d27d9SRadoslaw Biernacki secure_entrypoint = (unsigned long) sec_entrypoint; 237301d27d9SRadoslaw Biernacki *psci_ops = &plat_qemu_psci_pm_ops; 238301d27d9SRadoslaw Biernacki 239301d27d9SRadoslaw Biernacki return 0; 240301d27d9SRadoslaw Biernacki } 241