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 */
fpga_pwr_domain_on(u_register_t mpidr)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
fpga_pwr_domain_on_finish(const psci_power_state_t * target_state)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
fpga_pwr_domain_off(const psci_power_state_t * target_state)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
fpga_cpu_standby(plat_local_state_t cpu_state)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
plat_setup_psci_ops(uintptr_t sec_entrypoint,const plat_psci_ops_t ** psci_ops)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