summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 319c4d0)
raw | patch | inline | side by side (parent: 319c4d0)
author | Wendy Liang <jliang@xilinx.com> | |
Mon, 12 Sep 2016 21:44:12 +0000 (14:44 -0700) | ||
committer | Wendy Liang <jliang@xilinx.com> | |
Fri, 14 Oct 2016 00:14:44 +0000 (17:14 -0700) |
Introduce a remoteproc driver for the RPMsg between
two Linux processes.
Signed-off-by: Wendy Liang <jliang@xilinx.com>
two Linux processes.
Signed-off-by: Wendy Liang <jliang@xilinx.com>
lib/remoteproc/drivers/CMakeLists.txt | patch | blob | history | |
lib/remoteproc/drivers/linux_remoteproc.c | [new file with mode: 0644] | patch | blob |
index 8fe435847ef7cf54190de8a90003992266ee296e..15028ee3a9746bf6ce64b844f433b99ff1c0837a 100644 (file)
if ("${MACHINE}" STREQUAL "zynqmp")
collect (PROJECT_LIB_SOURCES zynqmp_remoteproc_r5.c)
endif ("${MACHINE}" STREQUAL "zynqmp")
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
--- /dev/null
@@ -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 <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <poll.h>
+#include <metal/io.h>
+#include <metal/device.h>
+#include <metal/utilities.h>
+#include <metal/atomic.h>
+#include <metal/irq.h>
+#include <metal/cpu.h>
+#include <metal/alloc.h>
+#include <metal/shmem.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#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;
+ }
+ }
+ }
+ }
+}
+