@@ -1194,6 +1194,12 @@ 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>
+L: linux-fpga@vger.kernel.org
+S: Supported
+F: drivers/fpga/amd/
+
AMD XGBE DRIVER
M: "Shyam Sundar S K" <Shyam-sundar.S-k@amd.com>
L: netdev@vger.kernel.org
@@ -290,4 +290,7 @@ config FPGA_MGR_LATTICE_SYSCONFIG_SPI
source "drivers/fpga/tests/Kconfig"
+# Driver files
+source "drivers/fpga/amd/Kconfig"
+
endif # FPGA
@@ -58,5 +58,8 @@ obj-$(CONFIG_FPGA_DFL_NIOS_INTEL_PAC_N3000) += dfl-n3000-nios.o
# Drivers for FPGAs which implement DFL
obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o
+# AMD PCIe Versal Management Driver
+obj-$(CONFIG_AMD_VERSAL_PCI) += amd/
+
# KUnit tests
obj-$(CONFIG_FPGA_KUNIT_TESTS) += tests/
new file mode 100644
@@ -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 FW_UPLOAD
+ depends on FPGA
+ depends on HAS_IOMEM
+ depends on PCI
+ help
+ AMD Versal PCIe Management Driver provides management services to
+ download firmware, program bitstream, and communicate with the User
+ function.
+
+ If "M" is selected, the driver module will be versal-pci
new file mode 100644
@@ -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
new file mode 100644
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Versal PCIe device
+ *
+ * Copyright (C) 2024 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 VERSAL_XCLBIN_MAGIC_ID "xclbin2"
+
+static int versal_pci_fpga_write_init(struct fpga_manager *mgr, struct fpga_image_info *info,
+ const char *buf, size_t count)
+{
+ /* TODO */
+ return 0;
+}
+
+static int versal_pci_fpga_write(struct fpga_manager *mgr, const char *buf,
+ size_t count)
+{
+ /* TODO */
+ return 0;
+}
+
+static int versal_pci_fpga_write_complete(struct fpga_manager *mgr,
+ struct fpga_image_info *info)
+{
+ /* TODO */
+ return 0;
+}
+
+static enum fpga_mgr_states versal_pci_fpga_state(struct fpga_manager *mgr)
+{
+ struct fpga_device *fdev = mgr->priv;
+
+ return fdev->state;
+}
+
+static const struct fpga_manager_ops versal_pci_fpga_ops = {
+ .write_init = versal_pci_fpga_write_init,
+ .write = versal_pci_fpga_write,
+ .write_complete = versal_pci_fpga_write_complete,
+ .state = versal_pci_fpga_state,
+};
+
+static void versal_pci_fpga_fini(struct fpga_device *fdev)
+{
+ fpga_mgr_unregister(fdev->mgr);
+}
+
+static void versal_pci_uuid_parse(struct versal_pci_device *vdev, uuid_t *uuid)
+{
+ char str[UUID_STRING_LEN];
+ u8 i, j;
+
+ /* parse uuid into a valid uuid string format */
+ for (i = 0, j = 0; i < strlen(vdev->fw_id) && 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 struct fpga_device *versal_pci_fpga_init(struct versal_pci_device *vdev)
+{
+ struct device *dev = &vdev->pdev->dev;
+ struct fpga_manager_info info = { 0 };
+ struct fpga_device *fdev;
+ int ret;
+
+ fdev = devm_kzalloc(dev, sizeof(*fdev), GFP_KERNEL);
+ if (!fdev)
+ return ERR_PTR(-ENOMEM);
+
+ fdev->vdev = vdev;
+
+ info = (struct fpga_manager_info) {
+ .name = "AMD Versal FPGA Manager",
+ .mops = &versal_pci_fpga_ops,
+ .priv = fdev,
+ };
+
+ fdev->mgr = fpga_mgr_register_full(dev, &info);
+ if (IS_ERR(fdev->mgr)) {
+ ret = PTR_ERR(fdev->mgr);
+ vdev_err(vdev, "Failed to register FPGA manager, err %d", ret);
+ return ERR_PTR(ret);
+ }
+
+ /* Place holder for rm_queue_get_fw_id(vdev->rdev) */
+ versal_pci_uuid_parse(vdev, &vdev->intf_uuid);
+
+ return fdev;
+}
+
+static int versal_pci_program_axlf(struct versal_pci_device *vdev, char *data, size_t size)
+{
+ const struct axlf *axlf = (struct axlf *)data;
+ struct fpga_image_info *image_info;
+ int ret;
+
+ image_info = fpga_image_info_alloc(&vdev->pdev->dev);
+ if (!image_info)
+ return -ENOMEM;
+
+ image_info->count = axlf->header.length;
+ image_info->buf = (char *)axlf;
+
+ ret = fpga_mgr_load(vdev->fdev->mgr, image_info);
+ if (ret) {
+ vdev_err(vdev, "failed to load xclbin: %d", ret);
+ goto exit;
+ }
+
+ vdev_info(vdev, "Downloaded axlf %pUb of size %zu Bytes", &axlf->header.uuid, size);
+ uuid_copy(&vdev->xclbin_uuid, &axlf->header.uuid);
+
+exit:
+ fpga_image_info_free(image_info);
+
+ return ret;
+}
+
+int versal_pci_load_xclbin(struct versal_pci_device *vdev, uuid_t *xuuid)
+{
+ const char *xclbin_location = "xilinx/xclbins";
+ char fw_name[100];
+ const struct firmware *fw;
+ int ret;
+
+ snprintf(fw_name, sizeof(fw_name), "%s/%pUb_%s.xclbin",
+ xclbin_location, xuuid, vdev->fw_id);
+
+ vdev_info(vdev, "trying to load %s", fw_name);
+ ret = request_firmware(&fw, fw_name, &vdev->pdev->dev);
+ if (ret) {
+ vdev_warn(vdev, "request xclbin fw %s failed %d", fw_name, ret);
+ return ret;
+ }
+ vdev_info(vdev, "loaded data size %zu", fw->size);
+
+ ret = versal_pci_program_axlf(vdev, (char *)fw->data, fw->size);
+ if (ret)
+ vdev_err(vdev, "program axlf %s failed %d", fw_name, ret);
+
+ release_firmware(fw);
+
+ return ret;
+}
+
+static enum fw_upload_err versal_pci_fw_prepare(struct fw_upload *fw_upload, const u8 *data,
+ u32 size)
+{
+ /* TODO */
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static enum fw_upload_err versal_pci_fw_write(struct fw_upload *fw_upload, const u8 *data,
+ u32 offset, u32 size, u32 *written)
+{
+ /* TODO */
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static enum fw_upload_err versal_pci_fw_poll_complete(struct fw_upload *fw_upload)
+{
+ /* TODO */
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static void versal_pci_fw_cancel(struct fw_upload *fw_upload)
+{
+ /* TODO */
+}
+
+static void versal_pci_fw_cleanup(struct fw_upload *fw_upload)
+{
+ /* TODO */
+}
+
+static const struct fw_upload_ops versal_pci_fw_ops = {
+ .prepare = versal_pci_fw_prepare,
+ .write = versal_pci_fw_write,
+ .poll_complete = versal_pci_fw_poll_complete,
+ .cancel = versal_pci_fw_cancel,
+ .cleanup = versal_pci_fw_cleanup,
+};
+
+static void versal_pci_fw_upload_fini(struct firmware_device *fwdev)
+{
+ firmware_upload_unregister(fwdev->fw);
+ kfree(fwdev->name);
+}
+
+static 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 struct firmware_device *versal_pci_fw_upload_init(struct versal_pci_device *vdev)
+{
+ struct device *dev = &vdev->pdev->dev;
+ struct firmware_device *fwdev;
+ u32 devid;
+
+ fwdev = devm_kzalloc(dev, sizeof(*fwdev), GFP_KERNEL);
+ if (!fwdev)
+ return ERR_PTR(-ENOMEM);
+
+ devid = versal_pci_devid(vdev);
+ fwdev->name = kasprintf(GFP_KERNEL, "%s%x", DRV_NAME, devid);
+ if (!fwdev->name)
+ return ERR_PTR(-ENOMEM);
+
+ fwdev->fw = firmware_upload_register(THIS_MODULE, dev, fwdev->name,
+ &versal_pci_fw_ops, fwdev);
+ if (IS_ERR(fwdev->fw)) {
+ kfree(fwdev->name);
+ return ERR_CAST(fwdev->fw);
+ }
+
+ fwdev->vdev = vdev;
+
+ return fwdev;
+}
+
+static void versal_pci_device_teardown(struct versal_pci_device *vdev)
+{
+ versal_pci_fpga_fini(vdev->fdev);
+ versal_pci_fw_upload_fini(vdev->fwdev);
+}
+
+static int versal_pci_device_setup(struct versal_pci_device *vdev)
+{
+ int ret;
+
+ vdev->fwdev = versal_pci_fw_upload_init(vdev);
+ if (IS_ERR(vdev->fwdev)) {
+ ret = PTR_ERR(vdev->fwdev);
+ vdev_err(vdev, "Failed to init FW uploader, err %d", ret);
+ return ret;
+ }
+
+ vdev->fdev = versal_pci_fpga_init(vdev);
+ if (IS_ERR(vdev->fdev)) {
+ ret = PTR_ERR(vdev->fdev);
+ vdev_err(vdev, "Failed to init FPGA manager, err %d", ret);
+ goto upload_fini;
+ }
+
+ return 0;
+
+upload_fini:
+ versal_pci_fw_upload_fini(vdev->fwdev);
+
+ 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;
+ }
+
+ vdev_dbg(vdev, "Successfully probed %s driver!", DRV_NAME);
+ return 0;
+}
+
+static const struct pci_device_id versal_pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_V70PQ2), },
+ { 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");
new file mode 100644
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Driver for Versal PCIe device
+ *
+ * Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#ifndef __VERSAL_PCI_H
+#define __VERSAL_PCI_H
+
+#include <linux/firmware.h>
+#include <linux/fpga/fpga-mgr.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_tnx {
+ struct rm_cmd *cmd;
+ __u32 opcode;
+ __u32 id;
+};
+
+struct fpga_device {
+ enum fpga_mgr_states state;
+ struct fpga_manager *mgr;
+ struct versal_pci_device *vdev;
+ struct fw_tnx fw;
+};
+
+struct firmware_device {
+ struct versal_pci_device *vdev;
+ struct fw_upload *fw;
+ __u8 *name;
+ __u32 fw_name_id;
+ struct rm_cmd *cmd;
+ __u32 id;
+ uuid_t uuid;
+};
+
+struct versal_pci_device {
+ struct pci_dev *pdev;
+
+ struct fpga_device *fdev;
+ struct firmware_device *fwdev;
+ struct device *device;
+
+ void __iomem *io_regs;
+ uuid_t xclbin_uuid;
+ uuid_t intf_uuid;
+ __u8 fw_id[UUID_STRING_LEN + 1];
+
+ __u8 *debugfs_root;
+};
+
+/* versal pci driver APIs */
+int versal_pci_load_xclbin(struct versal_pci_device *vdev, uuid_t *xclbin_uuid);
+
+#endif /* __VERSAL_PCI_H */