1 /*
2 * Copyright (c) 2014, Mentor Graphics Corporation
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. 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 * 3. Neither the name of Mentor Graphics Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
30 /**************************************************************************
31 * FILE NAME
32 *
33 * platform.c
34 *
35 * DESCRIPTION
36 *
37 * This file is the Implementation of IPC hardware layer interface
38 * for Xilinx Zynq ZC702EVK platform.
39 *
40 **************************************************************************/
42 #include <string.h>
43 #include <errno.h>
44 #include "openamp/hil.h"
45 #include "openamp/remoteproc_plat.h"
46 #include "metal/alloc.h"
47 #include "metal/irq.h"
48 #include "metal/atomic.h"
50 /* ------------------------- Macros --------------------------*/
51 #define ESAL_DP_SLCR_BASE 0xF8000000
52 #define PERIPH_BASE 0xF8F00000
53 #define GIC_DIST_BASE (PERIPH_BASE + 0x00001000)
54 #define GIC_DIST_SOFTINT 0xF00
55 #define GIC_SFI_TRIG_CPU_MASK 0x00FF0000
56 #define GIC_SFI_TRIG_SATT_MASK 0x00008000
57 #define GIC_SFI_TRIG_INTID_MASK 0x0000000F
58 #define GIC_CPU_ID_BASE (1 << 4)
59 #define A9_CPU_SLCR_RESET_CTRL 0x244
60 #define A9_CPU_SLCR_CLK_STOP (1 << 4)
61 #define A9_CPU_SLCR_RST (1 << 0)
63 #define unlock_slcr() HIL_MEM_WRITE32(ESAL_DP_SLCR_BASE + 0x08, 0xDF0DDF0D)
64 #define lock_slcr() HIL_MEM_WRITE32(ESAL_DP_SLCR_BASE + 0x04, 0x767B767B)
67 /* L2Cpl310 L2 cache controller base address. */
68 #define HIL_PL130_BASE 0xF8F02000
70 /********************/
71 /* Register offsets */
72 /********************/
74 #define HIL_PL130_INVALLINE 0x770
75 #define HIL_PL130_CLEANINVLINE 0x7F0
78 #define HIL_PA_SBZ_MASK ~(HIL_CACHE_LINE_SIZE - 1UL)
79 #define HIL_CACHE_LINE_SIZE 32
80 #define HIL_CACHE_INV_ALL_WAYS 0xFF
81 #define HIL_CACHE_UNLOCK_ALL_WAYS 0xFFFF0000
82 #define HIL_CACHE_CLEAR_INT 0x1FF
84 #define _rproc_wait() asm volatile("wfi")
86 /*--------------------------- Declare Functions ------------------------ */
87 static int _enable_interrupt(struct proc_vring *vring_hw);
88 static void _notify(int cpu_id, struct proc_intr *intr_info);
89 static int _boot_cpu(int cpu_id, unsigned int load_addr);
90 static void _shutdown_cpu(int cpu_id);
91 static int _poll(struct hil_proc *proc, int nonblock);
92 static struct hil_proc * _initialize(void *pdata, int cpu_id);
93 static void _release(struct hil_proc *proc);
94 static int _ipi_handler(int vect_id, void *data);
96 /*--------------------------- Globals ---------------------------------- */
97 struct hil_platform_ops zynq_a9_proc_ops = {
98 .enable_interrupt = _enable_interrupt,
99 .notify = _notify,
100 .boot_cpu = _boot_cpu,
101 .shutdown_cpu = _shutdown_cpu,
102 .poll = _poll,
103 .initialize = _initialize,
104 .release = _release,
105 };
107 int _ipi_handler(int vect_id, void *data)
108 {
109 (void) vect_id;
110 struct proc_vring *vring_hw = (struct proc_vring *)(data);
111 struct proc_intr *intr_info;
113 intr_info = &(vring_hw->intr_info);
114 atomic_flag_clear((atomic_uint *)&(intr_info->data));
115 return 0;
116 }
118 static int _enable_interrupt(struct proc_vring *vring_hw)
119 {
121 /* Register ISR */
122 metal_irq_register(vring_hw->intr_info.vect_id, _ipi_handler,
123 vring_hw->intr_info.dev, vring_hw);
125 /* Enable the interrupts */
126 metal_irq_enable(vring_hw->intr_info.vect_id);
127 return 0;
128 }
130 static void _notify(int cpu_id, struct proc_intr *intr_info)
131 {
133 unsigned long mask = 0;
135 mask = ((1 << (GIC_CPU_ID_BASE + cpu_id)) | (intr_info->vect_id))
136 & (GIC_SFI_TRIG_CPU_MASK | GIC_SFI_TRIG_INTID_MASK);
138 /* Trigger IPI */
139 metal_io_write32(intr_info->io, GIC_DIST_SOFTINT, mask);
140 }
142 static int _poll(struct hil_proc *proc, int nonblock)
143 {
144 struct proc_vring *vring;
145 unsigned int flags;
146 struct proc_intr *intr_info;
147 int i = 0;
148 int kicked = 0;
150 while(1) {
151 vring = &proc->vdev.vring_info[i];
152 intr_info = &(vring->intr_info);
153 flags = metal_irq_save_disable();
154 if (!(atomic_flag_test_and_set((atomic_uint *)&(intr_info->data)))) {
155 metal_irq_restore_enable(flags);
156 virtqueue_notification(vring->vq);
157 kicked = 1;
158 if (i)
159 return 0;
160 i++;
161 } else if (!i) {
162 metal_irq_restore_enable(flags);
163 i++;
164 } else {
165 if (kicked) {
166 metal_irq_restore_enable(flags);
167 return 0;
168 } else if (nonblock) {
169 metal_irq_restore_enable(flags);
170 return -EAGAIN;
171 } else {
172 _rproc_wait();
173 metal_irq_restore_enable(flags);
174 i--;
175 continue;
176 }
177 }
178 }
179 }
181 extern char zynq_trampoline;
182 extern char zynq_trampoline_jump;
183 extern char zynq_trampoline_end;
185 static int _boot_cpu(int cpu_id, unsigned int load_addr)
186 {
187 /* FIXME: Will need to add the boot_cpu implementation back */
188 #if 0
189 unsigned int reg;
190 unsigned int tramp_size;
191 unsigned int tramp_addr = 0;
193 if (load_addr) {
194 tramp_size = zynq_trampoline_end - zynq_trampoline;
195 if ((load_addr < tramp_size) || (load_addr & 0x3)) {
196 return -1;
197 }
199 tramp_size = &zynq_trampoline_jump - &zynq_trampoline;
201 /*
202 * Trampoline code is copied to address 0 from where remote core is expected to
203 * fetch first instruction after reset.If master is using the address 0 then
204 * this mem copy will screwed the system. It is user responsibility to not
205 * copy trampoline code in such cases.
206 *
207 */
208 memcpy((char *)tramp_addr, &zynq_trampoline, tramp_size);
209 /* Write image address at the word reserved at the trampoline end */
210 HIL_MEM_WRITE32((char *)(tramp_addr + tramp_size), load_addr);
211 }
213 unlock_slcr();
215 reg = HIL_MEM_READ32(ESAL_DP_SLCR_BASE + A9_CPU_SLCR_RESET_CTRL);
216 reg &= ~(A9_CPU_SLCR_CLK_STOP << cpu_id);
217 HIL_MEM_WRITE32(ESAL_DP_SLCR_BASE + A9_CPU_SLCR_RESET_CTRL, reg);
218 /* De-assert reset signal and start clock to start the core */
219 reg &= ~(A9_CPU_SLCR_RST << cpu_id);
220 HIL_MEM_WRITE32(ESAL_DP_SLCR_BASE + A9_CPU_SLCR_RESET_CTRL, reg);
222 lock_slcr();
224 #else
225 (void)cpu_id;
226 (void)load_addr;
227 #endif
228 return 0;
229 }
231 static void _shutdown_cpu(int cpu_id)
232 {
233 /* FIXME: Will need to add the shutdown CPU implementation back */
234 #if 0
235 unsigned int reg;
237 unlock_slcr();
239 reg = HIL_MEM_READ32(ESAL_DP_SLCR_BASE + A9_CPU_SLCR_RESET_CTRL);
240 /* Assert reset signal and stop clock to halt the core */
241 reg |= (A9_CPU_SLCR_CLK_STOP | A9_CPU_SLCR_RST) << cpu_id;
242 HIL_MEM_WRITE32(ESAL_DP_SLCR_BASE + A9_CPU_SLCR_RESET_CTRL, reg);
244 lock_slcr();
245 #else
246 (void)cpu_id;
247 #endif
248 }
250 static struct hil_proc * _initialize(void *pdata, int cpu_id)
251 {
252 (void) cpu_id;
253 int ret;
254 int i;
255 struct proc_intr *intr_info;
257 struct hil_proc *proc;
258 /* Allocate memory for proc instance */
259 proc = metal_allocate_memory(sizeof(struct hil_proc));
260 if (!proc) {
261 return NULL;
262 }
263 memset(proc, 0, sizeof(struct hil_proc));
265 ret = rproc_init_plat_data(pdata, proc);
266 if (ret)
267 goto error;
269 for (i = 0; i < 2; i++) {
270 intr_info = &(proc->vdev.vring_info[i].intr_info);
271 atomic_store((atomic_uint *)&(intr_info->data), 1);
272 }
274 return proc;
275 error:
276 if (proc) {
277 rproc_close_plat(proc);
278 metal_free_memory(proc);
279 }
280 return NULL;
281 }
283 static void _release(struct hil_proc *proc)
284 {
285 if (proc) {
286 rproc_close_plat(proc);
287 metal_free_memory(proc);
288 }
289 }