AMD Versal based PCIe card, including V70, is designed for AI inference efficiency and is tuned for video analytics and natural language processing applications. The driver architecture: +---------+ Communication +---------+ Remote +-----+------+ | | Channel | | Queue | | | | User PF | <============> | Mgmt PF | <=======>| FW | FPGA | +---------+ +---------+ +-----+------+ PL Data base FW APU FW PL Data (copy) - PL (FPGA Program Logic) - FW (Firmware) There are 2 separate drivers from the original XRT[1] design. - UserPF driver - MgmtPF driver The new AMD versal-pci driver will replace the MgmtPF driver for Versal PCIe card. The XRT[1] is already open-sourced. It includes solution of runtime for many different type of PCIe Based cards. It also provides utilities for managing and programming the devices. The AMD versal-pci stands for AMD Versal brand PCIe device management driver. This driver provides the following functionalities: - module and PCI device initialization this driver will attach to specific device id of V70 card; the driver will initialize itself based on bar resources for - communication channel: a hardware message service between mgmt PF and user PF - remote queue: a hardware queue based ring buffer service between mgmt PF and PCIe hardware firmware for programming FPGA Program Logic, loading firmware and checking card healthy status. - 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 MgmtPF driver will not change any existing hardware. - programming FPGA hardware binaries - PL Data - using fpga framework ops to support re-programing FPGA - the re-programming request will be initiated from the existing UserPF driver only, and the MgmtPF driver load the matched PL Data after receiving request from the communication channel. The matching PL Data is indexed by the PL Data UUID and Base FW UUID. - The Base FW UUID identifies unique based hardware. Often called the interface UUID. - The PL Data UUID identifies unique PL design that is generated based on the base hardware. Often called xclbin UUID. - Example: 4fdebe35[...trimmed...]_96df7d[...trimmed...].xclbin | | | | +-- xclbin UUID --+ +--interface UUID --+ [1] https://github.com/Xilinx/XRT/blob/master/README.rst Co-developed-by: DMG Karthik <Karthik.DMG@xxxxxxx> Signed-off-by: DMG Karthik <Karthik.DMG@xxxxxxx> Co-developed-by: Nishad Saraf <nishads@xxxxxxx> Signed-off-by: Nishad Saraf <nishads@xxxxxxx> Co-developed-by: Prapul Krishnamurthy <prapulk@xxxxxxx> Signed-off-by: Prapul Krishnamurthy <prapulk@xxxxxxx> Co-developed-by: Hayden Laccabue <hayden.laccabue@xxxxxxx> Signed-off-by: Hayden Laccabue <hayden.laccabue@xxxxxxx> Signed-off-by: Yidong Zhang <yidong.zhang@xxxxxxx> --- MAINTAINERS | 6 + drivers/fpga/Kconfig | 3 + drivers/fpga/Makefile | 3 + drivers/fpga/amd/Kconfig | 15 ++ drivers/fpga/amd/Makefile | 5 + drivers/fpga/amd/versal-pci-main.c | 328 +++++++++++++++++++++++++++++ drivers/fpga/amd/versal-pci.h | 86 ++++++++ 7 files changed, 446 insertions(+) create mode 100644 drivers/fpga/amd/Kconfig create mode 100644 drivers/fpga/amd/Makefile create mode 100644 drivers/fpga/amd/versal-pci-main.c create mode 100644 drivers/fpga/amd/versal-pci.h diff --git a/MAINTAINERS b/MAINTAINERS index 17daa9ee9384..302c10004c5d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1194,6 +1194,12 @@ L: linux-spi@xxxxxxxxxxxxxxx S: Supported F: drivers/spi/spi-amd.c +AMD VERSAL PCI DRIVER +M: Yidong Zhang <yidong.zhang@xxxxxxx> +L: linux-fpga@xxxxxxxxxxxxxxx +S: Supported +F: drivers/fpga/amd/ + AMD XGBE DRIVER M: "Shyam Sundar S K" <Shyam-sundar.S-k@xxxxxxx> L: netdev@xxxxxxxxxxxxxxx diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index 37b35f58f0df..dce060a7bd8f 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -290,4 +290,7 @@ config FPGA_MGR_LATTICE_SYSCONFIG_SPI source "drivers/fpga/tests/Kconfig" +# Driver files +source "drivers/fpga/amd/Kconfig" + endif # FPGA diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index aeb89bb13517..8412f3e211cc 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -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/ diff --git a/drivers/fpga/amd/Kconfig b/drivers/fpga/amd/Kconfig new file mode 100644 index 000000000000..b18a42a340ba --- /dev/null +++ b/drivers/fpga/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 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 diff --git a/drivers/fpga/amd/Makefile b/drivers/fpga/amd/Makefile new file mode 100644 index 000000000000..5d1ef04b5e80 --- /dev/null +++ b/drivers/fpga/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/fpga/amd/versal-pci-main.c b/drivers/fpga/amd/versal-pci-main.c new file mode 100644 index 000000000000..a10ccf86802b --- /dev/null +++ b/drivers/fpga/amd/versal-pci-main.c @@ -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@xxxxxxx>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/fpga/amd/versal-pci.h b/drivers/fpga/amd/versal-pci.h new file mode 100644 index 000000000000..1509bd0532ea --- /dev/null +++ b/drivers/fpga/amd/versal-pci.h @@ -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 */ -- 2.34.1