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