1*4a079c75SCarlo Caione /* 2*4a079c75SCarlo Caione * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. 3*4a079c75SCarlo Caione * 4*4a079c75SCarlo Caione * SPDX-License-Identifier: BSD-3-Clause 5*4a079c75SCarlo Caione */ 6*4a079c75SCarlo Caione 7*4a079c75SCarlo Caione #include <assert.h> 8*4a079c75SCarlo Caione #include <errno.h> 9*4a079c75SCarlo Caione 10*4a079c75SCarlo Caione #include <platform_def.h> 11*4a079c75SCarlo Caione 12*4a079c75SCarlo Caione #include <arch_helpers.h> 13*4a079c75SCarlo Caione #include <common/debug.h> 14*4a079c75SCarlo Caione #include <drivers/arm/gicv2.h> 15*4a079c75SCarlo Caione #include <drivers/console.h> 16*4a079c75SCarlo Caione #include <lib/mmio.h> 17*4a079c75SCarlo Caione #include <lib/psci/psci.h> 18*4a079c75SCarlo Caione #include <plat/common/platform.h> 19*4a079c75SCarlo Caione 20*4a079c75SCarlo Caione #include "gxbb_private.h" 21*4a079c75SCarlo Caione 22*4a079c75SCarlo Caione #define SCPI_POWER_ON 0 23*4a079c75SCarlo Caione #define SCPI_POWER_RETENTION 1 24*4a079c75SCarlo Caione #define SCPI_POWER_OFF 3 25*4a079c75SCarlo Caione 26*4a079c75SCarlo Caione #define SCPI_SYSTEM_SHUTDOWN 0 27*4a079c75SCarlo Caione #define SCPI_SYSTEM_REBOOT 1 28*4a079c75SCarlo Caione 29*4a079c75SCarlo Caione static uintptr_t gxbb_sec_entrypoint; 30*4a079c75SCarlo Caione static volatile uint32_t gxbb_cpu0_go; 31*4a079c75SCarlo Caione 32*4a079c75SCarlo Caione static void gxbb_program_mailbox(u_register_t mpidr, uint64_t value) 33*4a079c75SCarlo Caione { 34*4a079c75SCarlo Caione unsigned int core = plat_gxbb_calc_core_pos(mpidr); 35*4a079c75SCarlo Caione uintptr_t cpu_mailbox_addr = GXBB_PSCI_MAILBOX_BASE + (core << 4); 36*4a079c75SCarlo Caione 37*4a079c75SCarlo Caione mmio_write_64(cpu_mailbox_addr, value); 38*4a079c75SCarlo Caione flush_dcache_range(cpu_mailbox_addr, sizeof(uint64_t)); 39*4a079c75SCarlo Caione } 40*4a079c75SCarlo Caione 41*4a079c75SCarlo Caione static void __dead2 gxbb_system_reset(void) 42*4a079c75SCarlo Caione { 43*4a079c75SCarlo Caione INFO("BL31: PSCI_SYSTEM_RESET\n"); 44*4a079c75SCarlo Caione 45*4a079c75SCarlo Caione uint32_t status = mmio_read_32(GXBB_AO_RTI_STATUS_REG3); 46*4a079c75SCarlo Caione 47*4a079c75SCarlo Caione NOTICE("BL31: Reboot reason: 0x%x\n", status); 48*4a079c75SCarlo Caione 49*4a079c75SCarlo Caione status &= 0xFFFF0FF0; 50*4a079c75SCarlo Caione 51*4a079c75SCarlo Caione console_flush(); 52*4a079c75SCarlo Caione 53*4a079c75SCarlo Caione mmio_write_32(GXBB_AO_RTI_STATUS_REG3, status); 54*4a079c75SCarlo Caione 55*4a079c75SCarlo Caione int ret = scpi_sys_power_state(SCPI_SYSTEM_REBOOT); 56*4a079c75SCarlo Caione 57*4a079c75SCarlo Caione if (ret != 0) { 58*4a079c75SCarlo Caione ERROR("BL31: PSCI_SYSTEM_RESET: SCP error: %u\n", ret); 59*4a079c75SCarlo Caione panic(); 60*4a079c75SCarlo Caione } 61*4a079c75SCarlo Caione 62*4a079c75SCarlo Caione wfi(); 63*4a079c75SCarlo Caione 64*4a079c75SCarlo Caione ERROR("BL31: PSCI_SYSTEM_RESET: Operation not handled\n"); 65*4a079c75SCarlo Caione panic(); 66*4a079c75SCarlo Caione } 67*4a079c75SCarlo Caione 68*4a079c75SCarlo Caione static void __dead2 gxbb_system_off(void) 69*4a079c75SCarlo Caione { 70*4a079c75SCarlo Caione INFO("BL31: PSCI_SYSTEM_OFF\n"); 71*4a079c75SCarlo Caione 72*4a079c75SCarlo Caione unsigned int ret = scpi_sys_power_state(SCPI_SYSTEM_SHUTDOWN); 73*4a079c75SCarlo Caione 74*4a079c75SCarlo Caione if (ret != 0) { 75*4a079c75SCarlo Caione ERROR("BL31: PSCI_SYSTEM_OFF: SCP error %u\n", ret); 76*4a079c75SCarlo Caione panic(); 77*4a079c75SCarlo Caione } 78*4a079c75SCarlo Caione 79*4a079c75SCarlo Caione gxbb_program_mailbox(read_mpidr_el1(), 0); 80*4a079c75SCarlo Caione 81*4a079c75SCarlo Caione wfi(); 82*4a079c75SCarlo Caione 83*4a079c75SCarlo Caione ERROR("BL31: PSCI_SYSTEM_OFF: Operation not handled\n"); 84*4a079c75SCarlo Caione panic(); 85*4a079c75SCarlo Caione } 86*4a079c75SCarlo Caione 87*4a079c75SCarlo Caione static int32_t gxbb_pwr_domain_on(u_register_t mpidr) 88*4a079c75SCarlo Caione { 89*4a079c75SCarlo Caione unsigned int core = plat_gxbb_calc_core_pos(mpidr); 90*4a079c75SCarlo Caione 91*4a079c75SCarlo Caione /* CPU0 can't be turned OFF, emulate it with a WFE loop */ 92*4a079c75SCarlo Caione if (core == GXBB_PRIMARY_CPU) { 93*4a079c75SCarlo Caione VERBOSE("BL31: Releasing CPU0 from wait loop...\n"); 94*4a079c75SCarlo Caione 95*4a079c75SCarlo Caione gxbb_cpu0_go = 1; 96*4a079c75SCarlo Caione flush_dcache_range((uintptr_t)&gxbb_cpu0_go, sizeof(gxbb_cpu0_go)); 97*4a079c75SCarlo Caione dsb(); 98*4a079c75SCarlo Caione isb(); 99*4a079c75SCarlo Caione 100*4a079c75SCarlo Caione sev(); 101*4a079c75SCarlo Caione 102*4a079c75SCarlo Caione return PSCI_E_SUCCESS; 103*4a079c75SCarlo Caione } 104*4a079c75SCarlo Caione 105*4a079c75SCarlo Caione gxbb_program_mailbox(mpidr, gxbb_sec_entrypoint); 106*4a079c75SCarlo Caione scpi_set_css_power_state(mpidr, 107*4a079c75SCarlo Caione SCPI_POWER_ON, SCPI_POWER_ON, SCPI_POWER_ON); 108*4a079c75SCarlo Caione dmbsy(); 109*4a079c75SCarlo Caione sev(); 110*4a079c75SCarlo Caione 111*4a079c75SCarlo Caione return PSCI_E_SUCCESS; 112*4a079c75SCarlo Caione } 113*4a079c75SCarlo Caione 114*4a079c75SCarlo Caione static void gxbb_pwr_domain_on_finish(const psci_power_state_t *target_state) 115*4a079c75SCarlo Caione { 116*4a079c75SCarlo Caione unsigned int core = plat_gxbb_calc_core_pos(read_mpidr_el1()); 117*4a079c75SCarlo Caione 118*4a079c75SCarlo Caione assert(target_state->pwr_domain_state[MPIDR_AFFLVL0] == 119*4a079c75SCarlo Caione PLAT_LOCAL_STATE_OFF); 120*4a079c75SCarlo Caione 121*4a079c75SCarlo Caione if (core == GXBB_PRIMARY_CPU) { 122*4a079c75SCarlo Caione gxbb_cpu0_go = 0; 123*4a079c75SCarlo Caione flush_dcache_range((uintptr_t)&gxbb_cpu0_go, sizeof(gxbb_cpu0_go)); 124*4a079c75SCarlo Caione dsb(); 125*4a079c75SCarlo Caione isb(); 126*4a079c75SCarlo Caione } 127*4a079c75SCarlo Caione 128*4a079c75SCarlo Caione gicv2_pcpu_distif_init(); 129*4a079c75SCarlo Caione gicv2_cpuif_enable(); 130*4a079c75SCarlo Caione } 131*4a079c75SCarlo Caione 132*4a079c75SCarlo Caione static void gxbb_pwr_domain_off(const psci_power_state_t *target_state) 133*4a079c75SCarlo Caione { 134*4a079c75SCarlo Caione u_register_t mpidr = read_mpidr_el1(); 135*4a079c75SCarlo Caione unsigned int core = plat_gxbb_calc_core_pos(mpidr); 136*4a079c75SCarlo Caione uintptr_t addr = GXBB_PSCI_MAILBOX_BASE + 8 + (core << 4); 137*4a079c75SCarlo Caione 138*4a079c75SCarlo Caione mmio_write_32(addr, 0xFFFFFFFF); 139*4a079c75SCarlo Caione flush_dcache_range(addr, sizeof(uint32_t)); 140*4a079c75SCarlo Caione 141*4a079c75SCarlo Caione gicv2_cpuif_disable(); 142*4a079c75SCarlo Caione 143*4a079c75SCarlo Caione /* CPU0 can't be turned OFF, emulate it with a WFE loop */ 144*4a079c75SCarlo Caione if (core == GXBB_PRIMARY_CPU) 145*4a079c75SCarlo Caione return; 146*4a079c75SCarlo Caione 147*4a079c75SCarlo Caione scpi_set_css_power_state(mpidr, 148*4a079c75SCarlo Caione SCPI_POWER_OFF, SCPI_POWER_ON, SCPI_POWER_ON); 149*4a079c75SCarlo Caione } 150*4a079c75SCarlo Caione 151*4a079c75SCarlo Caione static void __dead2 gxbb_pwr_domain_pwr_down_wfi(const psci_power_state_t 152*4a079c75SCarlo Caione *target_state) 153*4a079c75SCarlo Caione { 154*4a079c75SCarlo Caione unsigned int core = plat_gxbb_calc_core_pos(read_mpidr_el1()); 155*4a079c75SCarlo Caione 156*4a079c75SCarlo Caione /* CPU0 can't be turned OFF, emulate it with a WFE loop */ 157*4a079c75SCarlo Caione if (core == GXBB_PRIMARY_CPU) { 158*4a079c75SCarlo Caione VERBOSE("BL31: CPU0 entering wait loop...\n"); 159*4a079c75SCarlo Caione 160*4a079c75SCarlo Caione while (gxbb_cpu0_go == 0) 161*4a079c75SCarlo Caione wfe(); 162*4a079c75SCarlo Caione 163*4a079c75SCarlo Caione VERBOSE("BL31: CPU0 resumed.\n"); 164*4a079c75SCarlo Caione 165*4a079c75SCarlo Caione write_rmr_el3(RMR_EL3_RR_BIT | RMR_EL3_AA64_BIT); 166*4a079c75SCarlo Caione } 167*4a079c75SCarlo Caione 168*4a079c75SCarlo Caione dsbsy(); 169*4a079c75SCarlo Caione 170*4a079c75SCarlo Caione for (;;) 171*4a079c75SCarlo Caione wfi(); 172*4a079c75SCarlo Caione } 173*4a079c75SCarlo Caione 174*4a079c75SCarlo Caione /******************************************************************************* 175*4a079c75SCarlo Caione * Platform handlers and setup function. 176*4a079c75SCarlo Caione ******************************************************************************/ 177*4a079c75SCarlo Caione static const plat_psci_ops_t gxbb_ops = { 178*4a079c75SCarlo Caione .pwr_domain_on = gxbb_pwr_domain_on, 179*4a079c75SCarlo Caione .pwr_domain_on_finish = gxbb_pwr_domain_on_finish, 180*4a079c75SCarlo Caione .pwr_domain_off = gxbb_pwr_domain_off, 181*4a079c75SCarlo Caione .pwr_domain_pwr_down_wfi = gxbb_pwr_domain_pwr_down_wfi, 182*4a079c75SCarlo Caione .system_off = gxbb_system_off, 183*4a079c75SCarlo Caione .system_reset = gxbb_system_reset, 184*4a079c75SCarlo Caione }; 185*4a079c75SCarlo Caione 186*4a079c75SCarlo Caione int plat_setup_psci_ops(uintptr_t sec_entrypoint, 187*4a079c75SCarlo Caione const plat_psci_ops_t **psci_ops) 188*4a079c75SCarlo Caione { 189*4a079c75SCarlo Caione gxbb_sec_entrypoint = sec_entrypoint; 190*4a079c75SCarlo Caione *psci_ops = &gxbb_ops; 191*4a079c75SCarlo Caione gxbb_cpu0_go = 0; 192*4a079c75SCarlo Caione return 0; 193*4a079c75SCarlo Caione } 194