fpga_pm.c 2.8 KB

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