1 /*
2 * Copyright (c) 2013-2018, Arm Limited and Contributors. All rights reserved.
3 * Copyright (c) 2022-2025, Advanced Micro Devices, Inc. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8 /*
9 * APU specific definition of processors in the subsystem as well as functions
10 * for getting information about and changing state of the APU.
11 */
12
13 #include <assert.h>
14 #include <string.h>
15
16 #include <common/bl_common.h>
17 #include <drivers/arm/gic_common.h>
18 #include <drivers/arm/gicv2.h>
19 #include <lib/bakery_lock.h>
20 #include <lib/mmio.h>
21 #include <lib/utils.h>
22
23 #include <plat_ipi.h>
24 #include "pm_client.h"
25 #include "pm_ipi.h"
26 #include <zynqmp_def.h>
27 #include "zynqmp_pm_api_sys.h"
28
29 #define IRQ_MAX 84U
30 #define NUM_GICD_ISENABLER ((IRQ_MAX >> 5U) + 1U)
31 #define UNDEFINED_CPUID (~0U)
32
33 #define PM_SUSPEND_MODE_STD 0U
34 #define PM_SUSPEND_MODE_POWER_OFF 1U
35
36 DEFINE_BAKERY_LOCK(pm_client_secure_lock);
37
38 static const struct pm_ipi apu_ipi = {
39 .local_ipi_id = IPI_LOCAL_ID,
40 .remote_ipi_id = IPI_REMOTE_ID,
41 .buffer_base = IPI_BUFFER_LOCAL_BASE,
42 };
43
44 static uint32_t suspend_mode = PM_SUSPEND_MODE_STD;
45
46 /* Order in pm_procs_all array must match cpu ids */
47 static const struct pm_proc pm_procs_all[] = {
48 {
49 .node_id = (uint32_t)NODE_APU_0,
50 .pwrdn_mask = APU_0_PWRCTL_CPUPWRDWNREQ_MASK,
51 .ipi = &apu_ipi,
52 },
53 {
54 .node_id = (uint32_t)NODE_APU_1,
55 .pwrdn_mask = APU_1_PWRCTL_CPUPWRDWNREQ_MASK,
56 .ipi = &apu_ipi,
57 },
58 {
59 .node_id = (uint32_t)NODE_APU_2,
60 .pwrdn_mask = APU_2_PWRCTL_CPUPWRDWNREQ_MASK,
61 .ipi = &apu_ipi,
62 },
63 {
64 .node_id = (uint32_t)NODE_APU_3,
65 .pwrdn_mask = APU_3_PWRCTL_CPUPWRDWNREQ_MASK,
66 .ipi = &apu_ipi,
67 },
68 };
69
70 /* Interrupt to PM node ID map */
71 static enum pm_node_id irq_node_map[IRQ_MAX + 1U] = {
72 NODE_UNKNOWN,
73 NODE_UNKNOWN,
74 NODE_UNKNOWN,
75 NODE_UNKNOWN, /* 3 */
76 NODE_UNKNOWN,
77 NODE_UNKNOWN,
78 NODE_UNKNOWN,
79 NODE_UNKNOWN, /* 7 */
80 NODE_UNKNOWN,
81 NODE_UNKNOWN,
82 NODE_UNKNOWN,
83 NODE_UNKNOWN, /* 11 */
84 NODE_UNKNOWN,
85 NODE_UNKNOWN,
86 NODE_NAND,
87 NODE_QSPI, /* 15 */
88 NODE_GPIO,
89 NODE_I2C_0,
90 NODE_I2C_1,
91 NODE_SPI_0, /* 19 */
92 NODE_SPI_1,
93 NODE_UART_0,
94 NODE_UART_1,
95 NODE_CAN_0, /* 23 */
96 NODE_CAN_1,
97 NODE_UNKNOWN,
98 NODE_RTC,
99 NODE_RTC, /* 27 */
100 NODE_UNKNOWN,
101 NODE_UNKNOWN,
102 NODE_UNKNOWN,
103 NODE_UNKNOWN, /* 31 */
104 NODE_UNKNOWN,
105 NODE_UNKNOWN,
106 NODE_UNKNOWN,
107 NODE_UNKNOWN, /* 35, NODE_IPI_APU */
108 NODE_TTC_0,
109 NODE_TTC_0,
110 NODE_TTC_0,
111 NODE_TTC_1, /* 39 */
112 NODE_TTC_1,
113 NODE_TTC_1,
114 NODE_TTC_2,
115 NODE_TTC_2, /* 43 */
116 NODE_TTC_2,
117 NODE_TTC_3,
118 NODE_TTC_3,
119 NODE_TTC_3, /* 47 */
120 NODE_SD_0,
121 NODE_SD_1,
122 NODE_SD_0,
123 NODE_SD_1, /* 51 */
124 NODE_UNKNOWN,
125 NODE_UNKNOWN,
126 NODE_UNKNOWN,
127 NODE_UNKNOWN, /* 55 */
128 NODE_UNKNOWN,
129 NODE_ETH_0,
130 NODE_ETH_0,
131 NODE_ETH_1, /* 59 */
132 NODE_ETH_1,
133 NODE_ETH_2,
134 NODE_ETH_2,
135 NODE_ETH_3, /* 63 */
136 NODE_ETH_3,
137 NODE_USB_0,
138 NODE_USB_0,
139 NODE_USB_0, /* 67 */
140 NODE_USB_0,
141 NODE_USB_0,
142 NODE_USB_1,
143 NODE_USB_1, /* 71 */
144 NODE_USB_1,
145 NODE_USB_1,
146 NODE_USB_1,
147 NODE_USB_0, /* 75 */
148 NODE_USB_0,
149 NODE_ADMA,
150 NODE_ADMA,
151 NODE_ADMA, /* 79 */
152 NODE_ADMA,
153 NODE_ADMA,
154 NODE_ADMA,
155 NODE_ADMA, /* 83 */
156 NODE_ADMA,
157 };
158
159 /**
160 * irq_to_pm_node - Get PM node ID corresponding to the interrupt number.
161 * @irq: Interrupt number.
162 *
163 * Return: PM node ID corresponding to the specified interrupt.
164 *
165 */
irq_to_pm_node(uint32_t irq)166 static enum pm_node_id irq_to_pm_node(uint32_t irq)
167 {
168 assert(irq <= IRQ_MAX);
169 return irq_node_map[irq];
170 }
171
172 /**
173 * pm_client_set_wakeup_sources - Set all slaves with enabled interrupts as wake
174 * sources in the PMU firmware.
175 * @flag: 0 - Call from secure source.
176 * 1 - Call from non-secure source.
177 *
178 */
pm_client_set_wakeup_sources(uint32_t flag)179 static void pm_client_set_wakeup_sources(uint32_t flag)
180 {
181 uint32_t reg_num;
182 uint8_t pm_wakeup_nodes_set[NODE_MAX] = { 0 };
183 uintptr_t isenabler1 = BASE_GICD_BASE + GICD_ISENABLER + 4U;
184
185 /* In case of power-off suspend, only NODE_EXTERN must be set */
186 if (suspend_mode == PM_SUSPEND_MODE_POWER_OFF) {
187 enum pm_ret_status ret;
188
189 ret = pm_set_wakeup_source(NODE_APU, NODE_EXTERN, 1U, flag);
190 /**
191 * If NODE_EXTERN could not be set as wake source, proceed with
192 * standard suspend (no one will wake the system otherwise)
193 */
194 if (ret == PM_RET_SUCCESS) {
195 return;
196 }
197 }
198
199 zeromem(&pm_wakeup_nodes_set, sizeof(pm_wakeup_nodes_set));
200
201 for (reg_num = 0U; reg_num < NUM_GICD_ISENABLER; reg_num++) {
202 uint32_t base_irq = reg_num << ISENABLER_SHIFT;
203 uint32_t reg = mmio_read_32(isenabler1 + (uint64_t)(reg_num << 2U));
204
205 if (reg == 0) {
206 continue;
207 }
208
209 while (reg != 0U) {
210 enum pm_node_id node;
211 uint32_t idx, irq, lowest_set = reg & (-reg);
212 enum pm_ret_status ret;
213
214 idx = (uint32_t)__builtin_ctz(lowest_set);
215 irq = base_irq + idx;
216
217 if (irq > IRQ_MAX) {
218 break;
219 }
220
221 node = irq_to_pm_node(irq);
222 reg &= ~lowest_set;
223
224 if ((node > NODE_UNKNOWN) && (node < NODE_MAX)) {
225 if (pm_wakeup_nodes_set[node] == 0U) {
226 ret = pm_set_wakeup_source(NODE_APU, node, 1U, flag);
227 pm_wakeup_nodes_set[node] = (ret == PM_RET_SUCCESS) ? 1U : 0U;
228 }
229 }
230 }
231 }
232 }
233
234 /**
235 * pm_get_proc() - returns pointer to the proc structure.
236 * @cpuid: id of the cpu whose proc struct pointer should be returned.
237 *
238 * Return: pointer to a proc structure if proc is found, otherwise NULL.
239 *
240 */
pm_get_proc(uint32_t cpuid)241 const struct pm_proc *pm_get_proc(uint32_t cpuid)
242 {
243 const struct pm_proc *ret = NULL;
244
245 if (cpuid < ARRAY_SIZE(pm_procs_all)) {
246 ret = &pm_procs_all[cpuid];
247 }
248
249 return ret;
250 }
251
252 /**
253 * pm_get_cpuid() - get the local cpu ID for a global node ID.
254 * @nid: node id of the processor.
255 *
256 * Return: the cpu ID (starting from 0) for the subsystem.
257 *
258 */
pm_get_cpuid(enum pm_node_id nid)259 static uint32_t pm_get_cpuid(enum pm_node_id nid)
260 {
261 uint32_t ret = UNDEFINED_CPUID;
262
263 for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) {
264 if (pm_procs_all[i].node_id == nid) {
265 ret = i;
266 break;
267 }
268 }
269
270 return ret;
271 }
272
273 const struct pm_proc *primary_proc = &pm_procs_all[0];
274
275 /**
276 * pm_client_suspend() - Client-specific suspend actions.
277 * @proc: processor which need to suspend.
278 * @state: desired suspend state.
279 * @flag: 0 - Call from secure source.
280 * 1 - Call from non-secure source.
281 *
282 * This function should contain any PU-specific actions
283 * required prior to sending suspend request to PMU
284 * Actions taken depend on the state system is suspending to.
285 *
286 */
pm_client_suspend(const struct pm_proc * proc,uint32_t state,uint32_t flag)287 void pm_client_suspend(const struct pm_proc *proc, uint32_t state, uint32_t flag)
288 {
289 bakery_lock_get(&pm_client_secure_lock);
290
291 if (state == PM_STATE_SUSPEND_TO_RAM) {
292 pm_client_set_wakeup_sources(flag);
293 }
294
295 /* Set powerdown request */
296 mmio_write_32(APU_PWRCTL, mmio_read_32(APU_PWRCTL) | proc->pwrdn_mask);
297
298 bakery_lock_release(&pm_client_secure_lock);
299 }
300
301 /**
302 * pm_client_wakeup() - Client-specific wakeup actions.
303 * @proc: Processor which need to wakeup.
304 *
305 * This function should contain any PU-specific actions
306 * required for waking up another APU core.
307 *
308 */
pm_client_wakeup(const struct pm_proc * proc)309 void pm_client_wakeup(const struct pm_proc *proc)
310 {
311 uint32_t cpuid = pm_get_cpuid(proc->node_id);
312 uint32_t val;
313
314 if (cpuid != UNDEFINED_CPUID) {
315 bakery_lock_get(&pm_client_secure_lock);
316
317 /* clear powerdown bit for affected cpu */
318 val = mmio_read_32(APU_PWRCTL);
319
320 val &= ~(proc->pwrdn_mask);
321 mmio_write_32(APU_PWRCTL, val);
322
323 bakery_lock_release(&pm_client_secure_lock);
324 }
325 }
326
pm_set_suspend_mode(uint32_t mode)327 enum pm_ret_status pm_set_suspend_mode(uint32_t mode)
328 {
329 enum pm_ret_status suspend_mode_status = PM_RET_ERROR_ARGS;
330
331 if ((mode == PM_SUSPEND_MODE_STD) ||
332 (mode == PM_SUSPEND_MODE_POWER_OFF)) {
333 suspend_mode = mode;
334 suspend_mode_status = PM_RET_SUCCESS;
335 }
336
337 return suspend_mode_status;
338 }
339