1 /*
2 * Copyright (c) 2024-2026, STMicroelectronics - All Rights Reserved
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <assert.h>
8 #include <errno.h>
9
10 #include <arch_helpers.h>
11 #include <common/debug.h>
12 #include <drivers/arm/gic_common.h>
13 #include <drivers/arm/gicv2.h>
14 #include <drivers/st/stm32mp_clkfunc.h>
15 #include <drivers/st/stm32mp_reset.h>
16 #include <lib/mmio.h>
17 #include <lib/psci/psci.h>
18 #include <plat/common/platform.h>
19
20 #include <platform_def.h>
21 #include <stm32mp2_private.h>
22
23 static uintptr_t stm32_sec_entrypoint;
24
25 /*******************************************************************************
26 * STM32MP2 handler called when a CPU is about to enter standby.
27 * Called by core 1 to enter in wfi.
28 ******************************************************************************/
stm32_cpu_standby(plat_local_state_t cpu_state)29 static void stm32_cpu_standby(plat_local_state_t cpu_state)
30 {
31 uint32_t interrupt = GIC_SPURIOUS_INTERRUPT;
32
33 assert(cpu_state == STM32MP_LOCAL_STATE_RET);
34
35 /*
36 * Enter standby state.
37 * Synchronize on memory accesses and instruction flow before the WFI
38 * instruction.
39 */
40 dsb();
41 isb();
42 while (interrupt == GIC_SPURIOUS_INTERRUPT) {
43 wfi();
44
45 /* Acknowledge IT */
46 interrupt = gicv2_acknowledge_interrupt();
47 /* If Interrupt == 1022 it will be acknowledged by non secure */
48 if ((interrupt != PENDING_G1_INTID) &&
49 (interrupt != GIC_SPURIOUS_INTERRUPT)) {
50 gicv2_end_of_interrupt(interrupt);
51 }
52 }
53 }
54
55 /*******************************************************************************
56 * STM32MP2 handler called when a power domain is about to be turned on. The
57 * mpidr determines the CPU to be turned on.
58 * Called by core 0 to activate core 1.
59 ******************************************************************************/
stm32_pwr_domain_on(u_register_t mpidr)60 static int stm32_pwr_domain_on(u_register_t mpidr)
61 {
62 unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr);
63
64 if (stm32mp_is_single_core()) {
65 return PSCI_E_NOT_SUPPORTED;
66 }
67
68 if (core_id == STM32MP_PRIMARY_CPU) {
69 /* Cortex-A35 core0 can't be turned OFF, emulate it with a WFE loop */
70 VERBOSE("BL31: Releasing core0 from wait loop...\n");
71 dsb();
72 isb();
73 sev();
74 } else {
75 #if !STM32MP21
76 /* Reset the secondary core */
77 mmio_write_32(RCC_BASE + RCC_C1P1RSTCSETR, RCC_C1P1RSTCSETR_C1P1PORRST);
78 #endif /* !STM32MP21 */
79 }
80
81 return PSCI_E_SUCCESS;
82 }
83
stm32_pwr_domain_off(const psci_power_state_t * target_state)84 static void stm32_pwr_domain_off(const psci_power_state_t *target_state)
85 {
86 /* Prevent interrupts from spuriously waking up this cpu */
87 stm32mp_gic_cpuif_disable();
88
89 }
90
stm32_pwr_domain_suspend(const psci_power_state_t * target_state)91 static void stm32_pwr_domain_suspend(const psci_power_state_t *target_state)
92 {
93 /* Nothing to do, power domain is not disabled */
94 }
95
96 /*******************************************************************************
97 * STM32MP2 handler called when a power domain has just been powered on after
98 * being turned off earlier. The target_state encodes the low power state that
99 * each level has woken up from.
100 * Called by core 1 just after wake up.
101 ******************************************************************************/
stm32_pwr_domain_on_finish(const psci_power_state_t * target_state)102 static void stm32_pwr_domain_on_finish(const psci_power_state_t *target_state)
103 {
104 uintptr_t rcc_base = stm32mp_rcc_base();
105 u_register_t mpidr = read_mpidr();
106 unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr);
107
108 if (core_id == STM32MP_PRIMARY_CPU) {
109 dsb();
110 isb();
111 } else {
112 /* Restore generic timer after reset */
113 stm32mp_stgen_restore_rate();
114 #if !STM32MP21
115 /* clear flag after core 1 power on */
116 mmio_setbits_32(rcc_base + RCC_C1HWRSTSCLRR, RCC_C1HWRSTSCLRR_C1P1RSTF);
117 #endif /* !STM32MP21 */
118 }
119
120 stm32mp_gic_pcpu_init();
121 stm32mp_gic_cpuif_enable();
122
123 mmio_write_32(rcc_base + RCC_C1SREQCLRR, RCC_C1SREQCLRR_STPREQ_MASK);
124 }
125
126 /*******************************************************************************
127 * STM32MP2 handler called when a power domain has just been powered on after
128 * having been suspended earlier. The target_state encodes the low power state
129 * that each level has woken up from.
130 ******************************************************************************/
stm32_pwr_domain_suspend_finish(const psci_power_state_t * target_state)131 static void stm32_pwr_domain_suspend_finish(const psci_power_state_t
132 *target_state)
133 {
134 /* Nothing to do, power domain is not disabled */
135 }
136
stm32_pwr_domain_pwr_down_wfi(const psci_power_state_t * target_state)137 static void stm32_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state)
138 {
139 u_register_t mpidr = read_mpidr();
140 unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr);
141
142 if (core_id == STM32MP_PRIMARY_CPU) {
143 ERROR("stm32mp2 Power Down WFI: operation not handled.\n");
144 panic();
145 }
146 }
147
stm32_system_off(void)148 static void __dead2 stm32_system_off(void)
149 {
150 ERROR("stm32mp2 System Off: operation not handled.\n");
151 panic();
152 }
153
stm32_system_reset(void)154 static void __dead2 stm32_system_reset(void)
155 {
156 stm32mp_system_reset();
157 }
158
stm32_validate_power_state(unsigned int power_state,psci_power_state_t * req_state)159 static int stm32_validate_power_state(unsigned int power_state,
160 psci_power_state_t *req_state)
161 {
162 return PSCI_E_INVALID_PARAMS;
163 }
164
stm32_validate_ns_entrypoint(uintptr_t entrypoint)165 static int stm32_validate_ns_entrypoint(uintptr_t entrypoint)
166 {
167 /* The non-secure entry point must be in DDR */
168 if (entrypoint < STM32MP_DDR_BASE) {
169 return PSCI_E_INVALID_ADDRESS;
170 }
171
172 return PSCI_E_SUCCESS;
173 }
174
stm32_get_sys_suspend_power_state(psci_power_state_t * req_state)175 static void stm32_get_sys_suspend_power_state(psci_power_state_t *req_state)
176 {
177 }
178
179 /*******************************************************************************
180 * Export the platform handlers. The ARM Standard platform layer will take care
181 * of registering the handlers with PSCI.
182 ******************************************************************************/
183 static const plat_psci_ops_t stm32_psci_ops = {
184 .cpu_standby = stm32_cpu_standby,
185 .pwr_domain_on = stm32_pwr_domain_on,
186 .pwr_domain_off = stm32_pwr_domain_off,
187 .pwr_domain_suspend = stm32_pwr_domain_suspend,
188 .pwr_domain_on_finish = stm32_pwr_domain_on_finish,
189 .pwr_domain_suspend_finish = stm32_pwr_domain_suspend_finish,
190 .pwr_domain_pwr_down = stm32_pwr_domain_pwr_down_wfi,
191 .system_off = stm32_system_off,
192 .system_reset = stm32_system_reset,
193 .validate_power_state = stm32_validate_power_state,
194 .validate_ns_entrypoint = stm32_validate_ns_entrypoint,
195 .get_sys_suspend_power_state = stm32_get_sys_suspend_power_state,
196 };
197
198 /*******************************************************************************
199 * Export the platform specific power ops.
200 ******************************************************************************/
plat_setup_psci_ops(uintptr_t sec_entrypoint,const plat_psci_ops_t ** psci_ops)201 int plat_setup_psci_ops(uintptr_t sec_entrypoint,
202 const plat_psci_ops_t **psci_ops)
203 {
204 /* Program secondary CPU entry points. */
205 stm32_sec_entrypoint = sec_entrypoint;
206 stm32mp_ca35_set_vbar(stm32_sec_entrypoint);
207
208 *psci_ops = &stm32_psci_ops;
209
210 return 0;
211 }
212