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 77ee4db6eSOliver Swede #include <assert.h> 8536d906aSOliver Swede 97ee4db6eSOliver Swede #include <lib/psci/psci.h> 107ee4db6eSOliver Swede #include <plat/arm/common/plat_arm.h> 117ee4db6eSOliver Swede #include <plat/common/platform.h> 12*87762bceSOliver Swede 13*87762bceSOliver Swede #include "fpga_private.h" 147ee4db6eSOliver Swede #include <platform_def.h> 157ee4db6eSOliver Swede 167ee4db6eSOliver Swede /* 177ee4db6eSOliver Swede * This is a basic PSCI implementation that allows secondary CPUs to be 187ee4db6eSOliver Swede * released from their initial state and continue to the warm boot entrypoint. 197ee4db6eSOliver Swede * 207ee4db6eSOliver Swede * The secondary CPUs are placed in a holding pen and released by calls 217ee4db6eSOliver Swede * to fpga_pwr_domain_on(mpidr), which updates the hold entry for the CPU 227ee4db6eSOliver Swede * specified by the mpidr argument - the (polling) target CPU will then branch 237ee4db6eSOliver Swede * to the BL31 warm boot sequence at the entrypoint address. 247ee4db6eSOliver Swede * 257ee4db6eSOliver Swede * Additionally, the secondary CPUs are kept in a low-power wfe() state 267ee4db6eSOliver Swede * (placed there at the end of each poll) and woken when necessary through 277ee4db6eSOliver Swede * calls to sev() in fpga_pwr_domain_on(mpidr), once the hold state for the 287ee4db6eSOliver Swede * relevant CPU has been updated. 297ee4db6eSOliver Swede * 307ee4db6eSOliver Swede * Hotplug is currently implemented using a wfi-loop, which removes the 317ee4db6eSOliver Swede * dependencies on any power controllers or other mechanism that is specific 327ee4db6eSOliver Swede * to the running system as specified by the FPGA image. 337ee4db6eSOliver Swede */ 347ee4db6eSOliver Swede 357ee4db6eSOliver Swede uint64_t hold_base[PLATFORM_CORE_COUNT]; 367ee4db6eSOliver Swede uintptr_t fpga_sec_entrypoint; 377ee4db6eSOliver Swede 387ee4db6eSOliver Swede /* 397ee4db6eSOliver Swede * Calls to the CPU specified by the mpidr will set its hold entry to a value 407ee4db6eSOliver Swede * indicating that it should stop polling and branch off to the warm entrypoint. 417ee4db6eSOliver Swede */ 427ee4db6eSOliver Swede static int fpga_pwr_domain_on(u_register_t mpidr) 437ee4db6eSOliver Swede { 447ee4db6eSOliver Swede unsigned int pos = plat_core_pos_by_mpidr(mpidr); 457ee4db6eSOliver Swede unsigned long current_mpidr = read_mpidr_el1(); 467ee4db6eSOliver Swede 477ee4db6eSOliver Swede if (mpidr == current_mpidr) { 487ee4db6eSOliver Swede return PSCI_E_ALREADY_ON; 497ee4db6eSOliver Swede } 507ee4db6eSOliver Swede hold_base[pos] = PLAT_FPGA_HOLD_STATE_GO; 517ee4db6eSOliver Swede flush_dcache_range((uintptr_t)&hold_base[pos], sizeof(uint64_t)); 527ee4db6eSOliver Swede sev(); /* Wake any CPUs from wfe */ 537ee4db6eSOliver Swede 547ee4db6eSOliver Swede return PSCI_E_SUCCESS; 557ee4db6eSOliver Swede } 567ee4db6eSOliver Swede 57*87762bceSOliver Swede void fpga_pwr_domain_on_finish(const psci_power_state_t *target_state) 58*87762bceSOliver Swede { 59*87762bceSOliver Swede fpga_pwr_gic_on_finish(); 60*87762bceSOliver Swede } 61*87762bceSOliver Swede 627ee4db6eSOliver Swede static void fpga_pwr_domain_off(const psci_power_state_t *target_state) 637ee4db6eSOliver Swede { 64*87762bceSOliver Swede fpga_pwr_gic_off(); 65*87762bceSOliver Swede 667ee4db6eSOliver Swede while (1) { 677ee4db6eSOliver Swede wfi(); 687ee4db6eSOliver Swede } 697ee4db6eSOliver Swede } 707ee4db6eSOliver Swede 717ee4db6eSOliver Swede static void fpga_cpu_standby(plat_local_state_t cpu_state) 727ee4db6eSOliver Swede { 737ee4db6eSOliver Swede /* 747ee4db6eSOliver Swede * Enter standby state 757ee4db6eSOliver Swede * dsb is good practice before using wfi to enter low power states 767ee4db6eSOliver Swede */ 777ee4db6eSOliver Swede u_register_t scr = read_scr_el3(); 787ee4db6eSOliver Swede write_scr_el3(scr|SCR_IRQ_BIT); 797ee4db6eSOliver Swede dsb(); 807ee4db6eSOliver Swede wfi(); 817ee4db6eSOliver Swede write_scr_el3(scr); 827ee4db6eSOliver Swede } 837ee4db6eSOliver Swede 847ee4db6eSOliver Swede plat_psci_ops_t plat_fpga_psci_pm_ops = { 857ee4db6eSOliver Swede .pwr_domain_on = fpga_pwr_domain_on, 86*87762bceSOliver Swede .pwr_domain_on_finish = fpga_pwr_domain_on_finish, 877ee4db6eSOliver Swede .pwr_domain_off = fpga_pwr_domain_off, 887ee4db6eSOliver Swede .cpu_standby = fpga_cpu_standby 897ee4db6eSOliver Swede }; 90536d906aSOliver Swede 91536d906aSOliver Swede int plat_setup_psci_ops(uintptr_t sec_entrypoint, 92536d906aSOliver Swede const plat_psci_ops_t **psci_ops) 93536d906aSOliver Swede { 947ee4db6eSOliver Swede fpga_sec_entrypoint = sec_entrypoint; 957ee4db6eSOliver Swede flush_dcache_range((uint64_t)&fpga_sec_entrypoint, 967ee4db6eSOliver Swede sizeof(fpga_sec_entrypoint)); 977ee4db6eSOliver Swede *psci_ops = &plat_fpga_psci_pm_ops; 98536d906aSOliver Swede return 0; 99536d906aSOliver Swede } 100