xref: /arm-trusted-firmware/plat/hobot/sigi/sigi_pm.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1 /*
2  * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <assert.h>
8 #include <platform_def.h>
9 #include <sigi_def.h>
10 
11 #include <arch_helpers.h>
12 #include <common/debug.h>
13 #include <lib/psci/psci.h>
14 #include <lib/el3_runtime/cpu_data.h>
15 #include <plat/common/platform.h>
16 #include <drivers/arm/gicv3.h>
17 #include <drivers/arm/gic_common.h>
18 #include <lib/el3_runtime/context_mgmt.h>
19 #include <lib/mmio.h>
20 
21 struct cpu_offset_map {
22     uint64_t offset;
23     u_register_t cpu_idx;
24 } cpu_map [] = {
25     {CPU_CL0_C0_0, 0x000},
26     {CPU_CL0_C1_0, 0x100},
27     {CPU_CL0_C2_0, 0x200},
28     {CPU_CL0_C3_0, 0x300},
29     {CPU_CL1_C0_0, 0x10000},
30     {CPU_CL1_C1_0, 0x10100},
31 };
32 
33 /*
34  * The secure entry point to be used on warm reset.
35  */
36 static unsigned long secure_entrypoint;
37 
38 /* Make composite power state parameter till power level 0 */
39 #if PSCI_EXTENDED_STATE_ID
40 
41 #define sigi_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \
42 		(((lvl0_state) << PSTATE_ID_SHIFT) | \
43 		 ((type) << PSTATE_TYPE_SHIFT))
44 #else
45 #define sigi_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \
46 		(((lvl0_state) << PSTATE_ID_SHIFT) | \
47 		 ((pwr_lvl) << PSTATE_PWR_LVL_SHIFT) | \
48 		 ((type) << PSTATE_TYPE_SHIFT))
49 #endif /* PSCI_EXTENDED_STATE_ID */
50 
51 
52 #define sigi_make_pwrstate_lvl1(lvl1_state, lvl0_state, pwr_lvl, type) \
53 		(((lvl1_state) << SIGI_LOCAL_PSTATE_WIDTH) | \
54 		 sigi_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type))
55 
56 
57 /*
58  *  The table storing the valid idle power states. Ensure that the
59  *  array entries are populated in ascending order of state-id to
60  *  enable us to use binary search during power state validation.
61  *  The table must be terminated by a NULL entry.
62  */
63 static const unsigned int sigi_pm_idle_states[] = {
64 	/* State-id - 0x01 */
65 	sigi_make_pwrstate_lvl1(SIGI_LOCAL_STATE_RUN, SIGI_LOCAL_STATE_RET,
66 				MPIDR_AFFLVL0, PSTATE_TYPE_STANDBY),
67 	/* State-id - 0x02 */
68 	sigi_make_pwrstate_lvl1(SIGI_LOCAL_STATE_RUN, SIGI_LOCAL_STATE_OFF,
69 				MPIDR_AFFLVL0, PSTATE_TYPE_POWERDOWN),
70 	/* State-id - 0x22 */
71 	sigi_make_pwrstate_lvl1(SIGI_LOCAL_STATE_OFF, SIGI_LOCAL_STATE_OFF,
72 				MPIDR_AFFLVL1, PSTATE_TYPE_POWERDOWN),
73 	0,
74 };
75 
76 /**
77  * sigi_validate_power_state() - Platform handler to check if power state is valid
78  *
79  * @power_state: power state prepares to go to.
80  * @req_state: power domain level specific local states
81  *
82  * This function is called by the PSCI implementation during the ``CPU_SUSPEND``
83  * call to validate the ``power_state`` parameter of the PSCI API and if valid,
84  * populate it in ``req_state`` (second argument) array as power domain level
85  * specific local states. If the ``power_state`` is invalid, the platform must
86  * return PSCI_E_INVALID_PARAMS as error, which is propagated back to the
87  * normal world PSCI client.
88  *
89  * Return: PSCI_E_SUCCESS if success, error code otherwise.
90  */
91 int sigi_validate_power_state(unsigned int power_state,
92 			    psci_power_state_t *req_state)
93 {
94 	unsigned int state_id;
95 	int i;
96 
97 	assert(req_state);
98 
99 	/*
100 	 *  Currently we are using a linear search for finding the matching
101 	 *  entry in the idle power state array. This can be made a binary
102 	 *  search if the number of entries justify the additional complexity.
103 	 */
104 	for (i = 0; !!sigi_pm_idle_states[i]; i++) {
105 		if (power_state == sigi_pm_idle_states[i])
106 			break;
107 	}
108 
109 	/* Return error if entry not found in the idle state array */
110 	if (!sigi_pm_idle_states[i])
111 		return PSCI_E_INVALID_PARAMS;
112 
113 	i = 0;
114 	state_id = psci_get_pstate_id(power_state);
115 
116 	/* Parse the State ID and populate the state info parameter */
117 	while (state_id) {
118 		req_state->pwr_domain_state[i++] = state_id &
119 						SIGI_LOCAL_PSTATE_MASK;
120 		state_id >>= SIGI_LOCAL_PSTATE_WIDTH;
121 	}
122 
123 	return PSCI_E_SUCCESS;
124 }
125 
126 /**
127  * sigi_validate_ns_entrypoint() - Platform handler to check ns_entrypoint
128  *
129  * @ns_entrypoint: entrypoint address to check
130  *
131  * This function need to check if the ns_entrypoint is in non-secure world.
132  *
133  * Return: PSCI_E_SUCCESS if success, error code otherwise.
134  */
135 static int sigi_validate_ns_entrypoint(uintptr_t entrypoint)
136 {
137 	/*
138 	 * Check if the non secure entrypoint lies within the non
139 	 * secure DRAM.
140 	 */
141 	if ((entrypoint >= SIGI_INTERLEAVE_DRAM_BASE) &&
142 	    (entrypoint < (SIGI_INTERLEAVE_DRAM_BASE + SIGI_NS_DDR_SIZE)))
143 		return PSCI_E_SUCCESS;
144 	else if ((entrypoint >= SIGI_NON_INTER_DRAM_BASE) &&
145 	    (entrypoint < (SIGI_NON_INTER_DRAM_BASE + SIGI_NS_DDR_SIZE)))
146 		return PSCI_E_SUCCESS;
147 	return PSCI_E_INVALID_ADDRESS;
148 }
149 
150 /**
151  * sigi_cpu_standby() - Put CPU to standy mode
152  *
153  * @cpu_state: local cpu state
154  *
155  * This function will put cpu into idle state, and will return when cpu wakeup.
156  *
157  * Return: void
158  */
159 void sigi_cpu_standby(plat_local_state_t cpu_state)
160 {
161 	assert(cpu_state == SIGI_LOCAL_STATE_RET);
162 
163 	/*
164 	 * Enter standby state
165 	 * dsb is good practice before using wfi to enter low power states
166 	 */
167 	dsb();
168 	wfi();
169 }
170 
171 static uint64_t get_offset_addr_by_mpidr(u_register_t mpidr)
172 {
173 	int i;
174 
175 	for (i = 0; i < ARRAY_SIZE(cpu_map); i++) {
176 		if (cpu_map[i].cpu_idx == mpidr)
177 			return cpu_map[i].offset;
178 	}
179 
180 	return ULONG_MAX;
181 }
182 
183 /**
184  * sigi_pwr_domain_on() -	Power on a cpu.
185  *
186  * @mpidr: mpidr value for this cpu
187  *
188  * This will be call before cpu power on, need to take action to bring cpu on
189  *
190  * Return: PSCI_E_SUCCESS if success, error code otherwise
191  */
192 static int sigi_pwr_domain_on(u_register_t mpidr)
193 {
194 	int rc = PSCI_E_SUCCESS;
195 	unsigned pos = plat_core_pos_by_mpidr(mpidr);
196 	uint64_t *hold_base = (uint64_t *)PLAT_SIGI_HOLD_BASE;
197 	uint64_t offset = get_offset_addr_by_mpidr(mpidr);
198 
199 	hold_base[pos] = PLAT_SIGI_HOLD_STATE_GO;
200 	sev();
201 
202 	mmio_setbits_32(SIGI_PMU_BASE + offset, BIT(12));
203 
204 	return rc;
205 }
206 
207 /**
208  * sigi_pwr_domain_off() - Power off a cpu.
209  *
210  * @target_state: CPU topological state
211  *
212  * This will be call before cpu power off, need to take action to put cpu off
213  *
214  * Return: void
215  */
216 static void sigi_pwr_domain_off(const psci_power_state_t *target_state)
217 {
218 	sigi_pwr_gic_off();
219 }
220 
221 /**
222  * sigi_pwr_domain_suspend() - Put system into suspend state.
223  *
224  * @target_state: Platform coordinated target local power states
225  *
226  * Perform the platform specific actions to prepare to suspend the calling
227  * CPU and its higher parent power domain levels as indicated by the
228  * ``target_state`` (first argument). It is called by the PSCI ``CPU_SUSPEND``
229  * API implementation.
230  *
231  * Return: void
232  */
233 static void sigi_pwr_domain_suspend(const psci_power_state_t *target_state)
234 {
235 	assert(0);
236 }
237 
238 void __dead2 plat_secondary_cold_boot_setup(void);
239 /**
240  * sigi_pwr_down_wfi() - Powerdown the CPU
241  *
242  * @target_state: Platform coordinated target local power states
243  *
244  * Perform platform specific actions including the ``wfi`` invocation which
245  * allows the CPU to powerdown.
246  *
247  * Return: void
248  */
249 static void __dead2 sigi_pwr_down_wfi(const psci_power_state_t *target_state)
250 {
251     disable_mmu_el3();
252 	plat_secondary_cold_boot_setup();
253 }
254 
255 /**
256  * sigi_pwr_domain_on_finish() - Platform handler when cpu is powered on.
257  *
258  * @target_state: Platform coordinated target local power states
259  *
260  * This function is called by the PSCI implementation after the calling CPU is
261  * powered on and released from reset in response to an earlier PSCI ``CPU_ON``
262  * call. It performs the platform-specific setup required to initialize enough
263  * state for this CPU to enter the normal world and also provide secure runtime
264  * firmware services.
265  *
266  * Return: void
267  */
268 static void sigi_pwr_domain_on_finish(const psci_power_state_t *target_state)
269 {
270 	assert(target_state->pwr_domain_state[MPIDR_AFFLVL0] ==
271 					SIGI_LOCAL_STATE_OFF);
272 
273 	sigi_pwr_gic_on_finish();
274 }
275 
276 /**
277  * sigi_pwr_domain_suspend_finish() - Platform handler after wakeup.
278  *
279  * @target_state: Platform coordinated target local power states
280  *
281  * Performs the platform-specific setup required to restore the saved state for
282  * this CPU to resume execution in the normal world and also provide secure
283  * runtime firmware services. This function will need to restore Distributor,
284  * Redistributors or ITS context.
285  *
286  * Return: void
287  */
288 static void sigi_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
289 {
290 	assert(0);
291 }
292 
293 /**
294  * sigi_system_off() - Platform handler to poweroff the system.
295  *
296  * Performs the platform-specific setup to power off the whole system.
297  * Due to we use fake shutdown for J5, this function just while (1) to wfi().
298  *
299  * Return: void
300  */
301 static void __dead2 sigi_system_off(void)
302 {
303     panic();
304 }
305 
306 /**
307  * sigi_system_reset() - Platform handler to reboot the system.
308  *
309  * Performs the platform-specific setup to reboot the whole system.
310  * We need to generated a wdt reset to reset the whole soc. In case of wdt
311  * trigger failed, going to panic().
312  *
313  * Return: void
314  */
315 static void __dead2 sigi_system_reset(void)
316 {
317     panic();
318 }
319 
320 /**
321  * sigi_psci_ops - j6 platform psci ops
322  */
323 static plat_psci_ops_t sigi_psci_ops = {
324 	.cpu_standby = sigi_cpu_standby,
325 	.pwr_domain_on = sigi_pwr_domain_on,
326 	.pwr_domain_off = sigi_pwr_domain_off,
327 	.pwr_domain_suspend = sigi_pwr_domain_suspend,
328 	.pwr_domain_pwr_down_wfi = sigi_pwr_down_wfi,
329 	.pwr_domain_on_finish = sigi_pwr_domain_on_finish,
330 	.pwr_domain_suspend_finish = sigi_pwr_domain_suspend_finish,
331 	.system_off = sigi_system_off,
332 	.system_reset = sigi_system_reset,
333 	.validate_power_state = sigi_validate_power_state,
334 	.validate_ns_entrypoint = sigi_validate_ns_entrypoint,
335 };
336 
337 /**
338  * plat_setup_psci_ops() - Platform handler to setup psci ops
339  *
340  * @sec_entrypoint: entry to going to after cpu powered on.
341  * @psci_ops: pointer to store psci ops.
342  *
343  * This function need to initialze
344  *
345  * Return: void.
346  */
347 int plat_setup_psci_ops(uintptr_t sec_entrypoint,
348 			const plat_psci_ops_t **psci_ops)
349 {
350 	uintptr_t *mailbox = (void *) PLAT_SIGI_TRUSTED_MAILBOX_BASE;
351 
352 	*mailbox = sec_entrypoint;
353 	assert(psci_ops);
354     secure_entrypoint = (unsigned long) sec_entrypoint;
355 	*psci_ops = &sigi_psci_ops;
356 
357 	return 0;
358 }
359