1 /*
2 * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <arch_helpers.h>
8 #include <bakery_lock.h>
9 #include <mmio.h>
10 #include <platform.h>
11 #include "../zynqmp_private.h"
12 #include "pm_ipi.h"
13
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
35 #define IPI_BUFFER_MAX_WORDS 8
36
37 #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)
47 #define IPI_APU_IXR_PMU_0_MASK (1 << 16)
48
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))
55 #define IPI_PMU_PM_INT_MASK (IPI_APU_IXR_PMU_0_MASK << PM_INT_NUM)
56 #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
62 DEFINE_BAKERY_LOCK(pm_secure_lock);
63
64 const 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 *
77 * Called from pm_setup initialization function
78 */
pm_ipi_init(void)79 int 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 */
pm_ipi_wait(const struct pm_proc * proc)94 static 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 */
pm_ipi_send_common(const struct pm_proc * proc,uint32_t payload[PAYLOAD_ARG_CNT])119 static 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 */
pm_ipi_send(const struct pm_proc * proc,uint32_t payload[PAYLOAD_ARG_CNT])150 enum 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
168 * @value Used to return value from IPI buffer element (optional)
169 * @count Number of values to return in @value
170 *
171 * @return Returns status, either success or error+reason
172 */
pm_ipi_buff_read(const struct pm_proc * proc,unsigned int * value,size_t count)173 static enum pm_ret_status pm_ipi_buff_read(const struct pm_proc *proc,
174 unsigned int *value, size_t count)
175 {
176 size_t i;
177 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 */
190 for (i = 1; i <= count; i++) {
191 *value = mmio_read_32(buffer_base + (i * PAYLOAD_ARG_SIZE));
192 value++;
193 }
194
195 return mmio_read_32(buffer_base);
196 }
197
198 /**
199 * 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 */
pm_ipi_buff_read_callb(unsigned int * value,size_t count)205 void 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 /**
222 * 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
225 * @value Used to return value from IPI buffer element (optional)
226 * @count Number of values to return in @value
227 *
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 */
pm_ipi_send_sync(const struct pm_proc * proc,uint32_t payload[PAYLOAD_ARG_CNT],unsigned int * value,size_t count)233 enum pm_ret_status pm_ipi_send_sync(const struct pm_proc *proc,
234 uint32_t payload[PAYLOAD_ARG_CNT],
235 unsigned int *value, size_t count)
236 {
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
245 ret = pm_ipi_buff_read(proc, value, count);
246
247 unlock:
248 bakery_lock_release(&pm_secure_lock);
249
250 return ret;
251 }
252
pm_ipi_irq_enable(void)253 void pm_ipi_irq_enable(void)
254 {
255 mmio_write_32(IPI_APU_IER, IPI_APU_IXR_PMU_0_MASK);
256 }
257
pm_ipi_irq_disable(void)258 void pm_ipi_irq_disable(void)
259 {
260 mmio_write_32(IPI_APU_IDR, IPI_APU_IXR_PMU_0_MASK);
261 }
262
pm_ipi_irq_clear(void)263 void pm_ipi_irq_clear(void)
264 {
265 mmio_write_32(IPI_APU_ISR, IPI_APU_IXR_PMU_0_MASK);
266 }
267