Remote Processor Messaging (RPMsg) framework is used to communicate with remote processor. RPMsg is a virtio-based messaging bus that allows U-Boot drivers to communicate with remote processors available on the system. This patch also introduces new virtio transport layer driver.
To keep things minimal for now hard-coded endpoint is used in U-Boot to communicate with remote processor using RPMsg protocol. This will be modified to use multiple endpoints in future.
U-Boot can't receive interrupts from remote processor as per design. Hence, user has to know when the data will be available from remote processor and call rpmsg_recv accordingly. Mostly it will be called to receive response of query done using rpmsg_send.
Some code is already available to allocate memory for RPMsg however, that framework is mostly TI platform specific and isn't used by any other platforms. This series makes sure it does not break previous code for other platforms. If platforms wants to use this new framework it is expected to set "support_rpmsg_virtio" flag in relative remoteproc device's platform data.
Also the framework introduced by this series is ported from the Linux kernel so other platforms which already support RPMsg in the Linux kernel can easily use this framework in U-Boot as well.
---
Tanmay Shah (10): remoteproc: move resource table definition firmware: zynqmp: move zynqmp_pm_request_node API remoteproc: add remoteproc virtio transport driver drivers: add RPMsg framework rpmsg: add sample client driver cmd: add rpmsg framework commands remoteproc: add attach/detach commands remoteproc: add zynqmp driver configs: zynqmp: enable remoteproc and rpmsg configs: sandbox: enable rpmsg
MAINTAINERS | 15 + arch/sandbox/dts/test.dts | 8 + cmd/Kconfig | 6 + cmd/Makefile | 1 + cmd/remoteproc.c | 14 +- cmd/rpmsg.c | 61 +++ configs/sandbox_defconfig | 4 + configs/xilinx_zynqmp_virt_defconfig | 6 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/firmware-zynqmp.c | 27 ++ drivers/power/domain/zynqmp-power-domain.c | 7 - drivers/remoteproc/Kconfig | 20 + drivers/remoteproc/Makefile | 2 + drivers/remoteproc/rproc-elf-loader.c | 33 -- drivers/remoteproc/rproc-uclass.c | 125 +++++- drivers/remoteproc/rproc_virtio.c | 422 +++++++++++++++++++++ drivers/remoteproc/xlnx_rproc.c | 411 ++++++++++++++++++++ drivers/rpmsg/Kconfig | 39 ++ drivers/rpmsg/Makefile | 13 + drivers/rpmsg/rpmsg-uclass.c | 156 ++++++++ drivers/rpmsg/rpmsg_internal.h | 52 +++ drivers/rpmsg/rpmsg_sample_client.c | 63 +++ drivers/rpmsg/sandbox_test_rpmsg.c | 88 +++++ drivers/rpmsg/virtio_rpmsg_bus.c | 384 +++++++++++++++++++ drivers/virtio/virtio-uclass.c | 1 + drivers/virtio/virtio_ring.c | 16 + include/dm/uclass-id.h | 1 + include/remoteproc.h | 130 ++++++- include/rpmsg.h | 145 +++++++ include/rproc_virtio.h | 33 ++ include/virtio.h | 7 +- include/virtio_ring.h | 32 ++ include/zynqmp_firmware.h | 30 +- test/dm/Makefile | 1 + test/dm/rpmsg.c | 41 ++ 36 files changed, 2321 insertions(+), 76 deletions(-) create mode 100644 cmd/rpmsg.c create mode 100644 drivers/remoteproc/rproc_virtio.c create mode 100644 drivers/remoteproc/xlnx_rproc.c 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/rpmsg_sample_client.c 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 include/rproc_virtio.h create mode 100644 test/dm/rpmsg.c
base-commit: 94e7cb181ad6bd25d36a11c58e0df64e9f815888
Resource table is defined in multiple files. Instead move definition to remoteproc header file and include header file in multiple c files where resource table definition is needed.
Signed-off-by: Tanmay Shah tanmay.shah@amd.com --- drivers/remoteproc/rproc-elf-loader.c | 33 --------------------------- drivers/remoteproc/rproc-uclass.c | 7 ------ include/remoteproc.h | 33 +++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 40 deletions(-)
diff --git a/drivers/remoteproc/rproc-elf-loader.c b/drivers/remoteproc/rproc-elf-loader.c index b185a6cafb..8a13872f85 100644 --- a/drivers/remoteproc/rproc-elf-loader.c +++ b/drivers/remoteproc/rproc-elf-loader.c @@ -12,39 +12,6 @@ #include <dm/device_compat.h> #include <linux/compat.h>
-/** - * struct resource_table - firmware resource table header - * @ver: version number - * @num: number of resource entries - * @reserved: reserved (must be zero) - * @offset: array of offsets pointing at the various resource entries - * - * A resource table is essentially a list of system resources required - * by the remote processor. It may also include configuration entries. - * If needed, the remote processor firmware should contain this table - * as a dedicated ".resource_table" ELF section. - * - * Some resources entries are mere announcements, where the host is informed - * of specific remoteproc configuration. Other entries require the host to - * do something (e.g. allocate a system resource). Sometimes a negotiation - * is expected, where the firmware requests a resource, and once allocated, - * the host should provide back its details (e.g. address of an allocated - * memory region). - * - * The header of the resource table, as expressed by this structure, - * contains a version number (should we need to change this format in the - * future), the number of available resource entries, and their offsets - * in the table. - * - * Immediately following this header are the resource entries themselves. - */ -struct resource_table { - u32 ver; - u32 num; - u32 reserved[2]; - u32 offset[0]; -} __packed; - /* Basic function to verify ELF32 image format */ int rproc_elf32_sanity_check(ulong addr, ulong size) { diff --git a/drivers/remoteproc/rproc-uclass.c b/drivers/remoteproc/rproc-uclass.c index 50bcc9030e..d697639cdd 100644 --- a/drivers/remoteproc/rproc-uclass.c +++ b/drivers/remoteproc/rproc-uclass.c @@ -23,13 +23,6 @@
DECLARE_GLOBAL_DATA_PTR;
-struct resource_table { - u32 ver; - u32 num; - u32 reserved[2]; - u32 offset[0]; -} __packed; - typedef int (*handle_resource_t) (struct udevice *, void *, int offset, int avail);
static struct resource_table *rsc_table; diff --git a/include/remoteproc.h b/include/remoteproc.h index f48054de6b..af5c584e6e 100644 --- a/include/remoteproc.h +++ b/include/remoteproc.h @@ -15,6 +15,39 @@ */ #include <dm/platdata.h> /* For platform data support - non dt world */
+/** + * struct resource_table - firmware resource table header + * @ver: version number + * @num: number of resource entries + * @reserved: reserved (must be zero) + * @offset: array of offsets pointing at the various resource entries + * + * A resource table is essentially a list of system resources required + * by the remote processor. It may also include configuration entries. + * If needed, the remote processor firmware should contain this table + * as a dedicated ".resource_table" ELF section. + * + * Some resources entries are mere announcements, where the host is informed + * of specific remoteproc configuration. Other entries require the host to + * do something (e.g. allocate a system resource). Sometimes a negotiation + * is expected, where the firmware requests a resource, and once allocated, + * the host should provide back its details (e.g. address of an allocated + * memory region). + * + * The header of the resource table, as expressed by this structure, + * contains a version number (should we need to change this format in the + * future), the number of available resource entries, and their offsets + * in the table. + * + * Immediately following this header are the resource entries themselves. + */ +struct resource_table { + u32 ver; + u32 num; + u32 reserved[2]; + u32 offset[0]; +} __packed; + /** * struct fw_rsc_hdr - firmware resource entry header * @type: resource type
Requesting node to firmware can be used by multiple drivers. Move this API to firmware driver from power driver so others can use the same interface.
Signed-off-by: Tanmay Shah tanmay.shah@amd.com --- drivers/firmware/firmware-zynqmp.c | 7 +++++++ drivers/power/domain/zynqmp-power-domain.c | 7 ------- include/zynqmp_firmware.h | 14 ++++++++------ 3 files changed, 15 insertions(+), 13 deletions(-)
diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c index ab4c4f1a69..0897992405 100644 --- a/drivers/firmware/firmware-zynqmp.c +++ b/drivers/firmware/firmware-zynqmp.c @@ -195,6 +195,13 @@ int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value) return ret; }
+int zynqmp_pm_request_node(const u32 node, const u32 capabilities, + const u32 qos, const enum zynqmp_pm_request_ack ack) +{ + return xilinx_pm_request(PM_REQUEST_NODE, node, capabilities, + qos, ack, NULL); +} + int zynqmp_pm_is_function_supported(const u32 api_id, const u32 id) { int ret; diff --git a/drivers/power/domain/zynqmp-power-domain.c b/drivers/power/domain/zynqmp-power-domain.c index 5ee9e020fb..d385dc5530 100644 --- a/drivers/power/domain/zynqmp-power-domain.c +++ b/drivers/power/domain/zynqmp-power-domain.c @@ -14,13 +14,6 @@
#include <zynqmp_firmware.h>
-static int zynqmp_pm_request_node(const u32 node, const u32 capabilities, - const u32 qos, const enum zynqmp_pm_request_ack ack) -{ - return xilinx_pm_request(PM_REQUEST_NODE, node, capabilities, - qos, ack, NULL); -} - static int zynqmp_power_domain_request(struct power_domain *power_domain) { int ret = 0; diff --git a/include/zynqmp_firmware.h b/include/zynqmp_firmware.h index 18a87d2749..ce086f48d4 100644 --- a/include/zynqmp_firmware.h +++ b/include/zynqmp_firmware.h @@ -444,12 +444,20 @@ enum pm_gem_config_type { */ #define PAYLOAD_ARG_CNT 5U
+enum zynqmp_pm_request_ack { + ZYNQMP_PM_REQUEST_ACK_NO = 1, + ZYNQMP_PM_REQUEST_ACK_BLOCKING = 2, + ZYNQMP_PM_REQUEST_ACK_NON_BLOCKING = 3, +}; + unsigned int zynqmp_firmware_version(void); int zynqmp_pmufw_node(u32 id); int zynqmp_pmufw_config_close(void); int zynqmp_pmufw_load_config_object(const void *cfg_obj, size_t size); int xilinx_pm_request(u32 api_id, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 *ret_payload); +int zynqmp_pm_request_node(const u32 node, const u32 capabilities, + const u32 qos, enum zynqmp_pm_request_ack ack); int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value); int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config, u32 value); @@ -474,12 +482,6 @@ int zynqmp_mmio_write(const u32 address, const u32 mask, const u32 value); #define PM_CONFIG_IPI_PSU_CORTEXR5_0_MASK 0x00000100 #define PM_CONFIG_IPI_PSU_CORTEXR5_1_MASK 0x00000200
-enum zynqmp_pm_request_ack { - ZYNQMP_PM_REQUEST_ACK_NO = 1, - ZYNQMP_PM_REQUEST_ACK_BLOCKING = 2, - ZYNQMP_PM_REQUEST_ACK_NON_BLOCKING = 3, -}; - /* Node capabilities */ #define ZYNQMP_PM_CAPABILITY_ACCESS 0x1U #define ZYNQMP_PM_CAPABILITY_CONTEXT 0x2U
Remoteproc virtio is virtio transport layer driver for remoteproc. Implement virtio config ops used by actual virtio driver using remoteproc virtio device
Ported from the Linux kernel version 6.4-rc2 (d848a4819d85), Introduced in kernel version v3.10 (ac8954a41393) file: drivers/remoteproc/remoteproc_virtio.c.
Signed-off-by: Tanmay Shah tanmay.shah@amd.com --- MAINTAINERS | 6 + drivers/remoteproc/Kconfig | 11 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/rproc-uclass.c | 42 ++- drivers/remoteproc/rproc_virtio.c | 422 ++++++++++++++++++++++++++++++ drivers/virtio/virtio_ring.c | 16 ++ include/remoteproc.h | 66 +++-- include/rproc_virtio.h | 29 ++ include/virtio.h | 3 + include/virtio_ring.h | 17 ++ 10 files changed, 593 insertions(+), 20 deletions(-) create mode 100644 drivers/remoteproc/rproc_virtio.c create mode 100644 include/rproc_virtio.h
diff --git a/MAINTAINERS b/MAINTAINERS index 87991cccdd..c4a32a0956 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1319,6 +1319,12 @@ S: Maintained T: git https://source.denx.de/u-boot/custodians/u-boot-nand-flash.git F: drivers/mtd/nand/raw/
+REMOTEPROC +M: Tanmay Shah tanmay.shah@amd.com +S: Maintained +F: drivers/remoteproc/rproc_virtio.c +F: include/rproc_virtio.h + RISC-V M: Rick Chen rick@andestech.com M: Leo ycliang@andestech.com diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 27e4a60ff5..b758c248e4 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -102,4 +102,15 @@ config REMOTEPROC_TI_IPU help Say 'y' here to add support for TI' K3 remoteproc driver.
+config REMOTEPROC_VIRTIO + bool "Support remoteproc virtio devices" + select REMOTEPROC + select VIRTIO + depends on DM + help + Say 'y' here to add support of remoteproc virtio devices. + rproc_virtio is virtio transport layer driver. The transport + drivers provide a set of ops for the real virtio device + driver to call. + endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index fbe9c172bc..61fdb87efb 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_REMOTEPROC_TI_K3_R5F) += ti_k3_r5f_rproc.o obj-$(CONFIG_REMOTEPROC_TI_POWER) += ti_power_proc.o obj-$(CONFIG_REMOTEPROC_TI_PRU) += pru_rproc.o obj-$(CONFIG_REMOTEPROC_TI_IPU) += ipu_rproc.o +obj-$(CONFIG_REMOTEPROC_VIRTIO) += rproc_virtio.o diff --git a/drivers/remoteproc/rproc-uclass.c b/drivers/remoteproc/rproc-uclass.c index d697639cdd..3aebaf6187 100644 --- a/drivers/remoteproc/rproc-uclass.c +++ b/drivers/remoteproc/rproc-uclass.c @@ -14,6 +14,7 @@ #include <malloc.h> #include <virtio_ring.h> #include <remoteproc.h> +#include <rproc_virtio.h> #include <asm/io.h> #include <dm/device-internal.h> #include <dm.h> @@ -279,6 +280,33 @@ static int rproc_config_pagetable(struct udevice *dev, unsigned int virt, return 0; }
+/** + * rproc_find_res_by_name() - After parsing the resource table add the mappings + * @dev: device we finished probing + * @name: name of rproc_mem_entry resource + * + * Return: If failed NULL, else first carveout entry with matching name. + */ +struct rproc_mem_entry *rproc_find_res_by_name(struct udevice *dev, + const char *name) +{ + struct rproc *rproc = rproc_get_cfg(dev); + struct rproc_mem_entry *mapping = NULL; + int ret; + + if (!rproc) + return NULL; + + list_for_each_entry(mapping, &rproc->mappings.node, node) { + ret = strcmp(mapping->name, name); + if (!ret) + return mapping; + } + + debug("%s: %s carveout not found\n", dev->name, name); + return NULL; +} + UCLASS_DRIVER(rproc) = { .id = UCLASS_REMOTEPROC, .name = "remoteproc", @@ -688,6 +716,7 @@ static int alloc_vring(struct udevice *dev, struct fw_rsc_vdev *rsc, int i) static int handle_vdev(struct udevice *dev, struct fw_rsc_vdev *rsc, int offset, int avail) { + struct rproc *rproc; int i, ret; void *pa;
@@ -720,7 +749,18 @@ static int handle_vdev(struct udevice *dev, struct fw_rsc_vdev *rsc, }
/* - * allocate the vrings + * If virtio device creation is supported, then prefer to get vrings + * during find_vq op + */ + rproc = rproc_get_cfg(dev); + + if (rproc && rproc->support_rpmsg_virtio && + !(IS_ENABLED(CONFIG_SANDBOX))) { + return rproc_virtio_create_dev(dev, rsc, offset); + } + + /* + * allocate the vrings traditional way */ for (i = 0; i < rsc->num_of_vrings; i++) { ret = alloc_vring(dev, rsc, i); diff --git a/drivers/remoteproc/rproc_virtio.c b/drivers/remoteproc/rproc_virtio.c new file mode 100644 index 0000000000..5e7cdfa12f --- /dev/null +++ b/drivers/remoteproc/rproc_virtio.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Remote Processor Messaging transport + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * Copyright (C) 2023, Advanced Micro Devices Inc. + * + * VirtIO RPMsg transport driver + * Ported from Linux drivers/remoteproc/remoteproc_virtio.c + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <remoteproc.h> +#include <rproc_virtio.h> +#include <virtio.h> +#include <virtio_ring.h> +#include <virtio_types.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <dm/device-internal.h> +#include <linux/bug.h> +#include <linux/compat.h> +#include <linux/err.h> + +/** + * rproc_flush_dcache() - flush shared memory regions + * @dev: valid remoteproc virtio device + * + * Some platforms have dcache enabled. If dcache is on then data written + * to shared memory is actually written to cache memory. This function + * checks if cache is on or not and if it is on, then it performs + * flush and invalidate operation on address range or reserved memory + * created by platform driver for remoteproc + * + * Return: none + */ +void rproc_flush_dcache(struct udevice *dev) +{ + struct rproc *rproc = rproc_get_cfg(dev->parent); + struct rproc_mem_entry *mapping = NULL; + struct list_head *mapping_node = NULL; + + /* If dcache is off, don't perform cache operation */ + if (dcache_status() == false) + return; + + /* + * If cache is on, then flush cache + * specially reserved mem regions for, + * resource table, vrings and vdevbuffer of platform dev + */ + list_for_each(mapping_node, &rproc->mappings.node) { + mapping = container_of(mapping_node, struct rproc_mem_entry, + node); + + /* + * First flush data so current data from cache is written + * to actual memory from cache. Then invalidate cache so + * next time data will be accessed from actual memory + */ + flush_dcache_range(mapping->da, mapping->da + mapping->len); + invalidate_dcache_range(mapping->da, mapping->da + mapping->len); + } +} + +int rproc_virtio_create_dev(struct udevice *parent, struct fw_rsc_vdev *rsc, + int offset) +{ + struct rproc_rvdev_data *rvdev_data; + struct udevice *vdev = NULL; + struct rproc *rproc; + char *dev_name; + int ret; + + rproc = rproc_get_cfg(parent); + if (!rproc) + return -EINVAL; + + rvdev_data = kzalloc(sizeof(*rvdev_data), GFP_KERNEL); + rvdev_data->id = rsc->id; + rvdev_data->rsc_offset = offset; + + dev_name = kcalloc(32, sizeof(char), GFP_KERNEL); + sprintf(dev_name, "rproc-virtio#%d", rproc->rproc_id); + + rvdev_data->index = rproc->rproc_id; + ret = device_bind(parent, DM_DRIVER_GET(rproc_virtio), + dev_name, rvdev_data, ofnode_null(), + &vdev); + if (ret) { + debug("failed to bind %s device\n", dev_name); + return ret; + } + + ret = device_probe(vdev); + if (ret) { + debug("probing device %s failed\n", dev_name); + return ret; + } + + free(dev_name); + dev_name = NULL; + + return 0; +} + +static int rproc_virtio_get_config(struct udevice *dev, unsigned int offset, + void *buf, unsigned int len) +{ + struct rproc_rvdev_data *rvdev_data; + struct fw_rsc_vdev *rsc; + struct rproc *rproc; + void *cfg; + + rvdev_data = dev_get_plat(dev); + rproc = rproc_get_cfg(dev->parent); + + rsc = (void *)rproc->table_ptr + rvdev_data->rsc_offset; + + /* flush cache before reading configurations */ + rproc_flush_dcache(dev); + + cfg = &rsc->vring[rsc->num_of_vrings]; + + if (offset + len > rsc->config_len || offset + len < len) { + dev_err(dev, "rproc_virtio_get: access out of bounds\n"); + return -EINVAL; + } + + memcpy(buf, cfg + offset, len); + + return 0; +} + +static void rproc_transport_features(struct udevice *dev) +{ + /* + * Packed ring isn't enabled on remoteproc for now, + * because remoteproc uses vring_new_virtqueue() which + * creates virtio rings on preallocated memory. + */ + __virtio_clear_bit(dev, VIRTIO_F_RING_PACKED); +} + +static int rproc_virtio_set_config(struct udevice *dev, unsigned int offset, + const void *buf, unsigned int len) +{ + struct rproc_rvdev_data *rvdev_data; + struct fw_rsc_vdev *rsc; + struct rproc *rproc; + void *cfg; + + rvdev_data = dev_get_plat(dev); + rproc = rproc_get_cfg(dev->parent); + + rsc = (void *)rproc->table_ptr + rvdev_data->rsc_offset; + cfg = &rsc->vring[rsc->num_of_vrings]; + + if (offset + len > rsc->config_len || offset + len < len) { + dev_err(dev, "rproc_virtio_set: access out of bounds\n"); + return -EINVAL; + } + + memcpy(cfg + offset, buf, len); + rproc_flush_dcache(dev); + + return 0; +} + +static int rproc_virtio_get_status(struct udevice *dev, u8 *status) +{ + struct rproc_rvdev_data *rvdev_data; + struct fw_rsc_vdev *rsc; + struct rproc *rproc; + + rvdev_data = dev_get_plat(dev); + rproc = rproc_get_cfg(dev->parent); + + rsc = (void *)rproc->table_ptr + rvdev_data->rsc_offset; + + *status = rsc->status; + + rproc_flush_dcache(dev); + + return 0; +} + +static int rproc_virtio_set_status(struct udevice *dev, u8 status) +{ + struct rproc_rvdev_data *rvdev_data; + struct fw_rsc_vdev *rsc; + struct rproc *rproc; + + rvdev_data = dev_get_plat(dev); + rproc = rproc_get_cfg(dev->parent); + + rsc = (void *)rproc->table_ptr + rvdev_data->rsc_offset; + + rsc->status = status; + + rproc_flush_dcache(dev); + + return 0; +} + +static int rproc_virtio_reset(struct udevice *dev) +{ + struct rproc_rvdev_data *rvdev_data; + struct fw_rsc_vdev *rsc; + struct rproc *rproc; + + rvdev_data = dev_get_plat(dev); + rproc = rproc_get_cfg(dev->parent); + + rsc = (void *)rproc->table_ptr + rvdev_data->rsc_offset; + + rsc->status = 0; + + rproc_flush_dcache(dev); + + return 0; +} + +static int rproc_virtio_get_features(struct udevice *dev, u64 *features) +{ + struct rproc_rvdev_data *rvdev_data; + struct fw_rsc_vdev *rsc; + struct rproc *rproc; + + rvdev_data = dev_get_plat(dev); + rproc = rproc_get_cfg(dev->parent); + + rsc = (void *)rproc->table_ptr + rvdev_data->rsc_offset; + + rproc_flush_dcache(dev); + + *features = rsc->dfeatures; + + return 0; +} + +static int rproc_virtio_set_features(struct udevice *dev) +{ + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct rproc_rvdev_data *rvdev_data; + struct fw_rsc_vdev *rsc; + struct rproc *rproc; + + rvdev_data = dev_get_plat(dev); + rproc = rproc_get_cfg(dev->parent); + + rsc = (void *)rproc->table_ptr + rvdev_data->rsc_offset; + + /* Give virtio_rproc a chance to accept features. */ + rproc_transport_features(dev); + + /* reject any features > 32 bits! */ + if (WARN_ON((u32)uc_priv->features != uc_priv->features)) + return -EINVAL; + + /* + * Remember the finalized features of our vdev, and provide it + * to the remote processor once it is powered on. + */ + rsc->gfeatures = uc_priv->features; + + rproc_flush_dcache(dev); + + return 0; +} + +static void rproc_virtio_del_vq(struct virtqueue *vq) +{ + vring_del_virtqueue(vq); +} + +static int rproc_virtio_del_vqs(struct udevice *vdev) +{ + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(vdev); + struct virtqueue *vq, *n; + + list_for_each_entry_safe(vq, n, &uc_priv->vqs, list) + rproc_virtio_del_vq(vq); + + return 0; +} + +static int rproc_virtio_find_vqs(struct udevice *vdev, unsigned int nvqs, + struct virtqueue *vqs[]) +{ + int queue_index = 0, align, i, core_id, da; + struct rproc_rvdev_data *rvdev_data; + struct fw_rsc_vdev_vring *rvring; + struct rproc_mem_entry *mapping; + struct fw_rsc_vdev *rsc; + char name[32] = {0}; + struct rproc *rproc; + struct vring vring; + unsigned int num; + + rvdev_data = dev_get_plat(vdev); + rproc = rproc_get_cfg(vdev->parent); + + if (nvqs > 2) + return -EINVAL; + + core_id = rvdev_data->index; + + rsc = (void *)rproc->table_ptr + rvdev_data->rsc_offset; + + for (i = 0; i < nvqs; i++) { + sprintf(name, "vdev%dvring%d", core_id, i); + mapping = rproc_find_res_by_name(vdev->parent, name); + if (!mapping) { + dev_err(vdev, "carveout %s not found\n", name); + return -EINVAL; + } + + num = rsc->vring[i].num; + /* assume num is a power of 2 */ + if (num & (num - 1)) { + dev_err(vdev, "Bad virtqueue length %u\n", num); + return -EINVAL; + } + + align = rsc->vring[i].align; + rvring = &rvdev_data->vring[i]; + + memset(mapping->va, 0, vring_size(num, align)); + vring_init(&vring, num, mapping->va, align, NULL); + + vqs[i] = vring_new_virtqueue(queue_index, vring, vdev); + if (!vqs[i]) { + dev_err(vdev, "failed to create vq\n"); + return -EINVAL; + } + + rvring->da = da; + rvring->notifyid = vqs[i]->index; + rvring->num = num; + rvring->align = align; + + /* update vring in resource table */ + rsc->vring[i].notifyid = rvring->notifyid; + rsc->vring[i].da = mapping->da; + rsc->vring[i].pa = mapping->da; + queue_index++; + + rproc_flush_dcache(vdev); + } + + return 0; +} + +static int rproc_virtio_notify(struct udevice *dev, struct virtqueue *vq) +{ + struct dm_rproc_ops *ops; + + ops = rproc_get_ops(dev->parent); + if (!ops || !ops->kick) { + dev_err(dev, "kick op not available for dev %s\n", + dev->parent->name); + return -EINVAL; + } + + rproc_flush_dcache(dev); + + return ops->kick(dev->parent, vq->index); +} + +static int rproc_virtio_probe(struct udevice *vdev) +{ + struct virtio_dev_priv *ucpriv = dev_get_uclass_priv(vdev); + struct rproc_rvdev_data *rvdev_data; + struct udevice *rproc_dev; + char rvdev_name[32] = {0}; + + rvdev_data = dev_get_plat(vdev); + + rproc_dev = vdev->parent; + + sprintf(rvdev_name, "vdev%dbuffer", rvdev_data->index); + rvdev_data->vdev_buf = rproc_find_res_by_name(vdev->parent, rvdev_name); + if (!rvdev_data->vdev_buf) { + dev_err(vdev, "%s carveout not found", rvdev_name); + return -EINVAL; + } + + ucpriv->vdev = vdev; + ucpriv->device = rvdev_data->id; + + return 0; +} + +static const struct dm_virtio_ops rproc_virtio_ops = { + .get_config = rproc_virtio_get_config, + .set_config = rproc_virtio_set_config, + .get_status = rproc_virtio_get_status, + .set_status = rproc_virtio_set_status, + .reset = rproc_virtio_reset, + .get_features = rproc_virtio_get_features, + .set_features = rproc_virtio_set_features, + .find_vqs = rproc_virtio_find_vqs, + .del_vqs = rproc_virtio_del_vqs, + .notify = rproc_virtio_notify, +}; + +static const struct udevice_id rproc_virtio_ids[] = { + { .compatible = "rproc,virtio" }, +}; + +U_BOOT_DRIVER(rproc_virtio) = { + .name = "rproc_virtio", + .id = UCLASS_VIRTIO, + .of_match = rproc_virtio_ids, + .ops = &rproc_virtio_ops, + .probe = rproc_virtio_probe, + .plat_auto = sizeof(struct rproc_rvdev_data), +}; diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index c9adcce5c0..4a9645fac4 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -331,6 +331,22 @@ static struct virtqueue *__vring_new_virtqueue(unsigned int index, return vq; }
+struct virtqueue *vring_new_virtqueue(unsigned int index, + struct vring vring, + struct udevice *udev) +{ + struct virtqueue *vq; + + vq = __vring_new_virtqueue(index, vring, udev); + if (!vq) + return NULL; + + debug("(%s): created vring @ %p for vq @ %p\n", udev->name, + vring.desc, vq); + + return vq; +} + struct virtqueue *vring_create_virtqueue(unsigned int index, unsigned int num, unsigned int vring_align, struct udevice *udev) diff --git a/include/remoteproc.h b/include/remoteproc.h index af5c584e6e..4251af52bd 100644 --- a/include/remoteproc.h +++ b/include/remoteproc.h @@ -343,25 +343,20 @@ enum rproc_crash_type { #define RPMSG_TOTAL_BUF_SPACE (RPMSG_NUM_BUFS * RPMSG_BUF_SIZE)
/** - * struct rproc_vring - remoteproc vring state - * @va: virtual address - * @dma: dma address - * @len: length, in bytes - * @da: device address - * @align: vring alignment - * @notifyid: rproc-specific unique vring index - * @rvdev: remote vdev - * @vq: the virtqueue of this vring - */ -struct rproc_vring { - void *va; - dma_addr_t dma; - int len; - u32 da; - u32 align; - int notifyid; - struct rproc_vdev *rvdev; - struct virtqueue *vq; + * struct rproc_rvdev_data - remoteproc virito device data + * + * @rsc_offset: resource offset + * @id: virtio dev id + * @index: vdev position vs other vdev declared in resource table + * @vring: store tx and rx vrings description + * @vdev_buf: vdev0buffer carveout mapping + */ +struct rproc_rvdev_data { + u32 rsc_offset; + unsigned int id; + u32 index; + struct fw_rsc_vdev_vring vring[2]; + struct rproc_mem_entry *vdev_buf; };
/** struct rproc - structure with all processor specific information for @@ -383,6 +378,7 @@ struct rproc_vring { * * @entry_point: address that is the entry point for the remote core. This * address is in the memory view of the remotecore. + * @rproc_id: per core id * * @load_addr: Address to which the bootloader loads the firmware from * persistent storage before invoking the ELF loader. Keeping this address @@ -393,6 +389,11 @@ struct rproc_vring { * @firmware_name: Name of the file that is expected to contain the ELF image. * * @has_rsc_table: Flag populated after parsing the ELF binary on target. + * @support_rpmsg_virtio: set in rproc platform driver to support new rpmsg framework + * @mappings: list of reserved memory regions created by platform driver + * such as vring0, vring1, vdevbuffer etc... + * @table_ptr: holds address of resource table from shared memory between host + * and remote processors */
struct rproc { @@ -403,6 +404,7 @@ struct rproc { unsigned long mmu_base_addr[2]; unsigned long load_addr; unsigned long entry_point; + int rproc_id; char *core_name; char *firmware_name; char *ptn; @@ -414,6 +416,9 @@ struct rproc { struct rproc_intmem_to_l3_mapping *intmem_to_l3_mapping; u32 trace_pa; u32 trace_len; + bool support_rpmsg_virtio; + struct rproc_mem_entry mappings; + struct resource_table *table_ptr; };
extern struct rproc *rproc_cfg_arr[2]; @@ -444,6 +449,7 @@ struct dm_rproc_uclass_pdata { const char *name; enum rproc_mem_type mem_type; void *driver_plat_data; + struct rproc *rproc; };
/** @@ -534,10 +540,22 @@ struct dm_rproc_ops { unsigned long align); unsigned int (*config_pagetable)(struct udevice *dev, unsigned int virt, unsigned int phys, unsigned int len); + struct resource_table *(*get_loaded_rsc_table)(struct udevice *dev, + int *tablesz); + /** + * kick() - kick the remote device for communication (needed for rpmsg) + * + * @dev: Remote proc device + * @notify_id: vq id to be notified + * @return 0 on success, 1 if not responding, -ve on other errors. + */ + int (*kick)(struct udevice *dev, int notify_id); };
/* Accessor */ #define rproc_get_ops(dev) ((struct dm_rproc_ops *)(dev)->driver->ops) +#define rproc_get_cfg(dev) (((struct dm_rproc_uclass_pdata *) \ + dev_get_plat(dev))->rproc)
#if CONFIG_IS_ENABLED(REMOTEPROC) /** @@ -737,6 +755,10 @@ unsigned long rproc_parse_resource_table(struct udevice *dev, struct resource_table *rproc_find_resource_table(struct udevice *dev, unsigned int addr, int *tablesz); +struct resource_table *rproc_get_loaded_rsc_table(struct udevice *dev, + struct rproc *cfg); +struct rproc_mem_entry *rproc_find_res_by_name(struct udevice *dev, + const char *name); #else static inline int rproc_init(void) { return -ENOSYS; } static inline int rproc_dev_init(int id) { return -ENOSYS; } @@ -776,6 +798,12 @@ static inline int rproc_elf_load_rsc_table(struct udevice *dev, ulong fw_addr, ulong fw_size, ulong *rsc_addr, ulong *rsc_size) { return -ENOSYS; } + +static inline struct rproc_mem_entry *rproc_find_res_by_name(struct udevice *dev, + const char *name) +{ + return NULL; +} #endif
#endif /* _RPROC_H_ */ diff --git a/include/rproc_virtio.h b/include/rproc_virtio.h new file mode 100644 index 0000000000..cbe8ff420f --- /dev/null +++ b/include/rproc_virtio.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023, Advanced Micro Devices Inc. + */ + +#ifndef _RPROC_VIRTIO_H_ +#define _RPROC_VIRTIO_H_ + +#include <remoteproc.h> +#include <dm/device.h> + +void rproc_flush_dcache(struct udevice *dev); + +#ifndef CONFIG_SANDBOX + +int rproc_virtio_create_dev(struct udevice *parent, struct fw_rsc_vdev *rsc, + int offset); + +#else + +int rproc_virtio_create_dev(struct udevice *parent, struct fw_rsc_vdev *rsc, + int offset) +{ + return -ENODEV; +} + +#endif /* CONFIG_SANDBOX */ + +#endif /* _RPROC_VIRTIO_H_ */ diff --git a/include/virtio.h b/include/virtio.h index 062a24630c..16d0f8aa7f 100644 --- a/include/virtio.h +++ b/include/virtio.h @@ -69,6 +69,9 @@ /* v1.0 compliant */ #define VIRTIO_F_VERSION_1 32
+/* packed virtqueue layout */ +#define VIRTIO_F_RING_PACKED 34 + /* * If clear - device has the IOMMU bypass quirk feature. * If set - use platform tools to detect the IOMMU. diff --git a/include/virtio_ring.h b/include/virtio_ring.h index e8e91044a2..4a9b4078ee 100644 --- a/include/virtio_ring.h +++ b/include/virtio_ring.h @@ -2,6 +2,7 @@ /* * Copyright (C) 2018, Tuomas Tynkkynen tuomas.tynkkynen@iki.fi * Copyright (C) 2018, Bin Meng bmeng.cn@gmail.com + * Copyright (C) 2023, Advanced Micro Devices Inc. * * From Linux kernel include/uapi/linux/virtio_ring.h */ @@ -227,6 +228,22 @@ void virtqueue_kick(struct virtqueue *vq); */ void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len);
+/** + * vring_new_virtqueue - create a virtqueue at user defined vring 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_create_virtqueue - create a virtqueue for a virtio device *
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);
Add driver to demonstrate use of rpmsg framework.
Signed-off-by: Tanmay Shah tanmay.shah@amd.com --- drivers/rpmsg/Kconfig | 8 ++++ drivers/rpmsg/Makefile | 3 ++ drivers/rpmsg/rpmsg_sample_client.c | 63 +++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 drivers/rpmsg/rpmsg_sample_client.c
diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index 4efb8dfcd7..808cd16275 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -28,4 +28,12 @@ config RPMSG_SANDBOX Say 'y' here to add sandbox driver for RPMsg framework used for dummy communication with remote processor on sandbox platform
+config RPMSG_SAMPLE_CLIENT + bool "Sample RPMsg client driver" + depends on DM + select RPMSG + help + Say 'y' here to enable driver that demonstrate use of RPMsg framework. + This driver uses RPMsg APIs to communicate with remote processor. + endmenu diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile index 21611725ea..53af0f7ea6 100644 --- a/drivers/rpmsg/Makefile +++ b/drivers/rpmsg/Makefile @@ -6,5 +6,8 @@ obj-$(CONFIG_RPMSG) += rpmsg-uclass.o
obj-$(CONFIG_RPMSG_SANDBOX) += sandbox_test_rpmsg.o
+# Sample driver demonstrate how to use rpmsg framework +obj-$(CONFIG_RPMSG_SAMPLE_CLIENT) += rpmsg_sample_client.o + # virtio driver for rpmsg obj-$(CONFIG_VIRTIO_RPMSG_BUS) += virtio_rpmsg_bus.o diff --git a/drivers/rpmsg/rpmsg_sample_client.c b/drivers/rpmsg/rpmsg_sample_client.c new file mode 100644 index 0000000000..ae591dbdde --- /dev/null +++ b/drivers/rpmsg/rpmsg_sample_client.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * Copyright (C) 2023, Advanced Micro Devices, Inc. + * + * Sample client that shows use of rpmsg APIs + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <malloc.h> +#include <rpmsg.h> +#include <dm/device-internal.h> +#include <dm/uclass.h> +#include <dm/uclass-internal.h> +#include <linux/delay.h> + +static int rpmsg_rx_callback(void *buf, int msg_len, u32 msgs_received) +{ + printf("APU: rx: %s\n", (char *)buf); + + return 0; +} + +static int rpmsg_sample_client_probe(struct udevice *udev) +{ + char data[80] = {0}; + int i; + + /* Initialize rpmsg for core 0 */ + rpmsg_init(0); + + /* wait for RPU to initialize vdev */ + mdelay(20); + + /* assume rpmsg init is already done */ + for (i = 1; i < 5; i++) { + sprintf(data, "rpmsg buf %d", i); + printf("APU: tx: %s\n", data); + rpmsg_send(0, data, strlen(data)); + + /* 20ms delay before receiving the data */ + mdelay(20); + + rpmsg_recv(0, rpmsg_rx_callback); + printf("\n"); + } + return 0; +} + +U_BOOT_DRIVER(rpmsg_sample_client) = { + .name = "rpmsg-sample-client", + .id = UCLASS_MISC, + .probe = rpmsg_sample_client_probe, + .flags = DM_FLAG_PRE_RELOC, +}; + +U_BOOT_DRVINFO(rpmsg_sample_client) = { + .name = "rpmsg-sample-client", +};
This patch introduces commands to use rpmsg framework
rpmsg init <core id> - Initialize rpmsg framework for remote core
rpmsg debug_data <core id> <"tx" / "rx"> - debug tx or rx virtqueues. This command simply uses virtqueue_dump_data and prints virtqueue for rpmsg device mapped to <core id>
Signed-off-by: Tanmay Shah tanmay.shah@amd.com --- MAINTAINERS | 1 + cmd/Kconfig | 6 +++++ cmd/Makefile | 1 + cmd/rpmsg.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ include/rpmsg.h | 5 ++++ 5 files changed, 74 insertions(+) create mode 100644 cmd/rpmsg.c
diff --git a/MAINTAINERS b/MAINTAINERS index 876a7fdbdf..8e40da38e7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1368,6 +1368,7 @@ F: doc/README.rockusb RPMSG M: Tanmay Shah tanmay.shah@amd.com S: Maintained +F: cmd/rpmsg.c F: drivers/rpmsg/* F: include/rpmsg.h F: test/dm/rpmsg.c diff --git a/cmd/Kconfig b/cmd/Kconfig index 2d6e5f993f..355644a3fa 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1450,6 +1450,12 @@ config CMD_REMOTEPROC help Support for Remote Processor control
+config CMD_RPMSG + bool "rpmsg" + help + Support for RPMsg protocol over virtio for data transfer between + U-Boot and remote processor firmware + config CMD_SATA bool "sata - Access SATA subsystem" select SATA diff --git a/cmd/Makefile b/cmd/Makefile index 9f8c0b058b..23838698ea 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -149,6 +149,7 @@ obj-$(CONFIG_CMD_REGINFO) += reginfo.o obj-$(CONFIG_CMD_REISER) += reiser.o obj-$(CONFIG_CMD_REMOTEPROC) += remoteproc.o obj-$(CONFIG_CMD_RNG) += rng.o +obj-$(CONFIG_CMD_RPMSG) += rpmsg.o obj-$(CONFIG_CMD_KASLRSEED) += kaslrseed.o obj-$(CONFIG_CMD_ROCKUSB) += rockusb.o obj-$(CONFIG_CMD_RTC) += rtc.o diff --git a/cmd/rpmsg.c b/cmd/rpmsg.c new file mode 100644 index 0000000000..da8aa05c9d --- /dev/null +++ b/cmd/rpmsg.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023, Advanced Micro Devices, Inc. + */ +#include <common.h> +#include <command.h> +#include <dm.h> +#include <errno.h> +#include <malloc.h> +#include <rpmsg.h> + +static int do_rpmsg_init(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + /* Expect args to <core id> <data string> */ + + int core_id, ret = CMD_RET_USAGE; + + if (argc != 2) + return ret; + + core_id = strtoul(argv[1], NULL, 16); + rpmsg_init(core_id); + + return 0; +} + +static int do_rpmsg_debug_data(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + /* + * Expect args to <core id> <data string> + */ + int core_id, vq_id; + + if (argc != 3) + return CMD_RET_USAGE; + + core_id = strtoul(argv[1], NULL, 10); + if (!strcmp(argv[2], "rx")) + vq_id = 0; + else if (!strcmp(argv[2], "tx")) + vq_id = 1; + else + return CMD_RET_USAGE; + + rpmsg_debug_data(core_id, vq_id); + + return 0; +} + +static char rpmsg_cmd_help[] = + "Send/Recv data over RPMsg channel from U-Boot to remote processor.\n" + "commands:\n" + "rpmsg init <rproc core id>\n" + "rpmsg debug_data <rproc core id> <vq name (tx or rx)>\n"; + +U_BOOT_CMD_WITH_SUBCMDS(rpmsg, "rpmsg subsystem", rpmsg_cmd_help, + U_BOOT_SUBCMD_MKENT(init, 2, 0, do_rpmsg_init), + U_BOOT_SUBCMD_MKENT(debug_data, 3, 1, + do_rpmsg_debug_data)); diff --git a/include/rpmsg.h b/include/rpmsg.h index bed61240ca..18e8ee1ee2 100644 --- a/include/rpmsg.h +++ b/include/rpmsg.h @@ -118,6 +118,7 @@ typedef int (*rpmsg_rx_cb_t)(void *buf, int msg_len, u32 msgs_received); 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); +void rpmsg_debug_data(int core_id, int vq_id);
#else int rpmsg_init(int core_id) @@ -135,6 +136,10 @@ int rpmsg_recv(int core_id, rpmsg_rx_cb_t cb) return -ENODEV; }
+void rpmsg_debug_data(int core_id, int vq_id) +{ +} + #endif /* IS_ENABLED(CONFIG_RPMSG) */
#endif /* _RPMSG_H */
Current remoteproc framework provides commands to start/stop remote processor. However, If remote processor is already running before U-Boot then there is no way to connect to remote processor.
Implements attach/detach commands to connect to already running remote processor. During attach operation, remoteproc framework gets resource table from remote processor using platform specific callback and then creates vdev devices and other resources accordingly.
This approach is derived from the Linux kernel remoteproc framework.
kernel version: 6.4-rc2 (d848a4819d85) file: drivers/remoteproc/remoteproc_core
Signed-off-by: Tanmay Shah tanmay.shah@amd.com --- cmd/remoteproc.c | 14 +++++- drivers/remoteproc/rproc-uclass.c | 76 ++++++++++++++++++++++++++++++- include/remoteproc.h | 31 +++++++++++++ 3 files changed, 119 insertions(+), 2 deletions(-)
diff --git a/cmd/remoteproc.c b/cmd/remoteproc.c index ca3b436242..200a233114 100644 --- a/cmd/remoteproc.c +++ b/cmd/remoteproc.c @@ -173,6 +173,10 @@ static int do_remoteproc_wrapper(struct cmd_tbl *cmdtp, int flag, int argc, ret = rproc_start(id); } else if (!strcmp(argv[0], "stop")) { ret = rproc_stop(id); + } else if (!strcmp(argv[0], "attach")) { + ret = rproc_attach(id); + } else if (!strcmp(argv[0], "detach")) { + ret = rproc_detach(id); } else if (!strcmp(argv[0], "reset")) { ret = rproc_reset(id); } else if (!strcmp(argv[0], "is_running")) { @@ -220,6 +224,12 @@ static struct cmd_tbl cmd_remoteproc_sub[] = { U_BOOT_CMD_MKENT(stop, 1, 1, do_remoteproc_wrapper, "Stop remote processor", "id - ID of the remote processor (see 'list' cmd)\n"), + U_BOOT_CMD_MKENT(attach, 1, 1, do_remoteproc_wrapper, + "Attach remote processor", + "id - ID of the remote processor (see 'list' cmd)\n"), + U_BOOT_CMD_MKENT(detach, 1, 1, do_remoteproc_wrapper, + "detach remote processor", + "id - ID of the remote processor (see 'list' cmd)\n"), U_BOOT_CMD_MKENT(reset, 1, 1, do_remoteproc_wrapper, "Reset remote processor", "id - ID of the remote processor (see 'list' cmd)\n"), @@ -262,7 +272,7 @@ static int do_remoteproc(struct cmd_tbl *cmdtp, int flag, int argc,
U_BOOT_CMD(rproc, 5, 1, do_remoteproc, "Control operation of remote processors in an SoC", - " [init|list|load|start|stop|reset|is_running|ping]\n" + " [attach|detach|init|list|load|start|stop|reset|is_running|ping]\n" "\t\t Where:\n" "\t\t[addr] is a memory address\n" "\t\t<id> is a numerical identifier for the remote processor\n" @@ -271,6 +281,8 @@ U_BOOT_CMD(rproc, 5, 1, do_remoteproc, "\t\tNote: Services are dependent on the driver capability\n" "\t\t 'list' command shows the capability of each device\n" "\n\tSubcommands:\n" + "\tattach <id> - attach to already running remote processor\n" + "\tdetach <id> - unbind driver with already attached device\n" "\tinit <id> - Enumerate and initalize the remote processor.\n" "\t if id is not passed, initialize all the remote prcessors\n" "\tlist - list available remote processors\n" diff --git a/drivers/remoteproc/rproc-uclass.c b/drivers/remoteproc/rproc-uclass.c index 3aebaf6187..31d2e70a0c 100644 --- a/drivers/remoteproc/rproc-uclass.c +++ b/drivers/remoteproc/rproc-uclass.c @@ -170,7 +170,7 @@ static int rproc_pre_probe(struct udevice *dev) return -EINVAL; }
- if (!ops->load || !ops->start) { + if ((!ops->load || !ops->start) && !ops->attach) { debug("%s driver has missing mandatory ops?\n", dev->name); return -EINVAL; } @@ -438,6 +438,8 @@ enum rproc_ops { RPROC_RESET, RPROC_PING, RPROC_RUNNING, + RPROC_ATTACH, + RPROC_DETACH, };
/** @@ -497,6 +499,14 @@ static int _rproc_ops_wrapper(int id, enum rproc_ops op) fn = ops->ping; op_str = "Pinging"; break; + case RPROC_ATTACH: + fn = ops->attach; + op_str = "Attaching"; + break; + case RPROC_DETACH: + fn = ops->detach; + op_str = "Detaching"; + break; default: debug("what is '%d' operation??\n", op); return -EINVAL; @@ -538,6 +548,15 @@ int rproc_is_running(int id) return _rproc_ops_wrapper(id, RPROC_RUNNING); };
+int rproc_attach(int id) +{ + return _rproc_ops_wrapper(id, RPROC_ATTACH); +}; + +int rproc_detach(int id) +{ + return _rproc_ops_wrapper(id, RPROC_DETACH); +};
static int handle_trace(struct udevice *dev, struct fw_rsc_trace *rsc, int offset, int avail) @@ -993,3 +1012,58 @@ unsigned long rproc_parse_resource_table(struct udevice *dev, struct rproc *cfg)
return 1; } + +unsigned long rproc_attach_resource_table(struct udevice *dev) +{ + struct dm_rproc_ops *ops = rproc_get_ops(dev); + struct resource_table *ptable; + int tablesz = 0, ret; + struct rproc *rproc; + + if (!ops->get_loaded_rsc_table) { + debug("get loaded rsctable op not supported\n"); + return -ENOSYS; + } + + ptable = (struct resource_table *)ops->get_loaded_rsc_table(dev, &tablesz); + if (!ptable) { + debug("can't get loaded resource table\n"); + return -EINVAL; + } + + rproc = rproc_get_cfg(dev); + if (!rproc) { + debug("remoteproc configurations not available\n"); + return -EINVAL; + } + + rproc->table_ptr = ptable; + + rsc_table = kzalloc(tablesz, GFP_KERNEL); + if (!rsc_table) + return -ENOMEM; + + /* + * Copy the resource table into a local buffer before handling the + * resource table. + */ + memcpy(rsc_table, ptable, tablesz); + + ret = handle_resources(dev, tablesz, loading_handlers); + if (ret) { + debug("handle_resources failed: %d\n", ret); + return 0; + } + + /* + * Instead of trying to mimic the kernel flow of copying the + * processed resource table into its post ELF load location in DDR + * copying it into its original location. + */ + memcpy(ptable, rsc_table, tablesz); + + free(rsc_table); + rsc_table = NULL; + + return 0; +} diff --git a/include/remoteproc.h b/include/remoteproc.h index 4251af52bd..8b7be05435 100644 --- a/include/remoteproc.h +++ b/include/remoteproc.h @@ -498,6 +498,22 @@ struct dm_rproc_ops { */ int (*stop)(struct udevice *dev);
+ /** + * attach() - Attach the remoteproc device (optional) + * + * @dev: Remote proc device + * @return 0 if all ok, else appropriate error value. + */ + int (*attach)(struct udevice *dev); + + /** + * detach() - Detach the remoteproc device (optional) + * + * @dev: Remote proc device + * @return 0 if all ok, else appropriate error value. + */ + int (*detach)(struct udevice *dev); + /** * reset() - Reset the remoteproc device (optional) * @@ -600,6 +616,19 @@ int rproc_start(int id); */ int rproc_stop(int id);
+/** + * rproc_attach() - Attach a remote processor + * @id: id of the remote processor + * Return: 0 if all ok, else appropriate error value. + */ +int rproc_attach(int id); + +/** + * rproc_detach() - Detach a remote processor + * @id: id of the remote processor + * Return: 0 if all ok, else appropriate error value. + */ +int rproc_detach(int id); /** * rproc_reset() - reset a remote processor * @id: id of the remote processor @@ -752,6 +781,8 @@ int rproc_elf_load_rsc_table(struct udevice *dev, ulong fw_addr, unsigned long rproc_parse_resource_table(struct udevice *dev, struct rproc *cfg);
+unsigned long rproc_attach_resource_table(struct udevice *dev); + struct resource_table *rproc_find_resource_table(struct udevice *dev, unsigned int addr, int *tablesz);
ZynqMP platform has ARM Cortex-R5 dual core remote processor also known as RPU (Real-time Processing Unit). This remoteproc platform driver is responsible to configure the RPU and manages life-cycle of the RPU along with other platform specific operations.
Signed-off-by: Tanmay Shah tanmay.shah@amd.com --- MAINTAINERS | 1 + drivers/firmware/firmware-zynqmp.c | 20 ++ drivers/remoteproc/Kconfig | 9 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/xlnx_rproc.c | 411 +++++++++++++++++++++++++++++ include/zynqmp_firmware.h | 16 ++ 6 files changed, 458 insertions(+) create mode 100644 drivers/remoteproc/xlnx_rproc.c
diff --git a/MAINTAINERS b/MAINTAINERS index 8e40da38e7..feaf2ecd90 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1323,6 +1323,7 @@ REMOTEPROC M: Tanmay Shah tanmay.shah@amd.com S: Maintained F: drivers/remoteproc/rproc_virtio.c +F: drivers/remoteproc/xlnx_rproc.c F: include/rproc_virtio.h
RISC-V diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c index 0897992405..ec6057d4e3 100644 --- a/drivers/firmware/firmware-zynqmp.c +++ b/drivers/firmware/firmware-zynqmp.c @@ -202,6 +202,26 @@ int zynqmp_pm_request_node(const u32 node, const u32 capabilities, qos, ack, NULL); }
+/** + * zynqmp_pm_request_wake - PM call to wake up selected master or subsystem + * @node: Node ID of the master or subsystem + * @set_addr: Specifies whether the address argument is relevant + * @address: Address from which to resume when woken up + * @ack: Flag to specify whether acknowledge requested + * + * Return: status, either success or error+reason + */ +int zynqmp_pm_request_wake(const u32 node, + const bool set_addr, + const u64 address, + const enum zynqmp_pm_request_ack ack) +{ + /* set_addr flag is encoded into 1st bit of address */ + return xilinx_pm_request(PM_REQUEST_WAKEUP, node, + lower_32_bits(address | set_addr), + upper_32_bits(address), ack, NULL); +} + int zynqmp_pm_is_function_supported(const u32 api_id, const u32 id) { int ret; diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index b758c248e4..9fd8cda3c8 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -113,4 +113,13 @@ config REMOTEPROC_VIRTIO drivers provide a set of ops for the real virtio device driver to call.
+config REMOTEPROC_XLNX + bool "Support for AMD-Xilinx platform's remoteproc driver" + select REMOTEPROC_VIRTIO + depends on DM + depends on ZYNQMP_FIRMWARE + help + Say 'y' here to add support for remoteproc platform + driver for various AMD-Xilinx platforms + endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 61fdb87efb..1d0c48820b 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_REMOTEPROC_TI_POWER) += ti_power_proc.o obj-$(CONFIG_REMOTEPROC_TI_PRU) += pru_rproc.o obj-$(CONFIG_REMOTEPROC_TI_IPU) += ipu_rproc.o obj-$(CONFIG_REMOTEPROC_VIRTIO) += rproc_virtio.o +obj-$(CONFIG_REMOTEPROC_XLNX) += xlnx_rproc.o diff --git a/drivers/remoteproc/xlnx_rproc.c b/drivers/remoteproc/xlnx_rproc.c new file mode 100644 index 0000000000..f29b958b7f --- /dev/null +++ b/drivers/remoteproc/xlnx_rproc.c @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD-Xilinx remoteproc driver + * + * Copyright (C) 2023, Advanced Micro Devices, Inc. + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <remoteproc.h> +#include <rpmsg.h> +#include <zynqmp_firmware.h> +#include <asm/dma-mapping.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <dm/device-internal.h> +#include <linux/ioport.h> + +#define MAX_CORE 2 +#define RSCTBL_PROP "xlnx,rsc-tbl" +#define RSC_TBL_SIZE 0x400 + +enum xlnx_cluster_mode_dt_prop { + split_mode = 0, + lockstep_mode = 1, + single_cpu_mode = 2, +}; + +enum xlnx_rpu_state { + INVALID_STATE = 0, + DRIVER_PROBE = 1, + RPU_INIT = 2, + RPU_ATTACH = 3, + RPU_DETACH = 4 +}; + +struct rproc xlnx_rproc_cfg_arr[2]; + +/** + * struct xlnx_rproc_core_privdata - contains private data of RPU core driver + * + * @cluster_dev: parent device of each core, device mapped to + * cluster node + * @rpu_mode: one of split, lockstep or single-cpu mode + * @tcm_mode: TCM configuration - split or lockstep + * @state: current state of RPU + * @rproc_cfg: core's corresponding remoteproc data + * @vdev: core's corresponding remoteproc virtio device + * @rsc_pa: core's physical resource table address + * @pd_node: power-domain id of core + * @index: core's child node index in cluster node in device-tree + */ +struct xlnx_rproc_core_privdata { + struct udevice *cluster_dev; + enum rpu_oper_mode rpu_mode; + enum rpu_tcm_comb tcm_mode; + enum xlnx_rpu_state state; + struct rproc rproc_cfg; + struct udevice *vdev; + phys_addr_t rsc_pa; + int pd_node; + int index; +}; + +/** + * struct xlnx_rproc_cluster_privdata - contains privdate data of cluster driver + * + * @cores: Array of pointers to R5 cores within the cluster + * @rpu_cluster_mode: xlnx,cluster-mode dt prop value to configure RPU + * @core_probed: keep count of number of cores probed + * @core_count: number of individual cores available based on cluster-mode + * for lockstep this is 1, for split this is 2. + */ +struct xlnx_rproc_cluster_privdata { + struct xlnx_rproc_core_privdata *cores[MAX_CORE]; + enum xlnx_cluster_mode_dt_prop rpu_cluster_mode; + int core_probed; + int core_count; +}; + +static int xlnx_rproc_detach(struct udevice *udev) +{ + struct xlnx_rproc_core_privdata *core = dev_get_priv(udev); + + if (core->state != RPU_ATTACH) { + debug("RPU %s isn't attached yet\n", udev->name); + return 0; + } + + core->state = RPU_DETACH; + + return 0; +} + +static int xlnx_rproc_attach(struct udevice *udev) +{ + struct xlnx_rproc_core_privdata *core = dev_get_priv(udev); + struct rproc *rproc = rproc_get_cfg(udev); + struct resource_table *rsctbl; + u32 rsc_tbl_start; + int ret; + + /* + * RPU attach will parse and alloc resources only after INIT state. + * Once the resources are allocated we won't be releasing them during + * detach, but we just change the state of RPU. So, during kick, based + * on state we can decided if RPU should be notified or not + */ + if (core->state == RPU_DETACH) { + core->state = RPU_ATTACH; + return 0; + } + + if (core->state != RPU_INIT) { + debug("RPU isn't initialized, can't attach\n"); + return 0; + } + + /* get rsc tbl carveout info */ + ret = dev_read_u32_index(udev, RSCTBL_PROP, 1, &rsc_tbl_start); + if (ret < 0) { + debug("failed to read phandle for prop %s", RSCTBL_PROP); + return ret; + } + + core->rsc_pa = (phys_addr_t)map_physmem(rsc_tbl_start, RSC_TBL_SIZE, + MAP_NOCACHE); + rproc->table_ptr = (struct resource_table *)core->rsc_pa; + + if (!core->rsc_pa) { + dev_info(udev, "rsc tbl not available\n"); + return -EINVAL; + } + + rsctbl = (struct resource_table *)core->rsc_pa; + if (rsctbl->ver != 1) { + debug("fw rsc table version %d not compatible\n", rsctbl->ver); + return -EINVAL; + } + + if (rsctbl->num < 1 || rsctbl->num > 255) { + debug("number of resources are invalid %d\n", rsctbl->num); + return -EINVAL; + } + + if (rproc_attach_resource_table(udev)) { + debug("rsc table not found\n"); + return -EINVAL; + } + + core->state = RPU_ATTACH; + + return 0; +} + +static void xlnx_remove_all_res(struct rproc_mem_entry *mapping) +{ + struct list_head *tmp, *head; + + head = &mapping->node; + if (!head) + return; + + /* remove the list */ + tmp = head; + while (tmp) { + head = head->next; + kfree(tmp); + tmp = head; + } +} + +static int xlnx_add_res(struct udevice *dev, struct rproc_mem_entry *mapping) +{ + struct rproc *rproc = rproc_get_cfg(dev); + + list_add_tail(&mapping->node, &rproc->mappings.node); + + return 0; +} + +static int zynqmp_r5_get_mem_region_node(struct udevice *udev) +{ + struct rproc_mem_entry *mapping; + int num_mem_region = 4, i, ret; + u32 mem_reg_vals[4] = {0}; + ofnode mem_reg_node; + struct rproc *rproc; + + rproc = rproc_get_cfg(udev); + if (!rproc) + return -EINVAL; + + ret = dev_read_u32_array(udev, "memory-region", mem_reg_vals, 4); + if (ret < 0) { + debug("Unable to read memory-region property\n"); + return -EINVAL; + } + + for (i = 0; i < num_mem_region; i++) { + mem_reg_node = ofnode_get_by_phandle(mem_reg_vals[i]); + if (!ofnode_valid(mem_reg_node)) { + debug("Could not parse mem region node\n"); + return -EINVAL; + } + + mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); + if (!mapping) + goto remove_mem_region; + + mapping->dma = ofnode_get_addr(mem_reg_node); + mapping->len = ofnode_get_size(mem_reg_node); + mapping->da = mapping->dma; + mapping->va = map_physmem(mapping->da, mapping->len, MAP_NOCACHE); + + strlcpy(mapping->name, ofnode_get_name(mem_reg_node), + RPMSG_NAME_SIZE); + + debug("dev %s mapping %s: va=0x%p, da=0x%x, dma=0x%llx, len=0x%x\n", + udev->name, mapping->name, mapping->va, mapping->da, + mapping->dma, mapping->len); + xlnx_add_res(udev, mapping); + } + + return 0; + +remove_mem_region: + xlnx_remove_all_res(&rproc->mappings); + + return -EINVAL; +} + +static int xlnx_rproc_init(struct udevice *udev) +{ + struct xlnx_rproc_core_privdata *core = dev_get_priv(udev); + struct rproc *rproc = rproc_get_cfg(udev); + int ret; + + if (core->state != DRIVER_PROBE) + return 0; + + /* + * If for some reason, memory-region property fails then don't fail + * the command as memory-region is not required property + */ + ret = zynqmp_r5_get_mem_region_node(udev); + if (ret) + debug("adding memory-region failed with ret %d\n", ret); + + rproc->support_rpmsg_virtio = true; + + core->state = RPU_INIT; + + return 0; +} + +static int xlnx_rproc_probe(struct udevice *udev) +{ + struct xlnx_rproc_cluster_privdata *cluster = dev_get_priv(udev->parent); + struct xlnx_rproc_core_privdata *core = dev_get_priv(udev); + struct dm_rproc_uclass_pdata *pdata = dev_get_plat(udev); + + /* Assume primary core gets probed first */ + if (cluster->core_probed >= cluster->core_count) { + debug("core %d isn't used in mode %d\n", + cluster->core_probed, cluster->rpu_cluster_mode); + return -EINVAL; + } + + core->index = cluster->core_probed; + cluster->cores[core->index] = core; + + pdata->rproc = &xlnx_rproc_cfg_arr[core->index]; + pdata->rproc->rproc_id = core->index; + + INIT_LIST_HEAD(&pdata->rproc->mappings.node); + + if (cluster->rpu_cluster_mode == split_mode) { + core->tcm_mode = PM_RPU_TCM_SPLIT; + core->rpu_mode = PM_RPU_MODE_SPLIT; + } else if (cluster->rpu_cluster_mode == lockstep_mode) { + core->tcm_mode = PM_RPU_TCM_COMB; + core->rpu_mode = PM_RPU_MODE_LOCKSTEP; + } else if (cluster->rpu_cluster_mode == single_cpu_mode) { + debug("single cpu cluster mode not supported\n"); + return -EINVAL; + } + + pdata->rproc->support_rpmsg_virtio = true; + + cluster->core_probed++; + core->state = DRIVER_PROBE; + + cluster->cores[core->index] = core; + INIT_LIST_HEAD(&pdata->rproc->mappings.node); + + return 0; +} + +static struct resource_table * +xlnx_rpu_rproc_get_loaded_rsc_table(struct udevice *dev, int *table_sz) +{ + struct xlnx_rproc_core_privdata *core = dev_get_priv(dev); + + *table_sz = RSC_TBL_SIZE; + + return (struct resource_table *)core->rsc_pa; +} + +static int xlnx_rproc_kick(struct udevice *dev, int notify_id) +{ + struct xlnx_rproc_core_privdata *core = dev_get_priv(dev); + + if (core->state != RPU_ATTACH) { + debug("error: RPU %s state=%d\n", dev->name, core->state); + return -EINVAL; + } + + return 0; +} + +static int xlnx_rproc_of_to_plat(struct udevice *udev) +{ + struct xlnx_rproc_core_privdata *core = dev_get_priv(udev); + int ret; + + ret = dev_read_u32_index(udev, "power-domains", 1, &core->pd_node); + if (ret) { + debug("failed to read power-domains property\n"); + return -EINVAL; + } + + return 0; +} + +static const struct dm_rproc_ops xlnx_rproc_ops = { + .init = xlnx_rproc_init, + .attach = xlnx_rproc_attach, + .detach = xlnx_rproc_detach, + .get_loaded_rsc_table = xlnx_rpu_rproc_get_loaded_rsc_table, + .add_res = xlnx_add_res, + .kick = xlnx_rproc_kick, +}; + +static const struct udevice_id xlnx_rproc_ids[] = { + { .compatible = "xlnx,zynqmp-r5f" } +}; + +U_BOOT_DRIVER(xlnx_rproc) = { + .name = "xlnx-rproc", + .of_match = xlnx_rproc_ids, + .id = UCLASS_REMOTEPROC, + .ops = &xlnx_rproc_ops, + .probe = xlnx_rproc_probe, + .of_to_plat = xlnx_rproc_of_to_plat, + .priv_auto = sizeof(struct xlnx_rproc_core_privdata), + .flags = DM_FLAG_VITAL, +}; + +static int xlnx_rproc_cluster_probe(struct udevice *udev) +{ + struct xlnx_rproc_cluster_privdata *cluster = dev_get_priv(udev); + enum xlnx_cluster_mode_dt_prop cluster_mode; + int ret; + + if (device_get_child_count(udev) < MAX_CORE) { + dev_err(udev, "Invalid number of R5 cores for cluster %s\n", + udev->name); + return -EINVAL; + } + + /* set mode */ + ret = dev_read_u32(udev, "xlnx,cluster-mode", &cluster_mode); + if (ret < 0) + cluster_mode = 1; /* default is lockstep */ + + if (cluster->rpu_cluster_mode < split_mode || + cluster->rpu_cluster_mode > single_cpu_mode) { + debug("invalid cluster mode %d\n", cluster->rpu_cluster_mode); + return -EINVAL; + } + + cluster->rpu_cluster_mode = cluster_mode; + + if (cluster_mode == split_mode) { /* split */ + cluster->core_count = 2; + } else if (cluster_mode == lockstep_mode) { /* lockstep */ + cluster->core_count = 1; + } else if (cluster_mode == single_cpu_mode) { /* single-cpu not supported */ + debug("single cpu cluster mode not supported\n"); + return -EINVAL; + } + + cluster->core_probed = 0; + + return 0; +} + +static const struct udevice_id xlnx_cluster_ids[] = { + { .compatible = "xlnx,zynqmp-r5fss", }, + {} +}; + +U_BOOT_DRIVER(xlnx_cluster) = { + .name = "xlnx_cluster", + .of_match = xlnx_cluster_ids, + .id = UCLASS_MISC, + .probe = xlnx_rproc_cluster_probe, + .priv_auto = sizeof(struct xlnx_rproc_cluster_privdata), +}; diff --git a/include/zynqmp_firmware.h b/include/zynqmp_firmware.h index ce086f48d4..6419ca0c58 100644 --- a/include/zynqmp_firmware.h +++ b/include/zynqmp_firmware.h @@ -450,6 +450,21 @@ enum zynqmp_pm_request_ack { ZYNQMP_PM_REQUEST_ACK_NON_BLOCKING = 3, };
+enum rpu_boot_mem { + PM_RPU_BOOTMEM_LOVEC = 0, + PM_RPU_BOOTMEM_HIVEC = 1, +}; + +enum rpu_tcm_comb { + PM_RPU_TCM_SPLIT = 0, + PM_RPU_TCM_COMB = 1, +}; + +enum rpu_oper_mode { + PM_RPU_MODE_LOCKSTEP = 0, + PM_RPU_MODE_SPLIT = 1, +}; + unsigned int zynqmp_firmware_version(void); int zynqmp_pmufw_node(u32 id); int zynqmp_pmufw_config_close(void); @@ -461,6 +476,7 @@ int zynqmp_pm_request_node(const u32 node, const u32 capabilities, int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value); int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config, u32 value); +int zynqmp_pm_set_rpu_mode(u32 node, int rpu_mode); int zynqmp_pm_is_function_supported(const u32 api_id, const u32 id); int zynqmp_mmio_read(const u32 address, u32 *value); int zynqmp_mmio_write(const u32 address, const u32 mask, const u32 value);
Enable configurations to build remoteproc and rpmsg framework. Also enable remoteproc and rpmsg framework commands. Enable misc commands to probe rpmsg_sample_client driver.
Signed-off-by: Tanmay Shah tanmay.shah@amd.com --- configs/xilinx_zynqmp_virt_defconfig | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/configs/xilinx_zynqmp_virt_defconfig b/configs/xilinx_zynqmp_virt_defconfig index c4bbde2206..eaf741bf31 100644 --- a/configs/xilinx_zynqmp_virt_defconfig +++ b/configs/xilinx_zynqmp_virt_defconfig @@ -73,10 +73,13 @@ CONFIG_CMD_GPIO=y CONFIG_CMD_PWM=y CONFIG_CMD_GPT=y CONFIG_CMD_I2C=y +CONFIG_CMD_MISC=y CONFIG_CMD_MMC=y CONFIG_CMD_MTD=y CONFIG_CMD_NAND_LOCK_UNLOCK=y CONFIG_CMD_POWEROFF=y +CONFIG_CMD_REMOTEPROC=y +CONFIG_CMD_RPMSG=y CONFIG_CMD_SDRAM=y CONFIG_CMD_SF_TEST=y CONFIG_CMD_SPI=y @@ -191,7 +194,10 @@ CONFIG_DM_REGULATOR=y CONFIG_DM_REGULATOR_FIXED=y CONFIG_DM_PWM=y CONFIG_PWM_CADENCE_TTC=y +CONFIG_REMOTEPROC_XLNX=y CONFIG_RESET_ZYNQMP=y +CONFIG_VIRTIO_RPMSG_BUS=y +CONFIG_RPMSG_SAMPLE_CLIENT=y CONFIG_DM_RTC=y CONFIG_RTC_EMULATION=y CONFIG_RTC_ZYNQMP=y
Enable configs required to test rpmsg on sandbox build. CMD_MISC is enabled to probe rpmsg_sample_client driver.
Test procedure: - boot sandbox build - command to run rpmsg tests: ut dm rpmsg - command to run sample driver: misc list
Signed-off-by: Tanmay Shah tanmay.shah@amd.com --- configs/sandbox_defconfig | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 19cc6701e6..cdada8c0b3 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -77,6 +77,7 @@ CONFIG_CMD_I2C=y CONFIG_CMD_LOADM=y CONFIG_CMD_LSBLK=y CONFIG_CMD_MBR=y +CONFIG_CMD_MISC=y CONFIG_CMD_MMC=y CONFIG_CMD_MUX=y CONFIG_CMD_OSD=y @@ -84,6 +85,7 @@ CONFIG_CMD_PCI=y CONFIG_CMD_PCI_MPS=y CONFIG_CMD_READ=y CONFIG_CMD_REMOTEPROC=y +CONFIG_CMD_RPMSG=y CONFIG_CMD_SPI=y CONFIG_CMD_TEMPERATURE=y CONFIG_CMD_USB=y @@ -274,6 +276,8 @@ CONFIG_REMOTEPROC_SANDBOX=y CONFIG_SANDBOX_RESET=y CONFIG_RESET_SYSCON=y CONFIG_RESET_SCMI=y +CONFIG_RPMSG_SANDBOX=y +CONFIG_RPMSG_SAMPLE_CLIENT=y CONFIG_DM_RTC=y CONFIG_RTC_MAX313XX=y CONFIG_RTC_RV8803=y
openamp-rp@lists.openampproject.org