* [PATCH V0 0/3] Add versal-pci driver
@ 2025-03-03 4:43 Yidong Zhang
2025-03-03 4:43 ` [PATCH V0 1/3] drivers/misc/amd: Add new driver amd versal-pci Yidong Zhang
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Yidong Zhang @ 2025-03-03 4:43 UTC (permalink / raw)
To: arnd, gregkh, linux-kernel; +Cc: Yidong Zhang, lizhi.hou, christophe.jaillet
This patchset introduces a new Linux Kernel Driver, versal-pci for AMD
Alevo Versal based PCIe Card.
The AMD Alevo Versal based PCIe Card, including V70 and RAVE, is the first
Alevo production card leveraging AMD XDNA architecture with AI Engines. It
is designed for AI inference efficiency and is tuned for video analytics
and natural language processing applications [1].
This versal-pci driver provides services, including:
- leveraging linux firmware and configfs framework to download management
firmware
- communicate with firmware running on the PCIe Card
- monitor device health
The driver is licensed under GPL-2.0.
The firmwares are distributed as a closed binary, delivered by AMD. Please
see [1] for more information.
[1] https://www.amd.com/en/products/accelerators/alveo/v70.html
Yidong Zhang (3):
drivers/misc/amd: Add new driver amd versal-pci
drivers/misc/amd: Add remote queue service
drivers/misc/amd: Add load base shell firmware
MAINTAINERS | 5 +
drivers/misc/Kconfig | 1 +
drivers/misc/Makefile | 1 +
drivers/misc/amd/Kconfig | 15 +
drivers/misc/amd/Makefile | 7 +
drivers/misc/amd/versal-pci-main.c | 313 ++++++++++++++
drivers/misc/amd/versal-pci-rm-queue.c | 314 ++++++++++++++
drivers/misc/amd/versal-pci-rm-queue.h | 21 +
drivers/misc/amd/versal-pci-rm-service.c | 497 +++++++++++++++++++++++
drivers/misc/amd/versal-pci-rm-service.h | 229 +++++++++++
drivers/misc/amd/versal-pci.h | 64 +++
11 files changed, 1467 insertions(+)
create mode 100644 drivers/misc/amd/Kconfig
create mode 100644 drivers/misc/amd/Makefile
create mode 100644 drivers/misc/amd/versal-pci-main.c
create mode 100644 drivers/misc/amd/versal-pci-rm-queue.c
create mode 100644 drivers/misc/amd/versal-pci-rm-queue.h
create mode 100644 drivers/misc/amd/versal-pci-rm-service.c
create mode 100644 drivers/misc/amd/versal-pci-rm-service.h
create mode 100644 drivers/misc/amd/versal-pci.h
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH V0 1/3] drivers/misc/amd: Add new driver amd versal-pci
2025-03-03 4:43 [PATCH V0 0/3] Add versal-pci driver Yidong Zhang
@ 2025-03-03 4:43 ` Yidong Zhang
2025-03-03 4:43 ` [PATCH V0 2/3] drivers/misc/amd: Add remote queue service Yidong Zhang
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Yidong Zhang @ 2025-03-03 4:43 UTC (permalink / raw)
To: arnd, gregkh, linux-kernel
Cc: Yidong Zhang, lizhi.hou, christophe.jaillet, DMG Karthik,
Nishad Saraf
AMD Versal based PCIe card, including V70 and RAVE, is designed for AI
inference efficiency and is tuned for video analytics and natural language
processing applications.
The driver architecture:
+---------+----+ Remote +-----+------+
| |PCIe| Queue | Embedded |
| Mgmt PF |Bar | <=======> | SoC |
| | | +------------+
+---------+----+ | base FW |
+-----+------+
This driver provides the following functionalities:
- module and PCI device initialization
the driver will initialize itself based on bar resources for a
hardware queue based ring buffer between PCIe bar and RTOS services
VMR [1] running on Embedded SoC hardware firmware for flashing
firmware and checking card healthy status.
- programming FW
The base FW is downloaded onto the flash of the card.
[1] https://github.com/Xilinx/VMR.git
Co-developed-by: DMG Karthik <Karthik.DMG@amd.com>
Signed-off-by: DMG Karthik <Karthik.DMG@amd.com>
Co-developed-by: Nishad Saraf <nishads@amd.com>
Signed-off-by: Nishad Saraf <nishads@amd.com>
Signed-off-by: Yidong Zhang <yidong.zhang@amd.com>
---
MAINTAINERS | 5 +
drivers/misc/Kconfig | 1 +
drivers/misc/Makefile | 1 +
drivers/misc/amd/Kconfig | 15 ++
drivers/misc/amd/Makefile | 5 +
drivers/misc/amd/versal-pci-main.c | 272 +++++++++++++++++++++++++++++
drivers/misc/amd/versal-pci.h | 62 +++++++
7 files changed, 361 insertions(+)
create mode 100644 drivers/misc/amd/Kconfig
create mode 100644 drivers/misc/amd/Makefile
create mode 100644 drivers/misc/amd/versal-pci-main.c
create mode 100644 drivers/misc/amd/versal-pci.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 8e0736dc2ee0..8d79e8b7e963 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1215,6 +1215,11 @@ L: linux-spi@vger.kernel.org
S: Supported
F: drivers/spi/spi-amd.c
+AMD VERSAL PCI DRIVER
+M: Yidong Zhang <yidong.zhang@amd.com>
+S: Supported
+F: drivers/misc/amd/
+
AMD XDNA DRIVER
M: Min Ma <min.ma@amd.com>
M: Lizhi Hou <lizhi.hou@amd.com>
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 56bc72c7ce4a..d1616f0fb12f 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -649,4 +649,5 @@ source "drivers/misc/uacce/Kconfig"
source "drivers/misc/pvpanic/Kconfig"
source "drivers/misc/mchp_pci1xxxx/Kconfig"
source "drivers/misc/keba/Kconfig"
+source "drivers/misc/amd/Kconfig"
endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 545aad06d088..230a8d6bafaf 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -75,3 +75,4 @@ lan966x-pci-objs := lan966x_pci.o
lan966x-pci-objs += lan966x_pci.dtbo.o
obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o
obj-y += keba/
+obj-$(CONFIG_AMD_VERSAL_PCI) += amd/
diff --git a/drivers/misc/amd/Kconfig b/drivers/misc/amd/Kconfig
new file mode 100644
index 000000000000..8b1a9bd0d150
--- /dev/null
+++ b/drivers/misc/amd/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config AMD_VERSAL_PCI
+ tristate "AMD Versal PCIe Management Driver"
+ select FW_LOADER
+ select CONFIGFS_FS
+ depends on HAS_IOMEM
+ depends on PCI
+ default m
+ help
+ AMD Versal PCIe Management Driver provides management services,
+ including download firmware, program bitstream, and communicate with
+ the User function.
+
+ If "M" is selected, the driver module will be versal-pci
diff --git a/drivers/misc/amd/Makefile b/drivers/misc/amd/Makefile
new file mode 100644
index 000000000000..e3868cb39563
--- /dev/null
+++ b/drivers/misc/amd/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-$(CONFIG_AMD_VERSAL_PCI) += versal-pci.o
+
+versal-pci-$(CONFIG_AMD_VERSAL_PCI) := versal-pci-main.o
diff --git a/drivers/misc/amd/versal-pci-main.c b/drivers/misc/amd/versal-pci-main.c
new file mode 100644
index 000000000000..1c61c88120b4
--- /dev/null
+++ b/drivers/misc/amd/versal-pci-main.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Versal PCIe device
+ *
+ * Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include <linux/pci.h>
+
+#include "versal-pci.h"
+
+#define DRV_NAME "amd-versal-pci"
+
+#define PCI_DEVICE_ID_V70PQ2 0x50B0
+#define PCI_DEVICE_ID_RAVE 0x5700
+#define VERSAL_XCLBIN_MAGIC_ID "xclbin2"
+
+static inline u32 versal_pci_devid(struct versal_pci_device *vdev)
+{
+ return ((pci_domain_nr(vdev->pdev->bus) << 16) |
+ PCI_DEVID(vdev->pdev->bus->number, vdev->pdev->devfn));
+}
+
+static int versal_pci_load_shell(struct versal_pci_device *vdev, char *fw_name)
+{
+ const struct firmware *fw;
+ struct axlf *xsabin;
+ int ret;
+
+ strim(fw_name);
+
+ ret = request_firmware(&fw, fw_name, &vdev->pdev->dev);
+ if (ret) {
+ vdev_warn(vdev, "request xsabin fw %s failed %d", fw_name, ret);
+ return ret;
+ }
+
+ xsabin = (struct axlf *)fw->data;
+ if (memcmp(xsabin->magic, VERSAL_XCLBIN_MAGIC_ID, sizeof(VERSAL_XCLBIN_MAGIC_ID))) {
+ vdev_err(vdev, "Invalid device firmware");
+ ret = -EINVAL;
+ goto release_firmware;
+ }
+
+ if (!fw->size ||
+ fw->size != xsabin->header.length ||
+ fw->size < sizeof(*xsabin) ||
+ fw->size > SZ_1G) {
+ vdev_err(vdev, "Invalid device firmware size %zu", fw->size);
+ ret = -EINVAL;
+ goto release_firmware;
+ }
+
+ if (!uuid_equal(&vdev->intf_uuid, &xsabin->header.rom_uuid)) {
+ vdev_err(vdev, "base shell doesn't match uuid %pUb", &xsabin->header.uuid);
+ ret = -EINVAL;
+ goto release_firmware;
+ }
+
+ /* TODO upload fw to card */
+ if (ret) {
+ vdev_err(vdev, "failed to load xsabin %s : %d", fw_name, ret);
+ goto release_firmware;
+ }
+
+ vdev_info(vdev, "Downloaded xsabin %pUb of size %lld Bytes",
+ &xsabin->header.uuid, xsabin->header.length);
+
+release_firmware:
+ release_firmware(fw);
+
+ return ret;
+}
+
+static inline struct versal_pci_device *item_to_vdev(struct config_item *item)
+{
+ return container_of(to_configfs_subsystem(to_config_group(item)),
+ struct versal_pci_device, cfs_subsys);
+}
+
+static ssize_t versal_pci_cfs_config_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct versal_pci_device *vdev = item_to_vdev(item);
+ u32 config;
+ int ret;
+
+ ret = kstrtou32(page, 0, &config);
+ if (ret)
+ return -EINVAL;
+
+ if (config)
+ ret = versal_pci_load_shell(vdev, vdev->fw.name);
+
+ if (ret)
+ return -EFAULT;
+
+ return count;
+}
+CONFIGFS_ATTR_WO(versal_pci_cfs_, config);
+
+static ssize_t versal_pci_cfs_image_show(struct config_item *item, char *page)
+{
+ struct versal_pci_device *vdev = item_to_vdev(item);
+
+ vdev_info(vdev, "fw name: %s", vdev->fw.name);
+
+ return 0;
+}
+
+static ssize_t versal_pci_cfs_image_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct versal_pci_device *vdev = item_to_vdev(item);
+
+ count = snprintf(vdev->fw.name, sizeof(vdev->fw.name), "%s", page);
+
+ vdev_info(vdev, "fw name: %s", vdev->fw.name);
+ return count;
+}
+CONFIGFS_ATTR(versal_pci_cfs_, image);
+
+static struct configfs_attribute *versal_pci_cfs_attrs[] = {
+ &versal_pci_cfs_attr_config,
+ &versal_pci_cfs_attr_image,
+ NULL,
+};
+
+static const struct config_item_type versal_pci_cfs_table = {
+ .ct_owner = THIS_MODULE,
+ .ct_attrs = versal_pci_cfs_attrs,
+};
+
+static int versal_pci_cfs_init(struct versal_pci_device *vdev)
+{
+ struct configfs_subsystem *subsys = &vdev->cfs_subsys;
+
+ snprintf(subsys->su_group.cg_item.ci_namebuf,
+ sizeof(subsys->su_group.cg_item.ci_namebuf),
+ "%s%x", DRV_NAME, versal_pci_devid(vdev));
+
+ subsys->su_group.cg_item.ci_type = &versal_pci_cfs_table;
+
+ config_group_init(&subsys->su_group);
+ return configfs_register_subsystem(subsys);
+}
+
+static void versal_pci_fw_fini(struct versal_pci_device *vdev)
+{
+ uuid_copy(&vdev->intf_uuid, &uuid_null);
+}
+
+static void versal_pci_cfs_fini(struct configfs_subsystem *subsys)
+{
+ configfs_unregister_subsystem(subsys);
+}
+
+static void versal_pci_device_teardown(struct versal_pci_device *vdev)
+{
+ versal_pci_cfs_fini(&vdev->cfs_subsys);
+ versal_pci_fw_fini(vdev);
+}
+
+static void versal_pci_uuid_parse(struct versal_pci_device *vdev, uuid_t *uuid)
+{
+ char str[UUID_STRING_LEN];
+ u8 i, j;
+ int len = strlen(vdev->fw_id);
+
+ /* parse uuid into a valid uuid string format */
+ for (i = 0, j = 0; i < len && i < sizeof(str); i++) {
+ str[j++] = vdev->fw_id[i];
+ if (j == 8 || j == 13 || j == 18 || j == 23)
+ str[j++] = '-';
+ }
+
+ uuid_parse(str, uuid);
+ vdev_info(vdev, "Interface uuid %pU", uuid);
+}
+
+static int versal_pci_fw_init(struct versal_pci_device *vdev)
+{
+ /* TODO request compatible fw_id from card */
+
+ versal_pci_uuid_parse(vdev, &vdev->intf_uuid);
+
+ return 0;
+}
+
+static int versal_pci_device_setup(struct versal_pci_device *vdev)
+{
+ int ret;
+
+ ret = versal_pci_fw_init(vdev);
+ if (ret) {
+ vdev_err(vdev, "Failed to init fw, err %d", ret);
+ goto comm_chan_fini;
+ }
+
+ ret = versal_pci_cfs_init(vdev);
+ if (ret) {
+ vdev_err(vdev, "Failed to init configfs subsys, err %d", ret);
+ goto comm_chan_fini;
+ }
+
+ return 0;
+
+comm_chan_fini:
+
+ return ret;
+}
+
+static void versal_pci_remove(struct pci_dev *pdev)
+{
+ struct versal_pci_device *vdev = pci_get_drvdata(pdev);
+
+ versal_pci_device_teardown(vdev);
+}
+
+static int versal_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pdev_id)
+{
+ struct versal_pci_device *vdev;
+ int ret;
+
+ vdev = devm_kzalloc(&pdev->dev, sizeof(*vdev), GFP_KERNEL);
+ if (!vdev)
+ return -ENOMEM;
+
+ pci_set_drvdata(pdev, vdev);
+ vdev->pdev = pdev;
+
+ ret = pcim_enable_device(pdev);
+ if (ret) {
+ vdev_err(vdev, "Failed to enable device %d", ret);
+ return ret;
+ }
+
+ vdev->io_regs = pcim_iomap_region(vdev->pdev, MGMT_BAR, DRV_NAME);
+ if (IS_ERR(vdev->io_regs)) {
+ vdev_err(vdev, "Failed to map RM shared memory BAR%d", MGMT_BAR);
+ return PTR_ERR(vdev->io_regs);
+ }
+
+ ret = versal_pci_device_setup(vdev);
+ if (ret) {
+ vdev_err(vdev, "Failed to setup Versal device %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct pci_device_id versal_pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_V70PQ2), },
+ { PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_RAVE), },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, versal_pci_ids);
+
+static struct pci_driver versal_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = versal_pci_ids,
+ .probe = versal_pci_probe,
+ .remove = versal_pci_remove,
+};
+
+module_pci_driver(versal_pci_driver);
+
+MODULE_DESCRIPTION("AMD Versal PCIe Management Driver");
+MODULE_AUTHOR("XRT Team <runtimeca39d@amd.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/amd/versal-pci.h b/drivers/misc/amd/versal-pci.h
new file mode 100644
index 000000000000..ca309aee87ad
--- /dev/null
+++ b/drivers/misc/amd/versal-pci.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Driver for Versal PCIe device
+ *
+ * Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#ifndef __VERSAL_PCI_H
+#define __VERSAL_PCI_H
+
+#include <linux/configfs.h>
+#include <linux/firmware.h>
+
+#define MGMT_BAR 0
+
+#define vdev_info(vdev, fmt, args...) \
+ dev_info(&(vdev)->pdev->dev, "%s: "fmt, __func__, ##args)
+
+#define vdev_warn(vdev, fmt, args...) \
+ dev_warn(&(vdev)->pdev->dev, "%s: "fmt, __func__, ##args)
+
+#define vdev_err(vdev, fmt, args...) \
+ dev_err(&(vdev)->pdev->dev, "%s: "fmt, __func__, ##args)
+
+#define vdev_dbg(vdev, fmt, args...) \
+ dev_dbg(&(vdev)->pdev->dev, fmt, ##args)
+
+struct versal_pci_device;
+
+struct axlf_header {
+ __u64 length;
+ __u8 reserved1[24];
+ uuid_t rom_uuid;
+ __u8 reserved2[64];
+ uuid_t uuid;
+ __u8 reserved3[24];
+} __packed;
+
+struct axlf {
+ __u8 magic[8];
+ __u8 reserved[296];
+ struct axlf_header header;
+} __packed;
+
+struct fw_info {
+ __u32 opcode;
+ char name[128];
+};
+
+struct versal_pci_device {
+ struct pci_dev *pdev;
+
+ struct fw_info fw;
+
+ void __iomem *io_regs;
+ uuid_t intf_uuid;
+ __u8 fw_id[UUID_STRING_LEN + 1];
+
+ struct configfs_subsystem cfs_subsys;
+};
+
+#endif /* __VERSAL_PCI_H */
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH V0 2/3] drivers/misc/amd: Add remote queue service
2025-03-03 4:43 [PATCH V0 0/3] Add versal-pci driver Yidong Zhang
2025-03-03 4:43 ` [PATCH V0 1/3] drivers/misc/amd: Add new driver amd versal-pci Yidong Zhang
@ 2025-03-03 4:43 ` Yidong Zhang
2025-03-03 4:43 ` [PATCH V0 3/3] drivers/misc/amd: Add load base shell firmware Yidong Zhang
2025-04-15 14:11 ` [PATCH V0 0/3] Add versal-pci driver Greg KH
3 siblings, 0 replies; 6+ messages in thread
From: Yidong Zhang @ 2025-03-03 4:43 UTC (permalink / raw)
To: arnd, gregkh, linux-kernel
Cc: Yidong Zhang, lizhi.hou, christophe.jaillet, Nishad Saraf
The remote queue is a hardware queue based ring buffer service between mgmt
PF and firmware for data and message transfer.
Co-developed-by: Nishad Saraf <nishads@amd.com>
Signed-off-by: Nishad Saraf <nishads@amd.com>
Signed-off-by: Yidong Zhang <yidong.zhang@amd.com>
---
drivers/misc/amd/Makefile | 3 +-
drivers/misc/amd/versal-pci-rm-queue.c | 316 +++++++++++++++++++++++
drivers/misc/amd/versal-pci-rm-queue.h | 21 ++
drivers/misc/amd/versal-pci-rm-service.h | 209 +++++++++++++++
drivers/misc/amd/versal-pci.h | 1 +
5 files changed, 549 insertions(+), 1 deletion(-)
create mode 100644 drivers/misc/amd/versal-pci-rm-queue.c
create mode 100644 drivers/misc/amd/versal-pci-rm-queue.h
create mode 100644 drivers/misc/amd/versal-pci-rm-service.h
diff --git a/drivers/misc/amd/Makefile b/drivers/misc/amd/Makefile
index e3868cb39563..63879800a3e5 100644
--- a/drivers/misc/amd/Makefile
+++ b/drivers/misc/amd/Makefile
@@ -2,4 +2,5 @@
obj-$(CONFIG_AMD_VERSAL_PCI) += versal-pci.o
-versal-pci-$(CONFIG_AMD_VERSAL_PCI) := versal-pci-main.o
+versal-pci-$(CONFIG_AMD_VERSAL_PCI) := versal-pci-main.o \
+ versal-pci-rm-queue.o
diff --git a/drivers/misc/amd/versal-pci-rm-queue.c b/drivers/misc/amd/versal-pci-rm-queue.c
new file mode 100644
index 000000000000..8c1aca442253
--- /dev/null
+++ b/drivers/misc/amd/versal-pci-rm-queue.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Versal PCIe device
+ *
+ * Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include <linux/pci.h>
+
+#include "versal-pci.h"
+#include "versal-pci-rm-queue.h"
+#include "versal-pci-rm-service.h"
+
+static inline struct rm_device *to_rdev_msg_monitor(struct work_struct *w)
+{
+ return container_of(w, struct rm_device, msg_monitor);
+}
+
+static inline struct rm_device *to_rdev_msg_timer(struct timer_list *t)
+{
+ return container_of(t, struct rm_device, msg_timer);
+}
+
+static inline u32 rm_io_read(struct rm_device *rdev, u32 offset)
+{
+ /* TODO */
+ return 0;
+}
+
+static inline int rm_io_write(struct rm_device *rdev, u32 offset, u32 value)
+{
+ /* TODO */
+ return 0;
+}
+
+static inline u32 rm_queue_read(struct rm_device *rdev, u32 offset)
+{
+ /* TODO */
+ return 0;
+}
+
+static inline void rm_queue_write(struct rm_device *rdev, u32 offset, u32 value)
+{
+ /* TODO */
+}
+
+static inline void rm_queue_bulk_read(struct rm_device *rdev, u32 offset,
+ u32 *value, u32 size)
+{
+ /* TODO */
+}
+
+static inline void rm_queue_bulk_write(struct rm_device *rdev, u32 offset,
+ u32 *value, u32 size)
+{
+ /* TODO */
+}
+
+static inline u32 rm_queue_get_cidx(struct rm_device *rdev, enum rm_queue_type type)
+{
+ u32 off;
+
+ if (type == RM_QUEUE_SQ)
+ off = offsetof(struct rm_queue_header, sq_cidx);
+ else
+ off = offsetof(struct rm_queue_header, cq_cidx);
+
+ return rm_queue_read(rdev, off);
+}
+
+static inline void rm_queue_set_cidx(struct rm_device *rdev, enum rm_queue_type type,
+ u32 value)
+{
+ u32 off;
+
+ if (type == RM_QUEUE_SQ)
+ off = offsetof(struct rm_queue_header, sq_cidx);
+ else
+ off = offsetof(struct rm_queue_header, cq_cidx);
+
+ rm_queue_write(rdev, off, value);
+}
+
+static inline u32 rm_queue_get_pidx(struct rm_device *rdev, enum rm_queue_type type)
+{
+ if (type == RM_QUEUE_SQ)
+ return rm_io_read(rdev, RM_IO_SQ_PIDX_OFF);
+ else
+ return rm_io_read(rdev, RM_IO_CQ_PIDX_OFF);
+}
+
+static inline int rm_queue_set_pidx(struct rm_device *rdev,
+ enum rm_queue_type type, u32 value)
+{
+ if (type == RM_QUEUE_SQ)
+ return rm_io_write(rdev, RM_IO_SQ_PIDX_OFF, value);
+ else
+ return rm_io_write(rdev, RM_IO_CQ_PIDX_OFF, value);
+}
+
+static inline u32 rm_queue_get_sq_slot_offset(struct rm_device *rdev)
+{
+ u32 index;
+
+ if ((rdev->sq.pidx - rdev->sq.cidx) >= rdev->queue_size)
+ return RM_INVALID_SLOT;
+
+ index = rdev->sq.pidx & (rdev->queue_size - 1);
+ return rdev->sq.offset + RM_CMD_SQ_SLOT_SIZE * index;
+}
+
+static inline u32 rm_queue_get_cq_slot_offset(struct rm_device *rdev)
+{
+ u32 index;
+
+ index = rdev->cq.cidx & (rdev->queue_size - 1);
+ return rdev->cq.offset + RM_CMD_CQ_SLOT_SIZE * index;
+}
+
+static int rm_queue_submit_cmd(struct rm_cmd *cmd)
+{
+ struct versal_pci_device *vdev = cmd->rdev->vdev;
+ struct rm_device *rdev = cmd->rdev;
+ u32 offset;
+ int ret;
+
+ guard(mutex)(&rdev->queue);
+
+ offset = rm_queue_get_sq_slot_offset(rdev);
+ if (!offset) {
+ vdev_err(vdev, "No SQ slot available");
+ return -ENOSPC;
+ }
+
+ rm_queue_bulk_write(rdev, offset, (u32 *)&cmd->sq_msg,
+ sizeof(cmd->sq_msg));
+
+ ret = rm_queue_set_pidx(rdev, RM_QUEUE_SQ, ++rdev->sq.pidx);
+ if (ret) {
+ vdev_err(vdev, "Failed to update PIDX, ret %d", ret);
+ return ret;
+ }
+
+ list_add_tail(&cmd->list, &rdev->submitted_cmds);
+ return ret;
+}
+
+void rm_queue_withdraw_cmd(struct rm_cmd *cmd)
+{
+ guard(mutex)(&cmd->rdev->queue);
+ list_del(&cmd->list);
+}
+
+static int rm_queue_wait_cmd_timeout(struct rm_cmd *cmd, unsigned long timeout)
+{
+ struct versal_pci_device *vdev = cmd->rdev->vdev;
+ int ret;
+
+ if (wait_for_completion_timeout(&cmd->executed, timeout)) {
+ ret = cmd->cq_msg.data.rcode;
+ if (!ret)
+ return 0;
+
+ vdev_err(vdev, "CMD returned with a failure: %d", ret);
+ return ret;
+ }
+
+ /*
+ * each cmds will be cleaned up by complete before it times out.
+ * if we reach here, the cmd should be cleared and hot reset should
+ * be issued.
+ */
+ vdev_err(vdev, "cmd is timedout after, please reset the card");
+ rm_queue_withdraw_cmd(cmd);
+ return -ETIME;
+}
+
+int rm_queue_send_cmd(struct rm_cmd *cmd, unsigned long timeout)
+{
+ int ret;
+
+ ret = rm_queue_submit_cmd(cmd);
+ if (ret)
+ return ret;
+
+ return rm_queue_wait_cmd_timeout(cmd, timeout);
+}
+
+static int rm_process_msg(struct rm_device *rdev)
+{
+ struct versal_pci_device *vdev = rdev->vdev;
+ struct rm_cmd *cmd, *next;
+ struct rm_cmd_cq_hdr header;
+ u32 offset;
+
+ offset = rm_queue_get_cq_slot_offset(rdev);
+ if (!offset) {
+ vdev_err(vdev, "Invalid CQ offset");
+ return -EINVAL;
+ }
+
+ rm_queue_bulk_read(rdev, offset, (u32 *)&header, sizeof(header));
+
+ list_for_each_entry_safe(cmd, next, &rdev->submitted_cmds, list) {
+ u32 value = 0;
+
+ if (cmd->sq_msg.hdr.id != header.id)
+ continue;
+
+ rm_queue_bulk_read(rdev, offset + sizeof(cmd->cq_msg.hdr),
+ (u32 *)&cmd->cq_msg.data,
+ sizeof(cmd->cq_msg.data));
+
+ rm_queue_write(rdev, offset, value);
+
+ list_del(&cmd->list);
+ complete(&cmd->executed);
+ return 0;
+ }
+
+ vdev_err(vdev, "Unknown cmd ID %d found in CQ", header.id);
+ return -EFAULT;
+}
+
+static void rm_check_msg(struct work_struct *w)
+{
+ struct rm_device *rdev = to_rdev_msg_monitor(w);
+ int ret;
+
+ guard(mutex)(&rdev->queue);
+
+ rdev->sq.cidx = rm_queue_get_cidx(rdev, RM_QUEUE_SQ);
+ rdev->cq.pidx = rm_queue_get_pidx(rdev, RM_QUEUE_CQ);
+
+ while (rdev->cq.cidx < rdev->cq.pidx) {
+ ret = rm_process_msg(rdev);
+ if (ret)
+ break;
+
+ rdev->cq.cidx++;
+
+ rm_queue_set_cidx(rdev, RM_QUEUE_CQ, rdev->cq.cidx);
+ }
+}
+
+static void rm_sched_work(struct timer_list *t)
+{
+ struct rm_device *rdev = to_rdev_msg_timer(t);
+
+ /* Schedule a work in the general workqueue */
+ schedule_work(&rdev->msg_monitor);
+ /* Periodic timer */
+ mod_timer(&rdev->msg_timer, jiffies + RM_COMPLETION_TIMER);
+}
+
+void rm_queue_fini(struct rm_device *rdev)
+{
+ del_timer_sync(&rdev->msg_timer);
+ cancel_work_sync(&rdev->msg_monitor);
+}
+
+int rm_queue_init(struct rm_device *rdev)
+{
+ struct versal_pci_device *vdev = rdev->vdev;
+ struct rm_queue_header header = {0};
+ int ret;
+
+ INIT_LIST_HEAD(&rdev->submitted_cmds);
+ ret = devm_mutex_init(&vdev->pdev->dev, &rdev->queue);
+ if (ret)
+ return ret;
+
+ rm_queue_bulk_read(rdev, RM_HDR_OFF, (u32 *)&header, sizeof(header));
+
+ if (header.magic != RM_QUEUE_HDR_MAGIC_NUM) {
+ vdev_err(vdev, "Invalid RM queue header");
+ return -ENODEV;
+ }
+
+ if (!header.version) {
+ vdev_err(vdev, "Invalid RM queue header");
+ return -ENODEV;
+ }
+
+ sema_init(&rdev->sq.data_lock, 1);
+ sema_init(&rdev->cq.data_lock, 1);
+ rdev->queue_size = header.size;
+ rdev->sq.offset = header.sq_off;
+ rdev->cq.offset = header.cq_off;
+ rdev->sq.type = RM_QUEUE_SQ;
+ rdev->cq.type = RM_QUEUE_CQ;
+ rdev->sq.data_size = rdev->queue_buffer_size - RM_CMD_CQ_BUFFER_SIZE;
+ rdev->cq.data_size = RM_CMD_CQ_BUFFER_SIZE;
+ rdev->sq.data_offset = rdev->queue_buffer_start +
+ RM_CMD_CQ_BUFFER_OFFSET + RM_CMD_CQ_BUFFER_SIZE;
+ rdev->cq.data_offset = rdev->queue_buffer_start +
+ RM_CMD_CQ_BUFFER_OFFSET;
+ rdev->sq.cidx = header.sq_cidx;
+ rdev->cq.cidx = header.cq_cidx;
+
+ rdev->sq.pidx = rm_queue_get_pidx(rdev, RM_QUEUE_SQ);
+ rdev->cq.pidx = rm_queue_get_pidx(rdev, RM_QUEUE_CQ);
+
+ if (rdev->cq.cidx != rdev->cq.pidx) {
+ vdev_warn(vdev, "Clearing stale completions");
+ rdev->cq.cidx = rdev->cq.pidx;
+ rm_queue_set_cidx(rdev, RM_QUEUE_CQ, rdev->cq.cidx);
+ }
+
+ /* Create and schedule timer to do recurring work */
+ INIT_WORK(&rdev->msg_monitor, &rm_check_msg);
+ timer_setup(&rdev->msg_timer, &rm_sched_work, 0);
+ mod_timer(&rdev->msg_timer, jiffies + RM_COMPLETION_TIMER);
+
+ return 0;
+}
diff --git a/drivers/misc/amd/versal-pci-rm-queue.h b/drivers/misc/amd/versal-pci-rm-queue.h
new file mode 100644
index 000000000000..d5d991704d5c
--- /dev/null
+++ b/drivers/misc/amd/versal-pci-rm-queue.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Driver for Versal PCIe device
+ *
+ * Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#ifndef __RM_QUEUE_H
+#define __RM_QUEUE_H
+
+struct rm_device;
+
+/* rm queue hardware setup */
+int rm_queue_init(struct rm_device *rdev);
+void rm_queue_fini(struct rm_device *rdev);
+
+/* rm queue common API */
+int rm_queue_send_cmd(struct rm_cmd *cmd, unsigned long timeout);
+void rm_queue_withdraw_cmd(struct rm_cmd *cmd);
+
+#endif /* __RM_QUEUE_H */
diff --git a/drivers/misc/amd/versal-pci-rm-service.h b/drivers/misc/amd/versal-pci-rm-service.h
new file mode 100644
index 000000000000..d2397a1a672c
--- /dev/null
+++ b/drivers/misc/amd/versal-pci-rm-service.h
@@ -0,0 +1,209 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Driver for Versal PCIe device
+ *
+ * Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#ifndef __RM_SERVICE_H
+#define __RM_SERVICE_H
+
+#define RM_HDR_OFF 0x0
+#define RM_HDR_MAGIC_NUM 0x564D5230
+#define RM_QUEUE_HDR_MAGIC_NUM 0x5847513F
+#define RM_PCI_IO_BAR_OFF 0x2010000
+#define RM_PCI_IO_SIZE SZ_4K
+#define RM_PCI_SHMEM_BAR_OFF 0x8000000
+#define RM_PCI_SHMEM_SIZE SZ_128M
+#define RM_PCI_SHMEM_HDR_SIZE 0x28
+
+#define RM_QUEUE_HDR_MAGIC_NUM_OFF 0x0
+#define RM_IO_SQ_PIDX_OFF 0x0
+#define RM_IO_CQ_PIDX_OFF 0x100
+
+#define RM_CMD_ID_MIN 1
+#define RM_CMD_ID_MAX (BIT(17) - 1)
+#define RM_CMD_SQ_HDR_OPS_MSK GENMASK(15, 0)
+#define RM_CMD_SQ_HDR_SIZE_MSK GENMASK(14, 0)
+#define RM_CMD_SQ_SLOT_SIZE SZ_512
+#define RM_CMD_CQ_SLOT_SIZE SZ_16
+#define RM_CMD_CQ_BUFFER_SIZE (1024 * 1024)
+#define RM_CMD_CQ_BUFFER_OFFSET 0x0
+#define RM_CMD_LOG_PAGE_TYPE_MASK GENMASK(15, 0)
+#define RM_CMD_VMR_CONTROL_MSK GENMASK(10, 8)
+#define RM_CMD_VMR_CONTROL_PS_MASK BIT(9)
+
+#define RM_CMD_WAIT_CONFIG_TIMEOUT msecs_to_jiffies(10 * 1000)
+#define RM_CMD_WAIT_DOWNLOAD_TIMEOUT msecs_to_jiffies(300 * 1000)
+
+#define RM_COMPLETION_TIMER (HZ / 10)
+#define RM_HEALTH_CHECK_TIMER (HZ)
+
+#define RM_INVALID_SLOT 0
+
+enum rm_queue_opcode {
+ RM_QUEUE_OP_LOAD_XCLBIN = 0x0,
+ RM_QUEUE_OP_GET_LOG_PAGE = 0x8,
+ RM_QUEUE_OP_LOAD_FW = 0xA,
+ RM_QUEUE_OP_LOAD_APU_FW = 0xD,
+ RM_QUEUE_OP_VMR_CONTROL = 0xE,
+ RM_QUEUE_OP_IDENTIFY = 0x202,
+};
+
+struct rm_cmd_sq_hdr {
+ __u16 opcode;
+ __u16 msg_size;
+ __u16 id;
+ __u16 reserved;
+} __packed;
+
+struct rm_cmd_cq_hdr {
+ __u16 id;
+ __u16 reserved;
+} __packed;
+
+struct rm_cmd_sq_bin {
+ __u64 address;
+ __u32 size;
+ __u32 reserved1;
+ __u32 reserved2;
+ __u32 reserved3;
+ __u64 reserved4;
+} __packed;
+
+struct rm_cmd_sq_log_page {
+ __u64 address;
+ __u32 size;
+ __u32 reserved1;
+ __u32 type;
+ __u32 reserved2;
+} __packed;
+
+struct rm_cmd_sq_ctrl {
+ __u32 status;
+} __packed;
+
+struct rm_cmd_sq_data {
+ union {
+ struct rm_cmd_sq_log_page page;
+ struct rm_cmd_sq_bin bin;
+ struct rm_cmd_sq_ctrl ctrl;
+ };
+} __packed;
+
+struct rm_cmd_cq_identify {
+ __u16 major;
+ __u16 minor;
+ __u32 reserved;
+} __packed;
+
+struct rm_cmd_cq_log_page {
+ __u32 len;
+ __u32 reserved;
+} __packed;
+
+struct rm_cmd_cq_control {
+ __u16 status;
+ __u16 reserved1;
+ __u32 reserved2;
+} __packed;
+
+struct rm_cmd_cq_data {
+ union {
+ struct rm_cmd_cq_identify identify;
+ struct rm_cmd_cq_log_page page;
+ struct rm_cmd_cq_control ctrl;
+ __u32 reserved[2];
+ };
+ __u32 rcode;
+} __packed;
+
+struct rm_cmd_sq_msg {
+ struct rm_cmd_sq_hdr hdr;
+ struct rm_cmd_sq_data data;
+} __packed;
+
+struct rm_cmd_cq_msg {
+ struct rm_cmd_cq_hdr hdr;
+ struct rm_cmd_cq_data data;
+} __packed;
+
+struct rm_cmd {
+ struct rm_device *rdev;
+ struct list_head list;
+ struct completion executed;
+ struct rm_cmd_sq_msg sq_msg;
+ struct rm_cmd_cq_msg cq_msg;
+ enum rm_queue_opcode opcode;
+ __u8 *buffer;
+ ssize_t size;
+};
+
+enum rm_queue_type {
+ RM_QUEUE_SQ,
+ RM_QUEUE_CQ
+};
+
+enum rm_cmd_log_page_type {
+ RM_CMD_LOG_PAGE_AXI_TRIP_STATUS = 0x0,
+ RM_CMD_LOG_PAGE_FW_ID = 0xA,
+};
+
+struct rm_queue {
+ enum rm_queue_type type;
+ __u32 pidx;
+ __u32 cidx;
+ __u32 offset;
+ __u32 data_offset;
+ __u32 data_size;
+ struct semaphore data_lock;
+};
+
+struct rm_queue_header {
+ __u32 magic;
+ __u32 version;
+ __u32 size;
+ __u32 sq_off;
+ __u32 sq_slot_size;
+ __u32 cq_off;
+ __u32 sq_cidx;
+ __u32 cq_cidx;
+};
+
+struct rm_header {
+ __u32 magic;
+ __u32 queue_base;
+ __u32 queue_size;
+ __u32 status_off;
+ __u32 status_len;
+ __u32 log_index;
+ __u32 log_off;
+ __u32 log_size;
+ __u32 data_start;
+ __u32 data_end;
+};
+
+struct rm_device {
+ struct versal_pci_device *vdev;
+
+ struct rm_header rm_metadata;
+ __u32 queue_buffer_start;
+ __u32 queue_buffer_size;
+ __u32 queue_base;
+
+ /* Lock to queue access */
+ struct mutex queue;
+ struct rm_queue sq;
+ struct rm_queue cq;
+ __u32 queue_size;
+
+ struct timer_list msg_timer;
+ struct work_struct msg_monitor;
+ struct timer_list health_timer;
+ struct work_struct health_monitor;
+ struct list_head submitted_cmds;
+
+ __u32 firewall_tripped;
+};
+
+#endif /* __RM_SERVICE_H */
diff --git a/drivers/misc/amd/versal-pci.h b/drivers/misc/amd/versal-pci.h
index ca309aee87ad..33f0ef881a33 100644
--- a/drivers/misc/amd/versal-pci.h
+++ b/drivers/misc/amd/versal-pci.h
@@ -26,6 +26,7 @@
dev_dbg(&(vdev)->pdev->dev, fmt, ##args)
struct versal_pci_device;
+struct rm_cmd;
struct axlf_header {
__u64 length;
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH V0 3/3] drivers/misc/amd: Add load base shell firmware
2025-03-03 4:43 [PATCH V0 0/3] Add versal-pci driver Yidong Zhang
2025-03-03 4:43 ` [PATCH V0 1/3] drivers/misc/amd: Add new driver amd versal-pci Yidong Zhang
2025-03-03 4:43 ` [PATCH V0 2/3] drivers/misc/amd: Add remote queue service Yidong Zhang
@ 2025-03-03 4:43 ` Yidong Zhang
2025-04-15 14:11 ` [PATCH V0 0/3] Add versal-pci driver Greg KH
3 siblings, 0 replies; 6+ messages in thread
From: Yidong Zhang @ 2025-03-03 4:43 UTC (permalink / raw)
To: arnd, gregkh, linux-kernel
Cc: Yidong Zhang, lizhi.hou, christophe.jaillet, Nishad Saraf
- programming FW
- The base FW is downloaded onto the flash of the card.
- The APU FW is downloaded once after a POR (power on reset).
- Reloading the driver will not change any existing hardware.
Co-developed-by: Nishad Saraf <nishads@amd.com>
Signed-off-by: Nishad Saraf <nishads@amd.com>
Signed-off-by: Yidong Zhang <yidong.zhang@amd.com>
---
drivers/misc/amd/Makefile | 3 +-
drivers/misc/amd/versal-pci-main.c | 45 +-
drivers/misc/amd/versal-pci-rm-queue.c | 14 +-
drivers/misc/amd/versal-pci-rm-service.c | 497 +++++++++++++++++++++++
drivers/misc/amd/versal-pci-rm-service.h | 20 +
drivers/misc/amd/versal-pci.h | 1 +
6 files changed, 569 insertions(+), 11 deletions(-)
create mode 100644 drivers/misc/amd/versal-pci-rm-service.c
diff --git a/drivers/misc/amd/Makefile b/drivers/misc/amd/Makefile
index 63879800a3e5..03394b06fd33 100644
--- a/drivers/misc/amd/Makefile
+++ b/drivers/misc/amd/Makefile
@@ -3,4 +3,5 @@
obj-$(CONFIG_AMD_VERSAL_PCI) += versal-pci.o
versal-pci-$(CONFIG_AMD_VERSAL_PCI) := versal-pci-main.o \
- versal-pci-rm-queue.o
+ versal-pci-rm-queue.o \
+ versal-pci-rm-service.o
diff --git a/drivers/misc/amd/versal-pci-main.c b/drivers/misc/amd/versal-pci-main.c
index 1c61c88120b4..6d3a2833f0c4 100644
--- a/drivers/misc/amd/versal-pci-main.c
+++ b/drivers/misc/amd/versal-pci-main.c
@@ -8,6 +8,8 @@
#include <linux/pci.h>
#include "versal-pci.h"
+#include "versal-pci-rm-service.h"
+#include "versal-pci-rm-queue.h"
#define DRV_NAME "amd-versal-pci"
@@ -21,6 +23,29 @@ static inline u32 versal_pci_devid(struct versal_pci_device *vdev)
PCI_DEVID(vdev->pdev->bus->number, vdev->pdev->devfn));
}
+static int versal_pci_upload_fw(struct versal_pci_device *vdev,
+ enum rm_queue_opcode opcode,
+ const char *data,
+ size_t size)
+{
+ struct rm_cmd *cmd;
+ int ret;
+
+ ret = rm_queue_create_cmd(vdev->rdev, opcode, &cmd);
+ if (ret)
+ return ret;
+
+ ret = rm_queue_data_init(cmd, data, size);
+ if (ret)
+ goto done;
+
+ ret = rm_queue_send_cmd(cmd, RM_CMD_WAIT_DOWNLOAD_TIMEOUT);
+
+done:
+ rm_queue_destroy_cmd(cmd);
+ return ret;
+}
+
static int versal_pci_load_shell(struct versal_pci_device *vdev, char *fw_name)
{
const struct firmware *fw;
@@ -57,7 +82,8 @@ static int versal_pci_load_shell(struct versal_pci_device *vdev, char *fw_name)
goto release_firmware;
}
- /* TODO upload fw to card */
+ ret = versal_pci_upload_fw(vdev, RM_QUEUE_OP_LOAD_FW,
+ (char *)xsabin, xsabin->header.length);
if (ret) {
vdev_err(vdev, "failed to load xsabin %s : %d", fw_name, ret);
goto release_firmware;
@@ -159,6 +185,7 @@ static void versal_pci_device_teardown(struct versal_pci_device *vdev)
{
versal_pci_cfs_fini(&vdev->cfs_subsys);
versal_pci_fw_fini(vdev);
+ versal_pci_rm_fini(vdev->rdev);
}
static void versal_pci_uuid_parse(struct versal_pci_device *vdev, uuid_t *uuid)
@@ -180,7 +207,13 @@ static void versal_pci_uuid_parse(struct versal_pci_device *vdev, uuid_t *uuid)
static int versal_pci_fw_init(struct versal_pci_device *vdev)
{
- /* TODO request compatible fw_id from card */
+ int ret;
+
+ ret = rm_queue_get_fw_id(vdev->rdev);
+ if (ret) {
+ vdev_warn(vdev, "Failed to get fw_id");
+ return -EINVAL;
+ }
versal_pci_uuid_parse(vdev, &vdev->intf_uuid);
@@ -191,6 +224,13 @@ static int versal_pci_device_setup(struct versal_pci_device *vdev)
{
int ret;
+ vdev->rdev = versal_pci_rm_init(vdev);
+ if (IS_ERR(vdev->rdev)) {
+ ret = PTR_ERR(vdev->rdev);
+ vdev_err(vdev, "Failed to init remote queue, err %d", ret);
+ return ret;
+ }
+
ret = versal_pci_fw_init(vdev);
if (ret) {
vdev_err(vdev, "Failed to init fw, err %d", ret);
@@ -206,6 +246,7 @@ static int versal_pci_device_setup(struct versal_pci_device *vdev)
return 0;
comm_chan_fini:
+ versal_pci_rm_fini(vdev->rdev);
return ret;
}
diff --git a/drivers/misc/amd/versal-pci-rm-queue.c b/drivers/misc/amd/versal-pci-rm-queue.c
index 8c1aca442253..7afc5a1e238c 100644
--- a/drivers/misc/amd/versal-pci-rm-queue.c
+++ b/drivers/misc/amd/versal-pci-rm-queue.c
@@ -23,37 +23,35 @@ static inline struct rm_device *to_rdev_msg_timer(struct timer_list *t)
static inline u32 rm_io_read(struct rm_device *rdev, u32 offset)
{
- /* TODO */
- return 0;
+ return rm_reg_read(rdev, RM_PCI_IO_BAR_OFF + offset);
}
static inline int rm_io_write(struct rm_device *rdev, u32 offset, u32 value)
{
- /* TODO */
+ rm_reg_write(rdev, RM_PCI_IO_BAR_OFF + offset, value);
return 0;
}
static inline u32 rm_queue_read(struct rm_device *rdev, u32 offset)
{
- /* TODO */
- return 0;
+ return rm_reg_read(rdev, RM_PCI_SHMEM_BAR_OFF + rdev->queue_base + offset);
}
static inline void rm_queue_write(struct rm_device *rdev, u32 offset, u32 value)
{
- /* TODO */
+ rm_reg_write(rdev, RM_PCI_SHMEM_BAR_OFF + rdev->queue_base + offset, value);
}
static inline void rm_queue_bulk_read(struct rm_device *rdev, u32 offset,
u32 *value, u32 size)
{
- /* TODO */
+ rm_bulk_reg_read(rdev, RM_PCI_SHMEM_BAR_OFF + rdev->queue_base + offset, value, size);
}
static inline void rm_queue_bulk_write(struct rm_device *rdev, u32 offset,
u32 *value, u32 size)
{
- /* TODO */
+ rm_bulk_reg_write(rdev, RM_PCI_SHMEM_BAR_OFF + rdev->queue_base + offset, value, size);
}
static inline u32 rm_queue_get_cidx(struct rm_device *rdev, enum rm_queue_type type)
diff --git a/drivers/misc/amd/versal-pci-rm-service.c b/drivers/misc/amd/versal-pci-rm-service.c
new file mode 100644
index 000000000000..6dbad1c6563d
--- /dev/null
+++ b/drivers/misc/amd/versal-pci-rm-service.c
@@ -0,0 +1,497 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Versal PCIe device
+ *
+ * Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+
+#include "versal-pci.h"
+#include "versal-pci-rm-service.h"
+#include "versal-pci-rm-queue.h"
+
+static DEFINE_IDA(rm_cmd_ids);
+
+static void rm_uninstall_health_monitor(struct rm_device *rdev);
+
+static inline struct rm_device *to_rdev_health_monitor(struct work_struct *w)
+{
+ return container_of(w, struct rm_device, health_monitor);
+}
+
+static inline struct rm_device *to_rdev_health_timer(struct timer_list *t)
+{
+ return container_of(t, struct rm_device, health_timer);
+}
+
+u32 rm_reg_read(struct rm_device *rdev, u32 offset)
+{
+ return readl(rdev->vdev->io_regs + offset);
+}
+
+void rm_reg_write(struct rm_device *rdev, u32 offset, const u32 value)
+{
+ writel(value, rdev->vdev->io_regs + offset);
+}
+
+void rm_bulk_reg_read(struct rm_device *rdev, u32 offset, u32 *value, size_t size)
+{
+ void __iomem *src = rdev->vdev->io_regs + offset;
+ void *dst = (void *)value;
+
+ memcpy_fromio(dst, src, size);
+ /* Barrier of reading data from device */
+ rmb();
+}
+
+void rm_bulk_reg_write(struct rm_device *rdev, u32 offset, const void *value, size_t size)
+{
+ void __iomem *dst = rdev->vdev->io_regs + offset;
+
+ memcpy_toio(dst, value, size);
+ /* Barrier of writing data to device */
+ wmb();
+}
+
+static inline u32 rm_shmem_read(struct rm_device *rdev, u32 offset)
+{
+ return rm_reg_read(rdev, RM_PCI_SHMEM_BAR_OFF + offset);
+}
+
+static inline void rm_shmem_bulk_read(struct rm_device *rdev, u32 offset,
+ u32 *value, u32 size)
+{
+ rm_bulk_reg_read(rdev, RM_PCI_SHMEM_BAR_OFF + offset, value, size);
+}
+
+static inline void rm_shmem_bulk_write(struct rm_device *rdev, u32 offset,
+ const void *value, u32 size)
+{
+ rm_bulk_reg_write(rdev, RM_PCI_SHMEM_BAR_OFF + offset, value, size);
+}
+
+void rm_queue_destroy_cmd(struct rm_cmd *cmd)
+{
+ ida_free(&rm_cmd_ids, cmd->sq_msg.hdr.id);
+ kfree(cmd);
+}
+
+static int rm_queue_copy_response(struct rm_cmd *cmd, void *buffer, ssize_t len)
+{
+ struct rm_cmd_cq_log_page *result = &cmd->cq_msg.data.page;
+ u64 off = cmd->sq_msg.data.page.address;
+
+ if (!result->len || len < result->len) {
+ vdev_err(cmd->rdev->vdev, "Invalid response or buffer size");
+ return -EINVAL;
+ }
+
+ rm_shmem_bulk_read(cmd->rdev, off, (u32 *)buffer, result->len);
+ return 0;
+}
+
+static void rm_queue_payload_fini(struct rm_cmd *cmd)
+{
+ up(&cmd->rdev->cq.data_lock);
+}
+
+static int rm_queue_payload_init(struct rm_cmd *cmd,
+ enum rm_cmd_log_page_type type)
+{
+ struct rm_device *rdev = cmd->rdev;
+ int ret;
+
+ ret = down_interruptible(&rdev->cq.data_lock);
+ if (ret)
+ return ret;
+
+ cmd->sq_msg.data.page.address = rdev->cq.data_offset;
+ cmd->sq_msg.data.page.size = rdev->cq.data_size;
+ cmd->sq_msg.data.page.reserved1 = 0;
+ cmd->sq_msg.data.page.type = FIELD_PREP(RM_CMD_LOG_PAGE_TYPE_MASK,
+ type);
+ return 0;
+}
+
+void rm_queue_data_fini(struct rm_cmd *cmd)
+{
+ up(&cmd->rdev->sq.data_lock);
+}
+
+int rm_queue_data_init(struct rm_cmd *cmd, const char *buffer, ssize_t size)
+{
+ struct rm_device *rdev = cmd->rdev;
+ int ret;
+
+ if (!size || size > rdev->sq.data_size) {
+ vdev_err(rdev->vdev, "Unsupported file size");
+ return -ENOMEM;
+ }
+
+ ret = down_interruptible(&rdev->sq.data_lock);
+ if (ret)
+ return ret;
+
+ rm_shmem_bulk_write(cmd->rdev, rdev->sq.data_offset, buffer, size);
+
+ cmd->sq_msg.data.bin.address = rdev->sq.data_offset;
+ cmd->sq_msg.data.bin.size = size;
+ return 0;
+}
+
+int rm_queue_create_cmd(struct rm_device *rdev, enum rm_queue_opcode opcode,
+ struct rm_cmd **cmd_ptr)
+{
+ struct rm_cmd *cmd = NULL;
+ int ret, id;
+ u16 size;
+
+ if (rdev->firewall_tripped)
+ return -ENODEV;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+ cmd->rdev = rdev;
+
+ switch (opcode) {
+ case RM_QUEUE_OP_LOAD_XCLBIN:
+ fallthrough;
+ case RM_QUEUE_OP_LOAD_FW:
+ fallthrough;
+ case RM_QUEUE_OP_LOAD_APU_FW:
+ size = sizeof(struct rm_cmd_sq_bin);
+ break;
+ case RM_QUEUE_OP_GET_LOG_PAGE:
+ size = sizeof(struct rm_cmd_sq_log_page);
+ break;
+ case RM_QUEUE_OP_IDENTIFY:
+ size = 0;
+ break;
+ case RM_QUEUE_OP_VMR_CONTROL:
+ size = sizeof(struct rm_cmd_sq_ctrl);
+ break;
+ default:
+ vdev_err(rdev->vdev, "Invalid cmd opcode %d", opcode);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ cmd->opcode = opcode;
+ cmd->sq_msg.hdr.opcode = FIELD_PREP(RM_CMD_SQ_HDR_OPS_MSK, opcode);
+ cmd->sq_msg.hdr.msg_size = FIELD_PREP(RM_CMD_SQ_HDR_SIZE_MSK, size);
+
+ id = ida_alloc_range(&rm_cmd_ids, RM_CMD_ID_MIN, RM_CMD_ID_MAX, GFP_KERNEL);
+ if (id < 0) {
+ vdev_err(rdev->vdev, "Failed to alloc cmd ID: %d", id);
+ ret = id;
+ goto error;
+ }
+ cmd->sq_msg.hdr.id = id;
+
+ init_completion(&cmd->executed);
+
+ *cmd_ptr = cmd;
+ return 0;
+error:
+ kfree(cmd);
+ return ret;
+}
+
+static int rm_queue_verify(struct rm_device *rdev)
+{
+ struct versal_pci_device *vdev = rdev->vdev;
+ struct rm_cmd_cq_identify *result;
+ struct rm_cmd *cmd;
+ u32 major, minor;
+ int ret;
+
+ ret = rm_queue_create_cmd(rdev, RM_QUEUE_OP_IDENTIFY, &cmd);
+ if (ret)
+ return ret;
+
+ ret = rm_queue_send_cmd(cmd, RM_CMD_WAIT_CONFIG_TIMEOUT);
+ if (ret)
+ goto error;
+
+ result = &cmd->cq_msg.data.identify;
+ major = result->major;
+ minor = result->minor;
+ vdev_dbg(vdev, "VMR version %d.%d", major, minor);
+ if (!major) {
+ vdev_err(vdev, "VMR version is unsupported");
+ ret = -EOPNOTSUPP;
+ }
+
+error:
+ rm_queue_destroy_cmd(cmd);
+ return ret;
+}
+
+static int rm_check_apu_status(struct rm_device *rdev, bool *status)
+{
+ struct rm_cmd_cq_control *result;
+ struct rm_cmd *cmd;
+ int ret;
+
+ ret = rm_queue_create_cmd(rdev, RM_QUEUE_OP_VMR_CONTROL, &cmd);
+ if (ret)
+ return ret;
+
+ ret = rm_queue_send_cmd(cmd, RM_CMD_WAIT_CONFIG_TIMEOUT);
+ if (ret)
+ goto error;
+
+ result = &cmd->cq_msg.data.ctrl;
+ *status = FIELD_GET(RM_CMD_VMR_CONTROL_PS_MASK, result->status);
+
+ rm_queue_destroy_cmd(cmd);
+ return 0;
+
+error:
+ rm_queue_destroy_cmd(cmd);
+ return ret;
+}
+
+static int rm_download_apu_fw(struct rm_device *rdev, char *data, ssize_t size)
+{
+ struct rm_cmd *cmd;
+ int ret;
+
+ ret = rm_queue_create_cmd(rdev, RM_QUEUE_OP_LOAD_APU_FW, &cmd);
+ if (ret)
+ return ret;
+
+ ret = rm_queue_data_init(cmd, data, size);
+ if (ret)
+ goto done;
+
+ ret = rm_queue_send_cmd(cmd, RM_CMD_WAIT_DOWNLOAD_TIMEOUT);
+
+done:
+ rm_queue_destroy_cmd(cmd);
+ return ret;
+}
+
+int rm_queue_boot_apu(struct rm_device *rdev)
+{
+ char *bin = "xilinx/xrt-versal-apu.xsabin";
+ const struct firmware *fw = NULL;
+ bool status;
+ int ret;
+
+ ret = rm_check_apu_status(rdev, &status);
+ if (ret) {
+ vdev_err(rdev->vdev, "Failed to get APU status");
+ return ret;
+ }
+
+ if (status) {
+ vdev_dbg(rdev->vdev, "APU online. Skipping APU FW download");
+ return 0;
+ }
+
+ ret = request_firmware(&fw, bin, &rdev->vdev->pdev->dev);
+ if (ret) {
+ vdev_warn(rdev->vdev, "Request APU FW %s failed %d", bin, ret);
+ return ret;
+ }
+
+ vdev_dbg(rdev->vdev, "Starting... APU FW download");
+ ret = rm_download_apu_fw(rdev, (char *)fw->data, fw->size);
+ vdev_dbg(rdev->vdev, "Finished... APU FW download %d", ret);
+
+ if (ret)
+ vdev_err(rdev->vdev, "Failed to download APU FW, ret:%d", ret);
+
+ release_firmware(fw);
+
+ return ret;
+}
+
+static void rm_check_health(struct work_struct *w)
+{
+ struct rm_device *rdev = to_rdev_health_monitor(w);
+ u32 max_len = PAGE_SIZE;
+ struct rm_cmd *cmd;
+ int ret;
+
+ ret = rm_queue_create_cmd(rdev, RM_QUEUE_OP_GET_LOG_PAGE, &cmd);
+ if (ret)
+ return;
+
+ ret = rm_queue_payload_init(cmd, RM_CMD_LOG_PAGE_AXI_TRIP_STATUS);
+ if (ret)
+ goto destroy_cmd;
+
+ ret = rm_queue_send_cmd(cmd, RM_CMD_WAIT_CONFIG_TIMEOUT);
+ if (ret == -ETIME || ret == -EINVAL)
+ goto payload_fini;
+
+ if (ret) {
+ u32 log_len = cmd->cq_msg.data.page.len;
+
+ if (log_len > max_len) {
+ vdev_warn(rdev->vdev, "msg size %d is greater than requested %d",
+ log_len, max_len);
+ log_len = max_len;
+ }
+
+ if (log_len) {
+ char *buffer = vzalloc(log_len);
+
+ if (!buffer)
+ goto payload_fini;
+
+ ret = rm_queue_copy_response(cmd, buffer, log_len);
+ if (ret) {
+ vfree(buffer);
+ goto payload_fini;
+ }
+
+ vdev_err(rdev->vdev, "%s", buffer);
+ vfree(buffer);
+
+ } else {
+ vdev_err(rdev->vdev, "firewall check ret%d", ret);
+ }
+
+ rdev->firewall_tripped = 1;
+ }
+
+payload_fini:
+ rm_queue_payload_fini(cmd);
+destroy_cmd:
+ rm_queue_destroy_cmd(cmd);
+
+ vdev_dbg(rdev->vdev, "check result: %d", ret);
+}
+
+static void rm_sched_health_check(struct timer_list *t)
+{
+ struct rm_device *rdev = to_rdev_health_timer(t);
+
+ if (rdev->firewall_tripped) {
+ vdev_err(rdev->vdev, "Firewall tripped, health check paused. Please reset card");
+ return;
+ }
+ /* Schedule a work in the general workqueue */
+ schedule_work(&rdev->health_monitor);
+ /* Periodic timer */
+ mod_timer(&rdev->health_timer, jiffies + RM_HEALTH_CHECK_TIMER);
+}
+
+static void rm_uninstall_health_monitor(struct rm_device *rdev)
+{
+ del_timer_sync(&rdev->health_timer);
+ cancel_work_sync(&rdev->health_monitor);
+}
+
+static void rm_install_health_monitor(struct rm_device *rdev)
+{
+ INIT_WORK(&rdev->health_monitor, &rm_check_health);
+ timer_setup(&rdev->health_timer, &rm_sched_health_check, 0);
+ mod_timer(&rdev->health_timer, jiffies + RM_HEALTH_CHECK_TIMER);
+}
+
+void versal_pci_rm_fini(struct rm_device *rdev)
+{
+ rm_uninstall_health_monitor(rdev);
+ rm_queue_fini(rdev);
+}
+
+struct rm_device *versal_pci_rm_init(struct versal_pci_device *vdev)
+{
+ struct rm_header *header;
+ struct rm_device *rdev;
+ u32 status;
+ int ret;
+
+ rdev = devm_kzalloc(&vdev->pdev->dev, sizeof(*rdev), GFP_KERNEL);
+ if (!rdev)
+ return ERR_PTR(-ENOMEM);
+
+ rdev->vdev = vdev;
+ header = &rdev->rm_metadata;
+
+ rm_shmem_bulk_read(rdev, RM_HDR_OFF, (u32 *)header, sizeof(*header));
+ if (header->magic != RM_HDR_MAGIC_NUM) {
+ vdev_err(vdev, "Invalid RM header 0x%x", header->magic);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ status = rm_shmem_read(rdev, header->status_off);
+ if (!status) {
+ vdev_err(vdev, "RM status %d is not ready", status);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ rdev->queue_buffer_size = header->data_end - header->data_start + 1;
+ rdev->queue_buffer_start = header->data_start;
+ rdev->queue_base = header->queue_base;
+
+ ret = rm_queue_init(rdev);
+ if (ret) {
+ vdev_err(vdev, "Failed to init cmd queue, ret %d", ret);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = rm_queue_verify(rdev);
+ if (ret) {
+ vdev_err(vdev, "Failed to verify cmd queue, ret %d", ret);
+ ret = -ENODEV;
+ goto queue_fini;
+ }
+
+ ret = rm_queue_boot_apu(rdev);
+ if (ret) {
+ vdev_err(vdev, "Failed to bringup APU, ret %d", ret);
+ ret = -ENODEV;
+ goto queue_fini;
+ }
+
+ rm_install_health_monitor(rdev);
+
+ return rdev;
+queue_fini:
+ rm_queue_fini(rdev);
+err:
+ return ERR_PTR(ret);
+}
+
+int rm_queue_get_fw_id(struct rm_device *rdev)
+{
+ struct rm_cmd *cmd;
+ int ret;
+
+ ret = rm_queue_create_cmd(rdev, RM_QUEUE_OP_GET_LOG_PAGE, &cmd);
+ if (ret)
+ return ret;
+
+ ret = rm_queue_payload_init(cmd, RM_CMD_LOG_PAGE_FW_ID);
+ if (ret)
+ goto destroy_cmd;
+
+ ret = rm_queue_send_cmd(cmd, RM_CMD_WAIT_CONFIG_TIMEOUT);
+ if (ret)
+ goto payload_fini;
+
+ ret = rm_queue_copy_response(cmd, rdev->vdev->fw_id, sizeof(rdev->vdev->fw_id));
+ if (ret)
+ goto payload_fini;
+
+ vdev_info(rdev->vdev, "fw_id %s", rdev->vdev->fw_id);
+
+payload_fini:
+ rm_queue_payload_fini(cmd);
+destroy_cmd:
+ rm_queue_destroy_cmd(cmd);
+
+ return ret;
+}
diff --git a/drivers/misc/amd/versal-pci-rm-service.h b/drivers/misc/amd/versal-pci-rm-service.h
index d2397a1a672c..57333bb1443c 100644
--- a/drivers/misc/amd/versal-pci-rm-service.h
+++ b/drivers/misc/amd/versal-pci-rm-service.h
@@ -206,4 +206,24 @@ struct rm_device {
__u32 firewall_tripped;
};
+/* rm service init api */
+struct rm_device *versal_pci_rm_init(struct versal_pci_device *vdev);
+void versal_pci_rm_fini(struct rm_device *rdev);
+
+/* rm services APIs */
+int rm_queue_create_cmd(struct rm_device *rdev, enum rm_queue_opcode opcode,
+ struct rm_cmd **cmd_ptr);
+void rm_queue_destroy_cmd(struct rm_cmd *cmd);
+
+int rm_queue_data_init(struct rm_cmd *cmd, const char *buffer, ssize_t size);
+void rm_queue_data_fini(struct rm_cmd *cmd);
+int rm_queue_get_fw_id(struct rm_device *rdev);
+int rm_queue_boot_apu(struct rm_device *rdev);
+
+/* rm bar register operation APIs */
+u32 rm_reg_read(struct rm_device *rdev, u32 offset);
+void rm_reg_write(struct rm_device *rdev, u32 offset, const u32 value);
+void rm_bulk_reg_read(struct rm_device *rdev, u32 offset, u32 *value, size_t size);
+void rm_bulk_reg_write(struct rm_device *rdev, u32 offset, const void *value, size_t size);
+
#endif /* __RM_SERVICE_H */
diff --git a/drivers/misc/amd/versal-pci.h b/drivers/misc/amd/versal-pci.h
index 33f0ef881a33..89f3590137ce 100644
--- a/drivers/misc/amd/versal-pci.h
+++ b/drivers/misc/amd/versal-pci.h
@@ -51,6 +51,7 @@ struct fw_info {
struct versal_pci_device {
struct pci_dev *pdev;
+ struct rm_device *rdev;
struct fw_info fw;
void __iomem *io_regs;
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH V0 0/3] Add versal-pci driver
2025-03-03 4:43 [PATCH V0 0/3] Add versal-pci driver Yidong Zhang
` (2 preceding siblings ...)
2025-03-03 4:43 ` [PATCH V0 3/3] drivers/misc/amd: Add load base shell firmware Yidong Zhang
@ 2025-04-15 14:11 ` Greg KH
2025-04-17 0:09 ` Yidong Zhang
3 siblings, 1 reply; 6+ messages in thread
From: Greg KH @ 2025-04-15 14:11 UTC (permalink / raw)
To: Yidong Zhang; +Cc: arnd, linux-kernel, lizhi.hou, christophe.jaillet
On Sun, Mar 02, 2025 at 08:43:09PM -0800, Yidong Zhang wrote:
> This patchset introduces a new Linux Kernel Driver, versal-pci for AMD
> Alevo Versal based PCIe Card.
>
> The AMD Alevo Versal based PCIe Card, including V70 and RAVE, is the first
> Alevo production card leveraging AMD XDNA architecture with AI Engines. It
> is designed for AI inference efficiency and is tuned for video analytics
> and natural language processing applications [1].
accelerator drivers should be under drivers/accel/, please work with the
maintainers there to get it reviewed properly in that location.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH V0 0/3] Add versal-pci driver
2025-04-15 14:11 ` [PATCH V0 0/3] Add versal-pci driver Greg KH
@ 2025-04-17 0:09 ` Yidong Zhang
0 siblings, 0 replies; 6+ messages in thread
From: Yidong Zhang @ 2025-04-17 0:09 UTC (permalink / raw)
To: Greg KH; +Cc: arnd, linux-kernel, lizhi.hou, christophe.jaillet
On 4/15/25 07:11, Greg KH wrote:
> Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding.
>
>
> On Sun, Mar 02, 2025 at 08:43:09PM -0800, Yidong Zhang wrote:
>> This patchset introduces a new Linux Kernel Driver, versal-pci for AMD
>> Alevo Versal based PCIe Card.
>>
>> The AMD Alevo Versal based PCIe Card, including V70 and RAVE, is the first
>> Alevo production card leveraging AMD XDNA architecture with AI Engines. It
>> is designed for AI inference efficiency and is tuned for video analytics
>> and natural language processing applications [1].
>
> accelerator drivers should be under drivers/accel/, please work with the
> maintainers there to get it reviewed properly in that location.
Hi Greg,
We will work with drivers/accel community and send updated patches to
them for further review.
Thank you so much for taking your time guiding us the right direction.
Thanks,
David
>
> thanks,
>
> greg k-h
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-04-17 0:09 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-03 4:43 [PATCH V0 0/3] Add versal-pci driver Yidong Zhang
2025-03-03 4:43 ` [PATCH V0 1/3] drivers/misc/amd: Add new driver amd versal-pci Yidong Zhang
2025-03-03 4:43 ` [PATCH V0 2/3] drivers/misc/amd: Add remote queue service Yidong Zhang
2025-03-03 4:43 ` [PATCH V0 3/3] drivers/misc/amd: Add load base shell firmware Yidong Zhang
2025-04-15 14:11 ` [PATCH V0 0/3] Add versal-pci driver Greg KH
2025-04-17 0:09 ` Yidong Zhang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox