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