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