1 /*
2 * Copyright (c) 2013-2015, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 /*
8 * ZynqMP system level PM-API functions and communication with PMU via
9 * IPI interrupts
10 */
11
12 #include <arch_helpers.h>
13 #include <platform.h>
14 #include "pm_api_sys.h"
15 #include "pm_client.h"
16 #include "pm_common.h"
17 #include "pm_ipi.h"
18
19 /**
20 * Assigning of argument values into array elements.
21 */
22 #define PM_PACK_PAYLOAD1(pl, arg0) { \
23 pl[0] = (uint32_t)(arg0); \
24 }
25
26 #define PM_PACK_PAYLOAD2(pl, arg0, arg1) { \
27 pl[1] = (uint32_t)(arg1); \
28 PM_PACK_PAYLOAD1(pl, arg0); \
29 }
30
31 #define PM_PACK_PAYLOAD3(pl, arg0, arg1, arg2) { \
32 pl[2] = (uint32_t)(arg2); \
33 PM_PACK_PAYLOAD2(pl, arg0, arg1); \
34 }
35
36 #define PM_PACK_PAYLOAD4(pl, arg0, arg1, arg2, arg3) { \
37 pl[3] = (uint32_t)(arg3); \
38 PM_PACK_PAYLOAD3(pl, arg0, arg1, arg2); \
39 }
40
41 #define PM_PACK_PAYLOAD5(pl, arg0, arg1, arg2, arg3, arg4) { \
42 pl[4] = (uint32_t)(arg4); \
43 PM_PACK_PAYLOAD4(pl, arg0, arg1, arg2, arg3); \
44 }
45
46 #define PM_PACK_PAYLOAD6(pl, arg0, arg1, arg2, arg3, arg4, arg5) { \
47 pl[5] = (uint32_t)(arg5); \
48 PM_PACK_PAYLOAD5(pl, arg0, arg1, arg2, arg3, arg4); \
49 }
50
51 /**
52 * pm_self_suspend() - PM call for processor to suspend itself
53 * @nid Node id of the processor or subsystem
54 * @latency Requested maximum wakeup latency (not supported)
55 * @state Requested state
56 * @address Resume address
57 *
58 * This is a blocking call, it will return only once PMU has responded.
59 * On a wakeup, resume address will be automatically set by PMU.
60 *
61 * @return Returns status, either success or error+reason
62 */
pm_self_suspend(enum pm_node_id nid,unsigned int latency,unsigned int state,uintptr_t address)63 enum pm_ret_status pm_self_suspend(enum pm_node_id nid,
64 unsigned int latency,
65 unsigned int state,
66 uintptr_t address)
67 {
68 uint32_t payload[PAYLOAD_ARG_CNT];
69 unsigned int cpuid = plat_my_core_pos();
70 const struct pm_proc *proc = pm_get_proc(cpuid);
71
72 /*
73 * Do client specific suspend operations
74 * (e.g. set powerdown request bit)
75 */
76 pm_client_suspend(proc, state);
77 /* Send request to the PMU */
78 PM_PACK_PAYLOAD6(payload, PM_SELF_SUSPEND, proc->node_id, latency,
79 state, address, (address >> 32));
80 return pm_ipi_send_sync(proc, payload, NULL, 0);
81 }
82
83 /**
84 * pm_req_suspend() - PM call to request for another PU or subsystem to
85 * be suspended gracefully.
86 * @target Node id of the targeted PU or subsystem
87 * @ack Flag to specify whether acknowledge is requested
88 * @latency Requested wakeup latency (not supported)
89 * @state Requested state (not supported)
90 *
91 * @return Returns status, either success or error+reason
92 */
pm_req_suspend(enum pm_node_id target,enum pm_request_ack ack,unsigned int latency,unsigned int state)93 enum pm_ret_status pm_req_suspend(enum pm_node_id target,
94 enum pm_request_ack ack,
95 unsigned int latency, unsigned int state)
96 {
97 uint32_t payload[PAYLOAD_ARG_CNT];
98
99 /* Send request to the PMU */
100 PM_PACK_PAYLOAD5(payload, PM_REQ_SUSPEND, target, ack, latency, state);
101 if (ack == REQ_ACK_BLOCKING)
102 return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
103 else
104 return pm_ipi_send(primary_proc, payload);
105 }
106
107 /**
108 * pm_req_wakeup() - PM call for processor to wake up selected processor
109 * or subsystem
110 * @target Node id of the processor or subsystem to wake up
111 * @ack Flag to specify whether acknowledge requested
112 * @set_address Resume address presence indicator
113 * 1 resume address specified, 0 otherwise
114 * @address Resume address
115 *
116 * This API function is either used to power up another APU core for SMP
117 * (by PSCI) or to power up an entirely different PU or subsystem, such
118 * as RPU0, RPU, or PL_CORE_xx. Resume address for the target PU will be
119 * automatically set by PMU.
120 *
121 * @return Returns status, either success or error+reason
122 */
pm_req_wakeup(enum pm_node_id target,unsigned int set_address,uintptr_t address,enum pm_request_ack ack)123 enum pm_ret_status pm_req_wakeup(enum pm_node_id target,
124 unsigned int set_address,
125 uintptr_t address,
126 enum pm_request_ack ack)
127 {
128 uint32_t payload[PAYLOAD_ARG_CNT];
129 uint64_t encoded_address;
130 const struct pm_proc *proc = pm_get_proc_by_node(target);
131
132 /* invoke APU-specific code for waking up another APU core */
133 pm_client_wakeup(proc);
134
135 /* encode set Address into 1st bit of address */
136 encoded_address = address;
137 encoded_address |= !!set_address;
138
139 /* Send request to the PMU to perform the wake of the PU */
140 PM_PACK_PAYLOAD5(payload, PM_REQ_WAKEUP, target, encoded_address,
141 encoded_address >> 32, ack);
142
143 if (ack == REQ_ACK_BLOCKING)
144 return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
145 else
146 return pm_ipi_send(primary_proc, payload);
147 }
148
149 /**
150 * pm_force_powerdown() - PM call to request for another PU or subsystem to
151 * be powered down forcefully
152 * @target Node id of the targeted PU or subsystem
153 * @ack Flag to specify whether acknowledge is requested
154 *
155 * @return Returns status, either success or error+reason
156 */
pm_force_powerdown(enum pm_node_id target,enum pm_request_ack ack)157 enum pm_ret_status pm_force_powerdown(enum pm_node_id target,
158 enum pm_request_ack ack)
159 {
160 uint32_t payload[PAYLOAD_ARG_CNT];
161
162 /* Send request to the PMU */
163 PM_PACK_PAYLOAD3(payload, PM_FORCE_POWERDOWN, target, ack);
164
165 if (ack == REQ_ACK_BLOCKING)
166 return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
167 else
168 return pm_ipi_send(primary_proc, payload);
169 }
170
171 /**
172 * pm_abort_suspend() - PM call to announce that a prior suspend request
173 * is to be aborted.
174 * @reason Reason for the abort
175 *
176 * Calling PU expects the PMU to abort the initiated suspend procedure.
177 * This is a non-blocking call without any acknowledge.
178 *
179 * @return Returns status, either success or error+reason
180 */
pm_abort_suspend(enum pm_abort_reason reason)181 enum pm_ret_status pm_abort_suspend(enum pm_abort_reason reason)
182 {
183 uint32_t payload[PAYLOAD_ARG_CNT];
184
185 /*
186 * Do client specific abort suspend operations
187 * (e.g. enable interrupts and clear powerdown request bit)
188 */
189 pm_client_abort_suspend();
190 /* Send request to the PMU */
191 /* TODO: allow passing the node ID of the affected CPU */
192 PM_PACK_PAYLOAD3(payload, PM_ABORT_SUSPEND, reason,
193 primary_proc->node_id);
194 return pm_ipi_send(primary_proc, payload);
195 }
196
197 /**
198 * pm_set_wakeup_source() - PM call to specify the wakeup source while suspended
199 * @target Node id of the targeted PU or subsystem
200 * @wkup_node Node id of the wakeup peripheral
201 * @enable Enable or disable the specified peripheral as wake source
202 *
203 * @return Returns status, either success or error+reason
204 */
pm_set_wakeup_source(enum pm_node_id target,enum pm_node_id wkup_node,unsigned int enable)205 enum pm_ret_status pm_set_wakeup_source(enum pm_node_id target,
206 enum pm_node_id wkup_node,
207 unsigned int enable)
208 {
209 uint32_t payload[PAYLOAD_ARG_CNT];
210
211 PM_PACK_PAYLOAD4(payload, PM_SET_WAKEUP_SOURCE, target, wkup_node,
212 enable);
213 return pm_ipi_send(primary_proc, payload);
214 }
215
216 /**
217 * pm_system_shutdown() - PM call to request a system shutdown or restart
218 * @restart Shutdown or restart? 0 for shutdown, 1 for restart
219 *
220 * @return Returns status, either success or error+reason
221 */
pm_system_shutdown(unsigned int type,unsigned int subtype)222 enum pm_ret_status pm_system_shutdown(unsigned int type, unsigned int subtype)
223 {
224 uint32_t payload[PAYLOAD_ARG_CNT];
225
226 PM_PACK_PAYLOAD3(payload, PM_SYSTEM_SHUTDOWN, type, subtype);
227 return pm_ipi_send(primary_proc, payload);
228 }
229
230 /* APIs for managing PM slaves: */
231
232 /**
233 * pm_req_node() - PM call to request a node with specific capabilities
234 * @nid Node id of the slave
235 * @capabilities Requested capabilities of the slave
236 * @qos Quality of service (not supported)
237 * @ack Flag to specify whether acknowledge is requested
238 *
239 * @return Returns status, either success or error+reason
240 */
pm_req_node(enum pm_node_id nid,unsigned int capabilities,unsigned int qos,enum pm_request_ack ack)241 enum pm_ret_status pm_req_node(enum pm_node_id nid,
242 unsigned int capabilities,
243 unsigned int qos,
244 enum pm_request_ack ack)
245 {
246 uint32_t payload[PAYLOAD_ARG_CNT];
247
248 PM_PACK_PAYLOAD5(payload, PM_REQ_NODE, nid, capabilities, qos, ack);
249
250 if (ack == REQ_ACK_BLOCKING)
251 return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
252 else
253 return pm_ipi_send(primary_proc, payload);
254 }
255
256 /**
257 * pm_set_requirement() - PM call to set requirement for PM slaves
258 * @nid Node id of the slave
259 * @capabilities Requested capabilities of the slave
260 * @qos Quality of service (not supported)
261 * @ack Flag to specify whether acknowledge is requested
262 *
263 * This API function is to be used for slaves a PU already has requested
264 *
265 * @return Returns status, either success or error+reason
266 */
pm_set_requirement(enum pm_node_id nid,unsigned int capabilities,unsigned int qos,enum pm_request_ack ack)267 enum pm_ret_status pm_set_requirement(enum pm_node_id nid,
268 unsigned int capabilities,
269 unsigned int qos,
270 enum pm_request_ack ack)
271 {
272 uint32_t payload[PAYLOAD_ARG_CNT];
273
274 PM_PACK_PAYLOAD5(payload, PM_SET_REQUIREMENT, nid, capabilities, qos,
275 ack);
276
277 if (ack == REQ_ACK_BLOCKING)
278 return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
279 else
280 return pm_ipi_send(primary_proc, payload);
281 }
282
283 /**
284 * pm_release_node() - PM call to release a node
285 * @nid Node id of the slave
286 *
287 * @return Returns status, either success or error+reason
288 */
pm_release_node(enum pm_node_id nid)289 enum pm_ret_status pm_release_node(enum pm_node_id nid)
290 {
291 uint32_t payload[PAYLOAD_ARG_CNT];
292
293 PM_PACK_PAYLOAD2(payload, PM_RELEASE_NODE, nid);
294 return pm_ipi_send(primary_proc, payload);
295 }
296
297 /**
298 * pm_set_max_latency() - PM call to set wakeup latency requirements
299 * @nid Node id of the slave
300 * @latency Requested maximum wakeup latency
301 *
302 * @return Returns status, either success or error+reason
303 */
pm_set_max_latency(enum pm_node_id nid,unsigned int latency)304 enum pm_ret_status pm_set_max_latency(enum pm_node_id nid,
305 unsigned int latency)
306 {
307 uint32_t payload[PAYLOAD_ARG_CNT];
308
309 PM_PACK_PAYLOAD3(payload, PM_SET_MAX_LATENCY, nid, latency);
310 return pm_ipi_send(primary_proc, payload);
311 }
312
313 /* Miscellaneous API functions */
314
315 /**
316 * pm_get_api_version() - Get version number of PMU PM firmware
317 * @version Returns 32-bit version number of PMU Power Management Firmware
318 *
319 * @return Returns status, either success or error+reason
320 */
pm_get_api_version(unsigned int * version)321 enum pm_ret_status pm_get_api_version(unsigned int *version)
322 {
323 uint32_t payload[PAYLOAD_ARG_CNT];
324
325 /* Send request to the PMU */
326 PM_PACK_PAYLOAD1(payload, PM_GET_API_VERSION);
327 return pm_ipi_send_sync(primary_proc, payload, version, 1);
328 }
329
330 /**
331 * pm_set_configuration() - PM call to set system configuration
332 * @phys_addr Physical 32-bit address of data structure in memory
333 *
334 * @return Returns status, either success or error+reason
335 */
pm_set_configuration(unsigned int phys_addr)336 enum pm_ret_status pm_set_configuration(unsigned int phys_addr)
337 {
338 return PM_RET_ERROR_NOTSUPPORTED;
339 }
340
341 /**
342 * pm_get_node_status() - PM call to request a node's current power state
343 * @nid Node id of the slave
344 *
345 * @return Returns status, either success or error+reason
346 */
pm_get_node_status(enum pm_node_id nid)347 enum pm_ret_status pm_get_node_status(enum pm_node_id nid)
348 {
349 /* TODO: Add power state argument!! */
350 uint32_t payload[PAYLOAD_ARG_CNT];
351
352 PM_PACK_PAYLOAD2(payload, PM_GET_NODE_STATUS, nid);
353 return pm_ipi_send(primary_proc, payload);
354 }
355
356 /**
357 * pm_register_notifier() - Register the PU to be notified of PM events
358 * @nid Node id of the slave
359 * @event The event to be notified about
360 * @wake Wake up on event
361 * @enable Enable or disable the notifier
362 *
363 * @return Returns status, either success or error+reason
364 */
pm_register_notifier(enum pm_node_id nid,unsigned int event,unsigned int wake,unsigned int enable)365 enum pm_ret_status pm_register_notifier(enum pm_node_id nid,
366 unsigned int event,
367 unsigned int wake,
368 unsigned int enable)
369 {
370 uint32_t payload[PAYLOAD_ARG_CNT];
371
372 PM_PACK_PAYLOAD5(payload, PM_REGISTER_NOTIFIER,
373 nid, event, wake, enable);
374
375 return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
376 }
377
378 /**
379 * pm_get_op_characteristic() - PM call to request operating characteristics
380 * of a node
381 * @nid Node id of the slave
382 * @type Type of the operating characteristic
383 * (power, temperature and latency)
384 * @result Returns the operating characteristic for the requested node,
385 * specified by the type
386 *
387 * @return Returns status, either success or error+reason
388 */
pm_get_op_characteristic(enum pm_node_id nid,enum pm_opchar_type type,uint32_t * result)389 enum pm_ret_status pm_get_op_characteristic(enum pm_node_id nid,
390 enum pm_opchar_type type,
391 uint32_t *result)
392 {
393 uint32_t payload[PAYLOAD_ARG_CNT];
394
395 /* Send request to the PMU */
396 PM_PACK_PAYLOAD3(payload, PM_GET_OP_CHARACTERISTIC, nid, type);
397 return pm_ipi_send_sync(primary_proc, payload, result, 1);
398 }
399
400 /* Direct-Control API functions */
401
402 /**
403 * pm_reset_assert() - Assert reset
404 * @reset Reset ID
405 * @assert Assert (1) or de-assert (0)
406 *
407 * @return Returns status, either success or error+reason
408 */
pm_reset_assert(unsigned int reset,unsigned int assert)409 enum pm_ret_status pm_reset_assert(unsigned int reset,
410 unsigned int assert)
411 {
412 uint32_t payload[PAYLOAD_ARG_CNT];
413
414 /* Send request to the PMU */
415 PM_PACK_PAYLOAD3(payload, PM_RESET_ASSERT, reset, assert);
416 return pm_ipi_send(primary_proc, payload);
417 }
418
419 /**
420 * pm_reset_get_status() - Get current status of a reset line
421 * @reset Reset ID
422 * @reset_status Returns current status of selected reset line
423 *
424 * @return Returns status, either success or error+reason
425 */
pm_reset_get_status(unsigned int reset,unsigned int * reset_status)426 enum pm_ret_status pm_reset_get_status(unsigned int reset,
427 unsigned int *reset_status)
428 {
429 uint32_t payload[PAYLOAD_ARG_CNT];
430
431 /* Send request to the PMU */
432 PM_PACK_PAYLOAD2(payload, PM_RESET_GET_STATUS, reset);
433 return pm_ipi_send_sync(primary_proc, payload, reset_status, 1);
434 }
435
436 /**
437 * pm_mmio_write() - Perform write to protected mmio
438 * @address Address to write to
439 * @mask Mask to apply
440 * @value Value to write
441 *
442 * This function provides access to PM-related control registers
443 * that may not be directly accessible by a particular PU.
444 *
445 * @return Returns status, either success or error+reason
446 */
pm_mmio_write(uintptr_t address,unsigned int mask,unsigned int value)447 enum pm_ret_status pm_mmio_write(uintptr_t address,
448 unsigned int mask,
449 unsigned int value)
450 {
451 uint32_t payload[PAYLOAD_ARG_CNT];
452
453 /* Send request to the PMU */
454 PM_PACK_PAYLOAD4(payload, PM_MMIO_WRITE, address, mask, value);
455 return pm_ipi_send_sync(primary_proc, payload, NULL, 0);
456 }
457
458 /**
459 * pm_mmio_read() - Read value from protected mmio
460 * @address Address to write to
461 * @value Value to write
462 *
463 * This function provides access to PM-related control registers
464 * that may not be directly accessible by a particular PU.
465 *
466 * @return Returns status, either success or error+reason
467 */
pm_mmio_read(uintptr_t address,unsigned int * value)468 enum pm_ret_status pm_mmio_read(uintptr_t address, unsigned int *value)
469 {
470 uint32_t payload[PAYLOAD_ARG_CNT];
471
472 /* Send request to the PMU */
473 PM_PACK_PAYLOAD2(payload, PM_MMIO_READ, address);
474 return pm_ipi_send_sync(primary_proc, payload, value, 1);
475 }
476
477 /**
478 * pm_fpga_load() - Load the bitstream into the PL.
479 *
480 * This function provides access to the xilfpga library to load
481 * the Bit-stream into PL.
482 *
483 * address_low: lower 32-bit Linear memory space address
484 *
485 * address_high: higher 32-bit Linear memory space address
486 *
487 * size: Number of 32bit words
488 *
489 * @return Returns status, either success or error+reason
490 */
pm_fpga_load(uint32_t address_low,uint32_t address_high,uint32_t size,uint32_t flags)491 enum pm_ret_status pm_fpga_load(uint32_t address_low,
492 uint32_t address_high,
493 uint32_t size,
494 uint32_t flags)
495 {
496 uint32_t payload[PAYLOAD_ARG_CNT];
497
498 /* Send request to the PMU */
499 PM_PACK_PAYLOAD5(payload, PM_FPGA_LOAD, address_high, address_low,
500 size, flags);
501 return pm_ipi_send(primary_proc, payload);
502 }
503
504 /**
505 * pm_fpga_get_status() - Read value from fpga status register
506 * @value Value to read
507 *
508 * This function provides access to the xilfpga library to get
509 * the fpga status
510 * @return Returns status, either success or error+reason
511 */
pm_fpga_get_status(unsigned int * value)512 enum pm_ret_status pm_fpga_get_status(unsigned int *value)
513 {
514 uint32_t payload[PAYLOAD_ARG_CNT];
515
516 /* Send request to the PMU */
517 PM_PACK_PAYLOAD1(payload, PM_FPGA_GET_STATUS);
518 return pm_ipi_send_sync(primary_proc, payload, value, 1);
519 }
520
521 /**
522 * pm_get_chipid() - Read silicon ID registers
523 * @value Buffer for return values. Must be large enough
524 * to hold 8 bytes.
525 *
526 * @return Returns silicon ID registers
527 */
pm_get_chipid(uint32_t * value)528 enum pm_ret_status pm_get_chipid(uint32_t *value)
529 {
530 uint32_t payload[PAYLOAD_ARG_CNT];
531
532 /* Send request to the PMU */
533 PM_PACK_PAYLOAD1(payload, PM_GET_CHIPID);
534 return pm_ipi_send_sync(primary_proc, payload, value, 2);
535 }
536
537 /**
538 * pm_get_callbackdata() - Read from IPI response buffer
539 * @data - array of PAYLOAD_ARG_CNT elements
540 *
541 * Read value from ipi buffer response buffer.
542 */
pm_get_callbackdata(uint32_t * data,size_t count)543 void pm_get_callbackdata(uint32_t *data, size_t count)
544 {
545
546 pm_ipi_buff_read_callb(data, count);
547 pm_ipi_irq_clear();
548 }
549