• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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