xref: /rk3399_ARM-atf/plat/st/stm32mp2/stm32mp2_pm.c (revision a8dc2595ab7e10dac8285d2c0b6da7ebbcd0edb0)
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  ******************************************************************************/
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  ******************************************************************************/
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 
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 
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  ******************************************************************************/
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  ******************************************************************************/
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 
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 
148 static void __dead2 stm32_system_off(void)
149 {
150 	ERROR("stm32mp2 System Off: operation not handled.\n");
151 	panic();
152 }
153 
154 static void __dead2 stm32_system_reset(void)
155 {
156 	stm32mp_system_reset();
157 }
158 
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 
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 
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  ******************************************************************************/
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