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.s...@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.s...@amd.com>
+S:     Maintained
+F:     drivers/rpmsg/*
+F:     include/rpmsg.h
+F:     test/dm/rpmsg.c
+
 SANDBOX
 M:     Simon Glass <s...@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@1 {
+               compatible = "sandbox,test-rpmsg";
+       };
+
+       rpmsg_2: rpmsg@2 {
+               compatible = "sandbox,test-rpmsg";
+       };
+
        rproc_1: rproc@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 <o...@wizery.com>
+ * Brian Swetland <swetl...@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


Reply via email to