blob: 2f2e2edfe6170a211066505b31404ccff8709187 [file] [log] [blame]
Soren Brinkmann76fcae32016-03-06 20:16:27 -08001/*
2 * Copyright (c) 2013-2015, ARM Limited and Contributors. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * Neither the name of ARM nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without specific
16 * prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/*
32 * ZynqMP system level PM-API functions and communication with PMU via
33 * IPI interrupts
34 */
35
36#include <arch_helpers.h>
37#include <platform.h>
38#include "pm_client.h"
39#include "pm_common.h"
40#include "pm_api_sys.h"
41
42/**
43 * Assigning of argument values into array elements.
44 */
45#define PM_PACK_PAYLOAD1(pl, arg0) { \
46 pl[0] = (uint32_t)(arg0); \
47}
48
49#define PM_PACK_PAYLOAD2(pl, arg0, arg1) { \
50 pl[1] = (uint32_t)(arg1); \
51 PM_PACK_PAYLOAD1(pl, arg0); \
52}
53
54#define PM_PACK_PAYLOAD3(pl, arg0, arg1, arg2) { \
55 pl[2] = (uint32_t)(arg2); \
56 PM_PACK_PAYLOAD2(pl, arg0, arg1); \
57}
58
59#define PM_PACK_PAYLOAD4(pl, arg0, arg1, arg2, arg3) { \
60 pl[3] = (uint32_t)(arg3); \
61 PM_PACK_PAYLOAD3(pl, arg0, arg1, arg2); \
62}
63
64#define PM_PACK_PAYLOAD5(pl, arg0, arg1, arg2, arg3, arg4) { \
65 pl[4] = (uint32_t)(arg4); \
66 PM_PACK_PAYLOAD4(pl, arg0, arg1, arg2, arg3); \
67}
68
69#define PM_PACK_PAYLOAD6(pl, arg0, arg1, arg2, arg3, arg4, arg5) { \
70 pl[5] = (uint32_t)(arg5); \
71 PM_PACK_PAYLOAD5(pl, arg0, arg1, arg2, arg3, arg4); \
72}
73
74/**
75 * pm_self_suspend() - PM call for processor to suspend itself
76 * @nid Node id of the processor or subsystem
77 * @latency Requested maximum wakeup latency (not supported)
78 * @state Requested state (not supported)
79 * @address Resume address
80 *
81 * This is a blocking call, it will return only once PMU has responded.
82 * On a wakeup, resume address will be automatically set by PMU.
83 *
84 * @return Returns status, either success or error+reason
85 */
86enum pm_ret_status pm_self_suspend(enum pm_node_id nid,
87 unsigned int latency,
88 unsigned int state,
89 uintptr_t address)
90{
91 uint32_t payload[PAYLOAD_ARG_CNT];
92 unsigned int cpuid = plat_my_core_pos();
93 const struct pm_proc *proc = pm_get_proc(cpuid);
94
95 /*
96 * Do client specific suspend operations
97 * (e.g. set powerdown request bit)
98 */
99 pm_client_suspend(proc);
100 /* Send request to the PMU */
101 PM_PACK_PAYLOAD6(payload, PM_SELF_SUSPEND, proc->node_id, latency,
102 state, address, (address >> 32));
103 return pm_ipi_send_sync(proc, payload, NULL);
104}
105
106/**
107 * pm_req_suspend() - PM call to request for another PU or subsystem to
108 * be suspended gracefully.
109 * @target Node id of the targeted PU or subsystem
110 * @ack Flag to specify whether acknowledge is requested
111 * @latency Requested wakeup latency (not supported)
112 * @state Requested state (not supported)
113 *
114 * @return Returns status, either success or error+reason
115 */
116enum pm_ret_status pm_req_suspend(enum pm_node_id target,
117 enum pm_request_ack ack,
118 unsigned int latency, unsigned int state)
119{
120 uint32_t payload[PAYLOAD_ARG_CNT];
121
122 /* Send request to the PMU */
123 PM_PACK_PAYLOAD5(payload, PM_REQ_SUSPEND, target, ack, latency, state);
124 if (ack == REQ_ACK_BLOCKING)
125 return pm_ipi_send_sync(primary_proc, payload, NULL);
126 else
127 return pm_ipi_send(primary_proc, payload);
128}
129
130/**
131 * pm_req_wakeup() - PM call for processor to wake up selected processor
132 * or subsystem
133 * @target Node id of the processor or subsystem to wake up
134 * @ack Flag to specify whether acknowledge requested
135 * @set_address Resume address presence indicator
136 * 1 resume address specified, 0 otherwise
137 * @address Resume address
138 *
139 * This API function is either used to power up another APU core for SMP
140 * (by PSCI) or to power up an entirely different PU or subsystem, such
141 * as RPU0, RPU, or PL_CORE_xx. Resume address for the target PU will be
142 * automatically set by PMU.
143 *
144 * @return Returns status, either success or error+reason
145 */
146enum pm_ret_status pm_req_wakeup(enum pm_node_id target,
147 unsigned int set_address,
148 uintptr_t address,
149 enum pm_request_ack ack)
150{
151 uint32_t payload[PAYLOAD_ARG_CNT];
152 uint64_t encoded_address;
153 const struct pm_proc *proc = pm_get_proc_by_node(target);
154
155 /* invoke APU-specific code for waking up another APU core */
156 pm_client_wakeup(proc);
157
158 /* encode set Address into 1st bit of address */
159 encoded_address = address;
160 encoded_address |= !!set_address;
161
162 /* Send request to the PMU to perform the wake of the PU */
163 PM_PACK_PAYLOAD5(payload, PM_REQ_WAKEUP, target, encoded_address,
164 encoded_address >> 32, ack);
165
166 if (ack == REQ_ACK_BLOCKING)
167 return pm_ipi_send_sync(primary_proc, payload, NULL);
168 else
169 return pm_ipi_send(primary_proc, payload);
170}
171
172/**
173 * pm_force_powerdown() - PM call to request for another PU or subsystem to
174 * be powered down forcefully
175 * @target Node id of the targeted PU or subsystem
176 * @ack Flag to specify whether acknowledge is requested
177 *
178 * @return Returns status, either success or error+reason
179 */
180enum pm_ret_status pm_force_powerdown(enum pm_node_id target,
181 enum pm_request_ack ack)
182{
183 uint32_t payload[PAYLOAD_ARG_CNT];
184
185 /* Send request to the PMU */
186 PM_PACK_PAYLOAD3(payload, PM_FORCE_POWERDOWN, target, ack);
187
188 if (ack == REQ_ACK_BLOCKING)
189 return pm_ipi_send_sync(primary_proc, payload, NULL);
190 else
191 return pm_ipi_send(primary_proc, payload);
192}
193
194/**
195 * pm_abort_suspend() - PM call to announce that a prior suspend request
196 * is to be aborted.
197 * @reason Reason for the abort
198 *
199 * Calling PU expects the PMU to abort the initiated suspend procedure.
200 * This is a non-blocking call without any acknowledge.
201 *
202 * @return Returns status, either success or error+reason
203 */
204enum pm_ret_status pm_abort_suspend(enum pm_abort_reason reason)
205{
206 uint32_t payload[PAYLOAD_ARG_CNT];
207
208 /*
209 * Do client specific abort suspend operations
210 * (e.g. enable interrupts and clear powerdown request bit)
211 */
212 pm_client_abort_suspend();
213 /* Send request to the PMU */
214 /* TODO: allow passing the node ID of the affected CPU */
215 PM_PACK_PAYLOAD3(payload, PM_ABORT_SUSPEND, reason,
216 primary_proc->node_id);
217 return pm_ipi_send(primary_proc, payload);
218}
219
220/**
221 * pm_set_wakeup_source() - PM call to specify the wakeup source while suspended
222 * @target Node id of the targeted PU or subsystem
223 * @wkup_node Node id of the wakeup peripheral
224 * @enable Enable or disable the specified peripheral as wake source
225 *
226 * @return Returns status, either success or error+reason
227 */
228enum pm_ret_status pm_set_wakeup_source(enum pm_node_id target,
229 enum pm_node_id wkup_node,
230 unsigned int enable)
231{
232 uint32_t payload[PAYLOAD_ARG_CNT];
233
234 PM_PACK_PAYLOAD4(payload, PM_SET_WAKEUP_SOURCE, target, wkup_node,
235 enable);
236 return pm_ipi_send(primary_proc, payload);
237}
238
239/**
240 * pm_system_shutdown() - PM call to request a system shutdown or restart
241 * @restart Shutdown or restart? 0 for shutdown, 1 for restart
242 *
243 * @return Returns status, either success or error+reason
244 */
245enum pm_ret_status pm_system_shutdown(unsigned int restart)
246{
247 uint32_t payload[PAYLOAD_ARG_CNT];
248
249 PM_PACK_PAYLOAD2(payload, PM_SYSTEM_SHUTDOWN, restart);
250 return pm_ipi_send(primary_proc, payload);
251}
252
253/* APIs for managing PM slaves: */
254
255/**
256 * pm_req_node() - PM call to request a node with specific capabilities
257 * @nid Node id of the slave
258 * @capabilities Requested capabilities of the slave
259 * @qos Quality of service (not supported)
260 * @ack Flag to specify whether acknowledge is requested
261 *
262 * @return Returns status, either success or error+reason
263 */
264enum pm_ret_status pm_req_node(enum pm_node_id nid,
265 unsigned int capabilities,
266 unsigned int qos,
267 enum pm_request_ack ack)
268{
269 uint32_t payload[PAYLOAD_ARG_CNT];
270
271 PM_PACK_PAYLOAD5(payload, PM_REQ_NODE, nid, capabilities, qos, ack);
272
273 if (ack == REQ_ACK_BLOCKING)
274 return pm_ipi_send_sync(primary_proc, payload, NULL);
275 else
276 return pm_ipi_send(primary_proc, payload);
277}
278
279/**
280 * pm_set_requirement() - PM call to set requirement for PM slaves
281 * @nid Node id of the slave
282 * @capabilities Requested capabilities of the slave
283 * @qos Quality of service (not supported)
284 * @ack Flag to specify whether acknowledge is requested
285 *
286 * This API function is to be used for slaves a PU already has requested
287 *
288 * @return Returns status, either success or error+reason
289 */
290enum pm_ret_status pm_set_requirement(enum pm_node_id nid,
291 unsigned int capabilities,
292 unsigned int qos,
293 enum pm_request_ack ack)
294{
295 uint32_t payload[PAYLOAD_ARG_CNT];
296
297 PM_PACK_PAYLOAD5(payload, PM_SET_REQUIREMENT, nid, capabilities, qos,
298 ack);
299
300 if (ack == REQ_ACK_BLOCKING)
301 return pm_ipi_send_sync(primary_proc, payload, NULL);
302 else
303 return pm_ipi_send(primary_proc, payload);
304}
305
306/**
307 * pm_release_node() - PM call to release a node
308 * @nid Node id of the slave
309 *
310 * @return Returns status, either success or error+reason
311 */
312enum pm_ret_status pm_release_node(enum pm_node_id nid)
313{
314 uint32_t payload[PAYLOAD_ARG_CNT];
315
316 PM_PACK_PAYLOAD2(payload, PM_RELEASE_NODE, nid);
317 return pm_ipi_send(primary_proc, payload);
318}
319
320/**
321 * pm_set_max_latency() - PM call to set wakeup latency requirements
322 * @nid Node id of the slave
323 * @latency Requested maximum wakeup latency
324 *
325 * @return Returns status, either success or error+reason
326 */
327enum pm_ret_status pm_set_max_latency(enum pm_node_id nid,
328 unsigned int latency)
329{
330 uint32_t payload[PAYLOAD_ARG_CNT];
331
332 PM_PACK_PAYLOAD3(payload, PM_SET_MAX_LATENCY, nid, latency);
333 return pm_ipi_send(primary_proc, payload);
334}
335
336/* Miscellaneous API functions */
337
338/**
339 * pm_get_api_version() - Get version number of PMU PM firmware
340 * @version Returns 32-bit version number of PMU Power Management Firmware
341 *
342 * @return Returns status, either success or error+reason
343 */
344enum pm_ret_status pm_get_api_version(unsigned int *version)
345{
346 uint32_t payload[PAYLOAD_ARG_CNT];
347
348 /* Send request to the PMU */
349 PM_PACK_PAYLOAD1(payload, PM_GET_API_VERSION);
350 return pm_ipi_send_sync(primary_proc, payload, version);
351}
352
353/**
354 * pm_set_configuration() - PM call to set system configuration
355 * @phys_addr Physical 32-bit address of data structure in memory
356 *
357 * @return Returns status, either success or error+reason
358 */
359enum pm_ret_status pm_set_configuration(unsigned int phys_addr)
360{
361 return PM_RET_ERROR_NOTSUPPORTED;
362}
363
364/**
365 * pm_get_node_status() - PM call to request a node's current power state
366 * @nid Node id of the slave
367 *
368 * @return Returns status, either success or error+reason
369 */
370enum pm_ret_status pm_get_node_status(enum pm_node_id nid)
371{
372 /* TODO: Add power state argument!! */
373 uint32_t payload[PAYLOAD_ARG_CNT];
374
375 PM_PACK_PAYLOAD2(payload, PM_GET_NODE_STATUS, nid);
376 return pm_ipi_send(primary_proc, payload);
377}
378
379/**
380 * pm_register_notifier() - Register the PU to be notified of PM events
381 * @nid Node id of the slave
382 * @event The event to be notified about
383 * @wake Wake up on event
384 * @enable Enable or disable the notifier
385 *
386 * @return Returns status, either success or error+reason
387 */
388enum pm_ret_status pm_register_notifier(enum pm_node_id nid,
389 unsigned int event,
390 unsigned int wake,
391 unsigned int enable)
392{
393 return PM_RET_ERROR_NOTSUPPORTED;
394}
395
396/**
397 * pm_get_op_characteristic() - PM call to get a particular operating
398 * characteristic of a node
399 * @nid Node ID
400 * @type Operating characterstic type to be returned
401 *
402 * @return Returns status, either success or error+reason
403 */
404enum pm_ret_status pm_get_op_characteristic(enum pm_node_id nid,
405 enum pm_opchar_type type)
406{
407 return PM_RET_ERROR_NOTSUPPORTED;
408}
409
410/* Direct-Control API functions */
411
412/**
413 * pm_reset_assert() - Assert reset
414 * @reset Reset ID
415 * @assert Assert (1) or de-assert (0)
416 *
417 * @return Returns status, either success or error+reason
418 */
419enum pm_ret_status pm_reset_assert(unsigned int reset,
420 unsigned int assert)
421{
422 uint32_t payload[PAYLOAD_ARG_CNT];
423
424 /* Send request to the PMU */
425 PM_PACK_PAYLOAD3(payload, PM_RESET_ASSERT, reset, assert);
426 return pm_ipi_send(primary_proc, payload);
427}
428
429/**
430 * pm_reset_get_status() - Get current status of a reset line
431 * @reset Reset ID
432 * @reset_status Returns current status of selected reset line
433 *
434 * @return Returns status, either success or error+reason
435 */
436enum pm_ret_status pm_reset_get_status(unsigned int reset,
437 unsigned int *reset_status)
438{
439 uint32_t payload[PAYLOAD_ARG_CNT];
440
441 /* Send request to the PMU */
442 PM_PACK_PAYLOAD2(payload, PM_RESET_GET_STATUS, reset);
443 return pm_ipi_send_sync(primary_proc, payload, reset_status);
444}
445
446/**
447 * pm_mmio_write() - Perform write to protected mmio
448 * @address Address to write to
449 * @mask Mask to apply
450 * @value Value to write
451 *
452 * This function provides access to PM-related control registers
453 * that may not be directly accessible by a particular PU.
454 *
455 * @return Returns status, either success or error+reason
456 */
457enum pm_ret_status pm_mmio_write(uintptr_t address,
458 unsigned int mask,
459 unsigned int value)
460{
461 uint32_t payload[PAYLOAD_ARG_CNT];
462
463 /* Send request to the PMU */
464 PM_PACK_PAYLOAD4(payload, PM_MMIO_WRITE, address, mask, value);
465 return pm_ipi_send(primary_proc, payload);
466}
467
468/**
469 * pm_mmio_read() - Read value from protected mmio
470 * @address Address to write to
471 * @value Value to write
472 *
473 * This function provides access to PM-related control registers
474 * that may not be directly accessible by a particular PU.
475 *
476 * @return Returns status, either success or error+reason
477 */
478enum pm_ret_status pm_mmio_read(uintptr_t address, unsigned int *value)
479{
480 uint32_t payload[PAYLOAD_ARG_CNT];
481
482 /* Send request to the PMU */
483 PM_PACK_PAYLOAD2(payload, PM_MMIO_READ, address);
484 return pm_ipi_send_sync(primary_proc, payload, value);
485}