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@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@amd.com +S: Maintained +F: drivers/rpmsg/* +F: include/rpmsg.h +F: test/dm/rpmsg.c + SANDBOX M: Simon Glass sjg@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 ohad@wizery.com + * Brian Swetland swetland@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);