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> 1287762bceSOliver Swede 1387762bceSOliver 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 { 44*535c824eSJavier Almansa Sobrino int pos = plat_core_pos_by_mpidr(mpidr); 457ee4db6eSOliver Swede unsigned long current_mpidr = read_mpidr_el1(); 467ee4db6eSOliver Swede 47*535c824eSJavier Almansa Sobrino if (pos < 0) { 48*535c824eSJavier Almansa Sobrino panic(); 49*535c824eSJavier Almansa Sobrino } 50*535c824eSJavier Almansa Sobrino 517ee4db6eSOliver Swede if (mpidr == current_mpidr) { 527ee4db6eSOliver Swede return PSCI_E_ALREADY_ON; 537ee4db6eSOliver Swede } 547ee4db6eSOliver Swede hold_base[pos] = PLAT_FPGA_HOLD_STATE_GO; 557ee4db6eSOliver Swede flush_dcache_range((uintptr_t)&hold_base[pos], sizeof(uint64_t)); 567ee4db6eSOliver Swede sev(); /* Wake any CPUs from wfe */ 577ee4db6eSOliver Swede 587ee4db6eSOliver Swede return PSCI_E_SUCCESS; 597ee4db6eSOliver Swede } 607ee4db6eSOliver Swede 6187762bceSOliver Swede void fpga_pwr_domain_on_finish(const psci_power_state_t *target_state) 6287762bceSOliver Swede { 6387762bceSOliver Swede fpga_pwr_gic_on_finish(); 6487762bceSOliver Swede } 6587762bceSOliver Swede 667ee4db6eSOliver Swede static void fpga_pwr_domain_off(const psci_power_state_t *target_state) 677ee4db6eSOliver Swede { 6887762bceSOliver Swede fpga_pwr_gic_off(); 6987762bceSOliver Swede 707ee4db6eSOliver Swede while (1) { 717ee4db6eSOliver Swede wfi(); 727ee4db6eSOliver Swede } 737ee4db6eSOliver Swede } 747ee4db6eSOliver Swede 757ee4db6eSOliver Swede static void fpga_cpu_standby(plat_local_state_t cpu_state) 767ee4db6eSOliver Swede { 777ee4db6eSOliver Swede /* 787ee4db6eSOliver Swede * Enter standby state 797ee4db6eSOliver Swede * dsb is good practice before using wfi to enter low power states 807ee4db6eSOliver Swede */ 817ee4db6eSOliver Swede u_register_t scr = read_scr_el3(); 827ee4db6eSOliver Swede write_scr_el3(scr|SCR_IRQ_BIT); 837ee4db6eSOliver Swede dsb(); 847ee4db6eSOliver Swede wfi(); 857ee4db6eSOliver Swede write_scr_el3(scr); 867ee4db6eSOliver Swede } 877ee4db6eSOliver Swede 887ee4db6eSOliver Swede plat_psci_ops_t plat_fpga_psci_pm_ops = { 897ee4db6eSOliver Swede .pwr_domain_on = fpga_pwr_domain_on, 9087762bceSOliver Swede .pwr_domain_on_finish = fpga_pwr_domain_on_finish, 917ee4db6eSOliver Swede .pwr_domain_off = fpga_pwr_domain_off, 927ee4db6eSOliver Swede .cpu_standby = fpga_cpu_standby 937ee4db6eSOliver Swede }; 94536d906aSOliver Swede 95536d906aSOliver Swede int plat_setup_psci_ops(uintptr_t sec_entrypoint, 96536d906aSOliver Swede const plat_psci_ops_t **psci_ops) 97536d906aSOliver Swede { 987ee4db6eSOliver Swede fpga_sec_entrypoint = sec_entrypoint; 997ee4db6eSOliver Swede flush_dcache_range((uint64_t)&fpga_sec_entrypoint, 1007ee4db6eSOliver Swede sizeof(fpga_sec_entrypoint)); 1017ee4db6eSOliver Swede *psci_ops = &plat_fpga_psci_pm_ops; 102536d906aSOliver Swede return 0; 103536d906aSOliver Swede } 104