1*91f16700Schasinglulu /* 2*91f16700Schasinglulu * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. 3*91f16700Schasinglulu * 4*91f16700Schasinglulu * SPDX-License-Identifier: BSD-3-Clause 5*91f16700Schasinglulu */ 6*91f16700Schasinglulu 7*91f16700Schasinglulu #include <assert.h> 8*91f16700Schasinglulu 9*91f16700Schasinglulu #include <lib/psci/psci.h> 10*91f16700Schasinglulu #include <plat/arm/common/plat_arm.h> 11*91f16700Schasinglulu #include <plat/common/platform.h> 12*91f16700Schasinglulu 13*91f16700Schasinglulu #include "fpga_private.h" 14*91f16700Schasinglulu #include <platform_def.h> 15*91f16700Schasinglulu 16*91f16700Schasinglulu /* 17*91f16700Schasinglulu * This is a basic PSCI implementation that allows secondary CPUs to be 18*91f16700Schasinglulu * released from their initial state and continue to the warm boot entrypoint. 19*91f16700Schasinglulu * 20*91f16700Schasinglulu * The secondary CPUs are placed in a holding pen and released by calls 21*91f16700Schasinglulu * to fpga_pwr_domain_on(mpidr), which updates the hold entry for the CPU 22*91f16700Schasinglulu * specified by the mpidr argument - the (polling) target CPU will then branch 23*91f16700Schasinglulu * to the BL31 warm boot sequence at the entrypoint address. 24*91f16700Schasinglulu * 25*91f16700Schasinglulu * Additionally, the secondary CPUs are kept in a low-power wfe() state 26*91f16700Schasinglulu * (placed there at the end of each poll) and woken when necessary through 27*91f16700Schasinglulu * calls to sev() in fpga_pwr_domain_on(mpidr), once the hold state for the 28*91f16700Schasinglulu * relevant CPU has been updated. 29*91f16700Schasinglulu * 30*91f16700Schasinglulu * Hotplug is currently implemented using a wfi-loop, which removes the 31*91f16700Schasinglulu * dependencies on any power controllers or other mechanism that is specific 32*91f16700Schasinglulu * to the running system as specified by the FPGA image. 33*91f16700Schasinglulu */ 34*91f16700Schasinglulu 35*91f16700Schasinglulu uint64_t hold_base[PLATFORM_CORE_COUNT]; 36*91f16700Schasinglulu uintptr_t fpga_sec_entrypoint; 37*91f16700Schasinglulu 38*91f16700Schasinglulu /* 39*91f16700Schasinglulu * Calls to the CPU specified by the mpidr will set its hold entry to a value 40*91f16700Schasinglulu * indicating that it should stop polling and branch off to the warm entrypoint. 41*91f16700Schasinglulu */ 42*91f16700Schasinglulu static int fpga_pwr_domain_on(u_register_t mpidr) 43*91f16700Schasinglulu { 44*91f16700Schasinglulu int pos = plat_core_pos_by_mpidr(mpidr); 45*91f16700Schasinglulu unsigned long current_mpidr = read_mpidr_el1(); 46*91f16700Schasinglulu 47*91f16700Schasinglulu if (pos < 0) { 48*91f16700Schasinglulu panic(); 49*91f16700Schasinglulu } 50*91f16700Schasinglulu 51*91f16700Schasinglulu if (mpidr == current_mpidr) { 52*91f16700Schasinglulu return PSCI_E_ALREADY_ON; 53*91f16700Schasinglulu } 54*91f16700Schasinglulu hold_base[pos] = PLAT_FPGA_HOLD_STATE_GO; 55*91f16700Schasinglulu flush_dcache_range((uintptr_t)&hold_base[pos], sizeof(uint64_t)); 56*91f16700Schasinglulu sev(); /* Wake any CPUs from wfe */ 57*91f16700Schasinglulu 58*91f16700Schasinglulu return PSCI_E_SUCCESS; 59*91f16700Schasinglulu } 60*91f16700Schasinglulu 61*91f16700Schasinglulu void fpga_pwr_domain_on_finish(const psci_power_state_t *target_state) 62*91f16700Schasinglulu { 63*91f16700Schasinglulu fpga_pwr_gic_on_finish(); 64*91f16700Schasinglulu } 65*91f16700Schasinglulu 66*91f16700Schasinglulu static void fpga_pwr_domain_off(const psci_power_state_t *target_state) 67*91f16700Schasinglulu { 68*91f16700Schasinglulu fpga_pwr_gic_off(); 69*91f16700Schasinglulu 70*91f16700Schasinglulu while (1) { 71*91f16700Schasinglulu wfi(); 72*91f16700Schasinglulu } 73*91f16700Schasinglulu } 74*91f16700Schasinglulu 75*91f16700Schasinglulu static void fpga_cpu_standby(plat_local_state_t cpu_state) 76*91f16700Schasinglulu { 77*91f16700Schasinglulu /* 78*91f16700Schasinglulu * Enter standby state 79*91f16700Schasinglulu * dsb is good practice before using wfi to enter low power states 80*91f16700Schasinglulu */ 81*91f16700Schasinglulu u_register_t scr = read_scr_el3(); 82*91f16700Schasinglulu write_scr_el3(scr|SCR_IRQ_BIT); 83*91f16700Schasinglulu dsb(); 84*91f16700Schasinglulu wfi(); 85*91f16700Schasinglulu write_scr_el3(scr); 86*91f16700Schasinglulu } 87*91f16700Schasinglulu 88*91f16700Schasinglulu plat_psci_ops_t plat_fpga_psci_pm_ops = { 89*91f16700Schasinglulu .pwr_domain_on = fpga_pwr_domain_on, 90*91f16700Schasinglulu .pwr_domain_on_finish = fpga_pwr_domain_on_finish, 91*91f16700Schasinglulu .pwr_domain_off = fpga_pwr_domain_off, 92*91f16700Schasinglulu .cpu_standby = fpga_cpu_standby 93*91f16700Schasinglulu }; 94*91f16700Schasinglulu 95*91f16700Schasinglulu int plat_setup_psci_ops(uintptr_t sec_entrypoint, 96*91f16700Schasinglulu const plat_psci_ops_t **psci_ops) 97*91f16700Schasinglulu { 98*91f16700Schasinglulu fpga_sec_entrypoint = sec_entrypoint; 99*91f16700Schasinglulu flush_dcache_range((uint64_t)&fpga_sec_entrypoint, 100*91f16700Schasinglulu sizeof(fpga_sec_entrypoint)); 101*91f16700Schasinglulu *psci_ops = &plat_fpga_psci_pm_ops; 102*91f16700Schasinglulu return 0; 103*91f16700Schasinglulu } 104