blob: 874b8a9eac9eb5200efa5b699b227add3d5ce3d2 [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>
Stefan Krsmanovic2b783662016-05-20 15:51:09 +020013#include <bakery_lock.h>
Soren Brinkmann76fcae32016-03-06 20:16:27 -080014#include <bl_common.h>
Isla Mitchelle3631462017-07-14 10:46:32 +010015#include <gic_common.h>
16#include <gicv2.h>
Soren Brinkmann76fcae32016-03-06 20:16:27 -080017#include <mmio.h>
Filip Drazic4c0765a2016-07-26 12:11:33 +020018#include <string.h>
Douglas Raillarda8954fc2017-01-26 15:54:44 +000019#include <utils.h>
Isla Mitchelle3631462017-07-14 10:46:32 +010020#include "../zynqmp_def.h"
Soren Brinkmann76fcae32016-03-06 20:16:27 -080021#include "pm_api_sys.h"
22#include "pm_client.h"
23#include "pm_ipi.h"
Soren Brinkmann76fcae32016-03-06 20:16:27 -080024
Filip Drazic4c0765a2016-07-26 12:11:33 +020025#define IRQ_MAX 84
26#define NUM_GICD_ISENABLER ((IRQ_MAX >> 5) + 1)
Soren Brinkmann76fcae32016-03-06 20:16:27 -080027#define UNDEFINED_CPUID (~0)
Filip Drazic0bd9d0c2016-07-20 17:17:39 +020028
Siva Durga Prasad Paladugu43b23a32018-04-27 16:26:47 +053029#define PM_SUSPEND_MODE_STD 0
30#define PM_SUSPEND_MODE_POWER_OFF 1
31
Stefan Krsmanovic2b783662016-05-20 15:51:09 +020032DEFINE_BAKERY_LOCK(pm_client_secure_lock);
Soren Brinkmann76fcae32016-03-06 20:16:27 -080033
Soren Brinkmann76fcae32016-03-06 20:16:27 -080034extern const struct pm_ipi apu_ipi;
35
Siva Durga Prasad Paladugu43b23a32018-04-27 16:26:47 +053036static uint32_t suspend_mode = PM_SUSPEND_MODE_STD;
37
Soren Brinkmann76fcae32016-03-06 20:16:27 -080038/* Order in pm_procs_all array must match cpu ids */
Soren Brinkmann4ae19c42017-07-01 20:24:47 -070039static const struct pm_proc pm_procs_all[] = {
Soren Brinkmann76fcae32016-03-06 20:16:27 -080040 {
41 .node_id = NODE_APU_0,
42 .pwrdn_mask = APU_0_PWRCTL_CPUPWRDWNREQ_MASK,
43 .ipi = &apu_ipi,
44 },
45 {
46 .node_id = NODE_APU_1,
47 .pwrdn_mask = APU_1_PWRCTL_CPUPWRDWNREQ_MASK,
48 .ipi = &apu_ipi,
49 },
50 {
51 .node_id = NODE_APU_2,
52 .pwrdn_mask = APU_2_PWRCTL_CPUPWRDWNREQ_MASK,
53 .ipi = &apu_ipi,
54 },
55 {
56 .node_id = NODE_APU_3,
57 .pwrdn_mask = APU_3_PWRCTL_CPUPWRDWNREQ_MASK,
58 .ipi = &apu_ipi,
59 },
60};
61
Filip Drazic4c0765a2016-07-26 12:11:33 +020062/* Interrupt to PM node ID map */
63static enum pm_node_id irq_node_map[IRQ_MAX + 1] = {
64 NODE_UNKNOWN,
65 NODE_UNKNOWN,
66 NODE_UNKNOWN,
67 NODE_UNKNOWN, /* 3 */
68 NODE_UNKNOWN,
69 NODE_UNKNOWN,
70 NODE_UNKNOWN,
71 NODE_UNKNOWN, /* 7 */
72 NODE_UNKNOWN,
73 NODE_UNKNOWN,
74 NODE_UNKNOWN,
75 NODE_UNKNOWN, /* 11 */
76 NODE_UNKNOWN,
77 NODE_UNKNOWN,
78 NODE_NAND,
79 NODE_QSPI, /* 15 */
80 NODE_GPIO,
81 NODE_I2C_0,
82 NODE_I2C_1,
83 NODE_SPI_0, /* 19 */
84 NODE_SPI_1,
85 NODE_UART_0,
86 NODE_UART_1,
87 NODE_CAN_0, /* 23 */
88 NODE_CAN_1,
89 NODE_UNKNOWN,
90 NODE_RTC,
91 NODE_RTC, /* 27 */
92 NODE_UNKNOWN,
93 NODE_UNKNOWN,
94 NODE_UNKNOWN,
95 NODE_UNKNOWN, /* 31 */
96 NODE_UNKNOWN,
97 NODE_UNKNOWN,
98 NODE_UNKNOWN,
99 NODE_UNKNOWN, /* 35, NODE_IPI_APU */
100 NODE_TTC_0,
101 NODE_TTC_0,
102 NODE_TTC_0,
103 NODE_TTC_1, /* 39 */
104 NODE_TTC_1,
105 NODE_TTC_1,
106 NODE_TTC_2,
107 NODE_TTC_2, /* 43 */
108 NODE_TTC_2,
109 NODE_TTC_3,
110 NODE_TTC_3,
111 NODE_TTC_3, /* 47 */
112 NODE_SD_0,
113 NODE_SD_1,
114 NODE_SD_0,
115 NODE_SD_1, /* 51 */
116 NODE_UNKNOWN,
117 NODE_UNKNOWN,
118 NODE_UNKNOWN,
119 NODE_UNKNOWN, /* 55 */
120 NODE_UNKNOWN,
121 NODE_ETH_0,
122 NODE_ETH_0,
123 NODE_ETH_1, /* 59 */
124 NODE_ETH_1,
125 NODE_ETH_2,
126 NODE_ETH_2,
127 NODE_ETH_3, /* 63 */
128 NODE_ETH_3,
129 NODE_USB_0,
130 NODE_USB_0,
131 NODE_USB_0, /* 67 */
132 NODE_USB_0,
133 NODE_USB_0,
134 NODE_USB_1,
135 NODE_USB_1, /* 71 */
136 NODE_USB_1,
137 NODE_USB_1,
138 NODE_USB_1,
139 NODE_USB_0, /* 75 */
140 NODE_USB_0,
141 NODE_ADMA,
142 NODE_ADMA,
143 NODE_ADMA, /* 79 */
144 NODE_ADMA,
145 NODE_ADMA,
146 NODE_ADMA,
147 NODE_ADMA, /* 83 */
148 NODE_ADMA,
149};
150
151/**
152 * irq_to_pm_node - Get PM node ID corresponding to the interrupt number
153 * @irq: Interrupt number
154 *
155 * Return: PM node ID corresponding to the specified interrupt
156 */
157static enum pm_node_id irq_to_pm_node(unsigned int irq)
158{
159 assert(irq <= IRQ_MAX);
160 return irq_node_map[irq];
161}
162
163/**
164 * pm_client_set_wakeup_sources - Set all slaves with enabled interrupts as wake
165 * sources in the PMU firmware
166 */
167static void pm_client_set_wakeup_sources(void)
168{
169 uint32_t reg_num;
170 uint8_t pm_wakeup_nodes_set[NODE_MAX];
171 uintptr_t isenabler1 = BASE_GICD_BASE + GICD_ISENABLER + 4;
172
Siva Durga Prasad Paladugu43b23a32018-04-27 16:26:47 +0530173 /* In case of power-off suspend, only NODE_EXTERN must be set */
174 if (suspend_mode == PM_SUSPEND_MODE_POWER_OFF) {
175 enum pm_ret_status ret;
176
177 ret = pm_set_wakeup_source(NODE_APU, NODE_EXTERN, 1);
178 /**
179 * If NODE_EXTERN could not be set as wake source, proceed with
180 * standard suspend (no one will wake the system otherwise)
181 */
182 if (ret == PM_RET_SUCCESS)
183 return;
184 }
185
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000186 zeromem(&pm_wakeup_nodes_set, sizeof(pm_wakeup_nodes_set));
Filip Drazic4c0765a2016-07-26 12:11:33 +0200187
188 for (reg_num = 0; reg_num < NUM_GICD_ISENABLER; reg_num++) {
189 uint32_t base_irq = reg_num << ISENABLER_SHIFT;
190 uint32_t reg = mmio_read_32(isenabler1 + (reg_num << 2));
191
192 if (!reg)
193 continue;
194
195 while (reg) {
196 enum pm_node_id node;
197 uint32_t idx, ret, irq, lowest_set = reg & (-reg);
198
199 idx = __builtin_ctz(lowest_set);
200 irq = base_irq + idx;
201
202 if (irq > IRQ_MAX)
203 break;
204
205 node = irq_to_pm_node(irq);
206 reg &= ~lowest_set;
207
208 if ((node != NODE_UNKNOWN) &&
209 (!pm_wakeup_nodes_set[node])) {
210 ret = pm_set_wakeup_source(NODE_APU, node, 1);
211 pm_wakeup_nodes_set[node] = !ret;
212 }
213 }
214 }
215}
216
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800217/**
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800218 * pm_get_proc() - returns pointer to the proc structure
219 * @cpuid: id of the cpu whose proc struct pointer should be returned
220 *
221 * Return: pointer to a proc structure if proc is found, otherwise NULL
222 */
223const struct pm_proc *pm_get_proc(unsigned int cpuid)
224{
225 if (cpuid < ARRAY_SIZE(pm_procs_all))
226 return &pm_procs_all[cpuid];
227
228 return NULL;
229}
230
231/**
232 * pm_get_proc_by_node() - returns pointer to the proc structure
233 * @nid: node id of the processor
234 *
235 * Return: pointer to a proc structure if proc is found, otherwise NULL
236 */
237const struct pm_proc *pm_get_proc_by_node(enum pm_node_id nid)
238{
239 for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) {
240 if (nid == pm_procs_all[i].node_id)
241 return &pm_procs_all[i];
242 }
243 return NULL;
244}
245
246/**
247 * pm_get_cpuid() - get the local cpu ID for a global node ID
248 * @nid: node id of the processor
249 *
250 * Return: the cpu ID (starting from 0) for the subsystem
251 */
252static unsigned int pm_get_cpuid(enum pm_node_id nid)
253{
254 for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) {
255 if (pm_procs_all[i].node_id == nid)
256 return i;
257 }
258 return UNDEFINED_CPUID;
259}
260
261const struct pm_proc *primary_proc = &pm_procs_all[0];
262
263/**
264 * pm_client_suspend() - Client-specific suspend actions
265 *
266 * This function should contain any PU-specific actions
267 * required prior to sending suspend request to PMU
Filip Drazic4c0765a2016-07-26 12:11:33 +0200268 * Actions taken depend on the state system is suspending to.
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800269 */
Filip Drazic4c0765a2016-07-26 12:11:33 +0200270void pm_client_suspend(const struct pm_proc *proc, unsigned int state)
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800271{
Stefan Krsmanovic2b783662016-05-20 15:51:09 +0200272 bakery_lock_get(&pm_client_secure_lock);
273
Filip Drazic4c0765a2016-07-26 12:11:33 +0200274 if (state == PM_STATE_SUSPEND_TO_RAM)
275 pm_client_set_wakeup_sources();
276
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800277 /* Set powerdown request */
278 mmio_write_32(APU_PWRCTL, mmio_read_32(APU_PWRCTL) | proc->pwrdn_mask);
Stefan Krsmanovic2b783662016-05-20 15:51:09 +0200279
280 bakery_lock_release(&pm_client_secure_lock);
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800281}
282
283
284/**
285 * pm_client_abort_suspend() - Client-specific abort-suspend actions
286 *
287 * This function should contain any PU-specific actions
288 * required for aborting a prior suspend request
289 */
290void pm_client_abort_suspend(void)
291{
292 /* Enable interrupts at processor level (for current cpu) */
293 gicv2_cpuif_enable();
Stefan Krsmanovic2b783662016-05-20 15:51:09 +0200294
295 bakery_lock_get(&pm_client_secure_lock);
296
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800297 /* Clear powerdown request */
298 mmio_write_32(APU_PWRCTL,
299 mmio_read_32(APU_PWRCTL) & ~primary_proc->pwrdn_mask);
Stefan Krsmanovic2b783662016-05-20 15:51:09 +0200300
301 bakery_lock_release(&pm_client_secure_lock);
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800302}
303
304/**
305 * pm_client_wakeup() - Client-specific wakeup actions
306 *
307 * This function should contain any PU-specific actions
308 * required for waking up another APU core
309 */
310void pm_client_wakeup(const struct pm_proc *proc)
311{
312 unsigned int cpuid = pm_get_cpuid(proc->node_id);
313
314 if (cpuid == UNDEFINED_CPUID)
315 return;
316
Stefan Krsmanovic2b783662016-05-20 15:51:09 +0200317 bakery_lock_get(&pm_client_secure_lock);
318
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800319 /* clear powerdown bit for affected cpu */
320 uint32_t val = mmio_read_32(APU_PWRCTL);
321 val &= ~(proc->pwrdn_mask);
322 mmio_write_32(APU_PWRCTL, val);
Stefan Krsmanovic2b783662016-05-20 15:51:09 +0200323
324 bakery_lock_release(&pm_client_secure_lock);
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800325}
Siva Durga Prasad Paladugu43b23a32018-04-27 16:26:47 +0530326
327enum pm_ret_status pm_set_suspend_mode(uint32_t mode)
328{
329 if ((mode != PM_SUSPEND_MODE_STD) &&
330 (mode != PM_SUSPEND_MODE_POWER_OFF))
331 return PM_RET_ERROR_ARGS;
332
333 suspend_mode = mode;
334 return PM_RET_SUCCESS;
335}