From: Wendy Liang Date: Mon, 12 Sep 2016 21:44:12 +0000 (-0700) Subject: Add Linux remoteproc between two processes X-Git-Tag: v2016.10~12 X-Git-Url: https://git.ti.com/gitweb?p=processor-sdk%2Fopen-amp.git;a=commitdiff_plain;h=6c9a1c1d12af55cf04cb54c16f2ae3ac6e929da8;ds=sidebyside Add Linux remoteproc between two processes Introduce a remoteproc driver for the RPMsg between two Linux processes. Signed-off-by: Wendy Liang --- diff --git a/lib/remoteproc/drivers/CMakeLists.txt b/lib/remoteproc/drivers/CMakeLists.txt index 8fe4358..15028ee 100644 --- a/lib/remoteproc/drivers/CMakeLists.txt +++ b/lib/remoteproc/drivers/CMakeLists.txt @@ -10,3 +10,7 @@ endif ("${MACHINE}" STREQUAL "zynq7") if ("${MACHINE}" STREQUAL "zynqmp") collect (PROJECT_LIB_SOURCES zynqmp_remoteproc_r5.c) endif ("${MACHINE}" STREQUAL "zynqmp") + +if ("${PROJECT_SYSTEM}" STREQUAL "linux") + collect (PROJECT_LIB_SOURCES linux_remoteproc.c) +endif ("${PROJECT_SYSTEM}" STREQUAL "linux") diff --git a/lib/remoteproc/drivers/linux_remoteproc.c b/lib/remoteproc/drivers/linux_remoteproc.c new file mode 100644 index 0000000..79157f1 --- /dev/null +++ b/lib/remoteproc/drivers/linux_remoteproc.c @@ -0,0 +1,346 @@ +/* + * 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_vring *vring_hw); +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) +{ + (void) vect_id; + (void) data; + char dummy_buf[32]; + struct proc_vring *vring_hw = (struct proc_vring *)(data); + struct vring_ipi_info *ipi = + (struct vring_ipi_info *)(vring_hw->intr_info.data); + read(vect_id, dummy_buf, sizeof(dummy_buf)); + atomic_flag_clear(&ipi->sync); + return 0; +} + +static int _enable_interrupt(struct proc_vring *vring_hw) +{ + struct vring_ipi_info *ipi = vring_hw->intr_info.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; + } + + vring_hw->intr_info.vect_id = ipi->fd; + + /* Register ISR */ + metal_irq_register(ipi->fd, _ipi_handler, + NULL, vring_hw); + 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; + } + } + } + } +} +