/* * Copyright (c) 2014, Mentor Graphics Corporation * All rights reserved. * Copyright (c) 2016 Xilinx, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of Mentor Graphics Corporation nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /************************************************************************** * FILE NAME * * zynqmp_remoteproc_r5.c * * DESCRIPTION * * This file is the Implementation of IPC hardware layer interface * for Xilinx Zynq UltraScale+ MPSoC system. * **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "openamp/hil.h" #include "openamp/virtqueue.h" #define MAX_VRING_MEM_SIZE 0x20000 #define _rproc_wait() metal_cpu_yield() #define UNIX_PREFIX "unix:" #define UNIXS_PREFIX "unixs:" struct vring_ipi_info { /* Socket file path */ char *path; int fd; struct metal_io_region *vring_io; atomic_int sync; }; /*--------------------------- Declare Functions ------------------------ */ static int _ipi_handler(int vect_id, void *data); static int _enable_interrupt(struct proc_intr *intr); static void _notify(struct hil_proc *proc, struct proc_intr *intr_info); static int _boot_cpu(struct hil_proc *proc, unsigned int load_addr); static void _shutdown_cpu(struct hil_proc *proc); static int _poll(struct hil_proc *proc, int nonblock); static int _initialize(struct hil_proc *proc); static void _release(struct hil_proc *proc); /*--------------------------- Globals ---------------------------------- */ struct hil_platform_ops linux_proc_ops = { .enable_interrupt = _enable_interrupt, .notify = _notify, .boot_cpu = _boot_cpu, .shutdown_cpu = _shutdown_cpu, .poll = _poll, .initialize = _initialize, .release = _release, }; static int sk_unix_client(const char *descr) { struct sockaddr_un addr; int fd; fd = socket(AF_UNIX, SOCK_STREAM, 0); memset(&addr, 0, sizeof addr); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, descr + strlen(UNIX_PREFIX), sizeof addr.sun_path); if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) >= 0) { printf("connected to %s\n", descr + strlen(UNIX_PREFIX)); return fd; } close(fd); return -1; } static int sk_unix_server(const char *descr) { struct sockaddr_un addr; int fd, nfd; fd = socket(AF_UNIX, SOCK_STREAM, 0); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, descr + strlen(UNIXS_PREFIX), sizeof addr.sun_path); unlink(addr.sun_path); if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { goto fail; } listen(fd, 5); printf("Waiting for connection on %s\n", addr.sun_path); nfd = accept(fd, NULL, NULL); close(fd); return nfd; fail: close(fd); return -1; } static int event_open(const char *descr) { int fd = -1; int i; if (descr == NULL) { return fd; } if (memcmp(UNIX_PREFIX, descr, strlen(UNIX_PREFIX)) == 0) { /* UNIX. Retry to connect a few times to give the peer a * chance to setup. */ for (i = 0; i < 100 && fd == -1; i++) { fd = sk_unix_client(descr); if (fd == -1) usleep(i * 10 * 1000); } } if (memcmp(UNIXS_PREFIX, descr, strlen(UNIXS_PREFIX)) == 0) { /* UNIX. */ fd = sk_unix_server(descr); } printf("Open IPI: %s\n", descr); return fd; } static int _ipi_handler(int vect_id, void *data) { char dummy_buf[32]; struct proc_intr *intr = data; struct vring_ipi_info *ipi = intr->data; (void) vect_id; read(vect_id, dummy_buf, sizeof(dummy_buf)); atomic_flag_clear(&ipi->sync); return 0; } static int _enable_interrupt(struct proc_intr *intr) { struct vring_ipi_info *ipi = intr->data; ipi->fd = event_open(ipi->path); if (ipi->fd < 0) { fprintf(stderr, "ERROR: Failed to open sock %s for IPI.\n", ipi->path); return -1; } intr->vect_id = ipi->fd; /* Register ISR */ metal_irq_register(ipi->fd, _ipi_handler, NULL, intr); return 0; } static void _notify(struct hil_proc *proc, struct proc_intr *intr_info) { (void)proc; struct vring_ipi_info *ipi = (struct vring_ipi_info *)(intr_info->data); if (ipi == NULL) return; char dummy = 1; send(ipi->fd, &dummy, 1, MSG_NOSIGNAL); //printf("%s:%d\n", __func__, ipi->fd); } static int _boot_cpu(struct hil_proc *proc, unsigned int load_addr) { (void)proc; (void)load_addr; return -1; } static void _shutdown_cpu(struct hil_proc *proc) { (void)proc; return; } static int _poll(struct hil_proc *proc, int nonblock) { (void) nonblock; struct proc_vring *vring; struct vring_ipi_info *ipi; unsigned int flags; //struct pollfd fds[32]; //char dummy_buf[32]; int num_vrings = proc->vdev.num_vrings; int ret = 0; int notified; //int r; int i; assert(proc); //assert(num_vrings <= (int)(sizeof(fds)/sizeof(fds[0]))); notified = 0; while (1) { for (i = 0; i < num_vrings; i++) { vring = &proc->vdev.vring_info[i]; ipi = (struct vring_ipi_info *)(vring->intr_info.data); flags = metal_irq_save_disable(); if (!(atomic_flag_test_and_set(&ipi->sync))) { metal_irq_restore_enable(flags); virtqueue_notification(vring->vq); notified = 1; } else { metal_irq_restore_enable(flags); } } if (notified) return 0; if (nonblock) return -EAGAIN; _rproc_wait(); } return ret; } /** * @brief _adjust_vring_io - Adjust the vring I/O region to map to the * specified start device address. * @param[in] io - vring I/O region * @param[in] start_phy - start device address of the vring, this is * not the actual physical address. * @return adjusted I/O region */ static struct metal_io_region *_create_vring_io(struct metal_io_region *in_io, int start_phy) { struct metal_io_region *io = 0; metal_phys_addr_t *phys; io = metal_allocate_memory(sizeof(struct metal_io_region)); if (!io) { fprintf(stderr, "ERROR: Failed to allocation I/O for vring.\n"); return NULL; } phys = metal_allocate_memory(sizeof(metal_phys_addr_t)); if (!phys) { fprintf(stderr, "ERROR: Failed to allocation phys for vring.\n"); metal_free_memory(io); return NULL; } *phys = (metal_phys_addr_t)start_phy; metal_io_init(io, in_io->virt, phys, in_io->size, -1, 0, NULL); return io; } static int _initialize(struct hil_proc *proc) { struct proc_vring *vring; struct vring_ipi_info *ipi; struct metal_io_region *io; int i; if (proc) { for (i = 0; i < 2; i++) { vring = &proc->vdev.vring_info[i]; ipi = (struct vring_ipi_info *)vring->intr_info.data; if (ipi && !ipi->vring_io && vring->io) { io = _create_vring_io(vring->io, 0); if (!io) return -1; ipi->vring_io = vring->io; vring->io = io; atomic_store(&ipi->sync, 1); } } } return 0; } static void _release(struct hil_proc *proc) { struct proc_vring *vring; struct vring_ipi_info *ipi; int i; if (proc) { for (i = 0; i < 2; i++) { vring = &proc->vdev.vring_info[i]; ipi = (struct vring_ipi_info *)vring->intr_info.data; if (ipi) { if (ipi->fd >= 0) { metal_irq_register(ipi->fd, 0, NULL, vring); close(ipi->fd); } if (ipi->vring_io) { metal_free_memory( (void *)vring->io->physmap); metal_free_memory(vring->io); vring->io = NULL; if (ipi->vring_io->ops.close) ipi->vring_io->ops.close( ipi->vring_io); ipi->vring_io = NULL; } } } } }