blob: bebb74ce7fb96963de4ae8430a2835e6f5e2b7ad [file] [log] [blame]
Soren Brinkmann76fcae32016-03-06 20:16:27 -08001/*
Douglas Raillarda8954fc2017-01-26 15:54:44 +00002 * Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved.
Soren Brinkmann76fcae32016-03-06 20:16:27 -08003 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Soren Brinkmann76fcae32016-03-06 20:16:27 -08005 */
6
7/*
8 * APU specific definition of processors in the subsystem as well as functions
9 * for getting information about and changing state of the APU.
10 */
11
Filip Drazic4c0765a2016-07-26 12:11:33 +020012#include <assert.h>
Filip Drazic4c0765a2016-07-26 12:11:33 +020013#include <string.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +000014
15#include <common/bl_common.h>
16#include <drivers/arm/gic_common.h>
17#include <drivers/arm/gicv2.h>
18#include <lib/bakery_lock.h>
19#include <lib/mmio.h>
20#include <lib/utils.h>
21
Isla Mitchelle3631462017-07-14 10:46:32 +010022#include "../zynqmp_def.h"
Soren Brinkmann76fcae32016-03-06 20:16:27 -080023#include "pm_api_sys.h"
24#include "pm_client.h"
25#include "pm_ipi.h"
Soren Brinkmann76fcae32016-03-06 20:16:27 -080026
Filip Drazic4c0765a2016-07-26 12:11:33 +020027#define IRQ_MAX 84
28#define NUM_GICD_ISENABLER ((IRQ_MAX >> 5) + 1)
Soren Brinkmann76fcae32016-03-06 20:16:27 -080029#define UNDEFINED_CPUID (~0)
Filip Drazic0bd9d0c2016-07-20 17:17:39 +020030
Siva Durga Prasad Paladugu43b23a32018-04-27 16:26:47 +053031#define PM_SUSPEND_MODE_STD 0
32#define PM_SUSPEND_MODE_POWER_OFF 1
33
Stefan Krsmanovic2b783662016-05-20 15:51:09 +020034DEFINE_BAKERY_LOCK(pm_client_secure_lock);
Soren Brinkmann76fcae32016-03-06 20:16:27 -080035
Soren Brinkmann76fcae32016-03-06 20:16:27 -080036extern const struct pm_ipi apu_ipi;
37
Siva Durga Prasad Paladugu43b23a32018-04-27 16:26:47 +053038static uint32_t suspend_mode = PM_SUSPEND_MODE_STD;
39
Soren Brinkmann76fcae32016-03-06 20:16:27 -080040/* Order in pm_procs_all array must match cpu ids */
Soren Brinkmann4ae19c42017-07-01 20:24:47 -070041static const struct pm_proc pm_procs_all[] = {
Soren Brinkmann76fcae32016-03-06 20:16:27 -080042 {
43 .node_id = NODE_APU_0,
44 .pwrdn_mask = APU_0_PWRCTL_CPUPWRDWNREQ_MASK,
45 .ipi = &apu_ipi,
46 },
47 {
48 .node_id = NODE_APU_1,
49 .pwrdn_mask = APU_1_PWRCTL_CPUPWRDWNREQ_MASK,
50 .ipi = &apu_ipi,
51 },
52 {
53 .node_id = NODE_APU_2,
54 .pwrdn_mask = APU_2_PWRCTL_CPUPWRDWNREQ_MASK,
55 .ipi = &apu_ipi,
56 },
57 {
58 .node_id = NODE_APU_3,
59 .pwrdn_mask = APU_3_PWRCTL_CPUPWRDWNREQ_MASK,
60 .ipi = &apu_ipi,
61 },
62};
63
Filip Drazic4c0765a2016-07-26 12:11:33 +020064/* Interrupt to PM node ID map */
65static enum pm_node_id irq_node_map[IRQ_MAX + 1] = {
66 NODE_UNKNOWN,
67 NODE_UNKNOWN,
68 NODE_UNKNOWN,
69 NODE_UNKNOWN, /* 3 */
70 NODE_UNKNOWN,
71 NODE_UNKNOWN,
72 NODE_UNKNOWN,
73 NODE_UNKNOWN, /* 7 */
74 NODE_UNKNOWN,
75 NODE_UNKNOWN,
76 NODE_UNKNOWN,
77 NODE_UNKNOWN, /* 11 */
78 NODE_UNKNOWN,
79 NODE_UNKNOWN,
80 NODE_NAND,
81 NODE_QSPI, /* 15 */
82 NODE_GPIO,
83 NODE_I2C_0,
84 NODE_I2C_1,
85 NODE_SPI_0, /* 19 */
86 NODE_SPI_1,
87 NODE_UART_0,
88 NODE_UART_1,
89 NODE_CAN_0, /* 23 */
90 NODE_CAN_1,
91 NODE_UNKNOWN,
92 NODE_RTC,
93 NODE_RTC, /* 27 */
94 NODE_UNKNOWN,
95 NODE_UNKNOWN,
96 NODE_UNKNOWN,
97 NODE_UNKNOWN, /* 31 */
98 NODE_UNKNOWN,
99 NODE_UNKNOWN,
100 NODE_UNKNOWN,
101 NODE_UNKNOWN, /* 35, NODE_IPI_APU */
102 NODE_TTC_0,
103 NODE_TTC_0,
104 NODE_TTC_0,
105 NODE_TTC_1, /* 39 */
106 NODE_TTC_1,
107 NODE_TTC_1,
108 NODE_TTC_2,
109 NODE_TTC_2, /* 43 */
110 NODE_TTC_2,
111 NODE_TTC_3,
112 NODE_TTC_3,
113 NODE_TTC_3, /* 47 */
114 NODE_SD_0,
115 NODE_SD_1,
116 NODE_SD_0,
117 NODE_SD_1, /* 51 */
118 NODE_UNKNOWN,
119 NODE_UNKNOWN,
120 NODE_UNKNOWN,
121 NODE_UNKNOWN, /* 55 */
122 NODE_UNKNOWN,
123 NODE_ETH_0,
124 NODE_ETH_0,
125 NODE_ETH_1, /* 59 */
126 NODE_ETH_1,
127 NODE_ETH_2,
128 NODE_ETH_2,
129 NODE_ETH_3, /* 63 */
130 NODE_ETH_3,
131 NODE_USB_0,
132 NODE_USB_0,
133 NODE_USB_0, /* 67 */
134 NODE_USB_0,
135 NODE_USB_0,
136 NODE_USB_1,
137 NODE_USB_1, /* 71 */
138 NODE_USB_1,
139 NODE_USB_1,
140 NODE_USB_1,
141 NODE_USB_0, /* 75 */
142 NODE_USB_0,
143 NODE_ADMA,
144 NODE_ADMA,
145 NODE_ADMA, /* 79 */
146 NODE_ADMA,
147 NODE_ADMA,
148 NODE_ADMA,
149 NODE_ADMA, /* 83 */
150 NODE_ADMA,
151};
152
153/**
154 * irq_to_pm_node - Get PM node ID corresponding to the interrupt number
155 * @irq: Interrupt number
156 *
157 * Return: PM node ID corresponding to the specified interrupt
158 */
159static enum pm_node_id irq_to_pm_node(unsigned int irq)
160{
161 assert(irq <= IRQ_MAX);
162 return irq_node_map[irq];
163}
164
165/**
166 * pm_client_set_wakeup_sources - Set all slaves with enabled interrupts as wake
167 * sources in the PMU firmware
168 */
169static void pm_client_set_wakeup_sources(void)
170{
171 uint32_t reg_num;
172 uint8_t pm_wakeup_nodes_set[NODE_MAX];
173 uintptr_t isenabler1 = BASE_GICD_BASE + GICD_ISENABLER + 4;
174
Siva Durga Prasad Paladugu43b23a32018-04-27 16:26:47 +0530175 /* In case of power-off suspend, only NODE_EXTERN must be set */
176 if (suspend_mode == PM_SUSPEND_MODE_POWER_OFF) {
177 enum pm_ret_status ret;
178
179 ret = pm_set_wakeup_source(NODE_APU, NODE_EXTERN, 1);
180 /**
181 * If NODE_EXTERN could not be set as wake source, proceed with
182 * standard suspend (no one will wake the system otherwise)
183 */
184 if (ret == PM_RET_SUCCESS)
185 return;
186 }
187
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000188 zeromem(&pm_wakeup_nodes_set, sizeof(pm_wakeup_nodes_set));
Filip Drazic4c0765a2016-07-26 12:11:33 +0200189
190 for (reg_num = 0; reg_num < NUM_GICD_ISENABLER; reg_num++) {
191 uint32_t base_irq = reg_num << ISENABLER_SHIFT;
192 uint32_t reg = mmio_read_32(isenabler1 + (reg_num << 2));
193
194 if (!reg)
195 continue;
196
197 while (reg) {
198 enum pm_node_id node;
199 uint32_t idx, ret, irq, lowest_set = reg & (-reg);
200
201 idx = __builtin_ctz(lowest_set);
202 irq = base_irq + idx;
203
204 if (irq > IRQ_MAX)
205 break;
206
207 node = irq_to_pm_node(irq);
208 reg &= ~lowest_set;
209
210 if ((node != NODE_UNKNOWN) &&
211 (!pm_wakeup_nodes_set[node])) {
212 ret = pm_set_wakeup_source(NODE_APU, node, 1);
213 pm_wakeup_nodes_set[node] = !ret;
214 }
215 }
216 }
217}
218
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800219/**
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800220 * pm_get_proc() - returns pointer to the proc structure
221 * @cpuid: id of the cpu whose proc struct pointer should be returned
222 *
223 * Return: pointer to a proc structure if proc is found, otherwise NULL
224 */
225const struct pm_proc *pm_get_proc(unsigned int cpuid)
226{
227 if (cpuid < ARRAY_SIZE(pm_procs_all))
228 return &pm_procs_all[cpuid];
229
230 return NULL;
231}
232
233/**
234 * pm_get_proc_by_node() - returns pointer to the proc structure
235 * @nid: node id of the processor
236 *
237 * Return: pointer to a proc structure if proc is found, otherwise NULL
238 */
239const struct pm_proc *pm_get_proc_by_node(enum pm_node_id nid)
240{
241 for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) {
242 if (nid == pm_procs_all[i].node_id)
243 return &pm_procs_all[i];
244 }
245 return NULL;
246}
247
248/**
249 * pm_get_cpuid() - get the local cpu ID for a global node ID
250 * @nid: node id of the processor
251 *
252 * Return: the cpu ID (starting from 0) for the subsystem
253 */
254static unsigned int pm_get_cpuid(enum pm_node_id nid)
255{
256 for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) {
257 if (pm_procs_all[i].node_id == nid)
258 return i;
259 }
260 return UNDEFINED_CPUID;
261}
262
263const struct pm_proc *primary_proc = &pm_procs_all[0];
264
265/**
266 * pm_client_suspend() - Client-specific suspend actions
267 *
268 * This function should contain any PU-specific actions
269 * required prior to sending suspend request to PMU
Filip Drazic4c0765a2016-07-26 12:11:33 +0200270 * Actions taken depend on the state system is suspending to.
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800271 */
Filip Drazic4c0765a2016-07-26 12:11:33 +0200272void pm_client_suspend(const struct pm_proc *proc, unsigned int state)
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800273{
Stefan Krsmanovic2b783662016-05-20 15:51:09 +0200274 bakery_lock_get(&pm_client_secure_lock);
275
Filip Drazic4c0765a2016-07-26 12:11:33 +0200276 if (state == PM_STATE_SUSPEND_TO_RAM)
277 pm_client_set_wakeup_sources();
278
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800279 /* Set powerdown request */
280 mmio_write_32(APU_PWRCTL, mmio_read_32(APU_PWRCTL) | proc->pwrdn_mask);
Stefan Krsmanovic2b783662016-05-20 15:51:09 +0200281
282 bakery_lock_release(&pm_client_secure_lock);
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800283}
284
285
286/**
287 * pm_client_abort_suspend() - Client-specific abort-suspend actions
288 *
289 * This function should contain any PU-specific actions
290 * required for aborting a prior suspend request
291 */
292void pm_client_abort_suspend(void)
293{
294 /* Enable interrupts at processor level (for current cpu) */
295 gicv2_cpuif_enable();
Stefan Krsmanovic2b783662016-05-20 15:51:09 +0200296
297 bakery_lock_get(&pm_client_secure_lock);
298
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800299 /* Clear powerdown request */
300 mmio_write_32(APU_PWRCTL,
301 mmio_read_32(APU_PWRCTL) & ~primary_proc->pwrdn_mask);
Stefan Krsmanovic2b783662016-05-20 15:51:09 +0200302
303 bakery_lock_release(&pm_client_secure_lock);
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800304}
305
306/**
307 * pm_client_wakeup() - Client-specific wakeup actions
308 *
309 * This function should contain any PU-specific actions
310 * required for waking up another APU core
311 */
312void pm_client_wakeup(const struct pm_proc *proc)
313{
314 unsigned int cpuid = pm_get_cpuid(proc->node_id);
315
316 if (cpuid == UNDEFINED_CPUID)
317 return;
318
Stefan Krsmanovic2b783662016-05-20 15:51:09 +0200319 bakery_lock_get(&pm_client_secure_lock);
320
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800321 /* clear powerdown bit for affected cpu */
322 uint32_t val = mmio_read_32(APU_PWRCTL);
323 val &= ~(proc->pwrdn_mask);
324 mmio_write_32(APU_PWRCTL, val);
Stefan Krsmanovic2b783662016-05-20 15:51:09 +0200325
326 bakery_lock_release(&pm_client_secure_lock);
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800327}
Siva Durga Prasad Paladugu43b23a32018-04-27 16:26:47 +0530328
329enum pm_ret_status pm_set_suspend_mode(uint32_t mode)
330{
331 if ((mode != PM_SUSPEND_MODE_STD) &&
332 (mode != PM_SUSPEND_MODE_POWER_OFF))
333 return PM_RET_ERROR_ARGS;
334
335 suspend_mode = mode;
336 return PM_RET_SUCCESS;
337}