1cdb8c52fSCarlo Caione /* 2cdb8c52fSCarlo Caione * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. 3cdb8c52fSCarlo Caione * 4cdb8c52fSCarlo Caione * SPDX-License-Identifier: BSD-3-Clause 5cdb8c52fSCarlo Caione */ 6cdb8c52fSCarlo Caione 7cdb8c52fSCarlo Caione #include <arch_helpers.h> 8cdb8c52fSCarlo Caione #include <assert.h> 9cdb8c52fSCarlo Caione #include <common/debug.h> 10cdb8c52fSCarlo Caione #include <drivers/arm/gicv2.h> 11cdb8c52fSCarlo Caione #include <drivers/console.h> 12cdb8c52fSCarlo Caione #include <errno.h> 13cdb8c52fSCarlo Caione #include <lib/mmio.h> 14cdb8c52fSCarlo Caione #include <lib/psci/psci.h> 15cdb8c52fSCarlo Caione #include <plat/common/platform.h> 16cdb8c52fSCarlo Caione #include <platform_def.h> 17cdb8c52fSCarlo Caione 18cdb8c52fSCarlo Caione #include "aml_private.h" 19cdb8c52fSCarlo Caione 20cdb8c52fSCarlo Caione #define SCPI_POWER_ON 0 21cdb8c52fSCarlo Caione #define SCPI_POWER_RETENTION 1 22cdb8c52fSCarlo Caione #define SCPI_POWER_OFF 3 23cdb8c52fSCarlo Caione 24cdb8c52fSCarlo Caione #define SCPI_SYSTEM_SHUTDOWN 0 25cdb8c52fSCarlo Caione #define SCPI_SYSTEM_REBOOT 1 26cdb8c52fSCarlo Caione 27cdb8c52fSCarlo Caione static uintptr_t g12a_sec_entrypoint; 28cdb8c52fSCarlo Caione static volatile uint32_t g12a_cpu0_go; 29cdb8c52fSCarlo Caione 30cdb8c52fSCarlo Caione static void g12a_pm_set_reset_addr(u_register_t mpidr, uint64_t value) 31cdb8c52fSCarlo Caione { 32cdb8c52fSCarlo Caione unsigned int core = plat_calc_core_pos(mpidr); 33cdb8c52fSCarlo Caione uintptr_t cpu_mailbox_addr = AML_PSCI_MAILBOX_BASE + (core << 4); 34cdb8c52fSCarlo Caione 35cdb8c52fSCarlo Caione mmio_write_64(cpu_mailbox_addr, value); 36cdb8c52fSCarlo Caione } 37cdb8c52fSCarlo Caione 38cdb8c52fSCarlo Caione static void g12a_pm_reset(u_register_t mpidr) 39cdb8c52fSCarlo Caione { 40cdb8c52fSCarlo Caione unsigned int core = plat_calc_core_pos(mpidr); 41cdb8c52fSCarlo Caione uintptr_t cpu_mailbox_addr = AML_PSCI_MAILBOX_BASE + (core << 4) + 8; 42cdb8c52fSCarlo Caione 43cdb8c52fSCarlo Caione mmio_write_32(cpu_mailbox_addr, 0); 44cdb8c52fSCarlo Caione } 45cdb8c52fSCarlo Caione 46cdb8c52fSCarlo Caione static void __dead2 g12a_system_reset(void) 47cdb8c52fSCarlo Caione { 48cdb8c52fSCarlo Caione INFO("BL31: PSCI_SYSTEM_RESET\n"); 49cdb8c52fSCarlo Caione 50cdb8c52fSCarlo Caione u_register_t mpidr = read_mpidr_el1(); 51cdb8c52fSCarlo Caione uint32_t status = mmio_read_32(AML_AO_RTI_STATUS_REG3); 52cdb8c52fSCarlo Caione int ret; 53cdb8c52fSCarlo Caione 54cdb8c52fSCarlo Caione NOTICE("BL31: Reboot reason: 0x%x\n", status); 55cdb8c52fSCarlo Caione 56cdb8c52fSCarlo Caione status &= 0xFFFF0FF0; 57cdb8c52fSCarlo Caione 58cdb8c52fSCarlo Caione console_flush(); 59cdb8c52fSCarlo Caione 60cdb8c52fSCarlo Caione mmio_write_32(AML_AO_RTI_STATUS_REG3, status); 61cdb8c52fSCarlo Caione 62cdb8c52fSCarlo Caione ret = aml_scpi_sys_power_state(SCPI_SYSTEM_REBOOT); 63cdb8c52fSCarlo Caione 64cdb8c52fSCarlo Caione if (ret != 0) { 65cdb8c52fSCarlo Caione ERROR("BL31: PSCI_SYSTEM_RESET: SCP error: %i\n", ret); 66cdb8c52fSCarlo Caione panic(); 67cdb8c52fSCarlo Caione } 68cdb8c52fSCarlo Caione 69cdb8c52fSCarlo Caione g12a_pm_reset(mpidr); 70cdb8c52fSCarlo Caione 71cdb8c52fSCarlo Caione wfi(); 72cdb8c52fSCarlo Caione 73cdb8c52fSCarlo Caione ERROR("BL31: PSCI_SYSTEM_RESET: Operation not handled\n"); 74cdb8c52fSCarlo Caione panic(); 75cdb8c52fSCarlo Caione } 76cdb8c52fSCarlo Caione 77cdb8c52fSCarlo Caione static void __dead2 g12a_system_off(void) 78cdb8c52fSCarlo Caione { 79cdb8c52fSCarlo Caione INFO("BL31: PSCI_SYSTEM_OFF\n"); 80cdb8c52fSCarlo Caione 81cdb8c52fSCarlo Caione u_register_t mpidr = read_mpidr_el1(); 82cdb8c52fSCarlo Caione int ret; 83cdb8c52fSCarlo Caione 84cdb8c52fSCarlo Caione ret = aml_scpi_sys_power_state(SCPI_SYSTEM_SHUTDOWN); 85cdb8c52fSCarlo Caione 86cdb8c52fSCarlo Caione if (ret != 0) { 87cdb8c52fSCarlo Caione ERROR("BL31: PSCI_SYSTEM_OFF: SCP error %i\n", ret); 88cdb8c52fSCarlo Caione panic(); 89cdb8c52fSCarlo Caione } 90cdb8c52fSCarlo Caione 91cdb8c52fSCarlo Caione g12a_pm_set_reset_addr(mpidr, 0); 92cdb8c52fSCarlo Caione g12a_pm_reset(mpidr); 93cdb8c52fSCarlo Caione 94cdb8c52fSCarlo Caione wfi(); 95cdb8c52fSCarlo Caione 96cdb8c52fSCarlo Caione ERROR("BL31: PSCI_SYSTEM_OFF: Operation not handled\n"); 97cdb8c52fSCarlo Caione panic(); 98cdb8c52fSCarlo Caione } 99cdb8c52fSCarlo Caione 100cdb8c52fSCarlo Caione static int32_t g12a_pwr_domain_on(u_register_t mpidr) 101cdb8c52fSCarlo Caione { 102cdb8c52fSCarlo Caione unsigned int core = plat_calc_core_pos(mpidr); 103cdb8c52fSCarlo Caione 104cdb8c52fSCarlo Caione /* CPU0 can't be turned OFF */ 105cdb8c52fSCarlo Caione if (core == AML_PRIMARY_CPU) { 106cdb8c52fSCarlo Caione VERBOSE("BL31: Releasing CPU0 from wait loop...\n"); 107cdb8c52fSCarlo Caione 108cdb8c52fSCarlo Caione g12a_cpu0_go = 1; 109cdb8c52fSCarlo Caione flush_dcache_range((uintptr_t)&g12a_cpu0_go, 110cdb8c52fSCarlo Caione sizeof(g12a_cpu0_go)); 111cdb8c52fSCarlo Caione dsb(); 112cdb8c52fSCarlo Caione isb(); 113cdb8c52fSCarlo Caione 114cdb8c52fSCarlo Caione sev(); 115cdb8c52fSCarlo Caione 116cdb8c52fSCarlo Caione return PSCI_E_SUCCESS; 117cdb8c52fSCarlo Caione } 118cdb8c52fSCarlo Caione 119cdb8c52fSCarlo Caione g12a_pm_set_reset_addr(mpidr, g12a_sec_entrypoint); 120cdb8c52fSCarlo Caione aml_scpi_set_css_power_state(mpidr, 121cdb8c52fSCarlo Caione SCPI_POWER_ON, SCPI_POWER_ON, SCPI_POWER_ON); 122cdb8c52fSCarlo Caione dmbsy(); 123cdb8c52fSCarlo Caione sev(); 124cdb8c52fSCarlo Caione 125cdb8c52fSCarlo Caione return PSCI_E_SUCCESS; 126cdb8c52fSCarlo Caione } 127cdb8c52fSCarlo Caione 128cdb8c52fSCarlo Caione static void g12a_pwr_domain_on_finish(const psci_power_state_t *target_state) 129cdb8c52fSCarlo Caione { 130cdb8c52fSCarlo Caione unsigned int core = plat_calc_core_pos(read_mpidr_el1()); 131cdb8c52fSCarlo Caione 132cdb8c52fSCarlo Caione assert(target_state->pwr_domain_state[MPIDR_AFFLVL0] == 133cdb8c52fSCarlo Caione PLAT_LOCAL_STATE_OFF); 134cdb8c52fSCarlo Caione 135cdb8c52fSCarlo Caione if (core == AML_PRIMARY_CPU) { 136cdb8c52fSCarlo Caione g12a_cpu0_go = 0; 137cdb8c52fSCarlo Caione flush_dcache_range((uintptr_t)&g12a_cpu0_go, 138cdb8c52fSCarlo Caione sizeof(g12a_cpu0_go)); 139cdb8c52fSCarlo Caione dsb(); 140cdb8c52fSCarlo Caione isb(); 141cdb8c52fSCarlo Caione } 142cdb8c52fSCarlo Caione 143cdb8c52fSCarlo Caione gicv2_pcpu_distif_init(); 144cdb8c52fSCarlo Caione gicv2_cpuif_enable(); 145cdb8c52fSCarlo Caione } 146cdb8c52fSCarlo Caione 147cdb8c52fSCarlo Caione static void g12a_pwr_domain_off(const psci_power_state_t *target_state) 148cdb8c52fSCarlo Caione { 149cdb8c52fSCarlo Caione u_register_t mpidr = read_mpidr_el1(); 150cdb8c52fSCarlo Caione unsigned int core = plat_calc_core_pos(mpidr); 151cdb8c52fSCarlo Caione 152cdb8c52fSCarlo Caione gicv2_cpuif_disable(); 153cdb8c52fSCarlo Caione 154cdb8c52fSCarlo Caione /* CPU0 can't be turned OFF */ 155cdb8c52fSCarlo Caione if (core == AML_PRIMARY_CPU) 156cdb8c52fSCarlo Caione return; 157cdb8c52fSCarlo Caione 158cdb8c52fSCarlo Caione aml_scpi_set_css_power_state(mpidr, 159cdb8c52fSCarlo Caione SCPI_POWER_OFF, SCPI_POWER_ON, 160cdb8c52fSCarlo Caione SCPI_POWER_ON); 161cdb8c52fSCarlo Caione } 162cdb8c52fSCarlo Caione 163cdb8c52fSCarlo Caione static void __dead2 g12a_pwr_domain_pwr_down_wfi(const psci_power_state_t 164cdb8c52fSCarlo Caione *target_state) 165cdb8c52fSCarlo Caione { 166cdb8c52fSCarlo Caione u_register_t mpidr = read_mpidr_el1(); 167cdb8c52fSCarlo Caione unsigned int core = plat_calc_core_pos(mpidr); 168cdb8c52fSCarlo Caione 169cdb8c52fSCarlo Caione /* CPU0 can't be turned OFF, emulate it with a WFE loop */ 170cdb8c52fSCarlo Caione if (core == AML_PRIMARY_CPU) { 171cdb8c52fSCarlo Caione VERBOSE("BL31: CPU0 entering wait loop...\n"); 172cdb8c52fSCarlo Caione 173cdb8c52fSCarlo Caione while (g12a_cpu0_go == 0) 174cdb8c52fSCarlo Caione wfe(); 175cdb8c52fSCarlo Caione 176cdb8c52fSCarlo Caione VERBOSE("BL31: CPU0 resumed.\n"); 177cdb8c52fSCarlo Caione 178cdb8c52fSCarlo Caione /* 179cdb8c52fSCarlo Caione * Because setting CPU0's warm reset entrypoint through PSCI 180cdb8c52fSCarlo Caione * mailbox and/or mmio mapped RVBAR (0xda834650) does not seem 181cdb8c52fSCarlo Caione * to work, jump to it manually. 182cdb8c52fSCarlo Caione * In order to avoid an assert, MMU has to be disabled. 183cdb8c52fSCarlo Caione */ 184cdb8c52fSCarlo Caione disable_mmu_el3(); 185cdb8c52fSCarlo Caione ((void(*)(void))g12a_sec_entrypoint)(); 186cdb8c52fSCarlo Caione } 187cdb8c52fSCarlo Caione 188cdb8c52fSCarlo Caione dsbsy(); 189cdb8c52fSCarlo Caione g12a_pm_set_reset_addr(mpidr, 0); 190cdb8c52fSCarlo Caione g12a_pm_reset(mpidr); 191cdb8c52fSCarlo Caione 192cdb8c52fSCarlo Caione for (;;) 193cdb8c52fSCarlo Caione wfi(); 194cdb8c52fSCarlo Caione } 195cdb8c52fSCarlo Caione 196cdb8c52fSCarlo Caione /******************************************************************************* 197cdb8c52fSCarlo Caione * Platform handlers and setup function. 198cdb8c52fSCarlo Caione ******************************************************************************/ 199cdb8c52fSCarlo Caione static const plat_psci_ops_t g12a_ops = { 200cdb8c52fSCarlo Caione .pwr_domain_on = g12a_pwr_domain_on, 201cdb8c52fSCarlo Caione .pwr_domain_on_finish = g12a_pwr_domain_on_finish, 202cdb8c52fSCarlo Caione .pwr_domain_off = g12a_pwr_domain_off, 203*db5fe4f4SBoyan Karatotev .pwr_domain_pwr_down = g12a_pwr_domain_pwr_down_wfi, 204cdb8c52fSCarlo Caione .system_off = g12a_system_off, 205cdb8c52fSCarlo Caione .system_reset = g12a_system_reset 206cdb8c52fSCarlo Caione }; 207cdb8c52fSCarlo Caione 208cdb8c52fSCarlo Caione int plat_setup_psci_ops(uintptr_t sec_entrypoint, 209cdb8c52fSCarlo Caione const plat_psci_ops_t **psci_ops) 210cdb8c52fSCarlo Caione { 211cdb8c52fSCarlo Caione g12a_sec_entrypoint = sec_entrypoint; 212cdb8c52fSCarlo Caione *psci_ops = &g12a_ops; 213cdb8c52fSCarlo Caione g12a_cpu0_go = 0; 214cdb8c52fSCarlo Caione return 0; 215cdb8c52fSCarlo Caione } 216