1 /* 2 * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <arch_helpers.h> 8 #include <common/debug.h> 9 #include <drivers/arm/gicv2.h> 10 #include <lib/mmio.h> 11 #include <lib/psci/psci.h> 12 #include <plat/common/platform.h> 13 14 #include "socfpga_mailbox.h" 15 #include "socfpga_plat_def.h" 16 17 18 19 /******************************************************************************* 20 * plat handler called when a CPU is about to enter standby. 21 ******************************************************************************/ 22 void socfpga_cpu_standby(plat_local_state_t cpu_state) 23 { 24 /* 25 * Enter standby state 26 * dsb is good practice before using wfi to enter low power states 27 */ 28 VERBOSE("%s: cpu_state: 0x%x\n", __func__, cpu_state); 29 dsb(); 30 wfi(); 31 } 32 33 /******************************************************************************* 34 * plat handler called when a power domain is about to be turned on. The 35 * mpidr determines the CPU to be turned on. 36 ******************************************************************************/ 37 int socfpga_pwr_domain_on(u_register_t mpidr) 38 { 39 unsigned int cpu_id = plat_core_pos_by_mpidr(mpidr); 40 41 VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr); 42 43 if (cpu_id == -1) 44 return PSCI_E_INTERN_FAIL; 45 46 mmio_write_64(PLAT_CPUID_RELEASE, cpu_id); 47 48 /* release core reset */ 49 mmio_setbits_32(SOCFPGA_RSTMGR_MPUMODRST_OFST, 1 << cpu_id); 50 return PSCI_E_SUCCESS; 51 } 52 53 /******************************************************************************* 54 * plat handler called when a power domain is about to be turned off. The 55 * target_state encodes the power state that each level should transition to. 56 ******************************************************************************/ 57 void socfpga_pwr_domain_off(const psci_power_state_t *target_state) 58 { 59 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 60 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 61 __func__, i, target_state->pwr_domain_state[i]); 62 63 /* Prevent interrupts from spuriously waking up this cpu */ 64 gicv2_cpuif_disable(); 65 } 66 67 /******************************************************************************* 68 * plat handler called when a power domain is about to be suspended. The 69 * target_state encodes the power state that each level should transition to. 70 ******************************************************************************/ 71 void socfpga_pwr_domain_suspend(const psci_power_state_t *target_state) 72 { 73 unsigned int cpu_id = plat_my_core_pos(); 74 75 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 76 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 77 __func__, i, target_state->pwr_domain_state[i]); 78 /* assert core reset */ 79 mmio_setbits_32(SOCFPGA_RSTMGR_MPUMODRST_OFST, 1 << cpu_id); 80 81 } 82 83 /******************************************************************************* 84 * plat handler called when a power domain has just been powered on after 85 * being turned off earlier. The target_state encodes the low power state that 86 * each level has woken up from. 87 ******************************************************************************/ 88 void socfpga_pwr_domain_on_finish(const psci_power_state_t *target_state) 89 { 90 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 91 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 92 __func__, i, target_state->pwr_domain_state[i]); 93 94 /* Program the gic per-cpu distributor or re-distributor interface */ 95 gicv2_pcpu_distif_init(); 96 gicv2_set_pe_target_mask(plat_my_core_pos()); 97 98 /* Enable the gic cpu interface */ 99 gicv2_cpuif_enable(); 100 } 101 102 /******************************************************************************* 103 * plat handler called when a power domain has just been powered on after 104 * having been suspended earlier. The target_state encodes the low power state 105 * that each level has woken up from. 106 * TODO: At the moment we reuse the on finisher and reinitialize the secure 107 * context. Need to implement a separate suspend finisher. 108 ******************************************************************************/ 109 void socfpga_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 110 { 111 unsigned int cpu_id = plat_my_core_pos(); 112 113 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 114 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 115 __func__, i, target_state->pwr_domain_state[i]); 116 117 /* release core reset */ 118 mmio_clrbits_32(SOCFPGA_RSTMGR_MPUMODRST_OFST, 1 << cpu_id); 119 } 120 121 /******************************************************************************* 122 * plat handlers to shutdown/reboot the system 123 ******************************************************************************/ 124 static void __dead2 socfpga_system_off(void) 125 { 126 wfi(); 127 ERROR("System Off: operation not handled.\n"); 128 panic(); 129 } 130 131 static void __dead2 socfpga_system_reset(void) 132 { 133 mailbox_reset_cold(); 134 135 while (1) 136 wfi(); 137 } 138 139 int socfpga_validate_power_state(unsigned int power_state, 140 psci_power_state_t *req_state) 141 { 142 VERBOSE("%s: power_state: 0x%x\n", __func__, power_state); 143 144 return PSCI_E_SUCCESS; 145 } 146 147 int socfpga_validate_ns_entrypoint(unsigned long ns_entrypoint) 148 { 149 VERBOSE("%s: ns_entrypoint: 0x%lx\n", __func__, ns_entrypoint); 150 return PSCI_E_SUCCESS; 151 } 152 153 void socfpga_get_sys_suspend_power_state(psci_power_state_t *req_state) 154 { 155 req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE; 156 req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE; 157 } 158 159 /******************************************************************************* 160 * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard 161 * platform layer will take care of registering the handlers with PSCI. 162 ******************************************************************************/ 163 const plat_psci_ops_t socfpga_psci_pm_ops = { 164 .cpu_standby = socfpga_cpu_standby, 165 .pwr_domain_on = socfpga_pwr_domain_on, 166 .pwr_domain_off = socfpga_pwr_domain_off, 167 .pwr_domain_suspend = socfpga_pwr_domain_suspend, 168 .pwr_domain_on_finish = socfpga_pwr_domain_on_finish, 169 .pwr_domain_suspend_finish = socfpga_pwr_domain_suspend_finish, 170 .system_off = socfpga_system_off, 171 .system_reset = socfpga_system_reset, 172 .validate_power_state = socfpga_validate_power_state, 173 .validate_ns_entrypoint = socfpga_validate_ns_entrypoint, 174 .get_sys_suspend_power_state = socfpga_get_sys_suspend_power_state 175 }; 176 177 /******************************************************************************* 178 * Export the platform specific power ops. 179 ******************************************************************************/ 180 int plat_setup_psci_ops(uintptr_t sec_entrypoint, 181 const struct plat_psci_ops **psci_ops) 182 { 183 /* Save warm boot entrypoint.*/ 184 mmio_write_64(PLAT_SEC_ENTRY, sec_entrypoint); 185 *psci_ops = &socfpga_psci_pm_ops; 186 187 return 0; 188 } 189