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