[RFC PATCH 04/10] drivers: add RPMsg framework
Tanmay Shah
tanmay.shah at amd.com
Tue Jul 25 16:06:44 CEST 2023
RPMsg framework is used to communicate to remote processor
using rpmsg protocol.
This framework is ported from the Linux kernel
directory: drivers/rpmsg/
kernel version: 6.4-rc2 (d848a4819d85)
Signed-off-by: Tanmay Shah <tanmay.shah at amd.com>
---
MAINTAINERS | 7 +
arch/sandbox/dts/test.dts | 8 +
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/rpmsg/Kconfig | 31 +++
drivers/rpmsg/Makefile | 10 +
drivers/rpmsg/rpmsg-uclass.c | 156 ++++++++++++
drivers/rpmsg/rpmsg_internal.h | 52 ++++
drivers/rpmsg/sandbox_test_rpmsg.c | 88 +++++++
drivers/rpmsg/virtio_rpmsg_bus.c | 384 +++++++++++++++++++++++++++++
drivers/virtio/virtio-uclass.c | 1 +
include/dm/uclass-id.h | 1 +
include/rpmsg.h | 140 +++++++++++
include/rproc_virtio.h | 8 +-
include/virtio.h | 4 +-
include/virtio_ring.h | 15 ++
test/dm/Makefile | 1 +
test/dm/rpmsg.c | 41 +++
18 files changed, 947 insertions(+), 3 deletions(-)
create mode 100644 drivers/rpmsg/Kconfig
create mode 100644 drivers/rpmsg/Makefile
create mode 100644 drivers/rpmsg/rpmsg-uclass.c
create mode 100644 drivers/rpmsg/rpmsg_internal.h
create mode 100644 drivers/rpmsg/sandbox_test_rpmsg.c
create mode 100644 drivers/rpmsg/virtio_rpmsg_bus.c
create mode 100644 include/rpmsg.h
create mode 100644 test/dm/rpmsg.c
diff --git a/MAINTAINERS b/MAINTAINERS
index c4a32a0956..876a7fdbdf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1365,6 +1365,13 @@ F: drivers/usb/gadget/f_rockusb.c
F: cmd/rockusb.c
F: doc/README.rockusb
+RPMSG
+M: Tanmay Shah <tanmay.shah at amd.com>
+S: Maintained
+F: drivers/rpmsg/*
+F: include/rpmsg.h
+F: test/dm/rpmsg.c
+
SANDBOX
M: Simon Glass <sjg at chromium.org>
S: Maintained
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index b5509eee8c..fca1a591fb 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -1247,6 +1247,14 @@
compatible = "sandbox,sandbox-rng";
};
+ rpmsg_1: rpmsg at 1 {
+ compatible = "sandbox,test-rpmsg";
+ };
+
+ rpmsg_2: rpmsg at 2 {
+ compatible = "sandbox,test-rpmsg";
+ };
+
rproc_1: rproc at 1 {
compatible = "sandbox,test-processor";
remoteproc-name = "remoteproc-test-dev1";
diff --git a/drivers/Kconfig b/drivers/Kconfig
index a25f6ae02f..69700f1f83 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -112,6 +112,8 @@ source "drivers/reset/Kconfig"
source "drivers/rng/Kconfig"
+source "drivers/rpmsg/Kconfig"
+
source "drivers/rtc/Kconfig"
source "drivers/scsi/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 3bc6d279d7..68e8d8b065 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -109,6 +109,7 @@ obj-y += mfd/
obj-y += mtd/
obj-y += pwm/
obj-y += reset/
+obj-y += rpmsg/
obj-y += input/
obj-y += iommu/
# SOC specific infrastructure drivers.
diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
new file mode 100644
index 0000000000..4efb8dfcd7
--- /dev/null
+++ b/drivers/rpmsg/Kconfig
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2023, Advanced Micro devices, Inc.
+
+menu "RPMsg drivers"
+
+# RPMsg gets selected by drivers as needed
+# All users should depend on DM
+config RPMSG
+ bool
+ depends on DM
+
+config VIRTIO_RPMSG_BUS
+ bool "virtio rpmsg bus"
+ depends on DM
+ select RPMSG
+ select REMOTEPROC_VIRTIO
+ help
+ Say 'y' here to enable virtio based RPMsg. RPMsg allows
+ U-Boot drivers to communicate with remote processors.
+
+config RPMSG_SANDBOX
+ bool "RPMsg driver for sandbox platform"
+ depends on DM
+ select RPMSG
+ depends on SANDBOX
+ help
+ Say 'y' here to add sandbox driver for RPMsg framework used
+ for dummy communication with remote processor on sandbox platform
+
+endmenu
diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile
new file mode 100644
index 0000000000..21611725ea
--- /dev/null
+++ b/drivers/rpmsg/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2023, Advanced Micro Devices, Inc.
+
+obj-$(CONFIG_RPMSG) += rpmsg-uclass.o
+
+obj-$(CONFIG_RPMSG_SANDBOX) += sandbox_test_rpmsg.o
+
+# virtio driver for rpmsg
+obj-$(CONFIG_VIRTIO_RPMSG_BUS) += virtio_rpmsg_bus.o
diff --git a/drivers/rpmsg/rpmsg-uclass.c b/drivers/rpmsg/rpmsg-uclass.c
new file mode 100644
index 0000000000..3e749a5827
--- /dev/null
+++ b/drivers/rpmsg/rpmsg-uclass.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * remote processor messaging bus
+ *
+ * Copyright (C) 2023, Advanced Micro Devices, Inc.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <log.h>
+#include <malloc.h>
+#include <rpmsg.h>
+#include <virtio.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/uclass.h>
+#include <dm/uclass-internal.h>
+
+#include "rpmsg_internal.h"
+
+int rpmsg_init(int core_id)
+{
+ struct udevice *udev = NULL;
+ int ret;
+
+ ret = uclass_find_device_by_seq(UCLASS_RPMSG, core_id, &udev);
+ if (ret) {
+ debug("can't find rpmsg dev for core_id %d\n", core_id);
+ return ret;
+ }
+
+ ret = device_probe(udev);
+ if (ret)
+ debug("failed to probe rpmsg dev, ret = %d\n", ret);
+
+ return ret;
+}
+
+static int rpmsg_find_device(int core_id, struct udevice **rpdev)
+{
+ int core_count;
+
+ core_count = uclass_id_count(UCLASS_RPMSG);
+ if (core_id >= core_count) {
+ debug("invalid core id = %d\n", core_id);
+ return -EINVAL;
+ }
+
+ return uclass_find_device(UCLASS_RPMSG, core_id, rpdev);
+}
+
+/**
+ * rpmsg_send() - send a message across to the remote processor
+ * @core_id: remote processor core id
+ * @data: payload of message
+ * @len: length of payload
+ *
+ * This function sends @data of length @len on the @core_id endpoint.
+ * The message will be sent to the remote processor which the @core_id
+ * belongs to, using @ept's address and its associated rpmsg
+ * device destination addresses.
+ * In case there are no TX buffers available, the function will fail
+ * immediately
+ *
+ * Return: 0 on success and an appropriate error value on failure.
+ */
+int rpmsg_send(int core_id, void *data, int len)
+{
+ struct udevice *rpdev = NULL;
+ const struct rpmsg_device_ops *ops;
+ int ret;
+
+ ret = rpmsg_find_device(core_id, &rpdev);
+ if (ret) {
+ debug("no rpmsg device for core = %d, ret = %d\n", core_id, ret);
+ return ret;
+ }
+ if (!rpdev)
+ return -ENODEV;
+
+ ops = (const struct rpmsg_device_ops *)device_get_ops(rpdev);
+ if (!ops) {
+ debug("send op not registered for device %s\n", rpdev->name);
+ return -EINVAL;
+ }
+
+ return ops->send(rpdev, data, len);
+}
+
+int rpmsg_recv(int core_id, rpmsg_rx_cb_t cb)
+{
+ struct udevice *rpdev = NULL;
+ const struct rpmsg_device_ops *ops;
+ int ret;
+
+ ret = rpmsg_find_device(core_id, &rpdev);
+ if (ret) {
+ debug("no rpmsg device for core = %d, ret = %d\n", core_id, ret);
+ return ret;
+ }
+ if (!rpdev)
+ return -ENODEV;
+
+ ops = (const struct rpmsg_device_ops *)device_get_ops(rpdev);
+ if (!ops) {
+ debug("recv op not registered for device %s\n", rpdev->name);
+ return -EINVAL;
+ }
+
+ return ops->recv(rpdev, cb);
+}
+
+void rpmsg_debug_data(int core_id, int vq_id)
+{
+ struct udevice *rpdev = NULL;
+ const struct rpmsg_device_ops *ops;
+ int ret;
+
+ if (vq_id > 1)
+ debug("vq_id %d not supported\n", vq_id);
+
+ ret = rpmsg_find_device(core_id, &rpdev);
+ if (ret || !rpdev) {
+ debug("no rpmsg device for core = %d, ret = %d\n", core_id, ret);
+ return;
+ }
+
+ ops = (const struct rpmsg_device_ops *)device_get_ops(rpdev);
+ if (!ops || !ops->debug_data) {
+ debug("recv op not registered for device %s\n", rpdev->name);
+ return;
+ }
+
+ ops->debug_data(rpdev, vq_id);
+}
+
+int rpmsg_uclass_init(struct uclass *class)
+{
+ int ret;
+
+ /* make sure virtio framework is initialized */
+ ret = virtio_init();
+ if (ret)
+ debug("virtio init failed, %d\n", ret);
+
+ return ret;
+}
+
+UCLASS_DRIVER(rpmsg_bus) = {
+ .name = "rpmsg_bus",
+ .id = UCLASS_RPMSG,
+ .init = rpmsg_uclass_init,
+ .flags = DM_UC_FLAG_SEQ_ALIAS,
+};
diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h
new file mode 100644
index 0000000000..4f7bf9fb90
--- /dev/null
+++ b/drivers/rpmsg/rpmsg_internal.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * remote processor messaging bus internals
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2023, Advanced Micro Devices, Inc.
+ *
+ * Ohad Ben-Cohen <ohad at wizery.com>
+ * Brian Swetland <swetland at google.com>
+ */
+
+#ifndef __RPMSG_INTERNAL_H__
+#define __RPMSG_INTERNAL_H__
+
+#include <rpmsg.h>
+#include <virtio.h>
+
+/**
+ * struct rpmsg_hdr - common header for all rpmsg messages
+ * @src: source address
+ * @dst: destination address
+ * @reserved: reserved for future use
+ * @len: length of payload (in bytes)
+ * @flags: message flags
+ * @data: @len bytes of message payload data
+ *
+ * Every message sent(/received) on the rpmsg bus begins with this header.
+ */
+struct rpmsg_hdr {
+ __rpmsg32 src;
+ __rpmsg32 dst;
+ __rpmsg32 reserved;
+ __rpmsg16 len;
+ __rpmsg16 flags;
+ u8 data[];
+} __packed;
+
+/**
+ * struct rpmsg_device_ops - indirection table for the rpmsg_device operations
+ * @send: send data to core
+ * @recv: recv data to buf. data limited to buf_len bytes and buf is expected
+ * to have that much memory allocated
+ * @debug_data: calls virtqueue_dump debug utility to print vq data
+ */
+struct rpmsg_device_ops {
+ int (*send)(struct udevice *rpdev, void *data, int len);
+ int (*recv)(struct udevice *rpdev, rpmsg_rx_cb_t cb);
+ void (*debug_data)(struct udevice *rpdev, int vq_id);
+};
+
+#endif
diff --git a/drivers/rpmsg/sandbox_test_rpmsg.c b/drivers/rpmsg/sandbox_test_rpmsg.c
new file mode 100644
index 0000000000..c94254aa4e
--- /dev/null
+++ b/drivers/rpmsg/sandbox_test_rpmsg.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023, Advanced Micro Devices, Inc.
+ */
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <log.h>
+#include <rpmsg.h>
+#include <asm/io.h>
+
+#include "rpmsg_internal.h"
+
+#define TEST_RPMSG_BUF_SIZE 512
+
+struct sandbox_test_devdata {
+ char rpmsg_data_buf[TEST_RPMSG_BUF_SIZE];
+ int msg_len;
+};
+
+static int sandbox_test_rpmsg_send(struct udevice *rpdev, void *msg, int len)
+{
+ /* ToDo: (maintainer) Implement send functionality for sandbox drv */
+ struct sandbox_test_devdata *data = dev_get_priv(rpdev);
+
+ if (len > TEST_RPMSG_BUF_SIZE)
+ len = TEST_RPMSG_BUF_SIZE;
+
+ data->msg_len = len;
+ memcpy(data->rpmsg_data_buf, msg, len);
+
+ return 0;
+}
+
+static int sandbox_test_rpmsg_recv(struct udevice *rpdev, rpmsg_rx_cb_t cb)
+{
+ /* ToDo: (maintainer) Implement recv functionality for sandbox drv */
+ struct sandbox_test_devdata *data = dev_get_priv(rpdev);
+ int len;
+
+ len = data->msg_len;
+
+ /* as of now only 1 buffer is used for testing. so third arg is 1 */
+ if (cb)
+ cb(&data->rpmsg_data_buf, len, 1);
+
+ return 0;
+}
+
+static void sandbox_test_rpmsg_debug_data(struct udevice *rpdev, int vq_id)
+{
+ /*
+ * ToDo: (maintainer) Implement debug_data functionality
+ * for sandbox drv
+ */
+}
+
+int sandbox_test_rpmsg_probe(struct udevice *dev)
+{
+ /* ToDo: (maintainer) any init work */
+ debug("sandbox driver probbed\n");
+
+ return 0;
+}
+
+static const struct rpmsg_device_ops sandbox_test_rpmsg_ops = {
+ .send = sandbox_test_rpmsg_send,
+ .recv = sandbox_test_rpmsg_recv,
+ .debug_data = sandbox_test_rpmsg_debug_data,
+};
+
+static const struct udevice_id sandbox_ids[] = {
+ {.compatible = "sandbox,test-rpmsg"},
+ {}
+};
+
+U_BOOT_DRIVER(sandbox_test_rpmsg) = {
+ .name = "sandbox_test_rpmsg",
+ .of_match = sandbox_ids,
+ .id = UCLASS_RPMSG,
+ .ops = &sandbox_test_rpmsg_ops,
+ .probe = sandbox_test_rpmsg_probe,
+ .priv_auto = sizeof(struct sandbox_test_devdata),
+};
+
+U_BOOT_DRVINFO(sandbox_test_rpmsg) = {
+ .name = "sandbox_test_rpmsg",
+};
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
new file mode 100644
index 0000000000..a41fab177d
--- /dev/null
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2023, Advanced Micro Devices, Inc.
+ *
+ * RPMsg virtio driver
+ * File proted from the Linux kernel:
+ * drivers/rpmsg/virtio_rpmsg_bus
+ */
+
+#include <common.h>
+#include <asm/dma-mapping.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <linux/compat.h>
+#include <linux/errno.h>
+#include <remoteproc.h>
+#include <rproc_virtio.h>
+#include <rpmsg.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+
+#include "rpmsg_internal.h"
+
+/*
+ * Allocate buffers of 512 bytes each for communications. The
+ * number of buffers will be computed from the number of buffers supported
+ * by the vring, upto a maximum of 512 buffers (256 in each direction).
+ *
+ * Each buffer will have 16 bytes for the msg header and 496 bytes for
+ * the payload.
+ *
+ * This will utilize a maximum total space of 256KB for the buffers.
+ *
+ * In future add support for user-provided buffers.
+ * This will allow bigger buffer size flexibility, and can also be used
+ * to achieve zero-copy messaging.
+ *
+ * Note that these numbers are purely a decision of this driver - user
+ * can change this without changing anything in the firmware of the remote
+ * processor.
+ */
+#define MAX_RPMSG_NUM_BUFS (512)
+#define MAX_RPMSG_BUF_SIZE (512)
+
+#define RPMSG_UBOOT_ADDR (1035)
+
+/*
+ * Local addresses are dynamically allocated on-demand.
+ * We do not dynamically assign addresses from the low 1024 range,
+ * in order to reserve that address range for predefined services.
+ */
+#define RPMSG_RESERVED_ADDRESSES (1024)
+
+/**
+ * struct virtproc_info - virtual remote processor state
+ * @vdev: the virtio device
+ * @vqs: rx and tx virtqueues
+ * @rvq: rx virtqueue
+ * @svq: tx virtqueue
+ * @rbufs: kernel address of rx buffers
+ * @sbufs: kernel address of tx buffers
+ * @num_bufs: total number of buffers for rx and tx
+ * @buf_size: size of one rx or tx buffer
+ * @last_sbuf: index of last tx buffer used
+ * @bufs_dma: dma base addr of the buffers
+ *
+ * This structure stores the rpmsg state of a given virtio remote processor
+ * device (there might be several virtio proc devices for each physical
+ * remote processor).
+ */
+struct virtproc_info {
+ struct udevice *vdev;
+
+ union {
+ struct virtqueue *vqs[2];
+ struct {
+ struct virtqueue *rvq;
+ struct virtqueue *svq;
+ };
+ };
+ void *rbufs, *sbufs;
+ unsigned int num_bufs;
+ unsigned int buf_size;
+ int last_sbuf;
+ unsigned long bufs_dma;
+};
+
+/* The feature bitmap for virtio rpmsg */
+#define VIRTIO_RPMSG_F_NS 0 /* RP doesn't support name service */
+
+/**
+ * rpmsg_sg_init - initialize scatterlist according to cpu address location
+ * @sg: scatterlist to fill
+ * @cpu_addr: virtual address of the buffer
+ * @len: buffer length
+ *
+ * An internal function filling scatterlist according to virtual address
+ * location (in vmalloc or in kernel).
+ */
+static void
+rpmsg_sg_init(struct virtio_sg *sg, void *cpu_addr, unsigned int len)
+{
+ sg->addr = cpu_addr;
+ sg->length = len;
+}
+
+void *get_tx_buf(struct virtproc_info *vrp)
+{
+ unsigned int len;
+ void *ret;
+
+ if (!vrp->svq) {
+ dev_err(vrp->vdev, "send vq not available for dev\n");
+ return NULL;
+ }
+
+ /*
+ * either pick the next unused tx buffer
+ * (half of our buffers are used for sending messages)
+ */
+ if (vrp->last_sbuf < vrp->num_bufs / 2)
+ ret = vrp->sbufs + (vrp->buf_size * vrp->last_sbuf++);
+ /* or recycle a used one */
+ else
+ ret = virtqueue_get_buf(vrp->svq, &len);
+
+ return ret;
+}
+
+int virtio_rpmsg_bus_send(struct udevice *vdev, void *data, int len)
+{
+ struct virtproc_info *vrp;
+ struct rpmsg_hdr *msg;
+ struct virtio_sg sg = {0};
+ struct virtio_sg *sgs[] = { &sg };
+ int msg_len, err = 0;
+
+ vrp = dev_get_priv(vdev);
+
+ if (len > (vrp->buf_size - sizeof(struct rpmsg_hdr))) {
+ dev_err(vdev, "msg len %d is out of bound\n", len);
+ return -EMSGSIZE;
+ }
+
+ msg = get_tx_buf(vrp);
+ if (!msg) {
+ dev_err(vdev, "rpmsg can't get tx buffer\n");
+ return -ENOMEM;
+ }
+
+ msg->len = cpu_to_rpmsg16(len);
+ msg->src = cpu_to_rpmsg32(RPMSG_UBOOT_ADDR);
+ msg->dst = cpu_to_rpmsg32(RPMSG_UBOOT_ADDR);
+ msg->flags = 0;
+ msg->reserved = 0;
+ memcpy(msg->data, (char *)data, len);
+ msg_len = len + sizeof(*msg);
+ rpmsg_sg_init(&sg, msg, msg_len);
+
+ /* add messages to the remote processor's virtqueue */
+ err = virtqueue_add(vrp->svq, sgs, 1, 0);
+ if (err) {
+ /*
+ * need to reclaim the buffer or it's lost.
+ * memory won't lost but rpmsg won't use it for tx.
+ * this will wait for buffer management overhaul.
+ */
+ dev_err(vdev, "failed to add out buf\n");
+ return err;
+ }
+
+ /* tell remote processor that it has pending message to read */
+ virtqueue_kick(vrp->svq);
+
+ return 0;
+}
+
+static int rpmsg_recv_single(struct virtproc_info *vrp, struct rpmsg_hdr *msg,
+ unsigned int len, int msgs_received,
+ rpmsg_rx_cb_t cb)
+{
+ struct virtio_sg sg;
+ struct virtio_sg *sgs[] = { &sg };
+ bool little_endian = virtio_is_little_endian(vrp->vdev);
+ unsigned int msg_len = __rpmsg16_to_cpu(little_endian, msg->len);
+ int err;
+
+ debug("From: 0x%x, To: 0x%x, Len: %d, Flags: %d, Reserved: %d\n",
+ __rpmsg32_to_cpu(little_endian, msg->src),
+ __rpmsg32_to_cpu(little_endian, msg->dst), msg_len,
+ __rpmsg16_to_cpu(little_endian, msg->flags),
+ __rpmsg32_to_cpu(little_endian, msg->reserved));
+
+ /*
+ * We currently use fixed-sized buffers, so trivially sanitize
+ * the reported payload length.
+ */
+ if (len > vrp->buf_size ||
+ msg_len > (len - sizeof(struct rpmsg_hdr))) {
+ debug("inbound msg too big: (%d, %d)\n", len, msg_len);
+ return -EINVAL;
+ }
+
+ /* call rpmsg callback */
+ if (cb && msg->dst == RPMSG_UBOOT_ADDR)
+ cb(msg->data, msg_len, msgs_received);
+
+ /* publish the real size of the buffer */
+ rpmsg_sg_init(&sg, msg, vrp->buf_size);
+
+ /* add the buffer back to the remote processor's virtqueue */
+ err = virtqueue_add(vrp->rvq, sgs, 0, 1);
+ if (err < 0) {
+ dev_err(vrp->vdev, "failed to add a virtqueue buffer %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+int virtio_rpmsg_bus_recv(struct udevice *vdev, rpmsg_rx_cb_t cb)
+{
+ struct virtproc_info *vrp = dev_get_priv(vdev);
+ struct rpmsg_hdr *msg;
+ unsigned int len, msgs_received = 0;
+ int err;
+
+ rproc_flush_dcache(vdev->parent);
+
+ msg = virtqueue_get_buf(vrp->rvq, &len);
+ if (!msg) {
+ debug("uhm, incoming signal, but no used buffer ?\n");
+ return -ENODATA;
+ }
+
+ while (msg) {
+ err = rpmsg_recv_single(vrp, msg, len, msgs_received, cb);
+ if (err) {
+ debug("rpmsg failed to recv msg\n");
+ return err;
+ }
+
+ msgs_received++;
+
+ msg = virtqueue_get_buf(vrp->rvq, &len);
+ }
+
+ debug("rpmsg messages received = %d\n", msgs_received);
+
+ return 0;
+}
+
+static int virtio_rpmsg_bus_remove(struct udevice *vdev)
+{
+ virtio_reset(vdev);
+
+ return 0;
+}
+
+static int virtio_rpmsg_bus_probe(struct udevice *uvdev)
+{
+ struct rproc_rvdev_data *rvdev_data;
+ struct rproc_mem_entry *vdev_buf;
+ struct virtproc_info *vrp;
+ size_t total_buf_space;
+ int err = 0, i;
+ void *bufs_va;
+
+ vrp = dev_get_priv(uvdev);
+ if (!vrp) {
+ dev_err(uvdev, "vrp not available\n");
+ return -EINVAL;
+ }
+
+ vrp->vdev = uvdev;
+ rvdev_data = (struct rproc_rvdev_data *)dev_get_plat(uvdev->parent);
+ vdev_buf = rvdev_data->vdev_buf;
+ if (!vdev_buf) {
+ dev_err(uvdev, "vdev buffer isn't availablne\n");
+ return -ENOMEM;
+ }
+
+ err = virtio_find_vqs(uvdev, 2, vrp->vqs);
+ if (err) {
+ dev_err(uvdev, "failed to find vqs with err = %d\n", err);
+ return -EINVAL;
+ }
+
+ if (!vrp->vqs[0] || !vrp->vqs[1]) {
+ dev_err(uvdev, "failed to find vq\n");
+ return -EINVAL;
+ }
+
+ /* symmetric tx/rx vrings are expected */
+ if (virtqueue_get_vring_size(vrp->rvq) != virtqueue_get_vring_size(vrp->svq))
+ dev_warn(uvdev, "rx vq and tx vq are not same size\n");
+
+ /* less buffers are needed if vrings are small */
+ if (virtqueue_get_vring_size(vrp->rvq) < MAX_RPMSG_NUM_BUFS / 2)
+ vrp->num_bufs = virtqueue_get_vring_size(vrp->rvq) * 2;
+ else
+ vrp->num_bufs = MAX_RPMSG_NUM_BUFS;
+
+ vrp->buf_size = MAX_RPMSG_BUF_SIZE;
+ vrp->last_sbuf = 0;
+
+ total_buf_space = vrp->num_bufs * vrp->buf_size;
+
+ if (vdev_buf->len < total_buf_space) {
+ dev_err(uvdev, "not enough vdev buffer memory\n");
+ return -EINVAL;
+ }
+
+ /* allocate coherent memory for the buffers */
+ bufs_va = vdev_buf->va;
+ if (!bufs_va) {
+ err = -ENOMEM;
+ goto vqs_del;
+ }
+
+ /* half of the buffers is dedicated for RX */
+ vrp->rbufs = bufs_va;
+
+ /* and half is dedicated for TX */
+ vrp->sbufs = bufs_va + (total_buf_space / 2);
+
+ /* set up the receive buffers */
+ for (i = 0; i < vrp->num_bufs / 2; i++) {
+ struct virtio_sg sg;
+ struct virtio_sg *sgs[] = { &sg };
+
+ void *cpu_addr = vrp->rbufs + i * vrp->buf_size;
+
+ rpmsg_sg_init(&sg, cpu_addr, vrp->buf_size);
+
+ err = virtqueue_add(vrp->rvq, sgs, 0, 1);
+ if (err) /* sanity check; this can't really happen */
+ dev_warn(uvdev, "adding inbuf for dev %s, vq %d failed\n",
+ vrp->vdev->name, vrp->rvq->index);
+ }
+
+ /* ToDo: Support Named service announcement */
+
+ /* let remote know that host can receive data */
+ virtqueue_kick(vrp->rvq);
+
+ return 0;
+
+vqs_del:
+ virtio_del_vqs(uvdev);
+
+ return err;
+}
+
+static void virtio_rpmsg_bus_debug_data(struct udevice *uvdev, int vq_id)
+{
+ struct virtproc_info *vrp;
+
+ vrp = dev_get_priv(uvdev);
+
+ if (vrp->vqs[vq_id])
+ virtqueue_dump(vrp->vqs[vq_id]);
+ else
+ dev_info(uvdev, "virtqueue %d not found\n", vq_id);
+}
+
+static const struct rpmsg_device_ops virtio_rpmsg_dev_ops = {
+ .send = virtio_rpmsg_bus_send,
+ .recv = virtio_rpmsg_bus_recv,
+ .debug_data = virtio_rpmsg_bus_debug_data,
+};
+
+U_BOOT_DRIVER(virtio_rpmsg_bus) = {
+ .name = VIRTIO_RPROC_DRV_NAME,
+ .id = UCLASS_RPMSG,
+ .probe = virtio_rpmsg_bus_probe,
+ .remove = virtio_rpmsg_bus_remove,
+ .ops = &virtio_rpmsg_dev_ops,
+ .priv_auto = sizeof(struct virtproc_info),
+ .flags = DM_FLAG_ACTIVE_DMA,
+};
diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c
index 31bb21c534..0c76479c45 100644
--- a/drivers/virtio/virtio-uclass.c
+++ b/drivers/virtio/virtio-uclass.c
@@ -31,6 +31,7 @@ static const char *const virtio_drv_name[VIRTIO_ID_MAX_NUM] = {
[VIRTIO_ID_NET] = VIRTIO_NET_DRV_NAME,
[VIRTIO_ID_BLOCK] = VIRTIO_BLK_DRV_NAME,
[VIRTIO_ID_RNG] = VIRTIO_RNG_DRV_NAME,
+ [VIRTIO_ID_RPROC] = VIRTIO_RPROC_DRV_NAME,
};
int virtio_get_config(struct udevice *vdev, unsigned int offset,
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 307ad6931c..a1a4746a63 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -114,6 +114,7 @@ enum uclass_id {
UCLASS_REMOTEPROC, /* Remote Processor device */
UCLASS_RESET, /* Reset controller device */
UCLASS_RNG, /* Random Number Generator */
+ UCLASS_RPMSG, /* RPMsg device */
UCLASS_RTC, /* Real time clock device */
UCLASS_SCMI_AGENT, /* Interface with an SCMI server */
UCLASS_SCSI, /* SCSI device */
diff --git a/include/rpmsg.h b/include/rpmsg.h
new file mode 100644
index 0000000000..bed61240ca
--- /dev/null
+++ b/include/rpmsg.h
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _RPMSG_H_
+#define _RPMSG_H_
+
+/**
+ * struct dm_rpmsg_uclass_pdata - platform data for a CPU
+ * @name: Platform-specific way of naming the RPMsg platform device
+ * @driver_plat_data: driver specific platform data that may be needed.
+ *
+ * This can be accessed with dev_get_uclass_plat() for any UCLASS_RPMSG
+ * device.
+ *
+ */
+struct dm_rpmsg_uclass_pdata {
+ const char *name;
+ void *driver_plat_data;
+};
+
+typedef __u16 __bitwise __rpmsg16;
+typedef __u32 __bitwise __rpmsg32;
+typedef __u64 __bitwise __rpmsg64;
+
+#define RPMSG_ADDR_ANY 0xFFFFFFFF
+
+#define RPMSG_NAME_SIZE 32
+
+static inline bool rpmsg_is_little_endian(void)
+{
+#ifdef __LITTLE_ENDIAN
+ return true;
+#else
+ return false;
+#endif
+}
+
+static inline u16 __rpmsg16_to_cpu(bool little_endian, __rpmsg16 val)
+{
+ if (little_endian)
+ return le16_to_cpu((__force __le16)val);
+ else
+ return be16_to_cpu((__force __be16)val);
+}
+
+static inline __rpmsg16 __cpu_to_rpmsg16(bool little_endian, u16 val)
+{
+ if (little_endian)
+ return (__force __rpmsg16)cpu_to_le16(val);
+ else
+ return (__force __rpmsg16)cpu_to_be16(val);
+}
+
+static inline u32 __rpmsg32_to_cpu(bool little_endian, __rpmsg32 val)
+{
+ if (little_endian)
+ return le32_to_cpu((__force __le32)val);
+ else
+ return be32_to_cpu((__force __be32)val);
+}
+
+static inline __rpmsg32 __cpu_to_rpmsg32(bool little_endian, u32 val)
+{
+ if (little_endian)
+ return (__force __rpmsg32)cpu_to_le32(val);
+ else
+ return (__force __rpmsg32)cpu_to_be32(val);
+}
+
+static inline u64 __rpmsg64_to_cpu(bool little_endian, __rpmsg64 val)
+{
+ if (little_endian)
+ return le64_to_cpu((__force __le64)val);
+ else
+ return be64_to_cpu((__force __be64)val);
+}
+
+static inline __rpmsg64 __cpu_to_rpmsg64(bool little_endian, u64 val)
+{
+ if (little_endian)
+ return (__force __rpmsg64)cpu_to_le64(val);
+ else
+ return (__force __rpmsg64)cpu_to_be64(val);
+}
+
+static inline u16 rpmsg16_to_cpu(__rpmsg16 val)
+{
+ return __rpmsg16_to_cpu(rpmsg_is_little_endian(), val);
+}
+
+static inline __rpmsg16 cpu_to_rpmsg16(u16 val)
+{
+ return __cpu_to_rpmsg16(rpmsg_is_little_endian(), val);
+}
+
+static inline u32 rpmsg32_to_cpu(__rpmsg32 val)
+{
+ return __rpmsg32_to_cpu(rpmsg_is_little_endian(), val);
+}
+
+static inline __rpmsg32 cpu_to_rpmsg32(u32 val)
+{
+ return __cpu_to_rpmsg32(rpmsg_is_little_endian(), val);
+}
+
+static inline u64 rpmsg64_to_cpu(__rpmsg64 val)
+{
+ return __rpmsg64_to_cpu(rpmsg_is_little_endian(), val);
+}
+
+static inline __rpmsg64 cpu_to_rpmsg64(u64 val)
+{
+ return __cpu_to_rpmsg64(rpmsg_is_little_endian(), val);
+}
+
+typedef int (*rpmsg_rx_cb_t)(void *buf, int msg_len, u32 msgs_received);
+
+#if IS_ENABLED(CONFIG_RPMSG)
+
+int rpmsg_init(int core_id);
+int rpmsg_send(int core_id, void *data, int len);
+int rpmsg_recv(int core_id, rpmsg_rx_cb_t cb);
+
+#else
+int rpmsg_init(int core_id)
+{
+ return -ENODEV;
+}
+
+int rpmsg_send(int core_id, void *data, int len)
+{
+ return -ENODEV;
+}
+
+int rpmsg_recv(int core_id, rpmsg_rx_cb_t cb)
+{
+ return -ENODEV;
+}
+
+#endif /* IS_ENABLED(CONFIG_RPMSG) */
+
+#endif /* _RPMSG_H */
diff --git a/include/rproc_virtio.h b/include/rproc_virtio.h
index cbe8ff420f..96d0e3d15c 100644
--- a/include/rproc_virtio.h
+++ b/include/rproc_virtio.h
@@ -9,15 +9,19 @@
#include <remoteproc.h>
#include <dm/device.h>
-void rproc_flush_dcache(struct udevice *dev);
-
#ifndef CONFIG_SANDBOX
+void rproc_flush_dcache(struct udevice *dev);
+
int rproc_virtio_create_dev(struct udevice *parent, struct fw_rsc_vdev *rsc,
int offset);
#else
+void rproc_flush_dcache(struct udevice *dev)
+{
+}
+
int rproc_virtio_create_dev(struct udevice *parent, struct fw_rsc_vdev *rsc,
int offset)
{
diff --git a/include/virtio.h b/include/virtio.h
index 16d0f8aa7f..35274a3593 100644
--- a/include/virtio.h
+++ b/include/virtio.h
@@ -26,11 +26,13 @@
#define VIRTIO_ID_NET 1 /* virtio net */
#define VIRTIO_ID_BLOCK 2 /* virtio block */
#define VIRTIO_ID_RNG 4 /* virtio rng */
-#define VIRTIO_ID_MAX_NUM 5
+#define VIRTIO_ID_RPROC 7 /* virtio rproc */
+#define VIRTIO_ID_MAX_NUM 8
#define VIRTIO_NET_DRV_NAME "virtio-net"
#define VIRTIO_BLK_DRV_NAME "virtio-blk"
#define VIRTIO_RNG_DRV_NAME "virtio-rng"
+#define VIRTIO_RPROC_DRV_NAME "virtio-rpmsg-bus"
/* Status byte for guest to report progress, and synchronize features */
diff --git a/include/virtio_ring.h b/include/virtio_ring.h
index 4a9b4078ee..e0cd773913 100644
--- a/include/virtio_ring.h
+++ b/include/virtio_ring.h
@@ -264,6 +264,21 @@ struct virtqueue *vring_create_virtqueue(unsigned int index, unsigned int num,
unsigned int vring_align,
struct udevice *udev);
+/**
+ * vring_new_virtqueue - create a virtqueue at user defined address
+ *
+ * @index: the index of the queue
+ * @vring: vring created at user defined address
+ * @udev: the virtio transport udevice
+ * @return: the virtqueue pointer or NULL if failed
+ *
+ * This creates a virtqueue using vring address decided by the user of API
+ *
+ * This API is supposed to be called by the virtio transport driver in the
+ * virtio find_vqs() uclass method.
+ */
+struct virtqueue *vring_new_virtqueue(unsigned int index, struct vring vring,
+ struct udevice *udev);
/**
* vring_del_virtqueue - destroy a virtqueue
*
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 3799b1ae8f..732c35a496 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_RAM) += ram.o
obj-y += regmap.o
obj-$(CONFIG_REMOTEPROC) += remoteproc.o
obj-$(CONFIG_DM_RESET) += reset.o
+obj-$(CONFIG_RPMSG) += rpmsg.o
obj-$(CONFIG_SYSRESET) += sysreset.o
obj-$(CONFIG_DM_REGULATOR) += regulator.o
obj-$(CONFIG_DM_RNG) += rng.o
diff --git a/test/dm/rpmsg.c b/test/dm/rpmsg.c
new file mode 100644
index 0000000000..ecd2ba3680
--- /dev/null
+++ b/test/dm/rpmsg.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023, Advanced Micro Devices, Inc.
+ */
+#include <common.h>
+#include <dm.h>
+#include <elf.h>
+#include <errno.h>
+#include <rpmsg.h>
+#include <asm/io.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static char test_buf[512];
+
+static int rpmsg_rx_cb(void *buf, int msg_len, u32 msgs_received)
+{
+ memcpy(test_buf, buf, msg_len);
+
+ return 0;
+}
+
+/**
+ * dm_test_rpmsg() - test the operations after initializations
+ * @uts: unit test state
+ *
+ * @return: 0 if all test pass
+ */
+static int dm_test_rpmsg(struct unit_test_state *uts)
+{
+ ut_assertok(rpmsg_init(0));
+ ut_assertok(rpmsg_send(0, "test rpmsg", strlen("test rpmsg")));
+ ut_assertok(rpmsg_recv(0, rpmsg_rx_cb));
+
+ ut_asserteq_str(test_buf, "test rpmsg");
+
+ return 0;
+}
+
+DM_TEST(dm_test_rpmsg, UT_TESTF_SCAN_PDATA | UT_TESTF_FLAT_TREE);
--
2.25.1
More information about the U-Boot
mailing list