blob: fdffde772c44574d4563683adf5c6969a1ecdd32 [file] [log] [blame]
Soren Brinkmann76fcae32016-03-06 20:16:27 -08001/*
2 * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
3 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Soren Brinkmann76fcae32016-03-06 20:16:27 -08005 */
6
Isla Mitchelle3631462017-07-14 10:46:32 +01007#include <arch_helpers.h>
Soren Brinkmann76fcae32016-03-06 20:16:27 -08008#include <bakery_lock.h>
9#include <mmio.h>
10#include <platform.h>
Soren Brinkmann76fcae32016-03-06 20:16:27 -080011#include "../zynqmp_private.h"
Isla Mitchelle3631462017-07-14 10:46:32 +010012#include "pm_ipi.h"
Soren Brinkmann76fcae32016-03-06 20:16:27 -080013
14/* IPI message buffers */
15#define IPI_BUFFER_BASEADDR 0xFF990000U
16
17#define IPI_BUFFER_RPU_0_BASE (IPI_BUFFER_BASEADDR + 0x0U)
18#define IPI_BUFFER_RPU_1_BASE (IPI_BUFFER_BASEADDR + 0x200U)
19#define IPI_BUFFER_APU_BASE (IPI_BUFFER_BASEADDR + 0x400U)
20#define IPI_BUFFER_PL_0_BASE (IPI_BUFFER_BASEADDR + 0x600U)
21#define IPI_BUFFER_PL_1_BASE (IPI_BUFFER_BASEADDR + 0x800U)
22#define IPI_BUFFER_PL_2_BASE (IPI_BUFFER_BASEADDR + 0xA00U)
23#define IPI_BUFFER_PL_3_BASE (IPI_BUFFER_BASEADDR + 0xC00U)
24#define IPI_BUFFER_PMU_BASE (IPI_BUFFER_BASEADDR + 0xE00U)
25
26#define IPI_BUFFER_TARGET_RPU_0_OFFSET 0x0U
27#define IPI_BUFFER_TARGET_RPU_1_OFFSET 0x40U
28#define IPI_BUFFER_TARGET_APU_OFFSET 0x80U
29#define IPI_BUFFER_TARGET_PL_0_OFFSET 0xC0U
30#define IPI_BUFFER_TARGET_PL_1_OFFSET 0x100U
31#define IPI_BUFFER_TARGET_PL_2_OFFSET 0x140U
32#define IPI_BUFFER_TARGET_PL_3_OFFSET 0x180U
33#define IPI_BUFFER_TARGET_PMU_OFFSET 0x1C0U
34
Soren Brinkmann84f0af42016-09-30 14:24:25 -070035#define IPI_BUFFER_MAX_WORDS 8
36
Soren Brinkmann76fcae32016-03-06 20:16:27 -080037#define IPI_BUFFER_REQ_OFFSET 0x0U
38#define IPI_BUFFER_RESP_OFFSET 0x20U
39
40/* IPI Base Address */
41#define IPI_BASEADDR 0XFF300000
42
43/* APU's IPI registers */
44#define IPI_APU_ISR (IPI_BASEADDR + 0X00000010)
45#define IPI_APU_IER (IPI_BASEADDR + 0X00000018)
46#define IPI_APU_IDR (IPI_BASEADDR + 0X0000001C)
Soren Brinkmannacff8a42016-04-11 15:30:56 -070047#define IPI_APU_IXR_PMU_0_MASK (1 << 16)
Soren Brinkmann76fcae32016-03-06 20:16:27 -080048
49#define IPI_TRIG_OFFSET 0
50#define IPI_OBS_OFFSET 4
51
52/* Power Management IPI interrupt number */
53#define PM_INT_NUM 0
54#define IPI_PMU_PM_INT_BASE (IPI_PMU_0_TRIG + (PM_INT_NUM * 0x1000))
Soren Brinkmannacff8a42016-04-11 15:30:56 -070055#define IPI_PMU_PM_INT_MASK (IPI_APU_IXR_PMU_0_MASK << PM_INT_NUM)
Soren Brinkmann76fcae32016-03-06 20:16:27 -080056#if (PM_INT_NUM < 0 || PM_INT_NUM > 3)
57 #error PM_INT_NUM value out of range
58#endif
59
60#define IPI_APU_MASK 1U
61
Stefan Krsmanovicc5e9f662016-05-20 15:51:08 +020062DEFINE_BAKERY_LOCK(pm_secure_lock);
Soren Brinkmann76fcae32016-03-06 20:16:27 -080063
64const struct pm_ipi apu_ipi = {
65 .mask = IPI_APU_MASK,
66 .base = IPI_BASEADDR,
67 .buffer_base = IPI_BUFFER_APU_BASE,
68};
69
70/**
71 * pm_ipi_init() - Initialize IPI peripheral for communication with PMU
72 *
73 * @return On success, the initialization function must return 0.
74 * Any other return value will cause the framework to ignore
75 * the service
76 *
Soren Brinkmann76fcae32016-03-06 20:16:27 -080077 * Called from pm_setup initialization function
78 */
79int pm_ipi_init(void)
80{
81 bakery_lock_init(&pm_secure_lock);
82
83 /* IPI Interrupts Clear & Disable */
84 mmio_write_32(IPI_APU_ISR, 0xffffffff);
85 mmio_write_32(IPI_APU_IDR, 0xffffffff);
86
87 return 0;
88}
89
90/**
91 * pm_ipi_wait() - wait for pmu to handle request
92 * @proc proc which is waiting for PMU to handle request
93 */
94static enum pm_ret_status pm_ipi_wait(const struct pm_proc *proc)
95{
96 int status;
97
98 /* Wait until previous interrupt is handled by PMU */
99 do {
100 status = mmio_read_32(proc->ipi->base + IPI_OBS_OFFSET) &
101 IPI_PMU_PM_INT_MASK;
102 /* TODO: 1) Use timer to add delay between read attempts */
103 /* TODO: 2) Return PM_RET_ERR_TIMEOUT if this times out */
104 } while (status);
105
106 return PM_RET_SUCCESS;
107}
108
109/**
110 * pm_ipi_send_common() - Sends IPI request to the PMU
111 * @proc Pointer to the processor who is initiating request
112 * @payload API id and call arguments to be written in IPI buffer
113 *
114 * Send an IPI request to the power controller. Caller needs to hold
115 * the 'pm_secure_lock' lock.
116 *
117 * @return Returns status, either success or error+reason
118 */
119static enum pm_ret_status pm_ipi_send_common(const struct pm_proc *proc,
120 uint32_t payload[PAYLOAD_ARG_CNT])
121{
122 unsigned int offset = 0;
123 uintptr_t buffer_base = proc->ipi->buffer_base +
124 IPI_BUFFER_TARGET_PMU_OFFSET +
125 IPI_BUFFER_REQ_OFFSET;
126
127 /* Wait until previous interrupt is handled by PMU */
128 pm_ipi_wait(proc);
129
130 /* Write payload into IPI buffer */
131 for (size_t i = 0; i < PAYLOAD_ARG_CNT; i++) {
132 mmio_write_32(buffer_base + offset, payload[i]);
133 offset += PAYLOAD_ARG_SIZE;
134 }
135 /* Generate IPI to PMU */
136 mmio_write_32(proc->ipi->base + IPI_TRIG_OFFSET, IPI_PMU_PM_INT_MASK);
137
138 return PM_RET_SUCCESS;
139}
140
141/**
142 * pm_ipi_send() - Sends IPI request to the PMU
143 * @proc Pointer to the processor who is initiating request
144 * @payload API id and call arguments to be written in IPI buffer
145 *
146 * Send an IPI request to the power controller.
147 *
148 * @return Returns status, either success or error+reason
149 */
150enum pm_ret_status pm_ipi_send(const struct pm_proc *proc,
151 uint32_t payload[PAYLOAD_ARG_CNT])
152{
153 enum pm_ret_status ret;
154
155 bakery_lock_get(&pm_secure_lock);
156
157 ret = pm_ipi_send_common(proc, payload);
158
159 bakery_lock_release(&pm_secure_lock);
160
161 return ret;
162}
163
164
165/**
166 * pm_ipi_buff_read() - Reads IPI response after PMU has handled interrupt
167 * @proc Pointer to the processor who is waiting and reading response
Soren Brinkmannd6c9e032016-09-22 11:35:47 -0700168 * @value Used to return value from IPI buffer element (optional)
169 * @count Number of values to return in @value
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800170 *
171 * @return Returns status, either success or error+reason
172 */
173static enum pm_ret_status pm_ipi_buff_read(const struct pm_proc *proc,
Soren Brinkmannd6c9e032016-09-22 11:35:47 -0700174 unsigned int *value, size_t count)
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800175{
Soren Brinkmannd6c9e032016-09-22 11:35:47 -0700176 size_t i;
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800177 uintptr_t buffer_base = proc->ipi->buffer_base +
178 IPI_BUFFER_TARGET_PMU_OFFSET +
179 IPI_BUFFER_RESP_OFFSET;
180
181 pm_ipi_wait(proc);
182
183 /*
184 * Read response from IPI buffer
185 * buf-0: success or error+reason
186 * buf-1: value
187 * buf-2: unused
188 * buf-3: unused
189 */
Soren Brinkmannd6c9e032016-09-22 11:35:47 -0700190 for (i = 1; i <= count; i++) {
191 *value = mmio_read_32(buffer_base + (i * PAYLOAD_ARG_SIZE));
192 value++;
193 }
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800194
195 return mmio_read_32(buffer_base);
196}
197
198/**
Soren Brinkmann84f0af42016-09-30 14:24:25 -0700199 * pm_ipi_buff_read_callb() - Reads IPI response after PMU has handled interrupt
200 * @value Used to return value from IPI buffer element (optional)
201 * @count Number of values to return in @value
202 *
203 * @return Returns status, either success or error+reason
204 */
205void pm_ipi_buff_read_callb(unsigned int *value, size_t count)
206{
207 size_t i;
208 uintptr_t buffer_base = IPI_BUFFER_PMU_BASE +
209 IPI_BUFFER_TARGET_APU_OFFSET +
210 IPI_BUFFER_REQ_OFFSET;
211
212 if (count > IPI_BUFFER_MAX_WORDS)
213 count = IPI_BUFFER_MAX_WORDS;
214
215 for (i = 0; i <= count; i++) {
216 *value = mmio_read_32(buffer_base + (i * PAYLOAD_ARG_SIZE));
217 value++;
218 }
219}
220
221/**
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800222 * pm_ipi_send_sync() - Sends IPI request to the PMU
223 * @proc Pointer to the processor who is initiating request
224 * @payload API id and call arguments to be written in IPI buffer
Soren Brinkmannd6c9e032016-09-22 11:35:47 -0700225 * @value Used to return value from IPI buffer element (optional)
226 * @count Number of values to return in @value
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800227 *
228 * Send an IPI request to the power controller and wait for it to be handled.
229 *
230 * @return Returns status, either success or error+reason and, optionally,
231 * @value
232 */
233enum pm_ret_status pm_ipi_send_sync(const struct pm_proc *proc,
234 uint32_t payload[PAYLOAD_ARG_CNT],
Soren Brinkmannd6c9e032016-09-22 11:35:47 -0700235 unsigned int *value, size_t count)
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800236{
237 enum pm_ret_status ret;
238
239 bakery_lock_get(&pm_secure_lock);
240
241 ret = pm_ipi_send_common(proc, payload);
242 if (ret != PM_RET_SUCCESS)
243 goto unlock;
244
Soren Brinkmannd6c9e032016-09-22 11:35:47 -0700245 ret = pm_ipi_buff_read(proc, value, count);
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800246
247unlock:
248 bakery_lock_release(&pm_secure_lock);
249
250 return ret;
251}
Soren Brinkmanna1b0a902016-09-30 11:30:21 -0700252
253void pm_ipi_irq_enable(void)
254{
255 mmio_write_32(IPI_APU_IER, IPI_APU_IXR_PMU_0_MASK);
256}
257
258void pm_ipi_irq_disable(void)
259{
260 mmio_write_32(IPI_APU_IDR, IPI_APU_IXR_PMU_0_MASK);
261}
Soren Brinkmann84f0af42016-09-30 14:24:25 -0700262
263void pm_ipi_irq_clear(void)
264{
265 mmio_write_32(IPI_APU_ISR, IPI_APU_IXR_PMU_0_MASK);
266}