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 "openamp/hil.h"
44 #include "openamp/remoteproc_plat.h"
45 #include "metal/alloc.h"
46 #include "metal/irq.h"
47 #include "metal/atomic.h"
49 /* ------------------------- Macros --------------------------*/
50 #define ESAL_DP_SLCR_BASE 0xF8000000
51 #define PERIPH_BASE 0xF8F00000
52 #define GIC_DIST_BASE (PERIPH_BASE + 0x00001000)
53 #define GIC_DIST_SOFTINT 0xF00
54 #define GIC_SFI_TRIG_CPU_MASK 0x00FF0000
55 #define GIC_SFI_TRIG_SATT_MASK 0x00008000
56 #define GIC_SFI_TRIG_INTID_MASK 0x0000000F
57 #define GIC_CPU_ID_BASE (1 << 4)
58 #define A9_CPU_SLCR_RESET_CTRL 0x244
59 #define A9_CPU_SLCR_CLK_STOP (1 << 4)
60 #define A9_CPU_SLCR_RST (1 << 0)
62 #define unlock_slcr() HIL_MEM_WRITE32(ESAL_DP_SLCR_BASE + 0x08, 0xDF0DDF0D)
63 #define lock_slcr() HIL_MEM_WRITE32(ESAL_DP_SLCR_BASE + 0x04, 0x767B767B)
66 /* L2Cpl310 L2 cache controller base address. */
67 #define HIL_PL130_BASE 0xF8F02000
69 /********************/
70 /* Register offsets */
71 /********************/
73 #define HIL_PL130_INVALLINE 0x770
74 #define HIL_PL130_CLEANINVLINE 0x7F0
77 #define HIL_PA_SBZ_MASK ~(HIL_CACHE_LINE_SIZE - 1UL)
78 #define HIL_CACHE_LINE_SIZE 32
79 #define HIL_CACHE_INV_ALL_WAYS 0xFF
80 #define HIL_CACHE_UNLOCK_ALL_WAYS 0xFFFF0000
81 #define HIL_CACHE_CLEAR_INT 0x1FF
83 #define _rproc_wait() asm volatile("wfi")
85 /*--------------------------- Declare Functions ------------------------ */
86 static int _enable_interrupt(struct proc_vring *vring_hw);
87 static void _notify(int cpu_id, struct proc_intr *intr_info);
88 static int _boot_cpu(int cpu_id, unsigned int load_addr);
89 static void _shutdown_cpu(int cpu_id);
90 static int _poll(struct hil_proc *proc, int nonblock);
91 static struct hil_proc * _initialize(void *pdata, int cpu_id);
92 static void _release(struct hil_proc *proc);
93 static int _ipi_handler(int vect_id, void *data);
95 /*--------------------------- Globals ---------------------------------- */
96 struct hil_platform_ops zynq_a9_proc_ops = {
97 .enable_interrupt = _enable_interrupt,
98 .notify = _notify,
99 .boot_cpu = _boot_cpu,
100 .shutdown_cpu = _shutdown_cpu,
101 .poll = _poll,
102 .initialize = _initialize,
103 .release = _release,
104 };
106 int _ipi_handler(int vect_id, void *data)
107 {
108 (void) vect_id;
109 struct proc_vring *vring_hw = (struct proc_vring *)(data);
110 struct proc_intr *intr_info;
112 intr_info = &(vring_hw->intr_info);
113 atomic_flag_clear(&(intr_info->data));
114 return 0;
115 }
117 static int _enable_interrupt(struct proc_vring *vring_hw)
118 {
120 /* Register ISR */
121 metal_irq_register(vring_hw->intr_info.vect_id, _ipi_handler,
122 vring_hw->intr_info.dev, vring_hw);
124 /* Enable the interrupts */
125 metal_irq_enable(vring_hw->intr_info.vect_id);
126 return 0;
127 }
129 static void _notify(int cpu_id, struct proc_intr *intr_info)
130 {
132 unsigned long mask = 0;
134 mask = ((1 << (GIC_CPU_ID_BASE + cpu_id)) | (intr_info->vect_id))
135 & (GIC_SFI_TRIG_CPU_MASK | GIC_SFI_TRIG_INTID_MASK);
137 /* Trigger IPI */
138 metal_io_write32(intr_info->io, GIC_DIST_SOFTINT, mask);
139 }
141 static int _poll(struct hil_proc *proc, int nonblock)
142 {
143 struct proc_vring *vring;
144 unsigned int flags;
145 struct proc_intr *intr_info;
146 int i = 0;
147 int kicked = 0;
149 while(1) {
150 vring = &proc->vdev.vring_info[i];
151 intr_info = &(vring->intr_info);
152 flags = metal_irq_save_disable();
153 if (!(atomic_flag_test_and_set(&intr_info->data))) {
154 metal_irq_restore_enable(flags);
155 virtqueue_notification(vring->vq);
156 kicked = 1;
157 if (i)
158 return 0;
159 i++;
160 } else if (!i) {
161 metal_irq_restore_enable(flags);
162 i++;
163 } else {
164 if (kicked) {
165 metal_irq_restore_enable(flags);
166 return 0;
167 } else if (nonblock) {
168 metal_irq_restore_enable(flags);
169 return -EAGAIN;
170 } else {
171 _rproc_wait();
172 metal_irq_restore_enable(flags);
173 i--;
174 continue;
175 }
176 }
177 }
178 }
180 extern char zynq_trampoline;
181 extern char zynq_trampoline_jump;
182 extern char zynq_trampoline_end;
184 static int _boot_cpu(int cpu_id, unsigned int load_addr)
185 {
186 /* FIXME: Will need to add the boot_cpu implementation back */
187 #if 0
188 unsigned int reg;
189 unsigned int tramp_size;
190 unsigned int tramp_addr = 0;
192 if (load_addr) {
193 tramp_size = zynq_trampoline_end - zynq_trampoline;
194 if ((load_addr < tramp_size) || (load_addr & 0x3)) {
195 return -1;
196 }
198 tramp_size = &zynq_trampoline_jump - &zynq_trampoline;
200 /*
201 * Trampoline code is copied to address 0 from where remote core is expected to
202 * fetch first instruction after reset.If master is using the address 0 then
203 * this mem copy will screwed the system. It is user responsibility to not
204 * copy trampoline code in such cases.
205 *
206 */
207 memcpy((char *)tramp_addr, &zynq_trampoline, tramp_size);
208 /* Write image address at the word reserved at the trampoline end */
209 HIL_MEM_WRITE32((char *)(tramp_addr + tramp_size), load_addr);
210 }
212 unlock_slcr();
214 reg = HIL_MEM_READ32(ESAL_DP_SLCR_BASE + A9_CPU_SLCR_RESET_CTRL);
215 reg &= ~(A9_CPU_SLCR_CLK_STOP << cpu_id);
216 HIL_MEM_WRITE32(ESAL_DP_SLCR_BASE + A9_CPU_SLCR_RESET_CTRL, reg);
217 /* De-assert reset signal and start clock to start the core */
218 reg &= ~(A9_CPU_SLCR_RST << cpu_id);
219 HIL_MEM_WRITE32(ESAL_DP_SLCR_BASE + A9_CPU_SLCR_RESET_CTRL, reg);
221 lock_slcr();
223 #else
224 (void)cpu_id;
225 (void)load_addr;
226 #endif
227 return 0;
228 }
230 static void _shutdown_cpu(int cpu_id)
231 {
232 /* FIXME: Will need to add the shutdown CPU implementation back */
233 #if 0
234 unsigned int reg;
236 unlock_slcr();
238 reg = HIL_MEM_READ32(ESAL_DP_SLCR_BASE + A9_CPU_SLCR_RESET_CTRL);
239 /* Assert reset signal and stop clock to halt the core */
240 reg |= (A9_CPU_SLCR_CLK_STOP | A9_CPU_SLCR_RST) << cpu_id;
241 HIL_MEM_WRITE32(ESAL_DP_SLCR_BASE + A9_CPU_SLCR_RESET_CTRL, reg);
243 lock_slcr();
244 #else
245 (void)cpu_id;
246 #endif
247 }
249 static struct hil_proc * _initialize(void *pdata, int cpu_id)
250 {
251 (void) cpu_id;
252 int ret;
253 int i;
254 struct proc_intr *intr_info;
256 struct hil_proc *proc;
257 /* Allocate memory for proc instance */
258 proc = metal_allocate_memory(sizeof(struct hil_proc));
259 if (!proc) {
260 return NULL;
261 }
262 memset(proc, 0, sizeof(struct hil_proc));
264 ret = rproc_init_plat_data(pdata, proc);
265 if (ret)
266 goto error;
268 for (i = 0; i < 2; i++) {
269 intr_info = &(proc->vdev.vring_info[i].intr_info);
270 atomic_store(&(intr_info->data), 1);
271 }
273 return proc;
274 error:
275 if (proc) {
276 rproc_close_plat(proc);
277 metal_free_memory(proc);
278 }
279 return NULL;
280 }
282 static void _release(struct hil_proc *proc)
283 {
284 if (proc) {
285 rproc_close_plat(proc);
286 metal_free_memory(proc);
287 }
288 }