1 /*
2 * Copyright (c) 2014, Mentor Graphics Corporation
3 * All rights reserved.
4 * Copyright (c) 2016 Xilinx, Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 * 3. Neither the name of Mentor Graphics Corporation nor the names of its
15 * contributors may be used to endorse or promote products derived from this
16 * software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
31 /**************************************************************************
32 * FILE NAME
33 *
34 * zynqmp_remoteproc_r5.c
35 *
36 * DESCRIPTION
37 *
38 * This file is the Implementation of IPC hardware layer interface
39 * for Xilinx Zynq UltraScale+ MPSoC system.
40 *
41 **************************************************************************/
43 #include <errno.h>
44 #include <string.h>
45 #include <stdio.h>
46 #include <poll.h>
47 #include <metal/io.h>
48 #include <metal/device.h>
49 #include <metal/utilities.h>
50 #include <metal/atomic.h>
51 #include <metal/irq.h>
52 #include <metal/cpu.h>
53 #include <metal/alloc.h>
54 #include <metal/shmem.h>
55 #include <sys/socket.h>
56 #include <sys/un.h>
57 #include "openamp/hil.h"
58 #include "openamp/virtqueue.h"
60 #define MAX_VRING_MEM_SIZE 0x20000
61 #define _rproc_wait() metal_cpu_yield()
63 #define UNIX_PREFIX "unix:"
64 #define UNIXS_PREFIX "unixs:"
66 struct vring_ipi_info {
67 /* Socket file path */
68 char *path;
69 int fd;
70 struct metal_io_region *vring_io;
71 atomic_int sync;
72 };
74 /*--------------------------- Declare Functions ------------------------ */
75 static int _ipi_handler(int vect_id, void *data);
76 static int _enable_interrupt(struct proc_vring *vring_hw);
77 static void _notify(struct hil_proc *proc, struct proc_intr *intr_info);
78 static int _boot_cpu(struct hil_proc *proc, unsigned int load_addr);
79 static void _shutdown_cpu(struct hil_proc *proc);
80 static int _poll(struct hil_proc *proc, int nonblock);
81 static int _initialize(struct hil_proc *proc);
82 static void _release(struct hil_proc *proc);
84 /*--------------------------- Globals ---------------------------------- */
85 struct hil_platform_ops linux_proc_ops = {
86 .enable_interrupt = _enable_interrupt,
87 .notify = _notify,
88 .boot_cpu = _boot_cpu,
89 .shutdown_cpu = _shutdown_cpu,
90 .poll = _poll,
91 .initialize = _initialize,
92 .release = _release,
93 };
95 static int sk_unix_client(const char *descr)
96 {
97 struct sockaddr_un addr;
98 int fd;
100 fd = socket(AF_UNIX, SOCK_STREAM, 0);
102 memset(&addr, 0, sizeof addr);
103 addr.sun_family = AF_UNIX;
104 strncpy(addr.sun_path, descr + strlen(UNIX_PREFIX),
105 sizeof addr.sun_path);
106 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) >= 0) {
107 printf("connected to %s\n", descr + strlen(UNIX_PREFIX));
108 return fd;
109 }
111 close(fd);
112 return -1;
113 }
115 static int sk_unix_server(const char *descr)
116 {
117 struct sockaddr_un addr;
118 int fd, nfd;
120 fd = socket(AF_UNIX, SOCK_STREAM, 0);
122 addr.sun_family = AF_UNIX;
123 strncpy(addr.sun_path, descr + strlen(UNIXS_PREFIX),
124 sizeof addr.sun_path);
125 unlink(addr.sun_path);
126 if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
127 goto fail;
128 }
130 listen(fd, 5);
131 printf("Waiting for connection on %s\n", addr.sun_path);
132 nfd = accept(fd, NULL, NULL);
133 close(fd);
134 return nfd;
135 fail:
136 close(fd);
137 return -1;
138 }
140 static int event_open(const char *descr)
141 {
142 int fd = -1;
143 int i;
145 if (descr == NULL) {
146 return fd;
147 }
149 if (memcmp(UNIX_PREFIX, descr, strlen(UNIX_PREFIX)) == 0) {
150 /* UNIX. Retry to connect a few times to give the peer a
151 * chance to setup. */
152 for (i = 0; i < 100 && fd == -1; i++) {
153 fd = sk_unix_client(descr);
154 if (fd == -1)
155 usleep(i * 10 * 1000);
156 }
157 }
158 if (memcmp(UNIXS_PREFIX, descr, strlen(UNIXS_PREFIX)) == 0) {
159 /* UNIX. */
160 fd = sk_unix_server(descr);
161 }
162 printf("Open IPI: %s\n", descr);
163 return fd;
164 }
166 static int _ipi_handler(int vect_id, void *data)
167 {
168 (void) vect_id;
169 (void) data;
170 char dummy_buf[32];
171 struct proc_vring *vring_hw = (struct proc_vring *)(data);
172 struct vring_ipi_info *ipi =
173 (struct vring_ipi_info *)(vring_hw->intr_info.data);
174 read(vect_id, dummy_buf, sizeof(dummy_buf));
175 atomic_flag_clear(&ipi->sync);
176 return 0;
177 }
179 static int _enable_interrupt(struct proc_vring *vring_hw)
180 {
181 struct vring_ipi_info *ipi = vring_hw->intr_info.data;
183 ipi->fd = event_open(ipi->path);
184 if (ipi->fd < 0) {
185 fprintf(stderr, "ERROR: Failed to open sock %s for IPI.\n",
186 ipi->path);
187 return -1;
188 }
190 vring_hw->intr_info.vect_id = ipi->fd;
192 /* Register ISR */
193 metal_irq_register(ipi->fd, _ipi_handler,
194 NULL, vring_hw);
195 return 0;
196 }
198 static void _notify(struct hil_proc *proc, struct proc_intr *intr_info)
199 {
201 (void)proc;
202 struct vring_ipi_info *ipi = (struct vring_ipi_info *)(intr_info->data);
203 if (ipi == NULL)
204 return;
206 char dummy = 1;
207 send(ipi->fd, &dummy, 1, MSG_NOSIGNAL);
208 //printf("%s:%d\n", __func__, ipi->fd);
209 }
211 static int _boot_cpu(struct hil_proc *proc, unsigned int load_addr)
212 {
213 (void)proc;
214 (void)load_addr;
215 return -1;
216 }
218 static void _shutdown_cpu(struct hil_proc *proc)
219 {
220 (void)proc;
221 return;
222 }
224 static int _poll(struct hil_proc *proc, int nonblock)
225 {
226 (void) nonblock;
227 struct proc_vring *vring;
228 struct vring_ipi_info *ipi;
229 unsigned int flags;
231 //struct pollfd fds[32];
232 //char dummy_buf[32];
233 int num_vrings = proc->vdev.num_vrings;
234 int ret = 0;
235 int notified;
236 //int r;
237 int i;
239 assert(proc);
240 //assert(num_vrings <= (int)(sizeof(fds)/sizeof(fds[0])));
242 notified = 0;
243 while (1) {
244 for (i = 0; i < num_vrings; i++) {
245 vring = &proc->vdev.vring_info[i];
246 ipi = (struct vring_ipi_info *)(vring->intr_info.data);
247 flags = metal_irq_save_disable();
248 if (!(atomic_flag_test_and_set(&ipi->sync))) {
249 metal_irq_restore_enable(flags);
250 virtqueue_notification(vring->vq);
251 notified = 1;
252 } else {
253 metal_irq_restore_enable(flags);
254 }
255 }
256 if (notified)
257 return 0;
258 if (nonblock)
259 return -EAGAIN;
260 _rproc_wait();
261 }
262 return ret;
263 }
265 /**
266 * @brief _adjust_vring_io - Adjust the vring I/O region to map to the
267 * specified start device address.
268 * @param[in] io - vring I/O region
269 * @param[in] start_phy - start device address of the vring, this is
270 * not the actual physical address.
271 * @return adjusted I/O region
272 */
273 static struct metal_io_region *_create_vring_io(struct metal_io_region *in_io,
274 int start_phy)
275 {
276 struct metal_io_region *io = 0;
277 metal_phys_addr_t *phys;
278 io = metal_allocate_memory(sizeof(struct metal_io_region));
279 if (!io) {
280 fprintf(stderr, "ERROR: Failed to allocation I/O for vring.\n");
281 return NULL;
282 }
283 phys = metal_allocate_memory(sizeof(metal_phys_addr_t));
284 if (!phys) {
285 fprintf(stderr, "ERROR: Failed to allocation phys for vring.\n");
286 metal_free_memory(io);
287 return NULL;
288 }
289 *phys = (metal_phys_addr_t)start_phy;
290 metal_io_init(io, in_io->virt, phys, in_io->size, -1, 0, NULL);
291 return io;
292 }
294 static int _initialize(struct hil_proc *proc)
295 {
296 struct proc_vring *vring;
297 struct vring_ipi_info *ipi;
298 struct metal_io_region *io;
299 int i;
300 if (proc) {
301 for (i = 0; i < 2; i++) {
302 vring = &proc->vdev.vring_info[i];
303 ipi = (struct vring_ipi_info *)vring->intr_info.data;
304 if (ipi && !ipi->vring_io && vring->io) {
305 io = _create_vring_io(vring->io, 0);
306 if (!io)
307 return -1;
308 ipi->vring_io = vring->io;
309 vring->io = io;
310 atomic_store(&ipi->sync, 1);
311 }
312 }
313 }
314 return 0;
315 }
317 static void _release(struct hil_proc *proc)
318 {
319 struct proc_vring *vring;
320 struct vring_ipi_info *ipi;
321 int i;
322 if (proc) {
323 for (i = 0; i < 2; i++) {
324 vring = &proc->vdev.vring_info[i];
325 ipi = (struct vring_ipi_info *)vring->intr_info.data;
326 if (ipi) {
327 if (ipi->fd >= 0) {
328 metal_irq_register(ipi->fd, 0, NULL,
329 vring);
330 close(ipi->fd);
331 }
332 if (ipi->vring_io) {
333 metal_free_memory(
334 (void *)vring->io->physmap);
335 metal_free_memory(vring->io);
336 vring->io = NULL;
337 if (ipi->vring_io->ops.close)
338 ipi->vring_io->ops.close(
339 ipi->vring_io);
340 ipi->vring_io = NULL;
341 }
342 }
343 }
344 }
345 }