Hello,
This is V9 of patch series which adds management physical function driver
for Xilinx Alveo PCIe accelerator cards.
https://www.xilinx.com/products/boards-and-kits/alveo.html
This driver is part of Xilinx Runtime (XRT) open source stack.
XILINX ALVEO PLATFORM ARCHITECTURE
Alveo PCIe FPGA based platforms have a static *shell* partition and a
partial re-configurable *user* partition. The shell partition is
automatically loaded from flash when host is booted and PCIe is enumerated
by BIOS. Shell cannot be changed till the next cold reboot. The shell
exposes two PCIe physical functions:
1. management physical function
2. user physical function
The patch series includes Documentation/xrt.rst which describes Alveo
platform, XRT driver architecture and deployment model in more detail.
Users compile their high level design in C/C++/OpenCL or RTL into FPGA
image using Vitis tools.
https://www.xilinx.com/products/design-tools/vitis/vitis-platform.html
The compiled image is packaged as xclbin which contains partial bitstream
for the user partition and necessary metadata. Users can dynamically swap
the image running on the user partition in order to switch between
different workloads by loading different xclbins.
XRT DRIVERS FOR XILINX ALVEO
XRT Linux kernel driver *xrt-mgmt* binds to management physical function of
Alveo platform. The modular driver framework is organized into several
platform drivers which primarily handle the following functionality:
1. Loading firmware container also called xsabin at driver attach time
2. Loading of user compiled xclbin with FPGA Manager integration
3. Clock scaling of image running on user partition
4. In-band sensors: temp, voltage, power, etc.
5. Device reset and rescan
The platform drivers are packaged into *xrt-lib* helper module with well
defined interfaces. The module provides a pseudo-bus implementation for the
platform drivers. More details on the driver model can be found in
Documentation/xrt.rst.
User physical function driver is not included in this patch series.
LIBFDT REQUIREMENT
XRT driver infrastructure uses Device Tree as a metadata format to discover
HW subsystems in the Alveo PCIe device. The Device Tree schema used by XRT
is documented in Documentation/xrt.rst.
TESTING AND VALIDATION
xrt-mgmt driver can be tested with full XRT open source stack which
includes user space libraries, board utilities and (out of tree) first
generation user physical function driver xocl. XRT open source runtime
stack is available at https://github.com/Xilinx/XRT
Complete documentation for XRT open source stack including sections on
Alveo/XRT security and platform architecture can be found here:
https://xilinx.github.io/XRT/master/html/index.html
https://xilinx.github.io/XRT/master/html/security.html
https://xilinx.github.io/XRT/master/html/platforms_partitions.html
Changes since v8:
- added comments for struct xrt_device, struct xrt_driver.
- removed module version
Changes since v7:
- Followed review comment to remove non fpga subdevices:
clock, clkfrq, ucs, ddr_calibaration, devctl and vsec
- Collapsed include/uapi/linux/xrt/*.h into include/uapi/linux/fpga-xrt.h
- Cleaned up comments in fpga-xrt.h
- Fixed spelling errors in xrt.rst
Changes since v6:
- Resolved grammatical errors and cleaned up taxonomy in xrt.rst
documentation.
- Fixed clang warnings.
- Updated code base to include v6 code review comments.
Changes since v5:
- Revert all changes 'mgnt/MGNT' back to 'mgmt/MGMT'
- Updated code base to include v5 code review comments.
xrt.rst: address grammar and taxonomy
subdev_id.h: defines XRT_SUBDEV_INVALID = 0
xclbin.c: change shift operation to be_to_cpu
- Resolved kernel test robot errors.
Changes since v4:
- Added xrt_bus_type and xrt_device. All sub devices were changed from
platform_bus_type/platform_device to xrt_bus_type/xrt_device.
- Renamed xrt-mgmt driver to xrt-mgnt driver.
- Replaced 'MGMT' with 'MGNT' and 'mgmt' with 'mgnt' in code and file names
- Moved pci function calls from infrastructure to xrt-mgnt driver.
- Renamed files: mgmt/main.c -> mgnt/xmgnt-main.c
mgmt/main-region.c -> mgnt/xmgnt-main-region.c
include/xmgmt-main.h -> include/xmgnt-main.h
mgmt/fmgr-drv.c -> mgnt/xrt-mgr.c
mgmt/fmgr.h -> mgnt/xrt-mgr.h
- Updated code base to include v4 code review comments.
Changes since v3:
- Leaf drivers use regmap-mmio to access hardware registers.
- Renamed driver module: xmgmt.ko -> xrt-mgmt.ko
- Renamed files: calib.[c|h] -> ddr_calibration.[c|h],
lib/main.[c|h] -> lib/lib-drv.[c|h],
mgmt/main-impl.h - > mgmt/xmgnt.h
- Updated code base to include v3 code review comments.
Changes since v2:
- Streamlined the driver framework into *xleaf*, *group* and *xroot*
- Updated documentation to show the driver model with examples
- Addressed kernel test robot errors
- Added a selftest for basic driver framework
- Documented device tree schema
- Removed need to export libfdt symbols
Changes since v1:
- Updated the driver to use fpga_region and fpga_bridge for FPGA
programming
- Dropped platform drivers not related to PR programming to focus on XRT
core framework
- Updated Documentation/fpga/xrt.rst with information on XRT core framework
- Addressed checkpatch issues
- Dropped xrt- prefix from some header files
For reference V8 version of patch series can be found here:
https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/
Lizhi Hou (14):
Documentation: fpga: Add a document describing XRT Alveo drivers
fpga: xrt: driver metadata helper functions
fpga: xrt: xclbin file helper functions
fpga: xrt: xrt-lib driver manager
fpga: xrt: group driver
fpga: xrt: char dev node helper functions
fpga: xrt: root driver infrastructure
fpga: xrt: driver infrastructure
fpga: xrt: management physical function driver (root)
fpga: xrt: main driver for management function device
fpga: xrt: fpga-mgr and region implementation for xclbin download
fpga: xrt: ICAP driver
fpga: xrt: partition isolation driver
fpga: xrt: Kconfig and Makefile updates for XRT drivers
Documentation/fpga/index.rst | 1 +
Documentation/fpga/xrt.rst | 870 ++++++++++++++++++
MAINTAINERS | 11 +
drivers/Makefile | 1 +
drivers/fpga/Kconfig | 2 +
drivers/fpga/Makefile | 5 +
drivers/fpga/xrt/Kconfig | 8 +
drivers/fpga/xrt/include/events.h | 45 +
drivers/fpga/xrt/include/group.h | 25 +
drivers/fpga/xrt/include/metadata.h | 236 +++++
drivers/fpga/xrt/include/subdev_id.h | 39 +
drivers/fpga/xrt/include/xclbin-helper.h | 48 +
drivers/fpga/xrt/include/xdevice.h | 141 +++
drivers/fpga/xrt/include/xleaf.h | 205 +++++
drivers/fpga/xrt/include/xleaf/axigate.h | 23 +
drivers/fpga/xrt/include/xleaf/clkfreq.h | 21 +
drivers/fpga/xrt/include/xleaf/clock.h | 29 +
.../fpga/xrt/include/xleaf/ddr_calibration.h | 28 +
drivers/fpga/xrt/include/xleaf/devctl.h | 40 +
drivers/fpga/xrt/include/xleaf/icap.h | 27 +
drivers/fpga/xrt/include/xmgmt-main.h | 34 +
drivers/fpga/xrt/include/xroot.h | 117 +++
drivers/fpga/xrt/lib/Kconfig | 17 +
drivers/fpga/xrt/lib/Makefile | 24 +
drivers/fpga/xrt/lib/cdev.c | 209 +++++
drivers/fpga/xrt/lib/group.c | 278 ++++++
drivers/fpga/xrt/lib/lib-drv.c | 318 +++++++
drivers/fpga/xrt/lib/lib-drv.h | 21 +
drivers/fpga/xrt/lib/subdev.c | 859 +++++++++++++++++
drivers/fpga/xrt/lib/subdev_pool.h | 53 ++
drivers/fpga/xrt/lib/xclbin.c | 381 ++++++++
drivers/fpga/xrt/lib/xleaf/axigate.c | 325 +++++++
drivers/fpga/xrt/lib/xleaf/icap.c | 328 +++++++
drivers/fpga/xrt/lib/xroot.c | 536 +++++++++++
drivers/fpga/xrt/metadata/Kconfig | 12 +
drivers/fpga/xrt/metadata/Makefile | 16 +
drivers/fpga/xrt/metadata/metadata.c | 578 ++++++++++++
drivers/fpga/xrt/mgmt/Kconfig | 15 +
drivers/fpga/xrt/mgmt/Makefile | 19 +
drivers/fpga/xrt/mgmt/root.c | 418 +++++++++
drivers/fpga/xrt/mgmt/xmgmt-main-region.c | 483 ++++++++++
drivers/fpga/xrt/mgmt/xmgmt-main.c | 662 +++++++++++++
drivers/fpga/xrt/mgmt/xmgmt.h | 33 +
drivers/fpga/xrt/mgmt/xrt-mgr.c | 190 ++++
drivers/fpga/xrt/mgmt/xrt-mgr.h | 16 +
include/uapi/linux/fpga-xrt.h | 428 +++++++++
46 files changed, 8175 insertions(+)
create mode 100644 Documentation/fpga/xrt.rst
create mode 100644 drivers/fpga/xrt/Kconfig
create mode 100644 drivers/fpga/xrt/include/events.h
create mode 100644 drivers/fpga/xrt/include/group.h
create mode 100644 drivers/fpga/xrt/include/metadata.h
create mode 100644 drivers/fpga/xrt/include/subdev_id.h
create mode 100644 drivers/fpga/xrt/include/xclbin-helper.h
create mode 100644 drivers/fpga/xrt/include/xdevice.h
create mode 100644 drivers/fpga/xrt/include/xleaf.h
create mode 100644 drivers/fpga/xrt/include/xleaf/axigate.h
create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
create mode 100644 drivers/fpga/xrt/include/xleaf/ddr_calibration.h
create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
create mode 100644 drivers/fpga/xrt/include/xleaf/icap.h
create mode 100644 drivers/fpga/xrt/include/xmgmt-main.h
create mode 100644 drivers/fpga/xrt/include/xroot.h
create mode 100644 drivers/fpga/xrt/lib/Kconfig
create mode 100644 drivers/fpga/xrt/lib/Makefile
create mode 100644 drivers/fpga/xrt/lib/cdev.c
create mode 100644 drivers/fpga/xrt/lib/group.c
create mode 100644 drivers/fpga/xrt/lib/lib-drv.c
create mode 100644 drivers/fpga/xrt/lib/lib-drv.h
create mode 100644 drivers/fpga/xrt/lib/subdev.c
create mode 100644 drivers/fpga/xrt/lib/subdev_pool.h
create mode 100644 drivers/fpga/xrt/lib/xclbin.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/axigate.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/icap.c
create mode 100644 drivers/fpga/xrt/lib/xroot.c
create mode 100644 drivers/fpga/xrt/metadata/Kconfig
create mode 100644 drivers/fpga/xrt/metadata/Makefile
create mode 100644 drivers/fpga/xrt/metadata/metadata.c
create mode 100644 drivers/fpga/xrt/mgmt/Kconfig
create mode 100644 drivers/fpga/xrt/mgmt/Makefile
create mode 100644 drivers/fpga/xrt/mgmt/root.c
create mode 100644 drivers/fpga/xrt/mgmt/xmgmt-main-region.c
create mode 100644 drivers/fpga/xrt/mgmt/xmgmt-main.c
create mode 100644 drivers/fpga/xrt/mgmt/xmgmt.h
create mode 100644 drivers/fpga/xrt/mgmt/xrt-mgr.c
create mode 100644 drivers/fpga/xrt/mgmt/xrt-mgr.h
create mode 100644 include/uapi/linux/fpga-xrt.h
--
2.27.0
Helper functions for char device node creation / removal for xrt
drivers. This is part of xrt driver infrastructure.
Signed-off-by: Sonal Santan <[email protected]>
Signed-off-by: Max Zhen <[email protected]>
Signed-off-by: Lizhi Hou <[email protected]>
Reviewed-by: Tom Rix <[email protected]>
---
drivers/fpga/xrt/lib/cdev.c | 209 ++++++++++++++++++++++++++++++++++++
1 file changed, 209 insertions(+)
create mode 100644 drivers/fpga/xrt/lib/cdev.c
diff --git a/drivers/fpga/xrt/lib/cdev.c b/drivers/fpga/xrt/lib/cdev.c
new file mode 100644
index 000000000000..3c20adac8c03
--- /dev/null
+++ b/drivers/fpga/xrt/lib/cdev.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Alveo FPGA device node helper functions.
+ *
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Cheng Zhen <[email protected]>
+ */
+
+#include "xleaf.h"
+#include "lib-drv.h"
+
+#define XRT_CDEV_DIR "xrt"
+#define INODE2PDATA(inode) \
+ container_of((inode)->i_cdev, struct xrt_subdev_platdata, xsp_cdev)
+#define INODE2PDEV(inode) \
+ to_xrt_dev(kobj_to_dev((inode)->i_cdev->kobj.parent))
+#define CDEV_NAME(sysdev) (strchr((sysdev)->kobj.name, '!') + 1)
+
+/* Allow it to be accessed from cdev. */
+static void xleaf_devnode_allowed(struct xrt_device *xdev)
+{
+ struct xrt_subdev_platdata *pdata = DEV_PDATA(xdev);
+
+ /* Allow new opens. */
+ mutex_lock(&pdata->xsp_devnode_lock);
+ pdata->xsp_devnode_online = true;
+ mutex_unlock(&pdata->xsp_devnode_lock);
+}
+
+/* Turn off access from cdev and wait for all existing user to go away. */
+static void xleaf_devnode_disallowed(struct xrt_device *xdev)
+{
+ struct xrt_subdev_platdata *pdata = DEV_PDATA(xdev);
+
+ mutex_lock(&pdata->xsp_devnode_lock);
+
+ /* Prevent new opens. */
+ pdata->xsp_devnode_online = false;
+ /* Wait for existing user to close. */
+ while (pdata->xsp_devnode_ref) {
+ mutex_unlock(&pdata->xsp_devnode_lock);
+ wait_for_completion(&pdata->xsp_devnode_comp);
+ mutex_lock(&pdata->xsp_devnode_lock);
+ }
+
+ mutex_unlock(&pdata->xsp_devnode_lock);
+}
+
+static struct xrt_device *
+__xleaf_devnode_open(struct inode *inode, bool excl)
+{
+ struct xrt_subdev_platdata *pdata = INODE2PDATA(inode);
+ struct xrt_device *xdev = INODE2PDEV(inode);
+ bool opened = false;
+
+ mutex_lock(&pdata->xsp_devnode_lock);
+
+ if (pdata->xsp_devnode_online) {
+ if (excl && pdata->xsp_devnode_ref) {
+ xrt_err(xdev, "%s has already been opened exclusively",
+ CDEV_NAME(pdata->xsp_sysdev));
+ } else if (!excl && pdata->xsp_devnode_excl) {
+ xrt_err(xdev, "%s has been opened exclusively",
+ CDEV_NAME(pdata->xsp_sysdev));
+ } else {
+ pdata->xsp_devnode_ref++;
+ pdata->xsp_devnode_excl = excl;
+ opened = true;
+ xrt_info(xdev, "opened %s, ref=%d",
+ CDEV_NAME(pdata->xsp_sysdev),
+ pdata->xsp_devnode_ref);
+ }
+ } else {
+ xrt_err(xdev, "%s is offline", CDEV_NAME(pdata->xsp_sysdev));
+ }
+
+ mutex_unlock(&pdata->xsp_devnode_lock);
+
+ xdev = opened ? xdev : NULL;
+ return xdev;
+}
+
+struct xrt_device *
+xleaf_devnode_open_excl(struct inode *inode)
+{
+ return __xleaf_devnode_open(inode, true);
+}
+
+struct xrt_device *
+xleaf_devnode_open(struct inode *inode)
+{
+ return __xleaf_devnode_open(inode, false);
+}
+EXPORT_SYMBOL_GPL(xleaf_devnode_open);
+
+void xleaf_devnode_close(struct inode *inode)
+{
+ struct xrt_subdev_platdata *pdata = INODE2PDATA(inode);
+ struct xrt_device *xdev = INODE2PDEV(inode);
+ bool notify = false;
+
+ mutex_lock(&pdata->xsp_devnode_lock);
+
+ WARN_ON(pdata->xsp_devnode_ref == 0);
+ pdata->xsp_devnode_ref--;
+ if (pdata->xsp_devnode_ref == 0) {
+ pdata->xsp_devnode_excl = false;
+ notify = true;
+ }
+ if (notify)
+ xrt_info(xdev, "closed %s", CDEV_NAME(pdata->xsp_sysdev));
+ else
+ xrt_info(xdev, "closed %s, notifying waiter", CDEV_NAME(pdata->xsp_sysdev));
+
+ mutex_unlock(&pdata->xsp_devnode_lock);
+
+ if (notify)
+ complete(&pdata->xsp_devnode_comp);
+}
+EXPORT_SYMBOL_GPL(xleaf_devnode_close);
+
+static inline enum xrt_dev_file_mode
+devnode_mode(struct xrt_device *xdev)
+{
+ return DEV_FILE_OPS(xdev)->xsf_mode;
+}
+
+int xleaf_devnode_create(struct xrt_device *xdev, const char *file_name,
+ const char *inst_name)
+{
+ struct xrt_subdev_platdata *pdata = DEV_PDATA(xdev);
+ struct xrt_dev_file_ops *fops = DEV_FILE_OPS(xdev);
+ struct cdev *cdevp;
+ struct device *sysdev;
+ int ret = 0;
+ char fname[256];
+
+ mutex_init(&pdata->xsp_devnode_lock);
+ init_completion(&pdata->xsp_devnode_comp);
+
+ cdevp = &DEV_PDATA(xdev)->xsp_cdev;
+ cdev_init(cdevp, &fops->xsf_ops);
+ cdevp->owner = fops->xsf_ops.owner;
+ cdevp->dev = MKDEV(MAJOR(fops->xsf_dev_t), xdev->instance);
+
+ /*
+ * Set xdev as parent of cdev so that when xdev (and its platform
+ * data) will not be freed when cdev is not freed.
+ */
+ cdev_set_parent(cdevp, &DEV(xdev)->kobj);
+
+ ret = cdev_add(cdevp, cdevp->dev, 1);
+ if (ret) {
+ xrt_err(xdev, "failed to add cdev: %d", ret);
+ goto failed;
+ }
+ if (!file_name)
+ file_name = xdev->name;
+ if (!inst_name) {
+ if (devnode_mode(xdev) == XRT_DEV_FILE_MULTI_INST) {
+ snprintf(fname, sizeof(fname), "%s/%s/%s.%u",
+ XRT_CDEV_DIR, DEV_PDATA(xdev)->xsp_root_name,
+ file_name, xdev->instance);
+ } else {
+ snprintf(fname, sizeof(fname), "%s/%s/%s",
+ XRT_CDEV_DIR, DEV_PDATA(xdev)->xsp_root_name,
+ file_name);
+ }
+ } else {
+ snprintf(fname, sizeof(fname), "%s/%s/%s.%s", XRT_CDEV_DIR,
+ DEV_PDATA(xdev)->xsp_root_name, file_name, inst_name);
+ }
+ sysdev = device_create(xrt_class, NULL, cdevp->dev, NULL, "%s", fname);
+ if (IS_ERR(sysdev)) {
+ ret = PTR_ERR(sysdev);
+ xrt_err(xdev, "failed to create device node: %d", ret);
+ goto failed_cdev_add;
+ }
+ pdata->xsp_sysdev = sysdev;
+
+ xleaf_devnode_allowed(xdev);
+
+ xrt_info(xdev, "created (%d, %d): /dev/%s",
+ MAJOR(cdevp->dev), xdev->instance, fname);
+ return 0;
+
+failed_cdev_add:
+ cdev_del(cdevp);
+failed:
+ cdevp->owner = NULL;
+ return ret;
+}
+
+void xleaf_devnode_destroy(struct xrt_device *xdev)
+{
+ struct xrt_subdev_platdata *pdata = DEV_PDATA(xdev);
+ struct cdev *cdevp = &pdata->xsp_cdev;
+ dev_t dev = cdevp->dev;
+
+ xleaf_devnode_disallowed(xdev);
+
+ xrt_info(xdev, "removed (%d, %d): /dev/%s/%s", MAJOR(dev), MINOR(dev),
+ XRT_CDEV_DIR, CDEV_NAME(pdata->xsp_sysdev));
+ device_destroy(xrt_class, cdevp->dev);
+ pdata->xsp_sysdev = NULL;
+ cdev_del(cdevp);
+}
--
2.27.0
Alveo FPGA firmware and partial reconfigure file are in xclbin format. This
code enumerates and extracts sections from xclbin files. xclbin.h is cross
platform and used across all platforms and OS.
Signed-off-by: Sonal Santan <[email protected]>
Signed-off-by: Max Zhen <[email protected]>
Signed-off-by: Lizhi Hou <[email protected]>
Reviewed-by: Tom Rix <[email protected]>
---
drivers/fpga/xrt/include/xclbin-helper.h | 48 +++
drivers/fpga/xrt/lib/xclbin.c | 381 ++++++++++++++++++++
include/uapi/linux/fpga-xrt.h | 428 +++++++++++++++++++++++
3 files changed, 857 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xclbin-helper.h
create mode 100644 drivers/fpga/xrt/lib/xclbin.c
create mode 100644 include/uapi/linux/fpga-xrt.h
diff --git a/drivers/fpga/xrt/include/xclbin-helper.h b/drivers/fpga/xrt/include/xclbin-helper.h
new file mode 100644
index 000000000000..518d55f74c25
--- /dev/null
+++ b/drivers/fpga/xrt/include/xclbin-helper.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * David Zhang <[email protected]>
+ * Sonal Santan <[email protected]>
+ */
+
+#ifndef _XCLBIN_HELPER_H_
+#define _XCLBIN_HELPER_H_
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/fpga-xrt.h>
+
+#define XCLBIN_VERSION2 "xclbin2"
+#define XCLBIN_HWICAP_BITFILE_BUF_SZ 1024
+#define XCLBIN_MAX_SZ_1G (1024 * 1024 * 1024) /* Assuming xclbin <= 1G, always */
+
+enum axlf_section_kind;
+struct axlf;
+
+/**
+ * Bitstream header information as defined by Xilinx tools.
+ * Please note that this struct definition is not owned by the driver.
+ */
+struct xclbin_bit_head_info {
+ u32 header_length; /* Length of header in 32 bit words */
+ u32 bitstream_length; /* Length of bitstream to read in bytes */
+ const unchar *design_name; /* Design name get from bitstream */
+ const unchar *part_name; /* Part name read from bitstream */
+ const unchar *date; /* Date read from bitstream header */
+ const unchar *time; /* Bitstream creation time */
+ u32 magic_length; /* Length of the magic numbers */
+ const unchar *version; /* Version string */
+};
+
+/* caller must free the allocated memory for **data. len could be NULL. */
+int xrt_xclbin_get_section(struct device *dev, const struct axlf *xclbin,
+ enum axlf_section_kind kind, void **data,
+ uint64_t *len);
+int xrt_xclbin_get_metadata(struct device *dev, const struct axlf *xclbin, char **dtb);
+int xrt_xclbin_parse_bitstream_header(struct device *dev, const unchar *data,
+ u32 size, struct xclbin_bit_head_info *head_info);
+const char *xrt_clock_type2epname(enum XCLBIN_CLOCK_TYPE type);
+
+#endif /* _XCLBIN_HELPER_H_ */
diff --git a/drivers/fpga/xrt/lib/xclbin.c b/drivers/fpga/xrt/lib/xclbin.c
new file mode 100644
index 000000000000..6edac3d418be
--- /dev/null
+++ b/drivers/fpga/xrt/lib/xclbin.c
@@ -0,0 +1,381 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Alveo FPGA Driver XCLBIN parser
+ *
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors: David Zhang <[email protected]>
+ */
+
+#include <asm/errno.h>
+#include <linux/vmalloc.h>
+#include <linux/device.h>
+#include "xclbin-helper.h"
+#include "metadata.h"
+
+/* Used for parsing bitstream header */
+#define BITSTREAM_EVEN_MAGIC_BYTE 0x0f
+#define BITSTREAM_ODD_MAGIC_BYTE 0xf0
+
+static inline u16 bitstream_read16(const char *data, u32 *offset)
+{
+ u16 val;
+
+ val = be16_to_cpu(*(__be16 *)(data + *offset));
+ *offset += sizeof(__be16);
+
+ return val;
+}
+
+static inline u32 bitstream_read32(const char *data, u32 *offset)
+{
+ u32 val;
+
+ val = be32_to_cpu(*(__be32 *)(data + *offset));
+ *offset += sizeof(__be32);
+
+ return val;
+}
+
+static int xrt_xclbin_get_section_hdr(const struct axlf *xclbin,
+ enum axlf_section_kind kind,
+ const struct axlf_section_header **header)
+{
+ const struct axlf_section_header *phead = NULL;
+ u64 xclbin_len;
+ int i;
+
+ *header = NULL;
+ for (i = 0; i < xclbin->header.num_sections; i++) {
+ if (xclbin->sections[i].section_kind == kind) {
+ phead = &xclbin->sections[i];
+ break;
+ }
+ }
+
+ if (!phead)
+ return -ENOENT;
+
+ xclbin_len = xclbin->header.length;
+ if (xclbin_len > XCLBIN_MAX_SZ_1G || !phead->section_size ||
+ phead->section_offset + phead->section_size > xclbin_len)
+ return -EINVAL;
+
+ *header = phead;
+ return 0;
+}
+
+static int xrt_xclbin_section_info(const struct axlf *xclbin,
+ enum axlf_section_kind kind,
+ u64 *offset, u64 *size)
+{
+ const struct axlf_section_header *mem_header = NULL;
+ int rc;
+
+ rc = xrt_xclbin_get_section_hdr(xclbin, kind, &mem_header);
+ if (rc)
+ return rc;
+
+ *offset = mem_header->section_offset;
+ *size = mem_header->section_size;
+
+ return 0;
+}
+
+/* caller must free the allocated memory for **data */
+int xrt_xclbin_get_section(struct device *dev,
+ const struct axlf *buf,
+ enum axlf_section_kind kind,
+ void **data, u64 *len)
+{
+ const struct axlf *xclbin = (const struct axlf *)buf;
+ void *section = NULL;
+ u64 offset;
+ u64 size;
+ int err;
+
+ if (!data) {
+ dev_err(dev, "invalid data pointer");
+ return -EINVAL;
+ }
+
+ err = xrt_xclbin_section_info(xclbin, kind, &offset, &size);
+ if (err) {
+ dev_dbg(dev, "parsing section failed. kind %d, err = %d", kind, err);
+ return err;
+ }
+
+ section = vzalloc(size);
+ if (!section)
+ return -ENOMEM;
+
+ memcpy(section, ((const char *)xclbin) + offset, size);
+
+ *data = section;
+ if (len)
+ *len = size;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xrt_xclbin_get_section);
+
+static inline int xclbin_bit_get_string(const unchar *data, u32 size,
+ u32 offset, unchar prefix,
+ const unchar **str)
+{
+ int len;
+ u32 tmp;
+
+ /* prefix and length will be 3 bytes */
+ if (offset + 3 > size)
+ return -EINVAL;
+
+ /* Read prefix */
+ tmp = data[offset++];
+ if (tmp != prefix)
+ return -EINVAL;
+
+ /* Get string length */
+ len = bitstream_read16(data, &offset);
+ if (offset + len > size)
+ return -EINVAL;
+
+ if (data[offset + len - 1] != '\0')
+ return -EINVAL;
+
+ *str = data + offset;
+
+ return len + 3;
+}
+
+/* parse bitstream header */
+int xrt_xclbin_parse_bitstream_header(struct device *dev, const unchar *data,
+ u32 size, struct xclbin_bit_head_info *head_info)
+{
+ u32 offset = 0;
+ int len, i;
+ u16 magic;
+
+ memset(head_info, 0, sizeof(*head_info));
+
+ /* Get "Magic" length */
+ if (size < sizeof(u16)) {
+ dev_err(dev, "invalid size");
+ return -EINVAL;
+ }
+
+ len = bitstream_read16(data, &offset);
+ if (offset + len > size) {
+ dev_err(dev, "invalid magic len");
+ return -EINVAL;
+ }
+ head_info->magic_length = len;
+
+ for (i = 0; i < head_info->magic_length - 1; i++) {
+ magic = data[offset++];
+ if (!(i % 2) && magic != BITSTREAM_EVEN_MAGIC_BYTE) {
+ dev_err(dev, "invalid magic even byte at %d", offset);
+ return -EINVAL;
+ }
+
+ if ((i % 2) && magic != BITSTREAM_ODD_MAGIC_BYTE) {
+ dev_err(dev, "invalid magic odd byte at %d", offset);
+ return -EINVAL;
+ }
+ }
+
+ if (offset + 3 > size) {
+ dev_err(dev, "invalid length of magic end");
+ return -EINVAL;
+ }
+ /* Read null end of magic data. */
+ if (data[offset++]) {
+ dev_err(dev, "invalid magic end");
+ return -EINVAL;
+ }
+
+ /* Read 0x01 (short) */
+ magic = bitstream_read16(data, &offset);
+
+ /* Check the "0x01" half word */
+ if (magic != 0x01) {
+ dev_err(dev, "invalid magic end");
+ return -EINVAL;
+ }
+
+ len = xclbin_bit_get_string(data, size, offset, 'a', &head_info->design_name);
+ if (len < 0) {
+ dev_err(dev, "get design name failed");
+ return -EINVAL;
+ }
+
+ head_info->version = strstr(head_info->design_name, "Version=") + strlen("Version=");
+ offset += len;
+
+ len = xclbin_bit_get_string(data, size, offset, 'b', &head_info->part_name);
+ if (len < 0) {
+ dev_err(dev, "get part name failed");
+ return -EINVAL;
+ }
+ offset += len;
+
+ len = xclbin_bit_get_string(data, size, offset, 'c', &head_info->date);
+ if (len < 0) {
+ dev_err(dev, "get data failed");
+ return -EINVAL;
+ }
+ offset += len;
+
+ len = xclbin_bit_get_string(data, size, offset, 'd', &head_info->time);
+ if (len < 0) {
+ dev_err(dev, "get time failed");
+ return -EINVAL;
+ }
+ offset += len;
+
+ if (offset + 5 >= size) {
+ dev_err(dev, "can not get bitstream length");
+ return -EINVAL;
+ }
+
+ /* Read 'e' */
+ if (data[offset++] != 'e') {
+ dev_err(dev, "invalid prefix of bitstream length");
+ return -EINVAL;
+ }
+
+ /* Get byte length of bitstream */
+ head_info->bitstream_length = bitstream_read32(data, &offset);
+
+ head_info->header_length = offset;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xrt_xclbin_parse_bitstream_header);
+
+static struct xrt_clock_desc {
+ char *clock_ep_name;
+ u32 clock_xclbin_type;
+ char *clkfreq_ep_name;
+} clock_desc[] = {
+ {
+ .clock_ep_name = XRT_MD_NODE_CLK_KERNEL1,
+ .clock_xclbin_type = CT_DATA,
+ .clkfreq_ep_name = XRT_MD_NODE_CLKFREQ_K1,
+ },
+ {
+ .clock_ep_name = XRT_MD_NODE_CLK_KERNEL2,
+ .clock_xclbin_type = CT_KERNEL,
+ .clkfreq_ep_name = XRT_MD_NODE_CLKFREQ_K2,
+ },
+ {
+ .clock_ep_name = XRT_MD_NODE_CLK_KERNEL3,
+ .clock_xclbin_type = CT_SYSTEM,
+ .clkfreq_ep_name = XRT_MD_NODE_CLKFREQ_HBM,
+ },
+};
+
+const char *xrt_clock_type2epname(enum XCLBIN_CLOCK_TYPE type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(clock_desc); i++) {
+ if (clock_desc[i].clock_xclbin_type == type)
+ return clock_desc[i].clock_ep_name;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(xrt_clock_type2epname);
+
+static const char *clock_type2clkfreq_name(enum XCLBIN_CLOCK_TYPE type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(clock_desc); i++) {
+ if (clock_desc[i].clock_xclbin_type == type)
+ return clock_desc[i].clkfreq_ep_name;
+ }
+ return NULL;
+}
+
+static int xrt_xclbin_add_clock_metadata(struct device *dev,
+ const struct axlf *xclbin,
+ char *dtb)
+{
+ struct clock_freq_topology *clock_topo;
+ u16 freq;
+ int rc;
+ int i;
+
+ /* if clock section does not exist, add nothing and return success */
+ rc = xrt_xclbin_get_section(dev, xclbin, CLOCK_FREQ_TOPOLOGY,
+ (void **)&clock_topo, NULL);
+ if (rc == -ENOENT)
+ return 0;
+ else if (rc)
+ return rc;
+
+ for (i = 0; i < clock_topo->count; i++) {
+ u8 type = clock_topo->clock_freq[i].type;
+ const char *ep_name = xrt_clock_type2epname(type);
+ const char *counter_name = clock_type2clkfreq_name(type);
+
+ if (!ep_name || !counter_name)
+ continue;
+
+ freq = be16_to_cpu((__force __be16)clock_topo->clock_freq[i].freq_MHZ);
+ rc = xrt_md_set_prop(dev, dtb, ep_name, NULL, XRT_MD_PROP_CLK_FREQ,
+ &freq, sizeof(freq));
+ if (rc)
+ break;
+
+ rc = xrt_md_set_prop(dev, dtb, ep_name, NULL, XRT_MD_PROP_CLK_CNT,
+ counter_name, strlen(counter_name) + 1);
+ if (rc)
+ break;
+ }
+
+ vfree(clock_topo);
+
+ return rc;
+}
+
+int xrt_xclbin_get_metadata(struct device *dev, const struct axlf *xclbin, char **dtb)
+{
+ char *md = NULL, *newmd = NULL;
+ u64 len, md_len;
+ int rc;
+
+ *dtb = NULL;
+
+ rc = xrt_xclbin_get_section(dev, xclbin, PARTITION_METADATA, (void **)&md, &len);
+ if (rc)
+ goto done;
+
+ md_len = xrt_md_size(dev, md);
+
+ /* Sanity check the dtb section. */
+ if (md_len > len) {
+ rc = -EINVAL;
+ goto done;
+ }
+
+ /* use dup function here to convert incoming metadata to writable */
+ newmd = xrt_md_dup(dev, md);
+ if (!newmd) {
+ rc = -EFAULT;
+ goto done;
+ }
+
+ /* Convert various needed xclbin sections into dtb. */
+ rc = xrt_xclbin_add_clock_metadata(dev, xclbin, newmd);
+
+ if (!rc)
+ *dtb = newmd;
+ else
+ vfree(newmd);
+done:
+ vfree(md);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(xrt_xclbin_get_metadata);
diff --git a/include/uapi/linux/fpga-xrt.h b/include/uapi/linux/fpga-xrt.h
new file mode 100644
index 000000000000..ace7d3d09897
--- /dev/null
+++ b/include/uapi/linux/fpga-xrt.h
@@ -0,0 +1,428 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Xilinx FPGA compiled binary container format
+ *
+ * Copyright (C) 2015-2021, Xilinx Inc
+ */
+
+#ifndef _UAPI_LINUX_FPGA_XRT_H
+#define _UAPI_LINUX_FPGA_XRT_H
+
+#if defined(__KERNEL__)
+
+#include <linux/types.h>
+
+#elif defined(__cplusplus)
+
+#include <cstdlib>
+#include <cstdint>
+#include <algorithm>
+#include <uuid/uuid.h>
+
+#else
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <uuid/uuid.h>
+
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <linux/ioctl.h>
+
+/**
+ * DOC: Container format for Xilinx FPGA images
+ * The container stores bitstreams, metadata and firmware images.
+ * xclbin/xsabin is an ELF-like binary container format. It is a structured
+ * series of sections. There is a file header followed by several section
+ * headers which is followed by sections. A section header points to an
+ * actual section. There is an optional signature at the end. The
+ * following figure illustrates a typical xclbin:
+ *
+ * +---------------------+
+ * | |
+ * | HEADER |
+ * +---------------------+
+ * | SECTION HEADER |
+ * | |
+ * +---------------------+
+ * | ... |
+ * | |
+ * +---------------------+
+ * | SECTION HEADER |
+ * | |
+ * +---------------------+
+ * | SECTION |
+ * | |
+ * +---------------------+
+ * | ... |
+ * | |
+ * +---------------------+
+ * | SECTION |
+ * | |
+ * +---------------------+
+ * | SIGNATURE |
+ * | (OPTIONAL) |
+ * +---------------------+
+ */
+
+enum XCLBIN_MODE {
+ XCLBIN_FLAT = 0,
+ XCLBIN_PR,
+ XCLBIN_TANDEM_STAGE2,
+ XCLBIN_TANDEM_STAGE2_WITH_PR,
+ XCLBIN_HW_EMU,
+ XCLBIN_SW_EMU,
+ XCLBIN_MODE_MAX
+};
+
+enum axlf_section_kind {
+ BITSTREAM = 0,
+ CLEARING_BITSTREAM,
+ EMBEDDED_METADATA,
+ FIRMWARE,
+ DEBUG_DATA,
+ SCHED_FIRMWARE,
+ MEM_TOPOLOGY,
+ CONNECTIVITY,
+ IP_LAYOUT,
+ DEBUG_IP_LAYOUT,
+ DESIGN_CHECK_POINT,
+ CLOCK_FREQ_TOPOLOGY,
+ MCS,
+ BMC,
+ BUILD_METADATA,
+ KEYVALUE_METADATA,
+ USER_METADATA,
+ DNA_CERTIFICATE,
+ PDI,
+ BITSTREAM_PARTIAL_PDI,
+ PARTITION_METADATA,
+ EMULATION_DATA,
+ SYSTEM_METADATA,
+ SOFT_KERNEL,
+ ASK_FLASH,
+ AIE_METADATA,
+ ASK_GROUP_TOPOLOGY,
+ ASK_GROUP_CONNECTIVITY
+};
+
+enum MEM_TYPE {
+ MEM_DDR3 = 0,
+ MEM_DDR4,
+ MEM_DRAM,
+ MEM_STREAMING,
+ MEM_PREALLOCATED_GLOB,
+ MEM_ARE,
+ MEM_HBM,
+ MEM_BRAM,
+ MEM_URAM,
+ MEM_STREAMING_CONNECTION
+};
+
+enum IP_TYPE {
+ IP_MB = 0,
+ IP_KERNEL,
+ IP_DNASC,
+ IP_DDR4_CONTROLLER,
+ IP_MEM_DDR4,
+ IP_MEM_HBM
+};
+
+struct axlf_section_header {
+ uint32_t section_kind; /* Section type */
+ char section_name[16]; /* Section name */
+ char rsvd[4];
+ uint64_t section_offset; /* File offset of section data */
+ uint64_t section_size; /* Size of section data */
+} __packed;
+
+struct axlf_header {
+ uint64_t length; /* Total size of the xclbin file */
+ uint64_t time_stamp; /* Timestamp when xclbin was created */
+ uint64_t feature_rom_timestamp; /* TimeSinceEpoch of the featureRom */
+ uint16_t version_patch; /* Patch Version */
+ uint8_t version_major; /* Major Version */
+ uint8_t version_minor; /* Minor Version */
+ uint32_t mode; /* Xclbin mode. See enum XCLBIN_MODE */
+ union {
+ struct {
+ uint64_t platform_id; /* 64 bit platform ID */
+ uint64_t feature_id; /* 64 bit feature ID */
+ } rom;
+ unsigned char rom_uuid[16]; /* feature ROM UUID */
+ };
+ unsigned char platform_vbnv[64]; /* String of Vendor:Board:Name:Version */
+ union {
+ char next_axlf[16]; /* Name of next xclbin file */
+ unsigned char uuid[16]; /* uuid of this xclbin */
+ };
+ char debug_bin[16]; /* Name of binary with debug information */
+ uint32_t num_sections; /* Number of section headers */
+ char rsvd[4];
+} __packed;
+
+struct axlf {
+ char magic[8]; /* Magic word: xclbin2\0 */
+ int32_t signature_length; /* Length. -1 indicates no signature */
+ uint8_t reserved[28];
+ uint8_t key_block[256]; /* Signature for validation of binary */
+ uint64_t unique_id; /* Unique ID */
+ struct axlf_header header; /* Inline header */
+ struct axlf_section_header sections[1]; /* One or more section headers follow */
+} __packed;
+
+/* bitstream information */
+struct xlnx_bitstream {
+ uint8_t freq[8];
+ char bits[1];
+} __packed;
+
+/**** MEMORY TOPOLOGY SECTION ****/
+struct mem_data {
+ uint8_t type; /* Memory type. See enum MEM_TYPE */
+ uint8_t used; /* Memory presence. 0 means not present */
+ uint8_t rsvd[6];
+ union {
+ uint64_t size; /* Memory size. Counted in KB */
+ uint64_t route_id; /* Stream route ID */
+ };
+ union {
+ uint64_t base_address; /* Memory base address */
+ uint64_t flow_id; /* Stream flow ID */
+ };
+ unsigned char tag[16]; /* Memory tag string */
+} __packed;
+
+struct mem_topology {
+ int32_t count; /* Number of mem_data */
+ struct mem_data mem_data[1]; /* mem_data array, sorted on mem_type */
+} __packed;
+
+/**** CONNECTIVITY SECTION ****/
+/* Connectivity of each argument of CU(Compute Unit). It will be in terms
+ * of argument index associated. For associating CU instances with arguments
+ * and banks, start at the connectivity section. Using the ip_layout_index
+ * access the ip_data.name. Now we can associate this CU instance with its
+ * original CU name and get the connectivity as well. This enables us to form
+ * related groups of CU instances.
+ */
+
+struct connection {
+ int32_t arg_index; /* Index of CU argument */
+ int32_t ip_layout_index; /* Index into the ip_layout section */
+ int32_t mem_data_index; /* Index into the mem_data section */
+} __packed;
+
+struct connectivity {
+ int32_t count;
+ struct connection connection[1];
+} __packed;
+
+/**** IP_LAYOUT SECTION ****/
+
+/* IP Kernel */
+#define IP_INT_ENABLE_MASK 0x0001
+#define IP_INTERRUPT_ID_MASK 0x00FE
+#define IP_INTERRUPT_ID_SHIFT 0x1
+
+enum IP_CONTROL {
+ AP_CTRL_HS = 0,
+ AP_CTRL_CHAIN,
+ AP_CTRL_NONE,
+ AP_CTRL_ME,
+ ACCEL_ADAPTER
+};
+
+#define IP_CONTROL_MASK 0xFF00
+#define IP_CONTROL_SHIFT 0x8
+
+/*
+ * IPs on AXI lite - their types, names, and base addresses.
+ *
+ * The defination of 32-bit follows IP_TYPE is based on IP_TYPE
+ * For IP_KERNEL
+ * int_enable : Bit - 0x0000_0001;
+ * interrupt_id : Bits - 0x0000_00FE;
+ * ip_control : Bits - 0x0000_FF00;
+ * For IP_MEM_*
+ * index : Bits - 0x0000_FFFF;
+ * pc_index : Bits - 0x00FF_0000;
+ */
+struct ip_data {
+ uint32_t type; /* Type. See enum IP_TYPE */
+ union {
+ uint32_t properties;
+ struct {
+ uint16_t index;
+ uint8_t pc_index;
+ uint8_t unused;
+ } indices;
+ };
+ uint64_t base_address;
+ uint8_t name[64]; /* Name of IP */
+} __packed;
+
+struct ip_layout {
+ int32_t count;
+ struct ip_data ip_data[1]; /* ip_data array, sorted by base_address */
+} __packed;
+
+/*** Debug IP section layout ****/
+enum DEBUG_IP_TYPE {
+ UNDEFINED = 0,
+ LAPC,
+ ILA,
+ AXI_MM_MONITOR,
+ AXI_TRACE_FUNNEL,
+ AXI_MONITOR_FIFO_LITE,
+ AXI_MONITOR_FIFO_FULL,
+ ACCEL_MONITOR,
+ AXI_STREAM_MONITOR,
+ AXI_STREAM_PROTOCOL_CHECKER,
+ TRACE_S2MM,
+ AXI_DMA,
+ TRACE_S2MM_FULL
+};
+
+struct debug_ip_data {
+ uint8_t type; /* Type. See enum DEBUG_IP_TYPE */
+ uint8_t index_lowbyte;
+ uint8_t properties;
+ uint8_t major;
+ uint8_t minor;
+ uint8_t index_highbyte;
+ uint8_t reserved[2];
+ uint64_t base_address;
+ char name[128];
+} __packed;
+
+struct debug_ip_layout {
+ uint16_t count;
+ struct debug_ip_data debug_ip_data[1];
+} __packed;
+
+/* Supported clock frequency types */
+enum XCLBIN_CLOCK_TYPE {
+ CT_UNUSED = 0, /* Initialized value */
+ CT_DATA = 1, /* Data clock */
+ CT_KERNEL = 2, /* Kernel clock */
+ CT_SYSTEM = 3 /* System Clock */
+};
+
+/* Clock Frequency Entry */
+struct clock_freq {
+ uint16_t freq_MHZ; /* Frequency in MHz */
+ uint8_t type; /* Clock type (enum CLOCK_TYPE) */
+ uint8_t unused[5];
+ char name[128]; /* Clock Name */
+} __packed;
+
+/* Clock frequency section */
+struct clock_freq_topology {
+ int16_t count; /* Number of entries */
+ struct clock_freq clock_freq[1]; /* Clock array */
+} __packed;
+
+/* Supported MCS file types */
+enum MCS_TYPE {
+ MCS_UNKNOWN = 0, /* Initialized value */
+ MCS_PRIMARY = 1, /* The primary mcs file data */
+ MCS_SECONDARY = 2, /* The secondary mcs file data */
+};
+
+/* One chunk of MCS data */
+struct mcs_chunk {
+ uint8_t type; /* MCS data type */
+ uint8_t unused[7];
+ uint64_t offset; /* Data offset */
+ uint64_t size; /* Data size */
+} __packed;
+
+/* MCS data section */
+struct mcs {
+ int8_t count; /* Number of chunks */
+ int8_t unused[7];
+ struct mcs_chunk chunk[1]; /* MCS chunks followed by data */
+} __packed;
+
+/* bmc data section */
+struct bmc {
+ uint64_t offset; /* Data offset */
+ uint64_t size; /* Data size (bytes) */
+ char image_name[64]; /* Name of the image */
+ char device_name[64]; /* Name of the device */
+ char version[64];
+ char md5value[33]; /* MD5 checksum */
+ char padding[7];
+} __packed;
+
+/*
+ * soft kernel data section, used by classic driver
+ * Prefix Syntax:
+ * mpo - member, pointer, offset
+ * This variable represents a zero terminated string
+ * that is offseted from the beginning of the section.
+ * The pointer to access the string is initialized as follows:
+ * char * pCharString = (address_of_section) + (mpo value)
+ */
+struct soft_kernel {
+ uint32_t mpo_name; /* Name of the soft kernel */
+ uint32_t image_offset; /* Image offset */
+ uint32_t image_size; /* Image size */
+ uint32_t mpo_version; /* Version */
+ uint32_t mpo_md5_value; /* MD5 checksum */
+ uint32_t mpo_symbol_name; /* Symbol name */
+ uint32_t num_instances; /* Number of instances */
+ uint8_t padding[36]; /* Reserved for future use */
+ uint8_t reserved_ext[16]; /* Reserved for future extended data */
+} __packed;
+
+enum CHECKSUM_TYPE {
+ CST_UNKNOWN = 0,
+ CST_SDBM = 1,
+ CST_LAST
+};
+
+/**
+ * DOC: PCIe Kernel Driver for Management Physical Function
+ * Interfaces exposed by *xrt-mgmt* driver are defined in file, *xmgmt-ioctl.h*.
+ * Core functionality provided by *xrt-mgmt* driver is described in the following table:
+ *
+ * =========== ============================== ==================================
+ * Functionality ioctl request code data format
+ * =========== ============================== ==================================
+ * 1 FPGA image download XMGMT_IOCICAPDOWNLOAD_AXLF xmgmt_ioc_bitstream_axlf
+ * =========== ============================== ==================================
+ */
+
+#define XMGMT_IOC_MAGIC 'X'
+#define XMGMT_IOC_ICAP_DOWNLOAD_AXLF 0x6
+
+/**
+ * struct xmgmt_ioc_bitstream_axlf - load xclbin (AXLF) device image
+ * used with XMGMT_IOCICAPDOWNLOAD_AXLF ioctl
+ *
+ * @xclbin: Pointer to user's xclbin structure in memory
+ */
+struct xmgmt_ioc_bitstream_axlf {
+ struct axlf *xclbin;
+};
+
+#define XMGMT_IOCICAPDOWNLOAD_AXLF \
+ _IOW(XMGMT_IOC_MAGIC, XMGMT_IOC_ICAP_DOWNLOAD_AXLF, struct xmgmt_ioc_bitstream_axlf)
+
+/*
+ * The following definitions are for binary compatibility with classic XRT management driver
+ */
+#define XCLMGMT_IOCICAPDOWNLOAD_AXLF XMGMT_IOCICAPDOWNLOAD_AXLF
+#define xclmgmt_ioc_bitstream_axlf xmgmt_ioc_bitstream_axlf
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--
2.27.0
Infrastructure code providing APIs for managing leaf driver instance
groups, facilitating inter-leaf driver calls and root calls.
Signed-off-by: Sonal Santan <[email protected]>
Signed-off-by: Max Zhen <[email protected]>
Signed-off-by: Lizhi Hou <[email protected]>
Reviewed-by: Tom Rix <[email protected]>
---
drivers/fpga/xrt/lib/subdev.c | 859 ++++++++++++++++++++++++++++++++++
1 file changed, 859 insertions(+)
create mode 100644 drivers/fpga/xrt/lib/subdev.c
diff --git a/drivers/fpga/xrt/lib/subdev.c b/drivers/fpga/xrt/lib/subdev.c
new file mode 100644
index 000000000000..350f67d5eb1e
--- /dev/null
+++ b/drivers/fpga/xrt/lib/subdev.c
@@ -0,0 +1,859 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Cheng Zhen <[email protected]>
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include "xleaf.h"
+#include "subdev_pool.h"
+#include "lib-drv.h"
+#include "metadata.h"
+
+extern struct bus_type xrt_bus_type;
+
+#define IS_ROOT_DEV(dev) ((dev)->bus != &xrt_bus_type)
+#define XRT_HOLDER_BUF_SZ 1024
+
+static inline struct device *find_root(struct xrt_device *xdev)
+{
+ struct device *d = DEV(xdev);
+
+ while (!IS_ROOT_DEV(d))
+ d = d->parent;
+ return d;
+}
+
+/*
+ * It represents a holder of a subdev. One holder can repeatedly hold a subdev
+ * as long as there is a unhold corresponding to a hold.
+ */
+struct xrt_subdev_holder {
+ struct list_head xsh_holder_list;
+ struct device *xsh_holder;
+ int xsh_count;
+ struct kref xsh_kref;
+};
+
+/*
+ * It represents a specific instance of platform driver for a subdev, which
+ * provides services to its clients (another subdev driver or root driver).
+ */
+struct xrt_subdev {
+ struct list_head xs_dev_list;
+ struct list_head xs_holder_list;
+ enum xrt_subdev_id xs_id; /* type of subdev */
+ struct xrt_device *xs_xdev;
+ struct completion xs_holder_comp;
+};
+
+static struct xrt_subdev *xrt_subdev_alloc(void)
+{
+ struct xrt_subdev *sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
+
+ if (!sdev)
+ return NULL;
+
+ INIT_LIST_HEAD(&sdev->xs_dev_list);
+ INIT_LIST_HEAD(&sdev->xs_holder_list);
+ init_completion(&sdev->xs_holder_comp);
+ return sdev;
+}
+
+int xrt_subdev_root_request(struct xrt_device *self, u32 cmd, void *arg)
+{
+ struct device *dev = DEV(self);
+ struct xrt_subdev_platdata *pdata = DEV_PDATA(self);
+
+ if (!pdata->xsp_root_cb) {
+ dev_err(dev, "invalid root callback");
+ return -EINVAL;
+ }
+ return (*pdata->xsp_root_cb)(dev->parent, pdata->xsp_root_cb_arg, cmd, arg);
+}
+
+/*
+ * Subdev common sysfs nodes.
+ */
+static ssize_t holders_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ ssize_t len;
+ struct xrt_device *xdev = to_xrt_dev(dev);
+ struct xrt_root_get_holders holders = { xdev, buf, XRT_HOLDER_BUF_SZ };
+
+ len = xrt_subdev_root_request(xdev, XRT_ROOT_GET_LEAF_HOLDERS, &holders);
+ if (len >= holders.xpigh_holder_buf_len)
+ return len;
+ buf[len] = '\n';
+ return len + 1;
+}
+static DEVICE_ATTR_RO(holders);
+
+static struct attribute *xrt_subdev_attrs[] = {
+ &dev_attr_holders.attr,
+ NULL,
+};
+
+static ssize_t metadata_output(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t off, size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct xrt_device *xdev = to_xrt_dev(dev);
+ struct xrt_subdev_platdata *pdata = DEV_PDATA(xdev);
+ unsigned char *blob;
+ unsigned long size;
+ ssize_t ret = 0;
+
+ blob = pdata->xsp_dtb;
+ size = xrt_md_size(dev, blob);
+ if (size == XRT_MD_INVALID_LENGTH) {
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ if (off >= size) {
+ dev_dbg(dev, "offset (%lld) beyond total size: %ld\n", off, size);
+ goto failed;
+ }
+
+ if (off + count > size) {
+ dev_dbg(dev, "count (%ld) beyond left bytes: %lld\n",
+ (unsigned long)count, size - off);
+ count = size - off;
+ }
+ memcpy(buf, blob + off, count);
+
+ ret = count;
+failed:
+ return ret;
+}
+
+static struct bin_attribute meta_data_attr = {
+ .attr = {
+ .name = "metadata",
+ .mode = 0400
+ },
+ .read = metadata_output,
+ .size = 0
+};
+
+static struct bin_attribute *xrt_subdev_bin_attrs[] = {
+ &meta_data_attr,
+ NULL,
+};
+
+static const struct attribute_group xrt_subdev_attrgroup = {
+ .attrs = xrt_subdev_attrs,
+ .bin_attrs = xrt_subdev_bin_attrs,
+};
+
+/*
+ * Given the device metadata, parse it to get IO ranges and construct
+ * resource array.
+ */
+static int
+xrt_subdev_getres(struct device *parent, enum xrt_subdev_id id,
+ char *dtb, struct resource **res, int *res_num)
+{
+ struct xrt_subdev_platdata *pdata;
+ struct resource *pci_res = NULL;
+ const __be64 *bar_range;
+ const __be32 *bar_idx;
+ char *ep_name = NULL, *compat = NULL;
+ uint bar;
+ int count1 = 0, count2 = 0, ret;
+
+ if (!dtb)
+ return -EINVAL;
+
+ pdata = DEV_PDATA(to_xrt_dev(parent));
+
+ /* go through metadata and count endpoints in it */
+ xrt_md_get_next_endpoint(parent, dtb, NULL, NULL, &ep_name, &compat);
+ while (ep_name) {
+ ret = xrt_md_get_prop(parent, dtb, ep_name, compat,
+ XRT_MD_PROP_IO_OFFSET, (const void **)&bar_range, NULL);
+ if (!ret)
+ count1++;
+ xrt_md_get_next_endpoint(parent, dtb, ep_name, compat, &ep_name, &compat);
+ }
+ if (!count1)
+ return 0;
+
+ /* allocate resource array for all endpoints been found in metadata */
+ *res = vzalloc(sizeof(**res) * count1);
+
+ /* go through all endpoints again and get IO range for each endpoint */
+ ep_name = NULL;
+ xrt_md_get_next_endpoint(parent, dtb, NULL, NULL, &ep_name, &compat);
+ while (ep_name) {
+ ret = xrt_md_get_prop(parent, dtb, ep_name, compat,
+ XRT_MD_PROP_IO_OFFSET, (const void **)&bar_range, NULL);
+ if (ret)
+ continue;
+ xrt_md_get_prop(parent, dtb, ep_name, compat,
+ XRT_MD_PROP_BAR_IDX, (const void **)&bar_idx, NULL);
+ bar = bar_idx ? be32_to_cpu(*bar_idx) : 0;
+ xleaf_get_root_res(to_xrt_dev(parent), bar, &pci_res);
+ if (!pci_res) {
+ dev_err(parent, "Invalid bar defined %d", bar);
+ ret = -EINVAL;
+ goto failed;
+ }
+ (*res)[count2].start = pci_res->start + be64_to_cpu(bar_range[0]);
+ (*res)[count2].end = pci_res->start + be64_to_cpu(bar_range[0]) +
+ be64_to_cpu(bar_range[1]) - 1;
+ (*res)[count2].flags = IORESOURCE_MEM;
+ /* check if there is conflicted resource */
+ ret = request_resource(pci_res, *res + count2);
+ if (ret) {
+ dev_err(parent, "Conflict resource %pR\n", *res + count2);
+ goto failed;
+ }
+ release_resource(*res + count2);
+
+ (*res)[count2].parent = pci_res;
+
+ xrt_md_find_endpoint(parent, pdata->xsp_dtb, ep_name,
+ compat, &(*res)[count2].name);
+
+ count2++;
+ xrt_md_get_next_endpoint(parent, dtb, ep_name, compat, &ep_name, &compat);
+ }
+
+ WARN_ON(count1 != count2);
+ *res_num = count2;
+
+ return 0;
+
+failed:
+ vfree(*res);
+ *res_num = 0;
+ *res = NULL;
+ return ret;
+}
+
+static inline enum xrt_dev_file_mode
+xleaf_devnode_mode(struct xrt_device *xdev)
+{
+ return DEV_FILE_OPS(xdev)->xsf_mode;
+}
+
+static bool xrt_subdev_cdev_auto_creation(struct xrt_device *xdev)
+{
+ enum xrt_dev_file_mode mode = xleaf_devnode_mode(xdev);
+
+ if (!xleaf_devnode_enabled(xdev))
+ return false;
+
+ return (mode == XRT_DEV_FILE_DEFAULT || mode == XRT_DEV_FILE_MULTI_INST);
+}
+
+static struct xrt_subdev *
+xrt_subdev_create(struct device *parent, enum xrt_subdev_id id,
+ xrt_subdev_root_cb_t pcb, void *pcb_arg, char *dtb)
+{
+ struct xrt_subdev_platdata *pdata = NULL;
+ struct xrt_subdev *sdev = NULL;
+ struct xrt_device *xdev = NULL;
+ struct resource *res = NULL;
+ unsigned long dtb_len = 0;
+ bool dtb_alloced = false;
+ int res_num = 0;
+ size_t pdata_sz;
+ int ret;
+
+ sdev = xrt_subdev_alloc();
+ if (!sdev) {
+ dev_err(parent, "failed to alloc subdev for ID %d", id);
+ return NULL;
+ }
+ sdev->xs_id = id;
+
+ if (!dtb) {
+ ret = xrt_md_create(parent, &dtb);
+ if (ret) {
+ dev_err(parent, "can't create empty dtb: %d", ret);
+ goto fail;
+ }
+ dtb_alloced = true;
+ }
+ xrt_md_pack(parent, dtb);
+ dtb_len = xrt_md_size(parent, dtb);
+ if (dtb_len == XRT_MD_INVALID_LENGTH) {
+ dev_err(parent, "invalid metadata len %ld", dtb_len);
+ goto fail1;
+ }
+ pdata_sz = sizeof(struct xrt_subdev_platdata) + dtb_len;
+
+ /* Prepare platform data passed to subdev. */
+ pdata = vzalloc(pdata_sz);
+ if (!pdata)
+ goto fail1;
+
+ pdata->xsp_root_cb = pcb;
+ pdata->xsp_root_cb_arg = pcb_arg;
+ memcpy(pdata->xsp_dtb, dtb, dtb_len);
+ if (id == XRT_SUBDEV_GRP) {
+ /* Group can only be created by root driver. */
+ pdata->xsp_root_name = dev_name(parent);
+ } else {
+ struct xrt_device *grp = to_xrt_dev(parent);
+
+ /* Leaf can only be created by group driver. */
+ WARN_ON(to_xrt_drv(parent->driver)->subdev_id != XRT_SUBDEV_GRP);
+ pdata->xsp_root_name = DEV_PDATA(grp)->xsp_root_name;
+ }
+
+ /* Create subdev. */
+ if (id != XRT_SUBDEV_GRP) {
+ int rc = xrt_subdev_getres(parent, id, dtb, &res, &res_num);
+
+ if (rc) {
+ dev_err(parent, "failed to get resource for %s: %d",
+ xrt_drv_name(id), rc);
+ goto fail2;
+ }
+ }
+ xdev = xrt_device_register(parent, id, res, res_num, pdata, pdata_sz);
+ vfree(res);
+ if (!xdev) {
+ dev_err(parent, "failed to create subdev for %s", xrt_drv_name(id));
+ goto fail2;
+ }
+ sdev->xs_xdev = xdev;
+
+ if (device_attach(DEV(xdev)) != 1) {
+ xrt_err(xdev, "failed to attach");
+ goto fail3;
+ }
+
+ if (sysfs_create_group(&DEV(xdev)->kobj, &xrt_subdev_attrgroup))
+ xrt_err(xdev, "failed to create sysfs group");
+
+ /*
+ * Create sysfs sym link under root for leaves
+ * under random groups for easy access to them.
+ */
+ if (id != XRT_SUBDEV_GRP) {
+ if (sysfs_create_link(&find_root(xdev)->kobj,
+ &DEV(xdev)->kobj, dev_name(DEV(xdev)))) {
+ xrt_err(xdev, "failed to create sysfs link");
+ }
+ }
+
+ /* All done, ready to handle req thru cdev. */
+ if (xrt_subdev_cdev_auto_creation(xdev))
+ xleaf_devnode_create(xdev, DEV_FILE_OPS(xdev)->xsf_dev_name, NULL);
+
+ vfree(pdata);
+ return sdev;
+
+fail3:
+ xrt_device_unregister(sdev->xs_xdev);
+fail2:
+ vfree(pdata);
+fail1:
+ if (dtb_alloced)
+ vfree(dtb);
+fail:
+ kfree(sdev);
+ return NULL;
+}
+
+static void xrt_subdev_destroy(struct xrt_subdev *sdev)
+{
+ struct xrt_device *xdev = sdev->xs_xdev;
+ struct device *dev = DEV(xdev);
+
+ /* Take down the device node */
+ if (xrt_subdev_cdev_auto_creation(xdev))
+ xleaf_devnode_destroy(xdev);
+ if (sdev->xs_id != XRT_SUBDEV_GRP)
+ sysfs_remove_link(&find_root(xdev)->kobj, dev_name(dev));
+ sysfs_remove_group(&dev->kobj, &xrt_subdev_attrgroup);
+ xrt_device_unregister(xdev);
+ kfree(sdev);
+}
+
+struct xrt_device *
+xleaf_get_leaf(struct xrt_device *xdev, xrt_subdev_match_t match_cb, void *match_arg)
+{
+ int rc;
+ struct xrt_root_get_leaf get_leaf = {
+ xdev, match_cb, match_arg, };
+
+ rc = xrt_subdev_root_request(xdev, XRT_ROOT_GET_LEAF, &get_leaf);
+ if (rc)
+ return NULL;
+ return get_leaf.xpigl_tgt_xdev;
+}
+EXPORT_SYMBOL_GPL(xleaf_get_leaf);
+
+bool xleaf_has_endpoint(struct xrt_device *xdev, const char *endpoint_name)
+{
+ struct resource *res;
+ int i = 0;
+
+ do {
+ res = xrt_get_resource(xdev, IORESOURCE_MEM, i);
+ if (res && !strncmp(res->name, endpoint_name, strlen(res->name) + 1))
+ return true;
+ ++i;
+ } while (res);
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(xleaf_has_endpoint);
+
+int xleaf_put_leaf(struct xrt_device *xdev, struct xrt_device *leaf)
+{
+ struct xrt_root_put_leaf put_leaf = { xdev, leaf };
+
+ return xrt_subdev_root_request(xdev, XRT_ROOT_PUT_LEAF, &put_leaf);
+}
+EXPORT_SYMBOL_GPL(xleaf_put_leaf);
+
+int xleaf_create_group(struct xrt_device *xdev, char *dtb)
+{
+ return xrt_subdev_root_request(xdev, XRT_ROOT_CREATE_GROUP, dtb);
+}
+EXPORT_SYMBOL_GPL(xleaf_create_group);
+
+int xleaf_destroy_group(struct xrt_device *xdev, int instance)
+{
+ return xrt_subdev_root_request(xdev, XRT_ROOT_REMOVE_GROUP, (void *)(uintptr_t)instance);
+}
+EXPORT_SYMBOL_GPL(xleaf_destroy_group);
+
+int xleaf_wait_for_group_bringup(struct xrt_device *xdev)
+{
+ return xrt_subdev_root_request(xdev, XRT_ROOT_WAIT_GROUP_BRINGUP, NULL);
+}
+EXPORT_SYMBOL_GPL(xleaf_wait_for_group_bringup);
+
+static ssize_t
+xrt_subdev_get_holders(struct xrt_subdev *sdev, char *buf, size_t len)
+{
+ const struct list_head *ptr;
+ struct xrt_subdev_holder *h;
+ ssize_t n = 0;
+
+ list_for_each(ptr, &sdev->xs_holder_list) {
+ h = list_entry(ptr, struct xrt_subdev_holder, xsh_holder_list);
+ n += snprintf(buf + n, len - n, "%s:%d ",
+ dev_name(h->xsh_holder), kref_read(&h->xsh_kref));
+ /* Truncation is fine here. Buffer content is only for debugging. */
+ if (n >= (len - 1))
+ break;
+ }
+ return n;
+}
+
+void xrt_subdev_pool_init(struct device *dev, struct xrt_subdev_pool *spool)
+{
+ INIT_LIST_HEAD(&spool->xsp_dev_list);
+ spool->xsp_owner = dev;
+ mutex_init(&spool->xsp_lock);
+ spool->xsp_closing = false;
+}
+
+static void xrt_subdev_free_holder(struct xrt_subdev_holder *holder)
+{
+ list_del(&holder->xsh_holder_list);
+ vfree(holder);
+}
+
+static void xrt_subdev_pool_wait_for_holders(struct xrt_subdev_pool *spool, struct xrt_subdev *sdev)
+{
+ const struct list_head *ptr, *next;
+ char holders[128];
+ struct xrt_subdev_holder *holder;
+ struct mutex *lk = &spool->xsp_lock;
+
+ while (!list_empty(&sdev->xs_holder_list)) {
+ int rc;
+
+ /* It's most likely a bug if we ever enters this loop. */
+ xrt_subdev_get_holders(sdev, holders, sizeof(holders));
+ xrt_err(sdev->xs_xdev, "awaits holders: %s", holders);
+ mutex_unlock(lk);
+ rc = wait_for_completion_killable(&sdev->xs_holder_comp);
+ mutex_lock(lk);
+ if (rc == -ERESTARTSYS) {
+ xrt_err(sdev->xs_xdev, "give up on waiting for holders, clean up now");
+ list_for_each_safe(ptr, next, &sdev->xs_holder_list) {
+ holder = list_entry(ptr, struct xrt_subdev_holder, xsh_holder_list);
+ xrt_subdev_free_holder(holder);
+ }
+ }
+ }
+}
+
+void xrt_subdev_pool_fini(struct xrt_subdev_pool *spool)
+{
+ struct list_head *dl = &spool->xsp_dev_list;
+ struct mutex *lk = &spool->xsp_lock;
+
+ mutex_lock(lk);
+ if (spool->xsp_closing) {
+ mutex_unlock(lk);
+ return;
+ }
+ spool->xsp_closing = true;
+ mutex_unlock(lk);
+
+ /* Remove subdev in the reverse order of added. */
+ while (!list_empty(dl)) {
+ struct xrt_subdev *sdev = list_first_entry(dl, struct xrt_subdev, xs_dev_list);
+
+ xrt_subdev_pool_wait_for_holders(spool, sdev);
+ list_del(&sdev->xs_dev_list);
+ xrt_subdev_destroy(sdev);
+ }
+}
+
+static struct xrt_subdev_holder *xrt_subdev_find_holder(struct xrt_subdev *sdev,
+ struct device *holder_dev)
+{
+ struct list_head *hl = &sdev->xs_holder_list;
+ struct xrt_subdev_holder *holder;
+ const struct list_head *ptr;
+
+ list_for_each(ptr, hl) {
+ holder = list_entry(ptr, struct xrt_subdev_holder, xsh_holder_list);
+ if (holder->xsh_holder == holder_dev)
+ return holder;
+ }
+ return NULL;
+}
+
+static int xrt_subdev_hold(struct xrt_subdev *sdev, struct device *holder_dev)
+{
+ struct xrt_subdev_holder *holder = xrt_subdev_find_holder(sdev, holder_dev);
+ struct list_head *hl = &sdev->xs_holder_list;
+
+ if (!holder) {
+ holder = vzalloc(sizeof(*holder));
+ if (!holder)
+ return -ENOMEM;
+ holder->xsh_holder = holder_dev;
+ kref_init(&holder->xsh_kref);
+ list_add_tail(&holder->xsh_holder_list, hl);
+ } else {
+ kref_get(&holder->xsh_kref);
+ }
+
+ return 0;
+}
+
+static void xrt_subdev_free_holder_kref(struct kref *kref)
+{
+ struct xrt_subdev_holder *holder = container_of(kref, struct xrt_subdev_holder, xsh_kref);
+
+ xrt_subdev_free_holder(holder);
+}
+
+static int
+xrt_subdev_release(struct xrt_subdev *sdev, struct device *holder_dev)
+{
+ struct xrt_subdev_holder *holder = xrt_subdev_find_holder(sdev, holder_dev);
+ struct list_head *hl = &sdev->xs_holder_list;
+
+ if (!holder) {
+ dev_err(holder_dev, "can't release, %s did not hold %s",
+ dev_name(holder_dev), dev_name(DEV(sdev->xs_xdev)));
+ return -EINVAL;
+ }
+ kref_put(&holder->xsh_kref, xrt_subdev_free_holder_kref);
+
+ /* kref_put above may remove holder from list. */
+ if (list_empty(hl))
+ complete(&sdev->xs_holder_comp);
+ return 0;
+}
+
+int xrt_subdev_pool_add(struct xrt_subdev_pool *spool, enum xrt_subdev_id id,
+ xrt_subdev_root_cb_t pcb, void *pcb_arg, char *dtb)
+{
+ struct mutex *lk = &spool->xsp_lock;
+ struct list_head *dl = &spool->xsp_dev_list;
+ struct xrt_subdev *sdev;
+ int ret = 0;
+
+ sdev = xrt_subdev_create(spool->xsp_owner, id, pcb, pcb_arg, dtb);
+ if (sdev) {
+ mutex_lock(lk);
+ if (spool->xsp_closing) {
+ /* No new subdev when pool is going away. */
+ xrt_err(sdev->xs_xdev, "pool is closing");
+ ret = -ENODEV;
+ } else {
+ list_add(&sdev->xs_dev_list, dl);
+ }
+ mutex_unlock(lk);
+ if (ret)
+ xrt_subdev_destroy(sdev);
+ } else {
+ ret = -EINVAL;
+ }
+
+ ret = ret ? ret : sdev->xs_xdev->instance;
+ return ret;
+}
+
+int xrt_subdev_pool_del(struct xrt_subdev_pool *spool, enum xrt_subdev_id id, int instance)
+{
+ const struct list_head *ptr;
+ struct mutex *lk = &spool->xsp_lock;
+ struct list_head *dl = &spool->xsp_dev_list;
+ struct xrt_subdev *sdev;
+ int ret = -ENOENT;
+
+ mutex_lock(lk);
+ if (spool->xsp_closing) {
+ /* Pool is going away, all subdevs will be gone. */
+ mutex_unlock(lk);
+ return 0;
+ }
+ list_for_each(ptr, dl) {
+ sdev = list_entry(ptr, struct xrt_subdev, xs_dev_list);
+ if (sdev->xs_id != id || sdev->xs_xdev->instance != instance)
+ continue;
+ xrt_subdev_pool_wait_for_holders(spool, sdev);
+ list_del(&sdev->xs_dev_list);
+ ret = 0;
+ break;
+ }
+ mutex_unlock(lk);
+ if (ret)
+ return ret;
+
+ xrt_subdev_destroy(sdev);
+ return 0;
+}
+
+static int xrt_subdev_pool_get_impl(struct xrt_subdev_pool *spool, xrt_subdev_match_t match,
+ void *arg, struct device *holder_dev, struct xrt_subdev **sdevp)
+{
+ struct xrt_device *xdev = (struct xrt_device *)arg;
+ struct list_head *dl = &spool->xsp_dev_list;
+ struct mutex *lk = &spool->xsp_lock;
+ struct xrt_subdev *sdev = NULL;
+ const struct list_head *ptr;
+ struct xrt_subdev *d = NULL;
+ int ret = -ENOENT;
+
+ mutex_lock(lk);
+
+ if (!xdev) {
+ if (match == XRT_SUBDEV_MATCH_PREV) {
+ sdev = list_empty(dl) ? NULL :
+ list_last_entry(dl, struct xrt_subdev, xs_dev_list);
+ } else if (match == XRT_SUBDEV_MATCH_NEXT) {
+ sdev = list_first_entry_or_null(dl, struct xrt_subdev, xs_dev_list);
+ }
+ }
+
+ list_for_each(ptr, dl) {
+ d = list_entry(ptr, struct xrt_subdev, xs_dev_list);
+ if (match == XRT_SUBDEV_MATCH_PREV || match == XRT_SUBDEV_MATCH_NEXT) {
+ if (d->xs_xdev != xdev)
+ continue;
+ } else {
+ if (!match(d->xs_id, d->xs_xdev, arg))
+ continue;
+ }
+
+ if (match == XRT_SUBDEV_MATCH_PREV)
+ sdev = !list_is_first(ptr, dl) ? list_prev_entry(d, xs_dev_list) : NULL;
+ else if (match == XRT_SUBDEV_MATCH_NEXT)
+ sdev = !list_is_last(ptr, dl) ? list_next_entry(d, xs_dev_list) : NULL;
+ else
+ sdev = d;
+ }
+
+ if (sdev)
+ ret = xrt_subdev_hold(sdev, holder_dev);
+
+ mutex_unlock(lk);
+
+ if (!ret)
+ *sdevp = sdev;
+ return ret;
+}
+
+int xrt_subdev_pool_get(struct xrt_subdev_pool *spool, xrt_subdev_match_t match, void *arg,
+ struct device *holder_dev, struct xrt_device **xdevp)
+{
+ int rc;
+ struct xrt_subdev *sdev;
+
+ rc = xrt_subdev_pool_get_impl(spool, match, arg, holder_dev, &sdev);
+ if (rc) {
+ if (rc != -ENOENT)
+ dev_err(holder_dev, "failed to hold device: %d", rc);
+ return rc;
+ }
+
+ if (!IS_ROOT_DEV(holder_dev)) {
+ xrt_dbg(to_xrt_dev(holder_dev), "%s <<==== %s",
+ dev_name(holder_dev), dev_name(DEV(sdev->xs_xdev)));
+ }
+
+ *xdevp = sdev->xs_xdev;
+ return 0;
+}
+
+static int xrt_subdev_pool_put_impl(struct xrt_subdev_pool *spool, struct xrt_device *xdev,
+ struct device *holder_dev)
+{
+ const struct list_head *ptr;
+ struct mutex *lk = &spool->xsp_lock;
+ struct list_head *dl = &spool->xsp_dev_list;
+ struct xrt_subdev *sdev;
+ int ret = -ENOENT;
+
+ mutex_lock(lk);
+ list_for_each(ptr, dl) {
+ sdev = list_entry(ptr, struct xrt_subdev, xs_dev_list);
+ if (sdev->xs_xdev != xdev)
+ continue;
+ ret = xrt_subdev_release(sdev, holder_dev);
+ break;
+ }
+ mutex_unlock(lk);
+
+ return ret;
+}
+
+int xrt_subdev_pool_put(struct xrt_subdev_pool *spool, struct xrt_device *xdev,
+ struct device *holder_dev)
+{
+ int ret = xrt_subdev_pool_put_impl(spool, xdev, holder_dev);
+
+ if (ret)
+ return ret;
+
+ if (!IS_ROOT_DEV(holder_dev)) {
+ xrt_dbg(to_xrt_dev(holder_dev), "%s <<==X== %s",
+ dev_name(holder_dev), dev_name(DEV(xdev)));
+ }
+ return 0;
+}
+
+void xrt_subdev_pool_trigger_event(struct xrt_subdev_pool *spool, enum xrt_events e)
+{
+ struct xrt_device *tgt = NULL;
+ struct xrt_subdev *sdev = NULL;
+ struct xrt_event evt;
+
+ while (!xrt_subdev_pool_get_impl(spool, XRT_SUBDEV_MATCH_NEXT,
+ tgt, spool->xsp_owner, &sdev)) {
+ tgt = sdev->xs_xdev;
+ evt.xe_evt = e;
+ evt.xe_subdev.xevt_subdev_id = sdev->xs_id;
+ evt.xe_subdev.xevt_subdev_instance = tgt->instance;
+ xrt_subdev_root_request(tgt, XRT_ROOT_EVENT_SYNC, &evt);
+ xrt_subdev_pool_put_impl(spool, tgt, spool->xsp_owner);
+ }
+}
+
+void xrt_subdev_pool_handle_event(struct xrt_subdev_pool *spool, struct xrt_event *evt)
+{
+ struct xrt_device *tgt = NULL;
+ struct xrt_subdev *sdev = NULL;
+
+ while (!xrt_subdev_pool_get_impl(spool, XRT_SUBDEV_MATCH_NEXT,
+ tgt, spool->xsp_owner, &sdev)) {
+ tgt = sdev->xs_xdev;
+ xleaf_call(tgt, XRT_XLEAF_EVENT, evt);
+ xrt_subdev_pool_put_impl(spool, tgt, spool->xsp_owner);
+ }
+}
+
+ssize_t xrt_subdev_pool_get_holders(struct xrt_subdev_pool *spool,
+ struct xrt_device *xdev, char *buf, size_t len)
+{
+ const struct list_head *ptr;
+ struct mutex *lk = &spool->xsp_lock;
+ struct list_head *dl = &spool->xsp_dev_list;
+ struct xrt_subdev *sdev;
+ ssize_t ret = 0;
+
+ mutex_lock(lk);
+ list_for_each(ptr, dl) {
+ sdev = list_entry(ptr, struct xrt_subdev, xs_dev_list);
+ if (sdev->xs_xdev != xdev)
+ continue;
+ ret = xrt_subdev_get_holders(sdev, buf, len);
+ break;
+ }
+ mutex_unlock(lk);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(xrt_subdev_pool_get_holders);
+
+int xleaf_broadcast_event(struct xrt_device *xdev, enum xrt_events evt, bool async)
+{
+ struct xrt_event e = { evt, };
+ enum xrt_root_cmd cmd = async ? XRT_ROOT_EVENT_ASYNC : XRT_ROOT_EVENT_SYNC;
+
+ WARN_ON(evt == XRT_EVENT_POST_CREATION || evt == XRT_EVENT_PRE_REMOVAL);
+ return xrt_subdev_root_request(xdev, cmd, &e);
+}
+EXPORT_SYMBOL_GPL(xleaf_broadcast_event);
+
+void xleaf_hot_reset(struct xrt_device *xdev)
+{
+ xrt_subdev_root_request(xdev, XRT_ROOT_HOT_RESET, NULL);
+}
+EXPORT_SYMBOL_GPL(xleaf_hot_reset);
+
+void xleaf_get_root_res(struct xrt_device *xdev, u32 region_id, struct resource **res)
+{
+ struct xrt_root_get_res arg = { 0 };
+
+ arg.xpigr_region_id = region_id;
+ xrt_subdev_root_request(xdev, XRT_ROOT_GET_RESOURCE, &arg);
+ *res = arg.xpigr_res;
+}
+
+void xleaf_get_root_id(struct xrt_device *xdev, unsigned short *vendor, unsigned short *device,
+ unsigned short *subvendor, unsigned short *subdevice)
+{
+ struct xrt_root_get_id id = { 0 };
+
+ WARN_ON(!vendor && !device && !subvendor && !subdevice);
+
+ xrt_subdev_root_request(xdev, XRT_ROOT_GET_ID, (void *)&id);
+ if (vendor)
+ *vendor = id.xpigi_vendor_id;
+ if (device)
+ *device = id.xpigi_device_id;
+ if (subvendor)
+ *subvendor = id.xpigi_sub_vendor_id;
+ if (subdevice)
+ *subdevice = id.xpigi_sub_device_id;
+}
+
+struct device *xleaf_register_hwmon(struct xrt_device *xdev, const char *name, void *drvdata,
+ const struct attribute_group **grps)
+{
+ struct xrt_root_hwmon hm = { true, name, drvdata, grps, };
+
+ xrt_subdev_root_request(xdev, XRT_ROOT_HWMON, (void *)&hm);
+ return hm.xpih_hwmon_dev;
+}
+
+void xleaf_unregister_hwmon(struct xrt_device *xdev, struct device *hwmon)
+{
+ struct xrt_root_hwmon hm = { false, };
+
+ hm.xpih_hwmon_dev = hwmon;
+ xrt_subdev_root_request(xdev, XRT_ROOT_HWMON, (void *)&hm);
+}
--
2.27.0
The PCIE device driver which attaches to management function on Alveo
devices. It instantiates one or more group drivers which, in turn,
instantiate xrt drivers. The instantiation of group and xrt drivers is
completely dtb driven.
Signed-off-by: Sonal Santan <[email protected]>
Signed-off-by: Max Zhen <[email protected]>
Signed-off-by: Lizhi Hou <[email protected]>
Reviewed-by: Tom Rix <[email protected]>
---
drivers/fpga/xrt/mgmt/root.c | 418 +++++++++++++++++++++++++++++++++++
1 file changed, 418 insertions(+)
create mode 100644 drivers/fpga/xrt/mgmt/root.c
diff --git a/drivers/fpga/xrt/mgmt/root.c b/drivers/fpga/xrt/mgmt/root.c
new file mode 100644
index 000000000000..566a6fcfbecd
--- /dev/null
+++ b/drivers/fpga/xrt/mgmt/root.c
@@ -0,0 +1,418 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Alveo Management Function Driver
+ *
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Cheng Zhen <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/aer.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+
+#include "xroot.h"
+#include "xmgmt.h"
+#include "metadata.h"
+
+#define XMGMT_MODULE_NAME "xrt-mgmt"
+
+#define XMGMT_PDEV(xm) ((xm)->pdev)
+#define XMGMT_DEV(xm) (&(XMGMT_PDEV(xm)->dev))
+#define xmgmt_err(xm, fmt, args...) \
+ dev_err(XMGMT_DEV(xm), "%s: " fmt, __func__, ##args)
+#define xmgmt_warn(xm, fmt, args...) \
+ dev_warn(XMGMT_DEV(xm), "%s: " fmt, __func__, ##args)
+#define xmgmt_info(xm, fmt, args...) \
+ dev_info(XMGMT_DEV(xm), "%s: " fmt, __func__, ##args)
+#define xmgmt_dbg(xm, fmt, args...) \
+ dev_dbg(XMGMT_DEV(xm), "%s: " fmt, __func__, ##args)
+#define XMGMT_DEV_ID(_pcidev) \
+ ({ typeof(_pcidev) (pcidev) = (_pcidev); \
+ ((pci_domain_nr((pcidev)->bus) << 16) | \
+ PCI_DEVID((pcidev)->bus->number, 0)); })
+#define XRT_VSEC_ID 0x20
+#define XRT_MAX_READRQ 512
+
+static struct class *xmgmt_class;
+
+/* PCI Device IDs */
+/*
+ * Golden image is preloaded on the device when it is shipped to customer.
+ * Then, customer can load other shells (from Xilinx or some other vendor).
+ * If something goes wrong with the shell, customer can always go back to
+ * golden and start over again.
+ */
+#define PCI_DEVICE_ID_U50_GOLDEN 0xD020
+#define PCI_DEVICE_ID_U50 0x5020
+static const struct pci_device_id xmgmt_pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_U50_GOLDEN), }, /* Alveo U50 (golden) */
+ { PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_U50), }, /* Alveo U50 */
+ { 0, }
+};
+
+struct xmgmt {
+ struct pci_dev *pdev;
+ void *root;
+
+ bool ready;
+};
+
+static int xmgmt_config_pci(struct xmgmt *xm)
+{
+ struct pci_dev *pdev = XMGMT_PDEV(xm);
+ int rc;
+
+ rc = pcim_enable_device(pdev);
+ if (rc < 0) {
+ xmgmt_err(xm, "failed to enable device: %d", rc);
+ return rc;
+ }
+
+ rc = pci_enable_pcie_error_reporting(pdev);
+ if (rc)
+ xmgmt_warn(xm, "failed to enable AER: %d", rc);
+
+ pci_set_master(pdev);
+
+ rc = pcie_get_readrq(pdev);
+ if (rc > XRT_MAX_READRQ)
+ pcie_set_readrq(pdev, XRT_MAX_READRQ);
+ return 0;
+}
+
+static int xmgmt_match_slot_and_save(struct device *dev, void *data)
+{
+ struct xmgmt *xm = data;
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ if (XMGMT_DEV_ID(pdev) == XMGMT_DEV_ID(xm->pdev)) {
+ pci_cfg_access_lock(pdev);
+ pci_save_state(pdev);
+ }
+
+ return 0;
+}
+
+static void xmgmt_pci_save_config_all(struct xmgmt *xm)
+{
+ bus_for_each_dev(&pci_bus_type, NULL, xm, xmgmt_match_slot_and_save);
+}
+
+static int xmgmt_match_slot_and_restore(struct device *dev, void *data)
+{
+ struct xmgmt *xm = data;
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ if (XMGMT_DEV_ID(pdev) == XMGMT_DEV_ID(xm->pdev)) {
+ pci_restore_state(pdev);
+ pci_cfg_access_unlock(pdev);
+ }
+
+ return 0;
+}
+
+static void xmgmt_pci_restore_config_all(struct xmgmt *xm)
+{
+ bus_for_each_dev(&pci_bus_type, NULL, xm, xmgmt_match_slot_and_restore);
+}
+
+static void xmgmt_root_hot_reset(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct pci_bus *bus;
+ u16 pci_cmd, devctl;
+ struct xmgmt *xm;
+ u8 pci_bctl;
+ int i, ret;
+
+ xm = pci_get_drvdata(pdev);
+ xmgmt_info(xm, "hot reset start");
+ xmgmt_pci_save_config_all(xm);
+ pci_disable_device(pdev);
+ bus = pdev->bus;
+
+ /*
+ * When flipping the SBR bit, device can fall off the bus. This is
+ * usually no problem at all so long as drivers are working properly
+ * after SBR. However, some systems complain bitterly when the device
+ * falls off the bus.
+ * The quick solution is to temporarily disable the SERR reporting of
+ * switch port during SBR.
+ */
+
+ pci_read_config_word(bus->self, PCI_COMMAND, &pci_cmd);
+ pci_write_config_word(bus->self, PCI_COMMAND, (pci_cmd & ~PCI_COMMAND_SERR));
+ pcie_capability_read_word(bus->self, PCI_EXP_DEVCTL, &devctl);
+ pcie_capability_write_word(bus->self, PCI_EXP_DEVCTL, (devctl & ~PCI_EXP_DEVCTL_FERE));
+ pci_read_config_byte(bus->self, PCI_BRIDGE_CONTROL, &pci_bctl);
+ pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, pci_bctl | PCI_BRIDGE_CTL_BUS_RESET);
+ msleep(100);
+ pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, pci_bctl);
+ ssleep(1);
+
+ pcie_capability_write_word(bus->self, PCI_EXP_DEVCTL, devctl);
+ pci_write_config_word(bus->self, PCI_COMMAND, pci_cmd);
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ xmgmt_err(xm, "failed to enable device, ret %d", ret);
+
+ for (i = 0; i < 300; i++) {
+ pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd);
+ if (pci_cmd != 0xffff)
+ break;
+ msleep(20);
+ }
+ if (i == 300)
+ xmgmt_err(xm, "timed out waiting for device to be online after reset");
+
+ xmgmt_info(xm, "waiting for %d ms", i * 20);
+ xmgmt_pci_restore_config_all(xm);
+ xmgmt_config_pci(xm);
+}
+
+static int xmgmt_add_vsec_node(struct xmgmt *xm, char *dtb)
+{
+ struct pci_dev *pdev = XMGMT_PDEV(xm);
+ struct xrt_md_endpoint ep = { 0 };
+ struct device *dev = DEV(pdev);
+ u32 off_low, off_high, header;
+ int cap = 0, ret = 0;
+ __be32 vsec_bar;
+ __be64 vsec_off;
+
+ while ((cap = pci_find_next_ext_capability(pdev, cap, PCI_EXT_CAP_ID_VNDR))) {
+ pci_read_config_dword(pdev, cap + PCI_VNDR_HEADER, &header);
+ if (PCI_VNDR_HEADER_ID(header) == XRT_VSEC_ID)
+ break;
+ }
+ if (!cap) {
+ xmgmt_info(xm, "No Vendor Specific Capability.");
+ return -ENOENT;
+ }
+
+ if (pci_read_config_dword(pdev, cap + 8, &off_low) ||
+ pci_read_config_dword(pdev, cap + 12, &off_high)) {
+ xmgmt_err(xm, "pci_read vendor specific failed.");
+ return -EINVAL;
+ }
+
+ ep.ep_name = XRT_MD_NODE_VSEC;
+ ret = xrt_md_add_endpoint(dev, dtb, &ep);
+ if (ret) {
+ xmgmt_err(xm, "add vsec metadata failed, ret %d", ret);
+ goto failed;
+ }
+
+ vsec_bar = cpu_to_be32(off_low & 0xf);
+ ret = xrt_md_set_prop(dev, dtb, XRT_MD_NODE_VSEC, NULL,
+ XRT_MD_PROP_BAR_IDX, &vsec_bar, sizeof(vsec_bar));
+ if (ret) {
+ xmgmt_err(xm, "add vsec bar idx failed, ret %d", ret);
+ goto failed;
+ }
+
+ vsec_off = cpu_to_be64(((u64)off_high << 32) | (off_low & ~0xfU));
+ ret = xrt_md_set_prop(dev, dtb, XRT_MD_NODE_VSEC, NULL,
+ XRT_MD_PROP_OFFSET, &vsec_off, sizeof(vsec_off));
+ if (ret) {
+ xmgmt_err(xm, "add vsec offset failed, ret %d", ret);
+ goto failed;
+ }
+
+failed:
+ return ret;
+}
+
+static int xmgmt_create_root_metadata(struct xmgmt *xm, char **root_dtb)
+{
+ char *dtb = NULL;
+ int ret;
+
+ ret = xrt_md_create(XMGMT_DEV(xm), &dtb);
+ if (ret) {
+ xmgmt_err(xm, "create metadata failed, ret %d", ret);
+ goto failed;
+ }
+
+ ret = xmgmt_add_vsec_node(xm, dtb);
+ if (ret == -ENOENT) {
+ /*
+ * We may be dealing with a MFG board.
+ * Try vsec-golden which will bring up all hard-coded leaves
+ * at hard-coded offsets.
+ */
+ ret = xroot_add_simple_node(xm->root, dtb, XRT_MD_NODE_VSEC_GOLDEN);
+ } else if (ret == 0) {
+ ret = xroot_add_simple_node(xm->root, dtb, XRT_MD_NODE_MGMT_MAIN);
+ }
+ if (ret)
+ goto failed;
+
+ *root_dtb = dtb;
+ return 0;
+
+failed:
+ vfree(dtb);
+ return ret;
+}
+
+static ssize_t ready_show(struct device *dev,
+ struct device_attribute *da,
+ char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct xmgmt *xm = pci_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", xm->ready);
+}
+static DEVICE_ATTR_RO(ready);
+
+static struct attribute *xmgmt_root_attrs[] = {
+ &dev_attr_ready.attr,
+ NULL
+};
+
+static struct attribute_group xmgmt_root_attr_group = {
+ .attrs = xmgmt_root_attrs,
+};
+
+static void xmgmt_root_get_id(struct device *dev, struct xrt_root_get_id *rid)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ rid->xpigi_vendor_id = pdev->vendor;
+ rid->xpigi_device_id = pdev->device;
+ rid->xpigi_sub_vendor_id = pdev->subsystem_vendor;
+ rid->xpigi_sub_device_id = pdev->subsystem_device;
+}
+
+static int xmgmt_root_get_resource(struct device *dev, struct xrt_root_get_res *res)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct xmgmt *xm;
+
+ xm = pci_get_drvdata(pdev);
+ if (res->xpigr_region_id > PCI_STD_RESOURCE_END) {
+ xmgmt_err(xm, "Invalid bar idx %d", res->xpigr_region_id);
+ return -EINVAL;
+ }
+
+ res->xpigr_res = &pdev->resource[res->xpigr_region_id];
+ return 0;
+}
+
+static struct xroot_physical_function_callback xmgmt_xroot_pf_cb = {
+ .xpc_get_id = xmgmt_root_get_id,
+ .xpc_get_resource = xmgmt_root_get_resource,
+ .xpc_hot_reset = xmgmt_root_hot_reset,
+};
+
+static int xmgmt_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int ret;
+ struct device *dev = &pdev->dev;
+ struct xmgmt *xm = devm_kzalloc(dev, sizeof(*xm), GFP_KERNEL);
+ char *dtb = NULL;
+
+ if (!xm)
+ return -ENOMEM;
+ xm->pdev = pdev;
+ pci_set_drvdata(pdev, xm);
+
+ ret = xmgmt_config_pci(xm);
+ if (ret)
+ goto failed;
+
+ ret = xroot_probe(&pdev->dev, &xmgmt_xroot_pf_cb, &xm->root);
+ if (ret)
+ goto failed;
+
+ ret = xmgmt_create_root_metadata(xm, &dtb);
+ if (ret)
+ goto failed_metadata;
+
+ ret = xroot_create_group(xm->root, dtb);
+ vfree(dtb);
+ if (ret)
+ xmgmt_err(xm, "failed to create root group: %d", ret);
+
+ if (!xroot_wait_for_bringup(xm->root))
+ xmgmt_err(xm, "failed to bringup all groups");
+ else
+ xm->ready = true;
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &xmgmt_root_attr_group);
+ if (ret) {
+ /* Warning instead of failing the probe. */
+ xmgmt_warn(xm, "create xmgmt root attrs failed: %d", ret);
+ }
+
+ xroot_broadcast(xm->root, XRT_EVENT_POST_CREATION);
+ xmgmt_info(xm, "%s started successfully", XMGMT_MODULE_NAME);
+ return 0;
+
+failed_metadata:
+ xroot_remove(xm->root);
+failed:
+ pci_set_drvdata(pdev, NULL);
+ return ret;
+}
+
+static void xmgmt_remove(struct pci_dev *pdev)
+{
+ struct xmgmt *xm = pci_get_drvdata(pdev);
+
+ xroot_broadcast(xm->root, XRT_EVENT_PRE_REMOVAL);
+ sysfs_remove_group(&pdev->dev.kobj, &xmgmt_root_attr_group);
+ xroot_remove(xm->root);
+ pci_disable_pcie_error_reporting(xm->pdev);
+ xmgmt_info(xm, "%s cleaned up successfully", XMGMT_MODULE_NAME);
+}
+
+static struct pci_driver xmgmt_driver = {
+ .name = XMGMT_MODULE_NAME,
+ .id_table = xmgmt_pci_ids,
+ .probe = xmgmt_probe,
+ .remove = xmgmt_remove,
+};
+
+static int __init xmgmt_init(void)
+{
+ int res = 0;
+
+ res = xmgmt_register_leaf();
+ if (res)
+ return res;
+
+ xmgmt_class = class_create(THIS_MODULE, XMGMT_MODULE_NAME);
+ if (IS_ERR(xmgmt_class))
+ return PTR_ERR(xmgmt_class);
+
+ res = pci_register_driver(&xmgmt_driver);
+ if (res) {
+ class_destroy(xmgmt_class);
+ return res;
+ }
+
+ return 0;
+}
+
+static __exit void xmgmt_exit(void)
+{
+ pci_unregister_driver(&xmgmt_driver);
+ class_destroy(xmgmt_class);
+ xmgmt_unregister_leaf();
+}
+
+module_init(xmgmt_init);
+module_exit(xmgmt_exit);
+
+MODULE_DEVICE_TABLE(pci, xmgmt_pci_ids);
+MODULE_AUTHOR("XRT Team <[email protected]>");
+MODULE_DESCRIPTION("Xilinx Alveo management function driver");
+MODULE_LICENSE("GPL v2");
--
2.27.0
Contains common code for all root drivers and handles root calls from
xrt drivers. This is part of root driver infrastructure.
Signed-off-by: Sonal Santan <[email protected]>
Signed-off-by: Max Zhen <[email protected]>
Signed-off-by: Lizhi Hou <[email protected]>
Reviewed-by: Tom Rix <[email protected]>
---
drivers/fpga/xrt/include/events.h | 45 +++
drivers/fpga/xrt/include/xroot.h | 117 +++++++
drivers/fpga/xrt/lib/subdev_pool.h | 53 +++
drivers/fpga/xrt/lib/xroot.c | 536 +++++++++++++++++++++++++++++
4 files changed, 751 insertions(+)
create mode 100644 drivers/fpga/xrt/include/events.h
create mode 100644 drivers/fpga/xrt/include/xroot.h
create mode 100644 drivers/fpga/xrt/lib/subdev_pool.h
create mode 100644 drivers/fpga/xrt/lib/xroot.c
diff --git a/drivers/fpga/xrt/include/events.h b/drivers/fpga/xrt/include/events.h
new file mode 100644
index 000000000000..775171a47c8e
--- /dev/null
+++ b/drivers/fpga/xrt/include/events.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Cheng Zhen <[email protected]>
+ */
+
+#ifndef _XRT_EVENTS_H_
+#define _XRT_EVENTS_H_
+
+#include "subdev_id.h"
+
+/*
+ * Event notification.
+ */
+enum xrt_events {
+ XRT_EVENT_TEST = 0, /* for testing */
+ /*
+ * Events related to specific subdev
+ * Callback arg: struct xrt_event_arg_subdev
+ */
+ XRT_EVENT_POST_CREATION,
+ XRT_EVENT_PRE_REMOVAL,
+ /*
+ * Events related to change of the whole board
+ * Callback arg: <none>
+ */
+ XRT_EVENT_PRE_HOT_RESET,
+ XRT_EVENT_POST_HOT_RESET,
+ XRT_EVENT_PRE_GATE_CLOSE,
+ XRT_EVENT_POST_GATE_OPEN,
+};
+
+struct xrt_event_arg_subdev {
+ enum xrt_subdev_id xevt_subdev_id;
+ int xevt_subdev_instance;
+};
+
+struct xrt_event {
+ enum xrt_events xe_evt;
+ struct xrt_event_arg_subdev xe_subdev;
+};
+
+#endif /* _XRT_EVENTS_H_ */
diff --git a/drivers/fpga/xrt/include/xroot.h b/drivers/fpga/xrt/include/xroot.h
new file mode 100644
index 000000000000..56461bcb07a9
--- /dev/null
+++ b/drivers/fpga/xrt/include/xroot.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Cheng Zhen <[email protected]>
+ */
+
+#ifndef _XRT_ROOT_H_
+#define _XRT_ROOT_H_
+
+#include "xdevice.h"
+#include "subdev_id.h"
+#include "events.h"
+
+typedef bool (*xrt_subdev_match_t)(enum xrt_subdev_id, struct xrt_device *, void *);
+#define XRT_SUBDEV_MATCH_PREV ((xrt_subdev_match_t)-1)
+#define XRT_SUBDEV_MATCH_NEXT ((xrt_subdev_match_t)-2)
+
+/*
+ * Root calls.
+ */
+enum xrt_root_cmd {
+ /* Leaf actions. */
+ XRT_ROOT_GET_LEAF = 0,
+ XRT_ROOT_PUT_LEAF,
+ XRT_ROOT_GET_LEAF_HOLDERS,
+
+ /* Group actions. */
+ XRT_ROOT_CREATE_GROUP,
+ XRT_ROOT_REMOVE_GROUP,
+ XRT_ROOT_LOOKUP_GROUP,
+ XRT_ROOT_WAIT_GROUP_BRINGUP,
+
+ /* Event actions. */
+ XRT_ROOT_EVENT_SYNC,
+ XRT_ROOT_EVENT_ASYNC,
+
+ /* Device info. */
+ XRT_ROOT_GET_RESOURCE,
+ XRT_ROOT_GET_ID,
+
+ /* Misc. */
+ XRT_ROOT_HOT_RESET,
+ XRT_ROOT_HWMON,
+};
+
+struct xrt_root_get_leaf {
+ struct xrt_device *xpigl_caller_xdev;
+ xrt_subdev_match_t xpigl_match_cb;
+ void *xpigl_match_arg;
+ struct xrt_device *xpigl_tgt_xdev;
+};
+
+struct xrt_root_put_leaf {
+ struct xrt_device *xpipl_caller_xdev;
+ struct xrt_device *xpipl_tgt_xdev;
+};
+
+struct xrt_root_lookup_group {
+ struct xrt_device *xpilp_xdev; /* caller's xdev */
+ xrt_subdev_match_t xpilp_match_cb;
+ void *xpilp_match_arg;
+ int xpilp_grp_inst;
+};
+
+struct xrt_root_get_holders {
+ struct xrt_device *xpigh_xdev; /* caller's xdev */
+ char *xpigh_holder_buf;
+ size_t xpigh_holder_buf_len;
+};
+
+struct xrt_root_get_res {
+ u32 xpigr_region_id;
+ struct resource *xpigr_res;
+};
+
+struct xrt_root_get_id {
+ unsigned short xpigi_vendor_id;
+ unsigned short xpigi_device_id;
+ unsigned short xpigi_sub_vendor_id;
+ unsigned short xpigi_sub_device_id;
+};
+
+struct xrt_root_hwmon {
+ bool xpih_register;
+ const char *xpih_name;
+ void *xpih_drvdata;
+ const struct attribute_group **xpih_groups;
+ struct device *xpih_hwmon_dev;
+};
+
+/*
+ * Callback for leaf to make a root request. Arguments are: parent device, parent cookie, req,
+ * and arg.
+ */
+typedef int (*xrt_subdev_root_cb_t)(struct device *, void *, u32, void *);
+int xrt_subdev_root_request(struct xrt_device *self, u32 cmd, void *arg);
+
+/*
+ * Defines physical function (MPF / UPF) specific operations
+ * needed in common root driver.
+ */
+struct xroot_physical_function_callback {
+ void (*xpc_get_id)(struct device *dev, struct xrt_root_get_id *rid);
+ int (*xpc_get_resource)(struct device *dev, struct xrt_root_get_res *res);
+ void (*xpc_hot_reset)(struct device *dev);
+};
+
+int xroot_probe(struct device *dev, struct xroot_physical_function_callback *cb, void **root);
+void xroot_remove(void *root);
+bool xroot_wait_for_bringup(void *root);
+int xroot_create_group(void *xr, char *dtb);
+int xroot_add_simple_node(void *root, char *dtb, const char *endpoint);
+void xroot_broadcast(void *root, enum xrt_events evt);
+
+#endif /* _XRT_ROOT_H_ */
diff --git a/drivers/fpga/xrt/lib/subdev_pool.h b/drivers/fpga/xrt/lib/subdev_pool.h
new file mode 100644
index 000000000000..03f617d7ffd7
--- /dev/null
+++ b/drivers/fpga/xrt/lib/subdev_pool.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Cheng Zhen <[email protected]>
+ */
+
+#ifndef _XRT_SUBDEV_POOL_H_
+#define _XRT_SUBDEV_POOL_H_
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include "xroot.h"
+
+/*
+ * The struct xrt_subdev_pool manages a list of xrt_subdevs for root and group drivers.
+ */
+struct xrt_subdev_pool {
+ struct list_head xsp_dev_list;
+ struct device *xsp_owner;
+ struct mutex xsp_lock; /* pool lock */
+ bool xsp_closing;
+};
+
+/*
+ * Subdev pool helper functions for root and group drivers only.
+ */
+void xrt_subdev_pool_init(struct device *dev,
+ struct xrt_subdev_pool *spool);
+void xrt_subdev_pool_fini(struct xrt_subdev_pool *spool);
+int xrt_subdev_pool_get(struct xrt_subdev_pool *spool,
+ xrt_subdev_match_t match,
+ void *arg, struct device *holder_dev,
+ struct xrt_device **xdevp);
+int xrt_subdev_pool_put(struct xrt_subdev_pool *spool,
+ struct xrt_device *xdev,
+ struct device *holder_dev);
+int xrt_subdev_pool_add(struct xrt_subdev_pool *spool,
+ enum xrt_subdev_id id, xrt_subdev_root_cb_t pcb,
+ void *pcb_arg, char *dtb);
+int xrt_subdev_pool_del(struct xrt_subdev_pool *spool,
+ enum xrt_subdev_id id, int instance);
+ssize_t xrt_subdev_pool_get_holders(struct xrt_subdev_pool *spool,
+ struct xrt_device *xdev,
+ char *buf, size_t len);
+
+void xrt_subdev_pool_trigger_event(struct xrt_subdev_pool *spool,
+ enum xrt_events evt);
+void xrt_subdev_pool_handle_event(struct xrt_subdev_pool *spool,
+ struct xrt_event *evt);
+
+#endif /* _XRT_SUBDEV_POOL_H_ */
diff --git a/drivers/fpga/xrt/lib/xroot.c b/drivers/fpga/xrt/lib/xroot.c
new file mode 100644
index 000000000000..f324a25e1d4d
--- /dev/null
+++ b/drivers/fpga/xrt/lib/xroot.c
@@ -0,0 +1,536 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Alveo FPGA Root Functions
+ *
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Cheng Zhen <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include "xroot.h"
+#include "subdev_pool.h"
+#include "group.h"
+#include "metadata.h"
+
+#define xroot_err(xr, fmt, args...) dev_err((xr)->dev, "%s: " fmt, __func__, ##args)
+#define xroot_warn(xr, fmt, args...) dev_warn((xr)->dev, "%s: " fmt, __func__, ##args)
+#define xroot_info(xr, fmt, args...) dev_info((xr)->dev, "%s: " fmt, __func__, ##args)
+#define xroot_dbg(xr, fmt, args...) dev_dbg((xr)->dev, "%s: " fmt, __func__, ##args)
+
+#define XROOT_GROUP_FIRST (-1)
+#define XROOT_GROUP_LAST (-2)
+
+static int xroot_root_cb(struct device *, void *, u32, void *);
+
+struct xroot_evt {
+ struct list_head list;
+ struct xrt_event evt;
+ struct completion comp;
+ bool async;
+};
+
+struct xroot_events {
+ struct mutex evt_lock; /* event lock */
+ struct list_head evt_list;
+ struct work_struct evt_work;
+};
+
+struct xroot_groups {
+ struct xrt_subdev_pool pool;
+ struct work_struct bringup_work;
+ atomic_t bringup_pending_cnt;
+ atomic_t bringup_failed_cnt;
+ struct completion bringup_comp;
+};
+
+struct xroot {
+ struct device *dev;
+ struct xroot_events events;
+ struct xroot_groups groups;
+ struct xroot_physical_function_callback pf_cb;
+};
+
+struct xroot_group_match_arg {
+ enum xrt_subdev_id id;
+ int instance;
+};
+
+static bool xroot_group_match(enum xrt_subdev_id id, struct xrt_device *xdev, void *arg)
+{
+ struct xroot_group_match_arg *a = (struct xroot_group_match_arg *)arg;
+
+ /* xdev->instance is the instance of the subdev. */
+ return id == a->id && xdev->instance == a->instance;
+}
+
+static int xroot_get_group(struct xroot *xr, int instance, struct xrt_device **grpp)
+{
+ int rc = 0;
+ struct xrt_subdev_pool *grps = &xr->groups.pool;
+ struct device *dev = xr->dev;
+ struct xroot_group_match_arg arg = { XRT_SUBDEV_GRP, instance };
+
+ if (instance == XROOT_GROUP_LAST) {
+ rc = xrt_subdev_pool_get(grps, XRT_SUBDEV_MATCH_NEXT,
+ *grpp, dev, grpp);
+ } else if (instance == XROOT_GROUP_FIRST) {
+ rc = xrt_subdev_pool_get(grps, XRT_SUBDEV_MATCH_PREV,
+ *grpp, dev, grpp);
+ } else {
+ rc = xrt_subdev_pool_get(grps, xroot_group_match,
+ &arg, dev, grpp);
+ }
+
+ if (rc && rc != -ENOENT)
+ xroot_err(xr, "failed to hold group %d: %d", instance, rc);
+ return rc;
+}
+
+static void xroot_put_group(struct xroot *xr, struct xrt_device *grp)
+{
+ int inst = grp->instance;
+ int rc = xrt_subdev_pool_put(&xr->groups.pool, grp, xr->dev);
+
+ if (rc)
+ xroot_err(xr, "failed to release group %d: %d", inst, rc);
+}
+
+static int xroot_trigger_event(struct xroot *xr, struct xrt_event *e, bool async)
+{
+ struct xroot_evt *enew = vzalloc(sizeof(*enew));
+
+ if (!enew)
+ return -ENOMEM;
+
+ enew->evt = *e;
+ enew->async = async;
+ init_completion(&enew->comp);
+
+ mutex_lock(&xr->events.evt_lock);
+ list_add(&enew->list, &xr->events.evt_list);
+ mutex_unlock(&xr->events.evt_lock);
+
+ schedule_work(&xr->events.evt_work);
+
+ if (async)
+ return 0;
+
+ wait_for_completion(&enew->comp);
+ vfree(enew);
+ return 0;
+}
+
+static void
+xroot_group_trigger_event(struct xroot *xr, int inst, enum xrt_events e)
+{
+ int ret;
+ struct xrt_device *xdev = NULL;
+ struct xrt_event evt = { 0 };
+
+ WARN_ON(inst < 0);
+ /* Only triggers subdev specific events. */
+ if (e != XRT_EVENT_POST_CREATION && e != XRT_EVENT_PRE_REMOVAL) {
+ xroot_err(xr, "invalid event %d", e);
+ return;
+ }
+
+ ret = xroot_get_group(xr, inst, &xdev);
+ if (ret)
+ return;
+
+ /* Triggers event for children, first. */
+ xleaf_call(xdev, XRT_GROUP_TRIGGER_EVENT, (void *)(uintptr_t)e);
+
+ /* Triggers event for itself. */
+ evt.xe_evt = e;
+ evt.xe_subdev.xevt_subdev_id = XRT_SUBDEV_GRP;
+ evt.xe_subdev.xevt_subdev_instance = inst;
+ xroot_trigger_event(xr, &evt, false);
+
+ xroot_put_group(xr, xdev);
+}
+
+int xroot_create_group(void *root, char *dtb)
+{
+ struct xroot *xr = (struct xroot *)root;
+ int ret;
+
+ atomic_inc(&xr->groups.bringup_pending_cnt);
+ ret = xrt_subdev_pool_add(&xr->groups.pool, XRT_SUBDEV_GRP, xroot_root_cb, xr, dtb);
+ if (ret >= 0) {
+ schedule_work(&xr->groups.bringup_work);
+ } else {
+ atomic_dec(&xr->groups.bringup_pending_cnt);
+ atomic_inc(&xr->groups.bringup_failed_cnt);
+ xroot_err(xr, "failed to create group: %d", ret);
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(xroot_create_group);
+
+static int xroot_destroy_single_group(struct xroot *xr, int instance)
+{
+ struct xrt_device *xdev = NULL;
+ int ret;
+
+ WARN_ON(instance < 0);
+ ret = xroot_get_group(xr, instance, &xdev);
+ if (ret)
+ return ret;
+
+ xroot_group_trigger_event(xr, instance, XRT_EVENT_PRE_REMOVAL);
+
+ /* Now tear down all children in this group. */
+ ret = xleaf_call(xdev, XRT_GROUP_FINI_CHILDREN, NULL);
+ xroot_put_group(xr, xdev);
+ if (!ret)
+ ret = xrt_subdev_pool_del(&xr->groups.pool, XRT_SUBDEV_GRP, instance);
+
+ return ret;
+}
+
+static int xroot_destroy_group(struct xroot *xr, int instance)
+{
+ struct xrt_device *target = NULL;
+ struct xrt_device *deps = NULL;
+ int ret;
+
+ WARN_ON(instance < 0);
+ /*
+ * Make sure target group exists and can't go away before
+ * we remove it's dependents
+ */
+ ret = xroot_get_group(xr, instance, &target);
+ if (ret)
+ return ret;
+
+ /*
+ * Remove all groups depend on target one.
+ * Assuming subdevs in higher group ID can depend on ones in
+ * lower ID groups, we remove them in the reservse order.
+ */
+ while (xroot_get_group(xr, XROOT_GROUP_LAST, &deps) != -ENOENT) {
+ int inst = deps->instance;
+
+ xroot_put_group(xr, deps);
+ /* Reached the target group instance, stop here. */
+ if (instance == inst)
+ break;
+ xroot_destroy_single_group(xr, inst);
+ deps = NULL;
+ }
+
+ /* Now we can remove the target group. */
+ xroot_put_group(xr, target);
+ return xroot_destroy_single_group(xr, instance);
+}
+
+static int xroot_lookup_group(struct xroot *xr,
+ struct xrt_root_lookup_group *arg)
+{
+ int rc = -ENOENT;
+ struct xrt_device *grp = NULL;
+
+ while (rc < 0 && xroot_get_group(xr, XROOT_GROUP_LAST, &grp) != -ENOENT) {
+ if (arg->xpilp_match_cb(XRT_SUBDEV_GRP, grp, arg->xpilp_match_arg))
+ rc = grp->instance;
+ xroot_put_group(xr, grp);
+ }
+ return rc;
+}
+
+static void xroot_event_work(struct work_struct *work)
+{
+ struct xroot_evt *tmp;
+ struct xroot *xr = container_of(work, struct xroot, events.evt_work);
+
+ mutex_lock(&xr->events.evt_lock);
+ while (!list_empty(&xr->events.evt_list)) {
+ tmp = list_first_entry(&xr->events.evt_list, struct xroot_evt, list);
+ list_del(&tmp->list);
+ mutex_unlock(&xr->events.evt_lock);
+
+ xrt_subdev_pool_handle_event(&xr->groups.pool, &tmp->evt);
+
+ if (tmp->async)
+ vfree(tmp);
+ else
+ complete(&tmp->comp);
+
+ mutex_lock(&xr->events.evt_lock);
+ }
+ mutex_unlock(&xr->events.evt_lock);
+}
+
+static void xroot_event_init(struct xroot *xr)
+{
+ INIT_LIST_HEAD(&xr->events.evt_list);
+ mutex_init(&xr->events.evt_lock);
+ INIT_WORK(&xr->events.evt_work, xroot_event_work);
+}
+
+static void xroot_event_fini(struct xroot *xr)
+{
+ flush_scheduled_work();
+ WARN_ON(!list_empty(&xr->events.evt_list));
+}
+
+static int xroot_get_leaf(struct xroot *xr, struct xrt_root_get_leaf *arg)
+{
+ int rc = -ENOENT;
+ struct xrt_device *grp = NULL;
+
+ while (rc && xroot_get_group(xr, XROOT_GROUP_LAST, &grp) != -ENOENT) {
+ rc = xleaf_call(grp, XRT_GROUP_GET_LEAF, arg);
+ xroot_put_group(xr, grp);
+ }
+ return rc;
+}
+
+static int xroot_put_leaf(struct xroot *xr, struct xrt_root_put_leaf *arg)
+{
+ int rc = -ENOENT;
+ struct xrt_device *grp = NULL;
+
+ while (rc && xroot_get_group(xr, XROOT_GROUP_LAST, &grp) != -ENOENT) {
+ rc = xleaf_call(grp, XRT_GROUP_PUT_LEAF, arg);
+ xroot_put_group(xr, grp);
+ }
+ return rc;
+}
+
+static int xroot_root_cb(struct device *dev, void *parg, enum xrt_root_cmd cmd, void *arg)
+{
+ struct xroot *xr = (struct xroot *)parg;
+ int rc = 0;
+
+ switch (cmd) {
+ /* Leaf actions. */
+ case XRT_ROOT_GET_LEAF: {
+ struct xrt_root_get_leaf *getleaf = (struct xrt_root_get_leaf *)arg;
+
+ rc = xroot_get_leaf(xr, getleaf);
+ break;
+ }
+ case XRT_ROOT_PUT_LEAF: {
+ struct xrt_root_put_leaf *putleaf = (struct xrt_root_put_leaf *)arg;
+
+ rc = xroot_put_leaf(xr, putleaf);
+ break;
+ }
+ case XRT_ROOT_GET_LEAF_HOLDERS: {
+ struct xrt_root_get_holders *holders = (struct xrt_root_get_holders *)arg;
+
+ rc = xrt_subdev_pool_get_holders(&xr->groups.pool,
+ holders->xpigh_xdev,
+ holders->xpigh_holder_buf,
+ holders->xpigh_holder_buf_len);
+ break;
+ }
+
+ /* Group actions. */
+ case XRT_ROOT_CREATE_GROUP:
+ rc = xroot_create_group(xr, (char *)arg);
+ break;
+ case XRT_ROOT_REMOVE_GROUP:
+ rc = xroot_destroy_group(xr, (int)(uintptr_t)arg);
+ break;
+ case XRT_ROOT_LOOKUP_GROUP: {
+ struct xrt_root_lookup_group *getgrp = (struct xrt_root_lookup_group *)arg;
+
+ rc = xroot_lookup_group(xr, getgrp);
+ break;
+ }
+ case XRT_ROOT_WAIT_GROUP_BRINGUP:
+ rc = xroot_wait_for_bringup(xr) ? 0 : -EINVAL;
+ break;
+
+ /* Event actions. */
+ case XRT_ROOT_EVENT_SYNC:
+ case XRT_ROOT_EVENT_ASYNC: {
+ bool async = (cmd == XRT_ROOT_EVENT_ASYNC);
+ struct xrt_event *evt = (struct xrt_event *)arg;
+
+ rc = xroot_trigger_event(xr, evt, async);
+ break;
+ }
+
+ /* Device info. */
+ case XRT_ROOT_GET_RESOURCE: {
+ struct xrt_root_get_res *res = (struct xrt_root_get_res *)arg;
+
+ if (xr->pf_cb.xpc_get_resource) {
+ rc = xr->pf_cb.xpc_get_resource(xr->dev, res);
+ } else {
+ xroot_err(xr, "get resource is not supported");
+ rc = -EOPNOTSUPP;
+ }
+ break;
+ }
+ case XRT_ROOT_GET_ID: {
+ struct xrt_root_get_id *id = (struct xrt_root_get_id *)arg;
+
+ if (xr->pf_cb.xpc_get_id)
+ xr->pf_cb.xpc_get_id(xr->dev, id);
+ else
+ memset(id, 0, sizeof(*id));
+ break;
+ }
+
+ /* MISC generic root driver functions. */
+ case XRT_ROOT_HOT_RESET: {
+ if (xr->pf_cb.xpc_hot_reset) {
+ xr->pf_cb.xpc_hot_reset(xr->dev);
+ } else {
+ xroot_err(xr, "hot reset is not supported");
+ rc = -EOPNOTSUPP;
+ }
+ break;
+ }
+ case XRT_ROOT_HWMON: {
+ struct xrt_root_hwmon *hwmon = (struct xrt_root_hwmon *)arg;
+
+ if (hwmon->xpih_register) {
+ hwmon->xpih_hwmon_dev =
+ hwmon_device_register_with_info(xr->dev,
+ hwmon->xpih_name,
+ hwmon->xpih_drvdata,
+ NULL,
+ hwmon->xpih_groups);
+ } else {
+ hwmon_device_unregister(hwmon->xpih_hwmon_dev);
+ }
+ break;
+ }
+
+ default:
+ xroot_err(xr, "unknown IOCTL cmd %d", cmd);
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static void xroot_bringup_group_work(struct work_struct *work)
+{
+ struct xrt_device *xdev = NULL;
+ struct xroot *xr = container_of(work, struct xroot, groups.bringup_work);
+
+ while (xroot_get_group(xr, XROOT_GROUP_FIRST, &xdev) != -ENOENT) {
+ int r, i;
+
+ i = xdev->instance;
+ r = xleaf_call(xdev, XRT_GROUP_INIT_CHILDREN, NULL);
+ xroot_put_group(xr, xdev);
+ if (r == -EEXIST)
+ continue; /* Already brought up, nothing to do. */
+ if (r)
+ atomic_inc(&xr->groups.bringup_failed_cnt);
+
+ xroot_group_trigger_event(xr, i, XRT_EVENT_POST_CREATION);
+
+ if (atomic_dec_and_test(&xr->groups.bringup_pending_cnt))
+ complete(&xr->groups.bringup_comp);
+ }
+}
+
+static void xroot_groups_init(struct xroot *xr)
+{
+ xrt_subdev_pool_init(xr->dev, &xr->groups.pool);
+ INIT_WORK(&xr->groups.bringup_work, xroot_bringup_group_work);
+ atomic_set(&xr->groups.bringup_pending_cnt, 0);
+ atomic_set(&xr->groups.bringup_failed_cnt, 0);
+ init_completion(&xr->groups.bringup_comp);
+}
+
+static void xroot_groups_fini(struct xroot *xr)
+{
+ flush_scheduled_work();
+ xrt_subdev_pool_fini(&xr->groups.pool);
+}
+
+int xroot_add_simple_node(void *root, char *dtb, const char *endpoint)
+{
+ struct xroot *xr = (struct xroot *)root;
+ struct device *dev = xr->dev;
+ struct xrt_md_endpoint ep = { 0 };
+ int ret = 0;
+
+ ep.ep_name = endpoint;
+ ret = xrt_md_add_endpoint(dev, dtb, &ep);
+ if (ret)
+ xroot_err(xr, "add %s failed, ret %d", endpoint, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(xroot_add_simple_node);
+
+bool xroot_wait_for_bringup(void *root)
+{
+ struct xroot *xr = (struct xroot *)root;
+
+ wait_for_completion(&xr->groups.bringup_comp);
+ return atomic_read(&xr->groups.bringup_failed_cnt) == 0;
+}
+EXPORT_SYMBOL_GPL(xroot_wait_for_bringup);
+
+int xroot_probe(struct device *dev, struct xroot_physical_function_callback *cb, void **root)
+{
+ struct xroot *xr = NULL;
+
+ dev_info(dev, "%s: probing...", __func__);
+
+ xr = devm_kzalloc(dev, sizeof(*xr), GFP_KERNEL);
+ if (!xr)
+ return -ENOMEM;
+
+ xr->dev = dev;
+ xr->pf_cb = *cb;
+ xroot_groups_init(xr);
+ xroot_event_init(xr);
+
+ *root = xr;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xroot_probe);
+
+void xroot_remove(void *root)
+{
+ struct xroot *xr = (struct xroot *)root;
+ struct xrt_device *grp = NULL;
+
+ xroot_info(xr, "leaving...");
+
+ if (xroot_get_group(xr, XROOT_GROUP_FIRST, &grp) == 0) {
+ int instance = grp->instance;
+
+ xroot_put_group(xr, grp);
+ xroot_destroy_group(xr, instance);
+ }
+
+ xroot_event_fini(xr);
+ xroot_groups_fini(xr);
+}
+EXPORT_SYMBOL_GPL(xroot_remove);
+
+void xroot_broadcast(void *root, enum xrt_events evt)
+{
+ struct xroot *xr = (struct xroot *)root;
+ struct xrt_event e = { 0 };
+
+ /* Root pf driver only broadcasts below two events. */
+ if (evt != XRT_EVENT_POST_CREATION && evt != XRT_EVENT_PRE_REMOVAL) {
+ xroot_info(xr, "invalid event %d", evt);
+ return;
+ }
+
+ e.xe_evt = evt;
+ e.xe_subdev.xevt_subdev_id = XRT_ROOT;
+ e.xe_subdev.xevt_subdev_instance = 0;
+ xroot_trigger_event(xr, &e, false);
+}
+EXPORT_SYMBOL_GPL(xroot_broadcast);
--
2.27.0
Describe XRT driver architecture and provide basic overview of
Xilinx Alveo platform.
Signed-off-by: Sonal Santan <[email protected]>
Signed-off-by: Max Zhen <[email protected]>
Signed-off-by: Lizhi Hou <[email protected]>
Reviewed-by: Tom Rix <[email protected]>
---
Documentation/fpga/index.rst | 1 +
Documentation/fpga/xrt.rst | 870 +++++++++++++++++++++++++++++++++++
MAINTAINERS | 11 +
3 files changed, 882 insertions(+)
create mode 100644 Documentation/fpga/xrt.rst
diff --git a/Documentation/fpga/index.rst b/Documentation/fpga/index.rst
index f80f95667ca2..30134357b70d 100644
--- a/Documentation/fpga/index.rst
+++ b/Documentation/fpga/index.rst
@@ -8,6 +8,7 @@ fpga
:maxdepth: 1
dfl
+ xrt
.. only:: subproject and html
diff --git a/Documentation/fpga/xrt.rst b/Documentation/fpga/xrt.rst
new file mode 100644
index 000000000000..84eb41be9ac1
--- /dev/null
+++ b/Documentation/fpga/xrt.rst
@@ -0,0 +1,870 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==================================
+XRTV2 Linux Kernel Driver Overview
+==================================
+
+Authors:
+
+* Sonal Santan <[email protected]>
+* Max Zhen <[email protected]>
+* Lizhi Hou <[email protected]>
+
+XRTV2 drivers are second generation `XRT <https://github.com/Xilinx/XRT>`_
+drivers which support `Alveo <https://www.xilinx.com/products/boards-and-kits/alveo.html>`_
+PCIe platforms from Xilinx.
+
+XRTV2 drivers support *subsystem* style data driven platforms where driver's
+configuration and behavior are determined by metadata provided by the platform
+(in *device tree* format). Primary management physical function (MPF) driver
+is called **xrt-mgmt**. Primary user physical function (UPF) driver is called
+**xrt-user** and is under development. xrt_driver framework and HW subsystem
+drivers are packaged into a library module called **xrt-lib**, which is shared
+by **xrt-mgmt** and **xrt-user** (under development). The xrt_driver framework
+implements a ``bus_type`` called **xrt_bus_type** which is used to discover HW
+subsystems and facilitate inter HW subsystem interaction.
+
+Driver Modules
+==============
+
+xrt-lib.ko
+----------
+
+xrt-lib is the repository of all subsystem drivers and pure software modules that
+can potentially be shared between xrt-mgmt and xrt-user. All these drivers are
+structured as **xrt_driver** and are instantiated by xrt-mgmt (or xrt-user under
+development) based on the metadata associated with the hardware. The metadata is
+in the form of a device tree as mentioned before. Each xrt_driver statically
+defines a subsystem node array by using a node name or a string in its ``.endpoints``
+property. And this array is eventually translated to IOMEM resources in the
+instantiated **xrt_device**.
+
+The xrt-lib infrastructure provides hooks to xrt_drivers for device node
+management, user file operations and ioctl callbacks. The core infrastructure also
+provides a bus functionality called **xrt_bus_type** for xrt_driver registration,
+discovery and inter xrt_driver calls. xrt-lib does not have any dependency on PCIe
+subsystem.
+
+.. note::
+ See code in ``include/xleaf.h`` and ``include/xdevice.h``
+
+
+xrt-mgmt.ko
+------------
+
+The xrt-mgmt driver is a PCIe device driver driving MPF found on Xilinx's Alveo
+PCIe device. It consists of one *root* driver, one or more *group* drivers
+and one or more *xleaf* drivers. The group and xleaf drivers are instantiations
+of the xrt_driver but are called group and xleaf to symbolize the logical operation
+performed by them.
+
+The root driver manages the life cycle of multiple group drivers, which, in turn,
+manages multiple xleaf drivers. This flexibility allows xrt-mgmt.ko and xrt-lib.ko
+to support various HW subsystems exposed by different Alveo shells. The differences
+among these Alveo shells is handled in xleaf drivers. The root and group
+drivers are part of the infrastructure which provide common services to xleaf
+drivers found on various Alveo shells. See :ref:`alveo_platform_overview`.
+
+The instantiation of specific group driver or xleaf drivers is completely data
+driven based on metadata (mostly in device tree format) found through VSEC
+capability and inside the firmware files, such as platform xsabin or user xclbin
+file.
+
+
+Driver Object Model
+===================
+
+The driver object model looks like the following::
+
+ +-----------+
+ | xroot |
+ +-----+-----+
+ |
+ +-----------+-----------+
+ | |
+ v v
+ +-----------+ +-----------+
+ | group | ... | group |
+ +-----+-----+ +------+----+
+ | |
+ | |
+ +-----+----+ +-----+----+
+ | | | |
+ v v v v
+ +-------+ +-------+ +-------+ +-------+
+ | xleaf |..| xleaf | | xleaf |..| xleaf |
+ +-------+ +-------+ +-------+ +-------+
+
+As an example, for Xilinx Alveo U50 before user xclbin download, the tree
+looks like the following::
+
+ +-----------+
+ | xrt-mgmt |
+ +-----+-----+
+ |
+ +-------------------------+--------------------+
+ | | |
+ v v v
+ +--------+ +--------+ +--------+
+ | group0 | | group1 | | group2 |
+ +----+---+ +----+---+ +---+----+
+ | | |
+ | | |
+ +-----+-----+ +----+-----+---+ +-----+-----+----+--------+
+ | | | | | | | | |
+ v v | v v | v v |
+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ |
+ | xmgmt_main | | VSEC | | | GPIO | | QSPI | | | CMC | | AXI-GATE0 | |
+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ |
+ | +---------+ | +------+ +-----------+ |
+ +>| MAILBOX | +->| ICAP | | AXI-GATE1 |<+
+ +---------+ | +------+ +-----------+
+ | +-------+
+ +->| CALIB |
+ +-------+
+
+After a xclbin is downloaded, group3 will be added and the tree looks like the
+following::
+
+ +-----------+
+ | xrt-mgmt |
+ +-----+-----+
+ |
+ +-------------------------+--------------------+-----------------+
+ | | | |
+ v v v |
+ +--------+ +--------+ +--------+ |
+ | group0 | | group1 | | group2 | |
+ +----+---+ +----+---+ +---+----+ |
+ | | | |
+ | | | |
+ +-----+-----+ +-----+-----+---+ +-----+-----+----+--------+ |
+ | | | | | | | | | |
+ v v | v v | v v | |
+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ | |
+ | xmgmt_main | | VSEC | | | GPIO | | QSPI | | | CMC | | AXI-GATE0 | | |
+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ | |
+ | +---------+ | +------+ +-----------+ | |
+ +>| MAILBOX | +->| ICAP | | AXI-GATE1 |<+ |
+ +---------+ | +------+ +-----------+ |
+ | +-------+ |
+ +->| CALIB | |
+ +-------+ |
+ +---+----+ |
+ | group3 |<--------------------------------------------+
+ +--------+
+ |
+ |
+ +-------+--------+---+--+--------+------+-------+
+ | | | | | | |
+ v | v | v | v
+ +--------+ | +--------+ | +--------+ | +-----+
+ | CLOCK0 | | | CLOCK1 | | | CLOCK2 | | | UCS |
+ +--------+ v +--------+ v +--------+ v +-----+
+ +-------------+ +-------------+ +-------------+
+ | CLOCK-FREQ0 | | CLOCK-FREQ1 | | CLOCK-FREQ2 |
+ +-------------+ +-------------+ +-------------+
+
+
+root
+----
+
+The root driver is a PCIe device driver attached to MPF. It's part of the
+infrastructure of the MPF driver and resides in xrt-mgmt.ko. This driver
+
+* manages one or more group drivers
+* provides access to functionalities that requires pci_dev, such as PCIE config
+ space access, to other xleaf drivers through root calls
+* facilities inter xleaf driver calls for other xleaf drivers
+* facilities event callbacks for other xleaf drivers
+
+When the root driver starts, it will explicitly create an initial group instance,
+which contains xleaf drivers that will trigger the creation of other group
+instances. The root driver will wait for all group and xleaf drivers to be
+created before it returns from its probe routine and claim success of the
+initialization of the entire xrt-mgmt driver. If any xleaf fails to initialize
+the xrt-mgmt driver will still come online but with limited functionality.
+
+.. note::
+ See code in ``lib/xroot.c`` and ``mgmt/root.c``
+
+
+group
+-----
+
+The group driver represents a pseudo device whose life cycle is managed by
+root and does not have real IO mem or IRQ resources. It's part of the
+infrastructure of the MPF driver and resides in xrt-lib.ko. This driver
+
+* manages one or more xleaf drivers
+* provides access to root from xleaf drivers, so that root calls, event
+ notifications and inter xleaf calls can happen
+
+In xrt-mgmt, an initial group driver instance will be created by the root. This
+instance contains xleaf drivers that will trigger group instances to be created
+to manage groups of xleaf drivers found on different partitions of hardware,
+such as VSEC, Shell, and User.
+
+Every *fpga_region* has a group driver associated with it. The group driver is
+created when a xclbin image is loaded on the fpga_region. The existing group
+is destroyed when a new xclbin image is loaded. The fpga_region persists
+across xclbin downloads.
+
+.. note::
+ See code in ``lib/group.c``
+
+
+xleaf
+-----
+
+The xleaf driver is a xrt_driver whose life cycle is managed by
+a group driver and may or may not have real IO mem or IRQ resources. They
+manage HW subsystems they are attached to.
+
+A xleaf driver without real hardware resources manages in-memory states for
+xrt-mgmt. These states are shareable by other xleaf drivers.
+
+Xleaf drivers assigned to specific hardware resources drive a specific subsystem
+in the device. To manipulate the subsystem or carry out a task, a xleaf driver
+may ask for help from the root via root calls and/or from other leaves via
+inter xleaf calls.
+
+A xleaf can also broadcast events through infrastructure code for other leaves
+to process. It can also receive event notification from infrastructure about
+certain events, such as post-creation or pre-exit of a particular xleaf.
+
+.. note::
+ See code in ``lib/xleaf/*.c``
+
+
+xrt_bus_type
+------------
+
+xrt_bus_type defines a virtual bus which handles xrt_driver probe, remove and match
+operations. All xrt_drivers register with xrt_bus_type as part of xrt-lib driver
+``module_init`` and un-register as part of xrt-lib driver ``module_exit``.
+
+.. note::
+ See code in ``lib/lib-drv.c``
+
+FPGA Manager Interaction
+========================
+
+fpga_manager
+------------
+
+An instance of fpga_manager is created by xmgmt_main and is used for xclbin
+image download. fpga_manager requires the full xclbin image before it can
+start programming the FPGA configuration engine via Internal Configuration
+Access Port (ICAP) xrt_driver.
+
+fpga_region
+-----------
+
+For every interface exposed by the currently loaded xclbin/xsabin in the
+*parent* fpga_region a new instance of fpga_region is created like a *child*
+fpga_region. The device tree of the *parent* fpga_region defines the
+resources for a new instance of fpga_bridge which isolates the parent from
+child fpga_region. This new instance of fpga_bridge will be used when a
+xclbin image is loaded on the child fpga_region. After the xclbin image is
+downloaded to the fpga_region, an instance of a group is created for the
+fpga_region using the device tree obtained as part of the xclbin. If this
+device tree defines any child interfaces, it can trigger the creation of
+fpga_bridge and fpga_region for the next region in the chain.
+
+fpga_bridge
+-----------
+
+Like the fpga_region, an fpga_bridge is created by walking the device tree
+of the parent group. The bridge is used for isolation between a parent and
+its child.
+
+Driver Interfaces
+=================
+
+xrt-mgmt Driver Ioctls
+----------------------
+
+Ioctls exposed by the xrt-mgmt driver to user space are enumerated in the
+following table:
+
+== ===================== ============================ ==========================
+# Functionality ioctl request code data format
+== ===================== ============================ ==========================
+1 FPGA image download XMGMT_IOCICAPDOWNLOAD_AXLF xmgmt_ioc_bitstream_axlf
+== ===================== ============================ ==========================
+
+A user xclbin can be downloaded by using the xbmgmt tool from the XRT open source
+suite. See example usage below::
+
+ xbmgmt partition --program --path /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/test/verify.xclbin --force
+
+xrt-mgmt Driver Sysfs
+----------------------
+
+The xrt-mgmt driver exposes a rich set of sysfs interfaces. Subsystem xrt
+drivers export sysfs node for every platform instance.
+
+Every partition also exports its UUIDs. See below for examples::
+
+ /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/interface_uuids
+ /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/logic_uuids
+
+
+hwmon
+-----
+
+The xrt-mgmt driver exposes standard hwmon interface to report voltage, current,
+temperature, power, etc. These can easily be viewed using *sensors* command line
+utility.
+
+.. _alveo_platform_overview:
+
+Alveo Platform Overview
+=======================
+
+Alveo platforms are architected as two physical FPGA partitions: *Shell* and
+*User*. The Shell provides basic infrastructure for the Alveo platform like
+PCIe connectivity, board management, Dynamic Function Exchange (DFX), sensors,
+clocking, reset, and security. DFX, partial reconfiguration, is responsible for
+loading the user compiled FPGA binary.
+
+For DFX to work properly, physical partitions require strict HW compatibility
+with each other. Every physical partition has two interface UUIDs: the *parent*
+UUID and the *child* UUID. For simple single stage platforms, Shell → User forms
+the parent child relationship.
+
+.. note::
+ Partition compatibility matching is a key design component of the Alveo platforms
+ and XRT. Partitions have child and parent relationship. A loaded partition
+ exposes child partition UUID to advertise its compatibility requirement. When
+ loading a child partition, the xrt-mgmt driver matches the parent
+ UUID of the child partition against the child UUID exported by the parent.
+ The parent and child partition UUIDs are stored in the *xclbin* (for the user)
+ and the *xsabin* (for the shell). Except for the root UUID exported by VSEC,
+ the hardware itself does not know about the UUIDs. The UUIDs are stored in
+ xsabin and xclbin. The image format has a special node called Partition UUIDs
+ which define the compatibility UUIDs. See :ref:`partition_uuids`.
+
+
+The physical partitions and their loading are illustrated below::
+
+ SHELL USER
+ +-----------+ +-------------------+
+ | | | |
+ | VSEC UUID | CHILD PARENT | LOGIC UUID |
+ | o------->|<--------o |
+ | | UUID UUID | |
+ +-----+-----+ +--------+----------+
+ | |
+ . .
+ | |
+ +---+---+ +------+--------+
+ | POR | | USER COMPILED |
+ | FLASH | | XCLBIN |
+ +-------+ +---------------+
+
+
+Loading Sequence
+----------------
+
+The Shell partition is loaded from flash at system boot time. It establishes the
+PCIe link and exposes two physical functions to the BIOS. After the OS boots,
+the xrt-mgmt driver attaches to the PCIe physical function 0 exposed by the Shell
+and then looks for VSEC in the PCIe extended configuration space. Using VSEC, it
+determines the logic UUID of the Shell and uses the UUID to load matching *xsabin*
+file from Linux firmware directory. The xsabin file contains the metadata to
+discover the peripherals that are part of the Shell and the firmware for any
+embedded soft processors in the Shell. The xsabin file also contains Partition
+UUIDs as described here :ref:`partition_uuids`.
+
+The Shell exports a child interface UUID which is used for the compatibility
+check when loading the user compiled xclbin over the User partition as part of DFX.
+When a user requests loading of a specific xclbin, the xrt-mgmt driver reads
+the parent interface UUID specified in the xclbin and matches it with the child
+interface UUID exported by the Shell to determine if the xclbin is compatible with
+the Shell. If the match fails, loading of xclbin is denied.
+
+xclbin loading is requested using the ICAP_DOWNLOAD_AXLF ioctl command. When loading
+a xclbin, the xrt-mgmt driver performs the following *logical* operations:
+
+1. Copy xclbin from user to kernel memory
+2. Sanity check the xclbin contents
+3. Isolate the User partition
+4. Download the bitstream using the FPGA config engine (ICAP)
+5. De-isolate the User partition
+6. Program the clocks (ClockWiz) driving the User partition
+7. Wait for the memory controller (MIG) calibration
+8. Return the loading status back to the caller
+
+`Platform Loading Overview <https://xilinx.github.io/XRT/master/html/platforms_partitions.html>`_
+provides more detailed information on platform loading.
+
+
+xsabin
+------
+
+Each Alveo platform comes packaged with its own xsabin. The xsabin is a trusted
+component of the platform. For format details refer to :ref:`xsabin_xclbin_container_format`
+below. xsabin contains basic information like UUIDs, platform name and metadata in the
+form of device tree. See :ref:`device_tree_usage` below for details and example.
+
+xclbin
+------
+
+xclbin is compiled by end user using
+`Vitis <https://www.xilinx.com/products/design-tools/vitis/vitis-platform.html>`_
+tool set from Xilinx. The xclbin contains sections describing user compiled
+acceleration engines/kernels, memory subsystems, clocking information etc. It also
+contains an FPGA bitstream for the user partition, UUIDs, platform name, etc.
+
+
+.. _xsabin_xclbin_container_format:
+
+xsabin/xclbin Container Format
+------------------------------
+
+xclbin/xsabin is ELF-like binary container format. It is structured as series of
+sections. There is a file header followed by several section headers which is
+followed by sections. A section header points to an actual section. There is an
+optional signature at the end. The format is defined by the header file ``xclbin.h``.
+The following figure illustrates a typical xclbin::
+
+
+ +---------------------+
+ | |
+ | HEADER |
+ +---------------------+
+ | SECTION HEADER |
+ | |
+ +---------------------+
+ | ... |
+ | |
+ +---------------------+
+ | SECTION HEADER |
+ | |
+ +---------------------+
+ | SECTION |
+ | |
+ +---------------------+
+ | ... |
+ | |
+ +---------------------+
+ | SECTION |
+ | |
+ +---------------------+
+ | SIGNATURE |
+ | (OPTIONAL) |
+ +---------------------+
+
+
+xclbin/xsabin files can be packaged, un-packaged and inspected using an XRT
+utility called **xclbinutil**. xclbinutil is part of the XRT open source
+software stack. The source code for xclbinutil can be found at
+https://github.com/Xilinx/XRT/tree/master/src/runtime_src/tools/xclbinutil
+
+For example, to enumerate the contents of a xclbin/xsabin use the *--info* switch
+as shown below::
+
+
+ xclbinutil --info --input /opt/xilinx/firmware/u50/gen3x16-xdma/blp/test/bandwidth.xclbin
+ xclbinutil --info --input /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/partition.xsabin
+
+
+.. _device_tree_usage:
+
+Device Tree Usage
+-----------------
+
+The xsabin file stores metadata which advertise HW subsystems present in a
+partition. The metadata is stored in device tree format with a well defined
+schema. XRT management driver uses this information to bind *xrt_drivers* to
+the subsystem instantiations. The xrt_drivers are found in **xrt-lib.ko** kernel
+module.
+
+Logic UUID
+^^^^^^^^^^
+A partition is identified uniquely through ``logic_uuid`` property::
+
+ /dts-v1/;
+ / {
+ logic_uuid = "0123456789abcdef0123456789abcdef";
+ ...
+ }
+
+Schema Version
+^^^^^^^^^^^^^^
+Schema version is defined through the ``schema_version`` node. It contains
+``major`` and ``minor`` properties as below::
+
+ /dts-v1/;
+ / {
+ schema_version {
+ major = <0x01>;
+ minor = <0x00>;
+ };
+ ...
+ }
+
+.. _partition_uuids:
+
+Partition UUIDs
+^^^^^^^^^^^^^^^
+Each partition may have parent and child UUIDs. These UUIDs are
+defined by ``interfaces`` node and ``interface_uuid`` property::
+
+ /dts-v1/;
+ / {
+ interfaces {
+ @0 {
+ interface_uuid = "0123456789abcdef0123456789abcdef";
+ };
+ @1 {
+ interface_uuid = "fedcba9876543210fedcba9876543210";
+ };
+ ...
+ };
+ ...
+ }
+
+
+Subsystem Instantiations
+^^^^^^^^^^^^^^^^^^^^^^^^
+Subsystem instantiations are captured as children of ``addressable_endpoints``
+node::
+
+ /dts-v1/;
+ / {
+ addressable_endpoints {
+ abc {
+ ...
+ };
+ def {
+ ...
+ };
+ ...
+ }
+ }
+
+Subnode 'abc' and 'def' are the name of subsystem nodes
+
+Subsystem Node
+^^^^^^^^^^^^^^
+Each subsystem node and its properties define a hardware instance::
+
+
+ addressable_endpoints {
+ abc {
+ reg = <0x00 0x1f05000 0x00 0x1000>>
+ pcie_physical_function = <0x0>;
+ pcie_bar_mapping = <0x2>;
+ compatible = "abc def";
+ interrupts = <0x09 0x0c>;
+ firmware {
+ firmware_product_name = "abc"
+ firmware_branch_name = "def"
+ firmware_version_major = <1>
+ firmware_version_minor = <2>
+ };
+ }
+ ...
+ }
+
+:reg:
+ Property defines an address range. `<0x00 0x1f05000 0x00 0x1000>` indicates
+ *0x00 0x1f05000* as BAR offset and *0x00 0x1000* as address length.
+:pcie_physical_function:
+ Property specifies which PCIe physical function the subsystem node resides.
+ `<0x0>` implies physical function 0.
+:pcie_bar_mapping:
+ Property specifies which PCIe BAR the subsystem node resides. `<0x2>` implies
+ BAR 2. A value of 0 means the property is not defined.
+:compatible:
+ Property is a list of strings. The first string in the list specifies the exact
+ subsystem node. The following strings represent other devices that the device
+ is compatible with.
+:interrupts:
+ Property specifies start and end interrupts for this subsystem node.
+ `<0x09 0x0c>` implies interrupts 9 to 13 are used by this subsystem.
+:firmware:
+ Subnode defines the firmware required by this subsystem node.
+
+Alveo U50 Platform Example
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+::
+
+ /dts-v1/;
+
+ /{
+ logic_uuid = "f465b0a3ae8c64f619bc150384ace69b";
+
+ schema_version {
+ major = <0x01>;
+ minor = <0x00>;
+ };
+
+ interfaces {
+
+ @0 {
+ interface_uuid = "862c7020a250293e32036f19956669e5";
+ };
+ };
+
+ addressable_endpoints {
+
+ ep_blp_rom_00 {
+ reg = <0x00 0x1f04000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
+ };
+
+ ep_card_flash_program_00 {
+ reg = <0x00 0x1f06000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_quad_spi-1.0\0axi_quad_spi";
+ interrupts = <0x03 0x03>;
+ };
+
+ ep_cmc_firmware_mem_00 {
+ reg = <0x00 0x1e20000 0x00 0x20000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
+
+ firmware {
+ firmware_product_name = "cmc";
+ firmware_branch_name = "u50";
+ firmware_version_major = <0x01>;
+ firmware_version_minor = <0x00>;
+ };
+ };
+
+ ep_cmc_intc_00 {
+ reg = <0x00 0x1e03000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";
+ interrupts = <0x04 0x04>;
+ };
+
+ ep_cmc_mutex_00 {
+ reg = <0x00 0x1e02000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
+ };
+
+ ep_cmc_regmap_00 {
+ reg = <0x00 0x1e08000 0x00 0x2000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
+
+ firmware {
+ firmware_product_name = "sc-fw";
+ firmware_branch_name = "u50";
+ firmware_version_major = <0x05>;
+ };
+ };
+
+ ep_cmc_reset_00 {
+ reg = <0x00 0x1e01000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
+ };
+
+ ep_ddr_mem_calib_00 {
+ reg = <0x00 0x63000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
+ };
+
+ ep_debug_bscan_mgmt_00 {
+ reg = <0x00 0x1e90000 0x00 0x10000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-debug_bridge-1.0\0debug_bridge";
+ };
+
+ ep_ert_base_address_00 {
+ reg = <0x00 0x21000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
+ };
+
+ ep_ert_command_queue_mgmt_00 {
+ reg = <0x00 0x40000 0x00 0x10000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-ert_command_queue-1.0\0ert_command_queue";
+ };
+
+ ep_ert_command_queue_user_00 {
+ reg = <0x00 0x40000 0x00 0x10000>;
+ pcie_physical_function = <0x01>;
+ compatible = "xilinx.com,reg_abs-ert_command_queue-1.0\0ert_command_queue";
+ };
+
+ ep_ert_firmware_mem_00 {
+ reg = <0x00 0x30000 0x00 0x8000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
+
+ firmware {
+ firmware_product_name = "ert";
+ firmware_branch_name = "v20";
+ firmware_version_major = <0x01>;
+ };
+ };
+
+ ep_ert_intc_00 {
+ reg = <0x00 0x23000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";
+ interrupts = <0x05 0x05>;
+ };
+
+ ep_ert_reset_00 {
+ reg = <0x00 0x22000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
+ };
+
+ ep_ert_sched_00 {
+ reg = <0x00 0x50000 0x00 0x1000>;
+ pcie_physical_function = <0x01>;
+ compatible = "xilinx.com,reg_abs-ert_sched-1.0\0ert_sched";
+ interrupts = <0x09 0x0c>;
+ };
+
+ ep_fpga_configuration_00 {
+ reg = <0x00 0x1e88000 0x00 0x8000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_hwicap-1.0\0axi_hwicap";
+ interrupts = <0x02 0x02>;
+ };
+
+ ep_icap_reset_00 {
+ reg = <0x00 0x1f07000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
+ };
+
+ ep_msix_00 {
+ reg = <0x00 0x00 0x00 0x20000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-msix-1.0\0msix";
+ pcie_bar_mapping = <0x02>;
+ };
+
+ ep_pcie_link_mon_00 {
+ reg = <0x00 0x1f05000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
+ };
+
+ ep_pr_isolate_plp_00 {
+ reg = <0x00 0x1f01000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
+ };
+
+ ep_pr_isolate_ulp_00 {
+ reg = <0x00 0x1000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
+ };
+
+ ep_uuid_rom_00 {
+ reg = <0x00 0x64000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
+ };
+
+ ep_xdma_00 {
+ reg = <0x00 0x00 0x00 0x10000>;
+ pcie_physical_function = <0x01>;
+ compatible = "xilinx.com,reg_abs-xdma-1.0\0xdma";
+ pcie_bar_mapping = <0x02>;
+ };
+ };
+
+ }
+
+
+
+Deployment Models
+=================
+
+Baremetal
+---------
+
+In bare-metal deployments, both MPF and UPF are visible and accessible. The
+xrt-mgmt driver binds to MPF. The xrt-mgmt driver operations are privileged and
+available to system administrator. The full stack is illustrated below::
+
+ HOST
+
+ [XRT-MGMT] [XRT-USER]
+ | |
+ | |
+ +-----+ +-----+
+ | MPF | | UPF |
+ | | | |
+ | PF0 | | PF1 |
+ +--+--+ +--+--+
+ ......... ^................. ^..........
+ | |
+ | PCIe DEVICE |
+ | |
+ +--+------------------+--+
+ | SHELL |
+ | |
+ +------------------------+
+ | USER |
+ | |
+ | |
+ | |
+ | |
+ +------------------------+
+
+
+
+Virtualized
+-----------
+
+In virtualized deployments, the privileged MPF is assigned to the host but the
+unprivileged UPF is assigned to a guest VM via PCIe pass-through. The xrt-mgmt
+driver in host binds to MPF. The xrt-mgmt driver operations are privileged and
+only accessible to the MPF. The full stack is illustrated below::
+
+
+ ..............
+ HOST . VM .
+ . .
+ [XRT-MGMT] . [XRT-USER] .
+ | . | .
+ | . | .
+ +-----+ . +-----+ .
+ | MPF | . | UPF | .
+ | | . | | .
+ | PF0 | . | PF1 | .
+ +--+--+ . +--+--+ .
+ ......... ^................. ^..........
+ | |
+ | PCIe DEVICE |
+ | |
+ +--+------------------+--+
+ | SHELL |
+ | |
+ +------------------------+
+ | USER |
+ | |
+ | |
+ | |
+ | |
+ +------------------------+
+
+
+
+
+
+Platform Security Considerations
+================================
+
+`Security of Alveo Platform <https://xilinx.github.io/XRT/master/html/security.html>`_
+discusses the deployment options and security implications in great detail.
diff --git a/MAINTAINERS b/MAINTAINERS
index 056966c9aac9..beeaf0257364 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7274,6 +7274,17 @@ F: Documentation/fpga/
F: drivers/fpga/
F: include/linux/fpga/
+FPGA XRT DRIVERS
+M: Lizhi Hou <[email protected]>
+R: Max Zhen <[email protected]>
+R: Sonal Santan <[email protected]>
+L: [email protected]
+S: Supported
+W: https://github.com/Xilinx/XRT
+F: Documentation/fpga/xrt.rst
+F: drivers/fpga/xrt/
+F: include/uapi/linux/xrt/
+
FPU EMULATOR
M: Bill Metzenthen <[email protected]>
S: Maintained
--
2.27.0
xrt driver that handles IOCTLs, such as hot reset and xclbin download.
Signed-off-by: Sonal Santan <[email protected]>
Signed-off-by: Max Zhen <[email protected]>
Signed-off-by: Lizhi Hou <[email protected]>
Reviewed-by: Tom Rix <[email protected]>
---
drivers/fpga/xrt/include/xmgmt-main.h | 34 ++
drivers/fpga/xrt/mgmt/xmgmt-main.c | 662 ++++++++++++++++++++++++++
drivers/fpga/xrt/mgmt/xmgmt.h | 33 ++
3 files changed, 729 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xmgmt-main.h
create mode 100644 drivers/fpga/xrt/mgmt/xmgmt-main.c
create mode 100644 drivers/fpga/xrt/mgmt/xmgmt.h
diff --git a/drivers/fpga/xrt/include/xmgmt-main.h b/drivers/fpga/xrt/include/xmgmt-main.h
new file mode 100644
index 000000000000..e7e95a839c12
--- /dev/null
+++ b/drivers/fpga/xrt/include/xmgmt-main.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Cheng Zhen <[email protected]>
+ */
+
+#ifndef _XMGMT_MAIN_H_
+#define _XMGMT_MAIN_H_
+
+#include <linux/fpga-xrt.h>
+#include "xleaf.h"
+
+enum xrt_mgmt_main_leaf_cmd {
+ XRT_MGMT_MAIN_GET_AXLF_SECTION = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
+ XRT_MGMT_MAIN_GET_VBNV,
+};
+
+/* There are three kind of partitions. Each of them is programmed independently. */
+enum provider_kind {
+ XMGMT_BLP, /* Base Logic Partition */
+ XMGMT_PLP, /* Provider Logic Partition */
+ XMGMT_ULP, /* User Logic Partition */
+};
+
+struct xrt_mgmt_main_get_axlf_section {
+ enum provider_kind xmmigas_axlf_kind;
+ enum axlf_section_kind xmmigas_section_kind;
+ void *xmmigas_section;
+ u64 xmmigas_section_size;
+};
+
+#endif /* _XMGMT_MAIN_H_ */
diff --git a/drivers/fpga/xrt/mgmt/xmgmt-main.c b/drivers/fpga/xrt/mgmt/xmgmt-main.c
new file mode 100644
index 000000000000..7275da8e5b01
--- /dev/null
+++ b/drivers/fpga/xrt/mgmt/xmgmt-main.c
@@ -0,0 +1,662 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Alveo FPGA MGMT PF entry point driver
+ *
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Sonal Santan <[email protected]>
+ */
+
+#include <linux/firmware.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include "xclbin-helper.h"
+#include "metadata.h"
+#include "xleaf.h"
+#include <linux/fpga-xrt.h>
+#include "xleaf/devctl.h"
+#include "xmgmt-main.h"
+#include "xrt-mgr.h"
+#include "xleaf/icap.h"
+#include "xleaf/axigate.h"
+#include "xmgmt.h"
+
+#define XMGMT_MAIN "xmgmt_main"
+#define XMGMT_SUPP_XCLBIN_MAJOR 2
+
+#define XMGMT_FLAG_FLASH_READY 1
+#define XMGMT_FLAG_DEVCTL_READY 2
+
+#define XMGMT_UUID_STR_LEN (UUID_SIZE * 2 + 1)
+
+struct xmgmt_main {
+ struct xrt_device *xdev;
+ struct axlf *firmware_blp;
+ struct axlf *firmware_plp;
+ struct axlf *firmware_ulp;
+ u32 flags;
+ struct fpga_manager *fmgr;
+ struct mutex lock; /* busy lock */
+ uuid_t *blp_interface_uuids;
+ u32 blp_interface_uuid_num;
+};
+
+/*
+ * VBNV stands for Vendor, BoardID, Name, Version. It is a string
+ * which describes board and shell.
+ *
+ * Caller is responsible for freeing the returned string.
+ */
+char *xmgmt_get_vbnv(struct xrt_device *xdev)
+{
+ struct xmgmt_main *xmm = xrt_get_drvdata(xdev);
+ const char *vbnv;
+ char *ret;
+ int i;
+
+ if (xmm->firmware_plp)
+ vbnv = xmm->firmware_plp->header.platform_vbnv;
+ else if (xmm->firmware_blp)
+ vbnv = xmm->firmware_blp->header.platform_vbnv;
+ else
+ return NULL;
+
+ ret = kstrdup(vbnv, GFP_KERNEL);
+ if (!ret)
+ return NULL;
+
+ for (i = 0; i < strlen(ret); i++) {
+ if (ret[i] == ':' || ret[i] == '.')
+ ret[i] = '_';
+ }
+ return ret;
+}
+
+static int get_dev_uuid(struct xrt_device *xdev, char *uuidstr, size_t len)
+{
+ struct xrt_devctl_rw devctl_arg = { 0 };
+ struct xrt_device *devctl_leaf;
+ char uuid_buf[UUID_SIZE];
+ uuid_t uuid;
+ int err;
+
+ devctl_leaf = xleaf_get_leaf_by_epname(xdev, XRT_MD_NODE_BLP_ROM);
+ if (!devctl_leaf) {
+ xrt_err(xdev, "can not get %s", XRT_MD_NODE_BLP_ROM);
+ return -EINVAL;
+ }
+
+ devctl_arg.xdr_id = XRT_DEVCTL_ROM_UUID;
+ devctl_arg.xdr_buf = uuid_buf;
+ devctl_arg.xdr_len = sizeof(uuid_buf);
+ devctl_arg.xdr_offset = 0;
+ err = xleaf_call(devctl_leaf, XRT_DEVCTL_READ, &devctl_arg);
+ xleaf_put_leaf(xdev, devctl_leaf);
+ if (err) {
+ xrt_err(xdev, "can not get uuid: %d", err);
+ return err;
+ }
+ import_uuid(&uuid, uuid_buf);
+ xrt_md_trans_uuid2str(&uuid, uuidstr);
+
+ return 0;
+}
+
+int xmgmt_hot_reset(struct xrt_device *xdev)
+{
+ int ret = xleaf_broadcast_event(xdev, XRT_EVENT_PRE_HOT_RESET, false);
+
+ if (ret) {
+ xrt_err(xdev, "offline failed, hot reset is canceled");
+ return ret;
+ }
+
+ xleaf_hot_reset(xdev);
+ xleaf_broadcast_event(xdev, XRT_EVENT_POST_HOT_RESET, false);
+ return 0;
+}
+
+static ssize_t reset_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct xrt_device *xdev = to_xrt_dev(dev);
+
+ xmgmt_hot_reset(xdev);
+ return count;
+}
+static DEVICE_ATTR_WO(reset);
+
+static ssize_t VBNV_show(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct xrt_device *xdev = to_xrt_dev(dev);
+ ssize_t ret;
+ char *vbnv;
+
+ vbnv = xmgmt_get_vbnv(xdev);
+ if (!vbnv)
+ return -EINVAL;
+ ret = sprintf(buf, "%s\n", vbnv);
+ kfree(vbnv);
+ return ret;
+}
+static DEVICE_ATTR_RO(VBNV);
+
+/* logic uuid is the uuid uniquely identify the partition */
+static ssize_t logic_uuids_show(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct xrt_device *xdev = to_xrt_dev(dev);
+ char uuid[XMGMT_UUID_STR_LEN];
+ ssize_t ret;
+
+ /* Getting UUID pointed to by VSEC, should be the same as logic UUID of BLP. */
+ ret = get_dev_uuid(xdev, uuid, sizeof(uuid));
+ if (ret)
+ return ret;
+ ret = sprintf(buf, "%s\n", uuid);
+ return ret;
+}
+static DEVICE_ATTR_RO(logic_uuids);
+
+static ssize_t interface_uuids_show(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct xrt_device *xdev = to_xrt_dev(dev);
+ struct xmgmt_main *xmm = xrt_get_drvdata(xdev);
+ ssize_t ret = 0;
+ u32 i;
+
+ for (i = 0; i < xmm->blp_interface_uuid_num; i++) {
+ char uuidstr[XMGMT_UUID_STR_LEN];
+
+ xrt_md_trans_uuid2str(&xmm->blp_interface_uuids[i], uuidstr);
+ ret += sprintf(buf + ret, "%s\n", uuidstr);
+ }
+ return ret;
+}
+static DEVICE_ATTR_RO(interface_uuids);
+
+static struct attribute *xmgmt_main_attrs[] = {
+ &dev_attr_reset.attr,
+ &dev_attr_VBNV.attr,
+ &dev_attr_logic_uuids.attr,
+ &dev_attr_interface_uuids.attr,
+ NULL,
+};
+
+static const struct attribute_group xmgmt_main_attrgroup = {
+ .attrs = xmgmt_main_attrs,
+};
+
+static int load_firmware_from_disk(struct xrt_device *xdev, struct axlf **fw_buf, size_t *len)
+{
+ char uuid[XMGMT_UUID_STR_LEN];
+ const struct firmware *fw;
+ char fw_name[256];
+ int err = 0;
+
+ *len = 0;
+ err = get_dev_uuid(xdev, uuid, sizeof(uuid));
+ if (err)
+ return err;
+
+ snprintf(fw_name, sizeof(fw_name), "xilinx/%s/partition.xsabin", uuid);
+ xrt_info(xdev, "try loading fw: %s", fw_name);
+
+ err = request_firmware(&fw, fw_name, DEV(xdev));
+ if (err)
+ return err;
+
+ *fw_buf = vmalloc(fw->size);
+ if (!*fw_buf) {
+ release_firmware(fw);
+ return -ENOMEM;
+ }
+
+ *len = fw->size;
+ memcpy(*fw_buf, fw->data, fw->size);
+
+ release_firmware(fw);
+ return 0;
+}
+
+static const struct axlf *xmgmt_get_axlf_firmware(struct xmgmt_main *xmm, enum provider_kind kind)
+{
+ switch (kind) {
+ case XMGMT_BLP:
+ return xmm->firmware_blp;
+ case XMGMT_PLP:
+ return xmm->firmware_plp;
+ case XMGMT_ULP:
+ return xmm->firmware_ulp;
+ default:
+ xrt_err(xmm->xdev, "unknown axlf kind: %d", kind);
+ return NULL;
+ }
+}
+
+/* The caller needs to free the returned dtb buffer */
+char *xmgmt_get_dtb(struct xrt_device *xdev, enum provider_kind kind)
+{
+ struct xmgmt_main *xmm = xrt_get_drvdata(xdev);
+ const struct axlf *provider;
+ char *dtb = NULL;
+ int rc;
+
+ provider = xmgmt_get_axlf_firmware(xmm, kind);
+ if (!provider)
+ return dtb;
+
+ rc = xrt_xclbin_get_metadata(DEV(xdev), provider, &dtb);
+ if (rc)
+ xrt_err(xdev, "failed to find dtb: %d", rc);
+ return dtb;
+}
+
+/* The caller needs to free the returned uuid buffer */
+static const char *get_uuid_from_firmware(struct xrt_device *xdev, const struct axlf *xclbin)
+{
+ const void *uuiddup = NULL;
+ const void *uuid = NULL;
+ void *dtb = NULL;
+ int rc;
+
+ rc = xrt_xclbin_get_section(DEV(xdev), xclbin, PARTITION_METADATA, &dtb, NULL);
+ if (rc)
+ return NULL;
+
+ rc = xrt_md_get_prop(DEV(xdev), dtb, NULL, NULL, XRT_MD_PROP_LOGIC_UUID, &uuid, NULL);
+ if (!rc)
+ uuiddup = kstrdup(uuid, GFP_KERNEL);
+ vfree(dtb);
+ return uuiddup;
+}
+
+static bool is_valid_firmware(struct xrt_device *xdev,
+ const struct axlf *xclbin, size_t fw_len)
+{
+ const char *fw_buf = (const char *)xclbin;
+ size_t axlflen = xclbin->header.length;
+ char dev_uuid[XMGMT_UUID_STR_LEN];
+ const char *fw_uuid;
+ int err;
+
+ err = get_dev_uuid(xdev, dev_uuid, sizeof(dev_uuid));
+ if (err)
+ return false;
+
+ if (memcmp(fw_buf, XCLBIN_VERSION2, sizeof(XCLBIN_VERSION2)) != 0) {
+ xrt_err(xdev, "unknown fw format");
+ return false;
+ }
+
+ if (axlflen > fw_len) {
+ xrt_err(xdev, "truncated fw, length: %zu, expect: %zu", fw_len, axlflen);
+ return false;
+ }
+
+ if (xclbin->header.version_major != XMGMT_SUPP_XCLBIN_MAJOR) {
+ xrt_err(xdev, "firmware is not supported");
+ return false;
+ }
+
+ fw_uuid = get_uuid_from_firmware(xdev, xclbin);
+ if (!fw_uuid || strncmp(fw_uuid, dev_uuid, sizeof(dev_uuid)) != 0) {
+ xrt_err(xdev, "bad fw UUID: %s, expect: %s",
+ fw_uuid ? fw_uuid : "<none>", dev_uuid);
+ kfree(fw_uuid);
+ return false;
+ }
+
+ kfree(fw_uuid);
+ return true;
+}
+
+int xmgmt_get_provider_uuid(struct xrt_device *xdev, enum provider_kind kind, uuid_t *uuid)
+{
+ struct xmgmt_main *xmm = xrt_get_drvdata(xdev);
+ const struct axlf *fwbuf;
+ const char *fw_uuid;
+ int rc = -ENOENT;
+
+ mutex_lock(&xmm->lock);
+
+ fwbuf = xmgmt_get_axlf_firmware(xmm, kind);
+ if (!fwbuf)
+ goto done;
+
+ fw_uuid = get_uuid_from_firmware(xdev, fwbuf);
+ if (!fw_uuid)
+ goto done;
+
+ rc = xrt_md_trans_str2uuid(DEV(xdev), fw_uuid, uuid);
+ kfree(fw_uuid);
+
+done:
+ mutex_unlock(&xmm->lock);
+ return rc;
+}
+
+static int xmgmt_create_blp(struct xmgmt_main *xmm)
+{
+ const struct axlf *provider = xmgmt_get_axlf_firmware(xmm, XMGMT_BLP);
+ struct xrt_device *xdev = xmm->xdev;
+ int rc = 0;
+ char *dtb = NULL;
+
+ dtb = xmgmt_get_dtb(xdev, XMGMT_BLP);
+ if (!dtb) {
+ xrt_err(xdev, "did not get BLP metadata");
+ return -EINVAL;
+ }
+
+ rc = xmgmt_process_xclbin(xmm->xdev, xmm->fmgr, provider, XMGMT_BLP);
+ if (rc) {
+ xrt_err(xdev, "failed to process BLP: %d", rc);
+ goto failed;
+ }
+
+ rc = xleaf_create_group(xdev, dtb);
+ if (rc < 0) {
+ xrt_err(xdev, "failed to create BLP group: %d", rc);
+ goto failed;
+ }
+
+ WARN_ON(xmm->blp_interface_uuids);
+ rc = xrt_md_get_interface_uuids(&xdev->dev, dtb, 0, NULL);
+ if (rc > 0) {
+ xmm->blp_interface_uuid_num = rc;
+ xmm->blp_interface_uuids =
+ kcalloc(xmm->blp_interface_uuid_num, sizeof(uuid_t), GFP_KERNEL);
+ if (!xmm->blp_interface_uuids) {
+ rc = -ENOMEM;
+ goto failed;
+ }
+ xrt_md_get_interface_uuids(&xdev->dev, dtb, xmm->blp_interface_uuid_num,
+ xmm->blp_interface_uuids);
+ }
+
+failed:
+ vfree(dtb);
+ return rc;
+}
+
+static int xmgmt_load_firmware(struct xmgmt_main *xmm)
+{
+ struct xrt_device *xdev = xmm->xdev;
+ size_t fwlen;
+ int rc;
+
+ rc = load_firmware_from_disk(xdev, &xmm->firmware_blp, &fwlen);
+ if (!rc && is_valid_firmware(xdev, xmm->firmware_blp, fwlen))
+ xmgmt_create_blp(xmm);
+ else
+ xrt_err(xdev, "failed to find firmware, giving up: %d", rc);
+ return rc;
+}
+
+static void xmgmt_main_event_cb(struct xrt_device *xdev, void *arg)
+{
+ struct xmgmt_main *xmm = xrt_get_drvdata(xdev);
+ struct xrt_event *evt = (struct xrt_event *)arg;
+ enum xrt_events e = evt->xe_evt;
+ struct xrt_device *leaf;
+ enum xrt_subdev_id id;
+
+ id = evt->xe_subdev.xevt_subdev_id;
+ switch (e) {
+ case XRT_EVENT_POST_CREATION: {
+ if (id == XRT_SUBDEV_DEVCTL && !(xmm->flags & XMGMT_FLAG_DEVCTL_READY)) {
+ leaf = xleaf_get_leaf_by_epname(xdev, XRT_MD_NODE_BLP_ROM);
+ if (leaf) {
+ xmm->flags |= XMGMT_FLAG_DEVCTL_READY;
+ xleaf_put_leaf(xdev, leaf);
+ }
+ } else if (id == XRT_SUBDEV_QSPI && !(xmm->flags & XMGMT_FLAG_FLASH_READY)) {
+ xmm->flags |= XMGMT_FLAG_FLASH_READY;
+ } else {
+ break;
+ }
+
+ if (xmm->flags & XMGMT_FLAG_DEVCTL_READY)
+ xmgmt_load_firmware(xmm);
+ break;
+ }
+ case XRT_EVENT_PRE_REMOVAL:
+ break;
+ default:
+ xrt_dbg(xdev, "ignored event %d", e);
+ break;
+ }
+}
+
+static int xmgmt_main_probe(struct xrt_device *xdev)
+{
+ struct xmgmt_main *xmm;
+
+ xrt_info(xdev, "probing...");
+
+ xmm = devm_kzalloc(DEV(xdev), sizeof(*xmm), GFP_KERNEL);
+ if (!xmm)
+ return -ENOMEM;
+
+ xmm->xdev = xdev;
+ xmm->fmgr = xmgmt_fmgr_probe(xdev);
+ if (IS_ERR(xmm->fmgr))
+ return PTR_ERR(xmm->fmgr);
+
+ xrt_set_drvdata(xdev, xmm);
+ mutex_init(&xmm->lock);
+
+ /* Ready to handle req thru sysfs nodes. */
+ if (sysfs_create_group(&DEV(xdev)->kobj, &xmgmt_main_attrgroup))
+ xrt_err(xdev, "failed to create sysfs group");
+ return 0;
+}
+
+static void xmgmt_main_remove(struct xrt_device *xdev)
+{
+ struct xmgmt_main *xmm = xrt_get_drvdata(xdev);
+
+ /* By now, group driver should prevent any inter-leaf call. */
+
+ xrt_info(xdev, "leaving...");
+
+ kfree(xmm->blp_interface_uuids);
+ vfree(xmm->firmware_blp);
+ vfree(xmm->firmware_plp);
+ vfree(xmm->firmware_ulp);
+ xmgmt_region_cleanup_all(xdev);
+ xmgmt_fmgr_remove(xmm->fmgr);
+ sysfs_remove_group(&DEV(xdev)->kobj, &xmgmt_main_attrgroup);
+}
+
+static int
+xmgmt_mainleaf_call(struct xrt_device *xdev, u32 cmd, void *arg)
+{
+ struct xmgmt_main *xmm = xrt_get_drvdata(xdev);
+ int ret = 0;
+
+ switch (cmd) {
+ case XRT_XLEAF_EVENT:
+ xmgmt_main_event_cb(xdev, arg);
+ break;
+ case XRT_MGMT_MAIN_GET_AXLF_SECTION: {
+ struct xrt_mgmt_main_get_axlf_section *get =
+ (struct xrt_mgmt_main_get_axlf_section *)arg;
+ const struct axlf *firmware = xmgmt_get_axlf_firmware(xmm, get->xmmigas_axlf_kind);
+
+ if (!firmware) {
+ ret = -ENOENT;
+ } else {
+ ret = xrt_xclbin_get_section(DEV(xdev), firmware,
+ get->xmmigas_section_kind,
+ &get->xmmigas_section,
+ &get->xmmigas_section_size);
+ }
+ break;
+ }
+ case XRT_MGMT_MAIN_GET_VBNV: {
+ char **vbnv_p = (char **)arg;
+
+ *vbnv_p = xmgmt_get_vbnv(xdev);
+ if (!*vbnv_p)
+ ret = -EINVAL;
+ break;
+ }
+ default:
+ xrt_err(xdev, "unknown cmd: %d", cmd);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int xmgmt_main_open(struct inode *inode, struct file *file)
+{
+ struct xrt_device *xdev = xleaf_devnode_open(inode);
+
+ /* Device may have gone already when we get here. */
+ if (!xdev)
+ return -ENODEV;
+
+ xrt_info(xdev, "opened");
+ file->private_data = xrt_get_drvdata(xdev);
+ return 0;
+}
+
+static int xmgmt_main_close(struct inode *inode, struct file *file)
+{
+ struct xmgmt_main *xmm = file->private_data;
+
+ xleaf_devnode_close(inode);
+
+ xrt_info(xmm->xdev, "closed");
+ return 0;
+}
+
+/*
+ * Called for xclbin download xclbin load ioctl.
+ */
+static int xmgmt_bitstream_axlf_fpga_mgr(struct xmgmt_main *xmm, void *axlf, size_t size)
+{
+ int ret;
+
+ WARN_ON(!mutex_is_locked(&xmm->lock));
+
+ /*
+ * Should any error happens during download, we can't trust
+ * the cached xclbin any more.
+ */
+ vfree(xmm->firmware_ulp);
+ xmm->firmware_ulp = NULL;
+
+ ret = xmgmt_process_xclbin(xmm->xdev, xmm->fmgr, axlf, XMGMT_ULP);
+ if (ret == 0)
+ xmm->firmware_ulp = axlf;
+
+ return ret;
+}
+
+static int bitstream_axlf_ioctl(struct xmgmt_main *xmm, const void __user *arg)
+{
+ struct xmgmt_ioc_bitstream_axlf ioc_obj = { 0 };
+ struct axlf xclbin_obj = { {0} };
+ const void __user *xclbin;
+ size_t copy_buffer_size = 0;
+ void *copy_buffer = NULL;
+ int ret = 0;
+
+ if (copy_from_user((void *)&ioc_obj, arg, sizeof(ioc_obj)))
+ return -EFAULT;
+ xclbin = (const void __user *)ioc_obj.xclbin;
+ if (copy_from_user((void *)&xclbin_obj, xclbin, sizeof(xclbin_obj)))
+ return -EFAULT;
+ if (memcmp(xclbin_obj.magic, XCLBIN_VERSION2, sizeof(XCLBIN_VERSION2)))
+ return -EINVAL;
+
+ copy_buffer_size = xclbin_obj.header.length;
+ if (copy_buffer_size > XCLBIN_MAX_SZ_1G || copy_buffer_size < sizeof(xclbin_obj))
+ return -EINVAL;
+ if (xclbin_obj.header.version_major != XMGMT_SUPP_XCLBIN_MAJOR)
+ return -EINVAL;
+
+ copy_buffer = vmalloc(copy_buffer_size);
+ if (!copy_buffer)
+ return -ENOMEM;
+
+ if (copy_from_user(copy_buffer, xclbin, copy_buffer_size)) {
+ vfree(copy_buffer);
+ return -EFAULT;
+ }
+
+ ret = xmgmt_bitstream_axlf_fpga_mgr(xmm, copy_buffer, copy_buffer_size);
+ if (ret)
+ vfree(copy_buffer);
+
+ return ret;
+}
+
+static long xmgmt_main_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct xmgmt_main *xmm = filp->private_data;
+ long result = 0;
+
+ if (_IOC_TYPE(cmd) != XMGMT_IOC_MAGIC)
+ return -ENOTTY;
+
+ mutex_lock(&xmm->lock);
+
+ xrt_info(xmm->xdev, "ioctl cmd %d, arg %ld", cmd, arg);
+ switch (cmd) {
+ case XMGMT_IOCICAPDOWNLOAD_AXLF:
+ result = bitstream_axlf_ioctl(xmm, (const void __user *)arg);
+ break;
+ default:
+ result = -ENOTTY;
+ break;
+ }
+
+ mutex_unlock(&xmm->lock);
+ return result;
+}
+
+static struct xrt_dev_endpoints xrt_mgmt_main_endpoints[] = {
+ {
+ .xse_names = (struct xrt_dev_ep_names []){
+ { .ep_name = XRT_MD_NODE_MGMT_MAIN },
+ { NULL },
+ },
+ .xse_min_ep = 1,
+ },
+ { 0 },
+};
+
+static struct xrt_driver xmgmt_main_driver = {
+ .driver = {
+ .name = XMGMT_MAIN,
+ },
+ .file_ops = {
+ .xsf_ops = {
+ .owner = THIS_MODULE,
+ .open = xmgmt_main_open,
+ .release = xmgmt_main_close,
+ .unlocked_ioctl = xmgmt_main_ioctl,
+ },
+ .xsf_dev_name = "xmgmt",
+ },
+ .subdev_id = XRT_SUBDEV_MGMT_MAIN,
+ .endpoints = xrt_mgmt_main_endpoints,
+ .probe = xmgmt_main_probe,
+ .remove = xmgmt_main_remove,
+ .leaf_call = xmgmt_mainleaf_call,
+};
+
+int xmgmt_register_leaf(void)
+{
+ return xrt_register_driver(&xmgmt_main_driver);
+}
+
+void xmgmt_unregister_leaf(void)
+{
+ xrt_unregister_driver(&xmgmt_main_driver);
+}
diff --git a/drivers/fpga/xrt/mgmt/xmgmt.h b/drivers/fpga/xrt/mgmt/xmgmt.h
new file mode 100644
index 000000000000..54ce3875471a
--- /dev/null
+++ b/drivers/fpga/xrt/mgmt/xmgmt.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Lizhi Hou <[email protected]>
+ * Cheng Zhen <[email protected]>
+ */
+
+#ifndef _XMGMT_H_
+#define _XMGMT_H_
+
+#include "xmgmt-main.h"
+
+struct fpga_manager;
+int xmgmt_process_xclbin(struct xrt_device *xdev,
+ struct fpga_manager *fmgr,
+ const struct axlf *xclbin,
+ enum provider_kind kind);
+void xmgmt_region_cleanup_all(struct xrt_device *xdev);
+
+int xmgmt_hot_reset(struct xrt_device *xdev);
+
+/* Getting dtb for specified group. Caller should vfree returned dtb. */
+char *xmgmt_get_dtb(struct xrt_device *xdev, enum provider_kind kind);
+char *xmgmt_get_vbnv(struct xrt_device *xdev);
+int xmgmt_get_provider_uuid(struct xrt_device *xdev,
+ enum provider_kind kind, uuid_t *uuid);
+
+int xmgmt_register_leaf(void);
+void xmgmt_unregister_leaf(void);
+
+#endif /* _XMGMT_H_ */
--
2.27.0
ICAP stands for Hardware Internal Configuration Access Port. ICAP is
discovered by walking the firmware metadata. A xrt device node will be
created for it. FPGA bitstream is written to hardware through ICAP.
Signed-off-by: Sonal Santan <[email protected]>
Signed-off-by: Max Zhen <[email protected]>
Signed-off-by: Lizhi Hou <[email protected]>
Reviewed-by: Tom Rix <[email protected]>
---
drivers/fpga/xrt/include/xleaf/icap.h | 27 +++
drivers/fpga/xrt/lib/xleaf/icap.c | 328 ++++++++++++++++++++++++++
2 files changed, 355 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/icap.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/icap.c
diff --git a/drivers/fpga/xrt/include/xleaf/icap.h b/drivers/fpga/xrt/include/xleaf/icap.h
new file mode 100644
index 000000000000..96d39a8934fa
--- /dev/null
+++ b/drivers/fpga/xrt/include/xleaf/icap.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Lizhi Hou <[email protected]>
+ */
+
+#ifndef _XRT_ICAP_H_
+#define _XRT_ICAP_H_
+
+#include "xleaf.h"
+
+/*
+ * ICAP driver leaf calls.
+ */
+enum xrt_icap_leaf_cmd {
+ XRT_ICAP_WRITE = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
+ XRT_ICAP_GET_IDCODE,
+};
+
+struct xrt_icap_wr {
+ void *xiiw_bit_data;
+ u32 xiiw_data_len;
+};
+
+#endif /* _XRT_ICAP_H_ */
diff --git a/drivers/fpga/xrt/lib/xleaf/icap.c b/drivers/fpga/xrt/lib/xleaf/icap.c
new file mode 100644
index 000000000000..071923f61537
--- /dev/null
+++ b/drivers/fpga/xrt/lib/xleaf/icap.c
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Alveo FPGA ICAP Driver
+ *
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Lizhi Hou<[email protected]>
+ * Sonal Santan <[email protected]>
+ * Max Zhen <[email protected]>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/io.h>
+#include "metadata.h"
+#include "xleaf.h"
+#include "xleaf/icap.h"
+#include "xclbin-helper.h"
+
+#define XRT_ICAP "xrt_icap"
+
+#define ICAP_ERR(icap, fmt, arg...) \
+ xrt_err((icap)->xdev, fmt "\n", ##arg)
+#define ICAP_WARN(icap, fmt, arg...) \
+ xrt_warn((icap)->xdev, fmt "\n", ##arg)
+#define ICAP_INFO(icap, fmt, arg...) \
+ xrt_info((icap)->xdev, fmt "\n", ##arg)
+#define ICAP_DBG(icap, fmt, arg...) \
+ xrt_dbg((icap)->xdev, fmt "\n", ##arg)
+
+/*
+ * AXI-HWICAP IP register layout. Please see
+ * https://www.xilinx.com/support/documentation/ip_documentation/axi_hwicap/v3_0/pg134-axi-hwicap.pdf
+ */
+#define ICAP_REG_GIER 0x1C
+#define ICAP_REG_ISR 0x20
+#define ICAP_REG_IER 0x28
+#define ICAP_REG_WF 0x100
+#define ICAP_REG_RF 0x104
+#define ICAP_REG_SZ 0x108
+#define ICAP_REG_CR 0x10C
+#define ICAP_REG_SR 0x110
+#define ICAP_REG_WFV 0x114
+#define ICAP_REG_RFO 0x118
+#define ICAP_REG_ASR 0x11C
+
+#define ICAP_STATUS_EOS 0x4
+#define ICAP_STATUS_DONE 0x1
+
+/*
+ * Canned command sequence to obtain IDCODE of the FPGA
+ */
+static const __be32 idcode_stream[] = {
+ /* dummy word */
+ cpu_to_be32(0xffffffff),
+ /* sync word */
+ cpu_to_be32(0xaa995566),
+ /* NOP word */
+ cpu_to_be32(0x20000000),
+ /* NOP word */
+ cpu_to_be32(0x20000000),
+ /* ID code */
+ cpu_to_be32(0x28018001),
+ /* NOP word */
+ cpu_to_be32(0x20000000),
+ /* NOP word */
+ cpu_to_be32(0x20000000),
+};
+
+XRT_DEFINE_REGMAP_CONFIG(icap_regmap_config);
+
+struct icap {
+ struct xrt_device *xdev;
+ struct regmap *regmap;
+ struct mutex icap_lock; /* icap dev lock */
+ u32 idcode;
+};
+
+static int wait_for_done(const struct icap *icap)
+{
+ int i = 0;
+ int ret;
+ u32 w;
+
+ for (i = 0; i < 10; i++) {
+ /*
+ * it requires few micro seconds for ICAP to process incoming data.
+ * Polling every 5us for 10 times would be good enough.
+ */
+ udelay(5);
+ ret = regmap_read(icap->regmap, ICAP_REG_SR, &w);
+ if (ret)
+ return ret;
+ ICAP_INFO(icap, "XHWICAP_SR: %x", w);
+ if (w & (ICAP_STATUS_EOS | ICAP_STATUS_DONE))
+ return 0;
+ }
+
+ ICAP_ERR(icap, "bitstream download timeout");
+ return -ETIMEDOUT;
+}
+
+static int icap_write(const struct icap *icap, const __be32 *word_buf, int size)
+{
+ u32 value = 0;
+ int ret;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ value = be32_to_cpu(word_buf[i]);
+ ret = regmap_write(icap->regmap, ICAP_REG_WF, value);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_write(icap->regmap, ICAP_REG_CR, 0x1);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < 20; i++) {
+ ret = regmap_read(icap->regmap, ICAP_REG_CR, &value);
+ if (ret)
+ return ret;
+
+ if ((value & 0x1) == 0)
+ return 0;
+ ndelay(50);
+ }
+
+ ICAP_ERR(icap, "writing %d dwords timeout", size);
+ return -EIO;
+}
+
+static int bitstream_helper(struct icap *icap, const __be32 *word_buffer,
+ u32 word_count)
+{
+ int wr_fifo_vacancy = 0;
+ u32 word_written = 0;
+ u32 remain_word;
+ int err = 0;
+
+ WARN_ON(!mutex_is_locked(&icap->icap_lock));
+ for (remain_word = word_count; remain_word > 0;
+ remain_word -= word_written, word_buffer += word_written) {
+ err = regmap_read(icap->regmap, ICAP_REG_WFV, &wr_fifo_vacancy);
+ if (err) {
+ ICAP_ERR(icap, "read wr_fifo_vacancy failed %d", err);
+ break;
+ }
+ if (wr_fifo_vacancy <= 0) {
+ ICAP_ERR(icap, "no vacancy: %d", wr_fifo_vacancy);
+ err = -EIO;
+ break;
+ }
+ word_written = (wr_fifo_vacancy < remain_word) ?
+ wr_fifo_vacancy : remain_word;
+ if (icap_write(icap, word_buffer, word_written) != 0) {
+ ICAP_ERR(icap, "write failed remain %d, written %d",
+ remain_word, word_written);
+ err = -EIO;
+ break;
+ }
+ }
+
+ return err;
+}
+
+static int icap_download(struct icap *icap, const char *buffer,
+ unsigned long length)
+{
+ u32 num_chars_read = XCLBIN_HWICAP_BITFILE_BUF_SZ;
+ u32 byte_read;
+ int err = 0;
+
+ if (length % sizeof(u32)) {
+ ICAP_ERR(icap, "invalid bitstream length %ld", length);
+ return -EINVAL;
+ }
+
+ mutex_lock(&icap->icap_lock);
+ for (byte_read = 0; byte_read < length; byte_read += num_chars_read) {
+ num_chars_read = length - byte_read;
+ if (num_chars_read > XCLBIN_HWICAP_BITFILE_BUF_SZ)
+ num_chars_read = XCLBIN_HWICAP_BITFILE_BUF_SZ;
+
+ err = bitstream_helper(icap, (__be32 *)buffer, num_chars_read / sizeof(u32));
+ if (err)
+ goto failed;
+ buffer += num_chars_read;
+ }
+
+ /* there is not any cleanup needs to be done if writing ICAP timeout. */
+ err = wait_for_done(icap);
+
+failed:
+ mutex_unlock(&icap->icap_lock);
+
+ return err;
+}
+
+/*
+ * Discover the FPGA IDCODE using special sequence of canned commands
+ */
+static int icap_probe_chip(struct icap *icap)
+{
+ int err;
+ u32 val = 0;
+
+ regmap_read(icap->regmap, ICAP_REG_SR, &val);
+ if (val != ICAP_STATUS_DONE)
+ return -ENODEV;
+ /* Read ICAP FIFO vacancy */
+ regmap_read(icap->regmap, ICAP_REG_WFV, &val);
+ if (val < 8)
+ return -ENODEV;
+ err = icap_write(icap, idcode_stream, ARRAY_SIZE(idcode_stream));
+ if (err)
+ return err;
+ err = wait_for_done(icap);
+ if (err)
+ return err;
+
+ /* Tell config engine how many words to transfer to read FIFO */
+ regmap_write(icap->regmap, ICAP_REG_SZ, 0x1);
+ /* Switch the ICAP to read mode */
+ regmap_write(icap->regmap, ICAP_REG_CR, 0x2);
+ err = wait_for_done(icap);
+ if (err)
+ return err;
+
+ /* Read IDCODE from Read FIFO */
+ regmap_read(icap->regmap, ICAP_REG_RF, &icap->idcode);
+ return 0;
+}
+
+static int
+xrt_icap_leaf_call(struct xrt_device *xdev, u32 cmd, void *arg)
+{
+ struct xrt_icap_wr *wr_arg = arg;
+ struct icap *icap;
+ int ret = 0;
+
+ icap = xrt_get_drvdata(xdev);
+
+ switch (cmd) {
+ case XRT_XLEAF_EVENT:
+ /* Does not handle any event. */
+ break;
+ case XRT_ICAP_WRITE:
+ ret = icap_download(icap, wr_arg->xiiw_bit_data,
+ wr_arg->xiiw_data_len);
+ break;
+ case XRT_ICAP_GET_IDCODE:
+ *(u32 *)arg = icap->idcode;
+ break;
+ default:
+ ICAP_ERR(icap, "unknown command %d", cmd);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int xrt_icap_probe(struct xrt_device *xdev)
+{
+ void __iomem *base = NULL;
+ struct resource *res;
+ struct icap *icap;
+ int result = 0;
+
+ icap = devm_kzalloc(&xdev->dev, sizeof(*icap), GFP_KERNEL);
+ if (!icap)
+ return -ENOMEM;
+
+ icap->xdev = xdev;
+ xrt_set_drvdata(xdev, icap);
+ mutex_init(&icap->icap_lock);
+
+ xrt_info(xdev, "probing");
+ res = xrt_get_resource(xdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ base = devm_ioremap_resource(&xdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ icap->regmap = devm_regmap_init_mmio(&xdev->dev, base, &icap_regmap_config);
+ if (IS_ERR(icap->regmap)) {
+ ICAP_ERR(icap, "init mmio failed");
+ return PTR_ERR(icap->regmap);
+ }
+ /* Disable ICAP interrupts */
+ regmap_write(icap->regmap, ICAP_REG_GIER, 0);
+
+ result = icap_probe_chip(icap);
+ if (result)
+ xrt_err(xdev, "Failed to probe FPGA");
+ else
+ xrt_info(xdev, "Discovered FPGA IDCODE %x", icap->idcode);
+ return result;
+}
+
+static struct xrt_dev_endpoints xrt_icap_endpoints[] = {
+ {
+ .xse_names = (struct xrt_dev_ep_names[]) {
+ { .ep_name = XRT_MD_NODE_FPGA_CONFIG },
+ { NULL },
+ },
+ .xse_min_ep = 1,
+ },
+ { 0 },
+};
+
+static struct xrt_driver xrt_icap_driver = {
+ .driver = {
+ .name = XRT_ICAP,
+ },
+ .subdev_id = XRT_SUBDEV_ICAP,
+ .endpoints = xrt_icap_endpoints,
+ .probe = xrt_icap_probe,
+ .leaf_call = xrt_icap_leaf_call,
+};
+
+XRT_LEAF_INIT_FINI_FUNC(icap);
--
2.27.0
group driver that manages life cycle of a bunch of leaf driver instances
and bridges them with root.
Signed-off-by: Sonal Santan <[email protected]>
Signed-off-by: Max Zhen <[email protected]>
Signed-off-by: Lizhi Hou <[email protected]>
Reviewed-by: Tom Rix <[email protected]>
---
drivers/fpga/xrt/include/group.h | 25 +++
drivers/fpga/xrt/lib/group.c | 278 +++++++++++++++++++++++++++++++
2 files changed, 303 insertions(+)
create mode 100644 drivers/fpga/xrt/include/group.h
create mode 100644 drivers/fpga/xrt/lib/group.c
diff --git a/drivers/fpga/xrt/include/group.h b/drivers/fpga/xrt/include/group.h
new file mode 100644
index 000000000000..09e9d03f53fe
--- /dev/null
+++ b/drivers/fpga/xrt/include/group.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Cheng Zhen <[email protected]>
+ */
+
+#ifndef _XRT_GROUP_H_
+#define _XRT_GROUP_H_
+
+#include "xleaf.h"
+
+/*
+ * Group driver leaf calls.
+ */
+enum xrt_group_leaf_cmd {
+ XRT_GROUP_GET_LEAF = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
+ XRT_GROUP_PUT_LEAF,
+ XRT_GROUP_INIT_CHILDREN,
+ XRT_GROUP_FINI_CHILDREN,
+ XRT_GROUP_TRIGGER_EVENT,
+};
+
+#endif /* _XRT_GROUP_H_ */
diff --git a/drivers/fpga/xrt/lib/group.c b/drivers/fpga/xrt/lib/group.c
new file mode 100644
index 000000000000..b45f05449e0b
--- /dev/null
+++ b/drivers/fpga/xrt/lib/group.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Alveo FPGA Group Driver
+ *
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Cheng Zhen <[email protected]>
+ */
+
+#include <linux/mod_devicetable.h>
+#include "xleaf.h"
+#include "subdev_pool.h"
+#include "group.h"
+#include "metadata.h"
+#include "lib-drv.h"
+
+#define XRT_GRP "xrt_group"
+
+struct xrt_group {
+ struct xrt_device *xdev;
+ struct xrt_subdev_pool leaves;
+ bool leaves_created;
+ struct mutex lock; /* lock for group */
+};
+
+static int xrt_grp_root_cb(struct device *dev, void *parg,
+ enum xrt_root_cmd cmd, void *arg)
+{
+ int rc;
+ struct xrt_device *xdev =
+ container_of(dev, struct xrt_device, dev);
+ struct xrt_group *xg = (struct xrt_group *)parg;
+
+ switch (cmd) {
+ case XRT_ROOT_GET_LEAF_HOLDERS: {
+ struct xrt_root_get_holders *holders =
+ (struct xrt_root_get_holders *)arg;
+ rc = xrt_subdev_pool_get_holders(&xg->leaves,
+ holders->xpigh_xdev,
+ holders->xpigh_holder_buf,
+ holders->xpigh_holder_buf_len);
+ break;
+ }
+ default:
+ /* Forward parent call to root. */
+ rc = xrt_subdev_root_request(xdev, cmd, arg);
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * Cut subdev's dtb from group's dtb based on passed-in endpoint descriptor.
+ * Return the subdev's dtb through dtbp, if found.
+ */
+static int xrt_grp_cut_subdev_dtb(struct xrt_group *xg, struct xrt_dev_endpoints *eps,
+ char *grp_dtb, char **dtbp)
+{
+ int ret, i, ep_count = 0;
+ char *dtb = NULL;
+
+ ret = xrt_md_create(DEV(xg->xdev), &dtb);
+ if (ret)
+ return ret;
+
+ for (i = 0; eps->xse_names[i].ep_name || eps->xse_names[i].compat; i++) {
+ const char *ep_name = eps->xse_names[i].ep_name;
+ const char *compat = eps->xse_names[i].compat;
+
+ if (!ep_name)
+ xrt_md_get_compatible_endpoint(DEV(xg->xdev), grp_dtb, compat, &ep_name);
+ if (!ep_name)
+ continue;
+
+ ret = xrt_md_copy_endpoint(DEV(xg->xdev), dtb, grp_dtb, ep_name, compat, NULL);
+ if (ret)
+ continue;
+ xrt_md_del_endpoint(DEV(xg->xdev), grp_dtb, ep_name, compat);
+ ep_count++;
+ }
+ /* Found enough endpoints, return the subdev's dtb. */
+ if (ep_count >= eps->xse_min_ep) {
+ *dtbp = dtb;
+ return 0;
+ }
+
+ /* Cleanup - Restore all endpoints that has been deleted, if any. */
+ if (ep_count > 0) {
+ xrt_md_copy_endpoint(DEV(xg->xdev), grp_dtb, dtb,
+ XRT_MD_NODE_ENDPOINTS, NULL, NULL);
+ }
+ vfree(dtb);
+ *dtbp = NULL;
+ return 0;
+}
+
+static int xrt_grp_create_leaves(struct xrt_group *xg)
+{
+ struct xrt_subdev_platdata *pdata = DEV_PDATA(xg->xdev);
+ struct xrt_dev_endpoints *eps = NULL;
+ int ret = 0, failed = 0;
+ enum xrt_subdev_id did;
+ char *grp_dtb = NULL;
+ unsigned long mlen;
+
+ if (!pdata)
+ return -EINVAL;
+
+ mlen = xrt_md_size(DEV(xg->xdev), pdata->xsp_dtb);
+ if (mlen == XRT_MD_INVALID_LENGTH) {
+ xrt_err(xg->xdev, "invalid dtb, len %ld", mlen);
+ return -EINVAL;
+ }
+
+ mutex_lock(&xg->lock);
+
+ if (xg->leaves_created) {
+ /*
+ * This is expected since caller does not keep track of the state of the group
+ * and may, in some cases, still try to create leaves after it has already been
+ * created. This special error code will let the caller know what is going on.
+ */
+ mutex_unlock(&xg->lock);
+ return -EEXIST;
+ }
+
+ grp_dtb = vmalloc(mlen);
+ if (!grp_dtb) {
+ mutex_unlock(&xg->lock);
+ return -ENOMEM;
+ }
+
+ /* Create all leaves based on dtb. */
+ xrt_info(xg->xdev, "bringing up leaves...");
+ memcpy(grp_dtb, pdata->xsp_dtb, mlen);
+ for (did = 0; did < XRT_SUBDEV_NUM; did++) {
+ eps = xrt_drv_get_endpoints(did);
+ while (eps && eps->xse_names) {
+ char *dtb = NULL;
+
+ ret = xrt_grp_cut_subdev_dtb(xg, eps, grp_dtb, &dtb);
+ if (ret) {
+ failed++;
+ xrt_err(xg->xdev, "failed to cut subdev dtb for drv %s: %d",
+ xrt_drv_name(did), ret);
+ }
+ if (!dtb) {
+ /*
+ * No more dtb to cut or bad things happened for this instance,
+ * switch to the next one.
+ */
+ eps++;
+ continue;
+ }
+
+ /* Found a dtb for this instance, let's add it. */
+ ret = xrt_subdev_pool_add(&xg->leaves, did, xrt_grp_root_cb, xg, dtb);
+ if (ret < 0) {
+ /*
+ * It is not a fatal error here. Some functionality is not usable
+ * due to this missing device, but the error can be handled
+ * when the functionality is used.
+ */
+ failed++;
+ xrt_err(xg->xdev, "failed to add %s: %d", xrt_drv_name(did), ret);
+ }
+ vfree(dtb);
+ /* Continue searching for the same instance from grp_dtb. */
+ }
+ }
+
+ xg->leaves_created = true;
+ vfree(grp_dtb);
+ mutex_unlock(&xg->lock);
+ return failed == 0 ? 0 : -ECHILD;
+}
+
+static void xrt_grp_remove_leaves(struct xrt_group *xg)
+{
+ mutex_lock(&xg->lock);
+
+ if (!xg->leaves_created) {
+ mutex_unlock(&xg->lock);
+ return;
+ }
+
+ xrt_info(xg->xdev, "tearing down leaves...");
+ xrt_subdev_pool_fini(&xg->leaves);
+ xg->leaves_created = false;
+
+ mutex_unlock(&xg->lock);
+}
+
+static int xrt_grp_probe(struct xrt_device *xdev)
+{
+ struct xrt_group *xg;
+
+ xrt_info(xdev, "probing...");
+
+ xg = devm_kzalloc(&xdev->dev, sizeof(*xg), GFP_KERNEL);
+ if (!xg)
+ return -ENOMEM;
+
+ xg->xdev = xdev;
+ mutex_init(&xg->lock);
+ xrt_subdev_pool_init(DEV(xdev), &xg->leaves);
+ xrt_set_drvdata(xdev, xg);
+
+ return 0;
+}
+
+static void xrt_grp_remove(struct xrt_device *xdev)
+{
+ struct xrt_group *xg = xrt_get_drvdata(xdev);
+
+ xrt_info(xdev, "leaving...");
+ xrt_grp_remove_leaves(xg);
+}
+
+static int xrt_grp_leaf_call(struct xrt_device *xdev, u32 cmd, void *arg)
+{
+ int rc = 0;
+ struct xrt_group *xg = xrt_get_drvdata(xdev);
+
+ switch (cmd) {
+ case XRT_XLEAF_EVENT:
+ /* Simply forward to every child. */
+ xrt_subdev_pool_handle_event(&xg->leaves,
+ (struct xrt_event *)arg);
+ break;
+ case XRT_GROUP_GET_LEAF: {
+ struct xrt_root_get_leaf *get_leaf =
+ (struct xrt_root_get_leaf *)arg;
+
+ rc = xrt_subdev_pool_get(&xg->leaves, get_leaf->xpigl_match_cb,
+ get_leaf->xpigl_match_arg,
+ DEV(get_leaf->xpigl_caller_xdev),
+ &get_leaf->xpigl_tgt_xdev);
+ break;
+ }
+ case XRT_GROUP_PUT_LEAF: {
+ struct xrt_root_put_leaf *put_leaf =
+ (struct xrt_root_put_leaf *)arg;
+
+ rc = xrt_subdev_pool_put(&xg->leaves, put_leaf->xpipl_tgt_xdev,
+ DEV(put_leaf->xpipl_caller_xdev));
+ break;
+ }
+ case XRT_GROUP_INIT_CHILDREN:
+ rc = xrt_grp_create_leaves(xg);
+ break;
+ case XRT_GROUP_FINI_CHILDREN:
+ xrt_grp_remove_leaves(xg);
+ break;
+ case XRT_GROUP_TRIGGER_EVENT:
+ xrt_subdev_pool_trigger_event(&xg->leaves, (enum xrt_events)(uintptr_t)arg);
+ break;
+ default:
+ xrt_err(xdev, "unknown IOCTL cmd %d", cmd);
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static struct xrt_driver xrt_group_driver = {
+ .driver = {
+ .name = XRT_GRP,
+ },
+ .subdev_id = XRT_SUBDEV_GRP,
+ .probe = xrt_grp_probe,
+ .remove = xrt_grp_remove,
+ .leaf_call = xrt_grp_leaf_call,
+};
+
+XRT_LEAF_INIT_FINI_FUNC(group);
--
2.27.0
xrt-lib kernel module infrastructure code to register and manage all
leaf driver modules.
Signed-off-by: Sonal Santan <[email protected]>
Signed-off-by: Max Zhen <[email protected]>
Signed-off-by: Lizhi Hou <[email protected]>
Reviewed-by: Tom Rix <[email protected]>
---
drivers/fpga/xrt/include/subdev_id.h | 39 +++
drivers/fpga/xrt/include/xdevice.h | 141 ++++++++
drivers/fpga/xrt/include/xleaf.h | 205 +++++++++++
drivers/fpga/xrt/include/xleaf/clkfreq.h | 21 ++
drivers/fpga/xrt/include/xleaf/clock.h | 29 ++
.../fpga/xrt/include/xleaf/ddr_calibration.h | 28 ++
drivers/fpga/xrt/include/xleaf/devctl.h | 40 +++
drivers/fpga/xrt/lib/lib-drv.c | 318 ++++++++++++++++++
drivers/fpga/xrt/lib/lib-drv.h | 21 ++
9 files changed, 842 insertions(+)
create mode 100644 drivers/fpga/xrt/include/subdev_id.h
create mode 100644 drivers/fpga/xrt/include/xdevice.h
create mode 100644 drivers/fpga/xrt/include/xleaf.h
create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
create mode 100644 drivers/fpga/xrt/include/xleaf/ddr_calibration.h
create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
create mode 100644 drivers/fpga/xrt/lib/lib-drv.c
create mode 100644 drivers/fpga/xrt/lib/lib-drv.h
diff --git a/drivers/fpga/xrt/include/subdev_id.h b/drivers/fpga/xrt/include/subdev_id.h
new file mode 100644
index 000000000000..02df4b939a1b
--- /dev/null
+++ b/drivers/fpga/xrt/include/subdev_id.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Cheng Zhen <[email protected]>
+ */
+
+#ifndef _XRT_SUBDEV_ID_H_
+#define _XRT_SUBDEV_ID_H_
+
+/*
+ * Every subdev driver has an ID for others to refer to it. There can be multiple number of
+ * instances of a subdev driver. A <subdev_id, subdev_instance> tuple is a unique identification
+ * of a specific instance of a subdev driver.
+ */
+enum xrt_subdev_id {
+ XRT_SUBDEV_INVALID = 0,
+ XRT_SUBDEV_GRP,
+ XRT_SUBDEV_VSEC,
+ XRT_SUBDEV_VSEC_GOLDEN,
+ XRT_SUBDEV_DEVCTL,
+ XRT_SUBDEV_AXIGATE,
+ XRT_SUBDEV_ICAP,
+ XRT_SUBDEV_TEST,
+ XRT_SUBDEV_MGMT_MAIN,
+ XRT_SUBDEV_QSPI,
+ XRT_SUBDEV_MAILBOX,
+ XRT_SUBDEV_CMC,
+ XRT_SUBDEV_CALIB,
+ XRT_SUBDEV_CLKFREQ,
+ XRT_SUBDEV_CLOCK,
+ XRT_SUBDEV_SRSR,
+ XRT_SUBDEV_UCS,
+ XRT_SUBDEV_NUM, /* Total number of subdevs. */
+ XRT_ROOT = -1, /* Special ID for root driver. */
+};
+
+#endif /* _XRT_SUBDEV_ID_H_ */
diff --git a/drivers/fpga/xrt/include/xdevice.h b/drivers/fpga/xrt/include/xdevice.h
new file mode 100644
index 000000000000..b40ebe98b54d
--- /dev/null
+++ b/drivers/fpga/xrt/include/xdevice.h
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Lizhi Hou <[email protected]>
+ */
+
+#ifndef _XRT_DEVICE_H_
+#define _XRT_DEVICE_H_
+
+#include <linux/fs.h>
+#include <linux/cdev.h>
+
+#define XRT_MAX_DEVICE_NODES 128
+#define XRT_INVALID_DEVICE_INST (XRT_MAX_DEVICE_NODES + 1)
+
+enum {
+ XRT_DEVICE_STATE_NONE = 0,
+ XRT_DEVICE_STATE_ADDED
+};
+
+/*
+ * struct xrt_device - represent an xrt device on xrt bus
+ *
+ * dev: generic device interface.
+ * subdev_id: id of the xrt device. See enum xrt_subdev_id.
+ * name: name of the xrt device.
+ * instance: instance of the xrt device. The xrt device with same id can have
+ * more than 1 instances.
+ * state: current state of the xrt device.
+ * num_resources: The total number of resource for the xrt device.
+ * resource: point to the xrt device resource array.
+ * sdev_data: private data pointer.
+ */
+struct xrt_device {
+ struct device dev;
+ u32 subdev_id;
+ const char *name;
+ u32 instance;
+ u32 state;
+ u32 num_resources;
+ struct resource *resource;
+ void *sdev_data;
+};
+
+/*
+ * If populated by xrt device driver, infra will handle the mechanics of
+ * char device (un)registration.
+ */
+enum xrt_dev_file_mode {
+ /* Infra create cdev, default file name */
+ XRT_DEV_FILE_DEFAULT = 0,
+ /* Infra create cdev, need to encode inst num in file name */
+ XRT_DEV_FILE_MULTI_INST,
+ /* No auto creation of cdev by infra, leaf handles it by itself */
+ XRT_DEV_FILE_NO_AUTO,
+};
+
+struct xrt_dev_file_ops {
+ const struct file_operations xsf_ops;
+ dev_t xsf_dev_t;
+ const char *xsf_dev_name;
+ enum xrt_dev_file_mode xsf_mode;
+};
+
+/*
+ * this struct define the endpoints belong to the same xrt device
+ * ep_name: endpoint name
+ * compat: compatible string
+ */
+struct xrt_dev_ep_names {
+ const char *ep_name;
+ const char *compat;
+};
+
+struct xrt_dev_endpoints {
+ struct xrt_dev_ep_names *xse_names;
+ /* minimum number of endpoints to support the subdevice */
+ u32 xse_min_ep;
+};
+
+/*
+ * struct xrt_driver - represent a xrt device driver
+ *
+ * driver: driver model structure.
+ * subdev_id: id of the xrt device. See enum xrt_subdev_id.
+ * file_ops: character device name and callbacks.
+ * probe: mandatory callback for device binding.
+ * remove: callback for device unbinding.
+ * leaf_call: callback for servicing other leaf drivers.
+ */
+struct xrt_driver {
+ struct device_driver driver;
+ u32 subdev_id;
+ struct xrt_dev_file_ops file_ops;
+ struct xrt_dev_endpoints *endpoints;
+
+ /*
+ * Subdev driver callbacks populated by subdev driver.
+ */
+ int (*probe)(struct xrt_device *xrt_dev);
+ void (*remove)(struct xrt_device *xrt_dev);
+ /*
+ * If leaf_call is defined, these are called by other leaf drivers.
+ * Note that root driver may call into leaf_call of a group driver.
+ */
+ int (*leaf_call)(struct xrt_device *xrt_dev, u32 cmd, void *arg);
+};
+
+#define to_xrt_dev(d) container_of(d, struct xrt_device, dev)
+#define to_xrt_drv(d) container_of(d, struct xrt_driver, driver)
+
+static inline void *xrt_get_drvdata(const struct xrt_device *xdev)
+{
+ return dev_get_drvdata(&xdev->dev);
+}
+
+static inline void xrt_set_drvdata(struct xrt_device *xdev, void *data)
+{
+ dev_set_drvdata(&xdev->dev, data);
+}
+
+static inline void *xrt_get_xdev_data(struct device *dev)
+{
+ struct xrt_device *xdev = to_xrt_dev(dev);
+
+ return xdev->sdev_data;
+}
+
+struct xrt_device *
+xrt_device_register(struct device *parent, u32 id,
+ struct resource *res, u32 res_num,
+ void *pdata, size_t data_sz);
+void xrt_device_unregister(struct xrt_device *xdev);
+int xrt_register_driver(struct xrt_driver *drv);
+void xrt_unregister_driver(struct xrt_driver *drv);
+void *xrt_get_xdev_data(struct device *dev);
+struct resource *xrt_get_resource(struct xrt_device *xdev, u32 type, u32 num);
+
+#endif /* _XRT_DEVICE_H_ */
diff --git a/drivers/fpga/xrt/include/xleaf.h b/drivers/fpga/xrt/include/xleaf.h
new file mode 100644
index 000000000000..f065fc766e0f
--- /dev/null
+++ b/drivers/fpga/xrt/include/xleaf.h
@@ -0,0 +1,205 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Cheng Zhen <[email protected]>
+ * Sonal Santan <[email protected]>
+ */
+
+#ifndef _XRT_XLEAF_H_
+#define _XRT_XLEAF_H_
+
+#include <linux/mod_devicetable.h>
+#include "xdevice.h"
+#include "subdev_id.h"
+#include "xroot.h"
+#include "events.h"
+
+/* All subdev drivers should use below common routines to print out msg. */
+#define DEV(xdev) (&(xdev)->dev)
+#define DEV_PDATA(xdev) \
+ ((struct xrt_subdev_platdata *)xrt_get_xdev_data(DEV(xdev)))
+#define DEV_FILE_OPS(xdev) \
+ (&(to_xrt_drv((xdev)->dev.driver))->file_ops)
+#define FMT_PRT(prt_fn, xdev, fmt, args...) \
+ ({typeof(xdev) (_xdev) = (xdev); \
+ prt_fn(DEV(_xdev), "%s %s: " fmt, \
+ DEV_PDATA(_xdev)->xsp_root_name, __func__, ##args); })
+#define xrt_err(xdev, fmt, args...) FMT_PRT(dev_err, xdev, fmt, ##args)
+#define xrt_warn(xdev, fmt, args...) FMT_PRT(dev_warn, xdev, fmt, ##args)
+#define xrt_info(xdev, fmt, args...) FMT_PRT(dev_info, xdev, fmt, ##args)
+#define xrt_dbg(xdev, fmt, args...) FMT_PRT(dev_dbg, xdev, fmt, ##args)
+
+#define XRT_DEFINE_REGMAP_CONFIG(config_name) \
+ static const struct regmap_config config_name = { \
+ .reg_bits = 32, \
+ .val_bits = 32, \
+ .reg_stride = 4, \
+ .max_register = 0x1000, \
+ }
+
+enum {
+ /* Starting cmd for common leaf cmd implemented by all leaves. */
+ XRT_XLEAF_COMMON_BASE = 0,
+ /* Starting cmd for leaves' specific leaf cmds. */
+ XRT_XLEAF_CUSTOM_BASE = 64,
+};
+
+enum xrt_xleaf_common_leaf_cmd {
+ XRT_XLEAF_EVENT = XRT_XLEAF_COMMON_BASE,
+};
+
+/*
+ * Partially initialized by the parent driver, then, passed in as subdev driver's
+ * platform data when creating subdev driver instance by calling platform
+ * device register API (xrt_device_register_data() or the likes).
+ *
+ * Once device register API returns, platform driver framework makes a copy of
+ * this buffer and maintains its life cycle. The content of the buffer is
+ * completely owned by subdev driver.
+ *
+ * Thus, parent driver should be very careful when it touches this buffer
+ * again once it's handed over to subdev driver. And the data structure
+ * should not contain pointers pointing to buffers that is managed by
+ * other or parent drivers since it could have been freed before platform
+ * data buffer is freed by platform driver framework.
+ */
+struct xrt_subdev_platdata {
+ /*
+ * Per driver instance callback. The xdev points to the instance.
+ * Should always be defined for subdev driver to get service from root.
+ */
+ xrt_subdev_root_cb_t xsp_root_cb;
+ void *xsp_root_cb_arg;
+
+ /* Something to associate w/ root for msg printing. */
+ const char *xsp_root_name;
+
+ /*
+ * Char dev support for this subdev instance.
+ * Initialized by subdev driver.
+ */
+ struct cdev xsp_cdev;
+ struct device *xsp_sysdev;
+ struct mutex xsp_devnode_lock; /* devnode lock */
+ struct completion xsp_devnode_comp;
+ int xsp_devnode_ref;
+ bool xsp_devnode_online;
+ bool xsp_devnode_excl;
+
+ /*
+ * Subdev driver specific init data. The buffer should be embedded
+ * in this data structure buffer after dtb, so that it can be freed
+ * together with platform data.
+ */
+ loff_t xsp_priv_off; /* Offset into this platform data buffer. */
+ size_t xsp_priv_len;
+
+ /*
+ * Populated by parent driver to describe the device tree for
+ * the subdev driver to handle. Should always be last one since it's
+ * of variable length.
+ */
+ bool xsp_dtb_valid;
+ char xsp_dtb[0];
+};
+
+struct subdev_match_arg {
+ enum xrt_subdev_id id;
+ int instance;
+};
+
+bool xleaf_has_endpoint(struct xrt_device *xdev, const char *endpoint_name);
+struct xrt_device *xleaf_get_leaf(struct xrt_device *xdev,
+ xrt_subdev_match_t cb, void *arg);
+
+static inline bool subdev_match(enum xrt_subdev_id id, struct xrt_device *xdev, void *arg)
+{
+ const struct subdev_match_arg *a = (struct subdev_match_arg *)arg;
+ int instance = a->instance;
+
+ if (id != a->id)
+ return false;
+ if (instance != xdev->instance && instance != XRT_INVALID_DEVICE_INST)
+ return false;
+ return true;
+}
+
+static inline bool xrt_subdev_match_epname(enum xrt_subdev_id id,
+ struct xrt_device *xdev, void *arg)
+{
+ return xleaf_has_endpoint(xdev, arg);
+}
+
+static inline struct xrt_device *
+xleaf_get_leaf_by_id(struct xrt_device *xdev,
+ enum xrt_subdev_id id, int instance)
+{
+ struct subdev_match_arg arg = { id, instance };
+
+ return xleaf_get_leaf(xdev, subdev_match, &arg);
+}
+
+static inline struct xrt_device *
+xleaf_get_leaf_by_epname(struct xrt_device *xdev, const char *name)
+{
+ return xleaf_get_leaf(xdev, xrt_subdev_match_epname, (void *)name);
+}
+
+static inline int xleaf_call(struct xrt_device *tgt, u32 cmd, void *arg)
+{
+ return (to_xrt_drv(tgt->dev.driver)->leaf_call)(tgt, cmd, arg);
+}
+
+int xleaf_broadcast_event(struct xrt_device *xdev, enum xrt_events evt, bool async);
+int xleaf_create_group(struct xrt_device *xdev, char *dtb);
+int xleaf_destroy_group(struct xrt_device *xdev, int instance);
+void xleaf_get_root_res(struct xrt_device *xdev, u32 region_id, struct resource **res);
+void xleaf_get_root_id(struct xrt_device *xdev, unsigned short *vendor, unsigned short *device,
+ unsigned short *subvendor, unsigned short *subdevice);
+void xleaf_hot_reset(struct xrt_device *xdev);
+int xleaf_put_leaf(struct xrt_device *xdev, struct xrt_device *leaf);
+struct device *xleaf_register_hwmon(struct xrt_device *xdev, const char *name, void *drvdata,
+ const struct attribute_group **grps);
+void xleaf_unregister_hwmon(struct xrt_device *xdev, struct device *hwmon);
+int xleaf_wait_for_group_bringup(struct xrt_device *xdev);
+
+/*
+ * Character device helper APIs for use by leaf drivers
+ */
+static inline bool xleaf_devnode_enabled(struct xrt_device *xdev)
+{
+ return DEV_FILE_OPS(xdev)->xsf_ops.open;
+}
+
+int xleaf_devnode_create(struct xrt_device *xdev,
+ const char *file_name, const char *inst_name);
+void xleaf_devnode_destroy(struct xrt_device *xdev);
+
+struct xrt_device *xleaf_devnode_open_excl(struct inode *inode);
+struct xrt_device *xleaf_devnode_open(struct inode *inode);
+void xleaf_devnode_close(struct inode *inode);
+
+/* Module's init/fini routines for leaf driver in xrt-lib module */
+#define XRT_LEAF_INIT_FINI_FUNC(name) \
+void name##_leaf_init_fini(bool init) \
+{ \
+ if (init) \
+ xrt_register_driver(&xrt_##name##_driver); \
+ else \
+ xrt_unregister_driver(&xrt_##name##_driver); \
+}
+
+/* Module's init/fini routines for leaf driver in xrt-lib module */
+void group_leaf_init_fini(bool init);
+void vsec_leaf_init_fini(bool init);
+void devctl_leaf_init_fini(bool init);
+void axigate_leaf_init_fini(bool init);
+void icap_leaf_init_fini(bool init);
+void calib_leaf_init_fini(bool init);
+void clkfreq_leaf_init_fini(bool init);
+void clock_leaf_init_fini(bool init);
+void ucs_leaf_init_fini(bool init);
+
+#endif /* _XRT_LEAF_H_ */
diff --git a/drivers/fpga/xrt/include/xleaf/clkfreq.h b/drivers/fpga/xrt/include/xleaf/clkfreq.h
new file mode 100644
index 000000000000..005441d5df78
--- /dev/null
+++ b/drivers/fpga/xrt/include/xleaf/clkfreq.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Lizhi Hou <[email protected]>
+ */
+
+#ifndef _XRT_CLKFREQ_H_
+#define _XRT_CLKFREQ_H_
+
+#include "xleaf.h"
+
+/*
+ * CLKFREQ driver leaf calls.
+ */
+enum xrt_clkfreq_leaf_cmd {
+ XRT_CLKFREQ_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
+};
+
+#endif /* _XRT_CLKFREQ_H_ */
diff --git a/drivers/fpga/xrt/include/xleaf/clock.h b/drivers/fpga/xrt/include/xleaf/clock.h
new file mode 100644
index 000000000000..1379e24fa5d0
--- /dev/null
+++ b/drivers/fpga/xrt/include/xleaf/clock.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Lizhi Hou <[email protected]>
+ */
+
+#ifndef _XRT_CLOCK_H_
+#define _XRT_CLOCK_H_
+
+#include "xleaf.h"
+#include <linux/fpga_xrt.h>
+
+/*
+ * CLOCK driver leaf calls.
+ */
+enum xrt_clock_leaf_cmd {
+ XRT_CLOCK_SET = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
+ XRT_CLOCK_GET,
+ XRT_CLOCK_VERIFY,
+};
+
+struct xrt_clock_get {
+ u16 freq;
+ u32 freq_cnter;
+};
+
+#endif /* _XRT_CLOCK_H_ */
diff --git a/drivers/fpga/xrt/include/xleaf/ddr_calibration.h b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
new file mode 100644
index 000000000000..c44ae30f939a
--- /dev/null
+++ b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Cheng Zhen <[email protected]>
+ */
+
+#ifndef _XRT_DDR_CALIBRATION_H_
+#define _XRT_DDR_CALIBRATION_H_
+
+#include "xleaf.h"
+#include <linux/fpga-xrt.h>
+
+/*
+ * Memory calibration driver leaf calls.
+ */
+enum xrt_calib_results {
+ XRT_CALIB_UNKNOWN = 0,
+ XRT_CALIB_SUCCEEDED,
+ XRT_CALIB_FAILED,
+};
+
+enum xrt_calib_leaf_cmd {
+ XRT_CALIB_RESULT = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
+};
+
+#endif /* _XRT_DDR_CALIBRATION_H_ */
diff --git a/drivers/fpga/xrt/include/xleaf/devctl.h b/drivers/fpga/xrt/include/xleaf/devctl.h
new file mode 100644
index 000000000000..b97f3b6d9326
--- /dev/null
+++ b/drivers/fpga/xrt/include/xleaf/devctl.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Lizhi Hou <[email protected]>
+ */
+
+#ifndef _XRT_DEVCTL_H_
+#define _XRT_DEVCTL_H_
+
+#include "xleaf.h"
+
+/*
+ * DEVCTL driver leaf calls.
+ */
+enum xrt_devctl_leaf_cmd {
+ XRT_DEVCTL_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
+};
+
+enum xrt_devctl_id {
+ XRT_DEVCTL_ROM_UUID = 0,
+ XRT_DEVCTL_DDR_CALIB,
+ XRT_DEVCTL_GOLDEN_VER,
+ XRT_DEVCTL_MAX
+};
+
+struct xrt_devctl_rw {
+ u32 xdr_id;
+ void *xdr_buf;
+ u32 xdr_len;
+ u32 xdr_offset;
+};
+
+struct xrt_devctl_intf_uuid {
+ u32 uuid_num;
+ uuid_t *uuids;
+};
+
+#endif /* _XRT_DEVCTL_H_ */
diff --git a/drivers/fpga/xrt/lib/lib-drv.c b/drivers/fpga/xrt/lib/lib-drv.c
new file mode 100644
index 000000000000..7d21ef565651
--- /dev/null
+++ b/drivers/fpga/xrt/lib/lib-drv.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Cheng Zhen <[email protected]>
+ * Lizhi Hou <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include "xleaf.h"
+#include "xroot.h"
+#include "lib-drv.h"
+
+#define XRT_IPLIB_MODULE_NAME "xrt-lib"
+#define XRT_DRVNAME(drv) ((drv)->driver.name)
+
+#define XRT_SUBDEV_ID_SHIFT 16
+#define XRT_SUBDEV_ID_MASK ((1 << XRT_SUBDEV_ID_SHIFT) - 1)
+
+struct xrt_find_drv_data {
+ enum xrt_subdev_id id;
+ struct xrt_driver *xdrv;
+};
+
+struct class *xrt_class;
+static DEFINE_IDA(xrt_device_ida);
+
+static inline u32 xrt_instance_to_id(enum xrt_subdev_id id, u32 instance)
+{
+ return (id << XRT_SUBDEV_ID_SHIFT) | instance;
+}
+
+static inline u32 xrt_id_to_instance(u32 id)
+{
+ return id & XRT_SUBDEV_ID_MASK;
+}
+
+static int xrt_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct xrt_device *xdev = to_xrt_dev(dev);
+ struct xrt_driver *xdrv = to_xrt_drv(drv);
+
+ if (xdev->subdev_id == xdrv->subdev_id)
+ return 1;
+
+ return 0;
+}
+
+static int xrt_bus_probe(struct device *dev)
+{
+ struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
+ struct xrt_device *xdev = to_xrt_dev(dev);
+
+ return xdrv->probe(xdev);
+}
+
+static int xrt_bus_remove(struct device *dev)
+{
+ struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
+ struct xrt_device *xdev = to_xrt_dev(dev);
+
+ if (xdrv->remove)
+ xdrv->remove(xdev);
+
+ return 0;
+}
+
+struct bus_type xrt_bus_type = {
+ .name = "xrt",
+ .match = xrt_bus_match,
+ .probe = xrt_bus_probe,
+ .remove = xrt_bus_remove,
+};
+
+int xrt_register_driver(struct xrt_driver *drv)
+{
+ const char *drvname = XRT_DRVNAME(drv);
+ int rc = 0;
+
+ /* Initialize dev_t for char dev node. */
+ if (drv->file_ops.xsf_ops.open) {
+ rc = alloc_chrdev_region(&drv->file_ops.xsf_dev_t, 0,
+ XRT_MAX_DEVICE_NODES, drvname);
+ if (rc) {
+ pr_err("failed to alloc dev minor for %s: %d\n", drvname, rc);
+ return rc;
+ }
+ } else {
+ drv->file_ops.xsf_dev_t = (dev_t)-1;
+ }
+
+ drv->driver.owner = THIS_MODULE;
+ drv->driver.bus = &xrt_bus_type;
+
+ rc = driver_register(&drv->driver);
+ if (rc) {
+ pr_err("register %s xrt driver failed\n", drvname);
+ if (drv->file_ops.xsf_dev_t != (dev_t)-1) {
+ unregister_chrdev_region(drv->file_ops.xsf_dev_t,
+ XRT_MAX_DEVICE_NODES);
+ }
+ return rc;
+ }
+
+ pr_info("%s registered successfully\n", drvname);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xrt_register_driver);
+
+void xrt_unregister_driver(struct xrt_driver *drv)
+{
+ driver_unregister(&drv->driver);
+
+ if (drv->file_ops.xsf_dev_t != (dev_t)-1)
+ unregister_chrdev_region(drv->file_ops.xsf_dev_t, XRT_MAX_DEVICE_NODES);
+}
+EXPORT_SYMBOL_GPL(xrt_unregister_driver);
+
+static int __find_driver(struct device_driver *drv, void *_data)
+{
+ struct xrt_driver *xdrv = to_xrt_drv(drv);
+ struct xrt_find_drv_data *data = _data;
+
+ if (xdrv->subdev_id == data->id) {
+ data->xdrv = xdrv;
+ return 1;
+ }
+
+ return 0;
+}
+
+const char *xrt_drv_name(enum xrt_subdev_id id)
+{
+ struct xrt_find_drv_data data = { 0 };
+
+ data.id = id;
+ bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
+
+ if (data.xdrv)
+ return XRT_DRVNAME(data.xdrv);
+
+ return NULL;
+}
+
+static int xrt_drv_get_instance(enum xrt_subdev_id id)
+{
+ int ret;
+
+ ret = ida_alloc_range(&xrt_device_ida, xrt_instance_to_id(id, 0),
+ xrt_instance_to_id(id, XRT_MAX_DEVICE_NODES),
+ GFP_KERNEL);
+ if (ret < 0)
+ return ret;
+
+ return xrt_id_to_instance((u32)ret);
+}
+
+static void xrt_drv_put_instance(enum xrt_subdev_id id, int instance)
+{
+ ida_free(&xrt_device_ida, xrt_instance_to_id(id, instance));
+}
+
+struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id id)
+{
+ struct xrt_find_drv_data data = { 0 };
+
+ data.id = id;
+ bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
+
+ if (data.xdrv)
+ return data.xdrv->endpoints;
+
+ return NULL;
+}
+
+static void xrt_device_release(struct device *dev)
+{
+ struct xrt_device *xdev = container_of(dev, struct xrt_device, dev);
+
+ kfree(xdev);
+}
+
+void xrt_device_unregister(struct xrt_device *xdev)
+{
+ if (xdev->state == XRT_DEVICE_STATE_ADDED)
+ device_del(&xdev->dev);
+
+ vfree(xdev->sdev_data);
+ kfree(xdev->resource);
+
+ if (xdev->instance != XRT_INVALID_DEVICE_INST)
+ xrt_drv_put_instance(xdev->subdev_id, xdev->instance);
+
+ if (xdev->dev.release == xrt_device_release)
+ put_device(&xdev->dev);
+}
+
+struct xrt_device *
+xrt_device_register(struct device *parent, u32 id,
+ struct resource *res, u32 res_num,
+ void *pdata, size_t data_sz)
+{
+ struct xrt_device *xdev = NULL;
+ int ret;
+
+ xdev = kzalloc(sizeof(*xdev), GFP_KERNEL);
+ if (!xdev)
+ return xdev;
+ xdev->instance = XRT_INVALID_DEVICE_INST;
+
+ /* Obtain dev instance number. */
+ ret = xrt_drv_get_instance(id);
+ if (ret < 0) {
+ dev_err(parent, "failed get instance, ret %d", ret);
+ goto fail;
+ }
+
+ xdev->instance = ret;
+ xdev->name = xrt_drv_name(id);
+ xdev->subdev_id = id;
+ device_initialize(&xdev->dev);
+ xdev->dev.release = xrt_device_release;
+ xdev->dev.parent = parent;
+
+ xdev->dev.bus = &xrt_bus_type;
+ dev_set_name(&xdev->dev, "%s.%d", xdev->name, xdev->instance);
+
+ xdev->num_resources = res_num;
+ xdev->resource = kmemdup(res, sizeof(*res) * res_num, GFP_KERNEL);
+ if (!xdev->resource)
+ goto fail;
+
+ xdev->sdev_data = vzalloc(data_sz);
+ if (!xdev->sdev_data)
+ goto fail;
+
+ memcpy(xdev->sdev_data, pdata, data_sz);
+
+ ret = device_add(&xdev->dev);
+ if (ret) {
+ dev_err(parent, "failed add device, ret %d", ret);
+ goto fail;
+ }
+ xdev->state = XRT_DEVICE_STATE_ADDED;
+
+ return xdev;
+
+fail:
+ xrt_device_unregister(xdev);
+ kfree(xdev);
+
+ return NULL;
+}
+
+struct resource *xrt_get_resource(struct xrt_device *xdev, u32 type, u32 num)
+{
+ u32 i;
+
+ for (i = 0; i < xdev->num_resources; i++) {
+ struct resource *r = &xdev->resource[i];
+
+ if (type == resource_type(r) && num-- == 0)
+ return r;
+ }
+ return NULL;
+}
+
+/*
+ * Leaf driver's module init/fini callbacks. This is not a open infrastructure for dynamic
+ * plugging in drivers. All drivers should be statically added.
+ */
+static void (*leaf_init_fini_cbs[])(bool) = {
+ group_leaf_init_fini,
+ axigate_leaf_init_fini,
+ icap_leaf_init_fini,
+};
+
+static __init int xrt_lib_init(void)
+{
+ int ret;
+ int i;
+
+ ret = bus_register(&xrt_bus_type);
+ if (ret)
+ return ret;
+
+ xrt_class = class_create(THIS_MODULE, XRT_IPLIB_MODULE_NAME);
+ if (IS_ERR(xrt_class)) {
+ bus_unregister(&xrt_bus_type);
+ return PTR_ERR(xrt_class);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
+ leaf_init_fini_cbs[i](true);
+ return 0;
+}
+
+static __exit void xrt_lib_fini(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
+ leaf_init_fini_cbs[i](false);
+
+ class_destroy(xrt_class);
+ bus_unregister(&xrt_bus_type);
+}
+
+module_init(xrt_lib_init);
+module_exit(xrt_lib_fini);
+
+MODULE_AUTHOR("XRT Team <[email protected]>");
+MODULE_DESCRIPTION("Xilinx Alveo IP Lib driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/fpga/xrt/lib/lib-drv.h b/drivers/fpga/xrt/lib/lib-drv.h
new file mode 100644
index 000000000000..0276c28e009f
--- /dev/null
+++ b/drivers/fpga/xrt/lib/lib-drv.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Cheng Zhen <[email protected]>
+ */
+
+#ifndef _LIB_DRV_H_
+#define _LIB_DRV_H_
+
+#include <linux/device/class.h>
+#include <linux/device/bus.h>
+
+extern struct class *xrt_class;
+extern struct bus_type xrt_bus_type;
+
+const char *xrt_drv_name(enum xrt_subdev_id id);
+struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id id);
+
+#endif /* _LIB_DRV_H_ */
--
2.27.0
Update fpga Kconfig/Makefile and add Kconfig/Makefile for new drivers.
Signed-off-by: Sonal Santan <[email protected]>
Signed-off-by: Max Zhen <[email protected]>
Signed-off-by: Lizhi Hou <[email protected]>
Reviewed-by: Tom Rix <[email protected]>
---
drivers/Makefile | 1 +
drivers/fpga/Kconfig | 2 ++
drivers/fpga/Makefile | 5 +++++
drivers/fpga/xrt/Kconfig | 8 ++++++++
drivers/fpga/xrt/lib/Kconfig | 17 +++++++++++++++++
drivers/fpga/xrt/lib/Makefile | 24 ++++++++++++++++++++++++
drivers/fpga/xrt/metadata/Kconfig | 12 ++++++++++++
drivers/fpga/xrt/metadata/Makefile | 16 ++++++++++++++++
drivers/fpga/xrt/mgmt/Kconfig | 15 +++++++++++++++
drivers/fpga/xrt/mgmt/Makefile | 19 +++++++++++++++++++
10 files changed, 119 insertions(+)
create mode 100644 drivers/fpga/xrt/Kconfig
create mode 100644 drivers/fpga/xrt/lib/Kconfig
create mode 100644 drivers/fpga/xrt/lib/Makefile
create mode 100644 drivers/fpga/xrt/metadata/Kconfig
create mode 100644 drivers/fpga/xrt/metadata/Makefile
create mode 100644 drivers/fpga/xrt/mgmt/Kconfig
create mode 100644 drivers/fpga/xrt/mgmt/Makefile
diff --git a/drivers/Makefile b/drivers/Makefile
index 27c018bdf4de..64fba9d3adb9 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -180,6 +180,7 @@ obj-$(CONFIG_STM) += hwtracing/stm/
obj-$(CONFIG_ANDROID) += android/
obj-$(CONFIG_NVMEM) += nvmem/
obj-$(CONFIG_FPGA) += fpga/
+obj-$(CONFIG_FPGA_XRT_METADATA) += fpga/
obj-$(CONFIG_FSI) += fsi/
obj-$(CONFIG_TEE) += tee/
obj-$(CONFIG_MULTIPLEXER) += mux/
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 8cd454ee20c0..526447770cab 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -234,4 +234,6 @@ config FPGA_MGR_ZYNQMP_FPGA
to configure the programmable logic(PL) through PS
on ZynqMP SoC.
+source "drivers/fpga/xrt/Kconfig"
+
endif # FPGA
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 18dc9885883a..4b887bf95cb3 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -48,3 +48,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
+
+# XRT drivers for Alveo
+obj-$(CONFIG_FPGA_XRT_METADATA) += xrt/metadata/
+obj-$(CONFIG_FPGA_XRT_LIB) += xrt/lib/
+obj-$(CONFIG_FPGA_XRT_XMGMT) += xrt/mgmt/
diff --git a/drivers/fpga/xrt/Kconfig b/drivers/fpga/xrt/Kconfig
new file mode 100644
index 000000000000..0e2c59589ddd
--- /dev/null
+++ b/drivers/fpga/xrt/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Xilinx Alveo FPGA device configuration
+#
+
+source "drivers/fpga/xrt/metadata/Kconfig"
+source "drivers/fpga/xrt/lib/Kconfig"
+source "drivers/fpga/xrt/mgmt/Kconfig"
diff --git a/drivers/fpga/xrt/lib/Kconfig b/drivers/fpga/xrt/lib/Kconfig
new file mode 100644
index 000000000000..935369fad570
--- /dev/null
+++ b/drivers/fpga/xrt/lib/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# XRT Alveo FPGA device configuration
+#
+
+config FPGA_XRT_LIB
+ tristate "XRT Alveo Driver Library"
+ depends on HWMON && PCI && HAS_IOMEM
+ select FPGA_XRT_METADATA
+ select REGMAP_MMIO
+ help
+ Select this option to enable Xilinx XRT Alveo driver library. This
+ library is core infrastructure of XRT Alveo FPGA drivers which
+ provides functions for working with device nodes, iteration and
+ lookup of platform devices, common interfaces for platform devices,
+ plumbing of function call and ioctls between platform devices and
+ parent partitions.
diff --git a/drivers/fpga/xrt/lib/Makefile b/drivers/fpga/xrt/lib/Makefile
new file mode 100644
index 000000000000..55cd6063a324
--- /dev/null
+++ b/drivers/fpga/xrt/lib/Makefile
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2020-2021 Xilinx, Inc. All rights reserved.
+#
+# Authors: [email protected]
+#
+
+FULL_XRT_PATH=$(srctree)/$(src)/..
+FULL_DTC_PATH=$(srctree)/scripts/dtc/libfdt
+
+obj-$(CONFIG_FPGA_XRT_LIB) += xrt-lib.o
+
+xrt-lib-objs := \
+ lib-drv.o \
+ xroot.o \
+ xclbin.o \
+ subdev.o \
+ cdev.o \
+ group.o \
+ xleaf/axigate.o \
+ xleaf/icap.o
+
+ccflags-y := -I$(FULL_XRT_PATH)/include \
+ -I$(FULL_DTC_PATH)
diff --git a/drivers/fpga/xrt/metadata/Kconfig b/drivers/fpga/xrt/metadata/Kconfig
new file mode 100644
index 000000000000..129adda47e94
--- /dev/null
+++ b/drivers/fpga/xrt/metadata/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# XRT Alveo FPGA device configuration
+#
+
+config FPGA_XRT_METADATA
+ bool "XRT Alveo Driver Metadata Parser"
+ select LIBFDT
+ help
+ This option provides helper functions to parse Xilinx Alveo FPGA
+ firmware metadata. The metadata is in device tree format and the
+ XRT driver uses it to discover the HW subsystems behind PCIe BAR.
diff --git a/drivers/fpga/xrt/metadata/Makefile b/drivers/fpga/xrt/metadata/Makefile
new file mode 100644
index 000000000000..14f65ef1595c
--- /dev/null
+++ b/drivers/fpga/xrt/metadata/Makefile
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2020-2021 Xilinx, Inc. All rights reserved.
+#
+# Authors: [email protected]
+#
+
+FULL_XRT_PATH=$(srctree)/$(src)/..
+FULL_DTC_PATH=$(srctree)/scripts/dtc/libfdt
+
+obj-$(CONFIG_FPGA_XRT_METADATA) += xrt-md.o
+
+xrt-md-objs := metadata.o
+
+ccflags-y := -I$(FULL_XRT_PATH)/include \
+ -I$(FULL_DTC_PATH)
diff --git a/drivers/fpga/xrt/mgmt/Kconfig b/drivers/fpga/xrt/mgmt/Kconfig
new file mode 100644
index 000000000000..31e9e19fffb8
--- /dev/null
+++ b/drivers/fpga/xrt/mgmt/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Xilinx XRT FPGA device configuration
+#
+
+config FPGA_XRT_XMGMT
+ tristate "Xilinx Alveo Management Driver"
+ depends on FPGA_XRT_LIB
+ select FPGA_XRT_METADATA
+ select FPGA_BRIDGE
+ select FPGA_REGION
+ help
+ Select this option to enable XRT PCIe driver for Xilinx Alveo FPGA.
+ This driver provides interfaces for userspace application to access
+ Alveo FPGA device.
diff --git a/drivers/fpga/xrt/mgmt/Makefile b/drivers/fpga/xrt/mgmt/Makefile
new file mode 100644
index 000000000000..16644571b673
--- /dev/null
+++ b/drivers/fpga/xrt/mgmt/Makefile
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2020-2021 Xilinx, Inc. All rights reserved.
+#
+# Authors: [email protected]
+#
+
+FULL_XRT_PATH=$(srctree)/$(src)/..
+FULL_DTC_PATH=$(srctree)/scripts/dtc/libfdt
+
+obj-$(CONFIG_FPGA_XRT_XMGMT) += xrt-mgmt.o
+
+xrt-mgmt-objs := root.o \
+ xmgmt-main.o \
+ xrt-mgr.o \
+ xmgmt-main-region.o
+
+ccflags-y := -I$(FULL_XRT_PATH)/include \
+ -I$(FULL_DTC_PATH)
--
2.27.0
fpga-mgr and region implementation for xclbin download which will be
called from main xrt driver
Signed-off-by: Sonal Santan <[email protected]>
Signed-off-by: Max Zhen <[email protected]>
Signed-off-by: Lizhi Hou <[email protected]>
Reviewed-by: Tom Rix <[email protected]>
---
drivers/fpga/xrt/mgmt/xmgmt-main-region.c | 483 ++++++++++++++++++++++
drivers/fpga/xrt/mgmt/xrt-mgr.c | 190 +++++++++
drivers/fpga/xrt/mgmt/xrt-mgr.h | 16 +
3 files changed, 689 insertions(+)
create mode 100644 drivers/fpga/xrt/mgmt/xmgmt-main-region.c
create mode 100644 drivers/fpga/xrt/mgmt/xrt-mgr.c
create mode 100644 drivers/fpga/xrt/mgmt/xrt-mgr.h
diff --git a/drivers/fpga/xrt/mgmt/xmgmt-main-region.c b/drivers/fpga/xrt/mgmt/xmgmt-main-region.c
new file mode 100644
index 000000000000..6e6a16b13258
--- /dev/null
+++ b/drivers/fpga/xrt/mgmt/xmgmt-main-region.c
@@ -0,0 +1,483 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * FPGA Region Support for Xilinx Alveo
+ *
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors: [email protected]
+ */
+
+#include <linux/uuid.h>
+#include <linux/fpga/fpga-bridge.h>
+#include <linux/fpga/fpga-region.h>
+#include <linux/slab.h>
+#include "metadata.h"
+#include "xleaf.h"
+#include "xleaf/axigate.h"
+#include "xclbin-helper.h"
+#include "xmgmt.h"
+
+struct xmgmt_bridge {
+ struct xrt_device *xdev;
+ const char *bridge_name;
+};
+
+struct xmgmt_region {
+ struct xrt_device *xdev;
+ struct fpga_region *region;
+ struct fpga_compat_id compat_id;
+ uuid_t interface_uuid;
+ struct fpga_bridge *bridge;
+ int group_instance;
+ uuid_t depend_uuid;
+ struct list_head list;
+};
+
+struct xmgmt_region_match_arg {
+ struct xrt_device *xdev;
+ uuid_t *uuids;
+ u32 uuid_num;
+};
+
+static int xmgmt_br_enable_set(struct fpga_bridge *bridge, bool enable)
+{
+ struct xmgmt_bridge *br_data = (struct xmgmt_bridge *)bridge->priv;
+ struct xrt_device *axigate_leaf;
+ int rc;
+
+ axigate_leaf = xleaf_get_leaf_by_epname(br_data->xdev, br_data->bridge_name);
+ if (!axigate_leaf) {
+ xrt_err(br_data->xdev, "failed to get leaf %s",
+ br_data->bridge_name);
+ return -ENOENT;
+ }
+
+ if (enable)
+ rc = xleaf_call(axigate_leaf, XRT_AXIGATE_OPEN, NULL);
+ else
+ rc = xleaf_call(axigate_leaf, XRT_AXIGATE_CLOSE, NULL);
+
+ if (rc) {
+ xrt_err(br_data->xdev, "failed to %s gate %s, rc %d",
+ (enable ? "free" : "freeze"), br_data->bridge_name,
+ rc);
+ }
+
+ xleaf_put_leaf(br_data->xdev, axigate_leaf);
+
+ return rc;
+}
+
+static const struct fpga_bridge_ops xmgmt_bridge_ops = {
+ .enable_set = xmgmt_br_enable_set
+};
+
+static void xmgmt_destroy_bridge(struct fpga_bridge *br)
+{
+ struct xmgmt_bridge *br_data = br->priv;
+
+ if (!br_data)
+ return;
+
+ xrt_info(br_data->xdev, "destroy fpga bridge %s", br_data->bridge_name);
+ fpga_bridge_unregister(br);
+
+ devm_kfree(DEV(br_data->xdev), br_data);
+
+ fpga_bridge_free(br);
+}
+
+static struct fpga_bridge *xmgmt_create_bridge(struct xrt_device *xdev,
+ char *dtb)
+{
+ struct fpga_bridge *br = NULL;
+ struct xmgmt_bridge *br_data;
+ const char *gate;
+ int rc;
+
+ br_data = devm_kzalloc(DEV(xdev), sizeof(*br_data), GFP_KERNEL);
+ if (!br_data)
+ return NULL;
+ br_data->xdev = xdev;
+
+ br_data->bridge_name = XRT_MD_NODE_GATE_ULP;
+ rc = xrt_md_find_endpoint(&xdev->dev, dtb, XRT_MD_NODE_GATE_ULP,
+ NULL, &gate);
+ if (rc) {
+ br_data->bridge_name = XRT_MD_NODE_GATE_PLP;
+ rc = xrt_md_find_endpoint(&xdev->dev, dtb, XRT_MD_NODE_GATE_PLP,
+ NULL, &gate);
+ }
+ if (rc) {
+ xrt_err(xdev, "failed to get axigate, rc %d", rc);
+ goto failed;
+ }
+
+ br = fpga_bridge_create(DEV(xdev), br_data->bridge_name,
+ &xmgmt_bridge_ops, br_data);
+ if (!br) {
+ xrt_err(xdev, "failed to create bridge");
+ goto failed;
+ }
+
+ rc = fpga_bridge_register(br);
+ if (rc) {
+ xrt_err(xdev, "failed to register bridge, rc %d", rc);
+ goto failed;
+ }
+
+ xrt_info(xdev, "created fpga bridge %s", br_data->bridge_name);
+
+ return br;
+
+failed:
+ if (br)
+ fpga_bridge_free(br);
+ if (br_data)
+ devm_kfree(DEV(xdev), br_data);
+
+ return NULL;
+}
+
+static void xmgmt_destroy_region(struct fpga_region *region)
+{
+ struct xmgmt_region *r_data = region->priv;
+
+ xrt_info(r_data->xdev, "destroy fpga region %llx.%llx",
+ region->compat_id->id_h, region->compat_id->id_l);
+
+ fpga_region_unregister(region);
+
+ if (r_data->group_instance > 0)
+ xleaf_destroy_group(r_data->xdev, r_data->group_instance);
+
+ if (r_data->bridge)
+ xmgmt_destroy_bridge(r_data->bridge);
+
+ if (r_data->region->info) {
+ fpga_image_info_free(r_data->region->info);
+ r_data->region->info = NULL;
+ }
+
+ fpga_region_free(region);
+
+ devm_kfree(DEV(r_data->xdev), r_data);
+}
+
+static int xmgmt_region_match(struct device *dev, const void *data)
+{
+ const struct xmgmt_region_match_arg *arg = data;
+ const struct fpga_region *match_region;
+ uuid_t compat_uuid;
+ int i;
+
+ if (dev->parent != &arg->xdev->dev)
+ return false;
+
+ match_region = to_fpga_region(dev);
+ /*
+ * The device tree provides both parent and child uuids for an
+ * xclbin in one array. Here we try both uuids to see if it matches
+ * with target region's compat_id. Strictly speaking we should
+ * only match xclbin's parent uuid with target region's compat_id
+ * but given the uuids by design are unique comparing with both
+ * does not hurt.
+ */
+ import_uuid(&compat_uuid, (const char *)match_region->compat_id);
+ for (i = 0; i < arg->uuid_num; i++) {
+ if (uuid_equal(&compat_uuid, &arg->uuids[i]))
+ return true;
+ }
+
+ return false;
+}
+
+static int xmgmt_region_match_base(struct device *dev, const void *data)
+{
+ const struct xmgmt_region_match_arg *arg = data;
+ const struct fpga_region *match_region;
+ const struct xmgmt_region *r_data;
+
+ if (dev->parent != &arg->xdev->dev)
+ return false;
+
+ match_region = to_fpga_region(dev);
+ r_data = match_region->priv;
+ if (uuid_is_null(&r_data->depend_uuid))
+ return true;
+
+ return false;
+}
+
+static int xmgmt_region_match_by_uuid(struct device *dev, const void *data)
+{
+ const struct xmgmt_region_match_arg *arg = data;
+ const struct fpga_region *match_region;
+ const struct xmgmt_region *r_data;
+
+ if (dev->parent != &arg->xdev->dev)
+ return false;
+
+ if (arg->uuid_num != 1)
+ return false;
+
+ match_region = to_fpga_region(dev);
+ r_data = match_region->priv;
+ if (uuid_equal(&r_data->depend_uuid, arg->uuids))
+ return true;
+
+ return false;
+}
+
+static void xmgmt_region_cleanup(struct fpga_region *region)
+{
+ struct xmgmt_region *r_data = region->priv, *pdata, *temp;
+ struct xrt_device *xdev = r_data->xdev;
+ struct xmgmt_region_match_arg arg = { 0 };
+ struct fpga_region *match_region = NULL;
+ struct device *start_dev = NULL;
+ LIST_HEAD(free_list);
+ uuid_t compat_uuid;
+
+ list_add_tail(&r_data->list, &free_list);
+ arg.xdev = xdev;
+ arg.uuid_num = 1;
+ arg.uuids = &compat_uuid;
+
+ /* find all regions depending on this region */
+ list_for_each_entry_safe(pdata, temp, &free_list, list) {
+ import_uuid(arg.uuids, (const char *)pdata->region->compat_id);
+ start_dev = NULL;
+ while ((match_region = fpga_region_class_find(start_dev, &arg,
+ xmgmt_region_match_by_uuid))) {
+ pdata = match_region->priv;
+ list_add_tail(&pdata->list, &free_list);
+ start_dev = &match_region->dev;
+ put_device(&match_region->dev);
+ }
+ }
+
+ list_del(&r_data->list);
+
+ list_for_each_entry_safe_reverse(pdata, temp, &free_list, list)
+ xmgmt_destroy_region(pdata->region);
+
+ if (r_data->group_instance > 0) {
+ xleaf_destroy_group(xdev, r_data->group_instance);
+ r_data->group_instance = -1;
+ }
+ if (r_data->region->info) {
+ fpga_image_info_free(r_data->region->info);
+ r_data->region->info = NULL;
+ }
+}
+
+void xmgmt_region_cleanup_all(struct xrt_device *xdev)
+{
+ struct xmgmt_region_match_arg arg = { 0 };
+ struct fpga_region *base_region;
+
+ arg.xdev = xdev;
+
+ while ((base_region = fpga_region_class_find(NULL, &arg, xmgmt_region_match_base))) {
+ put_device(&base_region->dev);
+
+ xmgmt_region_cleanup(base_region);
+ xmgmt_destroy_region(base_region);
+ }
+}
+
+/*
+ * Program a region with a xclbin image. Bring up the subdevs and the
+ * group object to contain the subdevs.
+ */
+static int xmgmt_region_program(struct fpga_region *region, const void *xclbin, char *dtb)
+{
+ const struct axlf *xclbin_obj = xclbin;
+ struct fpga_image_info *info;
+ struct xrt_device *xdev;
+ struct xmgmt_region *r_data;
+ int rc;
+
+ r_data = region->priv;
+ xdev = r_data->xdev;
+
+ info = fpga_image_info_alloc(&xdev->dev);
+ if (!info)
+ return -ENOMEM;
+
+ info->buf = xclbin;
+ info->count = xclbin_obj->header.length;
+ info->flags |= FPGA_MGR_PARTIAL_RECONFIG;
+ region->info = info;
+ rc = fpga_region_program_fpga(region);
+ if (rc) {
+ xrt_err(xdev, "programming xclbin failed, rc %d", rc);
+ return rc;
+ }
+
+ /* free bridges to allow reprogram */
+ if (region->get_bridges)
+ fpga_bridges_put(®ion->bridge_list);
+
+ /*
+ * Next bringup the subdevs for this region which will be managed by
+ * its own group object.
+ */
+ r_data->group_instance = xleaf_create_group(xdev, dtb);
+ if (r_data->group_instance < 0) {
+ xrt_err(xdev, "failed to create group, rc %d",
+ r_data->group_instance);
+ rc = r_data->group_instance;
+ return rc;
+ }
+
+ rc = xleaf_wait_for_group_bringup(xdev);
+ if (rc)
+ xrt_err(xdev, "group bringup failed, rc %d", rc);
+ return rc;
+}
+
+static int xmgmt_get_bridges(struct fpga_region *region)
+{
+ struct xmgmt_region *r_data = region->priv;
+ struct device *dev = &r_data->xdev->dev;
+
+ return fpga_bridge_get_to_list(dev, region->info, ®ion->bridge_list);
+}
+
+/*
+ * Program/create FPGA regions based on input xclbin file.
+ * 1. Identify a matching existing region for this xclbin
+ * 2. Tear down any previous objects for the found region
+ * 3. Program this region with input xclbin
+ * 4. Iterate over this region's interface uuids to determine if it defines any
+ * child region. Create fpga_region for the child region.
+ */
+int xmgmt_process_xclbin(struct xrt_device *xdev,
+ struct fpga_manager *fmgr,
+ const struct axlf *xclbin,
+ enum provider_kind kind)
+{
+ struct fpga_region *region, *compat_region = NULL;
+ struct xmgmt_region_match_arg arg = { 0 };
+ struct xmgmt_region *r_data;
+ uuid_t compat_uuid;
+ char *dtb = NULL;
+ int rc, i;
+
+ rc = xrt_xclbin_get_metadata(DEV(xdev), xclbin, &dtb);
+ if (rc) {
+ xrt_err(xdev, "failed to get dtb: %d", rc);
+ goto failed;
+ }
+
+ rc = xrt_md_get_interface_uuids(DEV(xdev), dtb, 0, NULL);
+ if (rc < 0) {
+ xrt_err(xdev, "failed to get intf uuid");
+ rc = -EINVAL;
+ goto failed;
+ }
+ arg.uuid_num = rc;
+ arg.uuids = kcalloc(arg.uuid_num, sizeof(uuid_t), GFP_KERNEL);
+ if (!arg.uuids) {
+ rc = -ENOMEM;
+ goto failed;
+ }
+ arg.xdev = xdev;
+
+ rc = xrt_md_get_interface_uuids(DEV(xdev), dtb, arg.uuid_num, arg.uuids);
+ if (rc != arg.uuid_num) {
+ xrt_err(xdev, "only get %d uuids, expect %d", rc, arg.uuid_num);
+ rc = -EINVAL;
+ goto failed;
+ }
+
+ /* if this is not base firmware, search for a compatible region */
+ if (kind != XMGMT_BLP) {
+ compat_region = fpga_region_class_find(NULL, &arg, xmgmt_region_match);
+ if (!compat_region) {
+ xrt_err(xdev, "failed to get compatible region");
+ rc = -ENOENT;
+ goto failed;
+ }
+
+ xmgmt_region_cleanup(compat_region);
+
+ rc = xmgmt_region_program(compat_region, xclbin, dtb);
+ if (rc) {
+ xrt_err(xdev, "failed to program region");
+ goto failed;
+ }
+ }
+
+ if (compat_region)
+ import_uuid(&compat_uuid, (const char *)compat_region->compat_id);
+
+ /* create all the new regions contained in this xclbin */
+ for (i = 0; i < arg.uuid_num; i++) {
+ if (compat_region && uuid_equal(&compat_uuid, &arg.uuids[i])) {
+ /* region for this interface already exists */
+ continue;
+ }
+
+ region = fpga_region_create(DEV(xdev), fmgr, xmgmt_get_bridges);
+ if (!region) {
+ xrt_err(xdev, "failed to create fpga region");
+ rc = -EFAULT;
+ goto failed;
+ }
+ r_data = devm_kzalloc(DEV(xdev), sizeof(*r_data), GFP_KERNEL);
+ if (!r_data) {
+ rc = -ENOMEM;
+ fpga_region_free(region);
+ goto failed;
+ }
+ r_data->xdev = xdev;
+ r_data->region = region;
+ r_data->group_instance = -1;
+ uuid_copy(&r_data->interface_uuid, &arg.uuids[i]);
+ if (compat_region)
+ import_uuid(&r_data->depend_uuid, (const char *)compat_region->compat_id);
+ r_data->bridge = xmgmt_create_bridge(xdev, dtb);
+ if (!r_data->bridge) {
+ xrt_err(xdev, "failed to create fpga bridge");
+ rc = -EFAULT;
+ devm_kfree(DEV(xdev), r_data);
+ fpga_region_free(region);
+ goto failed;
+ }
+
+ region->compat_id = &r_data->compat_id;
+ export_uuid((char *)region->compat_id, &r_data->interface_uuid);
+ region->priv = r_data;
+
+ rc = fpga_region_register(region);
+ if (rc) {
+ xrt_err(xdev, "failed to register fpga region");
+ xmgmt_destroy_bridge(r_data->bridge);
+ fpga_region_free(region);
+ devm_kfree(DEV(xdev), r_data);
+ goto failed;
+ }
+
+ xrt_info(xdev, "created fpga region %llx.%llx",
+ region->compat_id->id_h, region->compat_id->id_l);
+ }
+
+ if (compat_region)
+ put_device(&compat_region->dev);
+ vfree(dtb);
+ kfree(arg.uuids);
+ return 0;
+
+failed:
+ if (compat_region) {
+ put_device(&compat_region->dev);
+ xmgmt_region_cleanup(compat_region);
+ }
+
+ vfree(dtb);
+ kfree(arg.uuids);
+ return rc;
+}
diff --git a/drivers/fpga/xrt/mgmt/xrt-mgr.c b/drivers/fpga/xrt/mgmt/xrt-mgr.c
new file mode 100644
index 000000000000..ab253b516e8d
--- /dev/null
+++ b/drivers/fpga/xrt/mgmt/xrt-mgr.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * FPGA Manager Support for Xilinx Alveo
+ *
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors: [email protected]
+ */
+
+#include <linux/cred.h>
+#include <linux/efi.h>
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+
+#include "xclbin-helper.h"
+#include "xleaf.h"
+#include "xrt-mgr.h"
+#include "xleaf/axigate.h"
+#include "xleaf/icap.h"
+#include "xmgmt.h"
+
+struct xfpga_class {
+ struct xrt_device *xdev;
+ char name[64];
+};
+
+/*
+ * xclbin download plumbing -- find the download subsystem, ICAP and
+ * pass the xclbin for heavy lifting
+ */
+static int xmgmt_download_bitstream(struct xrt_device *xdev,
+ const struct axlf *xclbin)
+
+{
+ struct xclbin_bit_head_info bit_header = { 0 };
+ struct xrt_device *icap_leaf = NULL;
+ struct xrt_icap_wr arg;
+ char *bitstream = NULL;
+ u64 bit_len;
+ int ret;
+
+ ret = xrt_xclbin_get_section(DEV(xdev), xclbin, BITSTREAM, (void **)&bitstream, &bit_len);
+ if (ret) {
+ xrt_err(xdev, "bitstream not found");
+ return -ENOENT;
+ }
+ ret = xrt_xclbin_parse_bitstream_header(DEV(xdev), bitstream,
+ XCLBIN_HWICAP_BITFILE_BUF_SZ,
+ &bit_header);
+ if (ret) {
+ ret = -EINVAL;
+ xrt_err(xdev, "invalid bitstream header");
+ goto fail;
+ }
+ if (bit_header.header_length + bit_header.bitstream_length > bit_len) {
+ ret = -EINVAL;
+ xrt_err(xdev, "invalid bitstream length. header %d, bitstream %d, section len %lld",
+ bit_header.header_length, bit_header.bitstream_length, bit_len);
+ goto fail;
+ }
+
+ icap_leaf = xleaf_get_leaf_by_id(xdev, XRT_SUBDEV_ICAP, XRT_INVALID_DEVICE_INST);
+ if (!icap_leaf) {
+ ret = -ENODEV;
+ xrt_err(xdev, "icap does not exist");
+ goto fail;
+ }
+ arg.xiiw_bit_data = bitstream + bit_header.header_length;
+ arg.xiiw_data_len = bit_header.bitstream_length;
+ ret = xleaf_call(icap_leaf, XRT_ICAP_WRITE, &arg);
+ if (ret) {
+ xrt_err(xdev, "write bitstream failed, ret = %d", ret);
+ xleaf_put_leaf(xdev, icap_leaf);
+ goto fail;
+ }
+
+ xleaf_put_leaf(xdev, icap_leaf);
+ vfree(bitstream);
+
+ return 0;
+
+fail:
+ vfree(bitstream);
+
+ return ret;
+}
+
+/*
+ * There is no HW prep work we do here since we need the full
+ * xclbin for its sanity check.
+ */
+static int xmgmt_pr_write_init(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
+ const char *buf, size_t count)
+{
+ const struct axlf *bin = (const struct axlf *)buf;
+ struct xfpga_class *obj = mgr->priv;
+
+ if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
+ xrt_info(obj->xdev, "%s only supports partial reconfiguration\n", obj->name);
+ return -EINVAL;
+ }
+
+ if (count < sizeof(struct axlf))
+ return -EINVAL;
+
+ if (count > bin->header.length)
+ return -EINVAL;
+
+ xrt_info(obj->xdev, "Prepare download of xclbin %pUb of length %lld B",
+ &bin->header.uuid, bin->header.length);
+
+ return 0;
+}
+
+/*
+ * The implementation requires full xclbin image before we can start
+ * programming the hardware via ICAP subsystem. The full image is required
+ * for checking the validity of xclbin and walking the sections to
+ * discover the bitstream.
+ */
+static int xmgmt_pr_write(struct fpga_manager *mgr,
+ const char *buf, size_t count)
+{
+ const struct axlf *bin = (const struct axlf *)buf;
+ struct xfpga_class *obj = mgr->priv;
+
+ if (bin->header.length != count)
+ return -EINVAL;
+
+ return xmgmt_download_bitstream((void *)obj->xdev, bin);
+}
+
+static int xmgmt_pr_write_complete(struct fpga_manager *mgr,
+ struct fpga_image_info *info)
+{
+ const struct axlf *bin = (const struct axlf *)info->buf;
+ struct xfpga_class *obj = mgr->priv;
+
+ xrt_info(obj->xdev, "Finished download of xclbin %pUb",
+ &bin->header.uuid);
+ return 0;
+}
+
+static enum fpga_mgr_states xmgmt_pr_state(struct fpga_manager *mgr)
+{
+ return FPGA_MGR_STATE_UNKNOWN;
+}
+
+static const struct fpga_manager_ops xmgmt_pr_ops = {
+ .initial_header_size = sizeof(struct axlf),
+ .write_init = xmgmt_pr_write_init,
+ .write = xmgmt_pr_write,
+ .write_complete = xmgmt_pr_write_complete,
+ .state = xmgmt_pr_state,
+};
+
+struct fpga_manager *xmgmt_fmgr_probe(struct xrt_device *xdev)
+{
+ struct xfpga_class *obj = devm_kzalloc(DEV(xdev), sizeof(struct xfpga_class),
+ GFP_KERNEL);
+ struct fpga_manager *fmgr = NULL;
+ int ret = 0;
+
+ if (!obj)
+ return ERR_PTR(-ENOMEM);
+
+ snprintf(obj->name, sizeof(obj->name), "Xilinx Alveo FPGA Manager");
+ obj->xdev = xdev;
+ fmgr = fpga_mgr_create(&xdev->dev,
+ obj->name,
+ &xmgmt_pr_ops,
+ obj);
+ if (!fmgr)
+ return ERR_PTR(-ENOMEM);
+
+ ret = fpga_mgr_register(fmgr);
+ if (ret) {
+ fpga_mgr_free(fmgr);
+ return ERR_PTR(ret);
+ }
+ return fmgr;
+}
+
+int xmgmt_fmgr_remove(struct fpga_manager *fmgr)
+{
+ fpga_mgr_unregister(fmgr);
+ return 0;
+}
diff --git a/drivers/fpga/xrt/mgmt/xrt-mgr.h b/drivers/fpga/xrt/mgmt/xrt-mgr.h
new file mode 100644
index 000000000000..a3d1ab1c34f0
--- /dev/null
+++ b/drivers/fpga/xrt/mgmt/xrt-mgr.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors: [email protected]
+ */
+
+#ifndef _XRT_MGR_H_
+#define _XRT_MGR_H_
+
+#include <linux/fpga/fpga-mgr.h>
+
+struct fpga_manager *xmgmt_fmgr_probe(struct xrt_device *xdev);
+int xmgmt_fmgr_remove(struct fpga_manager *fmgr);
+
+#endif /* _XRT_MGR_H_ */
--
2.27.0
Add partition isolation xrt driver. partition isolation is
a hardware function discovered by walking firmware metadata.
A xrt device node will be created for it. Partition isolation
function isolate the different fpga regions
Signed-off-by: Sonal Santan <[email protected]>
Signed-off-by: Max Zhen <[email protected]>
Signed-off-by: Lizhi Hou <[email protected]>
Reviewed-by: Tom Rix <[email protected]>
---
drivers/fpga/xrt/include/xleaf/axigate.h | 23 ++
drivers/fpga/xrt/lib/xleaf/axigate.c | 325 +++++++++++++++++++++++
2 files changed, 348 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/axigate.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/axigate.c
diff --git a/drivers/fpga/xrt/include/xleaf/axigate.h b/drivers/fpga/xrt/include/xleaf/axigate.h
new file mode 100644
index 000000000000..58f32c76dca1
--- /dev/null
+++ b/drivers/fpga/xrt/include/xleaf/axigate.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Lizhi Hou <[email protected]>
+ */
+
+#ifndef _XRT_AXIGATE_H_
+#define _XRT_AXIGATE_H_
+
+#include "xleaf.h"
+#include "metadata.h"
+
+/*
+ * AXIGATE driver leaf calls.
+ */
+enum xrt_axigate_leaf_cmd {
+ XRT_AXIGATE_CLOSE = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
+ XRT_AXIGATE_OPEN,
+};
+
+#endif /* _XRT_AXIGATE_H_ */
diff --git a/drivers/fpga/xrt/lib/xleaf/axigate.c b/drivers/fpga/xrt/lib/xleaf/axigate.c
new file mode 100644
index 000000000000..493707b782e4
--- /dev/null
+++ b/drivers/fpga/xrt/lib/xleaf/axigate.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Alveo FPGA AXI Gate Driver
+ *
+ * Copyright (C) 2020-2021 Xilinx, Inc.
+ *
+ * Authors:
+ * Lizhi Hou<[email protected]>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/io.h>
+#include "metadata.h"
+#include "xleaf.h"
+#include "xleaf/axigate.h"
+
+#define XRT_AXIGATE "xrt_axigate"
+
+#define XRT_AXIGATE_WRITE_REG 0
+#define XRT_AXIGATE_READ_REG 8
+
+#define XRT_AXIGATE_CTRL_CLOSE 0
+#define XRT_AXIGATE_CTRL_OPEN_BIT0 1
+#define XRT_AXIGATE_CTRL_OPEN_BIT1 2
+
+#define XRT_AXIGATE_INTERVAL 500 /* ns */
+
+struct xrt_axigate {
+ struct xrt_device *xdev;
+ struct regmap *regmap;
+ struct mutex gate_lock; /* gate dev lock */
+ void *evt_hdl;
+ const char *ep_name;
+ bool gate_closed;
+};
+
+XRT_DEFINE_REGMAP_CONFIG(axigate_regmap_config);
+
+/* the ep names are in the order of hardware layers */
+static const char * const xrt_axigate_epnames[] = {
+ XRT_MD_NODE_GATE_PLP, /* PLP: Provider Logic Partition */
+ XRT_MD_NODE_GATE_ULP /* ULP: User Logic Partition */
+};
+
+static inline int close_gate(struct xrt_axigate *gate)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_write(gate->regmap, XRT_AXIGATE_WRITE_REG, XRT_AXIGATE_CTRL_CLOSE);
+ if (ret) {
+ xrt_err(gate->xdev, "write gate failed %d", ret);
+ return ret;
+ }
+ ndelay(XRT_AXIGATE_INTERVAL);
+ /*
+ * Legacy hardware requires extra read work properly.
+ * This is not on critical path, thus the extra read should not impact performance much.
+ */
+ ret = regmap_read(gate->regmap, XRT_AXIGATE_READ_REG, &val);
+ if (ret) {
+ xrt_err(gate->xdev, "read gate failed %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int open_gate(struct xrt_axigate *gate)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_write(gate->regmap, XRT_AXIGATE_WRITE_REG, XRT_AXIGATE_CTRL_OPEN_BIT1);
+ if (ret) {
+ xrt_err(gate->xdev, "write 2 failed %d", ret);
+ return ret;
+ }
+ ndelay(XRT_AXIGATE_INTERVAL);
+ /*
+ * Legacy hardware requires extra read work properly.
+ * This is not on critical path, thus the extra read should not impact performance much.
+ */
+ ret = regmap_read(gate->regmap, XRT_AXIGATE_READ_REG, &val);
+ if (ret) {
+ xrt_err(gate->xdev, "read 2 failed %d", ret);
+ return ret;
+ }
+ ret = regmap_write(gate->regmap, XRT_AXIGATE_WRITE_REG,
+ XRT_AXIGATE_CTRL_OPEN_BIT0 | XRT_AXIGATE_CTRL_OPEN_BIT1);
+ if (ret) {
+ xrt_err(gate->xdev, "write 3 failed %d", ret);
+ return ret;
+ }
+ ndelay(XRT_AXIGATE_INTERVAL);
+ ret = regmap_read(gate->regmap, XRT_AXIGATE_READ_REG, &val);
+ if (ret) {
+ xrt_err(gate->xdev, "read 3 failed %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int xrt_axigate_epname_idx(struct xrt_device *xdev)
+{
+ struct resource *res;
+ int ret, i;
+
+ res = xrt_get_resource(xdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ xrt_err(xdev, "Empty Resource!");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(xrt_axigate_epnames); i++) {
+ ret = strncmp(xrt_axigate_epnames[i], res->name,
+ strlen(xrt_axigate_epnames[i]) + 1);
+ if (!ret)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int xrt_axigate_close(struct xrt_device *xdev)
+{
+ struct xrt_axigate *gate;
+ u32 status = 0;
+ int ret;
+
+ gate = xrt_get_drvdata(xdev);
+
+ mutex_lock(&gate->gate_lock);
+ ret = regmap_read(gate->regmap, XRT_AXIGATE_READ_REG, &status);
+ if (ret) {
+ xrt_err(xdev, "read gate failed %d", ret);
+ goto failed;
+ }
+ if (status) { /* gate is opened */
+ xleaf_broadcast_event(xdev, XRT_EVENT_PRE_GATE_CLOSE, false);
+ ret = close_gate(gate);
+ if (ret)
+ goto failed;
+ }
+
+ gate->gate_closed = true;
+
+failed:
+ mutex_unlock(&gate->gate_lock);
+
+ xrt_info(xdev, "close gate %s", gate->ep_name);
+ return ret;
+}
+
+static int xrt_axigate_open(struct xrt_device *xdev)
+{
+ struct xrt_axigate *gate;
+ u32 status;
+ int ret;
+
+ gate = xrt_get_drvdata(xdev);
+
+ mutex_lock(&gate->gate_lock);
+ ret = regmap_read(gate->regmap, XRT_AXIGATE_READ_REG, &status);
+ if (ret) {
+ xrt_err(xdev, "read gate failed %d", ret);
+ goto failed;
+ }
+ if (!status) { /* gate is closed */
+ ret = open_gate(gate);
+ if (ret)
+ goto failed;
+ xleaf_broadcast_event(xdev, XRT_EVENT_POST_GATE_OPEN, true);
+ /* xrt_axigate_open() could be called in event cb, thus
+ * we can not wait for the completes
+ */
+ }
+
+ gate->gate_closed = false;
+
+failed:
+ mutex_unlock(&gate->gate_lock);
+
+ xrt_info(xdev, "open gate %s", gate->ep_name);
+ return ret;
+}
+
+static void xrt_axigate_event_cb(struct xrt_device *xdev, void *arg)
+{
+ struct xrt_axigate *gate = xrt_get_drvdata(xdev);
+ struct xrt_event *evt = (struct xrt_event *)arg;
+ enum xrt_events e = evt->xe_evt;
+ struct xrt_device *leaf;
+ enum xrt_subdev_id id;
+ struct resource *res;
+ int instance;
+
+ if (e != XRT_EVENT_POST_CREATION)
+ return;
+
+ instance = evt->xe_subdev.xevt_subdev_instance;
+ id = evt->xe_subdev.xevt_subdev_id;
+ if (id != XRT_SUBDEV_AXIGATE)
+ return;
+
+ leaf = xleaf_get_leaf_by_id(xdev, id, instance);
+ if (!leaf)
+ return;
+
+ res = xrt_get_resource(leaf, IORESOURCE_MEM, 0);
+ if (!res || !strncmp(res->name, gate->ep_name, strlen(res->name) + 1)) {
+ xleaf_put_leaf(xdev, leaf);
+ return;
+ }
+
+ /* higher level axigate instance created, make sure the gate is opened. */
+ if (xrt_axigate_epname_idx(leaf) > xrt_axigate_epname_idx(xdev))
+ xrt_axigate_open(xdev);
+ else
+ xleaf_call(leaf, XRT_AXIGATE_OPEN, NULL);
+
+ xleaf_put_leaf(xdev, leaf);
+}
+
+static int
+xrt_axigate_leaf_call(struct xrt_device *xdev, u32 cmd, void *arg)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case XRT_XLEAF_EVENT:
+ xrt_axigate_event_cb(xdev, arg);
+ break;
+ case XRT_AXIGATE_CLOSE:
+ ret = xrt_axigate_close(xdev);
+ break;
+ case XRT_AXIGATE_OPEN:
+ ret = xrt_axigate_open(xdev);
+ break;
+ default:
+ xrt_err(xdev, "unsupported cmd %d", cmd);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int xrt_axigate_probe(struct xrt_device *xdev)
+{
+ struct xrt_axigate *gate = NULL;
+ void __iomem *base = NULL;
+ struct resource *res;
+ int ret;
+
+ gate = devm_kzalloc(&xdev->dev, sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return -ENOMEM;
+
+ gate->xdev = xdev;
+ xrt_set_drvdata(xdev, gate);
+
+ xrt_info(xdev, "probing...");
+ res = xrt_get_resource(xdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ xrt_err(xdev, "Empty resource 0");
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ base = devm_ioremap_resource(&xdev->dev, res);
+ if (IS_ERR(base)) {
+ xrt_err(xdev, "map base iomem failed");
+ ret = PTR_ERR(base);
+ goto failed;
+ }
+
+ gate->regmap = devm_regmap_init_mmio(&xdev->dev, base, &axigate_regmap_config);
+ if (IS_ERR(gate->regmap)) {
+ xrt_err(xdev, "regmap %pR failed", res);
+ ret = PTR_ERR(gate->regmap);
+ goto failed;
+ }
+ gate->ep_name = res->name;
+
+ mutex_init(&gate->gate_lock);
+
+ return 0;
+
+failed:
+ return ret;
+}
+
+static struct xrt_dev_endpoints xrt_axigate_endpoints[] = {
+ {
+ .xse_names = (struct xrt_dev_ep_names[]) {
+ { .ep_name = XRT_MD_NODE_GATE_ULP },
+ { NULL },
+ },
+ .xse_min_ep = 1,
+ },
+ {
+ .xse_names = (struct xrt_dev_ep_names[]) {
+ { .ep_name = XRT_MD_NODE_GATE_PLP },
+ { NULL },
+ },
+ .xse_min_ep = 1,
+ },
+ { 0 },
+};
+
+static struct xrt_driver xrt_axigate_driver = {
+ .driver = {
+ .name = XRT_AXIGATE,
+ },
+ .subdev_id = XRT_SUBDEV_AXIGATE,
+ .endpoints = xrt_axigate_endpoints,
+ .probe = xrt_axigate_probe,
+ .leaf_call = xrt_axigate_leaf_call,
+};
+
+XRT_LEAF_INIT_FINI_FUNC(axigate);
--
2.27.0
On 8/2/21 9:05 AM, Lizhi Hou wrote:
> xrt-lib kernel module infrastructure code to register and manage all
> leaf driver modules.
>
> Signed-off-by: Sonal Santan <[email protected]>
> Signed-off-by: Max Zhen <[email protected]>
> Signed-off-by: Lizhi Hou <[email protected]>
> Reviewed-by: Tom Rix <[email protected]>
This was the only patch with requested changes in v8.
All the changes from v8 have been made.
They are itemized below with variations on 'ok'
It is still Moritz's call to accept them.
Tom
> ---
> drivers/fpga/xrt/include/subdev_id.h | 39 +++
> drivers/fpga/xrt/include/xdevice.h | 141 ++++++++
> drivers/fpga/xrt/include/xleaf.h | 205 +++++++++++
> drivers/fpga/xrt/include/xleaf/clkfreq.h | 21 ++
> drivers/fpga/xrt/include/xleaf/clock.h | 29 ++
> .../fpga/xrt/include/xleaf/ddr_calibration.h | 28 ++
> drivers/fpga/xrt/include/xleaf/devctl.h | 40 +++
> drivers/fpga/xrt/lib/lib-drv.c | 318 ++++++++++++++++++
> drivers/fpga/xrt/lib/lib-drv.h | 21 ++
> 9 files changed, 842 insertions(+)
> create mode 100644 drivers/fpga/xrt/include/subdev_id.h
> create mode 100644 drivers/fpga/xrt/include/xdevice.h
> create mode 100644 drivers/fpga/xrt/include/xleaf.h
> create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
> create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
> create mode 100644 drivers/fpga/xrt/include/xleaf/ddr_calibration.h
> create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
> create mode 100644 drivers/fpga/xrt/lib/lib-drv.c
> create mode 100644 drivers/fpga/xrt/lib/lib-drv.h
>
> diff --git a/drivers/fpga/xrt/include/subdev_id.h b/drivers/fpga/xrt/include/subdev_id.h
> new file mode 100644
> index 000000000000..02df4b939a1b
> --- /dev/null
> +++ b/drivers/fpga/xrt/include/subdev_id.h
> @@ -0,0 +1,39 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Cheng Zhen <[email protected]>
> + */
> +
> +#ifndef _XRT_SUBDEV_ID_H_
> +#define _XRT_SUBDEV_ID_H_
> +
> +/*
> + * Every subdev driver has an ID for others to refer to it. There can be multiple number of
> + * instances of a subdev driver. A <subdev_id, subdev_instance> tuple is a unique identification
> + * of a specific instance of a subdev driver.
> + */
> +enum xrt_subdev_id {
> + XRT_SUBDEV_INVALID = 0,
> + XRT_SUBDEV_GRP,
> + XRT_SUBDEV_VSEC,
> + XRT_SUBDEV_VSEC_GOLDEN,
> + XRT_SUBDEV_DEVCTL,
> + XRT_SUBDEV_AXIGATE,
> + XRT_SUBDEV_ICAP,
> + XRT_SUBDEV_TEST,
> + XRT_SUBDEV_MGMT_MAIN,
> + XRT_SUBDEV_QSPI,
> + XRT_SUBDEV_MAILBOX,
> + XRT_SUBDEV_CMC,
> + XRT_SUBDEV_CALIB,
> + XRT_SUBDEV_CLKFREQ,
> + XRT_SUBDEV_CLOCK,
> + XRT_SUBDEV_SRSR,
> + XRT_SUBDEV_UCS,
> + XRT_SUBDEV_NUM, /* Total number of subdevs. */
> + XRT_ROOT = -1, /* Special ID for root driver. */
> +};
> +
> +#endif /* _XRT_SUBDEV_ID_H_ */
> diff --git a/drivers/fpga/xrt/include/xdevice.h b/drivers/fpga/xrt/include/xdevice.h
> new file mode 100644
> index 000000000000..b40ebe98b54d
> --- /dev/null
> +++ b/drivers/fpga/xrt/include/xdevice.h
> @@ -0,0 +1,141 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Lizhi Hou <[email protected]>
> + */
> +
> +#ifndef _XRT_DEVICE_H_
> +#define _XRT_DEVICE_H_
> +
> +#include <linux/fs.h>
> +#include <linux/cdev.h>
> +
> +#define XRT_MAX_DEVICE_NODES 128
> +#define XRT_INVALID_DEVICE_INST (XRT_MAX_DEVICE_NODES + 1)
> +
> +enum {
> + XRT_DEVICE_STATE_NONE = 0,
> + XRT_DEVICE_STATE_ADDED
> +};
> +
> +/*
> + * struct xrt_device - represent an xrt device on xrt bus
> + *
> + * dev: generic device interface.
> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
ok
> + * name: name of the xrt device.
> + * instance: instance of the xrt device. The xrt device with same id can have
> + * more than 1 instances.
> + * state: current state of the xrt device.
> + * num_resources: The total number of resource for the xrt device.
> + * resource: point to the xrt device resource array.
> + * sdev_data: private data pointer.
> + */
> +struct xrt_device {
> + struct device dev;
> + u32 subdev_id;
> + const char *name;
> + u32 instance;
> + u32 state;
> + u32 num_resources;
> + struct resource *resource;
> + void *sdev_data;
> +};
> +
> +/*
> + * If populated by xrt device driver, infra will handle the mechanics of
> + * char device (un)registration.
> + */
> +enum xrt_dev_file_mode {
> + /* Infra create cdev, default file name */
> + XRT_DEV_FILE_DEFAULT = 0,
> + /* Infra create cdev, need to encode inst num in file name */
> + XRT_DEV_FILE_MULTI_INST,
> + /* No auto creation of cdev by infra, leaf handles it by itself */
> + XRT_DEV_FILE_NO_AUTO,
> +};
> +
> +struct xrt_dev_file_ops {
> + const struct file_operations xsf_ops;
> + dev_t xsf_dev_t;
> + const char *xsf_dev_name;
> + enum xrt_dev_file_mode xsf_mode;
> +};
> +
> +/*
> + * this struct define the endpoints belong to the same xrt device
> + * ep_name: endpoint name
> + * compat: compatible string
ok
> + */
> +struct xrt_dev_ep_names {
> + const char *ep_name;
> + const char *compat;
> +};
> +
> +struct xrt_dev_endpoints {
> + struct xrt_dev_ep_names *xse_names;
> + /* minimum number of endpoints to support the subdevice */
> + u32 xse_min_ep;
> +};
> +
> +/*
> + * struct xrt_driver - represent a xrt device driver
> + *
> + * driver: driver model structure.
> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
> + * file_ops: character device name and callbacks.
> + * probe: mandatory callback for device binding.
> + * remove: callback for device unbinding.
> + * leaf_call: callback for servicing other leaf drivers.
ok
> + */
> +struct xrt_driver {
> + struct device_driver driver;
> + u32 subdev_id;
> + struct xrt_dev_file_ops file_ops;
> + struct xrt_dev_endpoints *endpoints;
> +
> + /*
> + * Subdev driver callbacks populated by subdev driver.
> + */
> + int (*probe)(struct xrt_device *xrt_dev);
> + void (*remove)(struct xrt_device *xrt_dev);
> + /*
> + * If leaf_call is defined, these are called by other leaf drivers.
> + * Note that root driver may call into leaf_call of a group driver.
> + */
> + int (*leaf_call)(struct xrt_device *xrt_dev, u32 cmd, void *arg);
> +};
> +
> +#define to_xrt_dev(d) container_of(d, struct xrt_device, dev)
> +#define to_xrt_drv(d) container_of(d, struct xrt_driver, driver)
> +
> +static inline void *xrt_get_drvdata(const struct xrt_device *xdev)
> +{
> + return dev_get_drvdata(&xdev->dev);
> +}
> +
> +static inline void xrt_set_drvdata(struct xrt_device *xdev, void *data)
> +{
> + dev_set_drvdata(&xdev->dev, data);
> +}
> +
> +static inline void *xrt_get_xdev_data(struct device *dev)
> +{
> + struct xrt_device *xdev = to_xrt_dev(dev);
> +
> + return xdev->sdev_data;
> +}
> +
> +struct xrt_device *
> +xrt_device_register(struct device *parent, u32 id,
> + struct resource *res, u32 res_num,
> + void *pdata, size_t data_sz);
> +void xrt_device_unregister(struct xrt_device *xdev);
> +int xrt_register_driver(struct xrt_driver *drv);
> +void xrt_unregister_driver(struct xrt_driver *drv);
> +void *xrt_get_xdev_data(struct device *dev);
> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32 type, u32 num);
> +
> +#endif /* _XRT_DEVICE_H_ */
> diff --git a/drivers/fpga/xrt/include/xleaf.h b/drivers/fpga/xrt/include/xleaf.h
> new file mode 100644
> index 000000000000..f065fc766e0f
> --- /dev/null
> +++ b/drivers/fpga/xrt/include/xleaf.h
> @@ -0,0 +1,205 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Cheng Zhen <[email protected]>
> + * Sonal Santan <[email protected]>
> + */
> +
> +#ifndef _XRT_XLEAF_H_
> +#define _XRT_XLEAF_H_
> +
> +#include <linux/mod_devicetable.h>
> +#include "xdevice.h"
> +#include "subdev_id.h"
> +#include "xroot.h"
> +#include "events.h"
> +
> +/* All subdev drivers should use below common routines to print out msg. */
> +#define DEV(xdev) (&(xdev)->dev)
> +#define DEV_PDATA(xdev) \
> + ((struct xrt_subdev_platdata *)xrt_get_xdev_data(DEV(xdev)))
> +#define DEV_FILE_OPS(xdev) \
> + (&(to_xrt_drv((xdev)->dev.driver))->file_ops)
> +#define FMT_PRT(prt_fn, xdev, fmt, args...) \
> + ({typeof(xdev) (_xdev) = (xdev); \
> + prt_fn(DEV(_xdev), "%s %s: " fmt, \
> + DEV_PDATA(_xdev)->xsp_root_name, __func__, ##args); })
> +#define xrt_err(xdev, fmt, args...) FMT_PRT(dev_err, xdev, fmt, ##args)
> +#define xrt_warn(xdev, fmt, args...) FMT_PRT(dev_warn, xdev, fmt, ##args)
> +#define xrt_info(xdev, fmt, args...) FMT_PRT(dev_info, xdev, fmt, ##args)
> +#define xrt_dbg(xdev, fmt, args...) FMT_PRT(dev_dbg, xdev, fmt, ##args)
> +
> +#define XRT_DEFINE_REGMAP_CONFIG(config_name) \
> + static const struct regmap_config config_name = { \
> + .reg_bits = 32, \
> + .val_bits = 32, \
> + .reg_stride = 4, \
> + .max_register = 0x1000, \
> + }
> +
> +enum {
> + /* Starting cmd for common leaf cmd implemented by all leaves. */
> + XRT_XLEAF_COMMON_BASE = 0,
> + /* Starting cmd for leaves' specific leaf cmds. */
> + XRT_XLEAF_CUSTOM_BASE = 64,
> +};
> +
> +enum xrt_xleaf_common_leaf_cmd {
> + XRT_XLEAF_EVENT = XRT_XLEAF_COMMON_BASE,
> +};
> +
> +/*
> + * Partially initialized by the parent driver, then, passed in as subdev driver's
> + * platform data when creating subdev driver instance by calling platform
> + * device register API (xrt_device_register_data() or the likes).
> + *
> + * Once device register API returns, platform driver framework makes a copy of
> + * this buffer and maintains its life cycle. The content of the buffer is
> + * completely owned by subdev driver.
> + *
> + * Thus, parent driver should be very careful when it touches this buffer
> + * again once it's handed over to subdev driver. And the data structure
> + * should not contain pointers pointing to buffers that is managed by
> + * other or parent drivers since it could have been freed before platform
> + * data buffer is freed by platform driver framework.
> + */
> +struct xrt_subdev_platdata {
> + /*
> + * Per driver instance callback. The xdev points to the instance.
> + * Should always be defined for subdev driver to get service from root.
> + */
> + xrt_subdev_root_cb_t xsp_root_cb;
> + void *xsp_root_cb_arg;
> +
> + /* Something to associate w/ root for msg printing. */
> + const char *xsp_root_name;
> +
> + /*
> + * Char dev support for this subdev instance.
> + * Initialized by subdev driver.
> + */
> + struct cdev xsp_cdev;
> + struct device *xsp_sysdev;
> + struct mutex xsp_devnode_lock; /* devnode lock */
> + struct completion xsp_devnode_comp;
> + int xsp_devnode_ref;
> + bool xsp_devnode_online;
> + bool xsp_devnode_excl;
> +
> + /*
> + * Subdev driver specific init data. The buffer should be embedded
> + * in this data structure buffer after dtb, so that it can be freed
> + * together with platform data.
> + */
> + loff_t xsp_priv_off; /* Offset into this platform data buffer. */
> + size_t xsp_priv_len;
> +
> + /*
> + * Populated by parent driver to describe the device tree for
> + * the subdev driver to handle. Should always be last one since it's
> + * of variable length.
> + */
> + bool xsp_dtb_valid;
> + char xsp_dtb[0];
> +};
> +
> +struct subdev_match_arg {
> + enum xrt_subdev_id id;
> + int instance;
> +};
> +
> +bool xleaf_has_endpoint(struct xrt_device *xdev, const char *endpoint_name);
> +struct xrt_device *xleaf_get_leaf(struct xrt_device *xdev,
> + xrt_subdev_match_t cb, void *arg);
> +
> +static inline bool subdev_match(enum xrt_subdev_id id, struct xrt_device *xdev, void *arg)
> +{
> + const struct subdev_match_arg *a = (struct subdev_match_arg *)arg;
> + int instance = a->instance;
> +
> + if (id != a->id)
> + return false;
> + if (instance != xdev->instance && instance != XRT_INVALID_DEVICE_INST)
> + return false;
> + return true;
> +}
> +
> +static inline bool xrt_subdev_match_epname(enum xrt_subdev_id id,
> + struct xrt_device *xdev, void *arg)
> +{
> + return xleaf_has_endpoint(xdev, arg);
> +}
> +
> +static inline struct xrt_device *
> +xleaf_get_leaf_by_id(struct xrt_device *xdev,
> + enum xrt_subdev_id id, int instance)
> +{
> + struct subdev_match_arg arg = { id, instance };
> +
> + return xleaf_get_leaf(xdev, subdev_match, &arg);
> +}
> +
> +static inline struct xrt_device *
> +xleaf_get_leaf_by_epname(struct xrt_device *xdev, const char *name)
> +{
> + return xleaf_get_leaf(xdev, xrt_subdev_match_epname, (void *)name);
> +}
> +
> +static inline int xleaf_call(struct xrt_device *tgt, u32 cmd, void *arg)
> +{
> + return (to_xrt_drv(tgt->dev.driver)->leaf_call)(tgt, cmd, arg);
> +}
> +
> +int xleaf_broadcast_event(struct xrt_device *xdev, enum xrt_events evt, bool async);
> +int xleaf_create_group(struct xrt_device *xdev, char *dtb);
> +int xleaf_destroy_group(struct xrt_device *xdev, int instance);
> +void xleaf_get_root_res(struct xrt_device *xdev, u32 region_id, struct resource **res);
> +void xleaf_get_root_id(struct xrt_device *xdev, unsigned short *vendor, unsigned short *device,
> + unsigned short *subvendor, unsigned short *subdevice);
> +void xleaf_hot_reset(struct xrt_device *xdev);
> +int xleaf_put_leaf(struct xrt_device *xdev, struct xrt_device *leaf);
> +struct device *xleaf_register_hwmon(struct xrt_device *xdev, const char *name, void *drvdata,
> + const struct attribute_group **grps);
> +void xleaf_unregister_hwmon(struct xrt_device *xdev, struct device *hwmon);
> +int xleaf_wait_for_group_bringup(struct xrt_device *xdev);
> +
> +/*
> + * Character device helper APIs for use by leaf drivers
> + */
> +static inline bool xleaf_devnode_enabled(struct xrt_device *xdev)
> +{
> + return DEV_FILE_OPS(xdev)->xsf_ops.open;
> +}
> +
> +int xleaf_devnode_create(struct xrt_device *xdev,
> + const char *file_name, const char *inst_name);
> +void xleaf_devnode_destroy(struct xrt_device *xdev);
> +
> +struct xrt_device *xleaf_devnode_open_excl(struct inode *inode);
> +struct xrt_device *xleaf_devnode_open(struct inode *inode);
> +void xleaf_devnode_close(struct inode *inode);
> +
> +/* Module's init/fini routines for leaf driver in xrt-lib module */
> +#define XRT_LEAF_INIT_FINI_FUNC(name) \
> +void name##_leaf_init_fini(bool init) \
> +{ \
> + if (init) \
> + xrt_register_driver(&xrt_##name##_driver); \
> + else \
> + xrt_unregister_driver(&xrt_##name##_driver); \
> +}
> +
> +/* Module's init/fini routines for leaf driver in xrt-lib module */
> +void group_leaf_init_fini(bool init);
> +void vsec_leaf_init_fini(bool init);
> +void devctl_leaf_init_fini(bool init);
> +void axigate_leaf_init_fini(bool init);
> +void icap_leaf_init_fini(bool init);
> +void calib_leaf_init_fini(bool init);
> +void clkfreq_leaf_init_fini(bool init);
> +void clock_leaf_init_fini(bool init);
> +void ucs_leaf_init_fini(bool init);
> +
> +#endif /* _XRT_LEAF_H_ */
> diff --git a/drivers/fpga/xrt/include/xleaf/clkfreq.h b/drivers/fpga/xrt/include/xleaf/clkfreq.h
> new file mode 100644
> index 000000000000..005441d5df78
> --- /dev/null
> +++ b/drivers/fpga/xrt/include/xleaf/clkfreq.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Lizhi Hou <[email protected]>
> + */
> +
> +#ifndef _XRT_CLKFREQ_H_
> +#define _XRT_CLKFREQ_H_
> +
> +#include "xleaf.h"
> +
> +/*
> + * CLKFREQ driver leaf calls.
> + */
> +enum xrt_clkfreq_leaf_cmd {
> + XRT_CLKFREQ_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
> +};
> +
> +#endif /* _XRT_CLKFREQ_H_ */
> diff --git a/drivers/fpga/xrt/include/xleaf/clock.h b/drivers/fpga/xrt/include/xleaf/clock.h
> new file mode 100644
> index 000000000000..1379e24fa5d0
> --- /dev/null
> +++ b/drivers/fpga/xrt/include/xleaf/clock.h
> @@ -0,0 +1,29 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Lizhi Hou <[email protected]>
> + */
> +
> +#ifndef _XRT_CLOCK_H_
> +#define _XRT_CLOCK_H_
> +
> +#include "xleaf.h"
> +#include <linux/fpga_xrt.h>
> +
> +/*
> + * CLOCK driver leaf calls.
> + */
> +enum xrt_clock_leaf_cmd {
> + XRT_CLOCK_SET = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
> + XRT_CLOCK_GET,
> + XRT_CLOCK_VERIFY,
> +};
> +
> +struct xrt_clock_get {
> + u16 freq;
> + u32 freq_cnter;
> +};
> +
> +#endif /* _XRT_CLOCK_H_ */
> diff --git a/drivers/fpga/xrt/include/xleaf/ddr_calibration.h b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
> new file mode 100644
> index 000000000000..c44ae30f939a
> --- /dev/null
> +++ b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
> @@ -0,0 +1,28 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Cheng Zhen <[email protected]>
> + */
> +
> +#ifndef _XRT_DDR_CALIBRATION_H_
> +#define _XRT_DDR_CALIBRATION_H_
> +
> +#include "xleaf.h"
> +#include <linux/fpga-xrt.h>
> +
> +/*
> + * Memory calibration driver leaf calls.
> + */
> +enum xrt_calib_results {
> + XRT_CALIB_UNKNOWN = 0,
> + XRT_CALIB_SUCCEEDED,
> + XRT_CALIB_FAILED,
> +};
> +
> +enum xrt_calib_leaf_cmd {
> + XRT_CALIB_RESULT = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
> +};
> +
> +#endif /* _XRT_DDR_CALIBRATION_H_ */
> diff --git a/drivers/fpga/xrt/include/xleaf/devctl.h b/drivers/fpga/xrt/include/xleaf/devctl.h
> new file mode 100644
> index 000000000000..b97f3b6d9326
> --- /dev/null
> +++ b/drivers/fpga/xrt/include/xleaf/devctl.h
> @@ -0,0 +1,40 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Lizhi Hou <[email protected]>
> + */
> +
> +#ifndef _XRT_DEVCTL_H_
> +#define _XRT_DEVCTL_H_
> +
> +#include "xleaf.h"
> +
> +/*
> + * DEVCTL driver leaf calls.
> + */
> +enum xrt_devctl_leaf_cmd {
> + XRT_DEVCTL_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
> +};
> +
> +enum xrt_devctl_id {
> + XRT_DEVCTL_ROM_UUID = 0,
> + XRT_DEVCTL_DDR_CALIB,
> + XRT_DEVCTL_GOLDEN_VER,
> + XRT_DEVCTL_MAX
> +};
> +
> +struct xrt_devctl_rw {
> + u32 xdr_id;
> + void *xdr_buf;
> + u32 xdr_len;
> + u32 xdr_offset;
> +};
> +
> +struct xrt_devctl_intf_uuid {
> + u32 uuid_num;
> + uuid_t *uuids;
> +};
> +
> +#endif /* _XRT_DEVCTL_H_ */
> diff --git a/drivers/fpga/xrt/lib/lib-drv.c b/drivers/fpga/xrt/lib/lib-drv.c
> new file mode 100644
> index 000000000000..7d21ef565651
> --- /dev/null
> +++ b/drivers/fpga/xrt/lib/lib-drv.c
> @@ -0,0 +1,318 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Cheng Zhen <[email protected]>
> + * Lizhi Hou <[email protected]>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/vmalloc.h>
> +#include <linux/slab.h>
> +#include "xleaf.h"
> +#include "xroot.h"
> +#include "lib-drv.h"
> +
> +#define XRT_IPLIB_MODULE_NAME "xrt-lib"
ok
> +#define XRT_DRVNAME(drv) ((drv)->driver.name)
> +
> +#define XRT_SUBDEV_ID_SHIFT 16
> +#define XRT_SUBDEV_ID_MASK ((1 << XRT_SUBDEV_ID_SHIFT) - 1)
> +
> +struct xrt_find_drv_data {
> + enum xrt_subdev_id id;
> + struct xrt_driver *xdrv;
> +};
> +
> +struct class *xrt_class;
> +static DEFINE_IDA(xrt_device_ida);
> +
> +static inline u32 xrt_instance_to_id(enum xrt_subdev_id id, u32 instance)
> +{
> + return (id << XRT_SUBDEV_ID_SHIFT) | instance;
> +}
> +
> +static inline u32 xrt_id_to_instance(u32 id)
> +{
> + return id & XRT_SUBDEV_ID_MASK;
ok
> +}
> +
> +static int xrt_bus_match(struct device *dev, struct device_driver *drv)
> +{
> + struct xrt_device *xdev = to_xrt_dev(dev);
> + struct xrt_driver *xdrv = to_xrt_drv(drv);
> +
> + if (xdev->subdev_id == xdrv->subdev_id)
> + return 1;
> +
> + return 0;
> +}
> +
> +static int xrt_bus_probe(struct device *dev)
> +{
> + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
> + struct xrt_device *xdev = to_xrt_dev(dev);
> +
> + return xdrv->probe(xdev);
> +}
> +
> +static int xrt_bus_remove(struct device *dev)
> +{
> + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
> + struct xrt_device *xdev = to_xrt_dev(dev);
> +
> + if (xdrv->remove)
> + xdrv->remove(xdev);
> +
> + return 0;
> +}
> +
> +struct bus_type xrt_bus_type = {
> + .name = "xrt",
> + .match = xrt_bus_match,
> + .probe = xrt_bus_probe,
> + .remove = xrt_bus_remove,
> +};
> +
> +int xrt_register_driver(struct xrt_driver *drv)
> +{
> + const char *drvname = XRT_DRVNAME(drv);
> + int rc = 0;
> +
> + /* Initialize dev_t for char dev node. */
> + if (drv->file_ops.xsf_ops.open) {
> + rc = alloc_chrdev_region(&drv->file_ops.xsf_dev_t, 0,
> + XRT_MAX_DEVICE_NODES, drvname);
> + if (rc) {
> + pr_err("failed to alloc dev minor for %s: %d\n", drvname, rc);
> + return rc;
> + }
> + } else {
> + drv->file_ops.xsf_dev_t = (dev_t)-1;
> + }
> +
> + drv->driver.owner = THIS_MODULE;
> + drv->driver.bus = &xrt_bus_type;
> +
> + rc = driver_register(&drv->driver);
> + if (rc) {
> + pr_err("register %s xrt driver failed\n", drvname);
> + if (drv->file_ops.xsf_dev_t != (dev_t)-1) {
> + unregister_chrdev_region(drv->file_ops.xsf_dev_t,
> + XRT_MAX_DEVICE_NODES);
> + }
> + return rc;
> + }
> +
> + pr_info("%s registered successfully\n", drvname);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(xrt_register_driver);
> +
> +void xrt_unregister_driver(struct xrt_driver *drv)
> +{
> + driver_unregister(&drv->driver);
> +
> + if (drv->file_ops.xsf_dev_t != (dev_t)-1)
> + unregister_chrdev_region(drv->file_ops.xsf_dev_t, XRT_MAX_DEVICE_NODES);
ok
> +}
> +EXPORT_SYMBOL_GPL(xrt_unregister_driver);
> +
> +static int __find_driver(struct device_driver *drv, void *_data)
> +{
> + struct xrt_driver *xdrv = to_xrt_drv(drv);
> + struct xrt_find_drv_data *data = _data;
> +
> + if (xdrv->subdev_id == data->id) {
> + data->xdrv = xdrv;
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> +const char *xrt_drv_name(enum xrt_subdev_id id)
> +{
> + struct xrt_find_drv_data data = { 0 };
> +
> + data.id = id;
> + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
> +
> + if (data.xdrv)
> + return XRT_DRVNAME(data.xdrv);
> +
> + return NULL;
> +}
> +
> +static int xrt_drv_get_instance(enum xrt_subdev_id id)
> +{
> + int ret;
> +
> + ret = ida_alloc_range(&xrt_device_ida, xrt_instance_to_id(id, 0),
> + xrt_instance_to_id(id, XRT_MAX_DEVICE_NODES),
> + GFP_KERNEL);
> + if (ret < 0)
> + return ret;
> +
> + return xrt_id_to_instance((u32)ret);
> +}
> +
> +static void xrt_drv_put_instance(enum xrt_subdev_id id, int instance)
> +{
> + ida_free(&xrt_device_ida, xrt_instance_to_id(id, instance));
> +}
> +
> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id id)
> +{
> + struct xrt_find_drv_data data = { 0 };
> +
> + data.id = id;
> + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
> +
> + if (data.xdrv)
> + return data.xdrv->endpoints;
> +
> + return NULL;
> +}
> +
> +static void xrt_device_release(struct device *dev)
> +{
> + struct xrt_device *xdev = container_of(dev, struct xrt_device, dev);
> +
> + kfree(xdev);
> +}
> +
> +void xrt_device_unregister(struct xrt_device *xdev)
> +{
> + if (xdev->state == XRT_DEVICE_STATE_ADDED)
> + device_del(&xdev->dev);
> +
> + vfree(xdev->sdev_data);
cleanup of sdev_data here
fail: label in xrt_device_register is ok.
> + kfree(xdev->resource);
> +
> + if (xdev->instance != XRT_INVALID_DEVICE_INST)
> + xrt_drv_put_instance(xdev->subdev_id, xdev->instance);
> +
> + if (xdev->dev.release == xrt_device_release)
> + put_device(&xdev->dev);
> +}
> +
> +struct xrt_device *
> +xrt_device_register(struct device *parent, u32 id,
> + struct resource *res, u32 res_num,
> + void *pdata, size_t data_sz)
> +{
> + struct xrt_device *xdev = NULL;
> + int ret;
> +
> + xdev = kzalloc(sizeof(*xdev), GFP_KERNEL);
> + if (!xdev)
> + return xdev;
ok
> + xdev->instance = XRT_INVALID_DEVICE_INST;
> +
> + /* Obtain dev instance number. */
> + ret = xrt_drv_get_instance(id);
> + if (ret < 0) {
> + dev_err(parent, "failed get instance, ret %d", ret);
> + goto fail;
> + }
> +
> + xdev->instance = ret;
> + xdev->name = xrt_drv_name(id);
> + xdev->subdev_id = id;
> + device_initialize(&xdev->dev);
> + xdev->dev.release = xrt_device_release;
> + xdev->dev.parent = parent;
> +
> + xdev->dev.bus = &xrt_bus_type;
> + dev_set_name(&xdev->dev, "%s.%d", xdev->name, xdev->instance);
> +
> + xdev->num_resources = res_num;
> + xdev->resource = kmemdup(res, sizeof(*res) * res_num, GFP_KERNEL);
> + if (!xdev->resource)
> + goto fail;
> +
> + xdev->sdev_data = vzalloc(data_sz);
> + if (!xdev->sdev_data)
> + goto fail;
> +
> + memcpy(xdev->sdev_data, pdata, data_sz);
> +
> + ret = device_add(&xdev->dev);
> + if (ret) {
> + dev_err(parent, "failed add device, ret %d", ret);
> + goto fail;
> + }
> + xdev->state = XRT_DEVICE_STATE_ADDED;
> +
> + return xdev;
> +
> +fail:
> + xrt_device_unregister(xdev);
> + kfree(xdev);
> +
> + return NULL;
> +}
> +
> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32 type, u32 num)
> +{
> + u32 i;
> +
> + for (i = 0; i < xdev->num_resources; i++) {
> + struct resource *r = &xdev->resource[i];
> +
> + if (type == resource_type(r) && num-- == 0)
> + return r;
> + }
> + return NULL;
> +}
> +
> +/*
> + * Leaf driver's module init/fini callbacks. This is not a open infrastructure for dynamic
> + * plugging in drivers. All drivers should be statically added.
> + */
> +static void (*leaf_init_fini_cbs[])(bool) = {
> + group_leaf_init_fini,
> + axigate_leaf_init_fini,
> + icap_leaf_init_fini,
> +};
> +
> +static __init int xrt_lib_init(void)
> +{
> + int ret;
> + int i;
> +
> + ret = bus_register(&xrt_bus_type);
> + if (ret)
> + return ret;
> +
> + xrt_class = class_create(THIS_MODULE, XRT_IPLIB_MODULE_NAME);
> + if (IS_ERR(xrt_class)) {
> + bus_unregister(&xrt_bus_type);
> + return PTR_ERR(xrt_class);
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
> + leaf_init_fini_cbs[i](true);
> + return 0;
> +}
> +
> +static __exit void xrt_lib_fini(void)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
> + leaf_init_fini_cbs[i](false);
> +
> + class_destroy(xrt_class);
> + bus_unregister(&xrt_bus_type);
> +}
> +
> +module_init(xrt_lib_init);
> +module_exit(xrt_lib_fini);
> +
> +MODULE_AUTHOR("XRT Team <[email protected]>");
> +MODULE_DESCRIPTION("Xilinx Alveo IP Lib driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/fpga/xrt/lib/lib-drv.h b/drivers/fpga/xrt/lib/lib-drv.h
> new file mode 100644
> index 000000000000..0276c28e009f
> --- /dev/null
> +++ b/drivers/fpga/xrt/lib/lib-drv.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Cheng Zhen <[email protected]>
> + */
> +
> +#ifndef _LIB_DRV_H_
> +#define _LIB_DRV_H_
> +
> +#include <linux/device/class.h>
> +#include <linux/device/bus.h>
> +
> +extern struct class *xrt_class;
> +extern struct bus_type xrt_bus_type;
> +
> +const char *xrt_drv_name(enum xrt_subdev_id id);
> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id id);
> +
> +#endif /* _LIB_DRV_H_ */
On Thu, Aug 19, 2021 at 05:14:14AM -0700, Tom Rix wrote:
>
> On 8/2/21 9:05 AM, Lizhi Hou wrote:
>
> > xrt-lib kernel module infrastructure code to register and manage all
> > leaf driver modules.
> >
> > Signed-off-by: Sonal Santan <[email protected]>
> > Signed-off-by: Max Zhen <[email protected]>
> > Signed-off-by: Lizhi Hou <[email protected]>
> > Reviewed-by: Tom Rix <[email protected]>
>
> This was the only patch with requested changes in v8.
>
> All the changes from v8 have been made.
>
> They are itemized below with variations on 'ok'
>
> It is still Moritz's call to accept them.
I'll be OOO till 9/4/21. I'll get to it after.
- Moritz
>
> Tom
>
> > ---
> > drivers/fpga/xrt/include/subdev_id.h | 39 +++
> > drivers/fpga/xrt/include/xdevice.h | 141 ++++++++
> > drivers/fpga/xrt/include/xleaf.h | 205 +++++++++++
> > drivers/fpga/xrt/include/xleaf/clkfreq.h | 21 ++
> > drivers/fpga/xrt/include/xleaf/clock.h | 29 ++
> > .../fpga/xrt/include/xleaf/ddr_calibration.h | 28 ++
> > drivers/fpga/xrt/include/xleaf/devctl.h | 40 +++
> > drivers/fpga/xrt/lib/lib-drv.c | 318 ++++++++++++++++++
> > drivers/fpga/xrt/lib/lib-drv.h | 21 ++
> > 9 files changed, 842 insertions(+)
> > create mode 100644 drivers/fpga/xrt/include/subdev_id.h
> > create mode 100644 drivers/fpga/xrt/include/xdevice.h
> > create mode 100644 drivers/fpga/xrt/include/xleaf.h
> > create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
> > create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
> > create mode 100644 drivers/fpga/xrt/include/xleaf/ddr_calibration.h
> > create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
> > create mode 100644 drivers/fpga/xrt/lib/lib-drv.c
> > create mode 100644 drivers/fpga/xrt/lib/lib-drv.h
> >
> > diff --git a/drivers/fpga/xrt/include/subdev_id.h b/drivers/fpga/xrt/include/subdev_id.h
> > new file mode 100644
> > index 000000000000..02df4b939a1b
> > --- /dev/null
> > +++ b/drivers/fpga/xrt/include/subdev_id.h
> > @@ -0,0 +1,39 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020-2021 Xilinx, Inc.
> > + *
> > + * Authors:
> > + * Cheng Zhen <[email protected]>
> > + */
> > +
> > +#ifndef _XRT_SUBDEV_ID_H_
> > +#define _XRT_SUBDEV_ID_H_
> > +
> > +/*
> > + * Every subdev driver has an ID for others to refer to it. There can be multiple number of
> > + * instances of a subdev driver. A <subdev_id, subdev_instance> tuple is a unique identification
> > + * of a specific instance of a subdev driver.
> > + */
> > +enum xrt_subdev_id {
> > + XRT_SUBDEV_INVALID = 0,
> > + XRT_SUBDEV_GRP,
> > + XRT_SUBDEV_VSEC,
> > + XRT_SUBDEV_VSEC_GOLDEN,
> > + XRT_SUBDEV_DEVCTL,
> > + XRT_SUBDEV_AXIGATE,
> > + XRT_SUBDEV_ICAP,
> > + XRT_SUBDEV_TEST,
> > + XRT_SUBDEV_MGMT_MAIN,
> > + XRT_SUBDEV_QSPI,
> > + XRT_SUBDEV_MAILBOX,
> > + XRT_SUBDEV_CMC,
> > + XRT_SUBDEV_CALIB,
> > + XRT_SUBDEV_CLKFREQ,
> > + XRT_SUBDEV_CLOCK,
> > + XRT_SUBDEV_SRSR,
> > + XRT_SUBDEV_UCS,
> > + XRT_SUBDEV_NUM, /* Total number of subdevs. */
> > + XRT_ROOT = -1, /* Special ID for root driver. */
> > +};
> > +
> > +#endif /* _XRT_SUBDEV_ID_H_ */
> > diff --git a/drivers/fpga/xrt/include/xdevice.h b/drivers/fpga/xrt/include/xdevice.h
> > new file mode 100644
> > index 000000000000..b40ebe98b54d
> > --- /dev/null
> > +++ b/drivers/fpga/xrt/include/xdevice.h
> > @@ -0,0 +1,141 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020-2021 Xilinx, Inc.
> > + *
> > + * Authors:
> > + * Lizhi Hou <[email protected]>
> > + */
> > +
> > +#ifndef _XRT_DEVICE_H_
> > +#define _XRT_DEVICE_H_
> > +
> > +#include <linux/fs.h>
> > +#include <linux/cdev.h>
> > +
> > +#define XRT_MAX_DEVICE_NODES 128
> > +#define XRT_INVALID_DEVICE_INST (XRT_MAX_DEVICE_NODES + 1)
> > +
> > +enum {
> > + XRT_DEVICE_STATE_NONE = 0,
> > + XRT_DEVICE_STATE_ADDED
> > +};
> > +
> > +/*
> > + * struct xrt_device - represent an xrt device on xrt bus
> > + *
> > + * dev: generic device interface.
> > + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
> ok
> > + * name: name of the xrt device.
> > + * instance: instance of the xrt device. The xrt device with same id can have
> > + * more than 1 instances.
> > + * state: current state of the xrt device.
> > + * num_resources: The total number of resource for the xrt device.
> > + * resource: point to the xrt device resource array.
> > + * sdev_data: private data pointer.
> > + */
> > +struct xrt_device {
> > + struct device dev;
> > + u32 subdev_id;
> > + const char *name;
> > + u32 instance;
> > + u32 state;
> > + u32 num_resources;
> > + struct resource *resource;
> > + void *sdev_data;
> > +};
> > +
> > +/*
> > + * If populated by xrt device driver, infra will handle the mechanics of
> > + * char device (un)registration.
> > + */
> > +enum xrt_dev_file_mode {
> > + /* Infra create cdev, default file name */
> > + XRT_DEV_FILE_DEFAULT = 0,
> > + /* Infra create cdev, need to encode inst num in file name */
> > + XRT_DEV_FILE_MULTI_INST,
> > + /* No auto creation of cdev by infra, leaf handles it by itself */
> > + XRT_DEV_FILE_NO_AUTO,
> > +};
> > +
> > +struct xrt_dev_file_ops {
> > + const struct file_operations xsf_ops;
> > + dev_t xsf_dev_t;
> > + const char *xsf_dev_name;
> > + enum xrt_dev_file_mode xsf_mode;
> > +};
> > +
> > +/*
> > + * this struct define the endpoints belong to the same xrt device
> > + * ep_name: endpoint name
> > + * compat: compatible string
> ok
> > + */
> > +struct xrt_dev_ep_names {
> > + const char *ep_name;
> > + const char *compat;
> > +};
> > +
> > +struct xrt_dev_endpoints {
> > + struct xrt_dev_ep_names *xse_names;
> > + /* minimum number of endpoints to support the subdevice */
> > + u32 xse_min_ep;
> > +};
> > +
> > +/*
> > + * struct xrt_driver - represent a xrt device driver
> > + *
> > + * driver: driver model structure.
> > + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
> > + * file_ops: character device name and callbacks.
> > + * probe: mandatory callback for device binding.
> > + * remove: callback for device unbinding.
> > + * leaf_call: callback for servicing other leaf drivers.
> ok
> > + */
> > +struct xrt_driver {
> > + struct device_driver driver;
> > + u32 subdev_id;
> > + struct xrt_dev_file_ops file_ops;
> > + struct xrt_dev_endpoints *endpoints;
> > +
> > + /*
> > + * Subdev driver callbacks populated by subdev driver.
> > + */
> > + int (*probe)(struct xrt_device *xrt_dev);
> > + void (*remove)(struct xrt_device *xrt_dev);
> > + /*
> > + * If leaf_call is defined, these are called by other leaf drivers.
> > + * Note that root driver may call into leaf_call of a group driver.
> > + */
> > + int (*leaf_call)(struct xrt_device *xrt_dev, u32 cmd, void *arg);
> > +};
> > +
> > +#define to_xrt_dev(d) container_of(d, struct xrt_device, dev)
> > +#define to_xrt_drv(d) container_of(d, struct xrt_driver, driver)
> > +
> > +static inline void *xrt_get_drvdata(const struct xrt_device *xdev)
> > +{
> > + return dev_get_drvdata(&xdev->dev);
> > +}
> > +
> > +static inline void xrt_set_drvdata(struct xrt_device *xdev, void *data)
> > +{
> > + dev_set_drvdata(&xdev->dev, data);
> > +}
> > +
> > +static inline void *xrt_get_xdev_data(struct device *dev)
> > +{
> > + struct xrt_device *xdev = to_xrt_dev(dev);
> > +
> > + return xdev->sdev_data;
> > +}
> > +
> > +struct xrt_device *
> > +xrt_device_register(struct device *parent, u32 id,
> > + struct resource *res, u32 res_num,
> > + void *pdata, size_t data_sz);
> > +void xrt_device_unregister(struct xrt_device *xdev);
> > +int xrt_register_driver(struct xrt_driver *drv);
> > +void xrt_unregister_driver(struct xrt_driver *drv);
> > +void *xrt_get_xdev_data(struct device *dev);
> > +struct resource *xrt_get_resource(struct xrt_device *xdev, u32 type, u32 num);
> > +
> > +#endif /* _XRT_DEVICE_H_ */
> > diff --git a/drivers/fpga/xrt/include/xleaf.h b/drivers/fpga/xrt/include/xleaf.h
> > new file mode 100644
> > index 000000000000..f065fc766e0f
> > --- /dev/null
> > +++ b/drivers/fpga/xrt/include/xleaf.h
> > @@ -0,0 +1,205 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020-2021 Xilinx, Inc.
> > + *
> > + * Authors:
> > + * Cheng Zhen <[email protected]>
> > + * Sonal Santan <[email protected]>
> > + */
> > +
> > +#ifndef _XRT_XLEAF_H_
> > +#define _XRT_XLEAF_H_
> > +
> > +#include <linux/mod_devicetable.h>
> > +#include "xdevice.h"
> > +#include "subdev_id.h"
> > +#include "xroot.h"
> > +#include "events.h"
> > +
> > +/* All subdev drivers should use below common routines to print out msg. */
> > +#define DEV(xdev) (&(xdev)->dev)
> > +#define DEV_PDATA(xdev) \
> > + ((struct xrt_subdev_platdata *)xrt_get_xdev_data(DEV(xdev)))
> > +#define DEV_FILE_OPS(xdev) \
> > + (&(to_xrt_drv((xdev)->dev.driver))->file_ops)
> > +#define FMT_PRT(prt_fn, xdev, fmt, args...) \
> > + ({typeof(xdev) (_xdev) = (xdev); \
> > + prt_fn(DEV(_xdev), "%s %s: " fmt, \
> > + DEV_PDATA(_xdev)->xsp_root_name, __func__, ##args); })
> > +#define xrt_err(xdev, fmt, args...) FMT_PRT(dev_err, xdev, fmt, ##args)
> > +#define xrt_warn(xdev, fmt, args...) FMT_PRT(dev_warn, xdev, fmt, ##args)
> > +#define xrt_info(xdev, fmt, args...) FMT_PRT(dev_info, xdev, fmt, ##args)
> > +#define xrt_dbg(xdev, fmt, args...) FMT_PRT(dev_dbg, xdev, fmt, ##args)
> > +
> > +#define XRT_DEFINE_REGMAP_CONFIG(config_name) \
> > + static const struct regmap_config config_name = { \
> > + .reg_bits = 32, \
> > + .val_bits = 32, \
> > + .reg_stride = 4, \
> > + .max_register = 0x1000, \
> > + }
> > +
> > +enum {
> > + /* Starting cmd for common leaf cmd implemented by all leaves. */
> > + XRT_XLEAF_COMMON_BASE = 0,
> > + /* Starting cmd for leaves' specific leaf cmds. */
> > + XRT_XLEAF_CUSTOM_BASE = 64,
> > +};
> > +
> > +enum xrt_xleaf_common_leaf_cmd {
> > + XRT_XLEAF_EVENT = XRT_XLEAF_COMMON_BASE,
> > +};
> > +
> > +/*
> > + * Partially initialized by the parent driver, then, passed in as subdev driver's
> > + * platform data when creating subdev driver instance by calling platform
> > + * device register API (xrt_device_register_data() or the likes).
> > + *
> > + * Once device register API returns, platform driver framework makes a copy of
> > + * this buffer and maintains its life cycle. The content of the buffer is
> > + * completely owned by subdev driver.
> > + *
> > + * Thus, parent driver should be very careful when it touches this buffer
> > + * again once it's handed over to subdev driver. And the data structure
> > + * should not contain pointers pointing to buffers that is managed by
> > + * other or parent drivers since it could have been freed before platform
> > + * data buffer is freed by platform driver framework.
> > + */
> > +struct xrt_subdev_platdata {
> > + /*
> > + * Per driver instance callback. The xdev points to the instance.
> > + * Should always be defined for subdev driver to get service from root.
> > + */
> > + xrt_subdev_root_cb_t xsp_root_cb;
> > + void *xsp_root_cb_arg;
> > +
> > + /* Something to associate w/ root for msg printing. */
> > + const char *xsp_root_name;
> > +
> > + /*
> > + * Char dev support for this subdev instance.
> > + * Initialized by subdev driver.
> > + */
> > + struct cdev xsp_cdev;
> > + struct device *xsp_sysdev;
> > + struct mutex xsp_devnode_lock; /* devnode lock */
> > + struct completion xsp_devnode_comp;
> > + int xsp_devnode_ref;
> > + bool xsp_devnode_online;
> > + bool xsp_devnode_excl;
> > +
> > + /*
> > + * Subdev driver specific init data. The buffer should be embedded
> > + * in this data structure buffer after dtb, so that it can be freed
> > + * together with platform data.
> > + */
> > + loff_t xsp_priv_off; /* Offset into this platform data buffer. */
> > + size_t xsp_priv_len;
> > +
> > + /*
> > + * Populated by parent driver to describe the device tree for
> > + * the subdev driver to handle. Should always be last one since it's
> > + * of variable length.
> > + */
> > + bool xsp_dtb_valid;
> > + char xsp_dtb[0];
> > +};
> > +
> > +struct subdev_match_arg {
> > + enum xrt_subdev_id id;
> > + int instance;
> > +};
> > +
> > +bool xleaf_has_endpoint(struct xrt_device *xdev, const char *endpoint_name);
> > +struct xrt_device *xleaf_get_leaf(struct xrt_device *xdev,
> > + xrt_subdev_match_t cb, void *arg);
> > +
> > +static inline bool subdev_match(enum xrt_subdev_id id, struct xrt_device *xdev, void *arg)
> > +{
> > + const struct subdev_match_arg *a = (struct subdev_match_arg *)arg;
> > + int instance = a->instance;
> > +
> > + if (id != a->id)
> > + return false;
> > + if (instance != xdev->instance && instance != XRT_INVALID_DEVICE_INST)
> > + return false;
> > + return true;
> > +}
> > +
> > +static inline bool xrt_subdev_match_epname(enum xrt_subdev_id id,
> > + struct xrt_device *xdev, void *arg)
> > +{
> > + return xleaf_has_endpoint(xdev, arg);
> > +}
> > +
> > +static inline struct xrt_device *
> > +xleaf_get_leaf_by_id(struct xrt_device *xdev,
> > + enum xrt_subdev_id id, int instance)
> > +{
> > + struct subdev_match_arg arg = { id, instance };
> > +
> > + return xleaf_get_leaf(xdev, subdev_match, &arg);
> > +}
> > +
> > +static inline struct xrt_device *
> > +xleaf_get_leaf_by_epname(struct xrt_device *xdev, const char *name)
> > +{
> > + return xleaf_get_leaf(xdev, xrt_subdev_match_epname, (void *)name);
> > +}
> > +
> > +static inline int xleaf_call(struct xrt_device *tgt, u32 cmd, void *arg)
> > +{
> > + return (to_xrt_drv(tgt->dev.driver)->leaf_call)(tgt, cmd, arg);
> > +}
> > +
> > +int xleaf_broadcast_event(struct xrt_device *xdev, enum xrt_events evt, bool async);
> > +int xleaf_create_group(struct xrt_device *xdev, char *dtb);
> > +int xleaf_destroy_group(struct xrt_device *xdev, int instance);
> > +void xleaf_get_root_res(struct xrt_device *xdev, u32 region_id, struct resource **res);
> > +void xleaf_get_root_id(struct xrt_device *xdev, unsigned short *vendor, unsigned short *device,
> > + unsigned short *subvendor, unsigned short *subdevice);
> > +void xleaf_hot_reset(struct xrt_device *xdev);
> > +int xleaf_put_leaf(struct xrt_device *xdev, struct xrt_device *leaf);
> > +struct device *xleaf_register_hwmon(struct xrt_device *xdev, const char *name, void *drvdata,
> > + const struct attribute_group **grps);
> > +void xleaf_unregister_hwmon(struct xrt_device *xdev, struct device *hwmon);
> > +int xleaf_wait_for_group_bringup(struct xrt_device *xdev);
> > +
> > +/*
> > + * Character device helper APIs for use by leaf drivers
> > + */
> > +static inline bool xleaf_devnode_enabled(struct xrt_device *xdev)
> > +{
> > + return DEV_FILE_OPS(xdev)->xsf_ops.open;
> > +}
> > +
> > +int xleaf_devnode_create(struct xrt_device *xdev,
> > + const char *file_name, const char *inst_name);
> > +void xleaf_devnode_destroy(struct xrt_device *xdev);
> > +
> > +struct xrt_device *xleaf_devnode_open_excl(struct inode *inode);
> > +struct xrt_device *xleaf_devnode_open(struct inode *inode);
> > +void xleaf_devnode_close(struct inode *inode);
> > +
> > +/* Module's init/fini routines for leaf driver in xrt-lib module */
> > +#define XRT_LEAF_INIT_FINI_FUNC(name) \
> > +void name##_leaf_init_fini(bool init) \
> > +{ \
> > + if (init) \
> > + xrt_register_driver(&xrt_##name##_driver); \
> > + else \
> > + xrt_unregister_driver(&xrt_##name##_driver); \
> > +}
> > +
> > +/* Module's init/fini routines for leaf driver in xrt-lib module */
> > +void group_leaf_init_fini(bool init);
> > +void vsec_leaf_init_fini(bool init);
> > +void devctl_leaf_init_fini(bool init);
> > +void axigate_leaf_init_fini(bool init);
> > +void icap_leaf_init_fini(bool init);
> > +void calib_leaf_init_fini(bool init);
> > +void clkfreq_leaf_init_fini(bool init);
> > +void clock_leaf_init_fini(bool init);
> > +void ucs_leaf_init_fini(bool init);
> > +
> > +#endif /* _XRT_LEAF_H_ */
> > diff --git a/drivers/fpga/xrt/include/xleaf/clkfreq.h b/drivers/fpga/xrt/include/xleaf/clkfreq.h
> > new file mode 100644
> > index 000000000000..005441d5df78
> > --- /dev/null
> > +++ b/drivers/fpga/xrt/include/xleaf/clkfreq.h
> > @@ -0,0 +1,21 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020-2021 Xilinx, Inc.
> > + *
> > + * Authors:
> > + * Lizhi Hou <[email protected]>
> > + */
> > +
> > +#ifndef _XRT_CLKFREQ_H_
> > +#define _XRT_CLKFREQ_H_
> > +
> > +#include "xleaf.h"
> > +
> > +/*
> > + * CLKFREQ driver leaf calls.
> > + */
> > +enum xrt_clkfreq_leaf_cmd {
> > + XRT_CLKFREQ_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
> > +};
> > +
> > +#endif /* _XRT_CLKFREQ_H_ */
> > diff --git a/drivers/fpga/xrt/include/xleaf/clock.h b/drivers/fpga/xrt/include/xleaf/clock.h
> > new file mode 100644
> > index 000000000000..1379e24fa5d0
> > --- /dev/null
> > +++ b/drivers/fpga/xrt/include/xleaf/clock.h
> > @@ -0,0 +1,29 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020-2021 Xilinx, Inc.
> > + *
> > + * Authors:
> > + * Lizhi Hou <[email protected]>
> > + */
> > +
> > +#ifndef _XRT_CLOCK_H_
> > +#define _XRT_CLOCK_H_
> > +
> > +#include "xleaf.h"
> > +#include <linux/fpga_xrt.h>
> > +
> > +/*
> > + * CLOCK driver leaf calls.
> > + */
> > +enum xrt_clock_leaf_cmd {
> > + XRT_CLOCK_SET = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
> > + XRT_CLOCK_GET,
> > + XRT_CLOCK_VERIFY,
> > +};
> > +
> > +struct xrt_clock_get {
> > + u16 freq;
> > + u32 freq_cnter;
> > +};
> > +
> > +#endif /* _XRT_CLOCK_H_ */
> > diff --git a/drivers/fpga/xrt/include/xleaf/ddr_calibration.h b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
> > new file mode 100644
> > index 000000000000..c44ae30f939a
> > --- /dev/null
> > +++ b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
> > @@ -0,0 +1,28 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020-2021 Xilinx, Inc.
> > + *
> > + * Authors:
> > + * Cheng Zhen <[email protected]>
> > + */
> > +
> > +#ifndef _XRT_DDR_CALIBRATION_H_
> > +#define _XRT_DDR_CALIBRATION_H_
> > +
> > +#include "xleaf.h"
> > +#include <linux/fpga-xrt.h>
> > +
> > +/*
> > + * Memory calibration driver leaf calls.
> > + */
> > +enum xrt_calib_results {
> > + XRT_CALIB_UNKNOWN = 0,
> > + XRT_CALIB_SUCCEEDED,
> > + XRT_CALIB_FAILED,
> > +};
> > +
> > +enum xrt_calib_leaf_cmd {
> > + XRT_CALIB_RESULT = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
> > +};
> > +
> > +#endif /* _XRT_DDR_CALIBRATION_H_ */
> > diff --git a/drivers/fpga/xrt/include/xleaf/devctl.h b/drivers/fpga/xrt/include/xleaf/devctl.h
> > new file mode 100644
> > index 000000000000..b97f3b6d9326
> > --- /dev/null
> > +++ b/drivers/fpga/xrt/include/xleaf/devctl.h
> > @@ -0,0 +1,40 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020-2021 Xilinx, Inc.
> > + *
> > + * Authors:
> > + * Lizhi Hou <[email protected]>
> > + */
> > +
> > +#ifndef _XRT_DEVCTL_H_
> > +#define _XRT_DEVCTL_H_
> > +
> > +#include "xleaf.h"
> > +
> > +/*
> > + * DEVCTL driver leaf calls.
> > + */
> > +enum xrt_devctl_leaf_cmd {
> > + XRT_DEVCTL_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
> > +};
> > +
> > +enum xrt_devctl_id {
> > + XRT_DEVCTL_ROM_UUID = 0,
> > + XRT_DEVCTL_DDR_CALIB,
> > + XRT_DEVCTL_GOLDEN_VER,
> > + XRT_DEVCTL_MAX
> > +};
> > +
> > +struct xrt_devctl_rw {
> > + u32 xdr_id;
> > + void *xdr_buf;
> > + u32 xdr_len;
> > + u32 xdr_offset;
> > +};
> > +
> > +struct xrt_devctl_intf_uuid {
> > + u32 uuid_num;
> > + uuid_t *uuids;
> > +};
> > +
> > +#endif /* _XRT_DEVCTL_H_ */
> > diff --git a/drivers/fpga/xrt/lib/lib-drv.c b/drivers/fpga/xrt/lib/lib-drv.c
> > new file mode 100644
> > index 000000000000..7d21ef565651
> > --- /dev/null
> > +++ b/drivers/fpga/xrt/lib/lib-drv.c
> > @@ -0,0 +1,318 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020-2021 Xilinx, Inc.
> > + *
> > + * Authors:
> > + * Cheng Zhen <[email protected]>
> > + * Lizhi Hou <[email protected]>
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/vmalloc.h>
> > +#include <linux/slab.h>
> > +#include "xleaf.h"
> > +#include "xroot.h"
> > +#include "lib-drv.h"
> > +
> > +#define XRT_IPLIB_MODULE_NAME "xrt-lib"
> ok
> > +#define XRT_DRVNAME(drv) ((drv)->driver.name)
> > +
> > +#define XRT_SUBDEV_ID_SHIFT 16
> > +#define XRT_SUBDEV_ID_MASK ((1 << XRT_SUBDEV_ID_SHIFT) - 1)
> > +
> > +struct xrt_find_drv_data {
> > + enum xrt_subdev_id id;
> > + struct xrt_driver *xdrv;
> > +};
> > +
> > +struct class *xrt_class;
> > +static DEFINE_IDA(xrt_device_ida);
> > +
> > +static inline u32 xrt_instance_to_id(enum xrt_subdev_id id, u32 instance)
> > +{
> > + return (id << XRT_SUBDEV_ID_SHIFT) | instance;
> > +}
> > +
> > +static inline u32 xrt_id_to_instance(u32 id)
> > +{
> > + return id & XRT_SUBDEV_ID_MASK;
> ok
> > +}
> > +
> > +static int xrt_bus_match(struct device *dev, struct device_driver *drv)
> > +{
> > + struct xrt_device *xdev = to_xrt_dev(dev);
> > + struct xrt_driver *xdrv = to_xrt_drv(drv);
> > +
> > + if (xdev->subdev_id == xdrv->subdev_id)
> > + return 1;
> > +
> > + return 0;
> > +}
> > +
> > +static int xrt_bus_probe(struct device *dev)
> > +{
> > + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
> > + struct xrt_device *xdev = to_xrt_dev(dev);
> > +
> > + return xdrv->probe(xdev);
> > +}
> > +
> > +static int xrt_bus_remove(struct device *dev)
> > +{
> > + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
> > + struct xrt_device *xdev = to_xrt_dev(dev);
> > +
> > + if (xdrv->remove)
> > + xdrv->remove(xdev);
> > +
> > + return 0;
> > +}
> > +
> > +struct bus_type xrt_bus_type = {
> > + .name = "xrt",
> > + .match = xrt_bus_match,
> > + .probe = xrt_bus_probe,
> > + .remove = xrt_bus_remove,
> > +};
> > +
> > +int xrt_register_driver(struct xrt_driver *drv)
> > +{
> > + const char *drvname = XRT_DRVNAME(drv);
> > + int rc = 0;
> > +
> > + /* Initialize dev_t for char dev node. */
> > + if (drv->file_ops.xsf_ops.open) {
> > + rc = alloc_chrdev_region(&drv->file_ops.xsf_dev_t, 0,
> > + XRT_MAX_DEVICE_NODES, drvname);
> > + if (rc) {
> > + pr_err("failed to alloc dev minor for %s: %d\n", drvname, rc);
> > + return rc;
> > + }
> > + } else {
> > + drv->file_ops.xsf_dev_t = (dev_t)-1;
> > + }
> > +
> > + drv->driver.owner = THIS_MODULE;
> > + drv->driver.bus = &xrt_bus_type;
> > +
> > + rc = driver_register(&drv->driver);
> > + if (rc) {
> > + pr_err("register %s xrt driver failed\n", drvname);
> > + if (drv->file_ops.xsf_dev_t != (dev_t)-1) {
> > + unregister_chrdev_region(drv->file_ops.xsf_dev_t,
> > + XRT_MAX_DEVICE_NODES);
> > + }
> > + return rc;
> > + }
> > +
> > + pr_info("%s registered successfully\n", drvname);
> > +
> > + return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(xrt_register_driver);
> > +
> > +void xrt_unregister_driver(struct xrt_driver *drv)
> > +{
> > + driver_unregister(&drv->driver);
> > +
> > + if (drv->file_ops.xsf_dev_t != (dev_t)-1)
> > + unregister_chrdev_region(drv->file_ops.xsf_dev_t, XRT_MAX_DEVICE_NODES);
> ok
> > +}
> > +EXPORT_SYMBOL_GPL(xrt_unregister_driver);
> > +
> > +static int __find_driver(struct device_driver *drv, void *_data)
> > +{
> > + struct xrt_driver *xdrv = to_xrt_drv(drv);
> > + struct xrt_find_drv_data *data = _data;
> > +
> > + if (xdrv->subdev_id == data->id) {
> > + data->xdrv = xdrv;
> > + return 1;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +const char *xrt_drv_name(enum xrt_subdev_id id)
> > +{
> > + struct xrt_find_drv_data data = { 0 };
> > +
> > + data.id = id;
> > + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
> > +
> > + if (data.xdrv)
> > + return XRT_DRVNAME(data.xdrv);
> > +
> > + return NULL;
> > +}
> > +
> > +static int xrt_drv_get_instance(enum xrt_subdev_id id)
> > +{
> > + int ret;
> > +
> > + ret = ida_alloc_range(&xrt_device_ida, xrt_instance_to_id(id, 0),
> > + xrt_instance_to_id(id, XRT_MAX_DEVICE_NODES),
> > + GFP_KERNEL);
> > + if (ret < 0)
> > + return ret;
> > +
> > + return xrt_id_to_instance((u32)ret);
> > +}
> > +
> > +static void xrt_drv_put_instance(enum xrt_subdev_id id, int instance)
> > +{
> > + ida_free(&xrt_device_ida, xrt_instance_to_id(id, instance));
> > +}
> > +
> > +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id id)
> > +{
> > + struct xrt_find_drv_data data = { 0 };
> > +
> > + data.id = id;
> > + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
> > +
> > + if (data.xdrv)
> > + return data.xdrv->endpoints;
> > +
> > + return NULL;
> > +}
> > +
> > +static void xrt_device_release(struct device *dev)
> > +{
> > + struct xrt_device *xdev = container_of(dev, struct xrt_device, dev);
> > +
> > + kfree(xdev);
> > +}
> > +
> > +void xrt_device_unregister(struct xrt_device *xdev)
> > +{
> > + if (xdev->state == XRT_DEVICE_STATE_ADDED)
> > + device_del(&xdev->dev);
> > +
> > + vfree(xdev->sdev_data);
>
> cleanup of sdev_data here
>
> fail: label in xrt_device_register is ok.
>
> > + kfree(xdev->resource);
> > +
> > + if (xdev->instance != XRT_INVALID_DEVICE_INST)
> > + xrt_drv_put_instance(xdev->subdev_id, xdev->instance);
> > +
> > + if (xdev->dev.release == xrt_device_release)
> > + put_device(&xdev->dev);
> > +}
> > +
> > +struct xrt_device *
> > +xrt_device_register(struct device *parent, u32 id,
> > + struct resource *res, u32 res_num,
> > + void *pdata, size_t data_sz)
> > +{
> > + struct xrt_device *xdev = NULL;
> > + int ret;
> > +
> > + xdev = kzalloc(sizeof(*xdev), GFP_KERNEL);
> > + if (!xdev)
> > + return xdev;
> ok
> > + xdev->instance = XRT_INVALID_DEVICE_INST;
> > +
> > + /* Obtain dev instance number. */
> > + ret = xrt_drv_get_instance(id);
> > + if (ret < 0) {
> > + dev_err(parent, "failed get instance, ret %d", ret);
> > + goto fail;
> > + }
> > +
> > + xdev->instance = ret;
> > + xdev->name = xrt_drv_name(id);
> > + xdev->subdev_id = id;
> > + device_initialize(&xdev->dev);
> > + xdev->dev.release = xrt_device_release;
> > + xdev->dev.parent = parent;
> > +
> > + xdev->dev.bus = &xrt_bus_type;
> > + dev_set_name(&xdev->dev, "%s.%d", xdev->name, xdev->instance);
> > +
> > + xdev->num_resources = res_num;
> > + xdev->resource = kmemdup(res, sizeof(*res) * res_num, GFP_KERNEL);
> > + if (!xdev->resource)
> > + goto fail;
> > +
> > + xdev->sdev_data = vzalloc(data_sz);
> > + if (!xdev->sdev_data)
> > + goto fail;
> > +
> > + memcpy(xdev->sdev_data, pdata, data_sz);
> > +
> > + ret = device_add(&xdev->dev);
> > + if (ret) {
> > + dev_err(parent, "failed add device, ret %d", ret);
> > + goto fail;
> > + }
> > + xdev->state = XRT_DEVICE_STATE_ADDED;
> > +
> > + return xdev;
> > +
> > +fail:
> > + xrt_device_unregister(xdev);
> > + kfree(xdev);
> > +
> > + return NULL;
> > +}
> > +
> > +struct resource *xrt_get_resource(struct xrt_device *xdev, u32 type, u32 num)
> > +{
> > + u32 i;
> > +
> > + for (i = 0; i < xdev->num_resources; i++) {
> > + struct resource *r = &xdev->resource[i];
> > +
> > + if (type == resource_type(r) && num-- == 0)
> > + return r;
> > + }
> > + return NULL;
> > +}
> > +
> > +/*
> > + * Leaf driver's module init/fini callbacks. This is not a open infrastructure for dynamic
> > + * plugging in drivers. All drivers should be statically added.
> > + */
> > +static void (*leaf_init_fini_cbs[])(bool) = {
> > + group_leaf_init_fini,
> > + axigate_leaf_init_fini,
> > + icap_leaf_init_fini,
> > +};
> > +
> > +static __init int xrt_lib_init(void)
> > +{
> > + int ret;
> > + int i;
> > +
> > + ret = bus_register(&xrt_bus_type);
> > + if (ret)
> > + return ret;
> > +
> > + xrt_class = class_create(THIS_MODULE, XRT_IPLIB_MODULE_NAME);
> > + if (IS_ERR(xrt_class)) {
> > + bus_unregister(&xrt_bus_type);
> > + return PTR_ERR(xrt_class);
> > + }
> > +
> > + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
> > + leaf_init_fini_cbs[i](true);
> > + return 0;
> > +}
> > +
> > +static __exit void xrt_lib_fini(void)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
> > + leaf_init_fini_cbs[i](false);
> > +
> > + class_destroy(xrt_class);
> > + bus_unregister(&xrt_bus_type);
> > +}
> > +
> > +module_init(xrt_lib_init);
> > +module_exit(xrt_lib_fini);
> > +
> > +MODULE_AUTHOR("XRT Team <[email protected]>");
> > +MODULE_DESCRIPTION("Xilinx Alveo IP Lib driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/fpga/xrt/lib/lib-drv.h b/drivers/fpga/xrt/lib/lib-drv.h
> > new file mode 100644
> > index 000000000000..0276c28e009f
> > --- /dev/null
> > +++ b/drivers/fpga/xrt/lib/lib-drv.h
> > @@ -0,0 +1,21 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020-2021 Xilinx, Inc.
> > + *
> > + * Authors:
> > + * Cheng Zhen <[email protected]>
> > + */
> > +
> > +#ifndef _LIB_DRV_H_
> > +#define _LIB_DRV_H_
> > +
> > +#include <linux/device/class.h>
> > +#include <linux/device/bus.h>
> > +
> > +extern struct class *xrt_class;
> > +extern struct bus_type xrt_bus_type;
> > +
> > +const char *xrt_drv_name(enum xrt_subdev_id id);
> > +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id id);
> > +
> > +#endif /* _LIB_DRV_H_ */
>
On 8/21/21 9:44 AM, Moritz Fischer wrote:
> On Thu, Aug 19, 2021 at 05:14:14AM -0700, Tom Rix wrote:
>> On 8/2/21 9:05 AM, Lizhi Hou wrote:
>>
>>> xrt-lib kernel module infrastructure code to register and manage all
>>> leaf driver modules.
>>>
>>> Signed-off-by: Sonal Santan <[email protected]>
>>> Signed-off-by: Max Zhen <[email protected]>
>>> Signed-off-by: Lizhi Hou <[email protected]>
>>> Reviewed-by: Tom Rix <[email protected]>
>> This was the only patch with requested changes in v8.
>>
>> All the changes from v8 have been made.
>>
>> They are itemized below with variations on 'ok'
>>
>> It is still Moritz's call to accept them.
> I'll be OOO till 9/4/21. I'll get to it after.
For other subsystem issues, who is the backup for these two weeks?
Tom
>
> - Moritz
>> Tom
>>
>>> ---
>>> drivers/fpga/xrt/include/subdev_id.h | 39 +++
>>> drivers/fpga/xrt/include/xdevice.h | 141 ++++++++
>>> drivers/fpga/xrt/include/xleaf.h | 205 +++++++++++
>>> drivers/fpga/xrt/include/xleaf/clkfreq.h | 21 ++
>>> drivers/fpga/xrt/include/xleaf/clock.h | 29 ++
>>> .../fpga/xrt/include/xleaf/ddr_calibration.h | 28 ++
>>> drivers/fpga/xrt/include/xleaf/devctl.h | 40 +++
>>> drivers/fpga/xrt/lib/lib-drv.c | 318 ++++++++++++++++++
>>> drivers/fpga/xrt/lib/lib-drv.h | 21 ++
>>> 9 files changed, 842 insertions(+)
>>> create mode 100644 drivers/fpga/xrt/include/subdev_id.h
>>> create mode 100644 drivers/fpga/xrt/include/xdevice.h
>>> create mode 100644 drivers/fpga/xrt/include/xleaf.h
>>> create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
>>> create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
>>> create mode 100644 drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>> create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
>>> create mode 100644 drivers/fpga/xrt/lib/lib-drv.c
>>> create mode 100644 drivers/fpga/xrt/lib/lib-drv.h
>>>
>>> diff --git a/drivers/fpga/xrt/include/subdev_id.h b/drivers/fpga/xrt/include/subdev_id.h
>>> new file mode 100644
>>> index 000000000000..02df4b939a1b
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/include/subdev_id.h
>>> @@ -0,0 +1,39 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Cheng Zhen <[email protected]>
>>> + */
>>> +
>>> +#ifndef _XRT_SUBDEV_ID_H_
>>> +#define _XRT_SUBDEV_ID_H_
>>> +
>>> +/*
>>> + * Every subdev driver has an ID for others to refer to it. There can be multiple number of
>>> + * instances of a subdev driver. A <subdev_id, subdev_instance> tuple is a unique identification
>>> + * of a specific instance of a subdev driver.
>>> + */
>>> +enum xrt_subdev_id {
>>> + XRT_SUBDEV_INVALID = 0,
>>> + XRT_SUBDEV_GRP,
>>> + XRT_SUBDEV_VSEC,
>>> + XRT_SUBDEV_VSEC_GOLDEN,
>>> + XRT_SUBDEV_DEVCTL,
>>> + XRT_SUBDEV_AXIGATE,
>>> + XRT_SUBDEV_ICAP,
>>> + XRT_SUBDEV_TEST,
>>> + XRT_SUBDEV_MGMT_MAIN,
>>> + XRT_SUBDEV_QSPI,
>>> + XRT_SUBDEV_MAILBOX,
>>> + XRT_SUBDEV_CMC,
>>> + XRT_SUBDEV_CALIB,
>>> + XRT_SUBDEV_CLKFREQ,
>>> + XRT_SUBDEV_CLOCK,
>>> + XRT_SUBDEV_SRSR,
>>> + XRT_SUBDEV_UCS,
>>> + XRT_SUBDEV_NUM, /* Total number of subdevs. */
>>> + XRT_ROOT = -1, /* Special ID for root driver. */
>>> +};
>>> +
>>> +#endif /* _XRT_SUBDEV_ID_H_ */
>>> diff --git a/drivers/fpga/xrt/include/xdevice.h b/drivers/fpga/xrt/include/xdevice.h
>>> new file mode 100644
>>> index 000000000000..b40ebe98b54d
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/include/xdevice.h
>>> @@ -0,0 +1,141 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Lizhi Hou <[email protected]>
>>> + */
>>> +
>>> +#ifndef _XRT_DEVICE_H_
>>> +#define _XRT_DEVICE_H_
>>> +
>>> +#include <linux/fs.h>
>>> +#include <linux/cdev.h>
>>> +
>>> +#define XRT_MAX_DEVICE_NODES 128
>>> +#define XRT_INVALID_DEVICE_INST (XRT_MAX_DEVICE_NODES + 1)
>>> +
>>> +enum {
>>> + XRT_DEVICE_STATE_NONE = 0,
>>> + XRT_DEVICE_STATE_ADDED
>>> +};
>>> +
>>> +/*
>>> + * struct xrt_device - represent an xrt device on xrt bus
>>> + *
>>> + * dev: generic device interface.
>>> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
>> ok
>>> + * name: name of the xrt device.
>>> + * instance: instance of the xrt device. The xrt device with same id can have
>>> + * more than 1 instances.
>>> + * state: current state of the xrt device.
>>> + * num_resources: The total number of resource for the xrt device.
>>> + * resource: point to the xrt device resource array.
>>> + * sdev_data: private data pointer.
>>> + */
>>> +struct xrt_device {
>>> + struct device dev;
>>> + u32 subdev_id;
>>> + const char *name;
>>> + u32 instance;
>>> + u32 state;
>>> + u32 num_resources;
>>> + struct resource *resource;
>>> + void *sdev_data;
>>> +};
>>> +
>>> +/*
>>> + * If populated by xrt device driver, infra will handle the mechanics of
>>> + * char device (un)registration.
>>> + */
>>> +enum xrt_dev_file_mode {
>>> + /* Infra create cdev, default file name */
>>> + XRT_DEV_FILE_DEFAULT = 0,
>>> + /* Infra create cdev, need to encode inst num in file name */
>>> + XRT_DEV_FILE_MULTI_INST,
>>> + /* No auto creation of cdev by infra, leaf handles it by itself */
>>> + XRT_DEV_FILE_NO_AUTO,
>>> +};
>>> +
>>> +struct xrt_dev_file_ops {
>>> + const struct file_operations xsf_ops;
>>> + dev_t xsf_dev_t;
>>> + const char *xsf_dev_name;
>>> + enum xrt_dev_file_mode xsf_mode;
>>> +};
>>> +
>>> +/*
>>> + * this struct define the endpoints belong to the same xrt device
>>> + * ep_name: endpoint name
>>> + * compat: compatible string
>> ok
>>> + */
>>> +struct xrt_dev_ep_names {
>>> + const char *ep_name;
>>> + const char *compat;
>>> +};
>>> +
>>> +struct xrt_dev_endpoints {
>>> + struct xrt_dev_ep_names *xse_names;
>>> + /* minimum number of endpoints to support the subdevice */
>>> + u32 xse_min_ep;
>>> +};
>>> +
>>> +/*
>>> + * struct xrt_driver - represent a xrt device driver
>>> + *
>>> + * driver: driver model structure.
>>> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
>>> + * file_ops: character device name and callbacks.
>>> + * probe: mandatory callback for device binding.
>>> + * remove: callback for device unbinding.
>>> + * leaf_call: callback for servicing other leaf drivers.
>> ok
>>> + */
>>> +struct xrt_driver {
>>> + struct device_driver driver;
>>> + u32 subdev_id;
>>> + struct xrt_dev_file_ops file_ops;
>>> + struct xrt_dev_endpoints *endpoints;
>>> +
>>> + /*
>>> + * Subdev driver callbacks populated by subdev driver.
>>> + */
>>> + int (*probe)(struct xrt_device *xrt_dev);
>>> + void (*remove)(struct xrt_device *xrt_dev);
>>> + /*
>>> + * If leaf_call is defined, these are called by other leaf drivers.
>>> + * Note that root driver may call into leaf_call of a group driver.
>>> + */
>>> + int (*leaf_call)(struct xrt_device *xrt_dev, u32 cmd, void *arg);
>>> +};
>>> +
>>> +#define to_xrt_dev(d) container_of(d, struct xrt_device, dev)
>>> +#define to_xrt_drv(d) container_of(d, struct xrt_driver, driver)
>>> +
>>> +static inline void *xrt_get_drvdata(const struct xrt_device *xdev)
>>> +{
>>> + return dev_get_drvdata(&xdev->dev);
>>> +}
>>> +
>>> +static inline void xrt_set_drvdata(struct xrt_device *xdev, void *data)
>>> +{
>>> + dev_set_drvdata(&xdev->dev, data);
>>> +}
>>> +
>>> +static inline void *xrt_get_xdev_data(struct device *dev)
>>> +{
>>> + struct xrt_device *xdev = to_xrt_dev(dev);
>>> +
>>> + return xdev->sdev_data;
>>> +}
>>> +
>>> +struct xrt_device *
>>> +xrt_device_register(struct device *parent, u32 id,
>>> + struct resource *res, u32 res_num,
>>> + void *pdata, size_t data_sz);
>>> +void xrt_device_unregister(struct xrt_device *xdev);
>>> +int xrt_register_driver(struct xrt_driver *drv);
>>> +void xrt_unregister_driver(struct xrt_driver *drv);
>>> +void *xrt_get_xdev_data(struct device *dev);
>>> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32 type, u32 num);
>>> +
>>> +#endif /* _XRT_DEVICE_H_ */
>>> diff --git a/drivers/fpga/xrt/include/xleaf.h b/drivers/fpga/xrt/include/xleaf.h
>>> new file mode 100644
>>> index 000000000000..f065fc766e0f
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/include/xleaf.h
>>> @@ -0,0 +1,205 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Cheng Zhen <[email protected]>
>>> + * Sonal Santan <[email protected]>
>>> + */
>>> +
>>> +#ifndef _XRT_XLEAF_H_
>>> +#define _XRT_XLEAF_H_
>>> +
>>> +#include <linux/mod_devicetable.h>
>>> +#include "xdevice.h"
>>> +#include "subdev_id.h"
>>> +#include "xroot.h"
>>> +#include "events.h"
>>> +
>>> +/* All subdev drivers should use below common routines to print out msg. */
>>> +#define DEV(xdev) (&(xdev)->dev)
>>> +#define DEV_PDATA(xdev) \
>>> + ((struct xrt_subdev_platdata *)xrt_get_xdev_data(DEV(xdev)))
>>> +#define DEV_FILE_OPS(xdev) \
>>> + (&(to_xrt_drv((xdev)->dev.driver))->file_ops)
>>> +#define FMT_PRT(prt_fn, xdev, fmt, args...) \
>>> + ({typeof(xdev) (_xdev) = (xdev); \
>>> + prt_fn(DEV(_xdev), "%s %s: " fmt, \
>>> + DEV_PDATA(_xdev)->xsp_root_name, __func__, ##args); })
>>> +#define xrt_err(xdev, fmt, args...) FMT_PRT(dev_err, xdev, fmt, ##args)
>>> +#define xrt_warn(xdev, fmt, args...) FMT_PRT(dev_warn, xdev, fmt, ##args)
>>> +#define xrt_info(xdev, fmt, args...) FMT_PRT(dev_info, xdev, fmt, ##args)
>>> +#define xrt_dbg(xdev, fmt, args...) FMT_PRT(dev_dbg, xdev, fmt, ##args)
>>> +
>>> +#define XRT_DEFINE_REGMAP_CONFIG(config_name) \
>>> + static const struct regmap_config config_name = { \
>>> + .reg_bits = 32, \
>>> + .val_bits = 32, \
>>> + .reg_stride = 4, \
>>> + .max_register = 0x1000, \
>>> + }
>>> +
>>> +enum {
>>> + /* Starting cmd for common leaf cmd implemented by all leaves. */
>>> + XRT_XLEAF_COMMON_BASE = 0,
>>> + /* Starting cmd for leaves' specific leaf cmds. */
>>> + XRT_XLEAF_CUSTOM_BASE = 64,
>>> +};
>>> +
>>> +enum xrt_xleaf_common_leaf_cmd {
>>> + XRT_XLEAF_EVENT = XRT_XLEAF_COMMON_BASE,
>>> +};
>>> +
>>> +/*
>>> + * Partially initialized by the parent driver, then, passed in as subdev driver's
>>> + * platform data when creating subdev driver instance by calling platform
>>> + * device register API (xrt_device_register_data() or the likes).
>>> + *
>>> + * Once device register API returns, platform driver framework makes a copy of
>>> + * this buffer and maintains its life cycle. The content of the buffer is
>>> + * completely owned by subdev driver.
>>> + *
>>> + * Thus, parent driver should be very careful when it touches this buffer
>>> + * again once it's handed over to subdev driver. And the data structure
>>> + * should not contain pointers pointing to buffers that is managed by
>>> + * other or parent drivers since it could have been freed before platform
>>> + * data buffer is freed by platform driver framework.
>>> + */
>>> +struct xrt_subdev_platdata {
>>> + /*
>>> + * Per driver instance callback. The xdev points to the instance.
>>> + * Should always be defined for subdev driver to get service from root.
>>> + */
>>> + xrt_subdev_root_cb_t xsp_root_cb;
>>> + void *xsp_root_cb_arg;
>>> +
>>> + /* Something to associate w/ root for msg printing. */
>>> + const char *xsp_root_name;
>>> +
>>> + /*
>>> + * Char dev support for this subdev instance.
>>> + * Initialized by subdev driver.
>>> + */
>>> + struct cdev xsp_cdev;
>>> + struct device *xsp_sysdev;
>>> + struct mutex xsp_devnode_lock; /* devnode lock */
>>> + struct completion xsp_devnode_comp;
>>> + int xsp_devnode_ref;
>>> + bool xsp_devnode_online;
>>> + bool xsp_devnode_excl;
>>> +
>>> + /*
>>> + * Subdev driver specific init data. The buffer should be embedded
>>> + * in this data structure buffer after dtb, so that it can be freed
>>> + * together with platform data.
>>> + */
>>> + loff_t xsp_priv_off; /* Offset into this platform data buffer. */
>>> + size_t xsp_priv_len;
>>> +
>>> + /*
>>> + * Populated by parent driver to describe the device tree for
>>> + * the subdev driver to handle. Should always be last one since it's
>>> + * of variable length.
>>> + */
>>> + bool xsp_dtb_valid;
>>> + char xsp_dtb[0];
>>> +};
>>> +
>>> +struct subdev_match_arg {
>>> + enum xrt_subdev_id id;
>>> + int instance;
>>> +};
>>> +
>>> +bool xleaf_has_endpoint(struct xrt_device *xdev, const char *endpoint_name);
>>> +struct xrt_device *xleaf_get_leaf(struct xrt_device *xdev,
>>> + xrt_subdev_match_t cb, void *arg);
>>> +
>>> +static inline bool subdev_match(enum xrt_subdev_id id, struct xrt_device *xdev, void *arg)
>>> +{
>>> + const struct subdev_match_arg *a = (struct subdev_match_arg *)arg;
>>> + int instance = a->instance;
>>> +
>>> + if (id != a->id)
>>> + return false;
>>> + if (instance != xdev->instance && instance != XRT_INVALID_DEVICE_INST)
>>> + return false;
>>> + return true;
>>> +}
>>> +
>>> +static inline bool xrt_subdev_match_epname(enum xrt_subdev_id id,
>>> + struct xrt_device *xdev, void *arg)
>>> +{
>>> + return xleaf_has_endpoint(xdev, arg);
>>> +}
>>> +
>>> +static inline struct xrt_device *
>>> +xleaf_get_leaf_by_id(struct xrt_device *xdev,
>>> + enum xrt_subdev_id id, int instance)
>>> +{
>>> + struct subdev_match_arg arg = { id, instance };
>>> +
>>> + return xleaf_get_leaf(xdev, subdev_match, &arg);
>>> +}
>>> +
>>> +static inline struct xrt_device *
>>> +xleaf_get_leaf_by_epname(struct xrt_device *xdev, const char *name)
>>> +{
>>> + return xleaf_get_leaf(xdev, xrt_subdev_match_epname, (void *)name);
>>> +}
>>> +
>>> +static inline int xleaf_call(struct xrt_device *tgt, u32 cmd, void *arg)
>>> +{
>>> + return (to_xrt_drv(tgt->dev.driver)->leaf_call)(tgt, cmd, arg);
>>> +}
>>> +
>>> +int xleaf_broadcast_event(struct xrt_device *xdev, enum xrt_events evt, bool async);
>>> +int xleaf_create_group(struct xrt_device *xdev, char *dtb);
>>> +int xleaf_destroy_group(struct xrt_device *xdev, int instance);
>>> +void xleaf_get_root_res(struct xrt_device *xdev, u32 region_id, struct resource **res);
>>> +void xleaf_get_root_id(struct xrt_device *xdev, unsigned short *vendor, unsigned short *device,
>>> + unsigned short *subvendor, unsigned short *subdevice);
>>> +void xleaf_hot_reset(struct xrt_device *xdev);
>>> +int xleaf_put_leaf(struct xrt_device *xdev, struct xrt_device *leaf);
>>> +struct device *xleaf_register_hwmon(struct xrt_device *xdev, const char *name, void *drvdata,
>>> + const struct attribute_group **grps);
>>> +void xleaf_unregister_hwmon(struct xrt_device *xdev, struct device *hwmon);
>>> +int xleaf_wait_for_group_bringup(struct xrt_device *xdev);
>>> +
>>> +/*
>>> + * Character device helper APIs for use by leaf drivers
>>> + */
>>> +static inline bool xleaf_devnode_enabled(struct xrt_device *xdev)
>>> +{
>>> + return DEV_FILE_OPS(xdev)->xsf_ops.open;
>>> +}
>>> +
>>> +int xleaf_devnode_create(struct xrt_device *xdev,
>>> + const char *file_name, const char *inst_name);
>>> +void xleaf_devnode_destroy(struct xrt_device *xdev);
>>> +
>>> +struct xrt_device *xleaf_devnode_open_excl(struct inode *inode);
>>> +struct xrt_device *xleaf_devnode_open(struct inode *inode);
>>> +void xleaf_devnode_close(struct inode *inode);
>>> +
>>> +/* Module's init/fini routines for leaf driver in xrt-lib module */
>>> +#define XRT_LEAF_INIT_FINI_FUNC(name) \
>>> +void name##_leaf_init_fini(bool init) \
>>> +{ \
>>> + if (init) \
>>> + xrt_register_driver(&xrt_##name##_driver); \
>>> + else \
>>> + xrt_unregister_driver(&xrt_##name##_driver); \
>>> +}
>>> +
>>> +/* Module's init/fini routines for leaf driver in xrt-lib module */
>>> +void group_leaf_init_fini(bool init);
>>> +void vsec_leaf_init_fini(bool init);
>>> +void devctl_leaf_init_fini(bool init);
>>> +void axigate_leaf_init_fini(bool init);
>>> +void icap_leaf_init_fini(bool init);
>>> +void calib_leaf_init_fini(bool init);
>>> +void clkfreq_leaf_init_fini(bool init);
>>> +void clock_leaf_init_fini(bool init);
>>> +void ucs_leaf_init_fini(bool init);
>>> +
>>> +#endif /* _XRT_LEAF_H_ */
>>> diff --git a/drivers/fpga/xrt/include/xleaf/clkfreq.h b/drivers/fpga/xrt/include/xleaf/clkfreq.h
>>> new file mode 100644
>>> index 000000000000..005441d5df78
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/include/xleaf/clkfreq.h
>>> @@ -0,0 +1,21 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Lizhi Hou <[email protected]>
>>> + */
>>> +
>>> +#ifndef _XRT_CLKFREQ_H_
>>> +#define _XRT_CLKFREQ_H_
>>> +
>>> +#include "xleaf.h"
>>> +
>>> +/*
>>> + * CLKFREQ driver leaf calls.
>>> + */
>>> +enum xrt_clkfreq_leaf_cmd {
>>> + XRT_CLKFREQ_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
>>> +};
>>> +
>>> +#endif /* _XRT_CLKFREQ_H_ */
>>> diff --git a/drivers/fpga/xrt/include/xleaf/clock.h b/drivers/fpga/xrt/include/xleaf/clock.h
>>> new file mode 100644
>>> index 000000000000..1379e24fa5d0
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/include/xleaf/clock.h
>>> @@ -0,0 +1,29 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Lizhi Hou <[email protected]>
>>> + */
>>> +
>>> +#ifndef _XRT_CLOCK_H_
>>> +#define _XRT_CLOCK_H_
>>> +
>>> +#include "xleaf.h"
>>> +#include <linux/fpga_xrt.h>
>>> +
>>> +/*
>>> + * CLOCK driver leaf calls.
>>> + */
>>> +enum xrt_clock_leaf_cmd {
>>> + XRT_CLOCK_SET = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
>>> + XRT_CLOCK_GET,
>>> + XRT_CLOCK_VERIFY,
>>> +};
>>> +
>>> +struct xrt_clock_get {
>>> + u16 freq;
>>> + u32 freq_cnter;
>>> +};
>>> +
>>> +#endif /* _XRT_CLOCK_H_ */
>>> diff --git a/drivers/fpga/xrt/include/xleaf/ddr_calibration.h b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>> new file mode 100644
>>> index 000000000000..c44ae30f939a
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>> @@ -0,0 +1,28 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Cheng Zhen <[email protected]>
>>> + */
>>> +
>>> +#ifndef _XRT_DDR_CALIBRATION_H_
>>> +#define _XRT_DDR_CALIBRATION_H_
>>> +
>>> +#include "xleaf.h"
>>> +#include <linux/fpga-xrt.h>
>>> +
>>> +/*
>>> + * Memory calibration driver leaf calls.
>>> + */
>>> +enum xrt_calib_results {
>>> + XRT_CALIB_UNKNOWN = 0,
>>> + XRT_CALIB_SUCCEEDED,
>>> + XRT_CALIB_FAILED,
>>> +};
>>> +
>>> +enum xrt_calib_leaf_cmd {
>>> + XRT_CALIB_RESULT = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
>>> +};
>>> +
>>> +#endif /* _XRT_DDR_CALIBRATION_H_ */
>>> diff --git a/drivers/fpga/xrt/include/xleaf/devctl.h b/drivers/fpga/xrt/include/xleaf/devctl.h
>>> new file mode 100644
>>> index 000000000000..b97f3b6d9326
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/include/xleaf/devctl.h
>>> @@ -0,0 +1,40 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Lizhi Hou <[email protected]>
>>> + */
>>> +
>>> +#ifndef _XRT_DEVCTL_H_
>>> +#define _XRT_DEVCTL_H_
>>> +
>>> +#include "xleaf.h"
>>> +
>>> +/*
>>> + * DEVCTL driver leaf calls.
>>> + */
>>> +enum xrt_devctl_leaf_cmd {
>>> + XRT_DEVCTL_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
>>> +};
>>> +
>>> +enum xrt_devctl_id {
>>> + XRT_DEVCTL_ROM_UUID = 0,
>>> + XRT_DEVCTL_DDR_CALIB,
>>> + XRT_DEVCTL_GOLDEN_VER,
>>> + XRT_DEVCTL_MAX
>>> +};
>>> +
>>> +struct xrt_devctl_rw {
>>> + u32 xdr_id;
>>> + void *xdr_buf;
>>> + u32 xdr_len;
>>> + u32 xdr_offset;
>>> +};
>>> +
>>> +struct xrt_devctl_intf_uuid {
>>> + u32 uuid_num;
>>> + uuid_t *uuids;
>>> +};
>>> +
>>> +#endif /* _XRT_DEVCTL_H_ */
>>> diff --git a/drivers/fpga/xrt/lib/lib-drv.c b/drivers/fpga/xrt/lib/lib-drv.c
>>> new file mode 100644
>>> index 000000000000..7d21ef565651
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/lib/lib-drv.c
>>> @@ -0,0 +1,318 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Cheng Zhen <[email protected]>
>>> + * Lizhi Hou <[email protected]>
>>> + */
>>> +
>>> +#include <linux/module.h>
>>> +#include <linux/vmalloc.h>
>>> +#include <linux/slab.h>
>>> +#include "xleaf.h"
>>> +#include "xroot.h"
>>> +#include "lib-drv.h"
>>> +
>>> +#define XRT_IPLIB_MODULE_NAME "xrt-lib"
>> ok
>>> +#define XRT_DRVNAME(drv) ((drv)->driver.name)
>>> +
>>> +#define XRT_SUBDEV_ID_SHIFT 16
>>> +#define XRT_SUBDEV_ID_MASK ((1 << XRT_SUBDEV_ID_SHIFT) - 1)
>>> +
>>> +struct xrt_find_drv_data {
>>> + enum xrt_subdev_id id;
>>> + struct xrt_driver *xdrv;
>>> +};
>>> +
>>> +struct class *xrt_class;
>>> +static DEFINE_IDA(xrt_device_ida);
>>> +
>>> +static inline u32 xrt_instance_to_id(enum xrt_subdev_id id, u32 instance)
>>> +{
>>> + return (id << XRT_SUBDEV_ID_SHIFT) | instance;
>>> +}
>>> +
>>> +static inline u32 xrt_id_to_instance(u32 id)
>>> +{
>>> + return id & XRT_SUBDEV_ID_MASK;
>> ok
>>> +}
>>> +
>>> +static int xrt_bus_match(struct device *dev, struct device_driver *drv)
>>> +{
>>> + struct xrt_device *xdev = to_xrt_dev(dev);
>>> + struct xrt_driver *xdrv = to_xrt_drv(drv);
>>> +
>>> + if (xdev->subdev_id == xdrv->subdev_id)
>>> + return 1;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int xrt_bus_probe(struct device *dev)
>>> +{
>>> + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
>>> + struct xrt_device *xdev = to_xrt_dev(dev);
>>> +
>>> + return xdrv->probe(xdev);
>>> +}
>>> +
>>> +static int xrt_bus_remove(struct device *dev)
>>> +{
>>> + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
>>> + struct xrt_device *xdev = to_xrt_dev(dev);
>>> +
>>> + if (xdrv->remove)
>>> + xdrv->remove(xdev);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +struct bus_type xrt_bus_type = {
>>> + .name = "xrt",
>>> + .match = xrt_bus_match,
>>> + .probe = xrt_bus_probe,
>>> + .remove = xrt_bus_remove,
>>> +};
>>> +
>>> +int xrt_register_driver(struct xrt_driver *drv)
>>> +{
>>> + const char *drvname = XRT_DRVNAME(drv);
>>> + int rc = 0;
>>> +
>>> + /* Initialize dev_t for char dev node. */
>>> + if (drv->file_ops.xsf_ops.open) {
>>> + rc = alloc_chrdev_region(&drv->file_ops.xsf_dev_t, 0,
>>> + XRT_MAX_DEVICE_NODES, drvname);
>>> + if (rc) {
>>> + pr_err("failed to alloc dev minor for %s: %d\n", drvname, rc);
>>> + return rc;
>>> + }
>>> + } else {
>>> + drv->file_ops.xsf_dev_t = (dev_t)-1;
>>> + }
>>> +
>>> + drv->driver.owner = THIS_MODULE;
>>> + drv->driver.bus = &xrt_bus_type;
>>> +
>>> + rc = driver_register(&drv->driver);
>>> + if (rc) {
>>> + pr_err("register %s xrt driver failed\n", drvname);
>>> + if (drv->file_ops.xsf_dev_t != (dev_t)-1) {
>>> + unregister_chrdev_region(drv->file_ops.xsf_dev_t,
>>> + XRT_MAX_DEVICE_NODES);
>>> + }
>>> + return rc;
>>> + }
>>> +
>>> + pr_info("%s registered successfully\n", drvname);
>>> +
>>> + return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(xrt_register_driver);
>>> +
>>> +void xrt_unregister_driver(struct xrt_driver *drv)
>>> +{
>>> + driver_unregister(&drv->driver);
>>> +
>>> + if (drv->file_ops.xsf_dev_t != (dev_t)-1)
>>> + unregister_chrdev_region(drv->file_ops.xsf_dev_t, XRT_MAX_DEVICE_NODES);
>> ok
>>> +}
>>> +EXPORT_SYMBOL_GPL(xrt_unregister_driver);
>>> +
>>> +static int __find_driver(struct device_driver *drv, void *_data)
>>> +{
>>> + struct xrt_driver *xdrv = to_xrt_drv(drv);
>>> + struct xrt_find_drv_data *data = _data;
>>> +
>>> + if (xdrv->subdev_id == data->id) {
>>> + data->xdrv = xdrv;
>>> + return 1;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +const char *xrt_drv_name(enum xrt_subdev_id id)
>>> +{
>>> + struct xrt_find_drv_data data = { 0 };
>>> +
>>> + data.id = id;
>>> + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
>>> +
>>> + if (data.xdrv)
>>> + return XRT_DRVNAME(data.xdrv);
>>> +
>>> + return NULL;
>>> +}
>>> +
>>> +static int xrt_drv_get_instance(enum xrt_subdev_id id)
>>> +{
>>> + int ret;
>>> +
>>> + ret = ida_alloc_range(&xrt_device_ida, xrt_instance_to_id(id, 0),
>>> + xrt_instance_to_id(id, XRT_MAX_DEVICE_NODES),
>>> + GFP_KERNEL);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + return xrt_id_to_instance((u32)ret);
>>> +}
>>> +
>>> +static void xrt_drv_put_instance(enum xrt_subdev_id id, int instance)
>>> +{
>>> + ida_free(&xrt_device_ida, xrt_instance_to_id(id, instance));
>>> +}
>>> +
>>> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id id)
>>> +{
>>> + struct xrt_find_drv_data data = { 0 };
>>> +
>>> + data.id = id;
>>> + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
>>> +
>>> + if (data.xdrv)
>>> + return data.xdrv->endpoints;
>>> +
>>> + return NULL;
>>> +}
>>> +
>>> +static void xrt_device_release(struct device *dev)
>>> +{
>>> + struct xrt_device *xdev = container_of(dev, struct xrt_device, dev);
>>> +
>>> + kfree(xdev);
>>> +}
>>> +
>>> +void xrt_device_unregister(struct xrt_device *xdev)
>>> +{
>>> + if (xdev->state == XRT_DEVICE_STATE_ADDED)
>>> + device_del(&xdev->dev);
>>> +
>>> + vfree(xdev->sdev_data);
>> cleanup of sdev_data here
>>
>> fail: label in xrt_device_register is ok.
>>
>>> + kfree(xdev->resource);
>>> +
>>> + if (xdev->instance != XRT_INVALID_DEVICE_INST)
>>> + xrt_drv_put_instance(xdev->subdev_id, xdev->instance);
>>> +
>>> + if (xdev->dev.release == xrt_device_release)
>>> + put_device(&xdev->dev);
>>> +}
>>> +
>>> +struct xrt_device *
>>> +xrt_device_register(struct device *parent, u32 id,
>>> + struct resource *res, u32 res_num,
>>> + void *pdata, size_t data_sz)
>>> +{
>>> + struct xrt_device *xdev = NULL;
>>> + int ret;
>>> +
>>> + xdev = kzalloc(sizeof(*xdev), GFP_KERNEL);
>>> + if (!xdev)
>>> + return xdev;
>> ok
>>> + xdev->instance = XRT_INVALID_DEVICE_INST;
>>> +
>>> + /* Obtain dev instance number. */
>>> + ret = xrt_drv_get_instance(id);
>>> + if (ret < 0) {
>>> + dev_err(parent, "failed get instance, ret %d", ret);
>>> + goto fail;
>>> + }
>>> +
>>> + xdev->instance = ret;
>>> + xdev->name = xrt_drv_name(id);
>>> + xdev->subdev_id = id;
>>> + device_initialize(&xdev->dev);
>>> + xdev->dev.release = xrt_device_release;
>>> + xdev->dev.parent = parent;
>>> +
>>> + xdev->dev.bus = &xrt_bus_type;
>>> + dev_set_name(&xdev->dev, "%s.%d", xdev->name, xdev->instance);
>>> +
>>> + xdev->num_resources = res_num;
>>> + xdev->resource = kmemdup(res, sizeof(*res) * res_num, GFP_KERNEL);
>>> + if (!xdev->resource)
>>> + goto fail;
>>> +
>>> + xdev->sdev_data = vzalloc(data_sz);
>>> + if (!xdev->sdev_data)
>>> + goto fail;
>>> +
>>> + memcpy(xdev->sdev_data, pdata, data_sz);
>>> +
>>> + ret = device_add(&xdev->dev);
>>> + if (ret) {
>>> + dev_err(parent, "failed add device, ret %d", ret);
>>> + goto fail;
>>> + }
>>> + xdev->state = XRT_DEVICE_STATE_ADDED;
>>> +
>>> + return xdev;
>>> +
>>> +fail:
>>> + xrt_device_unregister(xdev);
>>> + kfree(xdev);
>>> +
>>> + return NULL;
>>> +}
>>> +
>>> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32 type, u32 num)
>>> +{
>>> + u32 i;
>>> +
>>> + for (i = 0; i < xdev->num_resources; i++) {
>>> + struct resource *r = &xdev->resource[i];
>>> +
>>> + if (type == resource_type(r) && num-- == 0)
>>> + return r;
>>> + }
>>> + return NULL;
>>> +}
>>> +
>>> +/*
>>> + * Leaf driver's module init/fini callbacks. This is not a open infrastructure for dynamic
>>> + * plugging in drivers. All drivers should be statically added.
>>> + */
>>> +static void (*leaf_init_fini_cbs[])(bool) = {
>>> + group_leaf_init_fini,
>>> + axigate_leaf_init_fini,
>>> + icap_leaf_init_fini,
>>> +};
>>> +
>>> +static __init int xrt_lib_init(void)
>>> +{
>>> + int ret;
>>> + int i;
>>> +
>>> + ret = bus_register(&xrt_bus_type);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + xrt_class = class_create(THIS_MODULE, XRT_IPLIB_MODULE_NAME);
>>> + if (IS_ERR(xrt_class)) {
>>> + bus_unregister(&xrt_bus_type);
>>> + return PTR_ERR(xrt_class);
>>> + }
>>> +
>>> + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
>>> + leaf_init_fini_cbs[i](true);
>>> + return 0;
>>> +}
>>> +
>>> +static __exit void xrt_lib_fini(void)
>>> +{
>>> + int i;
>>> +
>>> + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
>>> + leaf_init_fini_cbs[i](false);
>>> +
>>> + class_destroy(xrt_class);
>>> + bus_unregister(&xrt_bus_type);
>>> +}
>>> +
>>> +module_init(xrt_lib_init);
>>> +module_exit(xrt_lib_fini);
>>> +
>>> +MODULE_AUTHOR("XRT Team <[email protected]>");
>>> +MODULE_DESCRIPTION("Xilinx Alveo IP Lib driver");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/fpga/xrt/lib/lib-drv.h b/drivers/fpga/xrt/lib/lib-drv.h
>>> new file mode 100644
>>> index 000000000000..0276c28e009f
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/lib/lib-drv.h
>>> @@ -0,0 +1,21 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Cheng Zhen <[email protected]>
>>> + */
>>> +
>>> +#ifndef _LIB_DRV_H_
>>> +#define _LIB_DRV_H_
>>> +
>>> +#include <linux/device/class.h>
>>> +#include <linux/device/bus.h>
>>> +
>>> +extern struct class *xrt_class;
>>> +extern struct bus_type xrt_bus_type;
>>> +
>>> +const char *xrt_drv_name(enum xrt_subdev_id id);
>>> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id id);
>>> +
>>> +#endif /* _LIB_DRV_H_ */
Hi Moritz,
(sending it again because rejected by some mail alias)
Did you get time to review XRT patch set? Please let us know if there
are more we need to change.
We are looking for your comments.
Thanks,
Lizhi
On 8/21/21 9:44 AM, Moritz Fischer wrote:
> CAUTION: This message has originated from an External Source. Please use proper judgment and caution when opening attachments, clicking links, or responding to this email.
>
>
> On Thu, Aug 19, 2021 at 05:14:14AM -0700, Tom Rix wrote:
>> On 8/2/21 9:05 AM, Lizhi Hou wrote:
>>
>>> xrt-lib kernel module infrastructure code to register and manage all
>>> leaf driver modules.
>>>
>>> Signed-off-by: Sonal Santan <[email protected]>
>>> Signed-off-by: Max Zhen <[email protected]>
>>> Signed-off-by: Lizhi Hou <[email protected]>
>>> Reviewed-by: Tom Rix <[email protected]>
>> This was the only patch with requested changes in v8.
>>
>> All the changes from v8 have been made.
>>
>> They are itemized below with variations on 'ok'
>>
>> It is still Moritz's call to accept them.
> I'll be OOO till 9/4/21. I'll get to it after.
>
> - Moritz
>> Tom
>>
>>> ---
>>> drivers/fpga/xrt/include/subdev_id.h | 39 +++
>>> drivers/fpga/xrt/include/xdevice.h | 141 ++++++++
>>> drivers/fpga/xrt/include/xleaf.h | 205 +++++++++++
>>> drivers/fpga/xrt/include/xleaf/clkfreq.h | 21 ++
>>> drivers/fpga/xrt/include/xleaf/clock.h | 29 ++
>>> .../fpga/xrt/include/xleaf/ddr_calibration.h | 28 ++
>>> drivers/fpga/xrt/include/xleaf/devctl.h | 40 +++
>>> drivers/fpga/xrt/lib/lib-drv.c | 318 ++++++++++++++++++
>>> drivers/fpga/xrt/lib/lib-drv.h | 21 ++
>>> 9 files changed, 842 insertions(+)
>>> create mode 100644 drivers/fpga/xrt/include/subdev_id.h
>>> create mode 100644 drivers/fpga/xrt/include/xdevice.h
>>> create mode 100644 drivers/fpga/xrt/include/xleaf.h
>>> create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
>>> create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
>>> create mode 100644 drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>> create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
>>> create mode 100644 drivers/fpga/xrt/lib/lib-drv.c
>>> create mode 100644 drivers/fpga/xrt/lib/lib-drv.h
>>>
>>> diff --git a/drivers/fpga/xrt/include/subdev_id.h b/drivers/fpga/xrt/include/subdev_id.h
>>> new file mode 100644
>>> index 000000000000..02df4b939a1b
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/include/subdev_id.h
>>> @@ -0,0 +1,39 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Cheng Zhen <[email protected]>
>>> + */
>>> +
>>> +#ifndef _XRT_SUBDEV_ID_H_
>>> +#define _XRT_SUBDEV_ID_H_
>>> +
>>> +/*
>>> + * Every subdev driver has an ID for others to refer to it. There can be multiple number of
>>> + * instances of a subdev driver. A <subdev_id, subdev_instance> tuple is a unique identification
>>> + * of a specific instance of a subdev driver.
>>> + */
>>> +enum xrt_subdev_id {
>>> + XRT_SUBDEV_INVALID = 0,
>>> + XRT_SUBDEV_GRP,
>>> + XRT_SUBDEV_VSEC,
>>> + XRT_SUBDEV_VSEC_GOLDEN,
>>> + XRT_SUBDEV_DEVCTL,
>>> + XRT_SUBDEV_AXIGATE,
>>> + XRT_SUBDEV_ICAP,
>>> + XRT_SUBDEV_TEST,
>>> + XRT_SUBDEV_MGMT_MAIN,
>>> + XRT_SUBDEV_QSPI,
>>> + XRT_SUBDEV_MAILBOX,
>>> + XRT_SUBDEV_CMC,
>>> + XRT_SUBDEV_CALIB,
>>> + XRT_SUBDEV_CLKFREQ,
>>> + XRT_SUBDEV_CLOCK,
>>> + XRT_SUBDEV_SRSR,
>>> + XRT_SUBDEV_UCS,
>>> + XRT_SUBDEV_NUM, /* Total number of subdevs. */
>>> + XRT_ROOT = -1, /* Special ID for root driver. */
>>> +};
>>> +
>>> +#endif /* _XRT_SUBDEV_ID_H_ */
>>> diff --git a/drivers/fpga/xrt/include/xdevice.h b/drivers/fpga/xrt/include/xdevice.h
>>> new file mode 100644
>>> index 000000000000..b40ebe98b54d
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/include/xdevice.h
>>> @@ -0,0 +1,141 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Lizhi Hou <[email protected]>
>>> + */
>>> +
>>> +#ifndef _XRT_DEVICE_H_
>>> +#define _XRT_DEVICE_H_
>>> +
>>> +#include <linux/fs.h>
>>> +#include <linux/cdev.h>
>>> +
>>> +#define XRT_MAX_DEVICE_NODES 128
>>> +#define XRT_INVALID_DEVICE_INST (XRT_MAX_DEVICE_NODES + 1)
>>> +
>>> +enum {
>>> + XRT_DEVICE_STATE_NONE = 0,
>>> + XRT_DEVICE_STATE_ADDED
>>> +};
>>> +
>>> +/*
>>> + * struct xrt_device - represent an xrt device on xrt bus
>>> + *
>>> + * dev: generic device interface.
>>> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
>> ok
>>> + * name: name of the xrt device.
>>> + * instance: instance of the xrt device. The xrt device with same id can have
>>> + * more than 1 instances.
>>> + * state: current state of the xrt device.
>>> + * num_resources: The total number of resource for the xrt device.
>>> + * resource: point to the xrt device resource array.
>>> + * sdev_data: private data pointer.
>>> + */
>>> +struct xrt_device {
>>> + struct device dev;
>>> + u32 subdev_id;
>>> + const char *name;
>>> + u32 instance;
>>> + u32 state;
>>> + u32 num_resources;
>>> + struct resource *resource;
>>> + void *sdev_data;
>>> +};
>>> +
>>> +/*
>>> + * If populated by xrt device driver, infra will handle the mechanics of
>>> + * char device (un)registration.
>>> + */
>>> +enum xrt_dev_file_mode {
>>> + /* Infra create cdev, default file name */
>>> + XRT_DEV_FILE_DEFAULT = 0,
>>> + /* Infra create cdev, need to encode inst num in file name */
>>> + XRT_DEV_FILE_MULTI_INST,
>>> + /* No auto creation of cdev by infra, leaf handles it by itself */
>>> + XRT_DEV_FILE_NO_AUTO,
>>> +};
>>> +
>>> +struct xrt_dev_file_ops {
>>> + const struct file_operations xsf_ops;
>>> + dev_t xsf_dev_t;
>>> + const char *xsf_dev_name;
>>> + enum xrt_dev_file_mode xsf_mode;
>>> +};
>>> +
>>> +/*
>>> + * this struct define the endpoints belong to the same xrt device
>>> + * ep_name: endpoint name
>>> + * compat: compatible string
>> ok
>>> + */
>>> +struct xrt_dev_ep_names {
>>> + const char *ep_name;
>>> + const char *compat;
>>> +};
>>> +
>>> +struct xrt_dev_endpoints {
>>> + struct xrt_dev_ep_names *xse_names;
>>> + /* minimum number of endpoints to support the subdevice */
>>> + u32 xse_min_ep;
>>> +};
>>> +
>>> +/*
>>> + * struct xrt_driver - represent a xrt device driver
>>> + *
>>> + * driver: driver model structure.
>>> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
>>> + * file_ops: character device name and callbacks.
>>> + * probe: mandatory callback for device binding.
>>> + * remove: callback for device unbinding.
>>> + * leaf_call: callback for servicing other leaf drivers.
>> ok
>>> + */
>>> +struct xrt_driver {
>>> + struct device_driver driver;
>>> + u32 subdev_id;
>>> + struct xrt_dev_file_ops file_ops;
>>> + struct xrt_dev_endpoints *endpoints;
>>> +
>>> + /*
>>> + * Subdev driver callbacks populated by subdev driver.
>>> + */
>>> + int (*probe)(struct xrt_device *xrt_dev);
>>> + void (*remove)(struct xrt_device *xrt_dev);
>>> + /*
>>> + * If leaf_call is defined, these are called by other leaf drivers.
>>> + * Note that root driver may call into leaf_call of a group driver.
>>> + */
>>> + int (*leaf_call)(struct xrt_device *xrt_dev, u32 cmd, void *arg);
>>> +};
>>> +
>>> +#define to_xrt_dev(d) container_of(d, struct xrt_device, dev)
>>> +#define to_xrt_drv(d) container_of(d, struct xrt_driver, driver)
>>> +
>>> +static inline void *xrt_get_drvdata(const struct xrt_device *xdev)
>>> +{
>>> + return dev_get_drvdata(&xdev->dev);
>>> +}
>>> +
>>> +static inline void xrt_set_drvdata(struct xrt_device *xdev, void *data)
>>> +{
>>> + dev_set_drvdata(&xdev->dev, data);
>>> +}
>>> +
>>> +static inline void *xrt_get_xdev_data(struct device *dev)
>>> +{
>>> + struct xrt_device *xdev = to_xrt_dev(dev);
>>> +
>>> + return xdev->sdev_data;
>>> +}
>>> +
>>> +struct xrt_device *
>>> +xrt_device_register(struct device *parent, u32 id,
>>> + struct resource *res, u32 res_num,
>>> + void *pdata, size_t data_sz);
>>> +void xrt_device_unregister(struct xrt_device *xdev);
>>> +int xrt_register_driver(struct xrt_driver *drv);
>>> +void xrt_unregister_driver(struct xrt_driver *drv);
>>> +void *xrt_get_xdev_data(struct device *dev);
>>> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32 type, u32 num);
>>> +
>>> +#endif /* _XRT_DEVICE_H_ */
>>> diff --git a/drivers/fpga/xrt/include/xleaf.h b/drivers/fpga/xrt/include/xleaf.h
>>> new file mode 100644
>>> index 000000000000..f065fc766e0f
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/include/xleaf.h
>>> @@ -0,0 +1,205 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Cheng Zhen <[email protected]>
>>> + * Sonal Santan <[email protected]>
>>> + */
>>> +
>>> +#ifndef _XRT_XLEAF_H_
>>> +#define _XRT_XLEAF_H_
>>> +
>>> +#include <linux/mod_devicetable.h>
>>> +#include "xdevice.h"
>>> +#include "subdev_id.h"
>>> +#include "xroot.h"
>>> +#include "events.h"
>>> +
>>> +/* All subdev drivers should use below common routines to print out msg. */
>>> +#define DEV(xdev) (&(xdev)->dev)
>>> +#define DEV_PDATA(xdev) \
>>> + ((struct xrt_subdev_platdata *)xrt_get_xdev_data(DEV(xdev)))
>>> +#define DEV_FILE_OPS(xdev) \
>>> + (&(to_xrt_drv((xdev)->dev.driver))->file_ops)
>>> +#define FMT_PRT(prt_fn, xdev, fmt, args...) \
>>> + ({typeof(xdev) (_xdev) = (xdev); \
>>> + prt_fn(DEV(_xdev), "%s %s: " fmt, \
>>> + DEV_PDATA(_xdev)->xsp_root_name, __func__, ##args); })
>>> +#define xrt_err(xdev, fmt, args...) FMT_PRT(dev_err, xdev, fmt, ##args)
>>> +#define xrt_warn(xdev, fmt, args...) FMT_PRT(dev_warn, xdev, fmt, ##args)
>>> +#define xrt_info(xdev, fmt, args...) FMT_PRT(dev_info, xdev, fmt, ##args)
>>> +#define xrt_dbg(xdev, fmt, args...) FMT_PRT(dev_dbg, xdev, fmt, ##args)
>>> +
>>> +#define XRT_DEFINE_REGMAP_CONFIG(config_name) \
>>> + static const struct regmap_config config_name = { \
>>> + .reg_bits = 32, \
>>> + .val_bits = 32, \
>>> + .reg_stride = 4, \
>>> + .max_register = 0x1000, \
>>> + }
>>> +
>>> +enum {
>>> + /* Starting cmd for common leaf cmd implemented by all leaves. */
>>> + XRT_XLEAF_COMMON_BASE = 0,
>>> + /* Starting cmd for leaves' specific leaf cmds. */
>>> + XRT_XLEAF_CUSTOM_BASE = 64,
>>> +};
>>> +
>>> +enum xrt_xleaf_common_leaf_cmd {
>>> + XRT_XLEAF_EVENT = XRT_XLEAF_COMMON_BASE,
>>> +};
>>> +
>>> +/*
>>> + * Partially initialized by the parent driver, then, passed in as subdev driver's
>>> + * platform data when creating subdev driver instance by calling platform
>>> + * device register API (xrt_device_register_data() or the likes).
>>> + *
>>> + * Once device register API returns, platform driver framework makes a copy of
>>> + * this buffer and maintains its life cycle. The content of the buffer is
>>> + * completely owned by subdev driver.
>>> + *
>>> + * Thus, parent driver should be very careful when it touches this buffer
>>> + * again once it's handed over to subdev driver. And the data structure
>>> + * should not contain pointers pointing to buffers that is managed by
>>> + * other or parent drivers since it could have been freed before platform
>>> + * data buffer is freed by platform driver framework.
>>> + */
>>> +struct xrt_subdev_platdata {
>>> + /*
>>> + * Per driver instance callback. The xdev points to the instance.
>>> + * Should always be defined for subdev driver to get service from root.
>>> + */
>>> + xrt_subdev_root_cb_t xsp_root_cb;
>>> + void *xsp_root_cb_arg;
>>> +
>>> + /* Something to associate w/ root for msg printing. */
>>> + const char *xsp_root_name;
>>> +
>>> + /*
>>> + * Char dev support for this subdev instance.
>>> + * Initialized by subdev driver.
>>> + */
>>> + struct cdev xsp_cdev;
>>> + struct device *xsp_sysdev;
>>> + struct mutex xsp_devnode_lock; /* devnode lock */
>>> + struct completion xsp_devnode_comp;
>>> + int xsp_devnode_ref;
>>> + bool xsp_devnode_online;
>>> + bool xsp_devnode_excl;
>>> +
>>> + /*
>>> + * Subdev driver specific init data. The buffer should be embedded
>>> + * in this data structure buffer after dtb, so that it can be freed
>>> + * together with platform data.
>>> + */
>>> + loff_t xsp_priv_off; /* Offset into this platform data buffer. */
>>> + size_t xsp_priv_len;
>>> +
>>> + /*
>>> + * Populated by parent driver to describe the device tree for
>>> + * the subdev driver to handle. Should always be last one since it's
>>> + * of variable length.
>>> + */
>>> + bool xsp_dtb_valid;
>>> + char xsp_dtb[0];
>>> +};
>>> +
>>> +struct subdev_match_arg {
>>> + enum xrt_subdev_id id;
>>> + int instance;
>>> +};
>>> +
>>> +bool xleaf_has_endpoint(struct xrt_device *xdev, const char *endpoint_name);
>>> +struct xrt_device *xleaf_get_leaf(struct xrt_device *xdev,
>>> + xrt_subdev_match_t cb, void *arg);
>>> +
>>> +static inline bool subdev_match(enum xrt_subdev_id id, struct xrt_device *xdev, void *arg)
>>> +{
>>> + const struct subdev_match_arg *a = (struct subdev_match_arg *)arg;
>>> + int instance = a->instance;
>>> +
>>> + if (id != a->id)
>>> + return false;
>>> + if (instance != xdev->instance && instance != XRT_INVALID_DEVICE_INST)
>>> + return false;
>>> + return true;
>>> +}
>>> +
>>> +static inline bool xrt_subdev_match_epname(enum xrt_subdev_id id,
>>> + struct xrt_device *xdev, void *arg)
>>> +{
>>> + return xleaf_has_endpoint(xdev, arg);
>>> +}
>>> +
>>> +static inline struct xrt_device *
>>> +xleaf_get_leaf_by_id(struct xrt_device *xdev,
>>> + enum xrt_subdev_id id, int instance)
>>> +{
>>> + struct subdev_match_arg arg = { id, instance };
>>> +
>>> + return xleaf_get_leaf(xdev, subdev_match, &arg);
>>> +}
>>> +
>>> +static inline struct xrt_device *
>>> +xleaf_get_leaf_by_epname(struct xrt_device *xdev, const char *name)
>>> +{
>>> + return xleaf_get_leaf(xdev, xrt_subdev_match_epname, (void *)name);
>>> +}
>>> +
>>> +static inline int xleaf_call(struct xrt_device *tgt, u32 cmd, void *arg)
>>> +{
>>> + return (to_xrt_drv(tgt->dev.driver)->leaf_call)(tgt, cmd, arg);
>>> +}
>>> +
>>> +int xleaf_broadcast_event(struct xrt_device *xdev, enum xrt_events evt, bool async);
>>> +int xleaf_create_group(struct xrt_device *xdev, char *dtb);
>>> +int xleaf_destroy_group(struct xrt_device *xdev, int instance);
>>> +void xleaf_get_root_res(struct xrt_device *xdev, u32 region_id, struct resource **res);
>>> +void xleaf_get_root_id(struct xrt_device *xdev, unsigned short *vendor, unsigned short *device,
>>> + unsigned short *subvendor, unsigned short *subdevice);
>>> +void xleaf_hot_reset(struct xrt_device *xdev);
>>> +int xleaf_put_leaf(struct xrt_device *xdev, struct xrt_device *leaf);
>>> +struct device *xleaf_register_hwmon(struct xrt_device *xdev, const char *name, void *drvdata,
>>> + const struct attribute_group **grps);
>>> +void xleaf_unregister_hwmon(struct xrt_device *xdev, struct device *hwmon);
>>> +int xleaf_wait_for_group_bringup(struct xrt_device *xdev);
>>> +
>>> +/*
>>> + * Character device helper APIs for use by leaf drivers
>>> + */
>>> +static inline bool xleaf_devnode_enabled(struct xrt_device *xdev)
>>> +{
>>> + return DEV_FILE_OPS(xdev)->xsf_ops.open;
>>> +}
>>> +
>>> +int xleaf_devnode_create(struct xrt_device *xdev,
>>> + const char *file_name, const char *inst_name);
>>> +void xleaf_devnode_destroy(struct xrt_device *xdev);
>>> +
>>> +struct xrt_device *xleaf_devnode_open_excl(struct inode *inode);
>>> +struct xrt_device *xleaf_devnode_open(struct inode *inode);
>>> +void xleaf_devnode_close(struct inode *inode);
>>> +
>>> +/* Module's init/fini routines for leaf driver in xrt-lib module */
>>> +#define XRT_LEAF_INIT_FINI_FUNC(name) \
>>> +void name##_leaf_init_fini(bool init) \
>>> +{ \
>>> + if (init) \
>>> + xrt_register_driver(&xrt_##name##_driver); \
>>> + else \
>>> + xrt_unregister_driver(&xrt_##name##_driver); \
>>> +}
>>> +
>>> +/* Module's init/fini routines for leaf driver in xrt-lib module */
>>> +void group_leaf_init_fini(bool init);
>>> +void vsec_leaf_init_fini(bool init);
>>> +void devctl_leaf_init_fini(bool init);
>>> +void axigate_leaf_init_fini(bool init);
>>> +void icap_leaf_init_fini(bool init);
>>> +void calib_leaf_init_fini(bool init);
>>> +void clkfreq_leaf_init_fini(bool init);
>>> +void clock_leaf_init_fini(bool init);
>>> +void ucs_leaf_init_fini(bool init);
>>> +
>>> +#endif /* _XRT_LEAF_H_ */
>>> diff --git a/drivers/fpga/xrt/include/xleaf/clkfreq.h b/drivers/fpga/xrt/include/xleaf/clkfreq.h
>>> new file mode 100644
>>> index 000000000000..005441d5df78
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/include/xleaf/clkfreq.h
>>> @@ -0,0 +1,21 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Lizhi Hou <[email protected]>
>>> + */
>>> +
>>> +#ifndef _XRT_CLKFREQ_H_
>>> +#define _XRT_CLKFREQ_H_
>>> +
>>> +#include "xleaf.h"
>>> +
>>> +/*
>>> + * CLKFREQ driver leaf calls.
>>> + */
>>> +enum xrt_clkfreq_leaf_cmd {
>>> + XRT_CLKFREQ_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
>>> +};
>>> +
>>> +#endif /* _XRT_CLKFREQ_H_ */
>>> diff --git a/drivers/fpga/xrt/include/xleaf/clock.h b/drivers/fpga/xrt/include/xleaf/clock.h
>>> new file mode 100644
>>> index 000000000000..1379e24fa5d0
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/include/xleaf/clock.h
>>> @@ -0,0 +1,29 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Lizhi Hou <[email protected]>
>>> + */
>>> +
>>> +#ifndef _XRT_CLOCK_H_
>>> +#define _XRT_CLOCK_H_
>>> +
>>> +#include "xleaf.h"
>>> +#include <linux/fpga_xrt.h>
>>> +
>>> +/*
>>> + * CLOCK driver leaf calls.
>>> + */
>>> +enum xrt_clock_leaf_cmd {
>>> + XRT_CLOCK_SET = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
>>> + XRT_CLOCK_GET,
>>> + XRT_CLOCK_VERIFY,
>>> +};
>>> +
>>> +struct xrt_clock_get {
>>> + u16 freq;
>>> + u32 freq_cnter;
>>> +};
>>> +
>>> +#endif /* _XRT_CLOCK_H_ */
>>> diff --git a/drivers/fpga/xrt/include/xleaf/ddr_calibration.h b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>> new file mode 100644
>>> index 000000000000..c44ae30f939a
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>> @@ -0,0 +1,28 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Cheng Zhen <[email protected]>
>>> + */
>>> +
>>> +#ifndef _XRT_DDR_CALIBRATION_H_
>>> +#define _XRT_DDR_CALIBRATION_H_
>>> +
>>> +#include "xleaf.h"
>>> +#include <linux/fpga-xrt.h>
>>> +
>>> +/*
>>> + * Memory calibration driver leaf calls.
>>> + */
>>> +enum xrt_calib_results {
>>> + XRT_CALIB_UNKNOWN = 0,
>>> + XRT_CALIB_SUCCEEDED,
>>> + XRT_CALIB_FAILED,
>>> +};
>>> +
>>> +enum xrt_calib_leaf_cmd {
>>> + XRT_CALIB_RESULT = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
>>> +};
>>> +
>>> +#endif /* _XRT_DDR_CALIBRATION_H_ */
>>> diff --git a/drivers/fpga/xrt/include/xleaf/devctl.h b/drivers/fpga/xrt/include/xleaf/devctl.h
>>> new file mode 100644
>>> index 000000000000..b97f3b6d9326
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/include/xleaf/devctl.h
>>> @@ -0,0 +1,40 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Lizhi Hou <[email protected]>
>>> + */
>>> +
>>> +#ifndef _XRT_DEVCTL_H_
>>> +#define _XRT_DEVCTL_H_
>>> +
>>> +#include "xleaf.h"
>>> +
>>> +/*
>>> + * DEVCTL driver leaf calls.
>>> + */
>>> +enum xrt_devctl_leaf_cmd {
>>> + XRT_DEVCTL_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
>>> +};
>>> +
>>> +enum xrt_devctl_id {
>>> + XRT_DEVCTL_ROM_UUID = 0,
>>> + XRT_DEVCTL_DDR_CALIB,
>>> + XRT_DEVCTL_GOLDEN_VER,
>>> + XRT_DEVCTL_MAX
>>> +};
>>> +
>>> +struct xrt_devctl_rw {
>>> + u32 xdr_id;
>>> + void *xdr_buf;
>>> + u32 xdr_len;
>>> + u32 xdr_offset;
>>> +};
>>> +
>>> +struct xrt_devctl_intf_uuid {
>>> + u32 uuid_num;
>>> + uuid_t *uuids;
>>> +};
>>> +
>>> +#endif /* _XRT_DEVCTL_H_ */
>>> diff --git a/drivers/fpga/xrt/lib/lib-drv.c b/drivers/fpga/xrt/lib/lib-drv.c
>>> new file mode 100644
>>> index 000000000000..7d21ef565651
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/lib/lib-drv.c
>>> @@ -0,0 +1,318 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Cheng Zhen <[email protected]>
>>> + * Lizhi Hou <[email protected]>
>>> + */
>>> +
>>> +#include <linux/module.h>
>>> +#include <linux/vmalloc.h>
>>> +#include <linux/slab.h>
>>> +#include "xleaf.h"
>>> +#include "xroot.h"
>>> +#include "lib-drv.h"
>>> +
>>> +#define XRT_IPLIB_MODULE_NAME "xrt-lib"
>> ok
>>> +#define XRT_DRVNAME(drv) ((drv)->driver.name)
>>> +
>>> +#define XRT_SUBDEV_ID_SHIFT 16
>>> +#define XRT_SUBDEV_ID_MASK ((1 << XRT_SUBDEV_ID_SHIFT) - 1)
>>> +
>>> +struct xrt_find_drv_data {
>>> + enum xrt_subdev_id id;
>>> + struct xrt_driver *xdrv;
>>> +};
>>> +
>>> +struct class *xrt_class;
>>> +static DEFINE_IDA(xrt_device_ida);
>>> +
>>> +static inline u32 xrt_instance_to_id(enum xrt_subdev_id id, u32 instance)
>>> +{
>>> + return (id << XRT_SUBDEV_ID_SHIFT) | instance;
>>> +}
>>> +
>>> +static inline u32 xrt_id_to_instance(u32 id)
>>> +{
>>> + return id & XRT_SUBDEV_ID_MASK;
>> ok
>>> +}
>>> +
>>> +static int xrt_bus_match(struct device *dev, struct device_driver *drv)
>>> +{
>>> + struct xrt_device *xdev = to_xrt_dev(dev);
>>> + struct xrt_driver *xdrv = to_xrt_drv(drv);
>>> +
>>> + if (xdev->subdev_id == xdrv->subdev_id)
>>> + return 1;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int xrt_bus_probe(struct device *dev)
>>> +{
>>> + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
>>> + struct xrt_device *xdev = to_xrt_dev(dev);
>>> +
>>> + return xdrv->probe(xdev);
>>> +}
>>> +
>>> +static int xrt_bus_remove(struct device *dev)
>>> +{
>>> + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
>>> + struct xrt_device *xdev = to_xrt_dev(dev);
>>> +
>>> + if (xdrv->remove)
>>> + xdrv->remove(xdev);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +struct bus_type xrt_bus_type = {
>>> + .name = "xrt",
>>> + .match = xrt_bus_match,
>>> + .probe = xrt_bus_probe,
>>> + .remove = xrt_bus_remove,
>>> +};
>>> +
>>> +int xrt_register_driver(struct xrt_driver *drv)
>>> +{
>>> + const char *drvname = XRT_DRVNAME(drv);
>>> + int rc = 0;
>>> +
>>> + /* Initialize dev_t for char dev node. */
>>> + if (drv->file_ops.xsf_ops.open) {
>>> + rc = alloc_chrdev_region(&drv->file_ops.xsf_dev_t, 0,
>>> + XRT_MAX_DEVICE_NODES, drvname);
>>> + if (rc) {
>>> + pr_err("failed to alloc dev minor for %s: %d\n", drvname, rc);
>>> + return rc;
>>> + }
>>> + } else {
>>> + drv->file_ops.xsf_dev_t = (dev_t)-1;
>>> + }
>>> +
>>> + drv->driver.owner = THIS_MODULE;
>>> + drv->driver.bus = &xrt_bus_type;
>>> +
>>> + rc = driver_register(&drv->driver);
>>> + if (rc) {
>>> + pr_err("register %s xrt driver failed\n", drvname);
>>> + if (drv->file_ops.xsf_dev_t != (dev_t)-1) {
>>> + unregister_chrdev_region(drv->file_ops.xsf_dev_t,
>>> + XRT_MAX_DEVICE_NODES);
>>> + }
>>> + return rc;
>>> + }
>>> +
>>> + pr_info("%s registered successfully\n", drvname);
>>> +
>>> + return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(xrt_register_driver);
>>> +
>>> +void xrt_unregister_driver(struct xrt_driver *drv)
>>> +{
>>> + driver_unregister(&drv->driver);
>>> +
>>> + if (drv->file_ops.xsf_dev_t != (dev_t)-1)
>>> + unregister_chrdev_region(drv->file_ops.xsf_dev_t, XRT_MAX_DEVICE_NODES);
>> ok
>>> +}
>>> +EXPORT_SYMBOL_GPL(xrt_unregister_driver);
>>> +
>>> +static int __find_driver(struct device_driver *drv, void *_data)
>>> +{
>>> + struct xrt_driver *xdrv = to_xrt_drv(drv);
>>> + struct xrt_find_drv_data *data = _data;
>>> +
>>> + if (xdrv->subdev_id == data->id) {
>>> + data->xdrv = xdrv;
>>> + return 1;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +const char *xrt_drv_name(enum xrt_subdev_id id)
>>> +{
>>> + struct xrt_find_drv_data data = { 0 };
>>> +
>>> + data.id = id;
>>> + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
>>> +
>>> + if (data.xdrv)
>>> + return XRT_DRVNAME(data.xdrv);
>>> +
>>> + return NULL;
>>> +}
>>> +
>>> +static int xrt_drv_get_instance(enum xrt_subdev_id id)
>>> +{
>>> + int ret;
>>> +
>>> + ret = ida_alloc_range(&xrt_device_ida, xrt_instance_to_id(id, 0),
>>> + xrt_instance_to_id(id, XRT_MAX_DEVICE_NODES),
>>> + GFP_KERNEL);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + return xrt_id_to_instance((u32)ret);
>>> +}
>>> +
>>> +static void xrt_drv_put_instance(enum xrt_subdev_id id, int instance)
>>> +{
>>> + ida_free(&xrt_device_ida, xrt_instance_to_id(id, instance));
>>> +}
>>> +
>>> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id id)
>>> +{
>>> + struct xrt_find_drv_data data = { 0 };
>>> +
>>> + data.id = id;
>>> + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
>>> +
>>> + if (data.xdrv)
>>> + return data.xdrv->endpoints;
>>> +
>>> + return NULL;
>>> +}
>>> +
>>> +static void xrt_device_release(struct device *dev)
>>> +{
>>> + struct xrt_device *xdev = container_of(dev, struct xrt_device, dev);
>>> +
>>> + kfree(xdev);
>>> +}
>>> +
>>> +void xrt_device_unregister(struct xrt_device *xdev)
>>> +{
>>> + if (xdev->state == XRT_DEVICE_STATE_ADDED)
>>> + device_del(&xdev->dev);
>>> +
>>> + vfree(xdev->sdev_data);
>> cleanup of sdev_data here
>>
>> fail: label in xrt_device_register is ok.
>>
>>> + kfree(xdev->resource);
>>> +
>>> + if (xdev->instance != XRT_INVALID_DEVICE_INST)
>>> + xrt_drv_put_instance(xdev->subdev_id, xdev->instance);
>>> +
>>> + if (xdev->dev.release == xrt_device_release)
>>> + put_device(&xdev->dev);
>>> +}
>>> +
>>> +struct xrt_device *
>>> +xrt_device_register(struct device *parent, u32 id,
>>> + struct resource *res, u32 res_num,
>>> + void *pdata, size_t data_sz)
>>> +{
>>> + struct xrt_device *xdev = NULL;
>>> + int ret;
>>> +
>>> + xdev = kzalloc(sizeof(*xdev), GFP_KERNEL);
>>> + if (!xdev)
>>> + return xdev;
>> ok
>>> + xdev->instance = XRT_INVALID_DEVICE_INST;
>>> +
>>> + /* Obtain dev instance number. */
>>> + ret = xrt_drv_get_instance(id);
>>> + if (ret < 0) {
>>> + dev_err(parent, "failed get instance, ret %d", ret);
>>> + goto fail;
>>> + }
>>> +
>>> + xdev->instance = ret;
>>> + xdev->name = xrt_drv_name(id);
>>> + xdev->subdev_id = id;
>>> + device_initialize(&xdev->dev);
>>> + xdev->dev.release = xrt_device_release;
>>> + xdev->dev.parent = parent;
>>> +
>>> + xdev->dev.bus = &xrt_bus_type;
>>> + dev_set_name(&xdev->dev, "%s.%d", xdev->name, xdev->instance);
>>> +
>>> + xdev->num_resources = res_num;
>>> + xdev->resource = kmemdup(res, sizeof(*res) * res_num, GFP_KERNEL);
>>> + if (!xdev->resource)
>>> + goto fail;
>>> +
>>> + xdev->sdev_data = vzalloc(data_sz);
>>> + if (!xdev->sdev_data)
>>> + goto fail;
>>> +
>>> + memcpy(xdev->sdev_data, pdata, data_sz);
>>> +
>>> + ret = device_add(&xdev->dev);
>>> + if (ret) {
>>> + dev_err(parent, "failed add device, ret %d", ret);
>>> + goto fail;
>>> + }
>>> + xdev->state = XRT_DEVICE_STATE_ADDED;
>>> +
>>> + return xdev;
>>> +
>>> +fail:
>>> + xrt_device_unregister(xdev);
>>> + kfree(xdev);
>>> +
>>> + return NULL;
>>> +}
>>> +
>>> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32 type, u32 num)
>>> +{
>>> + u32 i;
>>> +
>>> + for (i = 0; i < xdev->num_resources; i++) {
>>> + struct resource *r = &xdev->resource[i];
>>> +
>>> + if (type == resource_type(r) && num-- == 0)
>>> + return r;
>>> + }
>>> + return NULL;
>>> +}
>>> +
>>> +/*
>>> + * Leaf driver's module init/fini callbacks. This is not a open infrastructure for dynamic
>>> + * plugging in drivers. All drivers should be statically added.
>>> + */
>>> +static void (*leaf_init_fini_cbs[])(bool) = {
>>> + group_leaf_init_fini,
>>> + axigate_leaf_init_fini,
>>> + icap_leaf_init_fini,
>>> +};
>>> +
>>> +static __init int xrt_lib_init(void)
>>> +{
>>> + int ret;
>>> + int i;
>>> +
>>> + ret = bus_register(&xrt_bus_type);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + xrt_class = class_create(THIS_MODULE, XRT_IPLIB_MODULE_NAME);
>>> + if (IS_ERR(xrt_class)) {
>>> + bus_unregister(&xrt_bus_type);
>>> + return PTR_ERR(xrt_class);
>>> + }
>>> +
>>> + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
>>> + leaf_init_fini_cbs[i](true);
>>> + return 0;
>>> +}
>>> +
>>> +static __exit void xrt_lib_fini(void)
>>> +{
>>> + int i;
>>> +
>>> + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
>>> + leaf_init_fini_cbs[i](false);
>>> +
>>> + class_destroy(xrt_class);
>>> + bus_unregister(&xrt_bus_type);
>>> +}
>>> +
>>> +module_init(xrt_lib_init);
>>> +module_exit(xrt_lib_fini);
>>> +
>>> +MODULE_AUTHOR("XRT Team <[email protected]>");
>>> +MODULE_DESCRIPTION("Xilinx Alveo IP Lib driver");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/fpga/xrt/lib/lib-drv.h b/drivers/fpga/xrt/lib/lib-drv.h
>>> new file mode 100644
>>> index 000000000000..0276c28e009f
>>> --- /dev/null
>>> +++ b/drivers/fpga/xrt/lib/lib-drv.h
>>> @@ -0,0 +1,21 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>> + *
>>> + * Authors:
>>> + * Cheng Zhen <[email protected]>
>>> + */
>>> +
>>> +#ifndef _LIB_DRV_H_
>>> +#define _LIB_DRV_H_
>>> +
>>> +#include <linux/device/class.h>
>>> +#include <linux/device/bus.h>
>>> +
>>> +extern struct class *xrt_class;
>>> +extern struct bus_type xrt_bus_type;
>>> +
>>> +const char *xrt_drv_name(enum xrt_subdev_id id);
>>> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id id);
>>> +
>>> +#endif /* _LIB_DRV_H_ */
On 9/10/21 2:34 PM, Lizhi Hou wrote:
> Hi Moritz,
>
> (sending it again because rejected by some mail alias)
>
>
> Did you get time to review XRT patch set? Please let us know if there
> are more we need to change.
> We are looking for your comments.
This patchset has been outstanding for 6 weeks to address Moritz'
comments for the v8 patchset of this single patch 4 of 14.
The changes look good to me.
https://lore.kernel.org/linux-fpga/[email protected]/
Moritz, can you review the changes ?
The xrt patchset starts to enable a whole class of general purpose
datacenter fpga's.
https://www.xilinx.com/products/boards-and-kits/alveo.html
This would be a great feature for the fpga/ subsystem.
Tom
>
> Thanks,
> Lizhi
>
> On 8/21/21 9:44 AM, Moritz Fischer wrote:
>> CAUTION: This message has originated from an External Source. Please
>> use proper judgment and caution when opening attachments, clicking
>> links, or responding to this email.
>>
>>
>> On Thu, Aug 19, 2021 at 05:14:14AM -0700, Tom Rix wrote:
>>> On 8/2/21 9:05 AM, Lizhi Hou wrote:
>>>
>>>> xrt-lib kernel module infrastructure code to register and manage all
>>>> leaf driver modules.
>>>>
>>>> Signed-off-by: Sonal Santan <[email protected]>
>>>> Signed-off-by: Max Zhen <[email protected]>
>>>> Signed-off-by: Lizhi Hou <[email protected]>
>>>> Reviewed-by: Tom Rix <[email protected]>
>>> This was the only patch with requested changes in v8.
>>>
>>> All the changes from v8 have been made.
>>>
>>> They are itemized below with variations on 'ok'
>>>
>>> It is still Moritz's call to accept them.
>> I'll be OOO till 9/4/21. I'll get to it after.
>>
>> - Moritz
>>> Tom
>>>
>>>> ---
>>>>   drivers/fpga/xrt/include/subdev_id.h         | 39 +++
>>>>   drivers/fpga/xrt/include/xdevice.h           | 141 ++++++++
>>>>   drivers/fpga/xrt/include/xleaf.h             | 205 +++++++++++
>>>>   drivers/fpga/xrt/include/xleaf/clkfreq.h     | 21 ++
>>>>   drivers/fpga/xrt/include/xleaf/clock.h       | 29 ++
>>>>   .../fpga/xrt/include/xleaf/ddr_calibration.h | 28 ++
>>>>   drivers/fpga/xrt/include/xleaf/devctl.h      | 40 +++
>>>>   drivers/fpga/xrt/lib/lib-drv.c               | 318
>>>> ++++++++++++++++++
>>>>   drivers/fpga/xrt/lib/lib-drv.h               | 21 ++
>>>> Â Â 9 files changed, 842 insertions(+)
>>>> Â Â create mode 100644 drivers/fpga/xrt/include/subdev_id.h
>>>> Â Â create mode 100644 drivers/fpga/xrt/include/xdevice.h
>>>> Â Â create mode 100644 drivers/fpga/xrt/include/xleaf.h
>>>> Â Â create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
>>>> Â Â create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
>>>> Â Â create mode 100644 drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>>> Â Â create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
>>>> Â Â create mode 100644 drivers/fpga/xrt/lib/lib-drv.c
>>>> Â Â create mode 100644 drivers/fpga/xrt/lib/lib-drv.h
>>>>
>>>> diff --git a/drivers/fpga/xrt/include/subdev_id.h
>>>> b/drivers/fpga/xrt/include/subdev_id.h
>>>> new file mode 100644
>>>> index 000000000000..02df4b939a1b
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/subdev_id.h
>>>> @@ -0,0 +1,39 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Cheng Zhen <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_SUBDEV_ID_H_
>>>> +#define _XRT_SUBDEV_ID_H_
>>>> +
>>>> +/*
>>>> + * Every subdev driver has an ID for others to refer to it. There
>>>> can be multiple number of
>>>> + * instances of a subdev driver. A <subdev_id, subdev_instance>
>>>> tuple is a unique identification
>>>> + * of a specific instance of a subdev driver.
>>>> + */
>>>> +enum xrt_subdev_id {
>>>> +Â Â XRT_SUBDEV_INVALID = 0,
>>>> +Â Â XRT_SUBDEV_GRP,
>>>> +Â Â XRT_SUBDEV_VSEC,
>>>> +Â Â XRT_SUBDEV_VSEC_GOLDEN,
>>>> +Â Â XRT_SUBDEV_DEVCTL,
>>>> +Â Â XRT_SUBDEV_AXIGATE,
>>>> +Â Â XRT_SUBDEV_ICAP,
>>>> +Â Â XRT_SUBDEV_TEST,
>>>> +Â Â XRT_SUBDEV_MGMT_MAIN,
>>>> +Â Â XRT_SUBDEV_QSPI,
>>>> +Â Â XRT_SUBDEV_MAILBOX,
>>>> +Â Â XRT_SUBDEV_CMC,
>>>> +Â Â XRT_SUBDEV_CALIB,
>>>> +Â Â XRT_SUBDEV_CLKFREQ,
>>>> +Â Â XRT_SUBDEV_CLOCK,
>>>> +Â Â XRT_SUBDEV_SRSR,
>>>> +Â Â XRT_SUBDEV_UCS,
>>>> +Â Â XRT_SUBDEV_NUM, /* Total number of subdevs. */
>>>> +Â Â XRT_ROOT = -1, /* Special ID for root driver. */
>>>> +};
>>>> +
>>>> +#endif    /* _XRT_SUBDEV_ID_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xdevice.h
>>>> b/drivers/fpga/xrt/include/xdevice.h
>>>> new file mode 100644
>>>> index 000000000000..b40ebe98b54d
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xdevice.h
>>>> @@ -0,0 +1,141 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + *Â Â Â Lizhi Hou <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_DEVICE_H_
>>>> +#define _XRT_DEVICE_H_
>>>> +
>>>> +#include <linux/fs.h>
>>>> +#include <linux/cdev.h>
>>>> +
>>>> +#define XRT_MAX_DEVICE_NODESÂ Â Â Â Â Â Â Â Â Â Â Â Â Â 128
>>>> +#define XRT_INVALID_DEVICE_INST (XRT_MAX_DEVICE_NODES + 1)
>>>> +
>>>> +enum {
>>>> +Â Â XRT_DEVICE_STATE_NONE = 0,
>>>> +Â Â XRT_DEVICE_STATE_ADDED
>>>> +};
>>>> +
>>>> +/*
>>>> + * struct xrt_device - represent an xrt device on xrt bus
>>>> + *
>>>> + * dev: generic device interface.
>>>> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
>>> ok
>>>> + * name: name of the xrt device.
>>>> + * instance: instance of the xrt device. The xrt device with same
>>>> id can have
>>>> + *Â Â Â Â Â Â Â Â Â Â more than 1 instances.
>>>> + * state: current state of the xrt device.
>>>> + * num_resources: The total number of resource for the xrt device.
>>>> + * resource: point to the xrt device resource array.
>>>> + * sdev_data: private data pointer.
>>>> + */
>>>> +struct xrt_device {
>>>> +Â Â struct device dev;
>>>> +Â Â u32 subdev_id;
>>>> +Â Â const char *name;
>>>> +Â Â u32 instance;
>>>> +Â Â u32 state;
>>>> +Â Â u32 num_resources;
>>>> +Â Â struct resource *resource;
>>>> +Â Â void *sdev_data;
>>>> +};
>>>> +
>>>> +/*
>>>> + * If populated by xrt device driver, infra will handle the
>>>> mechanics of
>>>> + * char device (un)registration.
>>>> + */
>>>> +enum xrt_dev_file_mode {
>>>> +Â Â /* Infra create cdev, default file name */
>>>> +Â Â XRT_DEV_FILE_DEFAULT = 0,
>>>> +Â Â /* Infra create cdev, need to encode inst num in file name */
>>>> +Â Â XRT_DEV_FILE_MULTI_INST,
>>>> +Â Â /* No auto creation of cdev by infra, leaf handles it by itself */
>>>> +Â Â XRT_DEV_FILE_NO_AUTO,
>>>> +};
>>>> +
>>>> +struct xrt_dev_file_ops {
>>>> +Â Â const struct file_operations xsf_ops;
>>>> +Â Â dev_t xsf_dev_t;
>>>> +Â Â const char *xsf_dev_name;
>>>> +Â Â enum xrt_dev_file_mode xsf_mode;
>>>> +};
>>>> +
>>>> +/*
>>>> + * this struct define the endpoints belong to the same xrt device
>>>> + * ep_name: endpoint name
>>>> + * compat: compatible string
>>> ok
>>>> + */
>>>> +struct xrt_dev_ep_names {
>>>> +Â Â const char *ep_name;
>>>> +Â Â const char *compat;
>>>> +};
>>>> +
>>>> +struct xrt_dev_endpoints {
>>>> +Â Â struct xrt_dev_ep_names *xse_names;
>>>> +Â Â /* minimum number of endpoints to support the subdevice */
>>>> +Â Â u32 xse_min_ep;
>>>> +};
>>>> +
>>>> +/*
>>>> + * struct xrt_driver - represent a xrt device driver
>>>> + *
>>>> + * driver: driver model structure.
>>>> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
>>>> + * file_ops: character device name and callbacks.
>>>> + * probe: mandatory callback for device binding.
>>>> + * remove: callback for device unbinding.
>>>> + * leaf_call: callback for servicing other leaf drivers.
>>> ok
>>>> + */
>>>> +struct xrt_driver {
>>>> +Â Â struct device_driver driver;
>>>> +Â Â u32 subdev_id;
>>>> +Â Â struct xrt_dev_file_ops file_ops;
>>>> +Â Â struct xrt_dev_endpoints *endpoints;
>>>> +
>>>> +Â Â /*
>>>> +Â Â Â * Subdev driver callbacks populated by subdev driver.
>>>> +Â Â Â */
>>>> +Â Â int (*probe)(struct xrt_device *xrt_dev);
>>>> +Â Â void (*remove)(struct xrt_device *xrt_dev);
>>>> +Â Â /*
>>>> +Â Â Â * If leaf_call is defined, these are called by other leaf
>>>> drivers.
>>>> +Â Â Â * Note that root driver may call into leaf_call of a group
>>>> driver.
>>>> +Â Â Â */
>>>> +Â Â int (*leaf_call)(struct xrt_device *xrt_dev, u32 cmd, void *arg);
>>>> +};
>>>> +
>>>> +#define to_xrt_dev(d) container_of(d, struct xrt_device, dev)
>>>> +#define to_xrt_drv(d) container_of(d, struct xrt_driver, driver)
>>>> +
>>>> +static inline void *xrt_get_drvdata(const struct xrt_device *xdev)
>>>> +{
>>>> +Â Â return dev_get_drvdata(&xdev->dev);
>>>> +}
>>>> +
>>>> +static inline void xrt_set_drvdata(struct xrt_device *xdev, void
>>>> *data)
>>>> +{
>>>> +Â Â dev_set_drvdata(&xdev->dev, data);
>>>> +}
>>>> +
>>>> +static inline void *xrt_get_xdev_data(struct device *dev)
>>>> +{
>>>> +Â Â struct xrt_device *xdev = to_xrt_dev(dev);
>>>> +
>>>> +Â Â return xdev->sdev_data;
>>>> +}
>>>> +
>>>> +struct xrt_device *
>>>> +xrt_device_register(struct device *parent, u32 id,
>>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct resource *res, u32 res_num,
>>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â void *pdata, size_t data_sz);
>>>> +void xrt_device_unregister(struct xrt_device *xdev);
>>>> +int xrt_register_driver(struct xrt_driver *drv);
>>>> +void xrt_unregister_driver(struct xrt_driver *drv);
>>>> +void *xrt_get_xdev_data(struct device *dev);
>>>> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32
>>>> type, u32 num);
>>>> +
>>>> +#endif /* _XRT_DEVICE_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xleaf.h
>>>> b/drivers/fpga/xrt/include/xleaf.h
>>>> new file mode 100644
>>>> index 000000000000..f065fc766e0f
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xleaf.h
>>>> @@ -0,0 +1,205 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + *Â Â Â Cheng Zhen <[email protected]>
>>>> + *Â Â Â Sonal Santan <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_XLEAF_H_
>>>> +#define _XRT_XLEAF_H_
>>>> +
>>>> +#include <linux/mod_devicetable.h>
>>>> +#include "xdevice.h"
>>>> +#include "subdev_id.h"
>>>> +#include "xroot.h"
>>>> +#include "events.h"
>>>> +
>>>> +/* All subdev drivers should use below common routines to print
>>>> out msg. */
>>>> +#define DEV(xdev)Â (&(xdev)->dev)
>>>> +#define DEV_PDATA(xdev) \
>>>> +Â Â ((struct xrt_subdev_platdata *)xrt_get_xdev_data(DEV(xdev)))
>>>> +#define DEV_FILE_OPS(xdev)Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â \
>>>> +Â Â (&(to_xrt_drv((xdev)->dev.driver))->file_ops)
>>>> +#define FMT_PRT(prt_fn, xdev, fmt, args...) \
>>>> +Â Â ({typeof(xdev) (_xdev) = (xdev);Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â \
>>>> +  prt_fn(DEV(_xdev), "%s %s: " fmt,              \
>>>> +Â Â DEV_PDATA(_xdev)->xsp_root_name, __func__, ##args); })
>>>> +#define xrt_err(xdev, fmt, args...) FMT_PRT(dev_err, xdev, fmt,
>>>> ##args)
>>>> +#define xrt_warn(xdev, fmt, args...) FMT_PRT(dev_warn, xdev, fmt,
>>>> ##args)
>>>> +#define xrt_info(xdev, fmt, args...) FMT_PRT(dev_info, xdev, fmt,
>>>> ##args)
>>>> +#define xrt_dbg(xdev, fmt, args...) FMT_PRT(dev_dbg, xdev, fmt,
>>>> ##args)
>>>> +
>>>> +#define XRT_DEFINE_REGMAP_CONFIG(config_name) \
>>>> +Â Â static const struct regmap_config config_name = {Â Â Â Â Â Â Â Â Â Â Â Â Â Â \
>>>> +          .reg_bits = 32,                                        \
>>>> +          .val_bits = 32,                                        \
>>>> +          .reg_stride = 4,                                       \
>>>> +          .max_register = 0x1000,                                \
>>>> +Â Â }
>>>> +
>>>> +enum {
>>>> +Â Â /* Starting cmd for common leaf cmd implemented by all leaves. */
>>>> +Â Â XRT_XLEAF_COMMON_BASE = 0,
>>>> +Â Â /* Starting cmd for leaves' specific leaf cmds. */
>>>> +Â Â XRT_XLEAF_CUSTOM_BASE = 64,
>>>> +};
>>>> +
>>>> +enum xrt_xleaf_common_leaf_cmd {
>>>> +Â Â XRT_XLEAF_EVENT = XRT_XLEAF_COMMON_BASE,
>>>> +};
>>>> +
>>>> +/*
>>>> + * Partially initialized by the parent driver, then, passed in as
>>>> subdev driver's
>>>> + * platform data when creating subdev driver instance by calling
>>>> platform
>>>> + * device register API (xrt_device_register_data() or the likes).
>>>> + *
>>>> + * Once device register API returns, platform driver framework
>>>> makes a copy of
>>>> + * this buffer and maintains its life cycle. The content of the
>>>> buffer is
>>>> + * completely owned by subdev driver.
>>>> + *
>>>> + * Thus, parent driver should be very careful when it touches this
>>>> buffer
>>>> + * again once it's handed over to subdev driver. And the data
>>>> structure
>>>> + * should not contain pointers pointing to buffers that is managed by
>>>> + * other or parent drivers since it could have been freed before
>>>> platform
>>>> + * data buffer is freed by platform driver framework.
>>>> + */
>>>> +struct xrt_subdev_platdata {
>>>> +Â Â /*
>>>> +Â Â Â * Per driver instance callback. The xdev points to the instance.
>>>> +Â Â Â * Should always be defined for subdev driver to get service
>>>> from root.
>>>> +Â Â Â */
>>>> +Â Â xrt_subdev_root_cb_t xsp_root_cb;
>>>> +Â Â void *xsp_root_cb_arg;
>>>> +
>>>> +Â Â /* Something to associate w/ root for msg printing. */
>>>> +Â Â const char *xsp_root_name;
>>>> +
>>>> +Â Â /*
>>>> +Â Â Â * Char dev support for this subdev instance.
>>>> +Â Â Â * Initialized by subdev driver.
>>>> +Â Â Â */
>>>> +Â Â struct cdev xsp_cdev;
>>>> +Â Â struct device *xsp_sysdev;
>>>> +Â Â struct mutex xsp_devnode_lock; /* devnode lock */
>>>> +Â Â struct completion xsp_devnode_comp;
>>>> +Â Â int xsp_devnode_ref;
>>>> +Â Â bool xsp_devnode_online;
>>>> +Â Â bool xsp_devnode_excl;
>>>> +
>>>> +Â Â /*
>>>> +Â Â Â * Subdev driver specific init data. The buffer should be embedded
>>>> +Â Â Â * in this data structure buffer after dtb, so that it can be
>>>> freed
>>>> +Â Â Â * together with platform data.
>>>> +Â Â Â */
>>>> +Â Â loff_t xsp_priv_off; /* Offset into this platform data buffer. */
>>>> +Â Â size_t xsp_priv_len;
>>>> +
>>>> +Â Â /*
>>>> +Â Â Â * Populated by parent driver to describe the device tree for
>>>> +Â Â Â * the subdev driver to handle. Should always be last one since
>>>> it's
>>>> +Â Â Â * of variable length.
>>>> +Â Â Â */
>>>> +Â Â bool xsp_dtb_valid;
>>>> +Â Â char xsp_dtb[0];
>>>> +};
>>>> +
>>>> +struct subdev_match_arg {
>>>> +Â Â enum xrt_subdev_id id;
>>>> +Â Â int instance;
>>>> +};
>>>> +
>>>> +bool xleaf_has_endpoint(struct xrt_device *xdev, const char
>>>> *endpoint_name);
>>>> +struct xrt_device *xleaf_get_leaf(struct xrt_device *xdev,
>>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â xrt_subdev_match_t cb, void *arg);
>>>> +
>>>> +static inline bool subdev_match(enum xrt_subdev_id id, struct
>>>> xrt_device *xdev, void *arg)
>>>> +{
>>>> +Â Â const struct subdev_match_arg *a = (struct subdev_match_arg *)arg;
>>>> +Â Â int instance = a->instance;
>>>> +
>>>> +Â Â if (id != a->id)
>>>> +Â Â Â Â Â Â Â Â Â Â return false;
>>>> +Â Â if (instance != xdev->instance && instance !=
>>>> XRT_INVALID_DEVICE_INST)
>>>> +Â Â Â Â Â Â Â Â Â Â return false;
>>>> +Â Â return true;
>>>> +}
>>>> +
>>>> +static inline bool xrt_subdev_match_epname(enum xrt_subdev_id id,
>>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct xrt_device *xdev,
>>>> void *arg)
>>>> +{
>>>> +Â Â return xleaf_has_endpoint(xdev, arg);
>>>> +}
>>>> +
>>>> +static inline struct xrt_device *
>>>> +xleaf_get_leaf_by_id(struct xrt_device *xdev,
>>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â enum xrt_subdev_id id, int instance)
>>>> +{
>>>> +Â Â struct subdev_match_arg arg = { id, instance };
>>>> +
>>>> +Â Â return xleaf_get_leaf(xdev, subdev_match, &arg);
>>>> +}
>>>> +
>>>> +static inline struct xrt_device *
>>>> +xleaf_get_leaf_by_epname(struct xrt_device *xdev, const char *name)
>>>> +{
>>>> +Â Â return xleaf_get_leaf(xdev, xrt_subdev_match_epname, (void
>>>> *)name);
>>>> +}
>>>> +
>>>> +static inline int xleaf_call(struct xrt_device *tgt, u32 cmd, void
>>>> *arg)
>>>> +{
>>>> +Â Â return (to_xrt_drv(tgt->dev.driver)->leaf_call)(tgt, cmd, arg);
>>>> +}
>>>> +
>>>> +int xleaf_broadcast_event(struct xrt_device *xdev, enum xrt_events
>>>> evt, bool async);
>>>> +int xleaf_create_group(struct xrt_device *xdev, char *dtb);
>>>> +int xleaf_destroy_group(struct xrt_device *xdev, int instance);
>>>> +void xleaf_get_root_res(struct xrt_device *xdev, u32 region_id,
>>>> struct resource **res);
>>>> +void xleaf_get_root_id(struct xrt_device *xdev, unsigned short
>>>> *vendor, unsigned short *device,
>>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â unsigned short *subvendor, unsigned short
>>>> *subdevice);
>>>> +void xleaf_hot_reset(struct xrt_device *xdev);
>>>> +int xleaf_put_leaf(struct xrt_device *xdev, struct xrt_device *leaf);
>>>> +struct device *xleaf_register_hwmon(struct xrt_device *xdev, const
>>>> char *name, void *drvdata,
>>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â const struct attribute_group **grps);
>>>> +void xleaf_unregister_hwmon(struct xrt_device *xdev, struct device
>>>> *hwmon);
>>>> +int xleaf_wait_for_group_bringup(struct xrt_device *xdev);
>>>> +
>>>> +/*
>>>> + * Character device helper APIs for use by leaf drivers
>>>> + */
>>>> +static inline bool xleaf_devnode_enabled(struct xrt_device *xdev)
>>>> +{
>>>> +Â Â return DEV_FILE_OPS(xdev)->xsf_ops.open;
>>>> +}
>>>> +
>>>> +int xleaf_devnode_create(struct xrt_device *xdev,
>>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â const char *file_name, const char *inst_name);
>>>> +void xleaf_devnode_destroy(struct xrt_device *xdev);
>>>> +
>>>> +struct xrt_device *xleaf_devnode_open_excl(struct inode *inode);
>>>> +struct xrt_device *xleaf_devnode_open(struct inode *inode);
>>>> +void xleaf_devnode_close(struct inode *inode);
>>>> +
>>>> +/* Module's init/fini routines for leaf driver in xrt-lib module */
>>>> +#define XRT_LEAF_INIT_FINI_FUNC(name) \
>>>> +void name##_leaf_init_fini(bool
>>>> init)Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â \
>>>> +{Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â \
>>>> +Â Â if (init) \
>>>> + xrt_register_driver(&xrt_##name##_driver); \
>>>> + else \
>>>> + xrt_unregister_driver(&xrt_##name##_driver); \
>>>> +}
>>>> +
>>>> +/* Module's init/fini routines for leaf driver in xrt-lib module */
>>>> +void group_leaf_init_fini(bool init);
>>>> +void vsec_leaf_init_fini(bool init);
>>>> +void devctl_leaf_init_fini(bool init);
>>>> +void axigate_leaf_init_fini(bool init);
>>>> +void icap_leaf_init_fini(bool init);
>>>> +void calib_leaf_init_fini(bool init);
>>>> +void clkfreq_leaf_init_fini(bool init);
>>>> +void clock_leaf_init_fini(bool init);
>>>> +void ucs_leaf_init_fini(bool init);
>>>> +
>>>> +#endif    /* _XRT_LEAF_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xleaf/clkfreq.h
>>>> b/drivers/fpga/xrt/include/xleaf/clkfreq.h
>>>> new file mode 100644
>>>> index 000000000000..005441d5df78
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xleaf/clkfreq.h
>>>> @@ -0,0 +1,21 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Lizhi Hou <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_CLKFREQ_H_
>>>> +#define _XRT_CLKFREQ_H_
>>>> +
>>>> +#include "xleaf.h"
>>>> +
>>>> +/*
>>>> + * CLKFREQ driver leaf calls.
>>>> + */
>>>> +enum xrt_clkfreq_leaf_cmd {
>>>> +Â Â XRT_CLKFREQ_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in
>>>> xleaf.h */
>>>> +};
>>>> +
>>>> +#endif    /* _XRT_CLKFREQ_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xleaf/clock.h
>>>> b/drivers/fpga/xrt/include/xleaf/clock.h
>>>> new file mode 100644
>>>> index 000000000000..1379e24fa5d0
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xleaf/clock.h
>>>> @@ -0,0 +1,29 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Lizhi Hou <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_CLOCK_H_
>>>> +#define _XRT_CLOCK_H_
>>>> +
>>>> +#include "xleaf.h"
>>>> +#include <linux/fpga_xrt.h>
>>>> +
>>>> +/*
>>>> + * CLOCK driver leaf calls.
>>>> + */
>>>> +enum xrt_clock_leaf_cmd {
>>>> +Â Â XRT_CLOCK_SET = XRT_XLEAF_CUSTOM_BASE, /* See comments in
>>>> xleaf.h */
>>>> +Â Â XRT_CLOCK_GET,
>>>> +Â Â XRT_CLOCK_VERIFY,
>>>> +};
>>>> +
>>>> +struct xrt_clock_get {
>>>> +Â Â u16 freq;
>>>> +Â Â u32 freq_cnter;
>>>> +};
>>>> +
>>>> +#endif    /* _XRT_CLOCK_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>>> b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>>> new file mode 100644
>>>> index 000000000000..c44ae30f939a
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>>> @@ -0,0 +1,28 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Cheng Zhen <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_DDR_CALIBRATION_H_
>>>> +#define _XRT_DDR_CALIBRATION_H_
>>>> +
>>>> +#include "xleaf.h"
>>>> +#include <linux/fpga-xrt.h>
>>>> +
>>>> +/*
>>>> + * Memory calibration driver leaf calls.
>>>> + */
>>>> +enum xrt_calib_results {
>>>> +Â Â XRT_CALIB_UNKNOWN = 0,
>>>> +Â Â XRT_CALIB_SUCCEEDED,
>>>> +Â Â XRT_CALIB_FAILED,
>>>> +};
>>>> +
>>>> +enum xrt_calib_leaf_cmd {
>>>> +Â Â XRT_CALIB_RESULT = XRT_XLEAF_CUSTOM_BASE, /* See comments in
>>>> xleaf.h */
>>>> +};
>>>> +
>>>> +#endif    /* _XRT_DDR_CALIBRATION_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xleaf/devctl.h
>>>> b/drivers/fpga/xrt/include/xleaf/devctl.h
>>>> new file mode 100644
>>>> index 000000000000..b97f3b6d9326
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xleaf/devctl.h
>>>> @@ -0,0 +1,40 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Lizhi Hou <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_DEVCTL_H_
>>>> +#define _XRT_DEVCTL_H_
>>>> +
>>>> +#include "xleaf.h"
>>>> +
>>>> +/*
>>>> + * DEVCTL driver leaf calls.
>>>> + */
>>>> +enum xrt_devctl_leaf_cmd {
>>>> +Â Â XRT_DEVCTL_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in
>>>> xleaf.h */
>>>> +};
>>>> +
>>>> +enum xrt_devctl_id {
>>>> +Â Â XRT_DEVCTL_ROM_UUID = 0,
>>>> +Â Â XRT_DEVCTL_DDR_CALIB,
>>>> +Â Â XRT_DEVCTL_GOLDEN_VER,
>>>> +Â Â XRT_DEVCTL_MAX
>>>> +};
>>>> +
>>>> +struct xrt_devctl_rw {
>>>> +Â Â u32Â Â Â Â xdr_id;
>>>> +  void   *xdr_buf;
>>>> +Â Â u32Â Â Â Â xdr_len;
>>>> +Â Â u32Â Â Â Â xdr_offset;
>>>> +};
>>>> +
>>>> +struct xrt_devctl_intf_uuid {
>>>> +Â Â u32Â Â Â Â uuid_num;
>>>> +  uuid_t *uuids;
>>>> +};
>>>> +
>>>> +#endif    /* _XRT_DEVCTL_H_ */
>>>> diff --git a/drivers/fpga/xrt/lib/lib-drv.c
>>>> b/drivers/fpga/xrt/lib/lib-drv.c
>>>> new file mode 100644
>>>> index 000000000000..7d21ef565651
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/lib/lib-drv.c
>>>> @@ -0,0 +1,318 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Cheng Zhen <[email protected]>
>>>> + * Lizhi Hou <[email protected]>
>>>> + */
>>>> +
>>>> +#include <linux/module.h>
>>>> +#include <linux/vmalloc.h>
>>>> +#include <linux/slab.h>
>>>> +#include "xleaf.h"
>>>> +#include "xroot.h"
>>>> +#include "lib-drv.h"
>>>> +
>>>> +#define XRT_IPLIB_MODULE_NAMEÂ Â Â Â Â Â Â Â Â Â Â Â Â "xrt-lib"
>>> ok
>>>> +#define XRT_DRVNAME(drv) ((drv)->driver.name)
>>>> +
>>>> +#define XRT_SUBDEV_ID_SHIFTÂ Â Â Â Â Â Â Â Â Â Â Â Â Â Â 16
>>>> +#define XRT_SUBDEV_ID_MASKÂ Â Â Â Â Â Â Â ((1 << XRT_SUBDEV_ID_SHIFT) - 1)
>>>> +
>>>> +struct xrt_find_drv_data {
>>>> +Â Â enum xrt_subdev_id id;
>>>> +Â Â struct xrt_driver *xdrv;
>>>> +};
>>>> +
>>>> +struct class *xrt_class;
>>>> +static DEFINE_IDA(xrt_device_ida);
>>>> +
>>>> +static inline u32 xrt_instance_to_id(enum xrt_subdev_id id, u32
>>>> instance)
>>>> +{
>>>> +Â Â return (id << XRT_SUBDEV_ID_SHIFT) | instance;
>>>> +}
>>>> +
>>>> +static inline u32 xrt_id_to_instance(u32 id)
>>>> +{
>>>> +Â Â return id & XRT_SUBDEV_ID_MASK;
>>> ok
>>>> +}
>>>> +
>>>> +static int xrt_bus_match(struct device *dev, struct device_driver
>>>> *drv)
>>>> +{
>>>> +Â Â struct xrt_device *xdev = to_xrt_dev(dev);
>>>> +Â Â struct xrt_driver *xdrv = to_xrt_drv(drv);
>>>> +
>>>> +Â Â if (xdev->subdev_id == xdrv->subdev_id)
>>>> +Â Â Â Â Â Â Â Â Â Â return 1;
>>>> +
>>>> +Â Â return 0;
>>>> +}
>>>> +
>>>> +static int xrt_bus_probe(struct device *dev)
>>>> +{
>>>> +Â Â struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
>>>> +Â Â struct xrt_device *xdev = to_xrt_dev(dev);
>>>> +
>>>> +Â Â return xdrv->probe(xdev);
>>>> +}
>>>> +
>>>> +static int xrt_bus_remove(struct device *dev)
>>>> +{
>>>> +Â Â struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
>>>> +Â Â struct xrt_device *xdev = to_xrt_dev(dev);
>>>> +
>>>> +Â Â if (xdrv->remove)
>>>> +Â Â Â Â Â Â Â Â Â Â xdrv->remove(xdev);
>>>> +
>>>> +Â Â return 0;
>>>> +}
>>>> +
>>>> +struct bus_type xrt_bus_type = {
>>>> +  .name          = "xrt",
>>>> +  .match         = xrt_bus_match,
>>>> +  .probe         = xrt_bus_probe,
>>>> +  .remove        = xrt_bus_remove,
>>>> +};
>>>> +
>>>> +int xrt_register_driver(struct xrt_driver *drv)
>>>> +{
>>>> +Â Â const char *drvname = XRT_DRVNAME(drv);
>>>> +Â Â int rc = 0;
>>>> +
>>>> +Â Â /* Initialize dev_t for char dev node. */
>>>> +Â Â if (drv->file_ops.xsf_ops.open) {
>>>> +Â Â Â Â Â Â Â Â Â Â rc = alloc_chrdev_region(&drv->file_ops.xsf_dev_t, 0,
>>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â XRT_MAX_DEVICE_NODES, drvname);
>>>> +Â Â Â Â Â Â Â Â Â Â if (rc) {
>>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â pr_err("failed to alloc dev minor for %s:
>>>> %d\n", drvname, rc);
>>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â return rc;
>>>> +Â Â Â Â Â Â Â Â Â Â }
>>>> +Â Â } else {
>>>> +Â Â Â Â Â Â Â Â Â Â drv->file_ops.xsf_dev_t = (dev_t)-1;
>>>> +Â Â }
>>>> +
>>>> +Â Â drv->driver.owner = THIS_MODULE;
>>>> +Â Â drv->driver.bus = &xrt_bus_type;
>>>> +
>>>> +Â Â rc = driver_register(&drv->driver);
>>>> +Â Â if (rc) {
>>>> +Â Â Â Â Â Â Â Â Â Â pr_err("register %s xrt driver failed\n", drvname);
>>>> +Â Â Â Â Â Â Â Â Â Â if (drv->file_ops.xsf_dev_t != (dev_t)-1) {
>>>> + unregister_chrdev_region(drv->file_ops.xsf_dev_t,
>>>> + XRT_MAX_DEVICE_NODES);
>>>> +Â Â Â Â Â Â Â Â Â Â }
>>>> +Â Â Â Â Â Â Â Â Â Â return rc;
>>>> +Â Â }
>>>> +
>>>> +Â Â pr_info("%s registered successfully\n", drvname);
>>>> +
>>>> +Â Â return 0;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(xrt_register_driver);
>>>> +
>>>> +void xrt_unregister_driver(struct xrt_driver *drv)
>>>> +{
>>>> +Â Â driver_unregister(&drv->driver);
>>>> +
>>>> +Â Â if (drv->file_ops.xsf_dev_t != (dev_t)-1)
>>>> + unregister_chrdev_region(drv->file_ops.xsf_dev_t,
>>>> XRT_MAX_DEVICE_NODES);
>>> ok
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(xrt_unregister_driver);
>>>> +
>>>> +static int __find_driver(struct device_driver *drv, void *_data)
>>>> +{
>>>> +Â Â struct xrt_driver *xdrv = to_xrt_drv(drv);
>>>> +Â Â struct xrt_find_drv_data *data = _data;
>>>> +
>>>> +Â Â if (xdrv->subdev_id == data->id) {
>>>> +Â Â Â Â Â Â Â Â Â Â data->xdrv = xdrv;
>>>> +Â Â Â Â Â Â Â Â Â Â return 1;
>>>> +Â Â }
>>>> +
>>>> +Â Â return 0;
>>>> +}
>>>> +
>>>> +const char *xrt_drv_name(enum xrt_subdev_id id)
>>>> +{
>>>> +Â Â struct xrt_find_drv_data data = { 0 };
>>>> +
>>>> +Â Â data.id = id;
>>>> +Â Â bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
>>>> +
>>>> +Â Â if (data.xdrv)
>>>> +Â Â Â Â Â Â Â Â Â Â return XRT_DRVNAME(data.xdrv);
>>>> +
>>>> +Â Â return NULL;
>>>> +}
>>>> +
>>>> +static int xrt_drv_get_instance(enum xrt_subdev_id id)
>>>> +{
>>>> +Â Â int ret;
>>>> +
>>>> +Â Â ret = ida_alloc_range(&xrt_device_ida, xrt_instance_to_id(id, 0),
>>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â xrt_instance_to_id(id,
>>>> XRT_MAX_DEVICE_NODES),
>>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â GFP_KERNEL);
>>>> +Â Â if (ret < 0)
>>>> +Â Â Â Â Â Â Â Â Â Â return ret;
>>>> +
>>>> +Â Â return xrt_id_to_instance((u32)ret);
>>>> +}
>>>> +
>>>> +static void xrt_drv_put_instance(enum xrt_subdev_id id, int instance)
>>>> +{
>>>> +Â Â ida_free(&xrt_device_ida, xrt_instance_to_id(id, instance));
>>>> +}
>>>> +
>>>> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id
>>>> id)
>>>> +{
>>>> +Â Â struct xrt_find_drv_data data = { 0 };
>>>> +
>>>> +Â Â data.id = id;
>>>> +Â Â bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
>>>> +
>>>> +Â Â if (data.xdrv)
>>>> +Â Â Â Â Â Â Â Â Â Â return data.xdrv->endpoints;
>>>> +
>>>> +Â Â return NULL;
>>>> +}
>>>> +
>>>> +static void xrt_device_release(struct device *dev)
>>>> +{
>>>> +Â Â struct xrt_device *xdev = container_of(dev, struct xrt_device,
>>>> dev);
>>>> +
>>>> +Â Â kfree(xdev);
>>>> +}
>>>> +
>>>> +void xrt_device_unregister(struct xrt_device *xdev)
>>>> +{
>>>> +Â Â if (xdev->state == XRT_DEVICE_STATE_ADDED)
>>>> +Â Â Â Â Â Â Â Â Â Â device_del(&xdev->dev);
>>>> +
>>>> +Â Â vfree(xdev->sdev_data);
>>> cleanup of sdev_data here
>>>
>>> fail: label in xrt_device_register is ok.
>>>
>>>> +Â Â kfree(xdev->resource);
>>>> +
>>>> +Â Â if (xdev->instance != XRT_INVALID_DEVICE_INST)
>>>> +Â Â Â Â Â Â Â Â Â Â xrt_drv_put_instance(xdev->subdev_id, xdev->instance);
>>>> +
>>>> +Â Â if (xdev->dev.release == xrt_device_release)
>>>> +Â Â Â Â Â Â Â Â Â Â put_device(&xdev->dev);
>>>> +}
>>>> +
>>>> +struct xrt_device *
>>>> +xrt_device_register(struct device *parent, u32 id,
>>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct resource *res, u32 res_num,
>>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â void *pdata, size_t data_sz)
>>>> +{
>>>> +Â Â struct xrt_device *xdev = NULL;
>>>> +Â Â int ret;
>>>> +
>>>> +Â Â xdev = kzalloc(sizeof(*xdev), GFP_KERNEL);
>>>> +Â Â if (!xdev)
>>>> +Â Â Â Â Â Â Â Â Â Â return xdev;
>>> ok
>>>> +Â Â xdev->instance = XRT_INVALID_DEVICE_INST;
>>>> +
>>>> +Â Â /* Obtain dev instance number. */
>>>> +Â Â ret = xrt_drv_get_instance(id);
>>>> +Â Â if (ret < 0) {
>>>> +Â Â Â Â Â Â Â Â Â Â dev_err(parent, "failed get instance, ret %d", ret);
>>>> +Â Â Â Â Â Â Â Â Â Â goto fail;
>>>> +Â Â }
>>>> +
>>>> +Â Â xdev->instance = ret;
>>>> +Â Â xdev->name = xrt_drv_name(id);
>>>> +Â Â xdev->subdev_id = id;
>>>> +Â Â device_initialize(&xdev->dev);
>>>> +Â Â xdev->dev.release = xrt_device_release;
>>>> +Â Â xdev->dev.parent = parent;
>>>> +
>>>> +Â Â xdev->dev.bus = &xrt_bus_type;
>>>> +Â Â dev_set_name(&xdev->dev, "%s.%d", xdev->name, xdev->instance);
>>>> +
>>>> +Â Â xdev->num_resources = res_num;
>>>> +Â Â xdev->resource = kmemdup(res, sizeof(*res) * res_num, GFP_KERNEL);
>>>> +Â Â if (!xdev->resource)
>>>> +Â Â Â Â Â Â Â Â Â Â goto fail;
>>>> +
>>>> +Â Â xdev->sdev_data = vzalloc(data_sz);
>>>> +Â Â if (!xdev->sdev_data)
>>>> +Â Â Â Â Â Â Â Â Â Â goto fail;
>>>> +
>>>> +Â Â memcpy(xdev->sdev_data, pdata, data_sz);
>>>> +
>>>> +Â Â ret = device_add(&xdev->dev);
>>>> +Â Â if (ret) {
>>>> +Â Â Â Â Â Â Â Â Â Â dev_err(parent, "failed add device, ret %d", ret);
>>>> +Â Â Â Â Â Â Â Â Â Â goto fail;
>>>> +Â Â }
>>>> +Â Â xdev->state = XRT_DEVICE_STATE_ADDED;
>>>> +
>>>> +Â Â return xdev;
>>>> +
>>>> +fail:
>>>> +Â Â xrt_device_unregister(xdev);
>>>> +Â Â kfree(xdev);
>>>> +
>>>> +Â Â return NULL;
>>>> +}
>>>> +
>>>> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32
>>>> type, u32 num)
>>>> +{
>>>> +Â Â u32 i;
>>>> +
>>>> +Â Â for (i = 0; i < xdev->num_resources; i++) {
>>>> +Â Â Â Â Â Â Â Â Â Â struct resource *r = &xdev->resource[i];
>>>> +
>>>> +Â Â Â Â Â Â Â Â Â Â if (type == resource_type(r) && num-- == 0)
>>>> +Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â return r;
>>>> +Â Â }
>>>> +Â Â return NULL;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Leaf driver's module init/fini callbacks. This is not a open
>>>> infrastructure for dynamic
>>>> + * plugging in drivers. All drivers should be statically added.
>>>> + */
>>>> +static void (*leaf_init_fini_cbs[])(bool) = {
>>>> +Â Â group_leaf_init_fini,
>>>> +Â Â axigate_leaf_init_fini,
>>>> +Â Â icap_leaf_init_fini,
>>>> +};
>>>> +
>>>> +static __init int xrt_lib_init(void)
>>>> +{
>>>> +Â Â int ret;
>>>> +Â Â int i;
>>>> +
>>>> +Â Â ret = bus_register(&xrt_bus_type);
>>>> +Â Â if (ret)
>>>> +Â Â Â Â Â Â Â Â Â Â return ret;
>>>> +
>>>> +Â Â xrt_class = class_create(THIS_MODULE, XRT_IPLIB_MODULE_NAME);
>>>> +Â Â if (IS_ERR(xrt_class)) {
>>>> +Â Â Â Â Â Â Â Â Â Â bus_unregister(&xrt_bus_type);
>>>> +Â Â Â Â Â Â Â Â Â Â return PTR_ERR(xrt_class);
>>>> +Â Â }
>>>> +
>>>> +Â Â for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
>>>> +Â Â Â Â Â Â Â Â Â Â leaf_init_fini_cbs[i](true);
>>>> +Â Â return 0;
>>>> +}
>>>> +
>>>> +static __exit void xrt_lib_fini(void)
>>>> +{
>>>> +Â Â int i;
>>>> +
>>>> +Â Â for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
>>>> +Â Â Â Â Â Â Â Â Â Â leaf_init_fini_cbs[i](false);
>>>> +
>>>> +Â Â class_destroy(xrt_class);
>>>> +Â Â bus_unregister(&xrt_bus_type);
>>>> +}
>>>> +
>>>> +module_init(xrt_lib_init);
>>>> +module_exit(xrt_lib_fini);
>>>> +
>>>> +MODULE_AUTHOR("XRT Team <[email protected]>");
>>>> +MODULE_DESCRIPTION("Xilinx Alveo IP Lib driver");
>>>> +MODULE_LICENSE("GPL v2");
>>>> diff --git a/drivers/fpga/xrt/lib/lib-drv.h
>>>> b/drivers/fpga/xrt/lib/lib-drv.h
>>>> new file mode 100644
>>>> index 000000000000..0276c28e009f
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/lib/lib-drv.h
>>>> @@ -0,0 +1,21 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Cheng Zhen <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _LIB_DRV_H_
>>>> +#define _LIB_DRV_H_
>>>> +
>>>> +#include <linux/device/class.h>
>>>> +#include <linux/device/bus.h>
>>>> +
>>>> +extern struct class *xrt_class;
>>>> +extern struct bus_type xrt_bus_type;
>>>> +
>>>> +const char *xrt_drv_name(enum xrt_subdev_id id);
>>>> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id
>>>> id);
>>>> +
>>>> +#endif    /* _LIB_DRV_H_ */
>
Hello Yilun,
Welcome as a co-maintainer of the Linux FPGA subsystem! Looking forward to working with you.
As you are aware, Xilinx has been working on upstreaming the XRT Alveo drivers to the Linux kernel. Thanks to the feedback from the reviewers over last several months, we have improved the design of our drivers. We are currently at V9 and awaiting feedback. Will appreciate your help with the review.
-Sonal
________________________________________
From: Tom Rix <[email protected]>
Sent: Tuesday, September 14, 2021 6:27 AM
To: Lizhi Hou; Moritz Fischer; Wu, Hao; Greg KH
Cc: [email protected]; [email protected]; Max Zhen; Sonal Santan; Yu Liu; Michal Simek; Stefano Stabellini; [email protected]; [email protected]; Max Zhen
Subject: Re: [PATCH V9 XRT Alveo 04/14] fpga: xrt: xrt-lib driver manager
On 9/10/21 2:34 PM, Lizhi Hou wrote:
> Hi Moritz,
>
> (sending it again because rejected by some mail alias)
>
>
> Did you get time to review XRT patch set? Please let us know if there
> are more we need to change.
> We are looking for your comments.
This patchset has been outstanding for 6 weeks to address Moritz'
comments for the v8 patchset of this single patch 4 of 14.
The changes look good to me.
https://lore.kernel.org/linux-fpga/[email protected]/
Moritz, can you review the changes ?
The xrt patchset starts to enable a whole class of general purpose
datacenter fpga's.
https://www.xilinx.com/products/boards-and-kits/alveo.html
This would be a great feature for the fpga/ subsystem.
Tom
>
> Thanks,
> Lizhi
>
> On 8/21/21 9:44 AM, Moritz Fischer wrote:
>> CAUTION: This message has originated from an External Source. Please
>> use proper judgment and caution when opening attachments, clicking
>> links, or responding to this email.
>>
>>
>> On Thu, Aug 19, 2021 at 05:14:14AM -0700, Tom Rix wrote:
>>> On 8/2/21 9:05 AM, Lizhi Hou wrote:
>>>
>>>> xrt-lib kernel module infrastructure code to register and manage all
>>>> leaf driver modules.
>>>>
>>>> Signed-off-by: Sonal Santan <[email protected]>
>>>> Signed-off-by: Max Zhen <[email protected]>
>>>> Signed-off-by: Lizhi Hou <[email protected]>
>>>> Reviewed-by: Tom Rix <[email protected]>
>>> This was the only patch with requested changes in v8.
>>>
>>> All the changes from v8 have been made.
>>>
>>> They are itemized below with variations on 'ok'
>>>
>>> It is still Moritz's call to accept them.
>> I'll be OOO till 9/4/21. I'll get to it after.
>>
>> - Moritz
>>> Tom
>>>
>>>> ---
>>>> drivers/fpga/xrt/include/subdev_id.h |? 39 +++
>>>> drivers/fpga/xrt/include/xdevice.h | 141 ++++++++
>>>> drivers/fpga/xrt/include/xleaf.h | 205 +++++++++++
>>>> drivers/fpga/xrt/include/xleaf/clkfreq.h |? 21 ++
>>>> drivers/fpga/xrt/include/xleaf/clock.h |? 29 ++
>>>> .../fpga/xrt/include/xleaf/ddr_calibration.h |? 28 ++
>>>> drivers/fpga/xrt/include/xleaf/devctl.h |? 40 +++
>>>> drivers/fpga/xrt/lib/lib-drv.c | 318
>>>> ++++++++++++++++++
>>>> drivers/fpga/xrt/lib/lib-drv.h |? 21 ++
>>>> 9 files changed, 842 insertions(+)
>>>> create mode 100644 drivers/fpga/xrt/include/subdev_id.h
>>>> create mode 100644 drivers/fpga/xrt/include/xdevice.h
>>>> create mode 100644 drivers/fpga/xrt/include/xleaf.h
>>>> create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
>>>> create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
>>>> create mode 100644 drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>>> create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
>>>> create mode 100644 drivers/fpga/xrt/lib/lib-drv.c
>>>> create mode 100644 drivers/fpga/xrt/lib/lib-drv.h
>>>>
>>>> diff --git a/drivers/fpga/xrt/include/subdev_id.h
>>>> b/drivers/fpga/xrt/include/subdev_id.h
>>>> new file mode 100644
>>>> index 000000000000..02df4b939a1b
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/subdev_id.h
>>>> @@ -0,0 +1,39 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Cheng Zhen <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_SUBDEV_ID_H_
>>>> +#define _XRT_SUBDEV_ID_H_
>>>> +
>>>> +/*
>>>> + * Every subdev driver has an ID for others to refer to it. There
>>>> can be multiple number of
>>>> + * instances of a subdev driver. A <subdev_id, subdev_instance>
>>>> tuple is a unique identification
>>>> + * of a specific instance of a subdev driver.
>>>> + */
>>>> +enum xrt_subdev_id {
>>>> + XRT_SUBDEV_INVALID = 0,
>>>> + XRT_SUBDEV_GRP,
>>>> + XRT_SUBDEV_VSEC,
>>>> + XRT_SUBDEV_VSEC_GOLDEN,
>>>> + XRT_SUBDEV_DEVCTL,
>>>> + XRT_SUBDEV_AXIGATE,
>>>> + XRT_SUBDEV_ICAP,
>>>> + XRT_SUBDEV_TEST,
>>>> + XRT_SUBDEV_MGMT_MAIN,
>>>> + XRT_SUBDEV_QSPI,
>>>> + XRT_SUBDEV_MAILBOX,
>>>> + XRT_SUBDEV_CMC,
>>>> + XRT_SUBDEV_CALIB,
>>>> + XRT_SUBDEV_CLKFREQ,
>>>> + XRT_SUBDEV_CLOCK,
>>>> + XRT_SUBDEV_SRSR,
>>>> + XRT_SUBDEV_UCS,
>>>> + XRT_SUBDEV_NUM, /* Total number of subdevs. */
>>>> + XRT_ROOT = -1, /* Special ID for root driver. */
>>>> +};
>>>> +
>>>> +#endif /* _XRT_SUBDEV_ID_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xdevice.h
>>>> b/drivers/fpga/xrt/include/xdevice.h
>>>> new file mode 100644
>>>> index 000000000000..b40ebe98b54d
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xdevice.h
>>>> @@ -0,0 +1,141 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Lizhi Hou <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_DEVICE_H_
>>>> +#define _XRT_DEVICE_H_
>>>> +
>>>> +#include <linux/fs.h>
>>>> +#include <linux/cdev.h>
>>>> +
>>>> +#define XRT_MAX_DEVICE_NODES 128
>>>> +#define XRT_INVALID_DEVICE_INST (XRT_MAX_DEVICE_NODES + 1)
>>>> +
>>>> +enum {
>>>> + XRT_DEVICE_STATE_NONE = 0,
>>>> + XRT_DEVICE_STATE_ADDED
>>>> +};
>>>> +
>>>> +/*
>>>> + * struct xrt_device - represent an xrt device on xrt bus
>>>> + *
>>>> + * dev: generic device interface.
>>>> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
>>> ok
>>>> + * name: name of the xrt device.
>>>> + * instance: instance of the xrt device. The xrt device with same
>>>> id can have
>>>> + * more than 1 instances.
>>>> + * state: current state of the xrt device.
>>>> + * num_resources: The total number of resource for the xrt device.
>>>> + * resource: point to the xrt device resource array.
>>>> + * sdev_data: private data pointer.
>>>> + */
>>>> +struct xrt_device {
>>>> + struct device dev;
>>>> + u32 subdev_id;
>>>> + const char *name;
>>>> + u32 instance;
>>>> + u32 state;
>>>> + u32 num_resources;
>>>> + struct resource *resource;
>>>> + void *sdev_data;
>>>> +};
>>>> +
>>>> +/*
>>>> + * If populated by xrt device driver, infra will handle the
>>>> mechanics of
>>>> + * char device (un)registration.
>>>> + */
>>>> +enum xrt_dev_file_mode {
>>>> + /* Infra create cdev, default file name */
>>>> + XRT_DEV_FILE_DEFAULT = 0,
>>>> + /* Infra create cdev, need to encode inst num in file name */
>>>> + XRT_DEV_FILE_MULTI_INST,
>>>> + /* No auto creation of cdev by infra, leaf handles it by itself */
>>>> + XRT_DEV_FILE_NO_AUTO,
>>>> +};
>>>> +
>>>> +struct xrt_dev_file_ops {
>>>> + const struct file_operations xsf_ops;
>>>> + dev_t xsf_dev_t;
>>>> + const char *xsf_dev_name;
>>>> + enum xrt_dev_file_mode xsf_mode;
>>>> +};
>>>> +
>>>> +/*
>>>> + * this struct define the endpoints belong to the same xrt device
>>>> + * ep_name: endpoint name
>>>> + * compat: compatible string
>>> ok
>>>> + */
>>>> +struct xrt_dev_ep_names {
>>>> + const char *ep_name;
>>>> + const char *compat;
>>>> +};
>>>> +
>>>> +struct xrt_dev_endpoints {
>>>> + struct xrt_dev_ep_names *xse_names;
>>>> + /* minimum number of endpoints to support the subdevice */
>>>> + u32 xse_min_ep;
>>>> +};
>>>> +
>>>> +/*
>>>> + * struct xrt_driver - represent a xrt device driver
>>>> + *
>>>> + * driver: driver model structure.
>>>> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
>>>> + * file_ops: character device name and callbacks.
>>>> + * probe: mandatory callback for device binding.
>>>> + * remove: callback for device unbinding.
>>>> + * leaf_call: callback for servicing other leaf drivers.
>>> ok
>>>> + */
>>>> +struct xrt_driver {
>>>> + struct device_driver driver;
>>>> + u32 subdev_id;
>>>> + struct xrt_dev_file_ops file_ops;
>>>> + struct xrt_dev_endpoints *endpoints;
>>>> +
>>>> + /*
>>>> + * Subdev driver callbacks populated by subdev driver.
>>>> + */
>>>> + int (*probe)(struct xrt_device *xrt_dev);
>>>> + void (*remove)(struct xrt_device *xrt_dev);
>>>> + /*
>>>> + * If leaf_call is defined, these are called by other leaf
>>>> drivers.
>>>> + * Note that root driver may call into leaf_call of a group
>>>> driver.
>>>> + */
>>>> + int (*leaf_call)(struct xrt_device *xrt_dev, u32 cmd, void *arg);
>>>> +};
>>>> +
>>>> +#define to_xrt_dev(d) container_of(d, struct xrt_device, dev)
>>>> +#define to_xrt_drv(d) container_of(d, struct xrt_driver, driver)
>>>> +
>>>> +static inline void *xrt_get_drvdata(const struct xrt_device *xdev)
>>>> +{
>>>> + return dev_get_drvdata(&xdev->dev);
>>>> +}
>>>> +
>>>> +static inline void xrt_set_drvdata(struct xrt_device *xdev, void
>>>> *data)
>>>> +{
>>>> + dev_set_drvdata(&xdev->dev, data);
>>>> +}
>>>> +
>>>> +static inline void *xrt_get_xdev_data(struct device *dev)
>>>> +{
>>>> + struct xrt_device *xdev = to_xrt_dev(dev);
>>>> +
>>>> + return xdev->sdev_data;
>>>> +}
>>>> +
>>>> +struct xrt_device *
>>>> +xrt_device_register(struct device *parent, u32 id,
>>>> + struct resource *res, u32 res_num,
>>>> + void *pdata, size_t data_sz);
>>>> +void xrt_device_unregister(struct xrt_device *xdev);
>>>> +int xrt_register_driver(struct xrt_driver *drv);
>>>> +void xrt_unregister_driver(struct xrt_driver *drv);
>>>> +void *xrt_get_xdev_data(struct device *dev);
>>>> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32
>>>> type, u32 num);
>>>> +
>>>> +#endif /* _XRT_DEVICE_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xleaf.h
>>>> b/drivers/fpga/xrt/include/xleaf.h
>>>> new file mode 100644
>>>> index 000000000000..f065fc766e0f
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xleaf.h
>>>> @@ -0,0 +1,205 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Cheng Zhen <[email protected]>
>>>> + * Sonal Santan <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_XLEAF_H_
>>>> +#define _XRT_XLEAF_H_
>>>> +
>>>> +#include <linux/mod_devicetable.h>
>>>> +#include "xdevice.h"
>>>> +#include "subdev_id.h"
>>>> +#include "xroot.h"
>>>> +#include "events.h"
>>>> +
>>>> +/* All subdev drivers should use below common routines to print
>>>> out msg. */
>>>> +#define DEV(xdev) (&(xdev)->dev)
>>>> +#define DEV_PDATA(xdev) \
>>>> + ((struct xrt_subdev_platdata *)xrt_get_xdev_data(DEV(xdev)))
>>>> +#define DEV_FILE_OPS(xdev) \
>>>> + (&(to_xrt_drv((xdev)->dev.driver))->file_ops)
>>>> +#define FMT_PRT(prt_fn, xdev, fmt, args...) \
>>>> + ({typeof(xdev) (_xdev) = (xdev); \
>>>> + prt_fn(DEV(_xdev), "%s %s: " fmt, \
>>>> + DEV_PDATA(_xdev)->xsp_root_name, __func__, ##args); })
>>>> +#define xrt_err(xdev, fmt, args...) FMT_PRT(dev_err, xdev, fmt,
>>>> ##args)
>>>> +#define xrt_warn(xdev, fmt, args...) FMT_PRT(dev_warn, xdev, fmt,
>>>> ##args)
>>>> +#define xrt_info(xdev, fmt, args...) FMT_PRT(dev_info, xdev, fmt,
>>>> ##args)
>>>> +#define xrt_dbg(xdev, fmt, args...) FMT_PRT(dev_dbg, xdev, fmt,
>>>> ##args)
>>>> +
>>>> +#define XRT_DEFINE_REGMAP_CONFIG(config_name) \
>>>> + static const struct regmap_config config_name = {?????????????? \
>>>> + .reg_bits = 32, \
>>>> + .val_bits = 32, \
>>>> + .reg_stride = 4, \
>>>> + .max_register = 0x1000, \
>>>> + }
>>>> +
>>>> +enum {
>>>> + /* Starting cmd for common leaf cmd implemented by all leaves. */
>>>> + XRT_XLEAF_COMMON_BASE = 0,
>>>> + /* Starting cmd for leaves' specific leaf cmds. */
>>>> + XRT_XLEAF_CUSTOM_BASE = 64,
>>>> +};
>>>> +
>>>> +enum xrt_xleaf_common_leaf_cmd {
>>>> + XRT_XLEAF_EVENT = XRT_XLEAF_COMMON_BASE,
>>>> +};
>>>> +
>>>> +/*
>>>> + * Partially initialized by the parent driver, then, passed in as
>>>> subdev driver's
>>>> + * platform data when creating subdev driver instance by calling
>>>> platform
>>>> + * device register API (xrt_device_register_data() or the likes).
>>>> + *
>>>> + * Once device register API returns, platform driver framework
>>>> makes a copy of
>>>> + * this buffer and maintains its life cycle. The content of the
>>>> buffer is
>>>> + * completely owned by subdev driver.
>>>> + *
>>>> + * Thus, parent driver should be very careful when it touches this
>>>> buffer
>>>> + * again once it's handed over to subdev driver. And the data
>>>> structure
>>>> + * should not contain pointers pointing to buffers that is managed by
>>>> + * other or parent drivers since it could have been freed before
>>>> platform
>>>> + * data buffer is freed by platform driver framework.
>>>> + */
>>>> +struct xrt_subdev_platdata {
>>>> + /*
>>>> + * Per driver instance callback. The xdev points to the instance.
>>>> + * Should always be defined for subdev driver to get service
>>>> from root.
>>>> + */
>>>> + xrt_subdev_root_cb_t xsp_root_cb;
>>>> + void *xsp_root_cb_arg;
>>>> +
>>>> + /* Something to associate w/ root for msg printing. */
>>>> + const char *xsp_root_name;
>>>> +
>>>> + /*
>>>> + * Char dev support for this subdev instance.
>>>> + * Initialized by subdev driver.
>>>> + */
>>>> + struct cdev xsp_cdev;
>>>> + struct device *xsp_sysdev;
>>>> + struct mutex xsp_devnode_lock; /* devnode lock */
>>>> + struct completion xsp_devnode_comp;
>>>> + int xsp_devnode_ref;
>>>> + bool xsp_devnode_online;
>>>> + bool xsp_devnode_excl;
>>>> +
>>>> + /*
>>>> + * Subdev driver specific init data. The buffer should be embedded
>>>> + * in this data structure buffer after dtb, so that it can be
>>>> freed
>>>> + * together with platform data.
>>>> + */
>>>> + loff_t xsp_priv_off; /* Offset into this platform data buffer. */
>>>> + size_t xsp_priv_len;
>>>> +
>>>> + /*
>>>> + * Populated by parent driver to describe the device tree for
>>>> + * the subdev driver to handle. Should always be last one since
>>>> it's
>>>> + * of variable length.
>>>> + */
>>>> + bool xsp_dtb_valid;
>>>> + char xsp_dtb[0];
>>>> +};
>>>> +
>>>> +struct subdev_match_arg {
>>>> + enum xrt_subdev_id id;
>>>> + int instance;
>>>> +};
>>>> +
>>>> +bool xleaf_has_endpoint(struct xrt_device *xdev, const char
>>>> *endpoint_name);
>>>> +struct xrt_device *xleaf_get_leaf(struct xrt_device *xdev,
>>>> + xrt_subdev_match_t cb, void *arg);
>>>> +
>>>> +static inline bool subdev_match(enum xrt_subdev_id id, struct
>>>> xrt_device *xdev, void *arg)
>>>> +{
>>>> + const struct subdev_match_arg *a = (struct subdev_match_arg *)arg;
>>>> + int instance = a->instance;
>>>> +
>>>> + if (id != a->id)
>>>> + return false;
>>>> + if (instance != xdev->instance && instance !=
>>>> XRT_INVALID_DEVICE_INST)
>>>> + return false;
>>>> + return true;
>>>> +}
>>>> +
>>>> +static inline bool xrt_subdev_match_epname(enum xrt_subdev_id id,
>>>> + struct xrt_device *xdev,
>>>> void *arg)
>>>> +{
>>>> + return xleaf_has_endpoint(xdev, arg);
>>>> +}
>>>> +
>>>> +static inline struct xrt_device *
>>>> +xleaf_get_leaf_by_id(struct xrt_device *xdev,
>>>> + enum xrt_subdev_id id, int instance)
>>>> +{
>>>> + struct subdev_match_arg arg = { id, instance };
>>>> +
>>>> + return xleaf_get_leaf(xdev, subdev_match, &arg);
>>>> +}
>>>> +
>>>> +static inline struct xrt_device *
>>>> +xleaf_get_leaf_by_epname(struct xrt_device *xdev, const char *name)
>>>> +{
>>>> + return xleaf_get_leaf(xdev, xrt_subdev_match_epname, (void
>>>> *)name);
>>>> +}
>>>> +
>>>> +static inline int xleaf_call(struct xrt_device *tgt, u32 cmd, void
>>>> *arg)
>>>> +{
>>>> + return (to_xrt_drv(tgt->dev.driver)->leaf_call)(tgt, cmd, arg);
>>>> +}
>>>> +
>>>> +int xleaf_broadcast_event(struct xrt_device *xdev, enum xrt_events
>>>> evt, bool async);
>>>> +int xleaf_create_group(struct xrt_device *xdev, char *dtb);
>>>> +int xleaf_destroy_group(struct xrt_device *xdev, int instance);
>>>> +void xleaf_get_root_res(struct xrt_device *xdev, u32 region_id,
>>>> struct resource **res);
>>>> +void xleaf_get_root_id(struct xrt_device *xdev, unsigned short
>>>> *vendor, unsigned short *device,
>>>> + unsigned short *subvendor, unsigned short
>>>> *subdevice);
>>>> +void xleaf_hot_reset(struct xrt_device *xdev);
>>>> +int xleaf_put_leaf(struct xrt_device *xdev, struct xrt_device *leaf);
>>>> +struct device *xleaf_register_hwmon(struct xrt_device *xdev, const
>>>> char *name, void *drvdata,
>>>> + const struct attribute_group **grps);
>>>> +void xleaf_unregister_hwmon(struct xrt_device *xdev, struct device
>>>> *hwmon);
>>>> +int xleaf_wait_for_group_bringup(struct xrt_device *xdev);
>>>> +
>>>> +/*
>>>> + * Character device helper APIs for use by leaf drivers
>>>> + */
>>>> +static inline bool xleaf_devnode_enabled(struct xrt_device *xdev)
>>>> +{
>>>> + return DEV_FILE_OPS(xdev)->xsf_ops.open;
>>>> +}
>>>> +
>>>> +int xleaf_devnode_create(struct xrt_device *xdev,
>>>> + const char *file_name, const char *inst_name);
>>>> +void xleaf_devnode_destroy(struct xrt_device *xdev);
>>>> +
>>>> +struct xrt_device *xleaf_devnode_open_excl(struct inode *inode);
>>>> +struct xrt_device *xleaf_devnode_open(struct inode *inode);
>>>> +void xleaf_devnode_close(struct inode *inode);
>>>> +
>>>> +/* Module's init/fini routines for leaf driver in xrt-lib module */
>>>> +#define XRT_LEAF_INIT_FINI_FUNC(name) \
>>>> +void name##_leaf_init_fini(bool
>>>> init) \
>>>> +{????????????????????????????????????????????????????????????????? \
>>>> + if (init) \
>>>> + xrt_register_driver(&xrt_##name##_driver); \
>>>> + else \
>>>> + xrt_unregister_driver(&xrt_##name##_driver); \
>>>> +}
>>>> +
>>>> +/* Module's init/fini routines for leaf driver in xrt-lib module */
>>>> +void group_leaf_init_fini(bool init);
>>>> +void vsec_leaf_init_fini(bool init);
>>>> +void devctl_leaf_init_fini(bool init);
>>>> +void axigate_leaf_init_fini(bool init);
>>>> +void icap_leaf_init_fini(bool init);
>>>> +void calib_leaf_init_fini(bool init);
>>>> +void clkfreq_leaf_init_fini(bool init);
>>>> +void clock_leaf_init_fini(bool init);
>>>> +void ucs_leaf_init_fini(bool init);
>>>> +
>>>> +#endif /* _XRT_LEAF_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xleaf/clkfreq.h
>>>> b/drivers/fpga/xrt/include/xleaf/clkfreq.h
>>>> new file mode 100644
>>>> index 000000000000..005441d5df78
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xleaf/clkfreq.h
>>>> @@ -0,0 +1,21 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Lizhi Hou <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_CLKFREQ_H_
>>>> +#define _XRT_CLKFREQ_H_
>>>> +
>>>> +#include "xleaf.h"
>>>> +
>>>> +/*
>>>> + * CLKFREQ driver leaf calls.
>>>> + */
>>>> +enum xrt_clkfreq_leaf_cmd {
>>>> + XRT_CLKFREQ_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in
>>>> xleaf.h */
>>>> +};
>>>> +
>>>> +#endif /* _XRT_CLKFREQ_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xleaf/clock.h
>>>> b/drivers/fpga/xrt/include/xleaf/clock.h
>>>> new file mode 100644
>>>> index 000000000000..1379e24fa5d0
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xleaf/clock.h
>>>> @@ -0,0 +1,29 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Lizhi Hou <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_CLOCK_H_
>>>> +#define _XRT_CLOCK_H_
>>>> +
>>>> +#include "xleaf.h"
>>>> +#include <linux/fpga_xrt.h>
>>>> +
>>>> +/*
>>>> + * CLOCK driver leaf calls.
>>>> + */
>>>> +enum xrt_clock_leaf_cmd {
>>>> + XRT_CLOCK_SET = XRT_XLEAF_CUSTOM_BASE, /* See comments in
>>>> xleaf.h */
>>>> + XRT_CLOCK_GET,
>>>> + XRT_CLOCK_VERIFY,
>>>> +};
>>>> +
>>>> +struct xrt_clock_get {
>>>> + u16 freq;
>>>> + u32 freq_cnter;
>>>> +};
>>>> +
>>>> +#endif /* _XRT_CLOCK_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>>> b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>>> new file mode 100644
>>>> index 000000000000..c44ae30f939a
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>>> @@ -0,0 +1,28 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Cheng Zhen <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_DDR_CALIBRATION_H_
>>>> +#define _XRT_DDR_CALIBRATION_H_
>>>> +
>>>> +#include "xleaf.h"
>>>> +#include <linux/fpga-xrt.h>
>>>> +
>>>> +/*
>>>> + * Memory calibration driver leaf calls.
>>>> + */
>>>> +enum xrt_calib_results {
>>>> + XRT_CALIB_UNKNOWN = 0,
>>>> + XRT_CALIB_SUCCEEDED,
>>>> + XRT_CALIB_FAILED,
>>>> +};
>>>> +
>>>> +enum xrt_calib_leaf_cmd {
>>>> + XRT_CALIB_RESULT = XRT_XLEAF_CUSTOM_BASE, /* See comments in
>>>> xleaf.h */
>>>> +};
>>>> +
>>>> +#endif /* _XRT_DDR_CALIBRATION_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xleaf/devctl.h
>>>> b/drivers/fpga/xrt/include/xleaf/devctl.h
>>>> new file mode 100644
>>>> index 000000000000..b97f3b6d9326
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xleaf/devctl.h
>>>> @@ -0,0 +1,40 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Lizhi Hou <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_DEVCTL_H_
>>>> +#define _XRT_DEVCTL_H_
>>>> +
>>>> +#include "xleaf.h"
>>>> +
>>>> +/*
>>>> + * DEVCTL driver leaf calls.
>>>> + */
>>>> +enum xrt_devctl_leaf_cmd {
>>>> + XRT_DEVCTL_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in
>>>> xleaf.h */
>>>> +};
>>>> +
>>>> +enum xrt_devctl_id {
>>>> + XRT_DEVCTL_ROM_UUID = 0,
>>>> + XRT_DEVCTL_DDR_CALIB,
>>>> + XRT_DEVCTL_GOLDEN_VER,
>>>> + XRT_DEVCTL_MAX
>>>> +};
>>>> +
>>>> +struct xrt_devctl_rw {
>>>> + u32 xdr_id;
>>>> + void *xdr_buf;
>>>> + u32 xdr_len;
>>>> + u32 xdr_offset;
>>>> +};
>>>> +
>>>> +struct xrt_devctl_intf_uuid {
>>>> + u32 uuid_num;
>>>> + uuid_t *uuids;
>>>> +};
>>>> +
>>>> +#endif /* _XRT_DEVCTL_H_ */
>>>> diff --git a/drivers/fpga/xrt/lib/lib-drv.c
>>>> b/drivers/fpga/xrt/lib/lib-drv.c
>>>> new file mode 100644
>>>> index 000000000000..7d21ef565651
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/lib/lib-drv.c
>>>> @@ -0,0 +1,318 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Cheng Zhen <[email protected]>
>>>> + * Lizhi Hou <[email protected]>
>>>> + */
>>>> +
>>>> +#include <linux/module.h>
>>>> +#include <linux/vmalloc.h>
>>>> +#include <linux/slab.h>
>>>> +#include "xleaf.h"
>>>> +#include "xroot.h"
>>>> +#include "lib-drv.h"
>>>> +
>>>> +#define XRT_IPLIB_MODULE_NAME "xrt-lib"
>>> ok
>>>> +#define XRT_DRVNAME(drv) ((drv)->driver.name)
>>>> +
>>>> +#define XRT_SUBDEV_ID_SHIFT 16
>>>> +#define XRT_SUBDEV_ID_MASK ((1 << XRT_SUBDEV_ID_SHIFT) - 1)
>>>> +
>>>> +struct xrt_find_drv_data {
>>>> + enum xrt_subdev_id id;
>>>> + struct xrt_driver *xdrv;
>>>> +};
>>>> +
>>>> +struct class *xrt_class;
>>>> +static DEFINE_IDA(xrt_device_ida);
>>>> +
>>>> +static inline u32 xrt_instance_to_id(enum xrt_subdev_id id, u32
>>>> instance)
>>>> +{
>>>> + return (id << XRT_SUBDEV_ID_SHIFT) | instance;
>>>> +}
>>>> +
>>>> +static inline u32 xrt_id_to_instance(u32 id)
>>>> +{
>>>> + return id & XRT_SUBDEV_ID_MASK;
>>> ok
>>>> +}
>>>> +
>>>> +static int xrt_bus_match(struct device *dev, struct device_driver
>>>> *drv)
>>>> +{
>>>> + struct xrt_device *xdev = to_xrt_dev(dev);
>>>> + struct xrt_driver *xdrv = to_xrt_drv(drv);
>>>> +
>>>> + if (xdev->subdev_id == xdrv->subdev_id)
>>>> + return 1;
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static int xrt_bus_probe(struct device *dev)
>>>> +{
>>>> + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
>>>> + struct xrt_device *xdev = to_xrt_dev(dev);
>>>> +
>>>> + return xdrv->probe(xdev);
>>>> +}
>>>> +
>>>> +static int xrt_bus_remove(struct device *dev)
>>>> +{
>>>> + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
>>>> + struct xrt_device *xdev = to_xrt_dev(dev);
>>>> +
>>>> + if (xdrv->remove)
>>>> + xdrv->remove(xdev);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +struct bus_type xrt_bus_type = {
>>>> + .name = "xrt",
>>>> + .match = xrt_bus_match,
>>>> + .probe = xrt_bus_probe,
>>>> + .remove = xrt_bus_remove,
>>>> +};
>>>> +
>>>> +int xrt_register_driver(struct xrt_driver *drv)
>>>> +{
>>>> + const char *drvname = XRT_DRVNAME(drv);
>>>> + int rc = 0;
>>>> +
>>>> + /* Initialize dev_t for char dev node. */
>>>> + if (drv->file_ops.xsf_ops.open) {
>>>> + rc = alloc_chrdev_region(&drv->file_ops.xsf_dev_t, 0,
>>>> + XRT_MAX_DEVICE_NODES, drvname);
>>>> + if (rc) {
>>>> + pr_err("failed to alloc dev minor for %s:
>>>> %d\n", drvname, rc);
>>>> + return rc;
>>>> + }
>>>> + } else {
>>>> + drv->file_ops.xsf_dev_t = (dev_t)-1;
>>>> + }
>>>> +
>>>> + drv->driver.owner = THIS_MODULE;
>>>> + drv->driver.bus = &xrt_bus_type;
>>>> +
>>>> + rc = driver_register(&drv->driver);
>>>> + if (rc) {
>>>> + pr_err("register %s xrt driver failed\n", drvname);
>>>> + if (drv->file_ops.xsf_dev_t != (dev_t)-1) {
>>>> + unregister_chrdev_region(drv->file_ops.xsf_dev_t,
>>>> + XRT_MAX_DEVICE_NODES);
>>>> + }
>>>> + return rc;
>>>> + }
>>>> +
>>>> + pr_info("%s registered successfully\n", drvname);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(xrt_register_driver);
>>>> +
>>>> +void xrt_unregister_driver(struct xrt_driver *drv)
>>>> +{
>>>> + driver_unregister(&drv->driver);
>>>> +
>>>> + if (drv->file_ops.xsf_dev_t != (dev_t)-1)
>>>> + unregister_chrdev_region(drv->file_ops.xsf_dev_t,
>>>> XRT_MAX_DEVICE_NODES);
>>> ok
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(xrt_unregister_driver);
>>>> +
>>>> +static int __find_driver(struct device_driver *drv, void *_data)
>>>> +{
>>>> + struct xrt_driver *xdrv = to_xrt_drv(drv);
>>>> + struct xrt_find_drv_data *data = _data;
>>>> +
>>>> + if (xdrv->subdev_id == data->id) {
>>>> + data->xdrv = xdrv;
>>>> + return 1;
>>>> + }
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +const char *xrt_drv_name(enum xrt_subdev_id id)
>>>> +{
>>>> + struct xrt_find_drv_data data = { 0 };
>>>> +
>>>> + data.id = id;
>>>> + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
>>>> +
>>>> + if (data.xdrv)
>>>> + return XRT_DRVNAME(data.xdrv);
>>>> +
>>>> + return NULL;
>>>> +}
>>>> +
>>>> +static int xrt_drv_get_instance(enum xrt_subdev_id id)
>>>> +{
>>>> + int ret;
>>>> +
>>>> + ret = ida_alloc_range(&xrt_device_ida, xrt_instance_to_id(id, 0),
>>>> + xrt_instance_to_id(id,
>>>> XRT_MAX_DEVICE_NODES),
>>>> + GFP_KERNEL);
>>>> + if (ret < 0)
>>>> + return ret;
>>>> +
>>>> + return xrt_id_to_instance((u32)ret);
>>>> +}
>>>> +
>>>> +static void xrt_drv_put_instance(enum xrt_subdev_id id, int instance)
>>>> +{
>>>> + ida_free(&xrt_device_ida, xrt_instance_to_id(id, instance));
>>>> +}
>>>> +
>>>> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id
>>>> id)
>>>> +{
>>>> + struct xrt_find_drv_data data = { 0 };
>>>> +
>>>> + data.id = id;
>>>> + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
>>>> +
>>>> + if (data.xdrv)
>>>> + return data.xdrv->endpoints;
>>>> +
>>>> + return NULL;
>>>> +}
>>>> +
>>>> +static void xrt_device_release(struct device *dev)
>>>> +{
>>>> + struct xrt_device *xdev = container_of(dev, struct xrt_device,
>>>> dev);
>>>> +
>>>> + kfree(xdev);
>>>> +}
>>>> +
>>>> +void xrt_device_unregister(struct xrt_device *xdev)
>>>> +{
>>>> + if (xdev->state == XRT_DEVICE_STATE_ADDED)
>>>> + device_del(&xdev->dev);
>>>> +
>>>> + vfree(xdev->sdev_data);
>>> cleanup of sdev_data here
>>>
>>> fail: label in xrt_device_register is ok.
>>>
>>>> + kfree(xdev->resource);
>>>> +
>>>> + if (xdev->instance != XRT_INVALID_DEVICE_INST)
>>>> + xrt_drv_put_instance(xdev->subdev_id, xdev->instance);
>>>> +
>>>> + if (xdev->dev.release == xrt_device_release)
>>>> + put_device(&xdev->dev);
>>>> +}
>>>> +
>>>> +struct xrt_device *
>>>> +xrt_device_register(struct device *parent, u32 id,
>>>> + struct resource *res, u32 res_num,
>>>> + void *pdata, size_t data_sz)
>>>> +{
>>>> + struct xrt_device *xdev = NULL;
>>>> + int ret;
>>>> +
>>>> + xdev = kzalloc(sizeof(*xdev), GFP_KERNEL);
>>>> + if (!xdev)
>>>> + return xdev;
>>> ok
>>>> + xdev->instance = XRT_INVALID_DEVICE_INST;
>>>> +
>>>> + /* Obtain dev instance number. */
>>>> + ret = xrt_drv_get_instance(id);
>>>> + if (ret < 0) {
>>>> + dev_err(parent, "failed get instance, ret %d", ret);
>>>> + goto fail;
>>>> + }
>>>> +
>>>> + xdev->instance = ret;
>>>> + xdev->name = xrt_drv_name(id);
>>>> + xdev->subdev_id = id;
>>>> + device_initialize(&xdev->dev);
>>>> + xdev->dev.release = xrt_device_release;
>>>> + xdev->dev.parent = parent;
>>>> +
>>>> + xdev->dev.bus = &xrt_bus_type;
>>>> + dev_set_name(&xdev->dev, "%s.%d", xdev->name, xdev->instance);
>>>> +
>>>> + xdev->num_resources = res_num;
>>>> + xdev->resource = kmemdup(res, sizeof(*res) * res_num, GFP_KERNEL);
>>>> + if (!xdev->resource)
>>>> + goto fail;
>>>> +
>>>> + xdev->sdev_data = vzalloc(data_sz);
>>>> + if (!xdev->sdev_data)
>>>> + goto fail;
>>>> +
>>>> + memcpy(xdev->sdev_data, pdata, data_sz);
>>>> +
>>>> + ret = device_add(&xdev->dev);
>>>> + if (ret) {
>>>> + dev_err(parent, "failed add device, ret %d", ret);
>>>> + goto fail;
>>>> + }
>>>> + xdev->state = XRT_DEVICE_STATE_ADDED;
>>>> +
>>>> + return xdev;
>>>> +
>>>> +fail:
>>>> + xrt_device_unregister(xdev);
>>>> + kfree(xdev);
>>>> +
>>>> + return NULL;
>>>> +}
>>>> +
>>>> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32
>>>> type, u32 num)
>>>> +{
>>>> + u32 i;
>>>> +
>>>> + for (i = 0; i < xdev->num_resources; i++) {
>>>> + struct resource *r = &xdev->resource[i];
>>>> +
>>>> + if (type == resource_type(r) && num-- == 0)
>>>> + return r;
>>>> + }
>>>> + return NULL;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Leaf driver's module init/fini callbacks. This is not a open
>>>> infrastructure for dynamic
>>>> + * plugging in drivers. All drivers should be statically added.
>>>> + */
>>>> +static void (*leaf_init_fini_cbs[])(bool) = {
>>>> + group_leaf_init_fini,
>>>> + axigate_leaf_init_fini,
>>>> + icap_leaf_init_fini,
>>>> +};
>>>> +
>>>> +static __init int xrt_lib_init(void)
>>>> +{
>>>> + int ret;
>>>> + int i;
>>>> +
>>>> + ret = bus_register(&xrt_bus_type);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + xrt_class = class_create(THIS_MODULE, XRT_IPLIB_MODULE_NAME);
>>>> + if (IS_ERR(xrt_class)) {
>>>> + bus_unregister(&xrt_bus_type);
>>>> + return PTR_ERR(xrt_class);
>>>> + }
>>>> +
>>>> + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
>>>> + leaf_init_fini_cbs[i](true);
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static __exit void xrt_lib_fini(void)
>>>> +{
>>>> + int i;
>>>> +
>>>> + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
>>>> + leaf_init_fini_cbs[i](false);
>>>> +
>>>> + class_destroy(xrt_class);
>>>> + bus_unregister(&xrt_bus_type);
>>>> +}
>>>> +
>>>> +module_init(xrt_lib_init);
>>>> +module_exit(xrt_lib_fini);
>>>> +
>>>> +MODULE_AUTHOR("XRT Team <[email protected]>");
>>>> +MODULE_DESCRIPTION("Xilinx Alveo IP Lib driver");
>>>> +MODULE_LICENSE("GPL v2");
>>>> diff --git a/drivers/fpga/xrt/lib/lib-drv.h
>>>> b/drivers/fpga/xrt/lib/lib-drv.h
>>>> new file mode 100644
>>>> index 000000000000..0276c28e009f
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/lib/lib-drv.h
>>>> @@ -0,0 +1,21 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Cheng Zhen <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _LIB_DRV_H_
>>>> +#define _LIB_DRV_H_
>>>> +
>>>> +#include <linux/device/class.h>
>>>> +#include <linux/device/bus.h>
>>>> +
>>>> +extern struct class *xrt_class;
>>>> +extern struct bus_type xrt_bus_type;
>>>> +
>>>> +const char *xrt_drv_name(enum xrt_subdev_id id);
>>>> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id
>>>> id);
>>>> +
>>>> +#endif /* _LIB_DRV_H_ */
>
Hello Moritz,
Hope things are better at your end. We are awaiting your feedback to the V9 patch series.
-Sonal
________________________________________
From: Sonal Santan <[email protected]>
Sent: Monday, September 27, 2021 3:21 PM
To: Tom Rix; Lizhi Hou; Moritz Fischer; Wu, Hao; Greg KH; Xu Yilun
Cc: [email protected]; [email protected]; Max Zhen; Yu Liu; Michal Simek; Stefano Stabellini; [email protected]; [email protected]
Subject: Re: [PATCH V9 XRT Alveo 04/14] fpga: xrt: xrt-lib driver manager
Hello Yilun,
Welcome as a co-maintainer of the Linux FPGA subsystem! Looking forward to working with you.
As you are aware, Xilinx has been working on upstreaming the XRT Alveo drivers to the Linux kernel. Thanks to the feedback from the reviewers over last several months, we have improved the design of our drivers. We are currently at V9 and awaiting feedback. Will appreciate your help with the review.
-Sonal
________________________________________
From: Tom Rix <[email protected]>
Sent: Tuesday, September 14, 2021 6:27 AM
To: Lizhi Hou; Moritz Fischer; Wu, Hao; Greg KH
Cc: [email protected]; [email protected]; Max Zhen; Sonal Santan; Yu Liu; Michal Simek; Stefano Stabellini; [email protected]; [email protected]; Max Zhen
Subject: Re: [PATCH V9 XRT Alveo 04/14] fpga: xrt: xrt-lib driver manager
On 9/10/21 2:34 PM, Lizhi Hou wrote:
> Hi Moritz,
>
> (sending it again because rejected by some mail alias)
>
>
> Did you get time to review XRT patch set? Please let us know if there
> are more we need to change.
> We are looking for your comments.
This patchset has been outstanding for 6 weeks to address Moritz'
comments for the v8 patchset of this single patch 4 of 14.
The changes look good to me.
https://lore.kernel.org/linux-fpga/[email protected]/
Moritz, can you review the changes ?
The xrt patchset starts to enable a whole class of general purpose
datacenter fpga's.
https://www.xilinx.com/products/boards-and-kits/alveo.html
This would be a great feature for the fpga/ subsystem.
Tom
>
> Thanks,
> Lizhi
>
> On 8/21/21 9:44 AM, Moritz Fischer wrote:
>> CAUTION: This message has originated from an External Source. Please
>> use proper judgment and caution when opening attachments, clicking
>> links, or responding to this email.
>>
>>
>> On Thu, Aug 19, 2021 at 05:14:14AM -0700, Tom Rix wrote:
>>> On 8/2/21 9:05 AM, Lizhi Hou wrote:
>>>
>>>> xrt-lib kernel module infrastructure code to register and manage all
>>>> leaf driver modules.
>>>>
>>>> Signed-off-by: Sonal Santan <[email protected]>
>>>> Signed-off-by: Max Zhen <[email protected]>
>>>> Signed-off-by: Lizhi Hou <[email protected]>
>>>> Reviewed-by: Tom Rix <[email protected]>
>>> This was the only patch with requested changes in v8.
>>>
>>> All the changes from v8 have been made.
>>>
>>> They are itemized below with variations on 'ok'
>>>
>>> It is still Moritz's call to accept them.
>> I'll be OOO till 9/4/21. I'll get to it after.
>>
>> - Moritz
>>> Tom
>>>
>>>> ---
>>>> drivers/fpga/xrt/include/subdev_id.h |? 39 +++
>>>> drivers/fpga/xrt/include/xdevice.h | 141 ++++++++
>>>> drivers/fpga/xrt/include/xleaf.h | 205 +++++++++++
>>>> drivers/fpga/xrt/include/xleaf/clkfreq.h |? 21 ++
>>>> drivers/fpga/xrt/include/xleaf/clock.h |? 29 ++
>>>> .../fpga/xrt/include/xleaf/ddr_calibration.h |? 28 ++
>>>> drivers/fpga/xrt/include/xleaf/devctl.h |? 40 +++
>>>> drivers/fpga/xrt/lib/lib-drv.c | 318
>>>> ++++++++++++++++++
>>>> drivers/fpga/xrt/lib/lib-drv.h |? 21 ++
>>>> 9 files changed, 842 insertions(+)
>>>> create mode 100644 drivers/fpga/xrt/include/subdev_id.h
>>>> create mode 100644 drivers/fpga/xrt/include/xdevice.h
>>>> create mode 100644 drivers/fpga/xrt/include/xleaf.h
>>>> create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
>>>> create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
>>>> create mode 100644 drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>>> create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
>>>> create mode 100644 drivers/fpga/xrt/lib/lib-drv.c
>>>> create mode 100644 drivers/fpga/xrt/lib/lib-drv.h
>>>>
>>>> diff --git a/drivers/fpga/xrt/include/subdev_id.h
>>>> b/drivers/fpga/xrt/include/subdev_id.h
>>>> new file mode 100644
>>>> index 000000000000..02df4b939a1b
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/subdev_id.h
>>>> @@ -0,0 +1,39 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Cheng Zhen <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_SUBDEV_ID_H_
>>>> +#define _XRT_SUBDEV_ID_H_
>>>> +
>>>> +/*
>>>> + * Every subdev driver has an ID for others to refer to it. There
>>>> can be multiple number of
>>>> + * instances of a subdev driver. A <subdev_id, subdev_instance>
>>>> tuple is a unique identification
>>>> + * of a specific instance of a subdev driver.
>>>> + */
>>>> +enum xrt_subdev_id {
>>>> + XRT_SUBDEV_INVALID = 0,
>>>> + XRT_SUBDEV_GRP,
>>>> + XRT_SUBDEV_VSEC,
>>>> + XRT_SUBDEV_VSEC_GOLDEN,
>>>> + XRT_SUBDEV_DEVCTL,
>>>> + XRT_SUBDEV_AXIGATE,
>>>> + XRT_SUBDEV_ICAP,
>>>> + XRT_SUBDEV_TEST,
>>>> + XRT_SUBDEV_MGMT_MAIN,
>>>> + XRT_SUBDEV_QSPI,
>>>> + XRT_SUBDEV_MAILBOX,
>>>> + XRT_SUBDEV_CMC,
>>>> + XRT_SUBDEV_CALIB,
>>>> + XRT_SUBDEV_CLKFREQ,
>>>> + XRT_SUBDEV_CLOCK,
>>>> + XRT_SUBDEV_SRSR,
>>>> + XRT_SUBDEV_UCS,
>>>> + XRT_SUBDEV_NUM, /* Total number of subdevs. */
>>>> + XRT_ROOT = -1, /* Special ID for root driver. */
>>>> +};
>>>> +
>>>> +#endif /* _XRT_SUBDEV_ID_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xdevice.h
>>>> b/drivers/fpga/xrt/include/xdevice.h
>>>> new file mode 100644
>>>> index 000000000000..b40ebe98b54d
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xdevice.h
>>>> @@ -0,0 +1,141 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Lizhi Hou <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_DEVICE_H_
>>>> +#define _XRT_DEVICE_H_
>>>> +
>>>> +#include <linux/fs.h>
>>>> +#include <linux/cdev.h>
>>>> +
>>>> +#define XRT_MAX_DEVICE_NODES 128
>>>> +#define XRT_INVALID_DEVICE_INST (XRT_MAX_DEVICE_NODES + 1)
>>>> +
>>>> +enum {
>>>> + XRT_DEVICE_STATE_NONE = 0,
>>>> + XRT_DEVICE_STATE_ADDED
>>>> +};
>>>> +
>>>> +/*
>>>> + * struct xrt_device - represent an xrt device on xrt bus
>>>> + *
>>>> + * dev: generic device interface.
>>>> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
>>> ok
>>>> + * name: name of the xrt device.
>>>> + * instance: instance of the xrt device. The xrt device with same
>>>> id can have
>>>> + * more than 1 instances.
>>>> + * state: current state of the xrt device.
>>>> + * num_resources: The total number of resource for the xrt device.
>>>> + * resource: point to the xrt device resource array.
>>>> + * sdev_data: private data pointer.
>>>> + */
>>>> +struct xrt_device {
>>>> + struct device dev;
>>>> + u32 subdev_id;
>>>> + const char *name;
>>>> + u32 instance;
>>>> + u32 state;
>>>> + u32 num_resources;
>>>> + struct resource *resource;
>>>> + void *sdev_data;
>>>> +};
>>>> +
>>>> +/*
>>>> + * If populated by xrt device driver, infra will handle the
>>>> mechanics of
>>>> + * char device (un)registration.
>>>> + */
>>>> +enum xrt_dev_file_mode {
>>>> + /* Infra create cdev, default file name */
>>>> + XRT_DEV_FILE_DEFAULT = 0,
>>>> + /* Infra create cdev, need to encode inst num in file name */
>>>> + XRT_DEV_FILE_MULTI_INST,
>>>> + /* No auto creation of cdev by infra, leaf handles it by itself */
>>>> + XRT_DEV_FILE_NO_AUTO,
>>>> +};
>>>> +
>>>> +struct xrt_dev_file_ops {
>>>> + const struct file_operations xsf_ops;
>>>> + dev_t xsf_dev_t;
>>>> + const char *xsf_dev_name;
>>>> + enum xrt_dev_file_mode xsf_mode;
>>>> +};
>>>> +
>>>> +/*
>>>> + * this struct define the endpoints belong to the same xrt device
>>>> + * ep_name: endpoint name
>>>> + * compat: compatible string
>>> ok
>>>> + */
>>>> +struct xrt_dev_ep_names {
>>>> + const char *ep_name;
>>>> + const char *compat;
>>>> +};
>>>> +
>>>> +struct xrt_dev_endpoints {
>>>> + struct xrt_dev_ep_names *xse_names;
>>>> + /* minimum number of endpoints to support the subdevice */
>>>> + u32 xse_min_ep;
>>>> +};
>>>> +
>>>> +/*
>>>> + * struct xrt_driver - represent a xrt device driver
>>>> + *
>>>> + * driver: driver model structure.
>>>> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
>>>> + * file_ops: character device name and callbacks.
>>>> + * probe: mandatory callback for device binding.
>>>> + * remove: callback for device unbinding.
>>>> + * leaf_call: callback for servicing other leaf drivers.
>>> ok
>>>> + */
>>>> +struct xrt_driver {
>>>> + struct device_driver driver;
>>>> + u32 subdev_id;
>>>> + struct xrt_dev_file_ops file_ops;
>>>> + struct xrt_dev_endpoints *endpoints;
>>>> +
>>>> + /*
>>>> + * Subdev driver callbacks populated by subdev driver.
>>>> + */
>>>> + int (*probe)(struct xrt_device *xrt_dev);
>>>> + void (*remove)(struct xrt_device *xrt_dev);
>>>> + /*
>>>> + * If leaf_call is defined, these are called by other leaf
>>>> drivers.
>>>> + * Note that root driver may call into leaf_call of a group
>>>> driver.
>>>> + */
>>>> + int (*leaf_call)(struct xrt_device *xrt_dev, u32 cmd, void *arg);
>>>> +};
>>>> +
>>>> +#define to_xrt_dev(d) container_of(d, struct xrt_device, dev)
>>>> +#define to_xrt_drv(d) container_of(d, struct xrt_driver, driver)
>>>> +
>>>> +static inline void *xrt_get_drvdata(const struct xrt_device *xdev)
>>>> +{
>>>> + return dev_get_drvdata(&xdev->dev);
>>>> +}
>>>> +
>>>> +static inline void xrt_set_drvdata(struct xrt_device *xdev, void
>>>> *data)
>>>> +{
>>>> + dev_set_drvdata(&xdev->dev, data);
>>>> +}
>>>> +
>>>> +static inline void *xrt_get_xdev_data(struct device *dev)
>>>> +{
>>>> + struct xrt_device *xdev = to_xrt_dev(dev);
>>>> +
>>>> + return xdev->sdev_data;
>>>> +}
>>>> +
>>>> +struct xrt_device *
>>>> +xrt_device_register(struct device *parent, u32 id,
>>>> + struct resource *res, u32 res_num,
>>>> + void *pdata, size_t data_sz);
>>>> +void xrt_device_unregister(struct xrt_device *xdev);
>>>> +int xrt_register_driver(struct xrt_driver *drv);
>>>> +void xrt_unregister_driver(struct xrt_driver *drv);
>>>> +void *xrt_get_xdev_data(struct device *dev);
>>>> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32
>>>> type, u32 num);
>>>> +
>>>> +#endif /* _XRT_DEVICE_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xleaf.h
>>>> b/drivers/fpga/xrt/include/xleaf.h
>>>> new file mode 100644
>>>> index 000000000000..f065fc766e0f
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xleaf.h
>>>> @@ -0,0 +1,205 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Cheng Zhen <[email protected]>
>>>> + * Sonal Santan <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_XLEAF_H_
>>>> +#define _XRT_XLEAF_H_
>>>> +
>>>> +#include <linux/mod_devicetable.h>
>>>> +#include "xdevice.h"
>>>> +#include "subdev_id.h"
>>>> +#include "xroot.h"
>>>> +#include "events.h"
>>>> +
>>>> +/* All subdev drivers should use below common routines to print
>>>> out msg. */
>>>> +#define DEV(xdev) (&(xdev)->dev)
>>>> +#define DEV_PDATA(xdev) \
>>>> + ((struct xrt_subdev_platdata *)xrt_get_xdev_data(DEV(xdev)))
>>>> +#define DEV_FILE_OPS(xdev) \
>>>> + (&(to_xrt_drv((xdev)->dev.driver))->file_ops)
>>>> +#define FMT_PRT(prt_fn, xdev, fmt, args...) \
>>>> + ({typeof(xdev) (_xdev) = (xdev); \
>>>> + prt_fn(DEV(_xdev), "%s %s: " fmt, \
>>>> + DEV_PDATA(_xdev)->xsp_root_name, __func__, ##args); })
>>>> +#define xrt_err(xdev, fmt, args...) FMT_PRT(dev_err, xdev, fmt,
>>>> ##args)
>>>> +#define xrt_warn(xdev, fmt, args...) FMT_PRT(dev_warn, xdev, fmt,
>>>> ##args)
>>>> +#define xrt_info(xdev, fmt, args...) FMT_PRT(dev_info, xdev, fmt,
>>>> ##args)
>>>> +#define xrt_dbg(xdev, fmt, args...) FMT_PRT(dev_dbg, xdev, fmt,
>>>> ##args)
>>>> +
>>>> +#define XRT_DEFINE_REGMAP_CONFIG(config_name) \
>>>> + static const struct regmap_config config_name = {?????????????? \
>>>> + .reg_bits = 32, \
>>>> + .val_bits = 32, \
>>>> + .reg_stride = 4, \
>>>> + .max_register = 0x1000, \
>>>> + }
>>>> +
>>>> +enum {
>>>> + /* Starting cmd for common leaf cmd implemented by all leaves. */
>>>> + XRT_XLEAF_COMMON_BASE = 0,
>>>> + /* Starting cmd for leaves' specific leaf cmds. */
>>>> + XRT_XLEAF_CUSTOM_BASE = 64,
>>>> +};
>>>> +
>>>> +enum xrt_xleaf_common_leaf_cmd {
>>>> + XRT_XLEAF_EVENT = XRT_XLEAF_COMMON_BASE,
>>>> +};
>>>> +
>>>> +/*
>>>> + * Partially initialized by the parent driver, then, passed in as
>>>> subdev driver's
>>>> + * platform data when creating subdev driver instance by calling
>>>> platform
>>>> + * device register API (xrt_device_register_data() or the likes).
>>>> + *
>>>> + * Once device register API returns, platform driver framework
>>>> makes a copy of
>>>> + * this buffer and maintains its life cycle. The content of the
>>>> buffer is
>>>> + * completely owned by subdev driver.
>>>> + *
>>>> + * Thus, parent driver should be very careful when it touches this
>>>> buffer
>>>> + * again once it's handed over to subdev driver. And the data
>>>> structure
>>>> + * should not contain pointers pointing to buffers that is managed by
>>>> + * other or parent drivers since it could have been freed before
>>>> platform
>>>> + * data buffer is freed by platform driver framework.
>>>> + */
>>>> +struct xrt_subdev_platdata {
>>>> + /*
>>>> + * Per driver instance callback. The xdev points to the instance.
>>>> + * Should always be defined for subdev driver to get service
>>>> from root.
>>>> + */
>>>> + xrt_subdev_root_cb_t xsp_root_cb;
>>>> + void *xsp_root_cb_arg;
>>>> +
>>>> + /* Something to associate w/ root for msg printing. */
>>>> + const char *xsp_root_name;
>>>> +
>>>> + /*
>>>> + * Char dev support for this subdev instance.
>>>> + * Initialized by subdev driver.
>>>> + */
>>>> + struct cdev xsp_cdev;
>>>> + struct device *xsp_sysdev;
>>>> + struct mutex xsp_devnode_lock; /* devnode lock */
>>>> + struct completion xsp_devnode_comp;
>>>> + int xsp_devnode_ref;
>>>> + bool xsp_devnode_online;
>>>> + bool xsp_devnode_excl;
>>>> +
>>>> + /*
>>>> + * Subdev driver specific init data. The buffer should be embedded
>>>> + * in this data structure buffer after dtb, so that it can be
>>>> freed
>>>> + * together with platform data.
>>>> + */
>>>> + loff_t xsp_priv_off; /* Offset into this platform data buffer. */
>>>> + size_t xsp_priv_len;
>>>> +
>>>> + /*
>>>> + * Populated by parent driver to describe the device tree for
>>>> + * the subdev driver to handle. Should always be last one since
>>>> it's
>>>> + * of variable length.
>>>> + */
>>>> + bool xsp_dtb_valid;
>>>> + char xsp_dtb[0];
>>>> +};
>>>> +
>>>> +struct subdev_match_arg {
>>>> + enum xrt_subdev_id id;
>>>> + int instance;
>>>> +};
>>>> +
>>>> +bool xleaf_has_endpoint(struct xrt_device *xdev, const char
>>>> *endpoint_name);
>>>> +struct xrt_device *xleaf_get_leaf(struct xrt_device *xdev,
>>>> + xrt_subdev_match_t cb, void *arg);
>>>> +
>>>> +static inline bool subdev_match(enum xrt_subdev_id id, struct
>>>> xrt_device *xdev, void *arg)
>>>> +{
>>>> + const struct subdev_match_arg *a = (struct subdev_match_arg *)arg;
>>>> + int instance = a->instance;
>>>> +
>>>> + if (id != a->id)
>>>> + return false;
>>>> + if (instance != xdev->instance && instance !=
>>>> XRT_INVALID_DEVICE_INST)
>>>> + return false;
>>>> + return true;
>>>> +}
>>>> +
>>>> +static inline bool xrt_subdev_match_epname(enum xrt_subdev_id id,
>>>> + struct xrt_device *xdev,
>>>> void *arg)
>>>> +{
>>>> + return xleaf_has_endpoint(xdev, arg);
>>>> +}
>>>> +
>>>> +static inline struct xrt_device *
>>>> +xleaf_get_leaf_by_id(struct xrt_device *xdev,
>>>> + enum xrt_subdev_id id, int instance)
>>>> +{
>>>> + struct subdev_match_arg arg = { id, instance };
>>>> +
>>>> + return xleaf_get_leaf(xdev, subdev_match, &arg);
>>>> +}
>>>> +
>>>> +static inline struct xrt_device *
>>>> +xleaf_get_leaf_by_epname(struct xrt_device *xdev, const char *name)
>>>> +{
>>>> + return xleaf_get_leaf(xdev, xrt_subdev_match_epname, (void
>>>> *)name);
>>>> +}
>>>> +
>>>> +static inline int xleaf_call(struct xrt_device *tgt, u32 cmd, void
>>>> *arg)
>>>> +{
>>>> + return (to_xrt_drv(tgt->dev.driver)->leaf_call)(tgt, cmd, arg);
>>>> +}
>>>> +
>>>> +int xleaf_broadcast_event(struct xrt_device *xdev, enum xrt_events
>>>> evt, bool async);
>>>> +int xleaf_create_group(struct xrt_device *xdev, char *dtb);
>>>> +int xleaf_destroy_group(struct xrt_device *xdev, int instance);
>>>> +void xleaf_get_root_res(struct xrt_device *xdev, u32 region_id,
>>>> struct resource **res);
>>>> +void xleaf_get_root_id(struct xrt_device *xdev, unsigned short
>>>> *vendor, unsigned short *device,
>>>> + unsigned short *subvendor, unsigned short
>>>> *subdevice);
>>>> +void xleaf_hot_reset(struct xrt_device *xdev);
>>>> +int xleaf_put_leaf(struct xrt_device *xdev, struct xrt_device *leaf);
>>>> +struct device *xleaf_register_hwmon(struct xrt_device *xdev, const
>>>> char *name, void *drvdata,
>>>> + const struct attribute_group **grps);
>>>> +void xleaf_unregister_hwmon(struct xrt_device *xdev, struct device
>>>> *hwmon);
>>>> +int xleaf_wait_for_group_bringup(struct xrt_device *xdev);
>>>> +
>>>> +/*
>>>> + * Character device helper APIs for use by leaf drivers
>>>> + */
>>>> +static inline bool xleaf_devnode_enabled(struct xrt_device *xdev)
>>>> +{
>>>> + return DEV_FILE_OPS(xdev)->xsf_ops.open;
>>>> +}
>>>> +
>>>> +int xleaf_devnode_create(struct xrt_device *xdev,
>>>> + const char *file_name, const char *inst_name);
>>>> +void xleaf_devnode_destroy(struct xrt_device *xdev);
>>>> +
>>>> +struct xrt_device *xleaf_devnode_open_excl(struct inode *inode);
>>>> +struct xrt_device *xleaf_devnode_open(struct inode *inode);
>>>> +void xleaf_devnode_close(struct inode *inode);
>>>> +
>>>> +/* Module's init/fini routines for leaf driver in xrt-lib module */
>>>> +#define XRT_LEAF_INIT_FINI_FUNC(name) \
>>>> +void name##_leaf_init_fini(bool
>>>> init) \
>>>> +{????????????????????????????????????????????????????????????????? \
>>>> + if (init) \
>>>> + xrt_register_driver(&xrt_##name##_driver); \
>>>> + else \
>>>> + xrt_unregister_driver(&xrt_##name##_driver); \
>>>> +}
>>>> +
>>>> +/* Module's init/fini routines for leaf driver in xrt-lib module */
>>>> +void group_leaf_init_fini(bool init);
>>>> +void vsec_leaf_init_fini(bool init);
>>>> +void devctl_leaf_init_fini(bool init);
>>>> +void axigate_leaf_init_fini(bool init);
>>>> +void icap_leaf_init_fini(bool init);
>>>> +void calib_leaf_init_fini(bool init);
>>>> +void clkfreq_leaf_init_fini(bool init);
>>>> +void clock_leaf_init_fini(bool init);
>>>> +void ucs_leaf_init_fini(bool init);
>>>> +
>>>> +#endif /* _XRT_LEAF_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xleaf/clkfreq.h
>>>> b/drivers/fpga/xrt/include/xleaf/clkfreq.h
>>>> new file mode 100644
>>>> index 000000000000..005441d5df78
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xleaf/clkfreq.h
>>>> @@ -0,0 +1,21 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Lizhi Hou <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_CLKFREQ_H_
>>>> +#define _XRT_CLKFREQ_H_
>>>> +
>>>> +#include "xleaf.h"
>>>> +
>>>> +/*
>>>> + * CLKFREQ driver leaf calls.
>>>> + */
>>>> +enum xrt_clkfreq_leaf_cmd {
>>>> + XRT_CLKFREQ_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in
>>>> xleaf.h */
>>>> +};
>>>> +
>>>> +#endif /* _XRT_CLKFREQ_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xleaf/clock.h
>>>> b/drivers/fpga/xrt/include/xleaf/clock.h
>>>> new file mode 100644
>>>> index 000000000000..1379e24fa5d0
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xleaf/clock.h
>>>> @@ -0,0 +1,29 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Lizhi Hou <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_CLOCK_H_
>>>> +#define _XRT_CLOCK_H_
>>>> +
>>>> +#include "xleaf.h"
>>>> +#include <linux/fpga_xrt.h>
>>>> +
>>>> +/*
>>>> + * CLOCK driver leaf calls.
>>>> + */
>>>> +enum xrt_clock_leaf_cmd {
>>>> + XRT_CLOCK_SET = XRT_XLEAF_CUSTOM_BASE, /* See comments in
>>>> xleaf.h */
>>>> + XRT_CLOCK_GET,
>>>> + XRT_CLOCK_VERIFY,
>>>> +};
>>>> +
>>>> +struct xrt_clock_get {
>>>> + u16 freq;
>>>> + u32 freq_cnter;
>>>> +};
>>>> +
>>>> +#endif /* _XRT_CLOCK_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>>> b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>>> new file mode 100644
>>>> index 000000000000..c44ae30f939a
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>>>> @@ -0,0 +1,28 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Cheng Zhen <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_DDR_CALIBRATION_H_
>>>> +#define _XRT_DDR_CALIBRATION_H_
>>>> +
>>>> +#include "xleaf.h"
>>>> +#include <linux/fpga-xrt.h>
>>>> +
>>>> +/*
>>>> + * Memory calibration driver leaf calls.
>>>> + */
>>>> +enum xrt_calib_results {
>>>> + XRT_CALIB_UNKNOWN = 0,
>>>> + XRT_CALIB_SUCCEEDED,
>>>> + XRT_CALIB_FAILED,
>>>> +};
>>>> +
>>>> +enum xrt_calib_leaf_cmd {
>>>> + XRT_CALIB_RESULT = XRT_XLEAF_CUSTOM_BASE, /* See comments in
>>>> xleaf.h */
>>>> +};
>>>> +
>>>> +#endif /* _XRT_DDR_CALIBRATION_H_ */
>>>> diff --git a/drivers/fpga/xrt/include/xleaf/devctl.h
>>>> b/drivers/fpga/xrt/include/xleaf/devctl.h
>>>> new file mode 100644
>>>> index 000000000000..b97f3b6d9326
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/include/xleaf/devctl.h
>>>> @@ -0,0 +1,40 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Lizhi Hou <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _XRT_DEVCTL_H_
>>>> +#define _XRT_DEVCTL_H_
>>>> +
>>>> +#include "xleaf.h"
>>>> +
>>>> +/*
>>>> + * DEVCTL driver leaf calls.
>>>> + */
>>>> +enum xrt_devctl_leaf_cmd {
>>>> + XRT_DEVCTL_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in
>>>> xleaf.h */
>>>> +};
>>>> +
>>>> +enum xrt_devctl_id {
>>>> + XRT_DEVCTL_ROM_UUID = 0,
>>>> + XRT_DEVCTL_DDR_CALIB,
>>>> + XRT_DEVCTL_GOLDEN_VER,
>>>> + XRT_DEVCTL_MAX
>>>> +};
>>>> +
>>>> +struct xrt_devctl_rw {
>>>> + u32 xdr_id;
>>>> + void *xdr_buf;
>>>> + u32 xdr_len;
>>>> + u32 xdr_offset;
>>>> +};
>>>> +
>>>> +struct xrt_devctl_intf_uuid {
>>>> + u32 uuid_num;
>>>> + uuid_t *uuids;
>>>> +};
>>>> +
>>>> +#endif /* _XRT_DEVCTL_H_ */
>>>> diff --git a/drivers/fpga/xrt/lib/lib-drv.c
>>>> b/drivers/fpga/xrt/lib/lib-drv.c
>>>> new file mode 100644
>>>> index 000000000000..7d21ef565651
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/lib/lib-drv.c
>>>> @@ -0,0 +1,318 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Cheng Zhen <[email protected]>
>>>> + * Lizhi Hou <[email protected]>
>>>> + */
>>>> +
>>>> +#include <linux/module.h>
>>>> +#include <linux/vmalloc.h>
>>>> +#include <linux/slab.h>
>>>> +#include "xleaf.h"
>>>> +#include "xroot.h"
>>>> +#include "lib-drv.h"
>>>> +
>>>> +#define XRT_IPLIB_MODULE_NAME "xrt-lib"
>>> ok
>>>> +#define XRT_DRVNAME(drv) ((drv)->driver.name)
>>>> +
>>>> +#define XRT_SUBDEV_ID_SHIFT 16
>>>> +#define XRT_SUBDEV_ID_MASK ((1 << XRT_SUBDEV_ID_SHIFT) - 1)
>>>> +
>>>> +struct xrt_find_drv_data {
>>>> + enum xrt_subdev_id id;
>>>> + struct xrt_driver *xdrv;
>>>> +};
>>>> +
>>>> +struct class *xrt_class;
>>>> +static DEFINE_IDA(xrt_device_ida);
>>>> +
>>>> +static inline u32 xrt_instance_to_id(enum xrt_subdev_id id, u32
>>>> instance)
>>>> +{
>>>> + return (id << XRT_SUBDEV_ID_SHIFT) | instance;
>>>> +}
>>>> +
>>>> +static inline u32 xrt_id_to_instance(u32 id)
>>>> +{
>>>> + return id & XRT_SUBDEV_ID_MASK;
>>> ok
>>>> +}
>>>> +
>>>> +static int xrt_bus_match(struct device *dev, struct device_driver
>>>> *drv)
>>>> +{
>>>> + struct xrt_device *xdev = to_xrt_dev(dev);
>>>> + struct xrt_driver *xdrv = to_xrt_drv(drv);
>>>> +
>>>> + if (xdev->subdev_id == xdrv->subdev_id)
>>>> + return 1;
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static int xrt_bus_probe(struct device *dev)
>>>> +{
>>>> + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
>>>> + struct xrt_device *xdev = to_xrt_dev(dev);
>>>> +
>>>> + return xdrv->probe(xdev);
>>>> +}
>>>> +
>>>> +static int xrt_bus_remove(struct device *dev)
>>>> +{
>>>> + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
>>>> + struct xrt_device *xdev = to_xrt_dev(dev);
>>>> +
>>>> + if (xdrv->remove)
>>>> + xdrv->remove(xdev);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +struct bus_type xrt_bus_type = {
>>>> + .name = "xrt",
>>>> + .match = xrt_bus_match,
>>>> + .probe = xrt_bus_probe,
>>>> + .remove = xrt_bus_remove,
>>>> +};
>>>> +
>>>> +int xrt_register_driver(struct xrt_driver *drv)
>>>> +{
>>>> + const char *drvname = XRT_DRVNAME(drv);
>>>> + int rc = 0;
>>>> +
>>>> + /* Initialize dev_t for char dev node. */
>>>> + if (drv->file_ops.xsf_ops.open) {
>>>> + rc = alloc_chrdev_region(&drv->file_ops.xsf_dev_t, 0,
>>>> + XRT_MAX_DEVICE_NODES, drvname);
>>>> + if (rc) {
>>>> + pr_err("failed to alloc dev minor for %s:
>>>> %d\n", drvname, rc);
>>>> + return rc;
>>>> + }
>>>> + } else {
>>>> + drv->file_ops.xsf_dev_t = (dev_t)-1;
>>>> + }
>>>> +
>>>> + drv->driver.owner = THIS_MODULE;
>>>> + drv->driver.bus = &xrt_bus_type;
>>>> +
>>>> + rc = driver_register(&drv->driver);
>>>> + if (rc) {
>>>> + pr_err("register %s xrt driver failed\n", drvname);
>>>> + if (drv->file_ops.xsf_dev_t != (dev_t)-1) {
>>>> + unregister_chrdev_region(drv->file_ops.xsf_dev_t,
>>>> + XRT_MAX_DEVICE_NODES);
>>>> + }
>>>> + return rc;
>>>> + }
>>>> +
>>>> + pr_info("%s registered successfully\n", drvname);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(xrt_register_driver);
>>>> +
>>>> +void xrt_unregister_driver(struct xrt_driver *drv)
>>>> +{
>>>> + driver_unregister(&drv->driver);
>>>> +
>>>> + if (drv->file_ops.xsf_dev_t != (dev_t)-1)
>>>> + unregister_chrdev_region(drv->file_ops.xsf_dev_t,
>>>> XRT_MAX_DEVICE_NODES);
>>> ok
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(xrt_unregister_driver);
>>>> +
>>>> +static int __find_driver(struct device_driver *drv, void *_data)
>>>> +{
>>>> + struct xrt_driver *xdrv = to_xrt_drv(drv);
>>>> + struct xrt_find_drv_data *data = _data;
>>>> +
>>>> + if (xdrv->subdev_id == data->id) {
>>>> + data->xdrv = xdrv;
>>>> + return 1;
>>>> + }
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +const char *xrt_drv_name(enum xrt_subdev_id id)
>>>> +{
>>>> + struct xrt_find_drv_data data = { 0 };
>>>> +
>>>> + data.id = id;
>>>> + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
>>>> +
>>>> + if (data.xdrv)
>>>> + return XRT_DRVNAME(data.xdrv);
>>>> +
>>>> + return NULL;
>>>> +}
>>>> +
>>>> +static int xrt_drv_get_instance(enum xrt_subdev_id id)
>>>> +{
>>>> + int ret;
>>>> +
>>>> + ret = ida_alloc_range(&xrt_device_ida, xrt_instance_to_id(id, 0),
>>>> + xrt_instance_to_id(id,
>>>> XRT_MAX_DEVICE_NODES),
>>>> + GFP_KERNEL);
>>>> + if (ret < 0)
>>>> + return ret;
>>>> +
>>>> + return xrt_id_to_instance((u32)ret);
>>>> +}
>>>> +
>>>> +static void xrt_drv_put_instance(enum xrt_subdev_id id, int instance)
>>>> +{
>>>> + ida_free(&xrt_device_ida, xrt_instance_to_id(id, instance));
>>>> +}
>>>> +
>>>> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id
>>>> id)
>>>> +{
>>>> + struct xrt_find_drv_data data = { 0 };
>>>> +
>>>> + data.id = id;
>>>> + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
>>>> +
>>>> + if (data.xdrv)
>>>> + return data.xdrv->endpoints;
>>>> +
>>>> + return NULL;
>>>> +}
>>>> +
>>>> +static void xrt_device_release(struct device *dev)
>>>> +{
>>>> + struct xrt_device *xdev = container_of(dev, struct xrt_device,
>>>> dev);
>>>> +
>>>> + kfree(xdev);
>>>> +}
>>>> +
>>>> +void xrt_device_unregister(struct xrt_device *xdev)
>>>> +{
>>>> + if (xdev->state == XRT_DEVICE_STATE_ADDED)
>>>> + device_del(&xdev->dev);
>>>> +
>>>> + vfree(xdev->sdev_data);
>>> cleanup of sdev_data here
>>>
>>> fail: label in xrt_device_register is ok.
>>>
>>>> + kfree(xdev->resource);
>>>> +
>>>> + if (xdev->instance != XRT_INVALID_DEVICE_INST)
>>>> + xrt_drv_put_instance(xdev->subdev_id, xdev->instance);
>>>> +
>>>> + if (xdev->dev.release == xrt_device_release)
>>>> + put_device(&xdev->dev);
>>>> +}
>>>> +
>>>> +struct xrt_device *
>>>> +xrt_device_register(struct device *parent, u32 id,
>>>> + struct resource *res, u32 res_num,
>>>> + void *pdata, size_t data_sz)
>>>> +{
>>>> + struct xrt_device *xdev = NULL;
>>>> + int ret;
>>>> +
>>>> + xdev = kzalloc(sizeof(*xdev), GFP_KERNEL);
>>>> + if (!xdev)
>>>> + return xdev;
>>> ok
>>>> + xdev->instance = XRT_INVALID_DEVICE_INST;
>>>> +
>>>> + /* Obtain dev instance number. */
>>>> + ret = xrt_drv_get_instance(id);
>>>> + if (ret < 0) {
>>>> + dev_err(parent, "failed get instance, ret %d", ret);
>>>> + goto fail;
>>>> + }
>>>> +
>>>> + xdev->instance = ret;
>>>> + xdev->name = xrt_drv_name(id);
>>>> + xdev->subdev_id = id;
>>>> + device_initialize(&xdev->dev);
>>>> + xdev->dev.release = xrt_device_release;
>>>> + xdev->dev.parent = parent;
>>>> +
>>>> + xdev->dev.bus = &xrt_bus_type;
>>>> + dev_set_name(&xdev->dev, "%s.%d", xdev->name, xdev->instance);
>>>> +
>>>> + xdev->num_resources = res_num;
>>>> + xdev->resource = kmemdup(res, sizeof(*res) * res_num, GFP_KERNEL);
>>>> + if (!xdev->resource)
>>>> + goto fail;
>>>> +
>>>> + xdev->sdev_data = vzalloc(data_sz);
>>>> + if (!xdev->sdev_data)
>>>> + goto fail;
>>>> +
>>>> + memcpy(xdev->sdev_data, pdata, data_sz);
>>>> +
>>>> + ret = device_add(&xdev->dev);
>>>> + if (ret) {
>>>> + dev_err(parent, "failed add device, ret %d", ret);
>>>> + goto fail;
>>>> + }
>>>> + xdev->state = XRT_DEVICE_STATE_ADDED;
>>>> +
>>>> + return xdev;
>>>> +
>>>> +fail:
>>>> + xrt_device_unregister(xdev);
>>>> + kfree(xdev);
>>>> +
>>>> + return NULL;
>>>> +}
>>>> +
>>>> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32
>>>> type, u32 num)
>>>> +{
>>>> + u32 i;
>>>> +
>>>> + for (i = 0; i < xdev->num_resources; i++) {
>>>> + struct resource *r = &xdev->resource[i];
>>>> +
>>>> + if (type == resource_type(r) && num-- == 0)
>>>> + return r;
>>>> + }
>>>> + return NULL;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Leaf driver's module init/fini callbacks. This is not a open
>>>> infrastructure for dynamic
>>>> + * plugging in drivers. All drivers should be statically added.
>>>> + */
>>>> +static void (*leaf_init_fini_cbs[])(bool) = {
>>>> + group_leaf_init_fini,
>>>> + axigate_leaf_init_fini,
>>>> + icap_leaf_init_fini,
>>>> +};
>>>> +
>>>> +static __init int xrt_lib_init(void)
>>>> +{
>>>> + int ret;
>>>> + int i;
>>>> +
>>>> + ret = bus_register(&xrt_bus_type);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + xrt_class = class_create(THIS_MODULE, XRT_IPLIB_MODULE_NAME);
>>>> + if (IS_ERR(xrt_class)) {
>>>> + bus_unregister(&xrt_bus_type);
>>>> + return PTR_ERR(xrt_class);
>>>> + }
>>>> +
>>>> + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
>>>> + leaf_init_fini_cbs[i](true);
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static __exit void xrt_lib_fini(void)
>>>> +{
>>>> + int i;
>>>> +
>>>> + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
>>>> + leaf_init_fini_cbs[i](false);
>>>> +
>>>> + class_destroy(xrt_class);
>>>> + bus_unregister(&xrt_bus_type);
>>>> +}
>>>> +
>>>> +module_init(xrt_lib_init);
>>>> +module_exit(xrt_lib_fini);
>>>> +
>>>> +MODULE_AUTHOR("XRT Team <[email protected]>");
>>>> +MODULE_DESCRIPTION("Xilinx Alveo IP Lib driver");
>>>> +MODULE_LICENSE("GPL v2");
>>>> diff --git a/drivers/fpga/xrt/lib/lib-drv.h
>>>> b/drivers/fpga/xrt/lib/lib-drv.h
>>>> new file mode 100644
>>>> index 000000000000..0276c28e009f
>>>> --- /dev/null
>>>> +++ b/drivers/fpga/xrt/lib/lib-drv.h
>>>> @@ -0,0 +1,21 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>>>> + *
>>>> + * Authors:
>>>> + * Cheng Zhen <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef _LIB_DRV_H_
>>>> +#define _LIB_DRV_H_
>>>> +
>>>> +#include <linux/device/class.h>
>>>> +#include <linux/device/bus.h>
>>>> +
>>>> +extern struct class *xrt_class;
>>>> +extern struct bus_type xrt_bus_type;
>>>> +
>>>> +const char *xrt_drv_name(enum xrt_subdev_id id);
>>>> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id
>>>> id);
>>>> +
>>>> +#endif /* _LIB_DRV_H_ */
>
On Mon, Sep 27, 2021 at 10:21:54PM +0000, Sonal Santan wrote:
> Hello Yilun,
>
> Welcome as a co-maintainer of the Linux FPGA subsystem! Looking forward to working with you.
>
> As you are aware, Xilinx has been working on upstreaming the XRT Alveo drivers to the Linux kernel. Thanks to the feedback from the reviewers over last several months, we have improved the design of our drivers. We are currently at V9 and awaiting feedback. Will appreciate your help with the review.
Yes, I'd like to have more time on FPGA subsystem and try to catch up on recent patchsets.
Thanks,
Yilun
>
> -Sonal
>
> ________________________________________
> From: Tom Rix <[email protected]>
> Sent: Tuesday, September 14, 2021 6:27 AM
> To: Lizhi Hou; Moritz Fischer; Wu, Hao; Greg KH
> Cc: [email protected]; [email protected]; Max Zhen; Sonal Santan; Yu Liu; Michal Simek; Stefano Stabellini; [email protected]; [email protected]; Max Zhen
> Subject: Re: [PATCH V9 XRT Alveo 04/14] fpga: xrt: xrt-lib driver manager
>
>
> On 9/10/21 2:34 PM, Lizhi Hou wrote:
> > Hi Moritz,
> >
> > (sending it again because rejected by some mail alias)
> >
> >
> > Did you get time to review XRT patch set? Please let us know if there
> > are more we need to change.
> > We are looking for your comments.
>
> This patchset has been outstanding for 6 weeks to address Moritz'
> comments for the v8 patchset of this single patch 4 of 14.
>
> The changes look good to me.
> https://lore.kernel.org/linux-fpga/[email protected]/
>
> Moritz, can you review the changes ?
>
>
> The xrt patchset starts to enable a whole class of general purpose
> datacenter fpga's.
>
> https://www.xilinx.com/products/boards-and-kits/alveo.html
>
> This would be a great feature for the fpga/ subsystem.
>
> Tom
>
>
> >
> > Thanks,
> > Lizhi
> >
> > On 8/21/21 9:44 AM, Moritz Fischer wrote:
> >> CAUTION: This message has originated from an External Source. Please
> >> use proper judgment and caution when opening attachments, clicking
> >> links, or responding to this email.
> >>
> >>
> >> On Thu, Aug 19, 2021 at 05:14:14AM -0700, Tom Rix wrote:
> >>> On 8/2/21 9:05 AM, Lizhi Hou wrote:
> >>>
> >>>> xrt-lib kernel module infrastructure code to register and manage all
> >>>> leaf driver modules.
> >>>>
> >>>> Signed-off-by: Sonal Santan <[email protected]>
> >>>> Signed-off-by: Max Zhen <[email protected]>
> >>>> Signed-off-by: Lizhi Hou <[email protected]>
> >>>> Reviewed-by: Tom Rix <[email protected]>
> >>> This was the only patch with requested changes in v8.
> >>>
> >>> All the changes from v8 have been made.
> >>>
> >>> They are itemized below with variations on 'ok'
> >>>
> >>> It is still Moritz's call to accept them.
> >> I'll be OOO till 9/4/21. I'll get to it after.
> >>
> >> - Moritz
> >>> Tom
> >>>
> >>>> ---
> >>>> drivers/fpga/xrt/include/subdev_id.h |? 39 +++
> >>>> drivers/fpga/xrt/include/xdevice.h | 141 ++++++++
> >>>> drivers/fpga/xrt/include/xleaf.h | 205 +++++++++++
> >>>> drivers/fpga/xrt/include/xleaf/clkfreq.h |? 21 ++
> >>>> drivers/fpga/xrt/include/xleaf/clock.h |? 29 ++
> >>>> .../fpga/xrt/include/xleaf/ddr_calibration.h |? 28 ++
> >>>> drivers/fpga/xrt/include/xleaf/devctl.h |? 40 +++
> >>>> drivers/fpga/xrt/lib/lib-drv.c | 318
> >>>> ++++++++++++++++++
> >>>> drivers/fpga/xrt/lib/lib-drv.h |? 21 ++
> >>>> 9 files changed, 842 insertions(+)
> >>>> create mode 100644 drivers/fpga/xrt/include/subdev_id.h
> >>>> create mode 100644 drivers/fpga/xrt/include/xdevice.h
> >>>> create mode 100644 drivers/fpga/xrt/include/xleaf.h
> >>>> create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
> >>>> create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
> >>>> create mode 100644 drivers/fpga/xrt/include/xleaf/ddr_calibration.h
> >>>> create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
> >>>> create mode 100644 drivers/fpga/xrt/lib/lib-drv.c
> >>>> create mode 100644 drivers/fpga/xrt/lib/lib-drv.h
> >>>>
> >>>> diff --git a/drivers/fpga/xrt/include/subdev_id.h
> >>>> b/drivers/fpga/xrt/include/subdev_id.h
> >>>> new file mode 100644
> >>>> index 000000000000..02df4b939a1b
> >>>> --- /dev/null
> >>>> +++ b/drivers/fpga/xrt/include/subdev_id.h
> >>>> @@ -0,0 +1,39 @@
> >>>> +/* SPDX-License-Identifier: GPL-2.0 */
> >>>> +/*
> >>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
> >>>> + *
> >>>> + * Authors:
> >>>> + * Cheng Zhen <[email protected]>
> >>>> + */
> >>>> +
> >>>> +#ifndef _XRT_SUBDEV_ID_H_
> >>>> +#define _XRT_SUBDEV_ID_H_
> >>>> +
> >>>> +/*
> >>>> + * Every subdev driver has an ID for others to refer to it. There
> >>>> can be multiple number of
> >>>> + * instances of a subdev driver. A <subdev_id, subdev_instance>
> >>>> tuple is a unique identification
> >>>> + * of a specific instance of a subdev driver.
> >>>> + */
> >>>> +enum xrt_subdev_id {
> >>>> + XRT_SUBDEV_INVALID = 0,
> >>>> + XRT_SUBDEV_GRP,
> >>>> + XRT_SUBDEV_VSEC,
> >>>> + XRT_SUBDEV_VSEC_GOLDEN,
> >>>> + XRT_SUBDEV_DEVCTL,
> >>>> + XRT_SUBDEV_AXIGATE,
> >>>> + XRT_SUBDEV_ICAP,
> >>>> + XRT_SUBDEV_TEST,
> >>>> + XRT_SUBDEV_MGMT_MAIN,
> >>>> + XRT_SUBDEV_QSPI,
> >>>> + XRT_SUBDEV_MAILBOX,
> >>>> + XRT_SUBDEV_CMC,
> >>>> + XRT_SUBDEV_CALIB,
> >>>> + XRT_SUBDEV_CLKFREQ,
> >>>> + XRT_SUBDEV_CLOCK,
> >>>> + XRT_SUBDEV_SRSR,
> >>>> + XRT_SUBDEV_UCS,
> >>>> + XRT_SUBDEV_NUM, /* Total number of subdevs. */
> >>>> + XRT_ROOT = -1, /* Special ID for root driver. */
> >>>> +};
> >>>> +
> >>>> +#endif /* _XRT_SUBDEV_ID_H_ */
> >>>> diff --git a/drivers/fpga/xrt/include/xdevice.h
> >>>> b/drivers/fpga/xrt/include/xdevice.h
> >>>> new file mode 100644
> >>>> index 000000000000..b40ebe98b54d
> >>>> --- /dev/null
> >>>> +++ b/drivers/fpga/xrt/include/xdevice.h
> >>>> @@ -0,0 +1,141 @@
> >>>> +/* SPDX-License-Identifier: GPL-2.0 */
> >>>> +/*
> >>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
> >>>> + *
> >>>> + * Authors:
> >>>> + * Lizhi Hou <[email protected]>
> >>>> + */
> >>>> +
> >>>> +#ifndef _XRT_DEVICE_H_
> >>>> +#define _XRT_DEVICE_H_
> >>>> +
> >>>> +#include <linux/fs.h>
> >>>> +#include <linux/cdev.h>
> >>>> +
> >>>> +#define XRT_MAX_DEVICE_NODES 128
> >>>> +#define XRT_INVALID_DEVICE_INST (XRT_MAX_DEVICE_NODES + 1)
> >>>> +
> >>>> +enum {
> >>>> + XRT_DEVICE_STATE_NONE = 0,
> >>>> + XRT_DEVICE_STATE_ADDED
> >>>> +};
> >>>> +
> >>>> +/*
> >>>> + * struct xrt_device - represent an xrt device on xrt bus
> >>>> + *
> >>>> + * dev: generic device interface.
> >>>> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
> >>> ok
> >>>> + * name: name of the xrt device.
> >>>> + * instance: instance of the xrt device. The xrt device with same
> >>>> id can have
> >>>> + * more than 1 instances.
> >>>> + * state: current state of the xrt device.
> >>>> + * num_resources: The total number of resource for the xrt device.
> >>>> + * resource: point to the xrt device resource array.
> >>>> + * sdev_data: private data pointer.
> >>>> + */
> >>>> +struct xrt_device {
> >>>> + struct device dev;
> >>>> + u32 subdev_id;
> >>>> + const char *name;
> >>>> + u32 instance;
> >>>> + u32 state;
> >>>> + u32 num_resources;
> >>>> + struct resource *resource;
> >>>> + void *sdev_data;
> >>>> +};
> >>>> +
> >>>> +/*
> >>>> + * If populated by xrt device driver, infra will handle the
> >>>> mechanics of
> >>>> + * char device (un)registration.
> >>>> + */
> >>>> +enum xrt_dev_file_mode {
> >>>> + /* Infra create cdev, default file name */
> >>>> + XRT_DEV_FILE_DEFAULT = 0,
> >>>> + /* Infra create cdev, need to encode inst num in file name */
> >>>> + XRT_DEV_FILE_MULTI_INST,
> >>>> + /* No auto creation of cdev by infra, leaf handles it by itself */
> >>>> + XRT_DEV_FILE_NO_AUTO,
> >>>> +};
> >>>> +
> >>>> +struct xrt_dev_file_ops {
> >>>> + const struct file_operations xsf_ops;
> >>>> + dev_t xsf_dev_t;
> >>>> + const char *xsf_dev_name;
> >>>> + enum xrt_dev_file_mode xsf_mode;
> >>>> +};
> >>>> +
> >>>> +/*
> >>>> + * this struct define the endpoints belong to the same xrt device
> >>>> + * ep_name: endpoint name
> >>>> + * compat: compatible string
> >>> ok
> >>>> + */
> >>>> +struct xrt_dev_ep_names {
> >>>> + const char *ep_name;
> >>>> + const char *compat;
> >>>> +};
> >>>> +
> >>>> +struct xrt_dev_endpoints {
> >>>> + struct xrt_dev_ep_names *xse_names;
> >>>> + /* minimum number of endpoints to support the subdevice */
> >>>> + u32 xse_min_ep;
> >>>> +};
> >>>> +
> >>>> +/*
> >>>> + * struct xrt_driver - represent a xrt device driver
> >>>> + *
> >>>> + * driver: driver model structure.
> >>>> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
> >>>> + * file_ops: character device name and callbacks.
> >>>> + * probe: mandatory callback for device binding.
> >>>> + * remove: callback for device unbinding.
> >>>> + * leaf_call: callback for servicing other leaf drivers.
> >>> ok
> >>>> + */
> >>>> +struct xrt_driver {
> >>>> + struct device_driver driver;
> >>>> + u32 subdev_id;
> >>>> + struct xrt_dev_file_ops file_ops;
> >>>> + struct xrt_dev_endpoints *endpoints;
> >>>> +
> >>>> + /*
> >>>> + * Subdev driver callbacks populated by subdev driver.
> >>>> + */
> >>>> + int (*probe)(struct xrt_device *xrt_dev);
> >>>> + void (*remove)(struct xrt_device *xrt_dev);
> >>>> + /*
> >>>> + * If leaf_call is defined, these are called by other leaf
> >>>> drivers.
> >>>> + * Note that root driver may call into leaf_call of a group
> >>>> driver.
> >>>> + */
> >>>> + int (*leaf_call)(struct xrt_device *xrt_dev, u32 cmd, void *arg);
> >>>> +};
> >>>> +
> >>>> +#define to_xrt_dev(d) container_of(d, struct xrt_device, dev)
> >>>> +#define to_xrt_drv(d) container_of(d, struct xrt_driver, driver)
> >>>> +
> >>>> +static inline void *xrt_get_drvdata(const struct xrt_device *xdev)
> >>>> +{
> >>>> + return dev_get_drvdata(&xdev->dev);
> >>>> +}
> >>>> +
> >>>> +static inline void xrt_set_drvdata(struct xrt_device *xdev, void
> >>>> *data)
> >>>> +{
> >>>> + dev_set_drvdata(&xdev->dev, data);
> >>>> +}
> >>>> +
> >>>> +static inline void *xrt_get_xdev_data(struct device *dev)
> >>>> +{
> >>>> + struct xrt_device *xdev = to_xrt_dev(dev);
> >>>> +
> >>>> + return xdev->sdev_data;
> >>>> +}
> >>>> +
> >>>> +struct xrt_device *
> >>>> +xrt_device_register(struct device *parent, u32 id,
> >>>> + struct resource *res, u32 res_num,
> >>>> + void *pdata, size_t data_sz);
> >>>> +void xrt_device_unregister(struct xrt_device *xdev);
> >>>> +int xrt_register_driver(struct xrt_driver *drv);
> >>>> +void xrt_unregister_driver(struct xrt_driver *drv);
> >>>> +void *xrt_get_xdev_data(struct device *dev);
> >>>> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32
> >>>> type, u32 num);
> >>>> +
> >>>> +#endif /* _XRT_DEVICE_H_ */
> >>>> diff --git a/drivers/fpga/xrt/include/xleaf.h
> >>>> b/drivers/fpga/xrt/include/xleaf.h
> >>>> new file mode 100644
> >>>> index 000000000000..f065fc766e0f
> >>>> --- /dev/null
> >>>> +++ b/drivers/fpga/xrt/include/xleaf.h
> >>>> @@ -0,0 +1,205 @@
> >>>> +/* SPDX-License-Identifier: GPL-2.0 */
> >>>> +/*
> >>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
> >>>> + *
> >>>> + * Authors:
> >>>> + * Cheng Zhen <[email protected]>
> >>>> + * Sonal Santan <[email protected]>
> >>>> + */
> >>>> +
> >>>> +#ifndef _XRT_XLEAF_H_
> >>>> +#define _XRT_XLEAF_H_
> >>>> +
> >>>> +#include <linux/mod_devicetable.h>
> >>>> +#include "xdevice.h"
> >>>> +#include "subdev_id.h"
> >>>> +#include "xroot.h"
> >>>> +#include "events.h"
> >>>> +
> >>>> +/* All subdev drivers should use below common routines to print
> >>>> out msg. */
> >>>> +#define DEV(xdev) (&(xdev)->dev)
> >>>> +#define DEV_PDATA(xdev) \
> >>>> + ((struct xrt_subdev_platdata *)xrt_get_xdev_data(DEV(xdev)))
> >>>> +#define DEV_FILE_OPS(xdev) \
> >>>> + (&(to_xrt_drv((xdev)->dev.driver))->file_ops)
> >>>> +#define FMT_PRT(prt_fn, xdev, fmt, args...) \
> >>>> + ({typeof(xdev) (_xdev) = (xdev); \
> >>>> + prt_fn(DEV(_xdev), "%s %s: " fmt, \
> >>>> + DEV_PDATA(_xdev)->xsp_root_name, __func__, ##args); })
> >>>> +#define xrt_err(xdev, fmt, args...) FMT_PRT(dev_err, xdev, fmt,
> >>>> ##args)
> >>>> +#define xrt_warn(xdev, fmt, args...) FMT_PRT(dev_warn, xdev, fmt,
> >>>> ##args)
> >>>> +#define xrt_info(xdev, fmt, args...) FMT_PRT(dev_info, xdev, fmt,
> >>>> ##args)
> >>>> +#define xrt_dbg(xdev, fmt, args...) FMT_PRT(dev_dbg, xdev, fmt,
> >>>> ##args)
> >>>> +
> >>>> +#define XRT_DEFINE_REGMAP_CONFIG(config_name) \
> >>>> + static const struct regmap_config config_name = {?????????????? \
> >>>> + .reg_bits = 32, \
> >>>> + .val_bits = 32, \
> >>>> + .reg_stride = 4, \
> >>>> + .max_register = 0x1000, \
> >>>> + }
> >>>> +
> >>>> +enum {
> >>>> + /* Starting cmd for common leaf cmd implemented by all leaves. */
> >>>> + XRT_XLEAF_COMMON_BASE = 0,
> >>>> + /* Starting cmd for leaves' specific leaf cmds. */
> >>>> + XRT_XLEAF_CUSTOM_BASE = 64,
> >>>> +};
> >>>> +
> >>>> +enum xrt_xleaf_common_leaf_cmd {
> >>>> + XRT_XLEAF_EVENT = XRT_XLEAF_COMMON_BASE,
> >>>> +};
> >>>> +
> >>>> +/*
> >>>> + * Partially initialized by the parent driver, then, passed in as
> >>>> subdev driver's
> >>>> + * platform data when creating subdev driver instance by calling
> >>>> platform
> >>>> + * device register API (xrt_device_register_data() or the likes).
> >>>> + *
> >>>> + * Once device register API returns, platform driver framework
> >>>> makes a copy of
> >>>> + * this buffer and maintains its life cycle. The content of the
> >>>> buffer is
> >>>> + * completely owned by subdev driver.
> >>>> + *
> >>>> + * Thus, parent driver should be very careful when it touches this
> >>>> buffer
> >>>> + * again once it's handed over to subdev driver. And the data
> >>>> structure
> >>>> + * should not contain pointers pointing to buffers that is managed by
> >>>> + * other or parent drivers since it could have been freed before
> >>>> platform
> >>>> + * data buffer is freed by platform driver framework.
> >>>> + */
> >>>> +struct xrt_subdev_platdata {
> >>>> + /*
> >>>> + * Per driver instance callback. The xdev points to the instance.
> >>>> + * Should always be defined for subdev driver to get service
> >>>> from root.
> >>>> + */
> >>>> + xrt_subdev_root_cb_t xsp_root_cb;
> >>>> + void *xsp_root_cb_arg;
> >>>> +
> >>>> + /* Something to associate w/ root for msg printing. */
> >>>> + const char *xsp_root_name;
> >>>> +
> >>>> + /*
> >>>> + * Char dev support for this subdev instance.
> >>>> + * Initialized by subdev driver.
> >>>> + */
> >>>> + struct cdev xsp_cdev;
> >>>> + struct device *xsp_sysdev;
> >>>> + struct mutex xsp_devnode_lock; /* devnode lock */
> >>>> + struct completion xsp_devnode_comp;
> >>>> + int xsp_devnode_ref;
> >>>> + bool xsp_devnode_online;
> >>>> + bool xsp_devnode_excl;
> >>>> +
> >>>> + /*
> >>>> + * Subdev driver specific init data. The buffer should be embedded
> >>>> + * in this data structure buffer after dtb, so that it can be
> >>>> freed
> >>>> + * together with platform data.
> >>>> + */
> >>>> + loff_t xsp_priv_off; /* Offset into this platform data buffer. */
> >>>> + size_t xsp_priv_len;
> >>>> +
> >>>> + /*
> >>>> + * Populated by parent driver to describe the device tree for
> >>>> + * the subdev driver to handle. Should always be last one since
> >>>> it's
> >>>> + * of variable length.
> >>>> + */
> >>>> + bool xsp_dtb_valid;
> >>>> + char xsp_dtb[0];
> >>>> +};
> >>>> +
> >>>> +struct subdev_match_arg {
> >>>> + enum xrt_subdev_id id;
> >>>> + int instance;
> >>>> +};
> >>>> +
> >>>> +bool xleaf_has_endpoint(struct xrt_device *xdev, const char
> >>>> *endpoint_name);
> >>>> +struct xrt_device *xleaf_get_leaf(struct xrt_device *xdev,
> >>>> + xrt_subdev_match_t cb, void *arg);
> >>>> +
> >>>> +static inline bool subdev_match(enum xrt_subdev_id id, struct
> >>>> xrt_device *xdev, void *arg)
> >>>> +{
> >>>> + const struct subdev_match_arg *a = (struct subdev_match_arg *)arg;
> >>>> + int instance = a->instance;
> >>>> +
> >>>> + if (id != a->id)
> >>>> + return false;
> >>>> + if (instance != xdev->instance && instance !=
> >>>> XRT_INVALID_DEVICE_INST)
> >>>> + return false;
> >>>> + return true;
> >>>> +}
> >>>> +
> >>>> +static inline bool xrt_subdev_match_epname(enum xrt_subdev_id id,
> >>>> + struct xrt_device *xdev,
> >>>> void *arg)
> >>>> +{
> >>>> + return xleaf_has_endpoint(xdev, arg);
> >>>> +}
> >>>> +
> >>>> +static inline struct xrt_device *
> >>>> +xleaf_get_leaf_by_id(struct xrt_device *xdev,
> >>>> + enum xrt_subdev_id id, int instance)
> >>>> +{
> >>>> + struct subdev_match_arg arg = { id, instance };
> >>>> +
> >>>> + return xleaf_get_leaf(xdev, subdev_match, &arg);
> >>>> +}
> >>>> +
> >>>> +static inline struct xrt_device *
> >>>> +xleaf_get_leaf_by_epname(struct xrt_device *xdev, const char *name)
> >>>> +{
> >>>> + return xleaf_get_leaf(xdev, xrt_subdev_match_epname, (void
> >>>> *)name);
> >>>> +}
> >>>> +
> >>>> +static inline int xleaf_call(struct xrt_device *tgt, u32 cmd, void
> >>>> *arg)
> >>>> +{
> >>>> + return (to_xrt_drv(tgt->dev.driver)->leaf_call)(tgt, cmd, arg);
> >>>> +}
> >>>> +
> >>>> +int xleaf_broadcast_event(struct xrt_device *xdev, enum xrt_events
> >>>> evt, bool async);
> >>>> +int xleaf_create_group(struct xrt_device *xdev, char *dtb);
> >>>> +int xleaf_destroy_group(struct xrt_device *xdev, int instance);
> >>>> +void xleaf_get_root_res(struct xrt_device *xdev, u32 region_id,
> >>>> struct resource **res);
> >>>> +void xleaf_get_root_id(struct xrt_device *xdev, unsigned short
> >>>> *vendor, unsigned short *device,
> >>>> + unsigned short *subvendor, unsigned short
> >>>> *subdevice);
> >>>> +void xleaf_hot_reset(struct xrt_device *xdev);
> >>>> +int xleaf_put_leaf(struct xrt_device *xdev, struct xrt_device *leaf);
> >>>> +struct device *xleaf_register_hwmon(struct xrt_device *xdev, const
> >>>> char *name, void *drvdata,
> >>>> + const struct attribute_group **grps);
> >>>> +void xleaf_unregister_hwmon(struct xrt_device *xdev, struct device
> >>>> *hwmon);
> >>>> +int xleaf_wait_for_group_bringup(struct xrt_device *xdev);
> >>>> +
> >>>> +/*
> >>>> + * Character device helper APIs for use by leaf drivers
> >>>> + */
> >>>> +static inline bool xleaf_devnode_enabled(struct xrt_device *xdev)
> >>>> +{
> >>>> + return DEV_FILE_OPS(xdev)->xsf_ops.open;
> >>>> +}
> >>>> +
> >>>> +int xleaf_devnode_create(struct xrt_device *xdev,
> >>>> + const char *file_name, const char *inst_name);
> >>>> +void xleaf_devnode_destroy(struct xrt_device *xdev);
> >>>> +
> >>>> +struct xrt_device *xleaf_devnode_open_excl(struct inode *inode);
> >>>> +struct xrt_device *xleaf_devnode_open(struct inode *inode);
> >>>> +void xleaf_devnode_close(struct inode *inode);
> >>>> +
> >>>> +/* Module's init/fini routines for leaf driver in xrt-lib module */
> >>>> +#define XRT_LEAF_INIT_FINI_FUNC(name) \
> >>>> +void name##_leaf_init_fini(bool
> >>>> init) \
> >>>> +{????????????????????????????????????????????????????????????????? \
> >>>> + if (init) \
> >>>> + xrt_register_driver(&xrt_##name##_driver); \
> >>>> + else \
> >>>> + xrt_unregister_driver(&xrt_##name##_driver); \
> >>>> +}
> >>>> +
> >>>> +/* Module's init/fini routines for leaf driver in xrt-lib module */
> >>>> +void group_leaf_init_fini(bool init);
> >>>> +void vsec_leaf_init_fini(bool init);
> >>>> +void devctl_leaf_init_fini(bool init);
> >>>> +void axigate_leaf_init_fini(bool init);
> >>>> +void icap_leaf_init_fini(bool init);
> >>>> +void calib_leaf_init_fini(bool init);
> >>>> +void clkfreq_leaf_init_fini(bool init);
> >>>> +void clock_leaf_init_fini(bool init);
> >>>> +void ucs_leaf_init_fini(bool init);
> >>>> +
> >>>> +#endif /* _XRT_LEAF_H_ */
> >>>> diff --git a/drivers/fpga/xrt/include/xleaf/clkfreq.h
> >>>> b/drivers/fpga/xrt/include/xleaf/clkfreq.h
> >>>> new file mode 100644
> >>>> index 000000000000..005441d5df78
> >>>> --- /dev/null
> >>>> +++ b/drivers/fpga/xrt/include/xleaf/clkfreq.h
> >>>> @@ -0,0 +1,21 @@
> >>>> +/* SPDX-License-Identifier: GPL-2.0 */
> >>>> +/*
> >>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
> >>>> + *
> >>>> + * Authors:
> >>>> + * Lizhi Hou <[email protected]>
> >>>> + */
> >>>> +
> >>>> +#ifndef _XRT_CLKFREQ_H_
> >>>> +#define _XRT_CLKFREQ_H_
> >>>> +
> >>>> +#include "xleaf.h"
> >>>> +
> >>>> +/*
> >>>> + * CLKFREQ driver leaf calls.
> >>>> + */
> >>>> +enum xrt_clkfreq_leaf_cmd {
> >>>> + XRT_CLKFREQ_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in
> >>>> xleaf.h */
> >>>> +};
> >>>> +
> >>>> +#endif /* _XRT_CLKFREQ_H_ */
> >>>> diff --git a/drivers/fpga/xrt/include/xleaf/clock.h
> >>>> b/drivers/fpga/xrt/include/xleaf/clock.h
> >>>> new file mode 100644
> >>>> index 000000000000..1379e24fa5d0
> >>>> --- /dev/null
> >>>> +++ b/drivers/fpga/xrt/include/xleaf/clock.h
> >>>> @@ -0,0 +1,29 @@
> >>>> +/* SPDX-License-Identifier: GPL-2.0 */
> >>>> +/*
> >>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
> >>>> + *
> >>>> + * Authors:
> >>>> + * Lizhi Hou <[email protected]>
> >>>> + */
> >>>> +
> >>>> +#ifndef _XRT_CLOCK_H_
> >>>> +#define _XRT_CLOCK_H_
> >>>> +
> >>>> +#include "xleaf.h"
> >>>> +#include <linux/fpga_xrt.h>
> >>>> +
> >>>> +/*
> >>>> + * CLOCK driver leaf calls.
> >>>> + */
> >>>> +enum xrt_clock_leaf_cmd {
> >>>> + XRT_CLOCK_SET = XRT_XLEAF_CUSTOM_BASE, /* See comments in
> >>>> xleaf.h */
> >>>> + XRT_CLOCK_GET,
> >>>> + XRT_CLOCK_VERIFY,
> >>>> +};
> >>>> +
> >>>> +struct xrt_clock_get {
> >>>> + u16 freq;
> >>>> + u32 freq_cnter;
> >>>> +};
> >>>> +
> >>>> +#endif /* _XRT_CLOCK_H_ */
> >>>> diff --git a/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
> >>>> b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
> >>>> new file mode 100644
> >>>> index 000000000000..c44ae30f939a
> >>>> --- /dev/null
> >>>> +++ b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
> >>>> @@ -0,0 +1,28 @@
> >>>> +/* SPDX-License-Identifier: GPL-2.0 */
> >>>> +/*
> >>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
> >>>> + *
> >>>> + * Authors:
> >>>> + * Cheng Zhen <[email protected]>
> >>>> + */
> >>>> +
> >>>> +#ifndef _XRT_DDR_CALIBRATION_H_
> >>>> +#define _XRT_DDR_CALIBRATION_H_
> >>>> +
> >>>> +#include "xleaf.h"
> >>>> +#include <linux/fpga-xrt.h>
> >>>> +
> >>>> +/*
> >>>> + * Memory calibration driver leaf calls.
> >>>> + */
> >>>> +enum xrt_calib_results {
> >>>> + XRT_CALIB_UNKNOWN = 0,
> >>>> + XRT_CALIB_SUCCEEDED,
> >>>> + XRT_CALIB_FAILED,
> >>>> +};
> >>>> +
> >>>> +enum xrt_calib_leaf_cmd {
> >>>> + XRT_CALIB_RESULT = XRT_XLEAF_CUSTOM_BASE, /* See comments in
> >>>> xleaf.h */
> >>>> +};
> >>>> +
> >>>> +#endif /* _XRT_DDR_CALIBRATION_H_ */
> >>>> diff --git a/drivers/fpga/xrt/include/xleaf/devctl.h
> >>>> b/drivers/fpga/xrt/include/xleaf/devctl.h
> >>>> new file mode 100644
> >>>> index 000000000000..b97f3b6d9326
> >>>> --- /dev/null
> >>>> +++ b/drivers/fpga/xrt/include/xleaf/devctl.h
> >>>> @@ -0,0 +1,40 @@
> >>>> +/* SPDX-License-Identifier: GPL-2.0 */
> >>>> +/*
> >>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
> >>>> + *
> >>>> + * Authors:
> >>>> + * Lizhi Hou <[email protected]>
> >>>> + */
> >>>> +
> >>>> +#ifndef _XRT_DEVCTL_H_
> >>>> +#define _XRT_DEVCTL_H_
> >>>> +
> >>>> +#include "xleaf.h"
> >>>> +
> >>>> +/*
> >>>> + * DEVCTL driver leaf calls.
> >>>> + */
> >>>> +enum xrt_devctl_leaf_cmd {
> >>>> + XRT_DEVCTL_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in
> >>>> xleaf.h */
> >>>> +};
> >>>> +
> >>>> +enum xrt_devctl_id {
> >>>> + XRT_DEVCTL_ROM_UUID = 0,
> >>>> + XRT_DEVCTL_DDR_CALIB,
> >>>> + XRT_DEVCTL_GOLDEN_VER,
> >>>> + XRT_DEVCTL_MAX
> >>>> +};
> >>>> +
> >>>> +struct xrt_devctl_rw {
> >>>> + u32 xdr_id;
> >>>> + void *xdr_buf;
> >>>> + u32 xdr_len;
> >>>> + u32 xdr_offset;
> >>>> +};
> >>>> +
> >>>> +struct xrt_devctl_intf_uuid {
> >>>> + u32 uuid_num;
> >>>> + uuid_t *uuids;
> >>>> +};
> >>>> +
> >>>> +#endif /* _XRT_DEVCTL_H_ */
> >>>> diff --git a/drivers/fpga/xrt/lib/lib-drv.c
> >>>> b/drivers/fpga/xrt/lib/lib-drv.c
> >>>> new file mode 100644
> >>>> index 000000000000..7d21ef565651
> >>>> --- /dev/null
> >>>> +++ b/drivers/fpga/xrt/lib/lib-drv.c
> >>>> @@ -0,0 +1,318 @@
> >>>> +// SPDX-License-Identifier: GPL-2.0
> >>>> +/*
> >>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
> >>>> + *
> >>>> + * Authors:
> >>>> + * Cheng Zhen <[email protected]>
> >>>> + * Lizhi Hou <[email protected]>
> >>>> + */
> >>>> +
> >>>> +#include <linux/module.h>
> >>>> +#include <linux/vmalloc.h>
> >>>> +#include <linux/slab.h>
> >>>> +#include "xleaf.h"
> >>>> +#include "xroot.h"
> >>>> +#include "lib-drv.h"
> >>>> +
> >>>> +#define XRT_IPLIB_MODULE_NAME "xrt-lib"
> >>> ok
> >>>> +#define XRT_DRVNAME(drv) ((drv)->driver.name)
> >>>> +
> >>>> +#define XRT_SUBDEV_ID_SHIFT 16
> >>>> +#define XRT_SUBDEV_ID_MASK ((1 << XRT_SUBDEV_ID_SHIFT) - 1)
> >>>> +
> >>>> +struct xrt_find_drv_data {
> >>>> + enum xrt_subdev_id id;
> >>>> + struct xrt_driver *xdrv;
> >>>> +};
> >>>> +
> >>>> +struct class *xrt_class;
> >>>> +static DEFINE_IDA(xrt_device_ida);
> >>>> +
> >>>> +static inline u32 xrt_instance_to_id(enum xrt_subdev_id id, u32
> >>>> instance)
> >>>> +{
> >>>> + return (id << XRT_SUBDEV_ID_SHIFT) | instance;
> >>>> +}
> >>>> +
> >>>> +static inline u32 xrt_id_to_instance(u32 id)
> >>>> +{
> >>>> + return id & XRT_SUBDEV_ID_MASK;
> >>> ok
> >>>> +}
> >>>> +
> >>>> +static int xrt_bus_match(struct device *dev, struct device_driver
> >>>> *drv)
> >>>> +{
> >>>> + struct xrt_device *xdev = to_xrt_dev(dev);
> >>>> + struct xrt_driver *xdrv = to_xrt_drv(drv);
> >>>> +
> >>>> + if (xdev->subdev_id == xdrv->subdev_id)
> >>>> + return 1;
> >>>> +
> >>>> + return 0;
> >>>> +}
> >>>> +
> >>>> +static int xrt_bus_probe(struct device *dev)
> >>>> +{
> >>>> + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
> >>>> + struct xrt_device *xdev = to_xrt_dev(dev);
> >>>> +
> >>>> + return xdrv->probe(xdev);
> >>>> +}
> >>>> +
> >>>> +static int xrt_bus_remove(struct device *dev)
> >>>> +{
> >>>> + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
> >>>> + struct xrt_device *xdev = to_xrt_dev(dev);
> >>>> +
> >>>> + if (xdrv->remove)
> >>>> + xdrv->remove(xdev);
> >>>> +
> >>>> + return 0;
> >>>> +}
> >>>> +
> >>>> +struct bus_type xrt_bus_type = {
> >>>> + .name = "xrt",
> >>>> + .match = xrt_bus_match,
> >>>> + .probe = xrt_bus_probe,
> >>>> + .remove = xrt_bus_remove,
> >>>> +};
> >>>> +
> >>>> +int xrt_register_driver(struct xrt_driver *drv)
> >>>> +{
> >>>> + const char *drvname = XRT_DRVNAME(drv);
> >>>> + int rc = 0;
> >>>> +
> >>>> + /* Initialize dev_t for char dev node. */
> >>>> + if (drv->file_ops.xsf_ops.open) {
> >>>> + rc = alloc_chrdev_region(&drv->file_ops.xsf_dev_t, 0,
> >>>> + XRT_MAX_DEVICE_NODES, drvname);
> >>>> + if (rc) {
> >>>> + pr_err("failed to alloc dev minor for %s:
> >>>> %d\n", drvname, rc);
> >>>> + return rc;
> >>>> + }
> >>>> + } else {
> >>>> + drv->file_ops.xsf_dev_t = (dev_t)-1;
> >>>> + }
> >>>> +
> >>>> + drv->driver.owner = THIS_MODULE;
> >>>> + drv->driver.bus = &xrt_bus_type;
> >>>> +
> >>>> + rc = driver_register(&drv->driver);
> >>>> + if (rc) {
> >>>> + pr_err("register %s xrt driver failed\n", drvname);
> >>>> + if (drv->file_ops.xsf_dev_t != (dev_t)-1) {
> >>>> + unregister_chrdev_region(drv->file_ops.xsf_dev_t,
> >>>> + XRT_MAX_DEVICE_NODES);
> >>>> + }
> >>>> + return rc;
> >>>> + }
> >>>> +
> >>>> + pr_info("%s registered successfully\n", drvname);
> >>>> +
> >>>> + return 0;
> >>>> +}
> >>>> +EXPORT_SYMBOL_GPL(xrt_register_driver);
> >>>> +
> >>>> +void xrt_unregister_driver(struct xrt_driver *drv)
> >>>> +{
> >>>> + driver_unregister(&drv->driver);
> >>>> +
> >>>> + if (drv->file_ops.xsf_dev_t != (dev_t)-1)
> >>>> + unregister_chrdev_region(drv->file_ops.xsf_dev_t,
> >>>> XRT_MAX_DEVICE_NODES);
> >>> ok
> >>>> +}
> >>>> +EXPORT_SYMBOL_GPL(xrt_unregister_driver);
> >>>> +
> >>>> +static int __find_driver(struct device_driver *drv, void *_data)
> >>>> +{
> >>>> + struct xrt_driver *xdrv = to_xrt_drv(drv);
> >>>> + struct xrt_find_drv_data *data = _data;
> >>>> +
> >>>> + if (xdrv->subdev_id == data->id) {
> >>>> + data->xdrv = xdrv;
> >>>> + return 1;
> >>>> + }
> >>>> +
> >>>> + return 0;
> >>>> +}
> >>>> +
> >>>> +const char *xrt_drv_name(enum xrt_subdev_id id)
> >>>> +{
> >>>> + struct xrt_find_drv_data data = { 0 };
> >>>> +
> >>>> + data.id = id;
> >>>> + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
> >>>> +
> >>>> + if (data.xdrv)
> >>>> + return XRT_DRVNAME(data.xdrv);
> >>>> +
> >>>> + return NULL;
> >>>> +}
> >>>> +
> >>>> +static int xrt_drv_get_instance(enum xrt_subdev_id id)
> >>>> +{
> >>>> + int ret;
> >>>> +
> >>>> + ret = ida_alloc_range(&xrt_device_ida, xrt_instance_to_id(id, 0),
> >>>> + xrt_instance_to_id(id,
> >>>> XRT_MAX_DEVICE_NODES),
> >>>> + GFP_KERNEL);
> >>>> + if (ret < 0)
> >>>> + return ret;
> >>>> +
> >>>> + return xrt_id_to_instance((u32)ret);
> >>>> +}
> >>>> +
> >>>> +static void xrt_drv_put_instance(enum xrt_subdev_id id, int instance)
> >>>> +{
> >>>> + ida_free(&xrt_device_ida, xrt_instance_to_id(id, instance));
> >>>> +}
> >>>> +
> >>>> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id
> >>>> id)
> >>>> +{
> >>>> + struct xrt_find_drv_data data = { 0 };
> >>>> +
> >>>> + data.id = id;
> >>>> + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
> >>>> +
> >>>> + if (data.xdrv)
> >>>> + return data.xdrv->endpoints;
> >>>> +
> >>>> + return NULL;
> >>>> +}
> >>>> +
> >>>> +static void xrt_device_release(struct device *dev)
> >>>> +{
> >>>> + struct xrt_device *xdev = container_of(dev, struct xrt_device,
> >>>> dev);
> >>>> +
> >>>> + kfree(xdev);
> >>>> +}
> >>>> +
> >>>> +void xrt_device_unregister(struct xrt_device *xdev)
> >>>> +{
> >>>> + if (xdev->state == XRT_DEVICE_STATE_ADDED)
> >>>> + device_del(&xdev->dev);
> >>>> +
> >>>> + vfree(xdev->sdev_data);
> >>> cleanup of sdev_data here
> >>>
> >>> fail: label in xrt_device_register is ok.
> >>>
> >>>> + kfree(xdev->resource);
> >>>> +
> >>>> + if (xdev->instance != XRT_INVALID_DEVICE_INST)
> >>>> + xrt_drv_put_instance(xdev->subdev_id, xdev->instance);
> >>>> +
> >>>> + if (xdev->dev.release == xrt_device_release)
> >>>> + put_device(&xdev->dev);
> >>>> +}
> >>>> +
> >>>> +struct xrt_device *
> >>>> +xrt_device_register(struct device *parent, u32 id,
> >>>> + struct resource *res, u32 res_num,
> >>>> + void *pdata, size_t data_sz)
> >>>> +{
> >>>> + struct xrt_device *xdev = NULL;
> >>>> + int ret;
> >>>> +
> >>>> + xdev = kzalloc(sizeof(*xdev), GFP_KERNEL);
> >>>> + if (!xdev)
> >>>> + return xdev;
> >>> ok
> >>>> + xdev->instance = XRT_INVALID_DEVICE_INST;
> >>>> +
> >>>> + /* Obtain dev instance number. */
> >>>> + ret = xrt_drv_get_instance(id);
> >>>> + if (ret < 0) {
> >>>> + dev_err(parent, "failed get instance, ret %d", ret);
> >>>> + goto fail;
> >>>> + }
> >>>> +
> >>>> + xdev->instance = ret;
> >>>> + xdev->name = xrt_drv_name(id);
> >>>> + xdev->subdev_id = id;
> >>>> + device_initialize(&xdev->dev);
> >>>> + xdev->dev.release = xrt_device_release;
> >>>> + xdev->dev.parent = parent;
> >>>> +
> >>>> + xdev->dev.bus = &xrt_bus_type;
> >>>> + dev_set_name(&xdev->dev, "%s.%d", xdev->name, xdev->instance);
> >>>> +
> >>>> + xdev->num_resources = res_num;
> >>>> + xdev->resource = kmemdup(res, sizeof(*res) * res_num, GFP_KERNEL);
> >>>> + if (!xdev->resource)
> >>>> + goto fail;
> >>>> +
> >>>> + xdev->sdev_data = vzalloc(data_sz);
> >>>> + if (!xdev->sdev_data)
> >>>> + goto fail;
> >>>> +
> >>>> + memcpy(xdev->sdev_data, pdata, data_sz);
> >>>> +
> >>>> + ret = device_add(&xdev->dev);
> >>>> + if (ret) {
> >>>> + dev_err(parent, "failed add device, ret %d", ret);
> >>>> + goto fail;
> >>>> + }
> >>>> + xdev->state = XRT_DEVICE_STATE_ADDED;
> >>>> +
> >>>> + return xdev;
> >>>> +
> >>>> +fail:
> >>>> + xrt_device_unregister(xdev);
> >>>> + kfree(xdev);
> >>>> +
> >>>> + return NULL;
> >>>> +}
> >>>> +
> >>>> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32
> >>>> type, u32 num)
> >>>> +{
> >>>> + u32 i;
> >>>> +
> >>>> + for (i = 0; i < xdev->num_resources; i++) {
> >>>> + struct resource *r = &xdev->resource[i];
> >>>> +
> >>>> + if (type == resource_type(r) && num-- == 0)
> >>>> + return r;
> >>>> + }
> >>>> + return NULL;
> >>>> +}
> >>>> +
> >>>> +/*
> >>>> + * Leaf driver's module init/fini callbacks. This is not a open
> >>>> infrastructure for dynamic
> >>>> + * plugging in drivers. All drivers should be statically added.
> >>>> + */
> >>>> +static void (*leaf_init_fini_cbs[])(bool) = {
> >>>> + group_leaf_init_fini,
> >>>> + axigate_leaf_init_fini,
> >>>> + icap_leaf_init_fini,
> >>>> +};
> >>>> +
> >>>> +static __init int xrt_lib_init(void)
> >>>> +{
> >>>> + int ret;
> >>>> + int i;
> >>>> +
> >>>> + ret = bus_register(&xrt_bus_type);
> >>>> + if (ret)
> >>>> + return ret;
> >>>> +
> >>>> + xrt_class = class_create(THIS_MODULE, XRT_IPLIB_MODULE_NAME);
> >>>> + if (IS_ERR(xrt_class)) {
> >>>> + bus_unregister(&xrt_bus_type);
> >>>> + return PTR_ERR(xrt_class);
> >>>> + }
> >>>> +
> >>>> + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
> >>>> + leaf_init_fini_cbs[i](true);
> >>>> + return 0;
> >>>> +}
> >>>> +
> >>>> +static __exit void xrt_lib_fini(void)
> >>>> +{
> >>>> + int i;
> >>>> +
> >>>> + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
> >>>> + leaf_init_fini_cbs[i](false);
> >>>> +
> >>>> + class_destroy(xrt_class);
> >>>> + bus_unregister(&xrt_bus_type);
> >>>> +}
> >>>> +
> >>>> +module_init(xrt_lib_init);
> >>>> +module_exit(xrt_lib_fini);
> >>>> +
> >>>> +MODULE_AUTHOR("XRT Team <[email protected]>");
> >>>> +MODULE_DESCRIPTION("Xilinx Alveo IP Lib driver");
> >>>> +MODULE_LICENSE("GPL v2");
> >>>> diff --git a/drivers/fpga/xrt/lib/lib-drv.h
> >>>> b/drivers/fpga/xrt/lib/lib-drv.h
> >>>> new file mode 100644
> >>>> index 000000000000..0276c28e009f
> >>>> --- /dev/null
> >>>> +++ b/drivers/fpga/xrt/lib/lib-drv.h
> >>>> @@ -0,0 +1,21 @@
> >>>> +/* SPDX-License-Identifier: GPL-2.0 */
> >>>> +/*
> >>>> + * Copyright (C) 2020-2021 Xilinx, Inc.
> >>>> + *
> >>>> + * Authors:
> >>>> + * Cheng Zhen <[email protected]>
> >>>> + */
> >>>> +
> >>>> +#ifndef _LIB_DRV_H_
> >>>> +#define _LIB_DRV_H_
> >>>> +
> >>>> +#include <linux/device/class.h>
> >>>> +#include <linux/device/bus.h>
> >>>> +
> >>>> +extern struct class *xrt_class;
> >>>> +extern struct bus_type xrt_bus_type;
> >>>> +
> >>>> +const char *xrt_drv_name(enum xrt_subdev_id id);
> >>>> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id
> >>>> id);
> >>>> +
> >>>> +#endif /* _LIB_DRV_H_ */
> >
On Mon, Aug 02, 2021 at 09:05:08AM -0700, Lizhi Hou wrote:
> Describe XRT driver architecture and provide basic overview of
> Xilinx Alveo platform.
>
> Signed-off-by: Sonal Santan <[email protected]>
> Signed-off-by: Max Zhen <[email protected]>
> Signed-off-by: Lizhi Hou <[email protected]>
> Reviewed-by: Tom Rix <[email protected]>
> ---
> Documentation/fpga/index.rst | 1 +
> Documentation/fpga/xrt.rst | 870 +++++++++++++++++++++++++++++++++++
> MAINTAINERS | 11 +
> 3 files changed, 882 insertions(+)
> create mode 100644 Documentation/fpga/xrt.rst
>
> diff --git a/Documentation/fpga/index.rst b/Documentation/fpga/index.rst
> index f80f95667ca2..30134357b70d 100644
> --- a/Documentation/fpga/index.rst
> +++ b/Documentation/fpga/index.rst
> @@ -8,6 +8,7 @@ fpga
> :maxdepth: 1
>
> dfl
> + xrt
>
> .. only:: subproject and html
>
> diff --git a/Documentation/fpga/xrt.rst b/Documentation/fpga/xrt.rst
> new file mode 100644
> index 000000000000..84eb41be9ac1
> --- /dev/null
> +++ b/Documentation/fpga/xrt.rst
> @@ -0,0 +1,870 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +==================================
> +XRTV2 Linux Kernel Driver Overview
> +==================================
> +
> +Authors:
> +
> +* Sonal Santan <[email protected]>
> +* Max Zhen <[email protected]>
> +* Lizhi Hou <[email protected]>
> +
> +XRTV2 drivers are second generation `XRT <https://github.com/Xilinx/XRT>`_
> +drivers which support `Alveo <https://www.xilinx.com/products/boards-and-kits/alveo.html>`_
> +PCIe platforms from Xilinx.
> +
> +XRTV2 drivers support *subsystem* style data driven platforms where driver's
> +configuration and behavior are determined by metadata provided by the platform
> +(in *device tree* format). Primary management physical function (MPF) driver
> +is called **xrt-mgmt**. Primary user physical function (UPF) driver is called
> +**xrt-user** and is under development. xrt_driver framework and HW subsystem
> +drivers are packaged into a library module called **xrt-lib**, which is shared
> +by **xrt-mgmt** and **xrt-user** (under development). The xrt_driver framework
> +implements a ``bus_type`` called **xrt_bus_type** which is used to discover HW
> +subsystems and facilitate inter HW subsystem interaction.
> +
> +Driver Modules
> +==============
> +
> +xrt-lib.ko
> +----------
> +
> +xrt-lib is the repository of all subsystem drivers and pure software modules that
> +can potentially be shared between xrt-mgmt and xrt-user. All these drivers are
> +structured as **xrt_driver** and are instantiated by xrt-mgmt (or xrt-user under
> +development) based on the metadata associated with the hardware. The metadata is
> +in the form of a device tree as mentioned before. Each xrt_driver statically
> +defines a subsystem node array by using a node name or a string in its ``.endpoints``
> +property. And this array is eventually translated to IOMEM resources in the
> +instantiated **xrt_device**.
> +
> +The xrt-lib infrastructure provides hooks to xrt_drivers for device node
> +management, user file operations and ioctl callbacks. The core infrastructure also
> +provides a bus functionality called **xrt_bus_type** for xrt_driver registration,
> +discovery and inter xrt_driver calls. xrt-lib does not have any dependency on PCIe
> +subsystem.
> +
> +.. note::
> + See code in ``include/xleaf.h`` and ``include/xdevice.h``
> +
> +
> +xrt-mgmt.ko
> +------------
> +
> +The xrt-mgmt driver is a PCIe device driver driving MPF found on Xilinx's Alveo
> +PCIe device. It consists of one *root* driver, one or more *group* drivers
> +and one or more *xleaf* drivers. The group and xleaf drivers are instantiations
> +of the xrt_driver but are called group and xleaf to symbolize the logical operation
> +performed by them.
> +
> +The root driver manages the life cycle of multiple group drivers, which, in turn,
> +manages multiple xleaf drivers. This flexibility allows xrt-mgmt.ko and xrt-lib.ko
> +to support various HW subsystems exposed by different Alveo shells. The differences
> +among these Alveo shells is handled in xleaf drivers. The root and group
> +drivers are part of the infrastructure which provide common services to xleaf
> +drivers found on various Alveo shells. See :ref:`alveo_platform_overview`.
> +
> +The instantiation of specific group driver or xleaf drivers is completely data
> +driven based on metadata (mostly in device tree format) found through VSEC
> +capability and inside the firmware files, such as platform xsabin or user xclbin
> +file.
> +
> +
> +Driver Object Model
> +===================
> +
> +The driver object model looks like the following::
> +
> + +-----------+
> + | xroot |
> + +-----+-----+
> + |
> + +-----------+-----------+
> + | |
> + v v
> + +-----------+ +-----------+
> + | group | ... | group |
> + +-----+-----+ +------+----+
> + | |
> + | |
> + +-----+----+ +-----+----+
> + | | | |
> + v v v v
> + +-------+ +-------+ +-------+ +-------+
> + | xleaf |..| xleaf | | xleaf |..| xleaf |
> + +-------+ +-------+ +-------+ +-------+
> +
> +As an example, for Xilinx Alveo U50 before user xclbin download, the tree
> +looks like the following::
> +
> + +-----------+
> + | xrt-mgmt |
> + +-----+-----+
> + |
> + +-------------------------+--------------------+
> + | | |
> + v v v
> + +--------+ +--------+ +--------+
> + | group0 | | group1 | | group2 |
> + +----+---+ +----+---+ +---+----+
> + | | |
> + | | |
> + +-----+-----+ +----+-----+---+ +-----+-----+----+--------+
> + | | | | | | | | |
> + v v | v v | v v |
> + +------------+ +------+ | +------+ +------+ | +------+ +-----------+ |
> + | xmgmt_main | | VSEC | | | GPIO | | QSPI | | | CMC | | AXI-GATE0 | |
> + +------------+ +------+ | +------+ +------+ | +------+ +-----------+ |
> + | +---------+ | +------+ +-----------+ |
> + +>| MAILBOX | +->| ICAP | | AXI-GATE1 |<+
> + +---------+ | +------+ +-----------+
> + | +-------+
> + +->| CALIB |
> + +-------+
> +
> +After a xclbin is downloaded, group3 will be added and the tree looks like the
> +following::
> +
> + +-----------+
> + | xrt-mgmt |
> + +-----+-----+
> + |
> + +-------------------------+--------------------+-----------------+
> + | | | |
> + v v v |
> + +--------+ +--------+ +--------+ |
> + | group0 | | group1 | | group2 | |
> + +----+---+ +----+---+ +---+----+ |
> + | | | |
> + | | | |
> + +-----+-----+ +-----+-----+---+ +-----+-----+----+--------+ |
> + | | | | | | | | | |
> + v v | v v | v v | |
> + +------------+ +------+ | +------+ +------+ | +------+ +-----------+ | |
> + | xmgmt_main | | VSEC | | | GPIO | | QSPI | | | CMC | | AXI-GATE0 | | |
> + +------------+ +------+ | +------+ +------+ | +------+ +-----------+ | |
> + | +---------+ | +------+ +-----------+ | |
> + +>| MAILBOX | +->| ICAP | | AXI-GATE1 |<+ |
> + +---------+ | +------+ +-----------+ |
> + | +-------+ |
> + +->| CALIB | |
> + +-------+ |
> + +---+----+ |
> + | group3 |<--------------------------------------------+
> + +--------+
> + |
> + |
> + +-------+--------+---+--+--------+------+-------+
> + | | | | | | |
> + v | v | v | v
> + +--------+ | +--------+ | +--------+ | +-----+
> + | CLOCK0 | | | CLOCK1 | | | CLOCK2 | | | UCS |
> + +--------+ v +--------+ v +--------+ v +-----+
> + +-------------+ +-------------+ +-------------+
> + | CLOCK-FREQ0 | | CLOCK-FREQ1 | | CLOCK-FREQ2 |
> + +-------------+ +-------------+ +-------------+
> +
> +
> +root
> +----
> +
> +The root driver is a PCIe device driver attached to MPF. It's part of the
> +infrastructure of the MPF driver and resides in xrt-mgmt.ko. This driver
> +
> +* manages one or more group drivers
> +* provides access to functionalities that requires pci_dev, such as PCIE config
> + space access, to other xleaf drivers through root calls
> +* facilities inter xleaf driver calls for other xleaf drivers
> +* facilities event callbacks for other xleaf drivers
> +
> +When the root driver starts, it will explicitly create an initial group instance,
> +which contains xleaf drivers that will trigger the creation of other group
> +instances. The root driver will wait for all group and xleaf drivers to be
> +created before it returns from its probe routine and claim success of the
> +initialization of the entire xrt-mgmt driver. If any xleaf fails to initialize
> +the xrt-mgmt driver will still come online but with limited functionality.
> +
> +.. note::
> + See code in ``lib/xroot.c`` and ``mgmt/root.c``
> +
> +
> +group
> +-----
> +
> +The group driver represents a pseudo device whose life cycle is managed by
> +root and does not have real IO mem or IRQ resources. It's part of the
> +infrastructure of the MPF driver and resides in xrt-lib.ko. This driver
> +
> +* manages one or more xleaf drivers
> +* provides access to root from xleaf drivers, so that root calls, event
> + notifications and inter xleaf calls can happen
> +
> +In xrt-mgmt, an initial group driver instance will be created by the root. This
> +instance contains xleaf drivers that will trigger group instances to be created
> +to manage groups of xleaf drivers found on different partitions of hardware,
> +such as VSEC, Shell, and User.
> +
> +Every *fpga_region* has a group driver associated with it. The group driver is
> +created when a xclbin image is loaded on the fpga_region. The existing group
> +is destroyed when a new xclbin image is loaded. The fpga_region persists
> +across xclbin downloads.
> +
> +.. note::
> + See code in ``lib/group.c``
> +
> +
> +xleaf
> +-----
> +
> +The xleaf driver is a xrt_driver whose life cycle is managed by
> +a group driver and may or may not have real IO mem or IRQ resources. They
> +manage HW subsystems they are attached to.
> +
> +A xleaf driver without real hardware resources manages in-memory states for
> +xrt-mgmt. These states are shareable by other xleaf drivers.
> +
> +Xleaf drivers assigned to specific hardware resources drive a specific subsystem
> +in the device. To manipulate the subsystem or carry out a task, a xleaf driver
> +may ask for help from the root via root calls and/or from other leaves via
> +inter xleaf calls.
> +
> +A xleaf can also broadcast events through infrastructure code for other leaves
> +to process. It can also receive event notification from infrastructure about
> +certain events, such as post-creation or pre-exit of a particular xleaf.
> +
> +.. note::
> + See code in ``lib/xleaf/*.c``
> +
> +
> +xrt_bus_type
> +------------
> +
> +xrt_bus_type defines a virtual bus which handles xrt_driver probe, remove and match
> +operations. All xrt_drivers register with xrt_bus_type as part of xrt-lib driver
> +``module_init`` and un-register as part of xrt-lib driver ``module_exit``.
> +
> +.. note::
> + See code in ``lib/lib-drv.c``
> +
> +FPGA Manager Interaction
> +========================
> +
> +fpga_manager
> +------------
> +
> +An instance of fpga_manager is created by xmgmt_main and is used for xclbin
> +image download. fpga_manager requires the full xclbin image before it can
> +start programming the FPGA configuration engine via Internal Configuration
> +Access Port (ICAP) xrt_driver.
> +
> +fpga_region
> +-----------
> +
> +For every interface exposed by the currently loaded xclbin/xsabin in the
> +*parent* fpga_region a new instance of fpga_region is created like a *child*
> +fpga_region. The device tree of the *parent* fpga_region defines the
> +resources for a new instance of fpga_bridge which isolates the parent from
> +child fpga_region. This new instance of fpga_bridge will be used when a
> +xclbin image is loaded on the child fpga_region. After the xclbin image is
> +downloaded to the fpga_region, an instance of a group is created for the
> +fpga_region using the device tree obtained as part of the xclbin. If this
> +device tree defines any child interfaces, it can trigger the creation of
> +fpga_bridge and fpga_region for the next region in the chain.
> +
> +fpga_bridge
> +-----------
> +
> +Like the fpga_region, an fpga_bridge is created by walking the device tree
> +of the parent group. The bridge is used for isolation between a parent and
> +its child.
> +
> +Driver Interfaces
> +=================
> +
> +xrt-mgmt Driver Ioctls
> +----------------------
> +
> +Ioctls exposed by the xrt-mgmt driver to user space are enumerated in the
> +following table:
> +
> +== ===================== ============================ ==========================
> +# Functionality ioctl request code data format
> +== ===================== ============================ ==========================
> +1 FPGA image download XMGMT_IOCICAPDOWNLOAD_AXLF xmgmt_ioc_bitstream_axlf
> +== ===================== ============================ ==========================
> +
> +A user xclbin can be downloaded by using the xbmgmt tool from the XRT open source
> +suite. See example usage below::
> +
> + xbmgmt partition --program --path /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/test/verify.xclbin --force
> +
> +xrt-mgmt Driver Sysfs
> +----------------------
> +
> +The xrt-mgmt driver exposes a rich set of sysfs interfaces. Subsystem xrt
> +drivers export sysfs node for every platform instance.
> +
> +Every partition also exports its UUIDs. See below for examples::
> +
> + /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/interface_uuids
> + /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/logic_uuids
> +
> +
> +hwmon
> +-----
> +
> +The xrt-mgmt driver exposes standard hwmon interface to report voltage, current,
> +temperature, power, etc. These can easily be viewed using *sensors* command line
> +utility.
> +
> +.. _alveo_platform_overview:
> +
> +Alveo Platform Overview
> +=======================
> +
> +Alveo platforms are architected as two physical FPGA partitions: *Shell* and
> +*User*. The Shell provides basic infrastructure for the Alveo platform like
> +PCIe connectivity, board management, Dynamic Function Exchange (DFX), sensors,
> +clocking, reset, and security. DFX, partial reconfiguration, is responsible for
> +loading the user compiled FPGA binary.
> +
> +For DFX to work properly, physical partitions require strict HW compatibility
> +with each other. Every physical partition has two interface UUIDs: the *parent*
> +UUID and the *child* UUID. For simple single stage platforms, Shell → User forms
> +the parent child relationship.
> +
> +.. note::
> + Partition compatibility matching is a key design component of the Alveo platforms
> + and XRT. Partitions have child and parent relationship. A loaded partition
> + exposes child partition UUID to advertise its compatibility requirement. When
> + loading a child partition, the xrt-mgmt driver matches the parent
> + UUID of the child partition against the child UUID exported by the parent.
> + The parent and child partition UUIDs are stored in the *xclbin* (for the user)
> + and the *xsabin* (for the shell). Except for the root UUID exported by VSEC,
> + the hardware itself does not know about the UUIDs. The UUIDs are stored in
> + xsabin and xclbin. The image format has a special node called Partition UUIDs
> + which define the compatibility UUIDs. See :ref:`partition_uuids`.
> +
> +
> +The physical partitions and their loading are illustrated below::
> +
> + SHELL USER
> + +-----------+ +-------------------+
> + | | | |
> + | VSEC UUID | CHILD PARENT | LOGIC UUID |
> + | o------->|<--------o |
> + | | UUID UUID | |
> + +-----+-----+ +--------+----------+
> + | |
> + . .
> + | |
> + +---+---+ +------+--------+
> + | POR | | USER COMPILED |
> + | FLASH | | XCLBIN |
> + +-------+ +---------------+
> +
> +
> +Loading Sequence
> +----------------
> +
> +The Shell partition is loaded from flash at system boot time. It establishes the
> +PCIe link and exposes two physical functions to the BIOS. After the OS boots,
> +the xrt-mgmt driver attaches to the PCIe physical function 0 exposed by the Shell
> +and then looks for VSEC in the PCIe extended configuration space. Using VSEC, it
> +determines the logic UUID of the Shell and uses the UUID to load matching *xsabin*
> +file from Linux firmware directory. The xsabin file contains the metadata to
> +discover the peripherals that are part of the Shell and the firmware for any
> +embedded soft processors in the Shell. The xsabin file also contains Partition
> +UUIDs as described here :ref:`partition_uuids`.
> +
> +The Shell exports a child interface UUID which is used for the compatibility
> +check when loading the user compiled xclbin over the User partition as part of DFX.
> +When a user requests loading of a specific xclbin, the xrt-mgmt driver reads
> +the parent interface UUID specified in the xclbin and matches it with the child
> +interface UUID exported by the Shell to determine if the xclbin is compatible with
> +the Shell. If the match fails, loading of xclbin is denied.
> +
> +xclbin loading is requested using the ICAP_DOWNLOAD_AXLF ioctl command. When loading
> +a xclbin, the xrt-mgmt driver performs the following *logical* operations:
> +
> +1. Copy xclbin from user to kernel memory
> +2. Sanity check the xclbin contents
> +3. Isolate the User partition
> +4. Download the bitstream using the FPGA config engine (ICAP)
> +5. De-isolate the User partition
> +6. Program the clocks (ClockWiz) driving the User partition
> +7. Wait for the memory controller (MIG) calibration
> +8. Return the loading status back to the caller
> +
> +`Platform Loading Overview <https://xilinx.github.io/XRT/master/html/platforms_partitions.html>`_
> +provides more detailed information on platform loading.
> +
> +
> +xsabin
> +------
> +
> +Each Alveo platform comes packaged with its own xsabin. The xsabin is a trusted
> +component of the platform. For format details refer to :ref:`xsabin_xclbin_container_format`
> +below. xsabin contains basic information like UUIDs, platform name and metadata in the
> +form of device tree. See :ref:`device_tree_usage` below for details and example.
> +
> +xclbin
> +------
> +
> +xclbin is compiled by end user using
> +`Vitis <https://www.xilinx.com/products/design-tools/vitis/vitis-platform.html>`_
> +tool set from Xilinx. The xclbin contains sections describing user compiled
> +acceleration engines/kernels, memory subsystems, clocking information etc. It also
> +contains an FPGA bitstream for the user partition, UUIDs, platform name, etc.
> +
> +
> +.. _xsabin_xclbin_container_format:
> +
> +xsabin/xclbin Container Format
> +------------------------------
> +
> +xclbin/xsabin is ELF-like binary container format. It is structured as series of
> +sections. There is a file header followed by several section headers which is
> +followed by sections. A section header points to an actual section. There is an
> +optional signature at the end. The format is defined by the header file ``xclbin.h``.
> +The following figure illustrates a typical xclbin::
> +
> +
> + +---------------------+
> + | |
> + | HEADER |
> + +---------------------+
> + | SECTION HEADER |
> + | |
> + +---------------------+
> + | ... |
> + | |
> + +---------------------+
> + | SECTION HEADER |
> + | |
> + +---------------------+
> + | SECTION |
> + | |
> + +---------------------+
> + | ... |
> + | |
> + +---------------------+
> + | SECTION |
> + | |
> + +---------------------+
> + | SIGNATURE |
> + | (OPTIONAL) |
> + +---------------------+
> +
> +
> +xclbin/xsabin files can be packaged, un-packaged and inspected using an XRT
> +utility called **xclbinutil**. xclbinutil is part of the XRT open source
> +software stack. The source code for xclbinutil can be found at
> +https://github.com/Xilinx/XRT/tree/master/src/runtime_src/tools/xclbinutil
> +
> +For example, to enumerate the contents of a xclbin/xsabin use the *--info* switch
> +as shown below::
> +
> +
> + xclbinutil --info --input /opt/xilinx/firmware/u50/gen3x16-xdma/blp/test/bandwidth.xclbin
> + xclbinutil --info --input /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/partition.xsabin
> +
> +
> +.. _device_tree_usage:
> +
> +Device Tree Usage
> +-----------------
> +
> +The xsabin file stores metadata which advertise HW subsystems present in a
> +partition. The metadata is stored in device tree format with a well defined
> +schema. XRT management driver uses this information to bind *xrt_drivers* to
> +the subsystem instantiations. The xrt_drivers are found in **xrt-lib.ko** kernel
> +module.
I'm still catching up the patchset from the very beginning, and just
finished the Documentation part. So far, I see the DT usage concern
which may impact the architecure a lot, so I should raise it ASAP.
The concern raised by the DT maintainer:
https://lore.kernel.org/linux-fpga/CAL_JsqLod6FBGFhu7WXtMrB_z7wj8-up0EetM1QS9M3gjm8d7Q@mail.gmail.com/
First of all, directly parsing FDT in device drivers is not a normal usage of DT
in linux. It is out of the current DT usage model. So it should be agreed by DT
maintainers.
Current FPGA framework modifies kernel's live tree by DT overlay, when FPGA is
dynamically reprogrammed and new HW devices appear. See
Documentation/devicetree/bindings/fpga/fpga-region.txt.
Then something less important:
1. The bindings should be documented in Documentation/devicetree/bindings/.
2. Are all the example DT usage conform to the exsiting bindings? I
didn't go through all device classes, but remember like the
interrupt-controller should have a "interrupt-controller" property, and
the PCI properties are also different from PCI bindings.
Thanks,
Yilun
> +
> +Logic UUID
> +^^^^^^^^^^
> +A partition is identified uniquely through ``logic_uuid`` property::
> +
> + /dts-v1/;
> + / {
> + logic_uuid = "0123456789abcdef0123456789abcdef";
> + ...
> + }
> +
> +Schema Version
> +^^^^^^^^^^^^^^
> +Schema version is defined through the ``schema_version`` node. It contains
> +``major`` and ``minor`` properties as below::
> +
> + /dts-v1/;
> + / {
> + schema_version {
> + major = <0x01>;
> + minor = <0x00>;
> + };
> + ...
> + }
> +
> +.. _partition_uuids:
> +
> +Partition UUIDs
> +^^^^^^^^^^^^^^^
> +Each partition may have parent and child UUIDs. These UUIDs are
> +defined by ``interfaces`` node and ``interface_uuid`` property::
> +
> + /dts-v1/;
> + / {
> + interfaces {
> + @0 {
> + interface_uuid = "0123456789abcdef0123456789abcdef";
> + };
> + @1 {
> + interface_uuid = "fedcba9876543210fedcba9876543210";
> + };
> + ...
> + };
> + ...
> + }
> +
> +
> +Subsystem Instantiations
> +^^^^^^^^^^^^^^^^^^^^^^^^
> +Subsystem instantiations are captured as children of ``addressable_endpoints``
> +node::
> +
> + /dts-v1/;
> + / {
> + addressable_endpoints {
> + abc {
> + ...
> + };
> + def {
> + ...
> + };
> + ...
> + }
> + }
> +
> +Subnode 'abc' and 'def' are the name of subsystem nodes
> +
> +Subsystem Node
> +^^^^^^^^^^^^^^
> +Each subsystem node and its properties define a hardware instance::
> +
> +
> + addressable_endpoints {
> + abc {
> + reg = <0x00 0x1f05000 0x00 0x1000>>
> + pcie_physical_function = <0x0>;
> + pcie_bar_mapping = <0x2>;
> + compatible = "abc def";
> + interrupts = <0x09 0x0c>;
> + firmware {
> + firmware_product_name = "abc"
> + firmware_branch_name = "def"
> + firmware_version_major = <1>
> + firmware_version_minor = <2>
> + };
> + }
> + ...
> + }
> +
> +:reg:
> + Property defines an address range. `<0x00 0x1f05000 0x00 0x1000>` indicates
> + *0x00 0x1f05000* as BAR offset and *0x00 0x1000* as address length.
> +:pcie_physical_function:
> + Property specifies which PCIe physical function the subsystem node resides.
> + `<0x0>` implies physical function 0.
> +:pcie_bar_mapping:
> + Property specifies which PCIe BAR the subsystem node resides. `<0x2>` implies
> + BAR 2. A value of 0 means the property is not defined.
> +:compatible:
> + Property is a list of strings. The first string in the list specifies the exact
> + subsystem node. The following strings represent other devices that the device
> + is compatible with.
> +:interrupts:
> + Property specifies start and end interrupts for this subsystem node.
> + `<0x09 0x0c>` implies interrupts 9 to 13 are used by this subsystem.
> +:firmware:
> + Subnode defines the firmware required by this subsystem node.
> +
> +Alveo U50 Platform Example
> +^^^^^^^^^^^^^^^^^^^^^^^^^^
> +::
> +
> + /dts-v1/;
> +
> + /{
> + logic_uuid = "f465b0a3ae8c64f619bc150384ace69b";
> +
> + schema_version {
> + major = <0x01>;
> + minor = <0x00>;
> + };
> +
> + interfaces {
> +
> + @0 {
> + interface_uuid = "862c7020a250293e32036f19956669e5";
> + };
> + };
> +
> + addressable_endpoints {
> +
> + ep_blp_rom_00 {
> + reg = <0x00 0x1f04000 0x00 0x1000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> + };
> +
> + ep_card_flash_program_00 {
> + reg = <0x00 0x1f06000 0x00 0x1000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_quad_spi-1.0\0axi_quad_spi";
> + interrupts = <0x03 0x03>;
> + };
> +
> + ep_cmc_firmware_mem_00 {
> + reg = <0x00 0x1e20000 0x00 0x20000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> +
> + firmware {
> + firmware_product_name = "cmc";
> + firmware_branch_name = "u50";
> + firmware_version_major = <0x01>;
> + firmware_version_minor = <0x00>;
> + };
> + };
> +
> + ep_cmc_intc_00 {
> + reg = <0x00 0x1e03000 0x00 0x1000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";
> + interrupts = <0x04 0x04>;
> + };
> +
> + ep_cmc_mutex_00 {
> + reg = <0x00 0x1e02000 0x00 0x1000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> + };
> +
> + ep_cmc_regmap_00 {
> + reg = <0x00 0x1e08000 0x00 0x2000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> +
> + firmware {
> + firmware_product_name = "sc-fw";
> + firmware_branch_name = "u50";
> + firmware_version_major = <0x05>;
> + };
> + };
> +
> + ep_cmc_reset_00 {
> + reg = <0x00 0x1e01000 0x00 0x1000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> + };
> +
> + ep_ddr_mem_calib_00 {
> + reg = <0x00 0x63000 0x00 0x1000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> + };
> +
> + ep_debug_bscan_mgmt_00 {
> + reg = <0x00 0x1e90000 0x00 0x10000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-debug_bridge-1.0\0debug_bridge";
> + };
> +
> + ep_ert_base_address_00 {
> + reg = <0x00 0x21000 0x00 0x1000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> + };
> +
> + ep_ert_command_queue_mgmt_00 {
> + reg = <0x00 0x40000 0x00 0x10000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-ert_command_queue-1.0\0ert_command_queue";
> + };
> +
> + ep_ert_command_queue_user_00 {
> + reg = <0x00 0x40000 0x00 0x10000>;
> + pcie_physical_function = <0x01>;
> + compatible = "xilinx.com,reg_abs-ert_command_queue-1.0\0ert_command_queue";
> + };
> +
> + ep_ert_firmware_mem_00 {
> + reg = <0x00 0x30000 0x00 0x8000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> +
> + firmware {
> + firmware_product_name = "ert";
> + firmware_branch_name = "v20";
> + firmware_version_major = <0x01>;
> + };
> + };
> +
> + ep_ert_intc_00 {
> + reg = <0x00 0x23000 0x00 0x1000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";
> + interrupts = <0x05 0x05>;
> + };
> +
> + ep_ert_reset_00 {
> + reg = <0x00 0x22000 0x00 0x1000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> + };
> +
> + ep_ert_sched_00 {
> + reg = <0x00 0x50000 0x00 0x1000>;
> + pcie_physical_function = <0x01>;
> + compatible = "xilinx.com,reg_abs-ert_sched-1.0\0ert_sched";
> + interrupts = <0x09 0x0c>;
> + };
> +
> + ep_fpga_configuration_00 {
> + reg = <0x00 0x1e88000 0x00 0x8000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_hwicap-1.0\0axi_hwicap";
> + interrupts = <0x02 0x02>;
> + };
> +
> + ep_icap_reset_00 {
> + reg = <0x00 0x1f07000 0x00 0x1000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> + };
> +
> + ep_msix_00 {
> + reg = <0x00 0x00 0x00 0x20000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-msix-1.0\0msix";
> + pcie_bar_mapping = <0x02>;
> + };
> +
> + ep_pcie_link_mon_00 {
> + reg = <0x00 0x1f05000 0x00 0x1000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> + };
> +
> + ep_pr_isolate_plp_00 {
> + reg = <0x00 0x1f01000 0x00 0x1000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> + };
> +
> + ep_pr_isolate_ulp_00 {
> + reg = <0x00 0x1000 0x00 0x1000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> + };
> +
> + ep_uuid_rom_00 {
> + reg = <0x00 0x64000 0x00 0x1000>;
> + pcie_physical_function = <0x00>;
> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> + };
> +
> + ep_xdma_00 {
> + reg = <0x00 0x00 0x00 0x10000>;
> + pcie_physical_function = <0x01>;
> + compatible = "xilinx.com,reg_abs-xdma-1.0\0xdma";
> + pcie_bar_mapping = <0x02>;
> + };
> + };
> +
> + }
> +
> +
> +
> +Deployment Models
> +=================
> +
> +Baremetal
> +---------
> +
> +In bare-metal deployments, both MPF and UPF are visible and accessible. The
> +xrt-mgmt driver binds to MPF. The xrt-mgmt driver operations are privileged and
> +available to system administrator. The full stack is illustrated below::
> +
> + HOST
> +
> + [XRT-MGMT] [XRT-USER]
> + | |
> + | |
> + +-----+ +-----+
> + | MPF | | UPF |
> + | | | |
> + | PF0 | | PF1 |
> + +--+--+ +--+--+
> + ......... ^................. ^..........
> + | |
> + | PCIe DEVICE |
> + | |
> + +--+------------------+--+
> + | SHELL |
> + | |
> + +------------------------+
> + | USER |
> + | |
> + | |
> + | |
> + | |
> + +------------------------+
> +
> +
> +
> +Virtualized
> +-----------
> +
> +In virtualized deployments, the privileged MPF is assigned to the host but the
> +unprivileged UPF is assigned to a guest VM via PCIe pass-through. The xrt-mgmt
> +driver in host binds to MPF. The xrt-mgmt driver operations are privileged and
> +only accessible to the MPF. The full stack is illustrated below::
> +
> +
> + ..............
> + HOST . VM .
> + . .
> + [XRT-MGMT] . [XRT-USER] .
> + | . | .
> + | . | .
> + +-----+ . +-----+ .
> + | MPF | . | UPF | .
> + | | . | | .
> + | PF0 | . | PF1 | .
> + +--+--+ . +--+--+ .
> + ......... ^................. ^..........
> + | |
> + | PCIe DEVICE |
> + | |
> + +--+------------------+--+
> + | SHELL |
> + | |
> + +------------------------+
> + | USER |
> + | |
> + | |
> + | |
> + | |
> + +------------------------+
> +
> +
> +
> +
> +
> +Platform Security Considerations
> +================================
> +
> +`Security of Alveo Platform <https://xilinx.github.io/XRT/master/html/security.html>`_
> +discusses the deployment options and security implications in great detail.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 056966c9aac9..beeaf0257364 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7274,6 +7274,17 @@ F: Documentation/fpga/
> F: drivers/fpga/
> F: include/linux/fpga/
>
> +FPGA XRT DRIVERS
> +M: Lizhi Hou <[email protected]>
> +R: Max Zhen <[email protected]>
> +R: Sonal Santan <[email protected]>
> +L: [email protected]
> +S: Supported
> +W: https://github.com/Xilinx/XRT
> +F: Documentation/fpga/xrt.rst
> +F: drivers/fpga/xrt/
> +F: include/uapi/linux/xrt/
> +
> FPU EMULATOR
> M: Bill Metzenthen <[email protected]>
> S: Maintained
> --
> 2.27.0
On 10/11/21 10:09 PM, Xu Yilun wrote:
> CAUTION: This message has originated from an External Source. Please use proper judgment and caution when opening attachments, clicking links, or responding to this email.
>
>
> On Mon, Aug 02, 2021 at 09:05:08AM -0700, Lizhi Hou wrote:
>> Describe XRT driver architecture and provide basic overview of
>> Xilinx Alveo platform.
>>
>> Signed-off-by: Sonal Santan <[email protected]>
>> Signed-off-by: Max Zhen <[email protected]>
>> Signed-off-by: Lizhi Hou <[email protected]>
>> Reviewed-by: Tom Rix <[email protected]>
>> ---
>> Documentation/fpga/index.rst | 1 +
>> Documentation/fpga/xrt.rst | 870 +++++++++++++++++++++++++++++++++++
>> MAINTAINERS | 11 +
>> 3 files changed, 882 insertions(+)
>> create mode 100644 Documentation/fpga/xrt.rst
>>
>> diff --git a/Documentation/fpga/index.rst b/Documentation/fpga/index.rst
>> index f80f95667ca2..30134357b70d 100644
>> --- a/Documentation/fpga/index.rst
>> +++ b/Documentation/fpga/index.rst
>> @@ -8,6 +8,7 @@ fpga
>> :maxdepth: 1
>>
>> dfl
>> + xrt
>>
>> .. only:: subproject and html
>>
>> diff --git a/Documentation/fpga/xrt.rst b/Documentation/fpga/xrt.rst
>> new file mode 100644
>> index 000000000000..84eb41be9ac1
>> --- /dev/null
>> +++ b/Documentation/fpga/xrt.rst
>> @@ -0,0 +1,870 @@
>> +.. SPDX-License-Identifier: GPL-2.0
>> +
>> +==================================
>> +XRTV2 Linux Kernel Driver Overview
>> +==================================
>> +
>> +Authors:
>> +
>> +* Sonal Santan <[email protected]>
>> +* Max Zhen <[email protected]>
>> +* Lizhi Hou <[email protected]>
>> +
>> +XRTV2 drivers are second generation `XRT <https://github.com/Xilinx/XRT>`_
>> +drivers which support `Alveo <https://www.xilinx.com/products/boards-and-kits/alveo.html>`_
>> +PCIe platforms from Xilinx.
>> +
>> +XRTV2 drivers support *subsystem* style data driven platforms where driver's
>> +configuration and behavior are determined by metadata provided by the platform
>> +(in *device tree* format). Primary management physical function (MPF) driver
>> +is called **xrt-mgmt**. Primary user physical function (UPF) driver is called
>> +**xrt-user** and is under development. xrt_driver framework and HW subsystem
>> +drivers are packaged into a library module called **xrt-lib**, which is shared
>> +by **xrt-mgmt** and **xrt-user** (under development). The xrt_driver framework
>> +implements a ``bus_type`` called **xrt_bus_type** which is used to discover HW
>> +subsystems and facilitate inter HW subsystem interaction.
>> +
>> +Driver Modules
>> +==============
>> +
>> +xrt-lib.ko
>> +----------
>> +
>> +xrt-lib is the repository of all subsystem drivers and pure software modules that
>> +can potentially be shared between xrt-mgmt and xrt-user. All these drivers are
>> +structured as **xrt_driver** and are instantiated by xrt-mgmt (or xrt-user under
>> +development) based on the metadata associated with the hardware. The metadata is
>> +in the form of a device tree as mentioned before. Each xrt_driver statically
>> +defines a subsystem node array by using a node name or a string in its ``.endpoints``
>> +property. And this array is eventually translated to IOMEM resources in the
>> +instantiated **xrt_device**.
>> +
>> +The xrt-lib infrastructure provides hooks to xrt_drivers for device node
>> +management, user file operations and ioctl callbacks. The core infrastructure also
>> +provides a bus functionality called **xrt_bus_type** for xrt_driver registration,
>> +discovery and inter xrt_driver calls. xrt-lib does not have any dependency on PCIe
>> +subsystem.
>> +
>> +.. note::
>> + See code in ``include/xleaf.h`` and ``include/xdevice.h``
>> +
>> +
>> +xrt-mgmt.ko
>> +------------
>> +
>> +The xrt-mgmt driver is a PCIe device driver driving MPF found on Xilinx's Alveo
>> +PCIe device. It consists of one *root* driver, one or more *group* drivers
>> +and one or more *xleaf* drivers. The group and xleaf drivers are instantiations
>> +of the xrt_driver but are called group and xleaf to symbolize the logical operation
>> +performed by them.
>> +
>> +The root driver manages the life cycle of multiple group drivers, which, in turn,
>> +manages multiple xleaf drivers. This flexibility allows xrt-mgmt.ko and xrt-lib.ko
>> +to support various HW subsystems exposed by different Alveo shells. The differences
>> +among these Alveo shells is handled in xleaf drivers. The root and group
>> +drivers are part of the infrastructure which provide common services to xleaf
>> +drivers found on various Alveo shells. See :ref:`alveo_platform_overview`.
>> +
>> +The instantiation of specific group driver or xleaf drivers is completely data
>> +driven based on metadata (mostly in device tree format) found through VSEC
>> +capability and inside the firmware files, such as platform xsabin or user xclbin
>> +file.
>> +
>> +
>> +Driver Object Model
>> +===================
>> +
>> +The driver object model looks like the following::
>> +
>> + +-----------+
>> + | xroot |
>> + +-----+-----+
>> + |
>> + +-----------+-----------+
>> + | |
>> + v v
>> + +-----------+ +-----------+
>> + | group | ... | group |
>> + +-----+-----+ +------+----+
>> + | |
>> + | |
>> + +-----+----+ +-----+----+
>> + | | | |
>> + v v v v
>> + +-------+ +-------+ +-------+ +-------+
>> + | xleaf |..| xleaf | | xleaf |..| xleaf |
>> + +-------+ +-------+ +-------+ +-------+
>> +
>> +As an example, for Xilinx Alveo U50 before user xclbin download, the tree
>> +looks like the following::
>> +
>> + +-----------+
>> + | xrt-mgmt |
>> + +-----+-----+
>> + |
>> + +-------------------------+--------------------+
>> + | | |
>> + v v v
>> + +--------+ +--------+ +--------+
>> + | group0 | | group1 | | group2 |
>> + +----+---+ +----+---+ +---+----+
>> + | | |
>> + | | |
>> + +-----+-----+ +----+-----+---+ +-----+-----+----+--------+
>> + | | | | | | | | |
>> + v v | v v | v v |
>> + +------------+ +------+ | +------+ +------+ | +------+ +-----------+ |
>> + | xmgmt_main | | VSEC | | | GPIO | | QSPI | | | CMC | | AXI-GATE0 | |
>> + +------------+ +------+ | +------+ +------+ | +------+ +-----------+ |
>> + | +---------+ | +------+ +-----------+ |
>> + +>| MAILBOX | +->| ICAP | | AXI-GATE1 |<+
>> + +---------+ | +------+ +-----------+
>> + | +-------+
>> + +->| CALIB |
>> + +-------+
>> +
>> +After a xclbin is downloaded, group3 will be added and the tree looks like the
>> +following::
>> +
>> + +-----------+
>> + | xrt-mgmt |
>> + +-----+-----+
>> + |
>> + +-------------------------+--------------------+-----------------+
>> + | | | |
>> + v v v |
>> + +--------+ +--------+ +--------+ |
>> + | group0 | | group1 | | group2 | |
>> + +----+---+ +----+---+ +---+----+ |
>> + | | | |
>> + | | | |
>> + +-----+-----+ +-----+-----+---+ +-----+-----+----+--------+ |
>> + | | | | | | | | | |
>> + v v | v v | v v | |
>> + +------------+ +------+ | +------+ +------+ | +------+ +-----------+ | |
>> + | xmgmt_main | | VSEC | | | GPIO | | QSPI | | | CMC | | AXI-GATE0 | | |
>> + +------------+ +------+ | +------+ +------+ | +------+ +-----------+ | |
>> + | +---------+ | +------+ +-----------+ | |
>> + +>| MAILBOX | +->| ICAP | | AXI-GATE1 |<+ |
>> + +---------+ | +------+ +-----------+ |
>> + | +-------+ |
>> + +->| CALIB | |
>> + +-------+ |
>> + +---+----+ |
>> + | group3 |<--------------------------------------------+
>> + +--------+
>> + |
>> + |
>> + +-------+--------+---+--+--------+------+-------+
>> + | | | | | | |
>> + v | v | v | v
>> + +--------+ | +--------+ | +--------+ | +-----+
>> + | CLOCK0 | | | CLOCK1 | | | CLOCK2 | | | UCS |
>> + +--------+ v +--------+ v +--------+ v +-----+
>> + +-------------+ +-------------+ +-------------+
>> + | CLOCK-FREQ0 | | CLOCK-FREQ1 | | CLOCK-FREQ2 |
>> + +-------------+ +-------------+ +-------------+
>> +
>> +
>> +root
>> +----
>> +
>> +The root driver is a PCIe device driver attached to MPF. It's part of the
>> +infrastructure of the MPF driver and resides in xrt-mgmt.ko. This driver
>> +
>> +* manages one or more group drivers
>> +* provides access to functionalities that requires pci_dev, such as PCIE config
>> + space access, to other xleaf drivers through root calls
>> +* facilities inter xleaf driver calls for other xleaf drivers
>> +* facilities event callbacks for other xleaf drivers
>> +
>> +When the root driver starts, it will explicitly create an initial group instance,
>> +which contains xleaf drivers that will trigger the creation of other group
>> +instances. The root driver will wait for all group and xleaf drivers to be
>> +created before it returns from its probe routine and claim success of the
>> +initialization of the entire xrt-mgmt driver. If any xleaf fails to initialize
>> +the xrt-mgmt driver will still come online but with limited functionality.
>> +
>> +.. note::
>> + See code in ``lib/xroot.c`` and ``mgmt/root.c``
>> +
>> +
>> +group
>> +-----
>> +
>> +The group driver represents a pseudo device whose life cycle is managed by
>> +root and does not have real IO mem or IRQ resources. It's part of the
>> +infrastructure of the MPF driver and resides in xrt-lib.ko. This driver
>> +
>> +* manages one or more xleaf drivers
>> +* provides access to root from xleaf drivers, so that root calls, event
>> + notifications and inter xleaf calls can happen
>> +
>> +In xrt-mgmt, an initial group driver instance will be created by the root. This
>> +instance contains xleaf drivers that will trigger group instances to be created
>> +to manage groups of xleaf drivers found on different partitions of hardware,
>> +such as VSEC, Shell, and User.
>> +
>> +Every *fpga_region* has a group driver associated with it. The group driver is
>> +created when a xclbin image is loaded on the fpga_region. The existing group
>> +is destroyed when a new xclbin image is loaded. The fpga_region persists
>> +across xclbin downloads.
>> +
>> +.. note::
>> + See code in ``lib/group.c``
>> +
>> +
>> +xleaf
>> +-----
>> +
>> +The xleaf driver is a xrt_driver whose life cycle is managed by
>> +a group driver and may or may not have real IO mem or IRQ resources. They
>> +manage HW subsystems they are attached to.
>> +
>> +A xleaf driver without real hardware resources manages in-memory states for
>> +xrt-mgmt. These states are shareable by other xleaf drivers.
>> +
>> +Xleaf drivers assigned to specific hardware resources drive a specific subsystem
>> +in the device. To manipulate the subsystem or carry out a task, a xleaf driver
>> +may ask for help from the root via root calls and/or from other leaves via
>> +inter xleaf calls.
>> +
>> +A xleaf can also broadcast events through infrastructure code for other leaves
>> +to process. It can also receive event notification from infrastructure about
>> +certain events, such as post-creation or pre-exit of a particular xleaf.
>> +
>> +.. note::
>> + See code in ``lib/xleaf/*.c``
>> +
>> +
>> +xrt_bus_type
>> +------------
>> +
>> +xrt_bus_type defines a virtual bus which handles xrt_driver probe, remove and match
>> +operations. All xrt_drivers register with xrt_bus_type as part of xrt-lib driver
>> +``module_init`` and un-register as part of xrt-lib driver ``module_exit``.
>> +
>> +.. note::
>> + See code in ``lib/lib-drv.c``
>> +
>> +FPGA Manager Interaction
>> +========================
>> +
>> +fpga_manager
>> +------------
>> +
>> +An instance of fpga_manager is created by xmgmt_main and is used for xclbin
>> +image download. fpga_manager requires the full xclbin image before it can
>> +start programming the FPGA configuration engine via Internal Configuration
>> +Access Port (ICAP) xrt_driver.
>> +
>> +fpga_region
>> +-----------
>> +
>> +For every interface exposed by the currently loaded xclbin/xsabin in the
>> +*parent* fpga_region a new instance of fpga_region is created like a *child*
>> +fpga_region. The device tree of the *parent* fpga_region defines the
>> +resources for a new instance of fpga_bridge which isolates the parent from
>> +child fpga_region. This new instance of fpga_bridge will be used when a
>> +xclbin image is loaded on the child fpga_region. After the xclbin image is
>> +downloaded to the fpga_region, an instance of a group is created for the
>> +fpga_region using the device tree obtained as part of the xclbin. If this
>> +device tree defines any child interfaces, it can trigger the creation of
>> +fpga_bridge and fpga_region for the next region in the chain.
>> +
>> +fpga_bridge
>> +-----------
>> +
>> +Like the fpga_region, an fpga_bridge is created by walking the device tree
>> +of the parent group. The bridge is used for isolation between a parent and
>> +its child.
>> +
>> +Driver Interfaces
>> +=================
>> +
>> +xrt-mgmt Driver Ioctls
>> +----------------------
>> +
>> +Ioctls exposed by the xrt-mgmt driver to user space are enumerated in the
>> +following table:
>> +
>> +== ===================== ============================ ==========================
>> +# Functionality ioctl request code data format
>> +== ===================== ============================ ==========================
>> +1 FPGA image download XMGMT_IOCICAPDOWNLOAD_AXLF xmgmt_ioc_bitstream_axlf
>> +== ===================== ============================ ==========================
>> +
>> +A user xclbin can be downloaded by using the xbmgmt tool from the XRT open source
>> +suite. See example usage below::
>> +
>> + xbmgmt partition --program --path /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/test/verify.xclbin --force
>> +
>> +xrt-mgmt Driver Sysfs
>> +----------------------
>> +
>> +The xrt-mgmt driver exposes a rich set of sysfs interfaces. Subsystem xrt
>> +drivers export sysfs node for every platform instance.
>> +
>> +Every partition also exports its UUIDs. See below for examples::
>> +
>> + /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/interface_uuids
>> + /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/logic_uuids
>> +
>> +
>> +hwmon
>> +-----
>> +
>> +The xrt-mgmt driver exposes standard hwmon interface to report voltage, current,
>> +temperature, power, etc. These can easily be viewed using *sensors* command line
>> +utility.
>> +
>> +.. _alveo_platform_overview:
>> +
>> +Alveo Platform Overview
>> +=======================
>> +
>> +Alveo platforms are architected as two physical FPGA partitions: *Shell* and
>> +*User*. The Shell provides basic infrastructure for the Alveo platform like
>> +PCIe connectivity, board management, Dynamic Function Exchange (DFX), sensors,
>> +clocking, reset, and security. DFX, partial reconfiguration, is responsible for
>> +loading the user compiled FPGA binary.
>> +
>> +For DFX to work properly, physical partitions require strict HW compatibility
>> +with each other. Every physical partition has two interface UUIDs: the *parent*
>> +UUID and the *child* UUID. For simple single stage platforms, Shell → User forms
>> +the parent child relationship.
>> +
>> +.. note::
>> + Partition compatibility matching is a key design component of the Alveo platforms
>> + and XRT. Partitions have child and parent relationship. A loaded partition
>> + exposes child partition UUID to advertise its compatibility requirement. When
>> + loading a child partition, the xrt-mgmt driver matches the parent
>> + UUID of the child partition against the child UUID exported by the parent.
>> + The parent and child partition UUIDs are stored in the *xclbin* (for the user)
>> + and the *xsabin* (for the shell). Except for the root UUID exported by VSEC,
>> + the hardware itself does not know about the UUIDs. The UUIDs are stored in
>> + xsabin and xclbin. The image format has a special node called Partition UUIDs
>> + which define the compatibility UUIDs. See :ref:`partition_uuids`.
>> +
>> +
>> +The physical partitions and their loading are illustrated below::
>> +
>> + SHELL USER
>> + +-----------+ +-------------------+
>> + | | | |
>> + | VSEC UUID | CHILD PARENT | LOGIC UUID |
>> + | o------->|<--------o |
>> + | | UUID UUID | |
>> + +-----+-----+ +--------+----------+
>> + | |
>> + . .
>> + | |
>> + +---+---+ +------+--------+
>> + | POR | | USER COMPILED |
>> + | FLASH | | XCLBIN |
>> + +-------+ +---------------+
>> +
>> +
>> +Loading Sequence
>> +----------------
>> +
>> +The Shell partition is loaded from flash at system boot time. It establishes the
>> +PCIe link and exposes two physical functions to the BIOS. After the OS boots,
>> +the xrt-mgmt driver attaches to the PCIe physical function 0 exposed by the Shell
>> +and then looks for VSEC in the PCIe extended configuration space. Using VSEC, it
>> +determines the logic UUID of the Shell and uses the UUID to load matching *xsabin*
>> +file from Linux firmware directory. The xsabin file contains the metadata to
>> +discover the peripherals that are part of the Shell and the firmware for any
>> +embedded soft processors in the Shell. The xsabin file also contains Partition
>> +UUIDs as described here :ref:`partition_uuids`.
>> +
>> +The Shell exports a child interface UUID which is used for the compatibility
>> +check when loading the user compiled xclbin over the User partition as part of DFX.
>> +When a user requests loading of a specific xclbin, the xrt-mgmt driver reads
>> +the parent interface UUID specified in the xclbin and matches it with the child
>> +interface UUID exported by the Shell to determine if the xclbin is compatible with
>> +the Shell. If the match fails, loading of xclbin is denied.
>> +
>> +xclbin loading is requested using the ICAP_DOWNLOAD_AXLF ioctl command. When loading
>> +a xclbin, the xrt-mgmt driver performs the following *logical* operations:
>> +
>> +1. Copy xclbin from user to kernel memory
>> +2. Sanity check the xclbin contents
>> +3. Isolate the User partition
>> +4. Download the bitstream using the FPGA config engine (ICAP)
>> +5. De-isolate the User partition
>> +6. Program the clocks (ClockWiz) driving the User partition
>> +7. Wait for the memory controller (MIG) calibration
>> +8. Return the loading status back to the caller
>> +
>> +`Platform Loading Overview <https://xilinx.github.io/XRT/master/html/platforms_partitions.html>`_
>> +provides more detailed information on platform loading.
>> +
>> +
>> +xsabin
>> +------
>> +
>> +Each Alveo platform comes packaged with its own xsabin. The xsabin is a trusted
>> +component of the platform. For format details refer to :ref:`xsabin_xclbin_container_format`
>> +below. xsabin contains basic information like UUIDs, platform name and metadata in the
>> +form of device tree. See :ref:`device_tree_usage` below for details and example.
>> +
>> +xclbin
>> +------
>> +
>> +xclbin is compiled by end user using
>> +`Vitis <https://www.xilinx.com/products/design-tools/vitis/vitis-platform.html>`_
>> +tool set from Xilinx. The xclbin contains sections describing user compiled
>> +acceleration engines/kernels, memory subsystems, clocking information etc. It also
>> +contains an FPGA bitstream for the user partition, UUIDs, platform name, etc.
>> +
>> +
>> +.. _xsabin_xclbin_container_format:
>> +
>> +xsabin/xclbin Container Format
>> +------------------------------
>> +
>> +xclbin/xsabin is ELF-like binary container format. It is structured as series of
>> +sections. There is a file header followed by several section headers which is
>> +followed by sections. A section header points to an actual section. There is an
>> +optional signature at the end. The format is defined by the header file ``xclbin.h``.
>> +The following figure illustrates a typical xclbin::
>> +
>> +
>> + +---------------------+
>> + | |
>> + | HEADER |
>> + +---------------------+
>> + | SECTION HEADER |
>> + | |
>> + +---------------------+
>> + | ... |
>> + | |
>> + +---------------------+
>> + | SECTION HEADER |
>> + | |
>> + +---------------------+
>> + | SECTION |
>> + | |
>> + +---------------------+
>> + | ... |
>> + | |
>> + +---------------------+
>> + | SECTION |
>> + | |
>> + +---------------------+
>> + | SIGNATURE |
>> + | (OPTIONAL) |
>> + +---------------------+
>> +
>> +
>> +xclbin/xsabin files can be packaged, un-packaged and inspected using an XRT
>> +utility called **xclbinutil**. xclbinutil is part of the XRT open source
>> +software stack. The source code for xclbinutil can be found at
>> +https://github.com/Xilinx/XRT/tree/master/src/runtime_src/tools/xclbinutil
>> +
>> +For example, to enumerate the contents of a xclbin/xsabin use the *--info* switch
>> +as shown below::
>> +
>> +
>> + xclbinutil --info --input /opt/xilinx/firmware/u50/gen3x16-xdma/blp/test/bandwidth.xclbin
>> + xclbinutil --info --input /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/partition.xsabin
>> +
>> +
>> +.. _device_tree_usage:
>> +
>> +Device Tree Usage
>> +-----------------
>> +
>> +The xsabin file stores metadata which advertise HW subsystems present in a
>> +partition. The metadata is stored in device tree format with a well defined
>> +schema. XRT management driver uses this information to bind *xrt_drivers* to
>> +the subsystem instantiations. The xrt_drivers are found in **xrt-lib.ko** kernel
>> +module.
> I'm still catching up the patchset from the very beginning, and just
> finished the Documentation part. So far, I see the DT usage concern
> which may impact the architecure a lot, so I should raise it ASAP.
>
> The concern raised by the DT maintainer:
> https://lore.kernel.org/linux-fpga/CAL_JsqLod6FBGFhu7WXtMrB_z7wj8-up0EetM1QS9M3gjm8d7Q@mail.gmail.com/
>
> First of all, directly parsing FDT in device drivers is not a normal usage of DT
> in linux. It is out of the current DT usage model. So it should be agreed by DT
> maintainers.
Thanks for reviewing XRT document and providing feedback.
Here is the reply from Sonal for Rob’s question:
https://lore.kernel.org/linux-fpga/BY5PR02MB62604B87C66A1AD139A6F153BBF40@BY5PR02MB6260.namprd02.prod.outlook.com/
Overall, libfdt is used by XRT driver to parse the metadata which comes
with an Alveo board.
When XRT driver discovers an Alveo board, it will read a fdt blob from
board firmware file resident on the host.
By parsing the fdt blob, XRT driver gets information about this Alveo
board, such as version, uuid, IPs exposed to PCI BAR, interrupt binding etc.
So libfdt is used simply as Alveo metadata parser here. XRT drivers do
not interact with system wide DT or present the Alveo device tree to
host. For many systems like x86_64, system wide DT is not present but
libfdt parsing services will still be needed.
>
> Current FPGA framework modifies kernel's live tree by DT overlay, when FPGA is
> dynamically reprogrammed and new HW devices appear. See
> Documentation/devicetree/bindings/fpga/fpga-region.txt.
>
> Then something less important:
>
> 1. The bindings should be documented in Documentation/devicetree/bindings/.
> 2. Are all the example DT usage conform to the exsiting bindings? I
> didn't go through all device classes, but remember like the
> interrupt-controller should have a "interrupt-controller" property, and
> the PCI properties are also different from PCI bindings.
The fdt properties are defined for Alveo firmware files. XRT driver is
the only consumer of this data. I am wondering if
Documentation/devicetree/bindings is the right place for Alveo/XRT
private format or should it be documented as part of XRT driver
documentation? Looking for guidance here.
Thanks,
Lizhi
>
> Thanks,
> Yilun
>
>> +
>> +Logic UUID
>> +^^^^^^^^^^
>> +A partition is identified uniquely through ``logic_uuid`` property::
>> +
>> + /dts-v1/;
>> + / {
>> + logic_uuid = "0123456789abcdef0123456789abcdef";
>> + ...
>> + }
>> +
>> +Schema Version
>> +^^^^^^^^^^^^^^
>> +Schema version is defined through the ``schema_version`` node. It contains
>> +``major`` and ``minor`` properties as below::
>> +
>> + /dts-v1/;
>> + / {
>> + schema_version {
>> + major = <0x01>;
>> + minor = <0x00>;
>> + };
>> + ...
>> + }
>> +
>> +.. _partition_uuids:
>> +
>> +Partition UUIDs
>> +^^^^^^^^^^^^^^^
>> +Each partition may have parent and child UUIDs. These UUIDs are
>> +defined by ``interfaces`` node and ``interface_uuid`` property::
>> +
>> + /dts-v1/;
>> + / {
>> + interfaces {
>> + @0 {
>> + interface_uuid = "0123456789abcdef0123456789abcdef";
>> + };
>> + @1 {
>> + interface_uuid = "fedcba9876543210fedcba9876543210";
>> + };
>> + ...
>> + };
>> + ...
>> + }
>> +
>> +
>> +Subsystem Instantiations
>> +^^^^^^^^^^^^^^^^^^^^^^^^
>> +Subsystem instantiations are captured as children of ``addressable_endpoints``
>> +node::
>> +
>> + /dts-v1/;
>> + / {
>> + addressable_endpoints {
>> + abc {
>> + ...
>> + };
>> + def {
>> + ...
>> + };
>> + ...
>> + }
>> + }
>> +
>> +Subnode 'abc' and 'def' are the name of subsystem nodes
>> +
>> +Subsystem Node
>> +^^^^^^^^^^^^^^
>> +Each subsystem node and its properties define a hardware instance::
>> +
>> +
>> + addressable_endpoints {
>> + abc {
>> + reg = <0x00 0x1f05000 0x00 0x1000>>
>> + pcie_physical_function = <0x0>;
>> + pcie_bar_mapping = <0x2>;
>> + compatible = "abc def";
>> + interrupts = <0x09 0x0c>;
>> + firmware {
>> + firmware_product_name = "abc"
>> + firmware_branch_name = "def"
>> + firmware_version_major = <1>
>> + firmware_version_minor = <2>
>> + };
>> + }
>> + ...
>> + }
>> +
>> +:reg:
>> + Property defines an address range. `<0x00 0x1f05000 0x00 0x1000>` indicates
>> + *0x00 0x1f05000* as BAR offset and *0x00 0x1000* as address length.
>> +:pcie_physical_function:
>> + Property specifies which PCIe physical function the subsystem node resides.
>> + `<0x0>` implies physical function 0.
>> +:pcie_bar_mapping:
>> + Property specifies which PCIe BAR the subsystem node resides. `<0x2>` implies
>> + BAR 2. A value of 0 means the property is not defined.
>> +:compatible:
>> + Property is a list of strings. The first string in the list specifies the exact
>> + subsystem node. The following strings represent other devices that the device
>> + is compatible with.
>> +:interrupts:
>> + Property specifies start and end interrupts for this subsystem node.
>> + `<0x09 0x0c>` implies interrupts 9 to 13 are used by this subsystem.
>> +:firmware:
>> + Subnode defines the firmware required by this subsystem node.
>> +
>> +Alveo U50 Platform Example
>> +^^^^^^^^^^^^^^^^^^^^^^^^^^
>> +::
>> +
>> + /dts-v1/;
>> +
>> + /{
>> + logic_uuid = "f465b0a3ae8c64f619bc150384ace69b";
>> +
>> + schema_version {
>> + major = <0x01>;
>> + minor = <0x00>;
>> + };
>> +
>> + interfaces {
>> +
>> + @0 {
>> + interface_uuid = "862c7020a250293e32036f19956669e5";
>> + };
>> + };
>> +
>> + addressable_endpoints {
>> +
>> + ep_blp_rom_00 {
>> + reg = <0x00 0x1f04000 0x00 0x1000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
>> + };
>> +
>> + ep_card_flash_program_00 {
>> + reg = <0x00 0x1f06000 0x00 0x1000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_quad_spi-1.0\0axi_quad_spi";
>> + interrupts = <0x03 0x03>;
>> + };
>> +
>> + ep_cmc_firmware_mem_00 {
>> + reg = <0x00 0x1e20000 0x00 0x20000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
>> +
>> + firmware {
>> + firmware_product_name = "cmc";
>> + firmware_branch_name = "u50";
>> + firmware_version_major = <0x01>;
>> + firmware_version_minor = <0x00>;
>> + };
>> + };
>> +
>> + ep_cmc_intc_00 {
>> + reg = <0x00 0x1e03000 0x00 0x1000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";
>> + interrupts = <0x04 0x04>;
>> + };
>> +
>> + ep_cmc_mutex_00 {
>> + reg = <0x00 0x1e02000 0x00 0x1000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>> + };
>> +
>> + ep_cmc_regmap_00 {
>> + reg = <0x00 0x1e08000 0x00 0x2000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
>> +
>> + firmware {
>> + firmware_product_name = "sc-fw";
>> + firmware_branch_name = "u50";
>> + firmware_version_major = <0x05>;
>> + };
>> + };
>> +
>> + ep_cmc_reset_00 {
>> + reg = <0x00 0x1e01000 0x00 0x1000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>> + };
>> +
>> + ep_ddr_mem_calib_00 {
>> + reg = <0x00 0x63000 0x00 0x1000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>> + };
>> +
>> + ep_debug_bscan_mgmt_00 {
>> + reg = <0x00 0x1e90000 0x00 0x10000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-debug_bridge-1.0\0debug_bridge";
>> + };
>> +
>> + ep_ert_base_address_00 {
>> + reg = <0x00 0x21000 0x00 0x1000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>> + };
>> +
>> + ep_ert_command_queue_mgmt_00 {
>> + reg = <0x00 0x40000 0x00 0x10000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-ert_command_queue-1.0\0ert_command_queue";
>> + };
>> +
>> + ep_ert_command_queue_user_00 {
>> + reg = <0x00 0x40000 0x00 0x10000>;
>> + pcie_physical_function = <0x01>;
>> + compatible = "xilinx.com,reg_abs-ert_command_queue-1.0\0ert_command_queue";
>> + };
>> +
>> + ep_ert_firmware_mem_00 {
>> + reg = <0x00 0x30000 0x00 0x8000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
>> +
>> + firmware {
>> + firmware_product_name = "ert";
>> + firmware_branch_name = "v20";
>> + firmware_version_major = <0x01>;
>> + };
>> + };
>> +
>> + ep_ert_intc_00 {
>> + reg = <0x00 0x23000 0x00 0x1000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";
>> + interrupts = <0x05 0x05>;
>> + };
>> +
>> + ep_ert_reset_00 {
>> + reg = <0x00 0x22000 0x00 0x1000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>> + };
>> +
>> + ep_ert_sched_00 {
>> + reg = <0x00 0x50000 0x00 0x1000>;
>> + pcie_physical_function = <0x01>;
>> + compatible = "xilinx.com,reg_abs-ert_sched-1.0\0ert_sched";
>> + interrupts = <0x09 0x0c>;
>> + };
>> +
>> + ep_fpga_configuration_00 {
>> + reg = <0x00 0x1e88000 0x00 0x8000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_hwicap-1.0\0axi_hwicap";
>> + interrupts = <0x02 0x02>;
>> + };
>> +
>> + ep_icap_reset_00 {
>> + reg = <0x00 0x1f07000 0x00 0x1000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>> + };
>> +
>> + ep_msix_00 {
>> + reg = <0x00 0x00 0x00 0x20000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-msix-1.0\0msix";
>> + pcie_bar_mapping = <0x02>;
>> + };
>> +
>> + ep_pcie_link_mon_00 {
>> + reg = <0x00 0x1f05000 0x00 0x1000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>> + };
>> +
>> + ep_pr_isolate_plp_00 {
>> + reg = <0x00 0x1f01000 0x00 0x1000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>> + };
>> +
>> + ep_pr_isolate_ulp_00 {
>> + reg = <0x00 0x1000 0x00 0x1000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>> + };
>> +
>> + ep_uuid_rom_00 {
>> + reg = <0x00 0x64000 0x00 0x1000>;
>> + pcie_physical_function = <0x00>;
>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
>> + };
>> +
>> + ep_xdma_00 {
>> + reg = <0x00 0x00 0x00 0x10000>;
>> + pcie_physical_function = <0x01>;
>> + compatible = "xilinx.com,reg_abs-xdma-1.0\0xdma";
>> + pcie_bar_mapping = <0x02>;
>> + };
>> + };
>> +
>> + }
>> +
>> +
>> +
>> +Deployment Models
>> +=================
>> +
>> +Baremetal
>> +---------
>> +
>> +In bare-metal deployments, both MPF and UPF are visible and accessible. The
>> +xrt-mgmt driver binds to MPF. The xrt-mgmt driver operations are privileged and
>> +available to system administrator. The full stack is illustrated below::
>> +
>> + HOST
>> +
>> + [XRT-MGMT] [XRT-USER]
>> + | |
>> + | |
>> + +-----+ +-----+
>> + | MPF | | UPF |
>> + | | | |
>> + | PF0 | | PF1 |
>> + +--+--+ +--+--+
>> + ......... ^................. ^..........
>> + | |
>> + | PCIe DEVICE |
>> + | |
>> + +--+------------------+--+
>> + | SHELL |
>> + | |
>> + +------------------------+
>> + | USER |
>> + | |
>> + | |
>> + | |
>> + | |
>> + +------------------------+
>> +
>> +
>> +
>> +Virtualized
>> +-----------
>> +
>> +In virtualized deployments, the privileged MPF is assigned to the host but the
>> +unprivileged UPF is assigned to a guest VM via PCIe pass-through. The xrt-mgmt
>> +driver in host binds to MPF. The xrt-mgmt driver operations are privileged and
>> +only accessible to the MPF. The full stack is illustrated below::
>> +
>> +
>> + ..............
>> + HOST . VM .
>> + . .
>> + [XRT-MGMT] . [XRT-USER] .
>> + | . | .
>> + | . | .
>> + +-----+ . +-----+ .
>> + | MPF | . | UPF | .
>> + | | . | | .
>> + | PF0 | . | PF1 | .
>> + +--+--+ . +--+--+ .
>> + ......... ^................. ^..........
>> + | |
>> + | PCIe DEVICE |
>> + | |
>> + +--+------------------+--+
>> + | SHELL |
>> + | |
>> + +------------------------+
>> + | USER |
>> + | |
>> + | |
>> + | |
>> + | |
>> + +------------------------+
>> +
>> +
>> +
>> +
>> +
>> +Platform Security Considerations
>> +================================
>> +
>> +`Security of Alveo Platform <https://xilinx.github.io/XRT/master/html/security.html>`_
>> +discusses the deployment options and security implications in great detail.
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 056966c9aac9..beeaf0257364 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -7274,6 +7274,17 @@ F: Documentation/fpga/
>> F: drivers/fpga/
>> F: include/linux/fpga/
>>
>> +FPGA XRT DRIVERS
>> +M: Lizhi Hou <[email protected]>
>> +R: Max Zhen <[email protected]>
>> +R: Sonal Santan <[email protected]>
>> +L: [email protected]
>> +S: Supported
>> +W: https://github.com/Xilinx/XRT
>> +F: Documentation/fpga/xrt.rst
>> +F: drivers/fpga/xrt/
>> +F: include/uapi/linux/xrt/
>> +
>> FPU EMULATOR
>> M: Bill Metzenthen <[email protected]>
>> S: Maintained
>> --
>> 2.27.0
> > > +.. _device_tree_usage:
> > > +
> > > +Device Tree Usage
> > > +-----------------
> > > +
> > > +The xsabin file stores metadata which advertise HW subsystems present in a
> > > +partition. The metadata is stored in device tree format with a well defined
> > > +schema. XRT management driver uses this information to bind *xrt_drivers* to
> > > +the subsystem instantiations. The xrt_drivers are found in **xrt-lib.ko** kernel
> > > +module.
> > I'm still catching up the patchset from the very beginning, and just
> > finished the Documentation part. So far, I see the DT usage concern
> > which may impact the architecure a lot, so I should raise it ASAP.
> >
> > The concern raised by the DT maintainer:
> > https://lore.kernel.org/linux-fpga/CAL_JsqLod6FBGFhu7WXtMrB_z7wj8-up0EetM1QS9M3gjm8d7Q@mail.gmail.com/
> >
> > First of all, directly parsing FDT in device drivers is not a normal usage of DT
> > in linux. It is out of the current DT usage model. So it should be agreed by DT
> > maintainers.
> Thanks for reviewing XRT document and providing feedback.
> Here is the reply from Sonal for Rob’s question:
> https://lore.kernel.org/linux-fpga/BY5PR02MB62604B87C66A1AD139A6F153BBF40@BY5PR02MB6260.namprd02.prod.outlook.com/
> Overall, libfdt is used by XRT driver to parse the metadata which comes with
> an Alveo board.
> When XRT driver discovers an Alveo board, it will read a fdt blob from board
> firmware file resident on the host.
> By parsing the fdt blob, XRT driver gets information about this Alveo board,
> such as version, uuid, IPs exposed to PCI BAR, interrupt binding etc.
> So libfdt is used simply as Alveo metadata parser here. XRT drivers do not
> interact with system wide DT or present the Alveo device tree to host. For
> many systems like x86_64, system wide DT is not present but libfdt parsing
> services will still be needed.
Yes, I understand the use case.
My concern is, directly parsing an isolated FDT in device driver and
populate sub devices, skipping the unflattening, this is a new working
model of device tree usage, but for the same purpose as the existing
one.
So I really need the confirmation of DT maintainers.
> >
> > Current FPGA framework modifies kernel's live tree by DT overlay, when FPGA is
> > dynamically reprogrammed and new HW devices appear. See
> > Documentation/devicetree/bindings/fpga/fpga-region.txt.
> >
> > Then something less important:
> >
> > 1. The bindings should be documented in Documentation/devicetree/bindings/.
> > 2. Are all the example DT usage conform to the exsiting bindings? I
> > didn't go through all device classes, but remember like the
> > interrupt-controller should have a "interrupt-controller" property, and
> > the PCI properties are also different from PCI bindings.
>
> The fdt properties are defined for Alveo firmware files. XRT driver is the
> only consumer of this data. I am wondering if
Personally I don't like the idea of a different binding definition set,
even if the new device tree usage is accepted. We all use device tree
for device enumeration, so why the different definitions.
Thanks,
Yilun
> Documentation/devicetree/bindings is the right place for Alveo/XRT private
> format or should it be documented as part of XRT driver documentation?
> Looking for guidance here.
>
>
> Thanks,
>
> Lizhi
>
> >
> > Thanks,
> > Yilun
> >
> > > +
> > > +Logic UUID
> > > +^^^^^^^^^^
> > > +A partition is identified uniquely through ``logic_uuid`` property::
> > > +
> > > + /dts-v1/;
> > > + / {
> > > + logic_uuid = "0123456789abcdef0123456789abcdef";
> > > + ...
> > > + }
> > > +
> > > +Schema Version
> > > +^^^^^^^^^^^^^^
> > > +Schema version is defined through the ``schema_version`` node. It contains
> > > +``major`` and ``minor`` properties as below::
> > > +
> > > + /dts-v1/;
> > > + / {
> > > + schema_version {
> > > + major = <0x01>;
> > > + minor = <0x00>;
> > > + };
> > > + ...
> > > + }
> > > +
> > > +.. _partition_uuids:
> > > +
> > > +Partition UUIDs
> > > +^^^^^^^^^^^^^^^
> > > +Each partition may have parent and child UUIDs. These UUIDs are
> > > +defined by ``interfaces`` node and ``interface_uuid`` property::
> > > +
> > > + /dts-v1/;
> > > + / {
> > > + interfaces {
> > > + @0 {
> > > + interface_uuid = "0123456789abcdef0123456789abcdef";
> > > + };
> > > + @1 {
> > > + interface_uuid = "fedcba9876543210fedcba9876543210";
> > > + };
> > > + ...
> > > + };
> > > + ...
> > > + }
> > > +
> > > +
> > > +Subsystem Instantiations
> > > +^^^^^^^^^^^^^^^^^^^^^^^^
> > > +Subsystem instantiations are captured as children of ``addressable_endpoints``
> > > +node::
> > > +
> > > + /dts-v1/;
> > > + / {
> > > + addressable_endpoints {
> > > + abc {
> > > + ...
> > > + };
> > > + def {
> > > + ...
> > > + };
> > > + ...
> > > + }
> > > + }
> > > +
> > > +Subnode 'abc' and 'def' are the name of subsystem nodes
> > > +
> > > +Subsystem Node
> > > +^^^^^^^^^^^^^^
> > > +Each subsystem node and its properties define a hardware instance::
> > > +
> > > +
> > > + addressable_endpoints {
> > > + abc {
> > > + reg = <0x00 0x1f05000 0x00 0x1000>>
> > > + pcie_physical_function = <0x0>;
> > > + pcie_bar_mapping = <0x2>;
> > > + compatible = "abc def";
> > > + interrupts = <0x09 0x0c>;
> > > + firmware {
> > > + firmware_product_name = "abc"
> > > + firmware_branch_name = "def"
> > > + firmware_version_major = <1>
> > > + firmware_version_minor = <2>
> > > + };
> > > + }
> > > + ...
> > > + }
> > > +
> > > +:reg:
> > > + Property defines an address range. `<0x00 0x1f05000 0x00 0x1000>` indicates
> > > + *0x00 0x1f05000* as BAR offset and *0x00 0x1000* as address length.
> > > +:pcie_physical_function:
> > > + Property specifies which PCIe physical function the subsystem node resides.
> > > + `<0x0>` implies physical function 0.
> > > +:pcie_bar_mapping:
> > > + Property specifies which PCIe BAR the subsystem node resides. `<0x2>` implies
> > > + BAR 2. A value of 0 means the property is not defined.
> > > +:compatible:
> > > + Property is a list of strings. The first string in the list specifies the exact
> > > + subsystem node. The following strings represent other devices that the device
> > > + is compatible with.
> > > +:interrupts:
> > > + Property specifies start and end interrupts for this subsystem node.
> > > + `<0x09 0x0c>` implies interrupts 9 to 13 are used by this subsystem.
> > > +:firmware:
> > > + Subnode defines the firmware required by this subsystem node.
> > > +
> > > +Alveo U50 Platform Example
> > > +^^^^^^^^^^^^^^^^^^^^^^^^^^
> > > +::
> > > +
> > > + /dts-v1/;
> > > +
> > > + /{
> > > + logic_uuid = "f465b0a3ae8c64f619bc150384ace69b";
> > > +
> > > + schema_version {
> > > + major = <0x01>;
> > > + minor = <0x00>;
> > > + };
> > > +
> > > + interfaces {
> > > +
> > > + @0 {
> > > + interface_uuid = "862c7020a250293e32036f19956669e5";
> > > + };
> > > + };
> > > +
> > > + addressable_endpoints {
> > > +
> > > + ep_blp_rom_00 {
> > > + reg = <0x00 0x1f04000 0x00 0x1000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> > > + };
> > > +
> > > + ep_card_flash_program_00 {
> > > + reg = <0x00 0x1f06000 0x00 0x1000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_quad_spi-1.0\0axi_quad_spi";
> > > + interrupts = <0x03 0x03>;
> > > + };
> > > +
> > > + ep_cmc_firmware_mem_00 {
> > > + reg = <0x00 0x1e20000 0x00 0x20000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> > > +
> > > + firmware {
> > > + firmware_product_name = "cmc";
> > > + firmware_branch_name = "u50";
> > > + firmware_version_major = <0x01>;
> > > + firmware_version_minor = <0x00>;
> > > + };
> > > + };
> > > +
> > > + ep_cmc_intc_00 {
> > > + reg = <0x00 0x1e03000 0x00 0x1000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";
> > > + interrupts = <0x04 0x04>;
> > > + };
> > > +
> > > + ep_cmc_mutex_00 {
> > > + reg = <0x00 0x1e02000 0x00 0x1000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> > > + };
> > > +
> > > + ep_cmc_regmap_00 {
> > > + reg = <0x00 0x1e08000 0x00 0x2000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> > > +
> > > + firmware {
> > > + firmware_product_name = "sc-fw";
> > > + firmware_branch_name = "u50";
> > > + firmware_version_major = <0x05>;
> > > + };
> > > + };
> > > +
> > > + ep_cmc_reset_00 {
> > > + reg = <0x00 0x1e01000 0x00 0x1000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> > > + };
> > > +
> > > + ep_ddr_mem_calib_00 {
> > > + reg = <0x00 0x63000 0x00 0x1000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> > > + };
> > > +
> > > + ep_debug_bscan_mgmt_00 {
> > > + reg = <0x00 0x1e90000 0x00 0x10000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-debug_bridge-1.0\0debug_bridge";
> > > + };
> > > +
> > > + ep_ert_base_address_00 {
> > > + reg = <0x00 0x21000 0x00 0x1000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> > > + };
> > > +
> > > + ep_ert_command_queue_mgmt_00 {
> > > + reg = <0x00 0x40000 0x00 0x10000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-ert_command_queue-1.0\0ert_command_queue";
> > > + };
> > > +
> > > + ep_ert_command_queue_user_00 {
> > > + reg = <0x00 0x40000 0x00 0x10000>;
> > > + pcie_physical_function = <0x01>;
> > > + compatible = "xilinx.com,reg_abs-ert_command_queue-1.0\0ert_command_queue";
> > > + };
> > > +
> > > + ep_ert_firmware_mem_00 {
> > > + reg = <0x00 0x30000 0x00 0x8000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> > > +
> > > + firmware {
> > > + firmware_product_name = "ert";
> > > + firmware_branch_name = "v20";
> > > + firmware_version_major = <0x01>;
> > > + };
> > > + };
> > > +
> > > + ep_ert_intc_00 {
> > > + reg = <0x00 0x23000 0x00 0x1000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";
> > > + interrupts = <0x05 0x05>;
> > > + };
> > > +
> > > + ep_ert_reset_00 {
> > > + reg = <0x00 0x22000 0x00 0x1000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> > > + };
> > > +
> > > + ep_ert_sched_00 {
> > > + reg = <0x00 0x50000 0x00 0x1000>;
> > > + pcie_physical_function = <0x01>;
> > > + compatible = "xilinx.com,reg_abs-ert_sched-1.0\0ert_sched";
> > > + interrupts = <0x09 0x0c>;
> > > + };
> > > +
> > > + ep_fpga_configuration_00 {
> > > + reg = <0x00 0x1e88000 0x00 0x8000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_hwicap-1.0\0axi_hwicap";
> > > + interrupts = <0x02 0x02>;
> > > + };
> > > +
> > > + ep_icap_reset_00 {
> > > + reg = <0x00 0x1f07000 0x00 0x1000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> > > + };
> > > +
> > > + ep_msix_00 {
> > > + reg = <0x00 0x00 0x00 0x20000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-msix-1.0\0msix";
> > > + pcie_bar_mapping = <0x02>;
> > > + };
> > > +
> > > + ep_pcie_link_mon_00 {
> > > + reg = <0x00 0x1f05000 0x00 0x1000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> > > + };
> > > +
> > > + ep_pr_isolate_plp_00 {
> > > + reg = <0x00 0x1f01000 0x00 0x1000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> > > + };
> > > +
> > > + ep_pr_isolate_ulp_00 {
> > > + reg = <0x00 0x1000 0x00 0x1000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> > > + };
> > > +
> > > + ep_uuid_rom_00 {
> > > + reg = <0x00 0x64000 0x00 0x1000>;
> > > + pcie_physical_function = <0x00>;
> > > + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> > > + };
> > > +
> > > + ep_xdma_00 {
> > > + reg = <0x00 0x00 0x00 0x10000>;
> > > + pcie_physical_function = <0x01>;
> > > + compatible = "xilinx.com,reg_abs-xdma-1.0\0xdma";
> > > + pcie_bar_mapping = <0x02>;
> > > + };
> > > + };
> > > +
> > > + }
> > > +
> > > +
> > > +
> > > +Deployment Models
> > > +=================
> > > +
> > > +Baremetal
> > > +---------
> > > +
> > > +In bare-metal deployments, both MPF and UPF are visible and accessible. The
> > > +xrt-mgmt driver binds to MPF. The xrt-mgmt driver operations are privileged and
> > > +available to system administrator. The full stack is illustrated below::
> > > +
> > > + HOST
> > > +
> > > + [XRT-MGMT] [XRT-USER]
> > > + | |
> > > + | |
> > > + +-----+ +-----+
> > > + | MPF | | UPF |
> > > + | | | |
> > > + | PF0 | | PF1 |
> > > + +--+--+ +--+--+
> > > + ......... ^................. ^..........
> > > + | |
> > > + | PCIe DEVICE |
> > > + | |
> > > + +--+------------------+--+
> > > + | SHELL |
> > > + | |
> > > + +------------------------+
> > > + | USER |
> > > + | |
> > > + | |
> > > + | |
> > > + | |
> > > + +------------------------+
> > > +
> > > +
> > > +
> > > +Virtualized
> > > +-----------
> > > +
> > > +In virtualized deployments, the privileged MPF is assigned to the host but the
> > > +unprivileged UPF is assigned to a guest VM via PCIe pass-through. The xrt-mgmt
> > > +driver in host binds to MPF. The xrt-mgmt driver operations are privileged and
> > > +only accessible to the MPF. The full stack is illustrated below::
> > > +
> > > +
> > > + ..............
> > > + HOST . VM .
> > > + . .
> > > + [XRT-MGMT] . [XRT-USER] .
> > > + | . | .
> > > + | . | .
> > > + +-----+ . +-----+ .
> > > + | MPF | . | UPF | .
> > > + | | . | | .
> > > + | PF0 | . | PF1 | .
> > > + +--+--+ . +--+--+ .
> > > + ......... ^................. ^..........
> > > + | |
> > > + | PCIe DEVICE |
> > > + | |
> > > + +--+------------------+--+
> > > + | SHELL |
> > > + | |
> > > + +------------------------+
> > > + | USER |
> > > + | |
> > > + | |
> > > + | |
> > > + | |
> > > + +------------------------+
> > > +
> > > +
> > > +
> > > +
> > > +
> > > +Platform Security Considerations
> > > +================================
> > > +
> > > +`Security of Alveo Platform <https://xilinx.github.io/XRT/master/html/security.html>`_
> > > +discusses the deployment options and security implications in great detail.
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index 056966c9aac9..beeaf0257364 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -7274,6 +7274,17 @@ F: Documentation/fpga/
> > > F: drivers/fpga/
> > > F: include/linux/fpga/
> > >
> > > +FPGA XRT DRIVERS
> > > +M: Lizhi Hou <[email protected]>
> > > +R: Max Zhen <[email protected]>
> > > +R: Sonal Santan <[email protected]>
> > > +L: [email protected]
> > > +S: Supported
> > > +W: https://github.com/Xilinx/XRT
> > > +F: Documentation/fpga/xrt.rst
> > > +F: drivers/fpga/xrt/
> > > +F: include/uapi/linux/xrt/
> > > +
> > > FPU EMULATOR
> > > M: Bill Metzenthen <[email protected]>
> > > S: Maintained
> > > --
> > > 2.27.0
Hello Rob,
Please help with the review of the proposed FDT usage by Alveo/XRT drivers.
Thanks,
Lizhi
On 10/13/21 7:21 PM, Xu Yilun wrote:
> CAUTION: This message has originated from an External Source. Please use proper judgment and caution when opening attachments, clicking links, or responding to this email.
>
>
>>>> +.. _device_tree_usage:
>>>> +
>>>> +Device Tree Usage
>>>> +-----------------
>>>> +
>>>> +The xsabin file stores metadata which advertise HW subsystems present in a
>>>> +partition. The metadata is stored in device tree format with a well defined
>>>> +schema. XRT management driver uses this information to bind *xrt_drivers* to
>>>> +the subsystem instantiations. The xrt_drivers are found in **xrt-lib.ko** kernel
>>>> +module.
>>> I'm still catching up the patchset from the very beginning, and just
>>> finished the Documentation part. So far, I see the DT usage concern
>>> which may impact the architecure a lot, so I should raise it ASAP.
>>>
>>> The concern raised by the DT maintainer:
>>> https://lore.kernel.org/linux-fpga/CAL_JsqLod6FBGFhu7WXtMrB_z7wj8-up0EetM1QS9M3gjm8d7Q@mail.gmail.com/
>>>
>>> First of all, directly parsing FDT in device drivers is not a normal usage of DT
>>> in linux. It is out of the current DT usage model. So it should be agreed by DT
>>> maintainers.
>> Thanks for reviewing XRT document and providing feedback.
>> Here is the reply from Sonal for Rob’s question:
>> https://lore.kernel.org/linux-fpga/BY5PR02MB62604B87C66A1AD139A6F153BBF40@BY5PR02MB6260.namprd02.prod.outlook.com/
>> Overall, libfdt is used by XRT driver to parse the metadata which comes with
>> an Alveo board.
>> When XRT driver discovers an Alveo board, it will read a fdt blob from board
>> firmware file resident on the host.
>> By parsing the fdt blob, XRT driver gets information about this Alveo board,
>> such as version, uuid, IPs exposed to PCI BAR, interrupt binding etc.
>> So libfdt is used simply as Alveo metadata parser here. XRT drivers do not
>> interact with system wide DT or present the Alveo device tree to host. For
>> many systems like x86_64, system wide DT is not present but libfdt parsing
>> services will still be needed.
> Yes, I understand the use case.
>
> My concern is, directly parsing an isolated FDT in device driver and
> populate sub devices, skipping the unflattening, this is a new working
> model of device tree usage, but for the same purpose as the existing
> one.
>
> So I really need the confirmation of DT maintainers.
>
>>> Current FPGA framework modifies kernel's live tree by DT overlay, when FPGA is
>>> dynamically reprogrammed and new HW devices appear. See
>>> Documentation/devicetree/bindings/fpga/fpga-region.txt.
>>>
>>> Then something less important:
>>>
>>> 1. The bindings should be documented in Documentation/devicetree/bindings/.
>>> 2. Are all the example DT usage conform to the exsiting bindings? I
>>> didn't go through all device classes, but remember like the
>>> interrupt-controller should have a "interrupt-controller" property, and
>>> the PCI properties are also different from PCI bindings.
>> The fdt properties are defined for Alveo firmware files. XRT driver is the
>> only consumer of this data. I am wondering if
> Personally I don't like the idea of a different binding definition set,
> even if the new device tree usage is accepted. We all use device tree
> for device enumeration, so why the different definitions.
>
> Thanks,
> Yilun
>
>> Documentation/devicetree/bindings is the right place for Alveo/XRT private
>> format or should it be documented as part of XRT driver documentation?
>> Looking for guidance here.
>>
>>
>> Thanks,
>>
>> Lizhi
>>
>>> Thanks,
>>> Yilun
>>>
>>>> +
>>>> +Logic UUID
>>>> +^^^^^^^^^^
>>>> +A partition is identified uniquely through ``logic_uuid`` property::
>>>> +
>>>> + /dts-v1/;
>>>> + / {
>>>> + logic_uuid = "0123456789abcdef0123456789abcdef";
>>>> + ...
>>>> + }
>>>> +
>>>> +Schema Version
>>>> +^^^^^^^^^^^^^^
>>>> +Schema version is defined through the ``schema_version`` node. It contains
>>>> +``major`` and ``minor`` properties as below::
>>>> +
>>>> + /dts-v1/;
>>>> + / {
>>>> + schema_version {
>>>> + major = <0x01>;
>>>> + minor = <0x00>;
>>>> + };
>>>> + ...
>>>> + }
>>>> +
>>>> +.. _partition_uuids:
>>>> +
>>>> +Partition UUIDs
>>>> +^^^^^^^^^^^^^^^
>>>> +Each partition may have parent and child UUIDs. These UUIDs are
>>>> +defined by ``interfaces`` node and ``interface_uuid`` property::
>>>> +
>>>> + /dts-v1/;
>>>> + / {
>>>> + interfaces {
>>>> + @0 {
>>>> + interface_uuid = "0123456789abcdef0123456789abcdef";
>>>> + };
>>>> + @1 {
>>>> + interface_uuid = "fedcba9876543210fedcba9876543210";
>>>> + };
>>>> + ...
>>>> + };
>>>> + ...
>>>> + }
>>>> +
>>>> +
>>>> +Subsystem Instantiations
>>>> +^^^^^^^^^^^^^^^^^^^^^^^^
>>>> +Subsystem instantiations are captured as children of ``addressable_endpoints``
>>>> +node::
>>>> +
>>>> + /dts-v1/;
>>>> + / {
>>>> + addressable_endpoints {
>>>> + abc {
>>>> + ...
>>>> + };
>>>> + def {
>>>> + ...
>>>> + };
>>>> + ...
>>>> + }
>>>> + }
>>>> +
>>>> +Subnode 'abc' and 'def' are the name of subsystem nodes
>>>> +
>>>> +Subsystem Node
>>>> +^^^^^^^^^^^^^^
>>>> +Each subsystem node and its properties define a hardware instance::
>>>> +
>>>> +
>>>> + addressable_endpoints {
>>>> + abc {
>>>> + reg = <0x00 0x1f05000 0x00 0x1000>>
>>>> + pcie_physical_function = <0x0>;
>>>> + pcie_bar_mapping = <0x2>;
>>>> + compatible = "abc def";
>>>> + interrupts = <0x09 0x0c>;
>>>> + firmware {
>>>> + firmware_product_name = "abc"
>>>> + firmware_branch_name = "def"
>>>> + firmware_version_major = <1>
>>>> + firmware_version_minor = <2>
>>>> + };
>>>> + }
>>>> + ...
>>>> + }
>>>> +
>>>> +:reg:
>>>> + Property defines an address range. `<0x00 0x1f05000 0x00 0x1000>` indicates
>>>> + *0x00 0x1f05000* as BAR offset and *0x00 0x1000* as address length.
>>>> +:pcie_physical_function:
>>>> + Property specifies which PCIe physical function the subsystem node resides.
>>>> + `<0x0>` implies physical function 0.
>>>> +:pcie_bar_mapping:
>>>> + Property specifies which PCIe BAR the subsystem node resides. `<0x2>` implies
>>>> + BAR 2. A value of 0 means the property is not defined.
>>>> +:compatible:
>>>> + Property is a list of strings. The first string in the list specifies the exact
>>>> + subsystem node. The following strings represent other devices that the device
>>>> + is compatible with.
>>>> +:interrupts:
>>>> + Property specifies start and end interrupts for this subsystem node.
>>>> + `<0x09 0x0c>` implies interrupts 9 to 13 are used by this subsystem.
>>>> +:firmware:
>>>> + Subnode defines the firmware required by this subsystem node.
>>>> +
>>>> +Alveo U50 Platform Example
>>>> +^^^^^^^^^^^^^^^^^^^^^^^^^^
>>>> +::
>>>> +
>>>> + /dts-v1/;
>>>> +
>>>> + /{
>>>> + logic_uuid = "f465b0a3ae8c64f619bc150384ace69b";
>>>> +
>>>> + schema_version {
>>>> + major = <0x01>;
>>>> + minor = <0x00>;
>>>> + };
>>>> +
>>>> + interfaces {
>>>> +
>>>> + @0 {
>>>> + interface_uuid = "862c7020a250293e32036f19956669e5";
>>>> + };
>>>> + };
>>>> +
>>>> + addressable_endpoints {
>>>> +
>>>> + ep_blp_rom_00 {
>>>> + reg = <0x00 0x1f04000 0x00 0x1000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
>>>> + };
>>>> +
>>>> + ep_card_flash_program_00 {
>>>> + reg = <0x00 0x1f06000 0x00 0x1000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_quad_spi-1.0\0axi_quad_spi";
>>>> + interrupts = <0x03 0x03>;
>>>> + };
>>>> +
>>>> + ep_cmc_firmware_mem_00 {
>>>> + reg = <0x00 0x1e20000 0x00 0x20000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
>>>> +
>>>> + firmware {
>>>> + firmware_product_name = "cmc";
>>>> + firmware_branch_name = "u50";
>>>> + firmware_version_major = <0x01>;
>>>> + firmware_version_minor = <0x00>;
>>>> + };
>>>> + };
>>>> +
>>>> + ep_cmc_intc_00 {
>>>> + reg = <0x00 0x1e03000 0x00 0x1000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";
>>>> + interrupts = <0x04 0x04>;
>>>> + };
>>>> +
>>>> + ep_cmc_mutex_00 {
>>>> + reg = <0x00 0x1e02000 0x00 0x1000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>>>> + };
>>>> +
>>>> + ep_cmc_regmap_00 {
>>>> + reg = <0x00 0x1e08000 0x00 0x2000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
>>>> +
>>>> + firmware {
>>>> + firmware_product_name = "sc-fw";
>>>> + firmware_branch_name = "u50";
>>>> + firmware_version_major = <0x05>;
>>>> + };
>>>> + };
>>>> +
>>>> + ep_cmc_reset_00 {
>>>> + reg = <0x00 0x1e01000 0x00 0x1000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>>>> + };
>>>> +
>>>> + ep_ddr_mem_calib_00 {
>>>> + reg = <0x00 0x63000 0x00 0x1000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>>>> + };
>>>> +
>>>> + ep_debug_bscan_mgmt_00 {
>>>> + reg = <0x00 0x1e90000 0x00 0x10000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-debug_bridge-1.0\0debug_bridge";
>>>> + };
>>>> +
>>>> + ep_ert_base_address_00 {
>>>> + reg = <0x00 0x21000 0x00 0x1000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>>>> + };
>>>> +
>>>> + ep_ert_command_queue_mgmt_00 {
>>>> + reg = <0x00 0x40000 0x00 0x10000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-ert_command_queue-1.0\0ert_command_queue";
>>>> + };
>>>> +
>>>> + ep_ert_command_queue_user_00 {
>>>> + reg = <0x00 0x40000 0x00 0x10000>;
>>>> + pcie_physical_function = <0x01>;
>>>> + compatible = "xilinx.com,reg_abs-ert_command_queue-1.0\0ert_command_queue";
>>>> + };
>>>> +
>>>> + ep_ert_firmware_mem_00 {
>>>> + reg = <0x00 0x30000 0x00 0x8000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
>>>> +
>>>> + firmware {
>>>> + firmware_product_name = "ert";
>>>> + firmware_branch_name = "v20";
>>>> + firmware_version_major = <0x01>;
>>>> + };
>>>> + };
>>>> +
>>>> + ep_ert_intc_00 {
>>>> + reg = <0x00 0x23000 0x00 0x1000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";
>>>> + interrupts = <0x05 0x05>;
>>>> + };
>>>> +
>>>> + ep_ert_reset_00 {
>>>> + reg = <0x00 0x22000 0x00 0x1000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>>>> + };
>>>> +
>>>> + ep_ert_sched_00 {
>>>> + reg = <0x00 0x50000 0x00 0x1000>;
>>>> + pcie_physical_function = <0x01>;
>>>> + compatible = "xilinx.com,reg_abs-ert_sched-1.0\0ert_sched";
>>>> + interrupts = <0x09 0x0c>;
>>>> + };
>>>> +
>>>> + ep_fpga_configuration_00 {
>>>> + reg = <0x00 0x1e88000 0x00 0x8000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_hwicap-1.0\0axi_hwicap";
>>>> + interrupts = <0x02 0x02>;
>>>> + };
>>>> +
>>>> + ep_icap_reset_00 {
>>>> + reg = <0x00 0x1f07000 0x00 0x1000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>>>> + };
>>>> +
>>>> + ep_msix_00 {
>>>> + reg = <0x00 0x00 0x00 0x20000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-msix-1.0\0msix";
>>>> + pcie_bar_mapping = <0x02>;
>>>> + };
>>>> +
>>>> + ep_pcie_link_mon_00 {
>>>> + reg = <0x00 0x1f05000 0x00 0x1000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>>>> + };
>>>> +
>>>> + ep_pr_isolate_plp_00 {
>>>> + reg = <0x00 0x1f01000 0x00 0x1000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>>>> + };
>>>> +
>>>> + ep_pr_isolate_ulp_00 {
>>>> + reg = <0x00 0x1000 0x00 0x1000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
>>>> + };
>>>> +
>>>> + ep_uuid_rom_00 {
>>>> + reg = <0x00 0x64000 0x00 0x1000>;
>>>> + pcie_physical_function = <0x00>;
>>>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
>>>> + };
>>>> +
>>>> + ep_xdma_00 {
>>>> + reg = <0x00 0x00 0x00 0x10000>;
>>>> + pcie_physical_function = <0x01>;
>>>> + compatible = "xilinx.com,reg_abs-xdma-1.0\0xdma";
>>>> + pcie_bar_mapping = <0x02>;
>>>> + };
>>>> + };
>>>> +
>>>> + }
>>>> +
>>>> +
>>>> +
>>>> +Deployment Models
>>>> +=================
>>>> +
>>>> +Baremetal
>>>> +---------
>>>> +
>>>> +In bare-metal deployments, both MPF and UPF are visible and accessible. The
>>>> +xrt-mgmt driver binds to MPF. The xrt-mgmt driver operations are privileged and
>>>> +available to system administrator. The full stack is illustrated below::
>>>> +
>>>> + HOST
>>>> +
>>>> + [XRT-MGMT] [XRT-USER]
>>>> + | |
>>>> + | |
>>>> + +-----+ +-----+
>>>> + | MPF | | UPF |
>>>> + | | | |
>>>> + | PF0 | | PF1 |
>>>> + +--+--+ +--+--+
>>>> + ......... ^................. ^..........
>>>> + | |
>>>> + | PCIe DEVICE |
>>>> + | |
>>>> + +--+------------------+--+
>>>> + | SHELL |
>>>> + | |
>>>> + +------------------------+
>>>> + | USER |
>>>> + | |
>>>> + | |
>>>> + | |
>>>> + | |
>>>> + +------------------------+
>>>> +
>>>> +
>>>> +
>>>> +Virtualized
>>>> +-----------
>>>> +
>>>> +In virtualized deployments, the privileged MPF is assigned to the host but the
>>>> +unprivileged UPF is assigned to a guest VM via PCIe pass-through. The xrt-mgmt
>>>> +driver in host binds to MPF. The xrt-mgmt driver operations are privileged and
>>>> +only accessible to the MPF. The full stack is illustrated below::
>>>> +
>>>> +
>>>> + ..............
>>>> + HOST . VM .
>>>> + . .
>>>> + [XRT-MGMT] . [XRT-USER] .
>>>> + | . | .
>>>> + | . | .
>>>> + +-----+ . +-----+ .
>>>> + | MPF | . | UPF | .
>>>> + | | . | | .
>>>> + | PF0 | . | PF1 | .
>>>> + +--+--+ . +--+--+ .
>>>> + ......... ^................. ^..........
>>>> + | |
>>>> + | PCIe DEVICE |
>>>> + | |
>>>> + +--+------------------+--+
>>>> + | SHELL |
>>>> + | |
>>>> + +------------------------+
>>>> + | USER |
>>>> + | |
>>>> + | |
>>>> + | |
>>>> + | |
>>>> + +------------------------+
>>>> +
>>>> +
>>>> +
>>>> +
>>>> +
>>>> +Platform Security Considerations
>>>> +================================
>>>> +
>>>> +`Security of Alveo Platform <https://xilinx.github.io/XRT/master/html/security.html>`_
>>>> +discusses the deployment options and security implications in great detail.
>>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>>> index 056966c9aac9..beeaf0257364 100644
>>>> --- a/MAINTAINERS
>>>> +++ b/MAINTAINERS
>>>> @@ -7274,6 +7274,17 @@ F: Documentation/fpga/
>>>> F: drivers/fpga/
>>>> F: include/linux/fpga/
>>>>
>>>> +FPGA XRT DRIVERS
>>>> +M: Lizhi Hou <[email protected]>
>>>> +R: Max Zhen <[email protected]>
>>>> +R: Sonal Santan <[email protected]>
>>>> +L: [email protected]
>>>> +S: Supported
>>>> +W: https://github.com/Xilinx/XRT
>>>> +F: Documentation/fpga/xrt.rst
>>>> +F: drivers/fpga/xrt/
>>>> +F: include/uapi/linux/xrt/
>>>> +
>>>> FPU EMULATOR
>>>> M: Bill Metzenthen <[email protected]>
>>>> S: Maintained
>>>> --
>>>> 2.27.0
Hello Rob,
> -----Original Message-----
> From: Lizhi Hou <[email protected]>
> Sent: Thursday, October 14, 2021 9:13 AM
> To: [email protected]
> Cc: [email protected]; [email protected]; Max Zhen
> <[email protected]>; Sonal Santan <[email protected]>; Yu Liu
> <[email protected]>; Michal Simek <[email protected]>; Stefano Stabellini
> <[email protected]>; [email protected]; [email protected];
> [email protected]; Max Zhen <[email protected]>; Lizhi Hou <[email protected]>;
> Xu Yilun <[email protected]>
> Subject: Re: [PATCH V9 XRT Alveo 01/14] Documentation: fpga: Add a
> document describing XRT Alveo drivers
>
> Hello Rob,
>
> Please help with the review of the proposed FDT usage by Alveo/XRT drivers.
>
> Thanks,
> Lizhi
>
> On 10/13/21 7:21 PM, Xu Yilun wrote:
> > CAUTION: This message has originated from an External Source. Please use
> proper judgment and caution when opening attachments, clicking links, or
> responding to this email.
> >
> >
> >>>> +.. _device_tree_usage:
> >>>> +
> >>>> +Device Tree Usage
> >>>> +-----------------
> >>>> +
> >>>> +The xsabin file stores metadata which advertise HW subsystems
> >>>> +present in a partition. The metadata is stored in device tree
> >>>> +format with a well defined schema. XRT management driver uses this
> >>>> +information to bind *xrt_drivers* to the subsystem instantiations.
> >>>> +The xrt_drivers are found in **xrt-lib.ko** kernel module.
> >>> I'm still catching up the patchset from the very beginning, and just
> >>> finished the Documentation part. So far, I see the DT usage concern
> >>> which may impact the architecure a lot, so I should raise it ASAP.
> >>>
> >>> The concern raised by the DT maintainer:
> >>> https://lore.kernel.org/linux-fpga/CAL_JsqLod6FBGFhu7WXtMrB_z7wj8-up
> >>> [email protected]/
> >>>
> >>> First of all, directly parsing FDT in device drivers is not a normal
> >>> usage of DT in linux. It is out of the current DT usage model. So it
> >>> should be agreed by DT maintainers.
Friendly reminder that we are waiting for your feedback on the proposed FDT use model by the Alveo/XRT drivers. Please advise on how to proceed forward.
Thanks,
-Sonal
> >> Thanks for reviewing XRT document and providing feedback.
> >> Here is the reply from Sonal for Rob’s question:
> >> https://lore.kernel.org/linux-
> fpga/BY5PR02MB62604B87C66A1AD139A6F153B
> >> [email protected]/
> >> Overall, libfdt is used by XRT driver to parse the metadata which
> >> comes with an Alveo board.
> >> When XRT driver discovers an Alveo board, it will read a fdt blob
> >> from board firmware file resident on the host.
> >> By parsing the fdt blob, XRT driver gets information about this Alveo
> >> board, such as version, uuid, IPs exposed to PCI BAR, interrupt binding etc.
> >> So libfdt is used simply as Alveo metadata parser here. XRT drivers
> >> do not interact with system wide DT or present the Alveo device tree
> >> to host. For many systems like x86_64, system wide DT is not present
> >> but libfdt parsing services will still be needed.
> > Yes, I understand the use case.
> >
> > My concern is, directly parsing an isolated FDT in device driver and
> > populate sub devices, skipping the unflattening, this is a new working
> > model of device tree usage, but for the same purpose as the existing
> > one.
> >
> > So I really need the confirmation of DT maintainers.
> >
> >>> Current FPGA framework modifies kernel's live tree by DT overlay,
> >>> when FPGA is dynamically reprogrammed and new HW devices appear. See
> >>> Documentation/devicetree/bindings/fpga/fpga-region.txt.
> >>>
> >>> Then something less important:
> >>>
> >>> 1. The bindings should be documented in
> Documentation/devicetree/bindings/.
> >>> 2. Are all the example DT usage conform to the exsiting bindings? I
> >>> didn't go through all device classes, but remember like the
> >>> interrupt-controller should have a "interrupt-controller" property, and
> >>> the PCI properties are also different from PCI bindings.
> >> The fdt properties are defined for Alveo firmware files. XRT driver
> >> is the only consumer of this data. I am wondering if
> > Personally I don't like the idea of a different binding definition
> > set, even if the new device tree usage is accepted. We all use device
> > tree for device enumeration, so why the different definitions.
> >
> > Thanks,
> > Yilun
> >
> >> Documentation/devicetree/bindings is the right place for Alveo/XRT
> >> private format or should it be documented as part of XRT driver
> documentation?
> >> Looking for guidance here.
> >>
> >>
> >> Thanks,
> >>
> >> Lizhi
> >>
> >>> Thanks,
> >>> Yilun
> >>>
> >>>> +
> >>>> +Logic UUID
> >>>> +^^^^^^^^^^
> >>>> +A partition is identified uniquely through ``logic_uuid`` property::
> >>>> +
> >>>> + /dts-v1/;
> >>>> + / {
> >>>> + logic_uuid = "0123456789abcdef0123456789abcdef";
> >>>> + ...
> >>>> + }
> >>>> +
> >>>> +Schema Version
> >>>> +^^^^^^^^^^^^^^
> >>>> +Schema version is defined through the ``schema_version`` node. It
> >>>> +contains ``major`` and ``minor`` properties as below::
> >>>> +
> >>>> + /dts-v1/;
> >>>> + / {
> >>>> + schema_version {
> >>>> + major = <0x01>;
> >>>> + minor = <0x00>;
> >>>> + };
> >>>> + ...
> >>>> + }
> >>>> +
> >>>> +.. _partition_uuids:
> >>>> +
> >>>> +Partition UUIDs
> >>>> +^^^^^^^^^^^^^^^
> >>>> +Each partition may have parent and child UUIDs. These UUIDs are
> >>>> +defined by ``interfaces`` node and ``interface_uuid`` property::
> >>>> +
> >>>> + /dts-v1/;
> >>>> + / {
> >>>> + interfaces {
> >>>> + @0 {
> >>>> + interface_uuid = "0123456789abcdef0123456789abcdef";
> >>>> + };
> >>>> + @1 {
> >>>> + interface_uuid = "fedcba9876543210fedcba9876543210";
> >>>> + };
> >>>> + ...
> >>>> + };
> >>>> + ...
> >>>> + }
> >>>> +
> >>>> +
> >>>> +Subsystem Instantiations
> >>>> +^^^^^^^^^^^^^^^^^^^^^^^^
> >>>> +Subsystem instantiations are captured as children of
> >>>> +``addressable_endpoints``
> >>>> +node::
> >>>> +
> >>>> + /dts-v1/;
> >>>> + / {
> >>>> + addressable_endpoints {
> >>>> + abc {
> >>>> + ...
> >>>> + };
> >>>> + def {
> >>>> + ...
> >>>> + };
> >>>> + ...
> >>>> + }
> >>>> + }
> >>>> +
> >>>> +Subnode 'abc' and 'def' are the name of subsystem nodes
> >>>> +
> >>>> +Subsystem Node
> >>>> +^^^^^^^^^^^^^^
> >>>> +Each subsystem node and its properties define a hardware instance::
> >>>> +
> >>>> +
> >>>> + addressable_endpoints {
> >>>> + abc {
> >>>> + reg = <0x00 0x1f05000 0x00 0x1000>>
> >>>> + pcie_physical_function = <0x0>;
> >>>> + pcie_bar_mapping = <0x2>;
> >>>> + compatible = "abc def";
> >>>> + interrupts = <0x09 0x0c>;
> >>>> + firmware {
> >>>> + firmware_product_name = "abc"
> >>>> + firmware_branch_name = "def"
> >>>> + firmware_version_major = <1>
> >>>> + firmware_version_minor = <2>
> >>>> + };
> >>>> + }
> >>>> + ...
> >>>> + }
> >>>> +
> >>>> +:reg:
> >>>> + Property defines an address range. `<0x00 0x1f05000 0x00 0x1000>`
> >>>> +indicates
> >>>> + *0x00 0x1f05000* as BAR offset and *0x00 0x1000* as address length.
> >>>> +:pcie_physical_function:
> >>>> + Property specifies which PCIe physical function the subsystem node
> resides.
> >>>> + `<0x0>` implies physical function 0.
> >>>> +:pcie_bar_mapping:
> >>>> + Property specifies which PCIe BAR the subsystem node resides.
> >>>> +`<0x2>` implies BAR 2. A value of 0 means the property is not defined.
> >>>> +:compatible:
> >>>> + Property is a list of strings. The first string in the list
> >>>> +specifies the exact subsystem node. The following strings
> >>>> +represent other devices that the device is compatible with.
> >>>> +:interrupts:
> >>>> + Property specifies start and end interrupts for this subsystem node.
> >>>> + `<0x09 0x0c>` implies interrupts 9 to 13 are used by this subsystem.
> >>>> +:firmware:
> >>>> + Subnode defines the firmware required by this subsystem node.
> >>>> +
> >>>> +Alveo U50 Platform Example
> >>>> +^^^^^^^^^^^^^^^^^^^^^^^^^^
> >>>> +::
> >>>> +
> >>>> + /dts-v1/;
> >>>> +
> >>>> + /{
> >>>> + logic_uuid = "f465b0a3ae8c64f619bc150384ace69b";
> >>>> +
> >>>> + schema_version {
> >>>> + major = <0x01>;
> >>>> + minor = <0x00>;
> >>>> + };
> >>>> +
> >>>> + interfaces {
> >>>> +
> >>>> + @0 {
> >>>> + interface_uuid =
> "862c7020a250293e32036f19956669e5";
> >>>> + };
> >>>> + };
> >>>> +
> >>>> + addressable_endpoints {
> >>>> +
> >>>> + ep_blp_rom_00 {
> >>>> + reg = <0x00 0x1f04000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-
> 1.0\0axi_bram_ctrl";
> >>>> + };
> >>>> +
> >>>> + ep_card_flash_program_00 {
> >>>> + reg = <0x00 0x1f06000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_quad_spi-
> 1.0\0axi_quad_spi";
> >>>> + interrupts = <0x03 0x03>;
> >>>> + };
> >>>> +
> >>>> + ep_cmc_firmware_mem_00 {
> >>>> + reg = <0x00 0x1e20000 0x00 0x20000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible =
> >>>> + "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> >>>> +
> >>>> + firmware {
> >>>> + firmware_product_name = "cmc";
> >>>> + firmware_branch_name = "u50";
> >>>> + firmware_version_major = <0x01>;
> >>>> + firmware_version_minor = <0x00>;
> >>>> + };
> >>>> + };
> >>>> +
> >>>> + ep_cmc_intc_00 {
> >>>> + reg = <0x00 0x1e03000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";
> >>>> + interrupts = <0x04 0x04>;
> >>>> + };
> >>>> +
> >>>> + ep_cmc_mutex_00 {
> >>>> + reg = <0x00 0x1e02000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_cmc_regmap_00 {
> >>>> + reg = <0x00 0x1e08000 0x00 0x2000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible =
> >>>> + "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> >>>> +
> >>>> + firmware {
> >>>> + firmware_product_name = "sc-fw";
> >>>> + firmware_branch_name = "u50";
> >>>> + firmware_version_major = <0x05>;
> >>>> + };
> >>>> + };
> >>>> +
> >>>> + ep_cmc_reset_00 {
> >>>> + reg = <0x00 0x1e01000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_ddr_mem_calib_00 {
> >>>> + reg = <0x00 0x63000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_debug_bscan_mgmt_00 {
> >>>> + reg = <0x00 0x1e90000 0x00 0x10000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-debug_bridge-
> 1.0\0debug_bridge";
> >>>> + };
> >>>> +
> >>>> + ep_ert_base_address_00 {
> >>>> + reg = <0x00 0x21000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_ert_command_queue_mgmt_00 {
> >>>> + reg = <0x00 0x40000 0x00 0x10000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-ert_command_queue-
> 1.0\0ert_command_queue";
> >>>> + };
> >>>> +
> >>>> + ep_ert_command_queue_user_00 {
> >>>> + reg = <0x00 0x40000 0x00 0x10000>;
> >>>> + pcie_physical_function = <0x01>;
> >>>> + compatible = "xilinx.com,reg_abs-ert_command_queue-
> 1.0\0ert_command_queue";
> >>>> + };
> >>>> +
> >>>> + ep_ert_firmware_mem_00 {
> >>>> + reg = <0x00 0x30000 0x00 0x8000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible =
> >>>> + "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> >>>> +
> >>>> + firmware {
> >>>> + firmware_product_name = "ert";
> >>>> + firmware_branch_name = "v20";
> >>>> + firmware_version_major = <0x01>;
> >>>> + };
> >>>> + };
> >>>> +
> >>>> + ep_ert_intc_00 {
> >>>> + reg = <0x00 0x23000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";
> >>>> + interrupts = <0x05 0x05>;
> >>>> + };
> >>>> +
> >>>> + ep_ert_reset_00 {
> >>>> + reg = <0x00 0x22000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_ert_sched_00 {
> >>>> + reg = <0x00 0x50000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x01>;
> >>>> + compatible = "xilinx.com,reg_abs-ert_sched-
> 1.0\0ert_sched";
> >>>> + interrupts = <0x09 0x0c>;
> >>>> + };
> >>>> +
> >>>> + ep_fpga_configuration_00 {
> >>>> + reg = <0x00 0x1e88000 0x00 0x8000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_hwicap-
> 1.0\0axi_hwicap";
> >>>> + interrupts = <0x02 0x02>;
> >>>> + };
> >>>> +
> >>>> + ep_icap_reset_00 {
> >>>> + reg = <0x00 0x1f07000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_msix_00 {
> >>>> + reg = <0x00 0x00 0x00 0x20000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-msix-1.0\0msix";
> >>>> + pcie_bar_mapping = <0x02>;
> >>>> + };
> >>>> +
> >>>> + ep_pcie_link_mon_00 {
> >>>> + reg = <0x00 0x1f05000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_pr_isolate_plp_00 {
> >>>> + reg = <0x00 0x1f01000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_pr_isolate_ulp_00 {
> >>>> + reg = <0x00 0x1000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_uuid_rom_00 {
> >>>> + reg = <0x00 0x64000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-
> 1.0\0axi_bram_ctrl";
> >>>> + };
> >>>> +
> >>>> + ep_xdma_00 {
> >>>> + reg = <0x00 0x00 0x00 0x10000>;
> >>>> + pcie_physical_function = <0x01>;
> >>>> + compatible = "xilinx.com,reg_abs-xdma-1.0\0xdma";
> >>>> + pcie_bar_mapping = <0x02>;
> >>>> + };
> >>>> + };
> >>>> +
> >>>> + }
> >>>> +
> >>>> +
> >>>> +
> >>>> +Deployment Models
> >>>> +=================
> >>>> +
> >>>> +Baremetal
> >>>> +---------
> >>>> +
> >>>> +In bare-metal deployments, both MPF and UPF are visible and
> >>>> +accessible. The xrt-mgmt driver binds to MPF. The xrt-mgmt driver
> >>>> +operations are privileged and available to system administrator. The full
> stack is illustrated below::
> >>>> +
> >>>> + HOST
> >>>> +
> >>>> + [XRT-MGMT] [XRT-USER]
> >>>> + | |
> >>>> + | |
> >>>> + +-----+ +-----+
> >>>> + | MPF | | UPF |
> >>>> + | | | |
> >>>> + | PF0 | | PF1 |
> >>>> + +--+--+ +--+--+
> >>>> + ......... ^................. ^..........
> >>>> + | |
> >>>> + | PCIe DEVICE |
> >>>> + | |
> >>>> + +--+------------------+--+
> >>>> + | SHELL |
> >>>> + | |
> >>>> + +------------------------+
> >>>> + | USER |
> >>>> + | |
> >>>> + | |
> >>>> + | |
> >>>> + | |
> >>>> + +------------------------+
> >>>> +
> >>>> +
> >>>> +
> >>>> +Virtualized
> >>>> +-----------
> >>>> +
> >>>> +In virtualized deployments, the privileged MPF is assigned to the
> >>>> +host but the unprivileged UPF is assigned to a guest VM via PCIe
> >>>> +pass-through. The xrt-mgmt driver in host binds to MPF. The
> >>>> +xrt-mgmt driver operations are privileged and only accessible to the
> MPF. The full stack is illustrated below::
> >>>> +
> >>>> +
> >>>> + ..............
> >>>> + HOST . VM .
> >>>> + . .
> >>>> + [XRT-MGMT] . [XRT-USER] .
> >>>> + | . | .
> >>>> + | . | .
> >>>> + +-----+ . +-----+ .
> >>>> + | MPF | . | UPF | .
> >>>> + | | . | | .
> >>>> + | PF0 | . | PF1 | .
> >>>> + +--+--+ . +--+--+ .
> >>>> + ......... ^................. ^..........
> >>>> + | |
> >>>> + | PCIe DEVICE |
> >>>> + | |
> >>>> + +--+------------------+--+
> >>>> + | SHELL |
> >>>> + | |
> >>>> + +------------------------+
> >>>> + | USER |
> >>>> + | |
> >>>> + | |
> >>>> + | |
> >>>> + | |
> >>>> + +------------------------+
> >>>> +
> >>>> +
> >>>> +
> >>>> +
> >>>> +
> >>>> +Platform Security Considerations
> >>>> +================================
> >>>> +
> >>>> +`Security of Alveo Platform
> >>>> +<https://xilinx.github.io/XRT/master/html/security.html>`_
> >>>> +discusses the deployment options and security implications in great
> detail.
> >>>> diff --git a/MAINTAINERS b/MAINTAINERS index
> >>>> 056966c9aac9..beeaf0257364 100644
> >>>> --- a/MAINTAINERS
> >>>> +++ b/MAINTAINERS
> >>>> @@ -7274,6 +7274,17 @@ F: Documentation/fpga/
> >>>> F: drivers/fpga/
> >>>> F: include/linux/fpga/
> >>>>
> >>>> +FPGA XRT DRIVERS
> >>>> +M: Lizhi Hou <[email protected]>
> >>>> +R: Max Zhen <[email protected]>
> >>>> +R: Sonal Santan <[email protected]>
> >>>> +L: [email protected]
> >>>> +S: Supported
> >>>> +W: https://github.com/Xilinx/XRT
> >>>> +F: Documentation/fpga/xrt.rst
> >>>> +F: drivers/fpga/xrt/
> >>>> +F: include/uapi/linux/xrt/
> >>>> +
> >>>> FPU EMULATOR
> >>>> M: Bill Metzenthen <[email protected]>
> >>>> S: Maintained
> >>>> --
> >>>> 2.27.0
On Mon, Aug 02, 2021 at 09:05:10AM -0700, Lizhi Hou wrote:
> Alveo FPGA firmware and partial reconfigure file are in xclbin format. This
> code enumerates and extracts sections from xclbin files. xclbin.h is cross
> platform and used across all platforms and OS.
>
> Signed-off-by: Sonal Santan <[email protected]>
> Signed-off-by: Max Zhen <[email protected]>
> Signed-off-by: Lizhi Hou <[email protected]>
> Reviewed-by: Tom Rix <[email protected]>
> ---
> drivers/fpga/xrt/include/xclbin-helper.h | 48 +++
> drivers/fpga/xrt/lib/xclbin.c | 381 ++++++++++++++++++++
> include/uapi/linux/fpga-xrt.h | 428 +++++++++++++++++++++++
> 3 files changed, 857 insertions(+)
> create mode 100644 drivers/fpga/xrt/include/xclbin-helper.h
> create mode 100644 drivers/fpga/xrt/lib/xclbin.c
> create mode 100644 include/uapi/linux/fpga-xrt.h
>
> diff --git a/drivers/fpga/xrt/include/xclbin-helper.h b/drivers/fpga/xrt/include/xclbin-helper.h
> new file mode 100644
> index 000000000000..518d55f74c25
> --- /dev/null
> +++ b/drivers/fpga/xrt/include/xclbin-helper.h
> @@ -0,0 +1,48 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * David Zhang <[email protected]>
> + * Sonal Santan <[email protected]>
> + */
> +
> +#ifndef _XCLBIN_HELPER_H_
> +#define _XCLBIN_HELPER_H_
> +
> +#include <linux/types.h>
> +#include <linux/device.h>
> +#include <linux/fpga-xrt.h>
> +
> +#define XCLBIN_VERSION2 "xclbin2"
> +#define XCLBIN_HWICAP_BITFILE_BUF_SZ 1024
> +#define XCLBIN_MAX_SZ_1G (1024 * 1024 * 1024) /* Assuming xclbin <= 1G, always */
> +
> +enum axlf_section_kind;
> +struct axlf;
> +
> +/**
> + * Bitstream header information as defined by Xilinx tools.
> + * Please note that this struct definition is not owned by the driver.
> + */
> +struct xclbin_bit_head_info {
> + u32 header_length; /* Length of header in 32 bit words */
> + u32 bitstream_length; /* Length of bitstream to read in bytes */
> + const unchar *design_name; /* Design name get from bitstream */
> + const unchar *part_name; /* Part name read from bitstream */
> + const unchar *date; /* Date read from bitstream header */
> + const unchar *time; /* Bitstream creation time */
> + u32 magic_length; /* Length of the magic numbers */
> + const unchar *version; /* Version string */
> +};
> +
> +/* caller must free the allocated memory for **data. len could be NULL. */
> +int xrt_xclbin_get_section(struct device *dev, const struct axlf *xclbin,
> + enum axlf_section_kind kind, void **data,
> + uint64_t *len);
> +int xrt_xclbin_get_metadata(struct device *dev, const struct axlf *xclbin, char **dtb);
> +int xrt_xclbin_parse_bitstream_header(struct device *dev, const unchar *data,
> + u32 size, struct xclbin_bit_head_info *head_info);
> +const char *xrt_clock_type2epname(enum XCLBIN_CLOCK_TYPE type);
> +
> +#endif /* _XCLBIN_HELPER_H_ */
> diff --git a/drivers/fpga/xrt/lib/xclbin.c b/drivers/fpga/xrt/lib/xclbin.c
> new file mode 100644
> index 000000000000..6edac3d418be
> --- /dev/null
> +++ b/drivers/fpga/xrt/lib/xclbin.c
> @@ -0,0 +1,381 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Xilinx Alveo FPGA Driver XCLBIN parser
> + *
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors: David Zhang <[email protected]>
> + */
> +
> +#include <asm/errno.h>
> +#include <linux/vmalloc.h>
> +#include <linux/device.h>
> +#include "xclbin-helper.h"
> +#include "metadata.h"
> +
> +/* Used for parsing bitstream header */
> +#define BITSTREAM_EVEN_MAGIC_BYTE 0x0f
> +#define BITSTREAM_ODD_MAGIC_BYTE 0xf0
> +
> +static inline u16 bitstream_read16(const char *data, u32 *offset)
> +{
> + u16 val;
> +
> + val = be16_to_cpu(*(__be16 *)(data + *offset));
> + *offset += sizeof(__be16);
> +
> + return val;
> +}
> +
> +static inline u32 bitstream_read32(const char *data, u32 *offset)
> +{
> + u32 val;
> +
> + val = be32_to_cpu(*(__be32 *)(data + *offset));
> + *offset += sizeof(__be32);
> +
> + return val;
> +}
> +
> +static int xrt_xclbin_get_section_hdr(const struct axlf *xclbin,
> + enum axlf_section_kind kind,
> + const struct axlf_section_header **header)
> +{
> + const struct axlf_section_header *phead = NULL;
> + u64 xclbin_len;
> + int i;
> +
> + *header = NULL;
> + for (i = 0; i < xclbin->header.num_sections; i++) {
> + if (xclbin->sections[i].section_kind == kind) {
> + phead = &xclbin->sections[i];
> + break;
> + }
> + }
> +
> + if (!phead)
> + return -ENOENT;
> +
> + xclbin_len = xclbin->header.length;
> + if (xclbin_len > XCLBIN_MAX_SZ_1G || !phead->section_size ||
> + phead->section_offset + phead->section_size > xclbin_len)
> + return -EINVAL;
> +
> + *header = phead;
> + return 0;
> +}
> +
> +static int xrt_xclbin_section_info(const struct axlf *xclbin,
> + enum axlf_section_kind kind,
> + u64 *offset, u64 *size)
> +{
> + const struct axlf_section_header *mem_header = NULL;
> + int rc;
> +
> + rc = xrt_xclbin_get_section_hdr(xclbin, kind, &mem_header);
> + if (rc)
> + return rc;
> +
> + *offset = mem_header->section_offset;
> + *size = mem_header->section_size;
> +
> + return 0;
> +}
> +
> +/* caller must free the allocated memory for **data */
> +int xrt_xclbin_get_section(struct device *dev,
> + const struct axlf *buf,
> + enum axlf_section_kind kind,
> + void **data, u64 *len)
> +{
> + const struct axlf *xclbin = (const struct axlf *)buf;
> + void *section = NULL;
> + u64 offset;
> + u64 size;
> + int err;
> +
> + if (!data) {
> + dev_err(dev, "invalid data pointer");
> + return -EINVAL;
> + }
> +
> + err = xrt_xclbin_section_info(xclbin, kind, &offset, &size);
> + if (err) {
> + dev_dbg(dev, "parsing section failed. kind %d, err = %d", kind, err);
> + return err;
> + }
> +
> + section = vzalloc(size);
> + if (!section)
> + return -ENOMEM;
> +
> + memcpy(section, ((const char *)xclbin) + offset, size);
> +
> + *data = section;
> + if (len)
> + *len = size;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(xrt_xclbin_get_section);
> +
> +static inline int xclbin_bit_get_string(const unchar *data, u32 size,
> + u32 offset, unchar prefix,
> + const unchar **str)
> +{
> + int len;
> + u32 tmp;
> +
> + /* prefix and length will be 3 bytes */
> + if (offset + 3 > size)
> + return -EINVAL;
> +
> + /* Read prefix */
> + tmp = data[offset++];
> + if (tmp != prefix)
> + return -EINVAL;
> +
> + /* Get string length */
> + len = bitstream_read16(data, &offset);
> + if (offset + len > size)
> + return -EINVAL;
> +
> + if (data[offset + len - 1] != '\0')
> + return -EINVAL;
> +
> + *str = data + offset;
> +
> + return len + 3;
> +}
> +
> +/* parse bitstream header */
> +int xrt_xclbin_parse_bitstream_header(struct device *dev, const unchar *data,
> + u32 size, struct xclbin_bit_head_info *head_info)
> +{
> + u32 offset = 0;
> + int len, i;
> + u16 magic;
> +
> + memset(head_info, 0, sizeof(*head_info));
> +
> + /* Get "Magic" length */
> + if (size < sizeof(u16)) {
> + dev_err(dev, "invalid size");
> + return -EINVAL;
> + }
> +
> + len = bitstream_read16(data, &offset);
> + if (offset + len > size) {
> + dev_err(dev, "invalid magic len");
> + return -EINVAL;
> + }
> + head_info->magic_length = len;
> +
> + for (i = 0; i < head_info->magic_length - 1; i++) {
> + magic = data[offset++];
> + if (!(i % 2) && magic != BITSTREAM_EVEN_MAGIC_BYTE) {
> + dev_err(dev, "invalid magic even byte at %d", offset);
> + return -EINVAL;
> + }
> +
> + if ((i % 2) && magic != BITSTREAM_ODD_MAGIC_BYTE) {
> + dev_err(dev, "invalid magic odd byte at %d", offset);
> + return -EINVAL;
> + }
> + }
> +
> + if (offset + 3 > size) {
> + dev_err(dev, "invalid length of magic end");
> + return -EINVAL;
> + }
> + /* Read null end of magic data. */
> + if (data[offset++]) {
> + dev_err(dev, "invalid magic end");
> + return -EINVAL;
> + }
> +
> + /* Read 0x01 (short) */
> + magic = bitstream_read16(data, &offset);
> +
> + /* Check the "0x01" half word */
> + if (magic != 0x01) {
> + dev_err(dev, "invalid magic end");
> + return -EINVAL;
> + }
> +
> + len = xclbin_bit_get_string(data, size, offset, 'a', &head_info->design_name);
> + if (len < 0) {
> + dev_err(dev, "get design name failed");
> + return -EINVAL;
> + }
> +
> + head_info->version = strstr(head_info->design_name, "Version=") + strlen("Version=");
> + offset += len;
> +
> + len = xclbin_bit_get_string(data, size, offset, 'b', &head_info->part_name);
> + if (len < 0) {
> + dev_err(dev, "get part name failed");
> + return -EINVAL;
> + }
> + offset += len;
> +
> + len = xclbin_bit_get_string(data, size, offset, 'c', &head_info->date);
> + if (len < 0) {
> + dev_err(dev, "get data failed");
> + return -EINVAL;
> + }
> + offset += len;
> +
> + len = xclbin_bit_get_string(data, size, offset, 'd', &head_info->time);
> + if (len < 0) {
> + dev_err(dev, "get time failed");
> + return -EINVAL;
> + }
> + offset += len;
> +
> + if (offset + 5 >= size) {
> + dev_err(dev, "can not get bitstream length");
> + return -EINVAL;
> + }
> +
> + /* Read 'e' */
> + if (data[offset++] != 'e') {
> + dev_err(dev, "invalid prefix of bitstream length");
> + return -EINVAL;
> + }
> +
> + /* Get byte length of bitstream */
> + head_info->bitstream_length = bitstream_read32(data, &offset);
> +
> + head_info->header_length = offset;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(xrt_xclbin_parse_bitstream_header);
> +
> +static struct xrt_clock_desc {
> + char *clock_ep_name;
> + u32 clock_xclbin_type;
> + char *clkfreq_ep_name;
> +} clock_desc[] = {
> + {
> + .clock_ep_name = XRT_MD_NODE_CLK_KERNEL1,
> + .clock_xclbin_type = CT_DATA,
> + .clkfreq_ep_name = XRT_MD_NODE_CLKFREQ_K1,
> + },
> + {
> + .clock_ep_name = XRT_MD_NODE_CLK_KERNEL2,
> + .clock_xclbin_type = CT_KERNEL,
> + .clkfreq_ep_name = XRT_MD_NODE_CLKFREQ_K2,
> + },
> + {
> + .clock_ep_name = XRT_MD_NODE_CLK_KERNEL3,
> + .clock_xclbin_type = CT_SYSTEM,
> + .clkfreq_ep_name = XRT_MD_NODE_CLKFREQ_HBM,
> + },
> +};
> +
> +const char *xrt_clock_type2epname(enum XCLBIN_CLOCK_TYPE type)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(clock_desc); i++) {
> + if (clock_desc[i].clock_xclbin_type == type)
> + return clock_desc[i].clock_ep_name;
> + }
> + return NULL;
> +}
> +EXPORT_SYMBOL_GPL(xrt_clock_type2epname);
> +
> +static const char *clock_type2clkfreq_name(enum XCLBIN_CLOCK_TYPE type)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(clock_desc); i++) {
> + if (clock_desc[i].clock_xclbin_type == type)
> + return clock_desc[i].clkfreq_ep_name;
> + }
> + return NULL;
> +}
> +
> +static int xrt_xclbin_add_clock_metadata(struct device *dev,
> + const struct axlf *xclbin,
> + char *dtb)
> +{
> + struct clock_freq_topology *clock_topo;
> + u16 freq;
> + int rc;
> + int i;
> +
> + /* if clock section does not exist, add nothing and return success */
> + rc = xrt_xclbin_get_section(dev, xclbin, CLOCK_FREQ_TOPOLOGY,
> + (void **)&clock_topo, NULL);
> + if (rc == -ENOENT)
> + return 0;
> + else if (rc)
> + return rc;
> +
> + for (i = 0; i < clock_topo->count; i++) {
> + u8 type = clock_topo->clock_freq[i].type;
> + const char *ep_name = xrt_clock_type2epname(type);
> + const char *counter_name = clock_type2clkfreq_name(type);
> +
> + if (!ep_name || !counter_name)
> + continue;
> +
> + freq = be16_to_cpu((__force __be16)clock_topo->clock_freq[i].freq_MHZ);
> + rc = xrt_md_set_prop(dev, dtb, ep_name, NULL, XRT_MD_PROP_CLK_FREQ,
> + &freq, sizeof(freq));
> + if (rc)
> + break;
> +
> + rc = xrt_md_set_prop(dev, dtb, ep_name, NULL, XRT_MD_PROP_CLK_CNT,
> + counter_name, strlen(counter_name) + 1);
> + if (rc)
> + break;
> + }
> +
> + vfree(clock_topo);
> +
> + return rc;
> +}
> +
> +int xrt_xclbin_get_metadata(struct device *dev, const struct axlf *xclbin, char **dtb)
> +{
> + char *md = NULL, *newmd = NULL;
> + u64 len, md_len;
> + int rc;
> +
> + *dtb = NULL;
> +
> + rc = xrt_xclbin_get_section(dev, xclbin, PARTITION_METADATA, (void **)&md, &len);
> + if (rc)
> + goto done;
> +
> + md_len = xrt_md_size(dev, md);
> +
> + /* Sanity check the dtb section. */
> + if (md_len > len) {
> + rc = -EINVAL;
> + goto done;
> + }
> +
> + /* use dup function here to convert incoming metadata to writable */
> + newmd = xrt_md_dup(dev, md);
> + if (!newmd) {
> + rc = -EFAULT;
> + goto done;
> + }
> +
> + /* Convert various needed xclbin sections into dtb. */
> + rc = xrt_xclbin_add_clock_metadata(dev, xclbin, newmd);
> +
> + if (!rc)
> + *dtb = newmd;
> + else
> + vfree(newmd);
> +done:
> + vfree(md);
> + return rc;
> +}
> +EXPORT_SYMBOL_GPL(xrt_xclbin_get_metadata);
> diff --git a/include/uapi/linux/fpga-xrt.h b/include/uapi/linux/fpga-xrt.h
> new file mode 100644
> index 000000000000..ace7d3d09897
> --- /dev/null
> +++ b/include/uapi/linux/fpga-xrt.h
> @@ -0,0 +1,428 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * Xilinx FPGA compiled binary container format
> + *
> + * Copyright (C) 2015-2021, Xilinx Inc
> + */
> +
> +#ifndef _UAPI_LINUX_FPGA_XRT_H
> +#define _UAPI_LINUX_FPGA_XRT_H
> +
> +#if defined(__KERNEL__)
> +
> +#include <linux/types.h>
> +
> +#elif defined(__cplusplus)
> +
> +#include <cstdlib>
> +#include <cstdint>
> +#include <algorithm>
> +#include <uuid/uuid.h>
> +
> +#else
> +
> +#include <stdlib.h>
> +#include <stdint.h>
> +#include <uuid/uuid.h>
> +
> +#endif
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +#include <linux/ioctl.h>
> +
> +/**
> + * DOC: Container format for Xilinx FPGA images
> + * The container stores bitstreams, metadata and firmware images.
> + * xclbin/xsabin is an ELF-like binary container format. It is a structured
I guess the format is called axlf? If yes, please add it. I was wondering
what the axlf_ structures stand for.
> + * series of sections. There is a file header followed by several section
> + * headers which is followed by sections. A section header points to an
> + * actual section. There is an optional signature at the end. The
> + * following figure illustrates a typical xclbin:
> + *
> + * +---------------------+
> + * | |
> + * | HEADER |
> + * +---------------------+
> + * | SECTION HEADER |
> + * | |
> + * +---------------------+
> + * | ... |
> + * | |
> + * +---------------------+
> + * | SECTION HEADER |
> + * | |
> + * +---------------------+
> + * | SECTION |
> + * | |
> + * +---------------------+
> + * | ... |
> + * | |
> + * +---------------------+
> + * | SECTION |
> + * | |
> + * +---------------------+
> + * | SIGNATURE |
> + * | (OPTIONAL) |
> + * +---------------------+
> + */
> +
> +enum XCLBIN_MODE {
> + XCLBIN_FLAT = 0,
> + XCLBIN_PR,
> + XCLBIN_TANDEM_STAGE2,
> + XCLBIN_TANDEM_STAGE2_WITH_PR,
> + XCLBIN_HW_EMU,
> + XCLBIN_SW_EMU,
> + XCLBIN_MODE_MAX
> +};
> +
> +enum axlf_section_kind {
> + BITSTREAM = 0,
> + CLEARING_BITSTREAM,
> + EMBEDDED_METADATA,
> + FIRMWARE,
> + DEBUG_DATA,
> + SCHED_FIRMWARE,
> + MEM_TOPOLOGY,
> + CONNECTIVITY,
> + IP_LAYOUT,
> + DEBUG_IP_LAYOUT,
> + DESIGN_CHECK_POINT,
> + CLOCK_FREQ_TOPOLOGY,
> + MCS,
> + BMC,
> + BUILD_METADATA,
> + KEYVALUE_METADATA,
> + USER_METADATA,
> + DNA_CERTIFICATE,
> + PDI,
> + BITSTREAM_PARTIAL_PDI,
> + PARTITION_METADATA,
> + EMULATION_DATA,
> + SYSTEM_METADATA,
> + SOFT_KERNEL,
> + ASK_FLASH,
> + AIE_METADATA,
> + ASK_GROUP_TOPOLOGY,
> + ASK_GROUP_CONNECTIVITY
> +};
> +
> +enum MEM_TYPE {
> + MEM_DDR3 = 0,
> + MEM_DDR4,
> + MEM_DRAM,
> + MEM_STREAMING,
> + MEM_PREALLOCATED_GLOB,
> + MEM_ARE,
> + MEM_HBM,
> + MEM_BRAM,
> + MEM_URAM,
> + MEM_STREAMING_CONNECTION
> +};
> +
> +enum IP_TYPE {
> + IP_MB = 0,
> + IP_KERNEL,
> + IP_DNASC,
> + IP_DDR4_CONTROLLER,
> + IP_MEM_DDR4,
> + IP_MEM_HBM
> +};
> +
> +struct axlf_section_header {
> + uint32_t section_kind; /* Section type */
> + char section_name[16]; /* Section name */
> + char rsvd[4];
> + uint64_t section_offset; /* File offset of section data */
> + uint64_t section_size; /* Size of section data */
> +} __packed;
> +
> +struct axlf_header {
> + uint64_t length; /* Total size of the xclbin file */
> + uint64_t time_stamp; /* Timestamp when xclbin was created */
> + uint64_t feature_rom_timestamp; /* TimeSinceEpoch of the featureRom */
> + uint16_t version_patch; /* Patch Version */
> + uint8_t version_major; /* Major Version */
> + uint8_t version_minor; /* Minor Version */
> + uint32_t mode; /* Xclbin mode. See enum XCLBIN_MODE */
> + union {
> + struct {
> + uint64_t platform_id; /* 64 bit platform ID */
> + uint64_t feature_id; /* 64 bit feature ID */
> + } rom;
> + unsigned char rom_uuid[16]; /* feature ROM UUID */
> + };
> + unsigned char platform_vbnv[64]; /* String of Vendor:Board:Name:Version */
> + union {
> + char next_axlf[16]; /* Name of next xclbin file */
> + unsigned char uuid[16]; /* uuid of this xclbin */
> + };
> + char debug_bin[16]; /* Name of binary with debug information */
> + uint32_t num_sections; /* Number of section headers */
> + char rsvd[4];
> +} __packed;
> +
> +struct axlf {
> + char magic[8]; /* Magic word: xclbin2\0 */
> + int32_t signature_length; /* Length. -1 indicates no signature */
> + uint8_t reserved[28];
> + uint8_t key_block[256]; /* Signature for validation of binary */
> + uint64_t unique_id; /* Unique ID */
> + struct axlf_header header; /* Inline header */
> + struct axlf_section_header sections[1]; /* One or more section headers follow */
> +} __packed;
> +
> +/* bitstream information */
> +struct xlnx_bitstream {
> + uint8_t freq[8];
> + char bits[1];
> +} __packed;
> +
> +/**** MEMORY TOPOLOGY SECTION ****/
> +struct mem_data {
> + uint8_t type; /* Memory type. See enum MEM_TYPE */
> + uint8_t used; /* Memory presence. 0 means not present */
> + uint8_t rsvd[6];
> + union {
> + uint64_t size; /* Memory size. Counted in KB */
> + uint64_t route_id; /* Stream route ID */
> + };
> + union {
> + uint64_t base_address; /* Memory base address */
> + uint64_t flow_id; /* Stream flow ID */
> + };
> + unsigned char tag[16]; /* Memory tag string */
> +} __packed;
> +
> +struct mem_topology {
> + int32_t count; /* Number of mem_data */
> + struct mem_data mem_data[1]; /* mem_data array, sorted on mem_type */
> +} __packed;
> +
> +/**** CONNECTIVITY SECTION ****/
> +/* Connectivity of each argument of CU(Compute Unit). It will be in terms
> + * of argument index associated. For associating CU instances with arguments
> + * and banks, start at the connectivity section. Using the ip_layout_index
> + * access the ip_data.name. Now we can associate this CU instance with its
> + * original CU name and get the connectivity as well. This enables us to form
> + * related groups of CU instances.
> + */
> +
> +struct connection {
> + int32_t arg_index; /* Index of CU argument */
> + int32_t ip_layout_index; /* Index into the ip_layout section */
> + int32_t mem_data_index; /* Index into the mem_data section */
> +} __packed;
> +
> +struct connectivity {
> + int32_t count;
> + struct connection connection[1];
> +} __packed;
> +
> +/**** IP_LAYOUT SECTION ****/
> +
> +/* IP Kernel */
> +#define IP_INT_ENABLE_MASK 0x0001
> +#define IP_INTERRUPT_ID_MASK 0x00FE
> +#define IP_INTERRUPT_ID_SHIFT 0x1
> +
> +enum IP_CONTROL {
> + AP_CTRL_HS = 0,
> + AP_CTRL_CHAIN,
> + AP_CTRL_NONE,
> + AP_CTRL_ME,
> + ACCEL_ADAPTER
> +};
> +
> +#define IP_CONTROL_MASK 0xFF00
> +#define IP_CONTROL_SHIFT 0x8
> +
> +/*
> + * IPs on AXI lite - their types, names, and base addresses.
> + *
> + * The defination of 32-bit follows IP_TYPE is based on IP_TYPE
> + * For IP_KERNEL
> + * int_enable : Bit - 0x0000_0001;
> + * interrupt_id : Bits - 0x0000_00FE;
> + * ip_control : Bits - 0x0000_FF00;
> + * For IP_MEM_*
> + * index : Bits - 0x0000_FFFF;
> + * pc_index : Bits - 0x00FF_0000;
> + */
> +struct ip_data {
> + uint32_t type; /* Type. See enum IP_TYPE */
> + union {
> + uint32_t properties;
> + struct {
> + uint16_t index;
> + uint8_t pc_index;
> + uint8_t unused;
> + } indices;
> + };
> + uint64_t base_address;
> + uint8_t name[64]; /* Name of IP */
> +} __packed;
> +
> +struct ip_layout {
> + int32_t count;
> + struct ip_data ip_data[1]; /* ip_data array, sorted by base_address */
> +} __packed;
> +
> +/*** Debug IP section layout ****/
> +enum DEBUG_IP_TYPE {
> + UNDEFINED = 0,
> + LAPC,
> + ILA,
> + AXI_MM_MONITOR,
> + AXI_TRACE_FUNNEL,
> + AXI_MONITOR_FIFO_LITE,
> + AXI_MONITOR_FIFO_FULL,
> + ACCEL_MONITOR,
> + AXI_STREAM_MONITOR,
> + AXI_STREAM_PROTOCOL_CHECKER,
> + TRACE_S2MM,
> + AXI_DMA,
> + TRACE_S2MM_FULL
> +};
> +
> +struct debug_ip_data {
> + uint8_t type; /* Type. See enum DEBUG_IP_TYPE */
> + uint8_t index_lowbyte;
> + uint8_t properties;
> + uint8_t major;
> + uint8_t minor;
> + uint8_t index_highbyte;
> + uint8_t reserved[2];
> + uint64_t base_address;
> + char name[128];
> +} __packed;
> +
> +struct debug_ip_layout {
> + uint16_t count;
> + struct debug_ip_data debug_ip_data[1];
> +} __packed;
> +
> +/* Supported clock frequency types */
> +enum XCLBIN_CLOCK_TYPE {
> + CT_UNUSED = 0, /* Initialized value */
> + CT_DATA = 1, /* Data clock */
> + CT_KERNEL = 2, /* Kernel clock */
> + CT_SYSTEM = 3 /* System Clock */
> +};
> +
> +/* Clock Frequency Entry */
> +struct clock_freq {
> + uint16_t freq_MHZ; /* Frequency in MHz */
> + uint8_t type; /* Clock type (enum CLOCK_TYPE) */
> + uint8_t unused[5];
> + char name[128]; /* Clock Name */
> +} __packed;
> +
> +/* Clock frequency section */
> +struct clock_freq_topology {
> + int16_t count; /* Number of entries */
> + struct clock_freq clock_freq[1]; /* Clock array */
> +} __packed;
> +
> +/* Supported MCS file types */
> +enum MCS_TYPE {
> + MCS_UNKNOWN = 0, /* Initialized value */
> + MCS_PRIMARY = 1, /* The primary mcs file data */
> + MCS_SECONDARY = 2, /* The secondary mcs file data */
> +};
> +
> +/* One chunk of MCS data */
> +struct mcs_chunk {
> + uint8_t type; /* MCS data type */
> + uint8_t unused[7];
> + uint64_t offset; /* Data offset */
> + uint64_t size; /* Data size */
> +} __packed;
> +
> +/* MCS data section */
> +struct mcs {
> + int8_t count; /* Number of chunks */
> + int8_t unused[7];
> + struct mcs_chunk chunk[1]; /* MCS chunks followed by data */
> +} __packed;
> +
> +/* bmc data section */
> +struct bmc {
> + uint64_t offset; /* Data offset */
> + uint64_t size; /* Data size (bytes) */
> + char image_name[64]; /* Name of the image */
> + char device_name[64]; /* Name of the device */
> + char version[64];
> + char md5value[33]; /* MD5 checksum */
> + char padding[7];
> +} __packed;
> +
> +/*
> + * soft kernel data section, used by classic driver
> + * Prefix Syntax:
> + * mpo - member, pointer, offset
> + * This variable represents a zero terminated string
> + * that is offseted from the beginning of the section.
> + * The pointer to access the string is initialized as follows:
> + * char * pCharString = (address_of_section) + (mpo value)
> + */
> +struct soft_kernel {
> + uint32_t mpo_name; /* Name of the soft kernel */
> + uint32_t image_offset; /* Image offset */
> + uint32_t image_size; /* Image size */
> + uint32_t mpo_version; /* Version */
> + uint32_t mpo_md5_value; /* MD5 checksum */
> + uint32_t mpo_symbol_name; /* Symbol name */
> + uint32_t num_instances; /* Number of instances */
> + uint8_t padding[36]; /* Reserved for future use */
> + uint8_t reserved_ext[16]; /* Reserved for future extended data */
> +} __packed;
> +
> +enum CHECKSUM_TYPE {
> + CST_UNKNOWN = 0,
> + CST_SDBM = 1,
> + CST_LAST
> +};
> +
> +/**
> + * DOC: PCIe Kernel Driver for Management Physical Function
> + * Interfaces exposed by *xrt-mgmt* driver are defined in file, *xmgmt-ioctl.h*.
> + * Core functionality provided by *xrt-mgmt* driver is described in the following table:
> + *
> + * =========== ============================== ==================================
> + * Functionality ioctl request code data format
> + * =========== ============================== ==================================
> + * 1 FPGA image download XMGMT_IOCICAPDOWNLOAD_AXLF xmgmt_ioc_bitstream_axlf
> + * =========== ============================== ==================================
Seems the lines are not aligned with the text.
> + */
> +
> +#define XMGMT_IOC_MAGIC 'X'
> +#define XMGMT_IOC_ICAP_DOWNLOAD_AXLF 0x6
> +
> +/**
> + * struct xmgmt_ioc_bitstream_axlf - load xclbin (AXLF) device image
> + * used with XMGMT_IOCICAPDOWNLOAD_AXLF ioctl
> + *
> + * @xclbin: Pointer to user's xclbin structure in memory
> + */
> +struct xmgmt_ioc_bitstream_axlf {
> + struct axlf *xclbin;
> +};
> +
> +#define XMGMT_IOCICAPDOWNLOAD_AXLF \
> + _IOW(XMGMT_IOC_MAGIC, XMGMT_IOC_ICAP_DOWNLOAD_AXLF, struct xmgmt_ioc_bitstream_axlf)
> +
> +/*
> + * The following definitions are for binary compatibility with classic XRT management driver
> + */
> +#define XCLMGMT_IOCICAPDOWNLOAD_AXLF XMGMT_IOCICAPDOWNLOAD_AXLF
> +#define xclmgmt_ioc_bitstream_axlf xmgmt_ioc_bitstream_axlf
I didn't see the 2 definitions used in the drivers. Could be removed?
Thanks,
Yilun
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif
> --
> 2.27.0
On Thu, Oct 14, 2021 at 11:12 AM Lizhi Hou <[email protected]> wrote:
>
> Hello Rob,
>
> Please help with the review of the proposed FDT usage by Alveo/XRT drivers.
>
> Thanks,
> Lizhi
>
> On 10/13/21 7:21 PM, Xu Yilun wrote:
> > CAUTION: This message has originated from an External Source. Please use proper judgment and caution when opening attachments, clicking links, or responding to this email.
> >
> >
> >>>> +.. _device_tree_usage:
> >>>> +
> >>>> +Device Tree Usage
> >>>> +-----------------
> >>>> +
> >>>> +The xsabin file stores metadata which advertise HW subsystems present in a
> >>>> +partition. The metadata is stored in device tree format with a well defined
> >>>> +schema. XRT management driver uses this information to bind *xrt_drivers* to
> >>>> +the subsystem instantiations. The xrt_drivers are found in **xrt-lib.ko** kernel
> >>>> +module.
> >>> I'm still catching up the patchset from the very beginning, and just
> >>> finished the Documentation part. So far, I see the DT usage concern
> >>> which may impact the architecure a lot, so I should raise it ASAP.
> >>>
> >>> The concern raised by the DT maintainer:
> >>> https://lore.kernel.org/linux-fpga/CAL_JsqLod6FBGFhu7WXtMrB_z7wj8-up0EetM1QS9M3gjm8d7Q@mail.gmail.com/
> >>>
> >>> First of all, directly parsing FDT in device drivers is not a normal usage of DT
> >>> in linux. It is out of the current DT usage model. So it should be agreed by DT
> >>> maintainers.
> >> Thanks for reviewing XRT document and providing feedback.
> >> Here is the reply from Sonal for Rob’s question:
> >> https://lore.kernel.org/linux-fpga/BY5PR02MB62604B87C66A1AD139A6F153BBF40@BY5PR02MB6260.namprd02.prod.outlook.com/
> >> Overall, libfdt is used by XRT driver to parse the metadata which comes with
> >> an Alveo board.
> >> When XRT driver discovers an Alveo board, it will read a fdt blob from board
> >> firmware file resident on the host.
> >> By parsing the fdt blob, XRT driver gets information about this Alveo board,
> >> such as version, uuid, IPs exposed to PCI BAR, interrupt binding etc.
> >> So libfdt is used simply as Alveo metadata parser here. XRT drivers do not
> >> interact with system wide DT or present the Alveo device tree to host. For
> >> many systems like x86_64, system wide DT is not present but libfdt parsing
> >> services will still be needed.
> > Yes, I understand the use case.
> >
> > My concern is, directly parsing an isolated FDT in device driver and
> > populate sub devices, skipping the unflattening, this is a new working
> > model of device tree usage, but for the same purpose as the existing
> > one.
> >
> > So I really need the confirmation of DT maintainers.
Perhaps you could explain why you think you need to use FDT instead of
unflattening. Without that, the answer is don't use FDT.
> >
> >>> Current FPGA framework modifies kernel's live tree by DT overlay, when FPGA is
> >>> dynamically reprogrammed and new HW devices appear. See
> >>> Documentation/devicetree/bindings/fpga/fpga-region.txt.
> >>>
> >>> Then something less important:
> >>>
> >>> 1. The bindings should be documented in Documentation/devicetree/bindings/.
Yes.
> >>> 2. Are all the example DT usage conform to the exsiting bindings? I
> >>> didn't go through all device classes, but remember like the
> >>> interrupt-controller should have a "interrupt-controller" property, and
> >>> the PCI properties are also different from PCI bindings.
They don't, but should. I can't tell what you are trying to do here,
but it looks like a mess.
> >> The fdt properties are defined for Alveo firmware files. XRT driver is the
> >> only consumer of this data. I am wondering if
> > Personally I don't like the idea of a different binding definition set,
> > even if the new device tree usage is accepted. We all use device tree
> > for device enumeration, so why the different definitions.
> >
> > Thanks,
> > Yilun
> >
> >> Documentation/devicetree/bindings is the right place for Alveo/XRT private
> >> format or should it be documented as part of XRT driver documentation?
> >> Looking for guidance here.
> >>
> >>
> >> Thanks,
> >>
> >> Lizhi
> >>
> >>> Thanks,
> >>> Yilun
> >>>
> >>>> +
> >>>> +Logic UUID
> >>>> +^^^^^^^^^^
> >>>> +A partition is identified uniquely through ``logic_uuid`` property::
> >>>> +
> >>>> + /dts-v1/;
> >>>> + / {
> >>>> + logic_uuid = "0123456789abcdef0123456789abcdef";
> >>>> + ...
> >>>> + }
> >>>> +
> >>>> +Schema Version
> >>>> +^^^^^^^^^^^^^^
> >>>> +Schema version is defined through the ``schema_version`` node. It contains
> >>>> +``major`` and ``minor`` properties as below::
> >>>> +
> >>>> + /dts-v1/;
> >>>> + / {
> >>>> + schema_version {
> >>>> + major = <0x01>;
> >>>> + minor = <0x00>;
> >>>> + };
> >>>> + ...
> >>>> + }
> >>>> +
> >>>> +.. _partition_uuids:
> >>>> +
> >>>> +Partition UUIDs
> >>>> +^^^^^^^^^^^^^^^
> >>>> +Each partition may have parent and child UUIDs. These UUIDs are
> >>>> +defined by ``interfaces`` node and ``interface_uuid`` property::
> >>>> +
> >>>> + /dts-v1/;
> >>>> + / {
> >>>> + interfaces {
> >>>> + @0 {
> >>>> + interface_uuid = "0123456789abcdef0123456789abcdef";
> >>>> + };
> >>>> + @1 {
> >>>> + interface_uuid = "fedcba9876543210fedcba9876543210";
> >>>> + };
> >>>> + ...
> >>>> + };
> >>>> + ...
> >>>> + }
> >>>> +
> >>>> +
> >>>> +Subsystem Instantiations
> >>>> +^^^^^^^^^^^^^^^^^^^^^^^^
> >>>> +Subsystem instantiations are captured as children of ``addressable_endpoints``
> >>>> +node::
> >>>> +
> >>>> + /dts-v1/;
> >>>> + / {
> >>>> + addressable_endpoints {
> >>>> + abc {
> >>>> + ...
> >>>> + };
> >>>> + def {
> >>>> + ...
> >>>> + };
> >>>> + ...
> >>>> + }
> >>>> + }
> >>>> +
> >>>> +Subnode 'abc' and 'def' are the name of subsystem nodes
> >>>> +
> >>>> +Subsystem Node
> >>>> +^^^^^^^^^^^^^^
> >>>> +Each subsystem node and its properties define a hardware instance::
> >>>> +
> >>>> +
> >>>> + addressable_endpoints {
> >>>> + abc {
> >>>> + reg = <0x00 0x1f05000 0x00 0x1000>>
> >>>> + pcie_physical_function = <0x0>;
> >>>> + pcie_bar_mapping = <0x2>;
> >>>> + compatible = "abc def";
> >>>> + interrupts = <0x09 0x0c>;
> >>>> + firmware {
> >>>> + firmware_product_name = "abc"
> >>>> + firmware_branch_name = "def"
> >>>> + firmware_version_major = <1>
> >>>> + firmware_version_minor = <2>
> >>>> + };
> >>>> + }
> >>>> + ...
> >>>> + }
> >>>> +
> >>>> +:reg:
> >>>> + Property defines an address range. `<0x00 0x1f05000 0x00 0x1000>` indicates
> >>>> + *0x00 0x1f05000* as BAR offset and *0x00 0x1000* as address length.
> >>>> +:pcie_physical_function:
> >>>> + Property specifies which PCIe physical function the subsystem node resides.
> >>>> + `<0x0>` implies physical function 0.
> >>>> +:pcie_bar_mapping:
> >>>> + Property specifies which PCIe BAR the subsystem node resides. `<0x2>` implies
> >>>> + BAR 2. A value of 0 means the property is not defined.
> >>>> +:compatible:
> >>>> + Property is a list of strings. The first string in the list specifies the exact
> >>>> + subsystem node. The following strings represent other devices that the device
> >>>> + is compatible with.
> >>>> +:interrupts:
> >>>> + Property specifies start and end interrupts for this subsystem node.
> >>>> + `<0x09 0x0c>` implies interrupts 9 to 13 are used by this subsystem.
> >>>> +:firmware:
> >>>> + Subnode defines the firmware required by this subsystem node.
> >>>> +
> >>>> +Alveo U50 Platform Example
> >>>> +^^^^^^^^^^^^^^^^^^^^^^^^^^
> >>>> +::
> >>>> +
> >>>> + /dts-v1/;
> >>>> +
> >>>> + /{
> >>>> + logic_uuid = "f465b0a3ae8c64f619bc150384ace69b";
> >>>> +
> >>>> + schema_version {
> >>>> + major = <0x01>;
> >>>> + minor = <0x00>;
> >>>> + };
> >>>> +
> >>>> + interfaces {
> >>>> +
> >>>> + @0 {
> >>>> + interface_uuid = "862c7020a250293e32036f19956669e5";
> >>>> + };
> >>>> + };
> >>>> +
> >>>> + addressable_endpoints {
> >>>> +
> >>>> + ep_blp_rom_00 {
> >>>> + reg = <0x00 0x1f04000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> >>>> + };
> >>>> +
> >>>> + ep_card_flash_program_00 {
> >>>> + reg = <0x00 0x1f06000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_quad_spi-1.0\0axi_quad_spi";
> >>>> + interrupts = <0x03 0x03>;
> >>>> + };
> >>>> +
> >>>> + ep_cmc_firmware_mem_00 {
> >>>> + reg = <0x00 0x1e20000 0x00 0x20000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> >>>> +
> >>>> + firmware {
> >>>> + firmware_product_name = "cmc";
> >>>> + firmware_branch_name = "u50";
> >>>> + firmware_version_major = <0x01>;
> >>>> + firmware_version_minor = <0x00>;
> >>>> + };
> >>>> + };
> >>>> +
> >>>> + ep_cmc_intc_00 {
> >>>> + reg = <0x00 0x1e03000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";
> >>>> + interrupts = <0x04 0x04>;
> >>>> + };
> >>>> +
> >>>> + ep_cmc_mutex_00 {
> >>>> + reg = <0x00 0x1e02000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_cmc_regmap_00 {
> >>>> + reg = <0x00 0x1e08000 0x00 0x2000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> >>>> +
> >>>> + firmware {
> >>>> + firmware_product_name = "sc-fw";
> >>>> + firmware_branch_name = "u50";
> >>>> + firmware_version_major = <0x05>;
> >>>> + };
> >>>> + };
> >>>> +
> >>>> + ep_cmc_reset_00 {
> >>>> + reg = <0x00 0x1e01000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_ddr_mem_calib_00 {
> >>>> + reg = <0x00 0x63000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_debug_bscan_mgmt_00 {
> >>>> + reg = <0x00 0x1e90000 0x00 0x10000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-debug_bridge-1.0\0debug_bridge";
> >>>> + };
> >>>> +
> >>>> + ep_ert_base_address_00 {
> >>>> + reg = <0x00 0x21000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_ert_command_queue_mgmt_00 {
> >>>> + reg = <0x00 0x40000 0x00 0x10000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-ert_command_queue-1.0\0ert_command_queue";
> >>>> + };
> >>>> +
> >>>> + ep_ert_command_queue_user_00 {
> >>>> + reg = <0x00 0x40000 0x00 0x10000>;
> >>>> + pcie_physical_function = <0x01>;
> >>>> + compatible = "xilinx.com,reg_abs-ert_command_queue-1.0\0ert_command_queue";
> >>>> + };
> >>>> +
> >>>> + ep_ert_firmware_mem_00 {
> >>>> + reg = <0x00 0x30000 0x00 0x8000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> >>>> +
> >>>> + firmware {
> >>>> + firmware_product_name = "ert";
> >>>> + firmware_branch_name = "v20";
> >>>> + firmware_version_major = <0x01>;
> >>>> + };
> >>>> + };
> >>>> +
> >>>> + ep_ert_intc_00 {
> >>>> + reg = <0x00 0x23000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";
> >>>> + interrupts = <0x05 0x05>;
> >>>> + };
> >>>> +
> >>>> + ep_ert_reset_00 {
> >>>> + reg = <0x00 0x22000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_ert_sched_00 {
> >>>> + reg = <0x00 0x50000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x01>;
> >>>> + compatible = "xilinx.com,reg_abs-ert_sched-1.0\0ert_sched";
> >>>> + interrupts = <0x09 0x0c>;
> >>>> + };
> >>>> +
> >>>> + ep_fpga_configuration_00 {
> >>>> + reg = <0x00 0x1e88000 0x00 0x8000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_hwicap-1.0\0axi_hwicap";
> >>>> + interrupts = <0x02 0x02>;
> >>>> + };
> >>>> +
> >>>> + ep_icap_reset_00 {
> >>>> + reg = <0x00 0x1f07000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_msix_00 {
> >>>> + reg = <0x00 0x00 0x00 0x20000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-msix-1.0\0msix";
> >>>> + pcie_bar_mapping = <0x02>;
> >>>> + };
> >>>> +
> >>>> + ep_pcie_link_mon_00 {
> >>>> + reg = <0x00 0x1f05000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_pr_isolate_plp_00 {
> >>>> + reg = <0x00 0x1f01000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_pr_isolate_ulp_00 {
> >>>> + reg = <0x00 0x1000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";
> >>>> + };
> >>>> +
> >>>> + ep_uuid_rom_00 {
> >>>> + reg = <0x00 0x64000 0x00 0x1000>;
> >>>> + pcie_physical_function = <0x00>;
> >>>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> >>>> + };
> >>>> +
> >>>> + ep_xdma_00 {
> >>>> + reg = <0x00 0x00 0x00 0x10000>;
> >>>> + pcie_physical_function = <0x01>;
> >>>> + compatible = "xilinx.com,reg_abs-xdma-1.0\0xdma";
> >>>> + pcie_bar_mapping = <0x02>;
> >>>> + };
> >>>> + };
> >>>> +
> >>>> + }
> >>>> +
> >>>> +
> >>>> +
> >>>> +Deployment Models
> >>>> +=================
> >>>> +
> >>>> +Baremetal
> >>>> +---------
> >>>> +
> >>>> +In bare-metal deployments, both MPF and UPF are visible and accessible. The
> >>>> +xrt-mgmt driver binds to MPF. The xrt-mgmt driver operations are privileged and
> >>>> +available to system administrator. The full stack is illustrated below::
> >>>> +
> >>>> + HOST
> >>>> +
> >>>> + [XRT-MGMT] [XRT-USER]
> >>>> + | |
> >>>> + | |
> >>>> + +-----+ +-----+
> >>>> + | MPF | | UPF |
> >>>> + | | | |
> >>>> + | PF0 | | PF1 |
> >>>> + +--+--+ +--+--+
> >>>> + ......... ^................. ^..........
> >>>> + | |
> >>>> + | PCIe DEVICE |
> >>>> + | |
> >>>> + +--+------------------+--+
> >>>> + | SHELL |
> >>>> + | |
> >>>> + +------------------------+
> >>>> + | USER |
> >>>> + | |
> >>>> + | |
> >>>> + | |
> >>>> + | |
> >>>> + +------------------------+
> >>>> +
> >>>> +
> >>>> +
> >>>> +Virtualized
> >>>> +-----------
> >>>> +
> >>>> +In virtualized deployments, the privileged MPF is assigned to the host but the
> >>>> +unprivileged UPF is assigned to a guest VM via PCIe pass-through. The xrt-mgmt
> >>>> +driver in host binds to MPF. The xrt-mgmt driver operations are privileged and
> >>>> +only accessible to the MPF. The full stack is illustrated below::
> >>>> +
> >>>> +
> >>>> + ..............
> >>>> + HOST . VM .
> >>>> + . .
> >>>> + [XRT-MGMT] . [XRT-USER] .
> >>>> + | . | .
> >>>> + | . | .
> >>>> + +-----+ . +-----+ .
> >>>> + | MPF | . | UPF | .
> >>>> + | | . | | .
> >>>> + | PF0 | . | PF1 | .
> >>>> + +--+--+ . +--+--+ .
> >>>> + ......... ^................. ^..........
> >>>> + | |
> >>>> + | PCIe DEVICE |
> >>>> + | |
> >>>> + +--+------------------+--+
> >>>> + | SHELL |
> >>>> + | |
> >>>> + +------------------------+
> >>>> + | USER |
> >>>> + | |
> >>>> + | |
> >>>> + | |
> >>>> + | |
> >>>> + +------------------------+
> >>>> +
> >>>> +
> >>>> +
> >>>> +
> >>>> +
> >>>> +Platform Security Considerations
> >>>> +================================
> >>>> +
> >>>> +`Security of Alveo Platform <https://xilinx.github.io/XRT/master/html/security.html>`_
> >>>> +discusses the deployment options and security implications in great detail.
> >>>> diff --git a/MAINTAINERS b/MAINTAINERS
> >>>> index 056966c9aac9..beeaf0257364 100644
> >>>> --- a/MAINTAINERS
> >>>> +++ b/MAINTAINERS
> >>>> @@ -7274,6 +7274,17 @@ F: Documentation/fpga/
> >>>> F: drivers/fpga/
> >>>> F: include/linux/fpga/
> >>>>
> >>>> +FPGA XRT DRIVERS
> >>>> +M: Lizhi Hou <[email protected]>
> >>>> +R: Max Zhen <[email protected]>
> >>>> +R: Sonal Santan <[email protected]>
> >>>> +L: [email protected]
> >>>> +S: Supported
> >>>> +W: https://github.com/Xilinx/XRT
> >>>> +F: Documentation/fpga/xrt.rst
> >>>> +F: drivers/fpga/xrt/
> >>>> +F: include/uapi/linux/xrt/
> >>>> +
> >>>> FPU EMULATOR
> >>>> M: Bill Metzenthen <[email protected]>
> >>>> S: Maintained
> >>>> --
> >>>> 2.27.0
Hello Rob,
> -----Original Message-----
> From: Rob Herring <[email protected]>
> Sent: Tuesday, October 19, 2021 6:03 AM
> To: Lizhi Hou <[email protected]>
> Cc: [email protected]; [email protected]; Max Zhen
> <[email protected]>; Sonal Santan <[email protected]>; Yu Liu
> <[email protected]>; Michal Simek <[email protected]>; Stefano Stabellini
> <[email protected]>; [email protected]; [email protected]; Moritz
> Fischer <[email protected]>; Max Zhen <[email protected]>; Xu Yilun
> <[email protected]>
> Subject: Re: [PATCH V9 XRT Alveo 01/14] Documentation: fpga: Add a
> document describing XRT Alveo drivers
>
> On Thu, Oct 14, 2021 at 11:12 AM Lizhi Hou <[email protected]> wrote:
> >
> > Hello Rob,
> >
> > Please help with the review of the proposed FDT usage by Alveo/XRT drivers.
> >
> > Thanks,
> > Lizhi
> >
> > On 10/13/21 7:21 PM, Xu Yilun wrote:
> > > CAUTION: This message has originated from an External Source. Please use
> proper judgment and caution when opening attachments, clicking links, or
> responding to this email.
> > >
> > >
> > >>>> +.. _device_tree_usage:
> > >>>> +
> > >>>> +Device Tree Usage
> > >>>> +-----------------
> > >>>> +
> > >>>> +The xsabin file stores metadata which advertise HW subsystems
> > >>>> +present in a partition. The metadata is stored in device tree
> > >>>> +format with a well defined schema. XRT management driver uses
> > >>>> +this information to bind *xrt_drivers* to the subsystem
> > >>>> +instantiations. The xrt_drivers are found in **xrt-lib.ko** kernel
> module.
> > >>> I'm still catching up the patchset from the very beginning, and
> > >>> just finished the Documentation part. So far, I see the DT usage
> > >>> concern which may impact the architecure a lot, so I should raise it ASAP.
> > >>>
> > >>> The concern raised by the DT maintainer:
> > >>> https://lore.kernel.org/linux-fpga/CAL_JsqLod6FBGFhu7WXtMrB_z7wj8-
> > >>> [email protected]/
> > >>>
> > >>> First of all, directly parsing FDT in device drivers is not a
> > >>> normal usage of DT in linux. It is out of the current DT usage
> > >>> model. So it should be agreed by DT maintainers.
> > >> Thanks for reviewing XRT document and providing feedback.
> > >> Here is the reply from Sonal for Rob’s question:
> > >> https://lore.kernel.org/linux-
> fpga/BY5PR02MB62604B87C66A1AD139A6F15
> > >> [email protected]/
> > >> Overall, libfdt is used by XRT driver to parse the metadata which
> > >> comes with an Alveo board.
> > >> When XRT driver discovers an Alveo board, it will read a fdt blob
> > >> from board firmware file resident on the host.
> > >> By parsing the fdt blob, XRT driver gets information about this
> > >> Alveo board, such as version, uuid, IPs exposed to PCI BAR, interrupt
> binding etc.
> > >> So libfdt is used simply as Alveo metadata parser here. XRT drivers
> > >> do not interact with system wide DT or present the Alveo device
> > >> tree to host. For many systems like x86_64, system wide DT is not
> > >> present but libfdt parsing services will still be needed.
> > > Yes, I understand the use case.
> > >
> > > My concern is, directly parsing an isolated FDT in device driver and
> > > populate sub devices, skipping the unflattening, this is a new
> > > working model of device tree usage, but for the same purpose as the
> > > existing one.
> > >
> > > So I really need the confirmation of DT maintainers.
>
> Perhaps you could explain why you think you need to use FDT instead of
> unflattening. Without that, the answer is don't use FDT.
>
Xilinx Alveo PCIe cards are predominantly used in x86_64 systems which do not have device tree support compiled into the kernel. XRT driver uses a matching FDT to discover IP subsystems sitting behind the PCIe BARs exposed by an Alveo PCIe card. The FDT blob (as part of an Alveo PCIe card firmware) can be freely downloaded from xilinx.com.
If using an unflattened tree (instead of FDT) is the right solution then we would certainly look into it. Should the PCIe driver then call something like of_fdt_unflatten_tree() with a FDT blob and the kernel would then build an unflattened tree and hang it off the PCIe device node? The FDT blob for a PCIe device is only known to the driver since different PCIe platforms may store it differently: a known location in the PCIe BAR, the flash on the PCIe board or a file on the filesystem. If the kernel can provide a general on demand unflattening service similar to DTO use model, we will have a more scalable solution to describe IP subsystems exposed by a PCIe device and make their discovery data driven. Can this feature also work on x86_64 systems which does not use OF?
> > >
> > >>> Current FPGA framework modifies kernel's live tree by DT overlay,
> > >>> when FPGA is dynamically reprogrammed and new HW devices appear.
> > >>> See Documentation/devicetree/bindings/fpga/fpga-region.txt.
> > >>>
> > >>> Then something less important:
> > >>>
> > >>> 1. The bindings should be documented in
> Documentation/devicetree/bindings/.
>
> Yes.
>
> > >>> 2. Are all the example DT usage conform to the exsiting bindings? I
> > >>> didn't go through all device classes, but remember like the
> > >>> interrupt-controller should have a "interrupt-controller" property,
> and
> > >>> the PCI properties are also different from PCI bindings.
>
> They don't, but should. I can't tell what you are trying to do here, but it looks
> like a mess.
>
Will appreciate any pointers on existing PCIe use case for the device tree.
Thanks,
-Sonal
> > >> The fdt properties are defined for Alveo firmware files. XRT driver
> > >> is the only consumer of this data. I am wondering if
> > > Personally I don't like the idea of a different binding definition
> > > set, even if the new device tree usage is accepted. We all use
> > > device tree for device enumeration, so why the different definitions.
> > >
> > > Thanks,
> > > Yilun
> > >
> > >> Documentation/devicetree/bindings is the right place for Alveo/XRT
> > >> private format or should it be documented as part of XRT driver
> documentation?
> > >> Looking for guidance here.
> > >>
> > >>
> > >> Thanks,
> > >>
> > >> Lizhi
> > >>
> > >>> Thanks,
> > >>> Yilun
> > >>>
> > >>>> +
> > >>>> +Logic UUID
> > >>>> +^^^^^^^^^^
> > >>>> +A partition is identified uniquely through ``logic_uuid`` property::
> > >>>> +
> > >>>> + /dts-v1/;
> > >>>> + / {
> > >>>> + logic_uuid = "0123456789abcdef0123456789abcdef";
> > >>>> + ...
> > >>>> + }
> > >>>> +
> > >>>> +Schema Version
> > >>>> +^^^^^^^^^^^^^^
> > >>>> +Schema version is defined through the ``schema_version`` node.
> > >>>> +It contains ``major`` and ``minor`` properties as below::
> > >>>> +
> > >>>> + /dts-v1/;
> > >>>> + / {
> > >>>> + schema_version {
> > >>>> + major = <0x01>;
> > >>>> + minor = <0x00>;
> > >>>> + };
> > >>>> + ...
> > >>>> + }
> > >>>> +
> > >>>> +.. _partition_uuids:
> > >>>> +
> > >>>> +Partition UUIDs
> > >>>> +^^^^^^^^^^^^^^^
> > >>>> +Each partition may have parent and child UUIDs. These UUIDs are
> > >>>> +defined by ``interfaces`` node and ``interface_uuid`` property::
> > >>>> +
> > >>>> + /dts-v1/;
> > >>>> + / {
> > >>>> + interfaces {
> > >>>> + @0 {
> > >>>> + interface_uuid = "0123456789abcdef0123456789abcdef";
> > >>>> + };
> > >>>> + @1 {
> > >>>> + interface_uuid = "fedcba9876543210fedcba9876543210";
> > >>>> + };
> > >>>> + ...
> > >>>> + };
> > >>>> + ...
> > >>>> + }
> > >>>> +
> > >>>> +
> > >>>> +Subsystem Instantiations
> > >>>> +^^^^^^^^^^^^^^^^^^^^^^^^
> > >>>> +Subsystem instantiations are captured as children of
> > >>>> +``addressable_endpoints``
> > >>>> +node::
> > >>>> +
> > >>>> + /dts-v1/;
> > >>>> + / {
> > >>>> + addressable_endpoints {
> > >>>> + abc {
> > >>>> + ...
> > >>>> + };
> > >>>> + def {
> > >>>> + ...
> > >>>> + };
> > >>>> + ...
> > >>>> + }
> > >>>> + }
> > >>>> +
> > >>>> +Subnode 'abc' and 'def' are the name of subsystem nodes
> > >>>> +
> > >>>> +Subsystem Node
> > >>>> +^^^^^^^^^^^^^^
> > >>>> +Each subsystem node and its properties define a hardware instance::
> > >>>> +
> > >>>> +
> > >>>> + addressable_endpoints {
> > >>>> + abc {
> > >>>> + reg = <0x00 0x1f05000 0x00 0x1000>>
> > >>>> + pcie_physical_function = <0x0>;
> > >>>> + pcie_bar_mapping = <0x2>;
> > >>>> + compatible = "abc def";
> > >>>> + interrupts = <0x09 0x0c>;
> > >>>> + firmware {
> > >>>> + firmware_product_name = "abc"
> > >>>> + firmware_branch_name = "def"
> > >>>> + firmware_version_major = <1>
> > >>>> + firmware_version_minor = <2>
> > >>>> + };
> > >>>> + }
> > >>>> + ...
> > >>>> + }
> > >>>> +
> > >>>> +:reg:
> > >>>> + Property defines an address range. `<0x00 0x1f05000 0x00
> > >>>> +0x1000>` indicates
> > >>>> + *0x00 0x1f05000* as BAR offset and *0x00 0x1000* as address
> length.
> > >>>> +:pcie_physical_function:
> > >>>> + Property specifies which PCIe physical function the subsystem node
> resides.
> > >>>> + `<0x0>` implies physical function 0.
> > >>>> +:pcie_bar_mapping:
> > >>>> + Property specifies which PCIe BAR the subsystem node resides.
> > >>>> +`<0x2>` implies BAR 2. A value of 0 means the property is not defined.
> > >>>> +:compatible:
> > >>>> + Property is a list of strings. The first string in the list
> > >>>> +specifies the exact subsystem node. The following strings
> > >>>> +represent other devices that the device is compatible with.
> > >>>> +:interrupts:
> > >>>> + Property specifies start and end interrupts for this subsystem node.
> > >>>> + `<0x09 0x0c>` implies interrupts 9 to 13 are used by this subsystem.
> > >>>> +:firmware:
> > >>>> + Subnode defines the firmware required by this subsystem node.
> > >>>> +
> > >>>> +Alveo U50 Platform Example
> > >>>> +^^^^^^^^^^^^^^^^^^^^^^^^^^
> > >>>> +::
> > >>>> +
> > >>>> + /dts-v1/;
> > >>>> +
> > >>>> + /{
> > >>>> + logic_uuid = "f465b0a3ae8c64f619bc150384ace69b";
> > >>>> +
> > >>>> + schema_version {
> > >>>> + major = <0x01>;
> > >>>> + minor = <0x00>;
> > >>>> + };
> > >>>> +
> > >>>> + interfaces {
> > >>>> +
> > >>>> + @0 {
> > >>>> + interface_uuid =
> "862c7020a250293e32036f19956669e5";
> > >>>> + };
> > >>>> + };
> > >>>> +
> > >>>> + addressable_endpoints {
> > >>>> +
> > >>>> + ep_blp_rom_00 {
> > >>>> + reg = <0x00 0x1f04000 0x00 0x1000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-
> 1.0\0axi_bram_ctrl";
> > >>>> + };
> > >>>> +
> > >>>> + ep_card_flash_program_00 {
> > >>>> + reg = <0x00 0x1f06000 0x00 0x1000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-axi_quad_spi-
> 1.0\0axi_quad_spi";
> > >>>> + interrupts = <0x03 0x03>;
> > >>>> + };
> > >>>> +
> > >>>> + ep_cmc_firmware_mem_00 {
> > >>>> + reg = <0x00 0x1e20000 0x00 0x20000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible =
> > >>>> + "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> > >>>> +
> > >>>> + firmware {
> > >>>> + firmware_product_name = "cmc";
> > >>>> + firmware_branch_name = "u50";
> > >>>> + firmware_version_major = <0x01>;
> > >>>> + firmware_version_minor = <0x00>;
> > >>>> + };
> > >>>> + };
> > >>>> +
> > >>>> + ep_cmc_intc_00 {
> > >>>> + reg = <0x00 0x1e03000 0x00 0x1000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-axi_intc-
> 1.0\0axi_intc";
> > >>>> + interrupts = <0x04 0x04>;
> > >>>> + };
> > >>>> +
> > >>>> + ep_cmc_mutex_00 {
> > >>>> + reg = <0x00 0x1e02000 0x00 0x1000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-
> 1.0\0axi_gpio";
> > >>>> + };
> > >>>> +
> > >>>> + ep_cmc_regmap_00 {
> > >>>> + reg = <0x00 0x1e08000 0x00 0x2000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible =
> > >>>> + "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> > >>>> +
> > >>>> + firmware {
> > >>>> + firmware_product_name = "sc-fw";
> > >>>> + firmware_branch_name = "u50";
> > >>>> + firmware_version_major = <0x05>;
> > >>>> + };
> > >>>> + };
> > >>>> +
> > >>>> + ep_cmc_reset_00 {
> > >>>> + reg = <0x00 0x1e01000 0x00 0x1000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-
> 1.0\0axi_gpio";
> > >>>> + };
> > >>>> +
> > >>>> + ep_ddr_mem_calib_00 {
> > >>>> + reg = <0x00 0x63000 0x00 0x1000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-
> 1.0\0axi_gpio";
> > >>>> + };
> > >>>> +
> > >>>> + ep_debug_bscan_mgmt_00 {
> > >>>> + reg = <0x00 0x1e90000 0x00 0x10000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-debug_bridge-
> 1.0\0debug_bridge";
> > >>>> + };
> > >>>> +
> > >>>> + ep_ert_base_address_00 {
> > >>>> + reg = <0x00 0x21000 0x00 0x1000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-
> 1.0\0axi_gpio";
> > >>>> + };
> > >>>> +
> > >>>> + ep_ert_command_queue_mgmt_00 {
> > >>>> + reg = <0x00 0x40000 0x00 0x10000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-ert_command_queue-
> 1.0\0ert_command_queue";
> > >>>> + };
> > >>>> +
> > >>>> + ep_ert_command_queue_user_00 {
> > >>>> + reg = <0x00 0x40000 0x00 0x10000>;
> > >>>> + pcie_physical_function = <0x01>;
> > >>>> + compatible = "xilinx.com,reg_abs-ert_command_queue-
> 1.0\0ert_command_queue";
> > >>>> + };
> > >>>> +
> > >>>> + ep_ert_firmware_mem_00 {
> > >>>> + reg = <0x00 0x30000 0x00 0x8000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible =
> > >>>> + "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";
> > >>>> +
> > >>>> + firmware {
> > >>>> + firmware_product_name = "ert";
> > >>>> + firmware_branch_name = "v20";
> > >>>> + firmware_version_major = <0x01>;
> > >>>> + };
> > >>>> + };
> > >>>> +
> > >>>> + ep_ert_intc_00 {
> > >>>> + reg = <0x00 0x23000 0x00 0x1000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-axi_intc-
> 1.0\0axi_intc";
> > >>>> + interrupts = <0x05 0x05>;
> > >>>> + };
> > >>>> +
> > >>>> + ep_ert_reset_00 {
> > >>>> + reg = <0x00 0x22000 0x00 0x1000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-
> 1.0\0axi_gpio";
> > >>>> + };
> > >>>> +
> > >>>> + ep_ert_sched_00 {
> > >>>> + reg = <0x00 0x50000 0x00 0x1000>;
> > >>>> + pcie_physical_function = <0x01>;
> > >>>> + compatible = "xilinx.com,reg_abs-ert_sched-
> 1.0\0ert_sched";
> > >>>> + interrupts = <0x09 0x0c>;
> > >>>> + };
> > >>>> +
> > >>>> + ep_fpga_configuration_00 {
> > >>>> + reg = <0x00 0x1e88000 0x00 0x8000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-axi_hwicap-
> 1.0\0axi_hwicap";
> > >>>> + interrupts = <0x02 0x02>;
> > >>>> + };
> > >>>> +
> > >>>> + ep_icap_reset_00 {
> > >>>> + reg = <0x00 0x1f07000 0x00 0x1000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-
> 1.0\0axi_gpio";
> > >>>> + };
> > >>>> +
> > >>>> + ep_msix_00 {
> > >>>> + reg = <0x00 0x00 0x00 0x20000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-msix-1.0\0msix";
> > >>>> + pcie_bar_mapping = <0x02>;
> > >>>> + };
> > >>>> +
> > >>>> + ep_pcie_link_mon_00 {
> > >>>> + reg = <0x00 0x1f05000 0x00 0x1000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-
> 1.0\0axi_gpio";
> > >>>> + };
> > >>>> +
> > >>>> + ep_pr_isolate_plp_00 {
> > >>>> + reg = <0x00 0x1f01000 0x00 0x1000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-
> 1.0\0axi_gpio";
> > >>>> + };
> > >>>> +
> > >>>> + ep_pr_isolate_ulp_00 {
> > >>>> + reg = <0x00 0x1000 0x00 0x1000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-axi_gpio-
> 1.0\0axi_gpio";
> > >>>> + };
> > >>>> +
> > >>>> + ep_uuid_rom_00 {
> > >>>> + reg = <0x00 0x64000 0x00 0x1000>;
> > >>>> + pcie_physical_function = <0x00>;
> > >>>> + compatible = "xilinx.com,reg_abs-axi_bram_ctrl-
> 1.0\0axi_bram_ctrl";
> > >>>> + };
> > >>>> +
> > >>>> + ep_xdma_00 {
> > >>>> + reg = <0x00 0x00 0x00 0x10000>;
> > >>>> + pcie_physical_function = <0x01>;
> > >>>> + compatible = "xilinx.com,reg_abs-xdma-1.0\0xdma";
> > >>>> + pcie_bar_mapping = <0x02>;
> > >>>> + };
> > >>>> + };
> > >>>> +
> > >>>> + }
> > >>>> +
> > >>>> +
> > >>>> +
> > >>>> +Deployment Models
> > >>>> +=================
> > >>>> +
> > >>>> +Baremetal
> > >>>> +---------
> > >>>> +
> > >>>> +In bare-metal deployments, both MPF and UPF are visible and
> > >>>> +accessible. The xrt-mgmt driver binds to MPF. The xrt-mgmt
> > >>>> +driver operations are privileged and available to system administrator.
> The full stack is illustrated below::
> > >>>> +
> > >>>> + HOST
> > >>>> +
> > >>>> + [XRT-MGMT] [XRT-USER]
> > >>>> + | |
> > >>>> + | |
> > >>>> + +-----+ +-----+
> > >>>> + | MPF | | UPF |
> > >>>> + | | | |
> > >>>> + | PF0 | | PF1 |
> > >>>> + +--+--+ +--+--+
> > >>>> + ......... ^................. ^..........
> > >>>> + | |
> > >>>> + | PCIe DEVICE |
> > >>>> + | |
> > >>>> + +--+------------------+--+
> > >>>> + | SHELL |
> > >>>> + | |
> > >>>> + +------------------------+
> > >>>> + | USER |
> > >>>> + | |
> > >>>> + | |
> > >>>> + | |
> > >>>> + | |
> > >>>> + +------------------------+
> > >>>> +
> > >>>> +
> > >>>> +
> > >>>> +Virtualized
> > >>>> +-----------
> > >>>> +
> > >>>> +In virtualized deployments, the privileged MPF is assigned to
> > >>>> +the host but the unprivileged UPF is assigned to a guest VM via
> > >>>> +PCIe pass-through. The xrt-mgmt driver in host binds to MPF. The
> > >>>> +xrt-mgmt driver operations are privileged and only accessible to the
> MPF. The full stack is illustrated below::
> > >>>> +
> > >>>> +
> > >>>> + ..............
> > >>>> + HOST . VM .
> > >>>> + . .
> > >>>> + [XRT-MGMT] . [XRT-USER] .
> > >>>> + | . | .
> > >>>> + | . | .
> > >>>> + +-----+ . +-----+ .
> > >>>> + | MPF | . | UPF | .
> > >>>> + | | . | | .
> > >>>> + | PF0 | . | PF1 | .
> > >>>> + +--+--+ . +--+--+ .
> > >>>> + ......... ^................. ^..........
> > >>>> + | |
> > >>>> + | PCIe DEVICE |
> > >>>> + | |
> > >>>> + +--+------------------+--+
> > >>>> + | SHELL |
> > >>>> + | |
> > >>>> + +------------------------+
> > >>>> + | USER |
> > >>>> + | |
> > >>>> + | |
> > >>>> + | |
> > >>>> + | |
> > >>>> + +------------------------+
> > >>>> +
> > >>>> +
> > >>>> +
> > >>>> +
> > >>>> +
> > >>>> +Platform Security Considerations
> > >>>> +================================
> > >>>> +
> > >>>> +`Security of Alveo Platform
> > >>>> +<https://xilinx.github.io/XRT/master/html/security.html>`_
> > >>>> +discusses the deployment options and security implications in great
> detail.
> > >>>> diff --git a/MAINTAINERS b/MAINTAINERS index
> > >>>> 056966c9aac9..beeaf0257364 100644
> > >>>> --- a/MAINTAINERS
> > >>>> +++ b/MAINTAINERS
> > >>>> @@ -7274,6 +7274,17 @@ F: Documentation/fpga/
> > >>>> F: drivers/fpga/
> > >>>> F: include/linux/fpga/
> > >>>>
> > >>>> +FPGA XRT DRIVERS
> > >>>> +M: Lizhi Hou <[email protected]>
> > >>>> +R: Max Zhen <[email protected]>
> > >>>> +R: Sonal Santan <[email protected]>
> > >>>> +L: [email protected]
> > >>>> +S: Supported
> > >>>> +W: https://github.com/Xilinx/XRT
> > >>>> +F: Documentation/fpga/xrt.rst
> > >>>> +F: drivers/fpga/xrt/
> > >>>> +F: include/uapi/linux/xrt/
> > >>>> +
> > >>>> FPU EMULATOR
> > >>>> M: Bill Metzenthen <[email protected]>
> > >>>> S: Maintained
> > >>>> --
> > >>>> 2.27.0
On Thu, Oct 21, 2021 at 12:36 AM Sonal Santan <[email protected]> wrote:
>
> Hello Rob,
>
> > -----Original Message-----
> > From: Rob Herring <[email protected]>
> > Sent: Tuesday, October 19, 2021 6:03 AM
> > To: Lizhi Hou <[email protected]>
> > Cc: [email protected]; [email protected]; Max Zhen
> > <[email protected]>; Sonal Santan <[email protected]>; Yu Liu
> > <[email protected]>; Michal Simek <[email protected]>; Stefano Stabellini
> > <[email protected]>; [email protected]; [email protected]; Moritz
> > Fischer <[email protected]>; Max Zhen <[email protected]>; Xu Yilun
> > <[email protected]>
> > Subject: Re: [PATCH V9 XRT Alveo 01/14] Documentation: fpga: Add a
> > document describing XRT Alveo drivers
> >
> > On Thu, Oct 14, 2021 at 11:12 AM Lizhi Hou <[email protected]> wrote:
> > >
> > > Hello Rob,
> > >
> > > Please help with the review of the proposed FDT usage by Alveo/XRT drivers.
> > >
> > > Thanks,
> > > Lizhi
> > >
> > > On 10/13/21 7:21 PM, Xu Yilun wrote:
> > > > CAUTION: This message has originated from an External Source. Please use
> > proper judgment and caution when opening attachments, clicking links, or
> > responding to this email.
> > > >
> > > >
> > > >>>> +.. _device_tree_usage:
> > > >>>> +
> > > >>>> +Device Tree Usage
> > > >>>> +-----------------
> > > >>>> +
> > > >>>> +The xsabin file stores metadata which advertise HW subsystems
> > > >>>> +present in a partition. The metadata is stored in device tree
> > > >>>> +format with a well defined schema. XRT management driver uses
> > > >>>> +this information to bind *xrt_drivers* to the subsystem
> > > >>>> +instantiations. The xrt_drivers are found in **xrt-lib.ko** kernel
> > module.
> > > >>> I'm still catching up the patchset from the very beginning, and
> > > >>> just finished the Documentation part. So far, I see the DT usage
> > > >>> concern which may impact the architecure a lot, so I should raise it ASAP.
> > > >>>
> > > >>> The concern raised by the DT maintainer:
> > > >>> https://lore.kernel.org/linux-fpga/CAL_JsqLod6FBGFhu7WXtMrB_z7wj8-
> > > >>> [email protected]/
> > > >>>
> > > >>> First of all, directly parsing FDT in device drivers is not a
> > > >>> normal usage of DT in linux. It is out of the current DT usage
> > > >>> model. So it should be agreed by DT maintainers.
> > > >> Thanks for reviewing XRT document and providing feedback.
> > > >> Here is the reply from Sonal for Rob’s question:
> > > >> https://lore.kernel.org/linux-
> > fpga/BY5PR02MB62604B87C66A1AD139A6F15
> > > >> [email protected]/
> > > >> Overall, libfdt is used by XRT driver to parse the metadata which
> > > >> comes with an Alveo board.
> > > >> When XRT driver discovers an Alveo board, it will read a fdt blob
> > > >> from board firmware file resident on the host.
> > > >> By parsing the fdt blob, XRT driver gets information about this
> > > >> Alveo board, such as version, uuid, IPs exposed to PCI BAR, interrupt
> > binding etc.
> > > >> So libfdt is used simply as Alveo metadata parser here. XRT drivers
> > > >> do not interact with system wide DT or present the Alveo device
> > > >> tree to host. For many systems like x86_64, system wide DT is not
> > > >> present but libfdt parsing services will still be needed.
> > > > Yes, I understand the use case.
> > > >
> > > > My concern is, directly parsing an isolated FDT in device driver and
> > > > populate sub devices, skipping the unflattening, this is a new
> > > > working model of device tree usage, but for the same purpose as the
> > > > existing one.
> > > >
> > > > So I really need the confirmation of DT maintainers.
> >
> > Perhaps you could explain why you think you need to use FDT instead of
> > unflattening. Without that, the answer is don't use FDT.
> >
> Xilinx Alveo PCIe cards are predominantly used in x86_64 systems which do not have device tree support compiled into the kernel. XRT driver uses a matching FDT to discover IP subsystems sitting behind the PCIe BARs exposed by an Alveo PCIe card. The FDT blob (as part of an Alveo PCIe card firmware) can be freely downloaded from xilinx.com.
If the kernel is going to consume that FDT blob, then it needs to
follow upstream practices which primarily means all the device
bindings must be documented with schema and reviewed.
> If using an unflattened tree (instead of FDT) is the right solution then we would certainly look into it. Should the PCIe driver then call something like of_fdt_unflatten_tree() with a FDT blob and the kernel would then build an unflattened tree and hang it off the PCIe device node? The FDT blob for a PCIe device is only known to the driver since different PCIe platforms may store it differently: a known location in the PCIe BAR, the flash on the PCIe board or a file on the filesystem. If the kernel can provide a general on demand unflattening service similar to DTO use model, we will have a more scalable solution to describe IP subsystems exposed by a PCIe device and make their discovery data driven. Can this feature also work on x86_64 systems which does not use OF?
There's other similar usecases like this. For example, an FTDI or
similar USB to serial chip that has GPIO, I2C, etc. and could have
downstream devices hanging off of those interfaces. And then you could
plug-in multiple of those devices to the host system. For this to
work, we'd need to create a base tree (if there isn't one) with nodes
for the USB or PCI device(s) and then an overlay for the device can be
applied to those nodes. This is also partially an issue on DT based
systems as the DT node may not exist given these are 'discoverable'
buses. It's a bit easier to solve given the PCI host bridge or USB
controller exists in the DT already.
There's really 2 separate parts here. There's how to attach a DT to a
device on a non-DT system (or DT system with a device not described in
the base DT). The second part is how to describe the PCI device and
downstream devices. This part is no different than any other device.
> > > >>> Current FPGA framework modifies kernel's live tree by DT overlay,
> > > >>> when FPGA is dynamically reprogrammed and new HW devices appear.
> > > >>> See Documentation/devicetree/bindings/fpga/fpga-region.txt.
> > > >>>
> > > >>> Then something less important:
> > > >>>
> > > >>> 1. The bindings should be documented in
> > Documentation/devicetree/bindings/.
> >
> > Yes.
> >
> > > >>> 2. Are all the example DT usage conform to the exsiting bindings? I
> > > >>> didn't go through all device classes, but remember like the
> > > >>> interrupt-controller should have a "interrupt-controller" property,
> > and
> > > >>> the PCI properties are also different from PCI bindings.
> >
> > They don't, but should. I can't tell what you are trying to do here, but it looks
> > like a mess.
> >
> Will appreciate any pointers on existing PCIe use case for the device tree.
Documentation/devicetree/bindings/pci/ and there's the PCI bus schema
here[1]. There's also the OpenFirmware PCI spec[2].
Rob
[1] https://github.com/devicetree-org/dt-schema/blob/main/schemas/pci/pci-bus.yaml
[2] https://www.devicetree.org/open-firmware/bindings/pci/pci2_1.pdf
Thanks for your suggestions and pointers.
We will look into creating a generic patchset for attaching a device tree for PCIe use case. Will then refactor the XRT Alveo driver to use this new infrastructure.
-Sonal
________________________________________
From: Rob Herring <[email protected]>
Sent: Thursday, October 21, 2021 10:38 AM
To: Sonal Santan
Cc: Lizhi Hou; [email protected]; [email protected]; Max Zhen; Yu Liu; Michal Simek; Stefano Stabellini; [email protected]; [email protected]; Moritz Fischer; Xu Yilun; David Woodhouse
Subject: Re: [PATCH V9 XRT Alveo 01/14] Documentation: fpga: Add a document describing XRT Alveo drivers
On Thu, Oct 21, 2021 at 12:36 AM Sonal Santan <[email protected]> wrote:
>
> Hello Rob,
>
> > -----Original Message-----
> > From: Rob Herring <[email protected]>
> > Sent: Tuesday, October 19, 2021 6:03 AM
> > To: Lizhi Hou <[email protected]>
> > Cc: [email protected]; [email protected]; Max Zhen
> > <[email protected]>; Sonal Santan <[email protected]>; Yu Liu
> > <[email protected]>; Michal Simek <[email protected]>; Stefano Stabellini
> > <[email protected]>; [email protected]; [email protected]; Moritz
> > Fischer <[email protected]>; Max Zhen <[email protected]>; Xu Yilun
> > <[email protected]>
> > Subject: Re: [PATCH V9 XRT Alveo 01/14] Documentation: fpga: Add a
> > document describing XRT Alveo drivers
> >
> > On Thu, Oct 14, 2021 at 11:12 AM Lizhi Hou <[email protected]> wrote:
> > >
> > > Hello Rob,
> > >
> > > Please help with the review of the proposed FDT usage by Alveo/XRT drivers.
> > >
> > > Thanks,
> > > Lizhi
> > >
> > > On 10/13/21 7:21 PM, Xu Yilun wrote:
> > > > CAUTION: This message has originated from an External Source. Please use
> > proper judgment and caution when opening attachments, clicking links, or
> > responding to this email.
> > > >
> > > >
> > > >>>> +.. _device_tree_usage:
> > > >>>> +
> > > >>>> +Device Tree Usage
> > > >>>> +-----------------
> > > >>>> +
> > > >>>> +The xsabin file stores metadata which advertise HW subsystems
> > > >>>> +present in a partition. The metadata is stored in device tree
> > > >>>> +format with a well defined schema. XRT management driver uses
> > > >>>> +this information to bind *xrt_drivers* to the subsystem
> > > >>>> +instantiations. The xrt_drivers are found in **xrt-lib.ko** kernel
> > module.
> > > >>> I'm still catching up the patchset from the very beginning, and
> > > >>> just finished the Documentation part. So far, I see the DT usage
> > > >>> concern which may impact the architecure a lot, so I should raise it ASAP.
> > > >>>
> > > >>> The concern raised by the DT maintainer:
> > > >>> https://lore.kernel.org/linux-fpga/CAL_JsqLod6FBGFhu7WXtMrB_z7wj8-
> > > >>> [email protected]/
> > > >>>
> > > >>> First of all, directly parsing FDT in device drivers is not a
> > > >>> normal usage of DT in linux. It is out of the current DT usage
> > > >>> model. So it should be agreed by DT maintainers.
> > > >> Thanks for reviewing XRT document and providing feedback.
> > > >> Here is the reply from Sonal for Rob's question:
> > > >> https://lore.kernel.org/linux-
> > fpga/BY5PR02MB62604B87C66A1AD139A6F15
> > > >> [email protected]/
> > > >> Overall, libfdt is used by XRT driver to parse the metadata which
> > > >> comes with an Alveo board.
> > > >> When XRT driver discovers an Alveo board, it will read a fdt blob
> > > >> from board firmware file resident on the host.
> > > >> By parsing the fdt blob, XRT driver gets information about this
> > > >> Alveo board, such as version, uuid, IPs exposed to PCI BAR, interrupt
> > binding etc.
> > > >> So libfdt is used simply as Alveo metadata parser here. XRT drivers
> > > >> do not interact with system wide DT or present the Alveo device
> > > >> tree to host. For many systems like x86_64, system wide DT is not
> > > >> present but libfdt parsing services will still be needed.
> > > > Yes, I understand the use case.
> > > >
> > > > My concern is, directly parsing an isolated FDT in device driver and
> > > > populate sub devices, skipping the unflattening, this is a new
> > > > working model of device tree usage, but for the same purpose as the
> > > > existing one.
> > > >
> > > > So I really need the confirmation of DT maintainers.
> >
> > Perhaps you could explain why you think you need to use FDT instead of
> > unflattening. Without that, the answer is don't use FDT.
> >
> Xilinx Alveo PCIe cards are predominantly used in x86_64 systems which do not have device tree support compiled into the kernel. XRT driver uses a matching FDT to discover IP subsystems sitting behind the PCIe BARs exposed by an Alveo PCIe card. The FDT blob (as part of an Alveo PCIe card firmware) can be freely downloaded from xilinx.com.
If the kernel is going to consume that FDT blob, then it needs to
follow upstream practices which primarily means all the device
bindings must be documented with schema and reviewed.
> If using an unflattened tree (instead of FDT) is the right solution then we would certainly look into it. Should the PCIe driver then call something like of_fdt_unflatten_tree() with a FDT blob and the kernel would then build an unflattened tree and hang it off the PCIe device node? The FDT blob for a PCIe device is only known to the driver since different PCIe platforms may store it differently: a known location in the PCIe BAR, the flash on the PCIe board or a file on the filesystem. If the kernel can provide a general on demand unflattening service similar to DTO use model, we will have a more scalable solution to describe IP subsystems exposed by a PCIe device and make their discovery data driven. Can this feature also work on x86_64 systems which does not use OF?
There's other similar usecases like this. For example, an FTDI or
similar USB to serial chip that has GPIO, I2C, etc. and could have
downstream devices hanging off of those interfaces. And then you could
plug-in multiple of those devices to the host system. For this to
work, we'd need to create a base tree (if there isn't one) with nodes
for the USB or PCI device(s) and then an overlay for the device can be
applied to those nodes. This is also partially an issue on DT based
systems as the DT node may not exist given these are 'discoverable'
buses. It's a bit easier to solve given the PCI host bridge or USB
controller exists in the DT already.
There's really 2 separate parts here. There's how to attach a DT to a
device on a non-DT system (or DT system with a device not described in
the base DT). The second part is how to describe the PCI device and
downstream devices. This part is no different than any other device.
> > > >>> Current FPGA framework modifies kernel's live tree by DT overlay,
> > > >>> when FPGA is dynamically reprogrammed and new HW devices appear.
> > > >>> See Documentation/devicetree/bindings/fpga/fpga-region.txt.
> > > >>>
> > > >>> Then something less important:
> > > >>>
> > > >>> 1. The bindings should be documented in
> > Documentation/devicetree/bindings/.
> >
> > Yes.
> >
> > > >>> 2. Are all the example DT usage conform to the exsiting bindings? I
> > > >>> didn't go through all device classes, but remember like the
> > > >>> interrupt-controller should have a "interrupt-controller" property,
> > and
> > > >>> the PCI properties are also different from PCI bindings.
> >
> > They don't, but should. I can't tell what you are trying to do here, but it looks
> > like a mess.
> >
> Will appreciate any pointers on existing PCIe use case for the device tree.
Documentation/devicetree/bindings/pci/ and there's the PCI bus schema
here[1]. There's also the OpenFirmware PCI spec[2].
Rob
[1] https://github.com/devicetree-org/dt-schema/blob/main/schemas/pci/pci-bus.yaml
[2] https://www.devicetree.org/open-firmware/bindings/pci/pci2_1.pdf
On Mon, Aug 02, 2021 at 09:05:11AM -0700, Lizhi Hou wrote:
> xrt-lib kernel module infrastructure code to register and manage all
> leaf driver modules.
>
> Signed-off-by: Sonal Santan <[email protected]>
> Signed-off-by: Max Zhen <[email protected]>
> Signed-off-by: Lizhi Hou <[email protected]>
> Reviewed-by: Tom Rix <[email protected]>
> ---
> drivers/fpga/xrt/include/subdev_id.h | 39 +++
> drivers/fpga/xrt/include/xdevice.h | 141 ++++++++
> drivers/fpga/xrt/include/xleaf.h | 205 +++++++++++
> drivers/fpga/xrt/include/xleaf/clkfreq.h | 21 ++
> drivers/fpga/xrt/include/xleaf/clock.h | 29 ++
> .../fpga/xrt/include/xleaf/ddr_calibration.h | 28 ++
> drivers/fpga/xrt/include/xleaf/devctl.h | 40 +++
> drivers/fpga/xrt/lib/lib-drv.c | 318 ++++++++++++++++++
> drivers/fpga/xrt/lib/lib-drv.h | 21 ++
> 9 files changed, 842 insertions(+)
> create mode 100644 drivers/fpga/xrt/include/subdev_id.h
> create mode 100644 drivers/fpga/xrt/include/xdevice.h
> create mode 100644 drivers/fpga/xrt/include/xleaf.h
> create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
> create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
> create mode 100644 drivers/fpga/xrt/include/xleaf/ddr_calibration.h
> create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
> create mode 100644 drivers/fpga/xrt/lib/lib-drv.c
> create mode 100644 drivers/fpga/xrt/lib/lib-drv.h
>
> diff --git a/drivers/fpga/xrt/include/subdev_id.h b/drivers/fpga/xrt/include/subdev_id.h
> new file mode 100644
> index 000000000000..02df4b939a1b
> --- /dev/null
> +++ b/drivers/fpga/xrt/include/subdev_id.h
> @@ -0,0 +1,39 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Cheng Zhen <[email protected]>
> + */
> +
> +#ifndef _XRT_SUBDEV_ID_H_
> +#define _XRT_SUBDEV_ID_H_
> +
> +/*
> + * Every subdev driver has an ID for others to refer to it. There can be multiple number of
> + * instances of a subdev driver. A <subdev_id, subdev_instance> tuple is a unique identification
> + * of a specific instance of a subdev driver.
> + */
> +enum xrt_subdev_id {
> + XRT_SUBDEV_INVALID = 0,
> + XRT_SUBDEV_GRP,
> + XRT_SUBDEV_VSEC,
> + XRT_SUBDEV_VSEC_GOLDEN,
> + XRT_SUBDEV_DEVCTL,
> + XRT_SUBDEV_AXIGATE,
> + XRT_SUBDEV_ICAP,
> + XRT_SUBDEV_TEST,
> + XRT_SUBDEV_MGMT_MAIN,
> + XRT_SUBDEV_QSPI,
> + XRT_SUBDEV_MAILBOX,
> + XRT_SUBDEV_CMC,
> + XRT_SUBDEV_CALIB,
> + XRT_SUBDEV_CLKFREQ,
> + XRT_SUBDEV_CLOCK,
> + XRT_SUBDEV_SRSR,
> + XRT_SUBDEV_UCS,
> + XRT_SUBDEV_NUM, /* Total number of subdevs. */
> + XRT_ROOT = -1, /* Special ID for root driver. */
> +};
> +
> +#endif /* _XRT_SUBDEV_ID_H_ */
> diff --git a/drivers/fpga/xrt/include/xdevice.h b/drivers/fpga/xrt/include/xdevice.h
> new file mode 100644
> index 000000000000..b40ebe98b54d
> --- /dev/null
> +++ b/drivers/fpga/xrt/include/xdevice.h
> @@ -0,0 +1,141 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Lizhi Hou <[email protected]>
> + */
> +
> +#ifndef _XRT_DEVICE_H_
> +#define _XRT_DEVICE_H_
> +
> +#include <linux/fs.h>
> +#include <linux/cdev.h>
> +
> +#define XRT_MAX_DEVICE_NODES 128
> +#define XRT_INVALID_DEVICE_INST (XRT_MAX_DEVICE_NODES + 1)
> +
> +enum {
> + XRT_DEVICE_STATE_NONE = 0,
> + XRT_DEVICE_STATE_ADDED
> +};
> +
> +/*
> + * struct xrt_device - represent an xrt device on xrt bus
> + *
> + * dev: generic device interface.
> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
> + * name: name of the xrt device.
> + * instance: instance of the xrt device. The xrt device with same id can have
> + * more than 1 instances.
> + * state: current state of the xrt device.
> + * num_resources: The total number of resource for the xrt device.
> + * resource: point to the xrt device resource array.
> + * sdev_data: private data pointer.
> + */
> +struct xrt_device {
> + struct device dev;
> + u32 subdev_id;
> + const char *name;
> + u32 instance;
> + u32 state;
> + u32 num_resources;
> + struct resource *resource;
> + void *sdev_data;
> +};
> +
> +/*
> + * If populated by xrt device driver, infra will handle the mechanics of
> + * char device (un)registration.
> + */
> +enum xrt_dev_file_mode {
> + /* Infra create cdev, default file name */
> + XRT_DEV_FILE_DEFAULT = 0,
> + /* Infra create cdev, need to encode inst num in file name */
> + XRT_DEV_FILE_MULTI_INST,
> + /* No auto creation of cdev by infra, leaf handles it by itself */
> + XRT_DEV_FILE_NO_AUTO,
> +};
> +
> +struct xrt_dev_file_ops {
> + const struct file_operations xsf_ops;
> + dev_t xsf_dev_t;
> + const char *xsf_dev_name;
> + enum xrt_dev_file_mode xsf_mode;
> +};
> +
> +/*
> + * this struct define the endpoints belong to the same xrt device
> + * ep_name: endpoint name
> + * compat: compatible string
> + */
> +struct xrt_dev_ep_names {
> + const char *ep_name;
> + const char *compat;
> +};
> +
> +struct xrt_dev_endpoints {
> + struct xrt_dev_ep_names *xse_names;
> + /* minimum number of endpoints to support the subdevice */
> + u32 xse_min_ep;
Why some device must have several DT nodes?
> +};
> +
> +/*
> + * struct xrt_driver - represent a xrt device driver
> + *
> + * driver: driver model structure.
> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
> + * file_ops: character device name and callbacks.
> + * probe: mandatory callback for device binding.
> + * remove: callback for device unbinding.
> + * leaf_call: callback for servicing other leaf drivers.
> + */
> +struct xrt_driver {
> + struct device_driver driver;
> + u32 subdev_id;
> + struct xrt_dev_file_ops file_ops;
> + struct xrt_dev_endpoints *endpoints;
No DOC for this field.
Seems this is to list the device types the driver supports, but usually
this is done by an id_table in driver.
> +
> + /*
> + * Subdev driver callbacks populated by subdev driver.
> + */
> + int (*probe)(struct xrt_device *xrt_dev);
> + void (*remove)(struct xrt_device *xrt_dev);
> + /*
> + * If leaf_call is defined, these are called by other leaf drivers.
> + * Note that root driver may call into leaf_call of a group driver.
> + */
> + int (*leaf_call)(struct xrt_device *xrt_dev, u32 cmd, void *arg);
Why this is needed? If a function of a driver module needs to be called
by other modules, just export the function.
> +};
> +
> +#define to_xrt_dev(d) container_of(d, struct xrt_device, dev)
> +#define to_xrt_drv(d) container_of(d, struct xrt_driver, driver)
> +
> +static inline void *xrt_get_drvdata(const struct xrt_device *xdev)
> +{
> + return dev_get_drvdata(&xdev->dev);
> +}
> +
> +static inline void xrt_set_drvdata(struct xrt_device *xdev, void *data)
> +{
> + dev_set_drvdata(&xdev->dev, data);
> +}
> +
> +static inline void *xrt_get_xdev_data(struct device *dev)
> +{
> + struct xrt_device *xdev = to_xrt_dev(dev);
> +
> + return xdev->sdev_data;
> +}
> +
> +struct xrt_device *
> +xrt_device_register(struct device *parent, u32 id,
> + struct resource *res, u32 res_num,
> + void *pdata, size_t data_sz);
> +void xrt_device_unregister(struct xrt_device *xdev);
> +int xrt_register_driver(struct xrt_driver *drv);
> +void xrt_unregister_driver(struct xrt_driver *drv);
> +void *xrt_get_xdev_data(struct device *dev);
> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32 type, u32 num);
> +
> +#endif /* _XRT_DEVICE_H_ */
> diff --git a/drivers/fpga/xrt/include/xleaf.h b/drivers/fpga/xrt/include/xleaf.h
> new file mode 100644
> index 000000000000..f065fc766e0f
> --- /dev/null
> +++ b/drivers/fpga/xrt/include/xleaf.h
> @@ -0,0 +1,205 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Cheng Zhen <[email protected]>
> + * Sonal Santan <[email protected]>
> + */
> +
> +#ifndef _XRT_XLEAF_H_
> +#define _XRT_XLEAF_H_
> +
> +#include <linux/mod_devicetable.h>
> +#include "xdevice.h"
> +#include "subdev_id.h"
> +#include "xroot.h"
> +#include "events.h"
> +
> +/* All subdev drivers should use below common routines to print out msg. */
> +#define DEV(xdev) (&(xdev)->dev)
> +#define DEV_PDATA(xdev) \
> + ((struct xrt_subdev_platdata *)xrt_get_xdev_data(DEV(xdev)))
> +#define DEV_FILE_OPS(xdev) \
> + (&(to_xrt_drv((xdev)->dev.driver))->file_ops)
> +#define FMT_PRT(prt_fn, xdev, fmt, args...) \
> + ({typeof(xdev) (_xdev) = (xdev); \
> + prt_fn(DEV(_xdev), "%s %s: " fmt, \
> + DEV_PDATA(_xdev)->xsp_root_name, __func__, ##args); })
> +#define xrt_err(xdev, fmt, args...) FMT_PRT(dev_err, xdev, fmt, ##args)
> +#define xrt_warn(xdev, fmt, args...) FMT_PRT(dev_warn, xdev, fmt, ##args)
> +#define xrt_info(xdev, fmt, args...) FMT_PRT(dev_info, xdev, fmt, ##args)
> +#define xrt_dbg(xdev, fmt, args...) FMT_PRT(dev_dbg, xdev, fmt, ##args)
> +
> +#define XRT_DEFINE_REGMAP_CONFIG(config_name) \
> + static const struct regmap_config config_name = { \
> + .reg_bits = 32, \
> + .val_bits = 32, \
> + .reg_stride = 4, \
> + .max_register = 0x1000, \
> + }
> +
> +enum {
> + /* Starting cmd for common leaf cmd implemented by all leaves. */
> + XRT_XLEAF_COMMON_BASE = 0,
> + /* Starting cmd for leaves' specific leaf cmds. */
> + XRT_XLEAF_CUSTOM_BASE = 64,
> +};
> +
> +enum xrt_xleaf_common_leaf_cmd {
> + XRT_XLEAF_EVENT = XRT_XLEAF_COMMON_BASE,
> +};
> +
> +/*
> + * Partially initialized by the parent driver, then, passed in as subdev driver's
> + * platform data when creating subdev driver instance by calling platform
> + * device register API (xrt_device_register_data() or the likes).
Didn't find the xrt_device_register_data, should be xrt_device_register?
> + *
> + * Once device register API returns, platform driver framework makes a copy of
> + * this buffer and maintains its life cycle. The content of the buffer is
> + * completely owned by subdev driver.
Generally the platform data contains some specific description of the device, so
it should also be managered by the parent same as the device. The driver
owned data should be in dev.driver_data
> + *
> + * Thus, parent driver should be very careful when it touches this buffer
> + * again once it's handed over to subdev driver. And the data structure
> + * should not contain pointers pointing to buffers that is managed by
> + * other or parent drivers since it could have been freed before platform
> + * data buffer is freed by platform driver framework.
> + */
> +struct xrt_subdev_platdata {
> + /*
> + * Per driver instance callback. The xdev points to the instance.
> + * Should always be defined for subdev driver to get service from root.
> + */
> + xrt_subdev_root_cb_t xsp_root_cb;
> + void *xsp_root_cb_arg;
> +
> + /* Something to associate w/ root for msg printing. */
> + const char *xsp_root_name;
> +
> + /*
> + * Char dev support for this subdev instance.
> + * Initialized by subdev driver.
> + */
> + struct cdev xsp_cdev;
> + struct device *xsp_sysdev;
> + struct mutex xsp_devnode_lock; /* devnode lock */
> + struct completion xsp_devnode_comp;
> + int xsp_devnode_ref;
> + bool xsp_devnode_online;
> + bool xsp_devnode_excl;
> +
> + /*
> + * Subdev driver specific init data. The buffer should be embedded
> + * in this data structure buffer after dtb, so that it can be freed
> + * together with platform data.
> + */
> + loff_t xsp_priv_off; /* Offset into this platform data buffer. */
> + size_t xsp_priv_len;
Didn't see the usage.
> +
> + /*
> + * Populated by parent driver to describe the device tree for
> + * the subdev driver to handle. Should always be last one since it's
> + * of variable length.
> + */
> + bool xsp_dtb_valid;
> + char xsp_dtb[0];
> +};
> +
> +struct subdev_match_arg {
> + enum xrt_subdev_id id;
> + int instance;
> +};
> +
> +bool xleaf_has_endpoint(struct xrt_device *xdev, const char *endpoint_name);
> +struct xrt_device *xleaf_get_leaf(struct xrt_device *xdev,
> + xrt_subdev_match_t cb, void *arg);
> +
> +static inline bool subdev_match(enum xrt_subdev_id id, struct xrt_device *xdev, void *arg)
> +{
> + const struct subdev_match_arg *a = (struct subdev_match_arg *)arg;
> + int instance = a->instance;
> +
> + if (id != a->id)
> + return false;
> + if (instance != xdev->instance && instance != XRT_INVALID_DEVICE_INST)
> + return false;
> + return true;
> +}
> +
> +static inline bool xrt_subdev_match_epname(enum xrt_subdev_id id,
> + struct xrt_device *xdev, void *arg)
> +{
> + return xleaf_has_endpoint(xdev, arg);
> +}
> +
> +static inline struct xrt_device *
> +xleaf_get_leaf_by_id(struct xrt_device *xdev,
> + enum xrt_subdev_id id, int instance)
> +{
> + struct subdev_match_arg arg = { id, instance };
> +
> + return xleaf_get_leaf(xdev, subdev_match, &arg);
> +}
> +
> +static inline struct xrt_device *
> +xleaf_get_leaf_by_epname(struct xrt_device *xdev, const char *name)
> +{
> + return xleaf_get_leaf(xdev, xrt_subdev_match_epname, (void *)name);
> +}
> +
> +static inline int xleaf_call(struct xrt_device *tgt, u32 cmd, void *arg)
> +{
> + return (to_xrt_drv(tgt->dev.driver)->leaf_call)(tgt, cmd, arg);
> +}
> +
> +int xleaf_broadcast_event(struct xrt_device *xdev, enum xrt_events evt, bool async);
> +int xleaf_create_group(struct xrt_device *xdev, char *dtb);
> +int xleaf_destroy_group(struct xrt_device *xdev, int instance);
> +void xleaf_get_root_res(struct xrt_device *xdev, u32 region_id, struct resource **res);
> +void xleaf_get_root_id(struct xrt_device *xdev, unsigned short *vendor, unsigned short *device,
> + unsigned short *subvendor, unsigned short *subdevice);
> +void xleaf_hot_reset(struct xrt_device *xdev);
> +int xleaf_put_leaf(struct xrt_device *xdev, struct xrt_device *leaf);
> +struct device *xleaf_register_hwmon(struct xrt_device *xdev, const char *name, void *drvdata,
> + const struct attribute_group **grps);
> +void xleaf_unregister_hwmon(struct xrt_device *xdev, struct device *hwmon);
> +int xleaf_wait_for_group_bringup(struct xrt_device *xdev);
> +
> +/*
> + * Character device helper APIs for use by leaf drivers
> + */
> +static inline bool xleaf_devnode_enabled(struct xrt_device *xdev)
> +{
> + return DEV_FILE_OPS(xdev)->xsf_ops.open;
> +}
> +
> +int xleaf_devnode_create(struct xrt_device *xdev,
> + const char *file_name, const char *inst_name);
> +void xleaf_devnode_destroy(struct xrt_device *xdev);
> +
> +struct xrt_device *xleaf_devnode_open_excl(struct inode *inode);
> +struct xrt_device *xleaf_devnode_open(struct inode *inode);
> +void xleaf_devnode_close(struct inode *inode);
> +
> +/* Module's init/fini routines for leaf driver in xrt-lib module */
> +#define XRT_LEAF_INIT_FINI_FUNC(name) \
> +void name##_leaf_init_fini(bool init) \
> +{ \
> + if (init) \
> + xrt_register_driver(&xrt_##name##_driver); \
> + else \
> + xrt_unregister_driver(&xrt_##name##_driver); \
> +}
> +
> +/* Module's init/fini routines for leaf driver in xrt-lib module */
> +void group_leaf_init_fini(bool init);
> +void vsec_leaf_init_fini(bool init);
> +void devctl_leaf_init_fini(bool init);
> +void axigate_leaf_init_fini(bool init);
> +void icap_leaf_init_fini(bool init);
> +void calib_leaf_init_fini(bool init);
> +void clkfreq_leaf_init_fini(bool init);
> +void clock_leaf_init_fini(bool init);
> +void ucs_leaf_init_fini(bool init);
> +
> +#endif /* _XRT_LEAF_H_ */
> diff --git a/drivers/fpga/xrt/include/xleaf/clkfreq.h b/drivers/fpga/xrt/include/xleaf/clkfreq.h
> new file mode 100644
> index 000000000000..005441d5df78
> --- /dev/null
> +++ b/drivers/fpga/xrt/include/xleaf/clkfreq.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Lizhi Hou <[email protected]>
> + */
> +
> +#ifndef _XRT_CLKFREQ_H_
> +#define _XRT_CLKFREQ_H_
> +
> +#include "xleaf.h"
> +
> +/*
> + * CLKFREQ driver leaf calls.
> + */
> +enum xrt_clkfreq_leaf_cmd {
> + XRT_CLKFREQ_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
> +};
> +
> +#endif /* _XRT_CLKFREQ_H_ */
> diff --git a/drivers/fpga/xrt/include/xleaf/clock.h b/drivers/fpga/xrt/include/xleaf/clock.h
> new file mode 100644
> index 000000000000..1379e24fa5d0
> --- /dev/null
> +++ b/drivers/fpga/xrt/include/xleaf/clock.h
> @@ -0,0 +1,29 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Lizhi Hou <[email protected]>
> + */
> +
> +#ifndef _XRT_CLOCK_H_
> +#define _XRT_CLOCK_H_
> +
> +#include "xleaf.h"
> +#include <linux/fpga_xrt.h>
> +
> +/*
> + * CLOCK driver leaf calls.
> + */
> +enum xrt_clock_leaf_cmd {
> + XRT_CLOCK_SET = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
> + XRT_CLOCK_GET,
> + XRT_CLOCK_VERIFY,
> +};
> +
> +struct xrt_clock_get {
> + u16 freq;
> + u32 freq_cnter;
> +};
> +
> +#endif /* _XRT_CLOCK_H_ */
> diff --git a/drivers/fpga/xrt/include/xleaf/ddr_calibration.h b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
> new file mode 100644
> index 000000000000..c44ae30f939a
> --- /dev/null
> +++ b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
> @@ -0,0 +1,28 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Cheng Zhen <[email protected]>
> + */
> +
> +#ifndef _XRT_DDR_CALIBRATION_H_
> +#define _XRT_DDR_CALIBRATION_H_
> +
> +#include "xleaf.h"
> +#include <linux/fpga-xrt.h>
> +
> +/*
> + * Memory calibration driver leaf calls.
> + */
> +enum xrt_calib_results {
> + XRT_CALIB_UNKNOWN = 0,
> + XRT_CALIB_SUCCEEDED,
> + XRT_CALIB_FAILED,
> +};
> +
> +enum xrt_calib_leaf_cmd {
> + XRT_CALIB_RESULT = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
> +};
> +
> +#endif /* _XRT_DDR_CALIBRATION_H_ */
> diff --git a/drivers/fpga/xrt/include/xleaf/devctl.h b/drivers/fpga/xrt/include/xleaf/devctl.h
> new file mode 100644
> index 000000000000..b97f3b6d9326
> --- /dev/null
> +++ b/drivers/fpga/xrt/include/xleaf/devctl.h
> @@ -0,0 +1,40 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Lizhi Hou <[email protected]>
> + */
> +
> +#ifndef _XRT_DEVCTL_H_
> +#define _XRT_DEVCTL_H_
> +
> +#include "xleaf.h"
> +
> +/*
> + * DEVCTL driver leaf calls.
> + */
> +enum xrt_devctl_leaf_cmd {
> + XRT_DEVCTL_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
> +};
> +
> +enum xrt_devctl_id {
> + XRT_DEVCTL_ROM_UUID = 0,
> + XRT_DEVCTL_DDR_CALIB,
> + XRT_DEVCTL_GOLDEN_VER,
> + XRT_DEVCTL_MAX
> +};
> +
> +struct xrt_devctl_rw {
> + u32 xdr_id;
> + void *xdr_buf;
> + u32 xdr_len;
> + u32 xdr_offset;
> +};
> +
> +struct xrt_devctl_intf_uuid {
> + u32 uuid_num;
> + uuid_t *uuids;
> +};
> +
> +#endif /* _XRT_DEVCTL_H_ */
> diff --git a/drivers/fpga/xrt/lib/lib-drv.c b/drivers/fpga/xrt/lib/lib-drv.c
> new file mode 100644
> index 000000000000..7d21ef565651
> --- /dev/null
> +++ b/drivers/fpga/xrt/lib/lib-drv.c
> @@ -0,0 +1,318 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Cheng Zhen <[email protected]>
> + * Lizhi Hou <[email protected]>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/vmalloc.h>
> +#include <linux/slab.h>
> +#include "xleaf.h"
> +#include "xroot.h"
> +#include "lib-drv.h"
> +
> +#define XRT_IPLIB_MODULE_NAME "xrt-lib"
> +#define XRT_DRVNAME(drv) ((drv)->driver.name)
> +
> +#define XRT_SUBDEV_ID_SHIFT 16
> +#define XRT_SUBDEV_ID_MASK ((1 << XRT_SUBDEV_ID_SHIFT) - 1)
> +
> +struct xrt_find_drv_data {
> + enum xrt_subdev_id id;
> + struct xrt_driver *xdrv;
> +};
> +
> +struct class *xrt_class;
> +static DEFINE_IDA(xrt_device_ida);
> +
> +static inline u32 xrt_instance_to_id(enum xrt_subdev_id id, u32 instance)
> +{
> + return (id << XRT_SUBDEV_ID_SHIFT) | instance;
> +}
> +
> +static inline u32 xrt_id_to_instance(u32 id)
> +{
> + return id & XRT_SUBDEV_ID_MASK;
> +}
> +
> +static int xrt_bus_match(struct device *dev, struct device_driver *drv)
> +{
> + struct xrt_device *xdev = to_xrt_dev(dev);
> + struct xrt_driver *xdrv = to_xrt_drv(drv);
> +
> + if (xdev->subdev_id == xdrv->subdev_id)
> + return 1;
> +
> + return 0;
> +}
> +
> +static int xrt_bus_probe(struct device *dev)
> +{
> + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
> + struct xrt_device *xdev = to_xrt_dev(dev);
> +
> + return xdrv->probe(xdev);
> +}
> +
> +static int xrt_bus_remove(struct device *dev)
> +{
> + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
> + struct xrt_device *xdev = to_xrt_dev(dev);
> +
> + if (xdrv->remove)
> + xdrv->remove(xdev);
> +
> + return 0;
> +}
> +
> +struct bus_type xrt_bus_type = {
> + .name = "xrt",
> + .match = xrt_bus_match,
> + .probe = xrt_bus_probe,
> + .remove = xrt_bus_remove,
> +};
Actually I'm not sure a new bus type is needed. The bus itself is a
virtual bus and the device identifier (xdev.subdev_id) for the bus
is SW defined, then why don't we just use the name string matching and
create platform devices for each leaf?
> +
> +int xrt_register_driver(struct xrt_driver *drv)
> +{
> + const char *drvname = XRT_DRVNAME(drv);
> + int rc = 0;
> +
> + /* Initialize dev_t for char dev node. */
> + if (drv->file_ops.xsf_ops.open) {
> + rc = alloc_chrdev_region(&drv->file_ops.xsf_dev_t, 0,
> + XRT_MAX_DEVICE_NODES, drvname);
I think no need to help alloc char majors for each driver here. The
function is for driver registration, the char dev is for the actual driver
functionality and had better be managered in its own module.
I only see the xmgmt_main module needs the char dev, so maybe we only do
it in xmgmt_main init function.
> + if (rc) {
> + pr_err("failed to alloc dev minor for %s: %d\n", drvname, rc);
> + return rc;
> + }
> + } else {
> + drv->file_ops.xsf_dev_t = (dev_t)-1;
> + }
> +
> + drv->driver.owner = THIS_MODULE;
> + drv->driver.bus = &xrt_bus_type;
> +
> + rc = driver_register(&drv->driver);
> + if (rc) {
> + pr_err("register %s xrt driver failed\n", drvname);
> + if (drv->file_ops.xsf_dev_t != (dev_t)-1) {
> + unregister_chrdev_region(drv->file_ops.xsf_dev_t,
> + XRT_MAX_DEVICE_NODES);
> + }
> + return rc;
> + }
> +
> + pr_info("%s registered successfully\n", drvname);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(xrt_register_driver);
> +
> +void xrt_unregister_driver(struct xrt_driver *drv)
> +{
> + driver_unregister(&drv->driver);
> +
> + if (drv->file_ops.xsf_dev_t != (dev_t)-1)
> + unregister_chrdev_region(drv->file_ops.xsf_dev_t, XRT_MAX_DEVICE_NODES);
> +}
> +EXPORT_SYMBOL_GPL(xrt_unregister_driver);
> +
> +static int __find_driver(struct device_driver *drv, void *_data)
> +{
> + struct xrt_driver *xdrv = to_xrt_drv(drv);
> + struct xrt_find_drv_data *data = _data;
> +
> + if (xdrv->subdev_id == data->id) {
> + data->xdrv = xdrv;
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> +const char *xrt_drv_name(enum xrt_subdev_id id)
> +{
> + struct xrt_find_drv_data data = { 0 };
> +
> + data.id = id;
> + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
> +
> + if (data.xdrv)
> + return XRT_DRVNAME(data.xdrv);
> +
> + return NULL;
> +}
> +
> +static int xrt_drv_get_instance(enum xrt_subdev_id id)
> +{
> + int ret;
> +
> + ret = ida_alloc_range(&xrt_device_ida, xrt_instance_to_id(id, 0),
> + xrt_instance_to_id(id, XRT_MAX_DEVICE_NODES),
> + GFP_KERNEL);
> + if (ret < 0)
> + return ret;
> +
> + return xrt_id_to_instance((u32)ret);
> +}
> +
> +static void xrt_drv_put_instance(enum xrt_subdev_id id, int instance)
> +{
> + ida_free(&xrt_device_ida, xrt_instance_to_id(id, instance));
> +}
> +
> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id id)
> +{
> + struct xrt_find_drv_data data = { 0 };
> +
> + data.id = id;
> + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
> +
> + if (data.xdrv)
> + return data.xdrv->endpoints;
> +
> + return NULL;
> +}
> +
> +static void xrt_device_release(struct device *dev)
> +{
> + struct xrt_device *xdev = container_of(dev, struct xrt_device, dev);
> +
> + kfree(xdev);
> +}
> +
> +void xrt_device_unregister(struct xrt_device *xdev)
> +{
> + if (xdev->state == XRT_DEVICE_STATE_ADDED)
> + device_del(&xdev->dev);
> +
> + vfree(xdev->sdev_data);
> + kfree(xdev->resource);
> +
> + if (xdev->instance != XRT_INVALID_DEVICE_INST)
> + xrt_drv_put_instance(xdev->subdev_id, xdev->instance);
I suggest all the free work be done in dev.release. The xrt_device may
still be accessible after this function, these resources may still be
accessed too.
> +
> + if (xdev->dev.release == xrt_device_release)
> + put_device(&xdev->dev);
It is OK but unnormal, I suggest you don't integrate the error handling
of xrt_device_register here. That would make the code easier to read.
> +}
> +
> +struct xrt_device *
> +xrt_device_register(struct device *parent, u32 id,
> + struct resource *res, u32 res_num,
> + void *pdata, size_t data_sz)
> +{
> + struct xrt_device *xdev = NULL;
> + int ret;
> +
> + xdev = kzalloc(sizeof(*xdev), GFP_KERNEL);
> + if (!xdev)
> + return xdev;
> + xdev->instance = XRT_INVALID_DEVICE_INST;
> +
> + /* Obtain dev instance number. */
> + ret = xrt_drv_get_instance(id);
> + if (ret < 0) {
> + dev_err(parent, "failed get instance, ret %d", ret);
> + goto fail;
> + }
> +
> + xdev->instance = ret;
> + xdev->name = xrt_drv_name(id);
What if the xrt_driver is not loaded yet?
Normally the device represents the HW devices in system, it could be
enumerated and created no matter whether its device driver exists. So I
think some code in this patchset is not reasonable, that creates
xrt_device when the corresponding xrt_driver exists.
> + xdev->subdev_id = id;
> + device_initialize(&xdev->dev);
> + xdev->dev.release = xrt_device_release;
> + xdev->dev.parent = parent;
> +
> + xdev->dev.bus = &xrt_bus_type;
> + dev_set_name(&xdev->dev, "%s.%d", xdev->name, xdev->instance);
> +
> + xdev->num_resources = res_num;
> + xdev->resource = kmemdup(res, sizeof(*res) * res_num, GFP_KERNEL);
> + if (!xdev->resource)
> + goto fail;
> +
> + xdev->sdev_data = vzalloc(data_sz);
vmalloc the platform_data is quite unnormal, the concern is the
performance. Maybe you should split the structures and only vmalloc
for big data chunk.
> + if (!xdev->sdev_data)
> + goto fail;
> +
> + memcpy(xdev->sdev_data, pdata, data_sz);
As I can see, you need to cut and copy the selected dtb data in
platform_data for each dev. But why? Why not put the whole dtb somewhere
and let the driver access it as needed?
> +
> + ret = device_add(&xdev->dev);
> + if (ret) {
> + dev_err(parent, "failed add device, ret %d", ret);
> + goto fail;
> + }
> + xdev->state = XRT_DEVICE_STATE_ADDED;
> +
> + return xdev;
> +
> +fail:
> + xrt_device_unregister(xdev);
> + kfree(xdev);
> +
> + return NULL;
> +}
> +
> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32 type, u32 num)
> +{
> + u32 i;
> +
> + for (i = 0; i < xdev->num_resources; i++) {
> + struct resource *r = &xdev->resource[i];
> +
> + if (type == resource_type(r) && num-- == 0)
> + return r;
> + }
> + return NULL;
> +}
> +
> +/*
> + * Leaf driver's module init/fini callbacks. This is not a open infrastructure for dynamic
> + * plugging in drivers. All drivers should be statically added.
That's not the normal way, moreover, as a FPGA, users may integrate all
kinds of IP blocks whose drivers are not belong to FPGA domain. Put them
all in FPGA folder is not a good idea.
Thanks,
Yilun
> + */
> +static void (*leaf_init_fini_cbs[])(bool) = {
> + group_leaf_init_fini,
> + axigate_leaf_init_fini,
> + icap_leaf_init_fini,
> +};
> +
> +static __init int xrt_lib_init(void)
> +{
> + int ret;
> + int i;
> +
> + ret = bus_register(&xrt_bus_type);
> + if (ret)
> + return ret;
> +
> + xrt_class = class_create(THIS_MODULE, XRT_IPLIB_MODULE_NAME);
> + if (IS_ERR(xrt_class)) {
> + bus_unregister(&xrt_bus_type);
> + return PTR_ERR(xrt_class);
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
> + leaf_init_fini_cbs[i](true);
> + return 0;
> +}
> +
> +static __exit void xrt_lib_fini(void)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
> + leaf_init_fini_cbs[i](false);
> +
> + class_destroy(xrt_class);
> + bus_unregister(&xrt_bus_type);
> +}
> +
> +module_init(xrt_lib_init);
> +module_exit(xrt_lib_fini);
> +
> +MODULE_AUTHOR("XRT Team <[email protected]>");
> +MODULE_DESCRIPTION("Xilinx Alveo IP Lib driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/fpga/xrt/lib/lib-drv.h b/drivers/fpga/xrt/lib/lib-drv.h
> new file mode 100644
> index 000000000000..0276c28e009f
> --- /dev/null
> +++ b/drivers/fpga/xrt/lib/lib-drv.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Xilinx, Inc.
> + *
> + * Authors:
> + * Cheng Zhen <[email protected]>
> + */
> +
> +#ifndef _LIB_DRV_H_
> +#define _LIB_DRV_H_
> +
> +#include <linux/device/class.h>
> +#include <linux/device/bus.h>
> +
> +extern struct class *xrt_class;
> +extern struct bus_type xrt_bus_type;
> +
> +const char *xrt_drv_name(enum xrt_subdev_id id);
> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id id);
> +
> +#endif /* _LIB_DRV_H_ */
> --
> 2.27.0
Hi Yilun,
Thanks a lot for reviewing the patch. On Rob's advice we are exploring
the device tree changes which will likely have some software interface
changes. Will get back to you.
Lizhi
On 10/25/21 1:01 AM, Xu Yilun wrote:
>
> On Mon, Aug 02, 2021 at 09:05:11AM -0700, Lizhi Hou wrote:
>> xrt-lib kernel module infrastructure code to register and manage all
>> leaf driver modules.
>>
>> Signed-off-by: Sonal Santan <[email protected]>
>> Signed-off-by: Max Zhen <[email protected]>
>> Signed-off-by: Lizhi Hou <[email protected]>
>> Reviewed-by: Tom Rix <[email protected]>
>> ---
>> drivers/fpga/xrt/include/subdev_id.h | 39 +++
>> drivers/fpga/xrt/include/xdevice.h | 141 ++++++++
>> drivers/fpga/xrt/include/xleaf.h | 205 +++++++++++
>> drivers/fpga/xrt/include/xleaf/clkfreq.h | 21 ++
>> drivers/fpga/xrt/include/xleaf/clock.h | 29 ++
>> .../fpga/xrt/include/xleaf/ddr_calibration.h | 28 ++
>> drivers/fpga/xrt/include/xleaf/devctl.h | 40 +++
>> drivers/fpga/xrt/lib/lib-drv.c | 318 ++++++++++++++++++
>> drivers/fpga/xrt/lib/lib-drv.h | 21 ++
>> 9 files changed, 842 insertions(+)
>> create mode 100644 drivers/fpga/xrt/include/subdev_id.h
>> create mode 100644 drivers/fpga/xrt/include/xdevice.h
>> create mode 100644 drivers/fpga/xrt/include/xleaf.h
>> create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
>> create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
>> create mode 100644 drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>> create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
>> create mode 100644 drivers/fpga/xrt/lib/lib-drv.c
>> create mode 100644 drivers/fpga/xrt/lib/lib-drv.h
>>
>> diff --git a/drivers/fpga/xrt/include/subdev_id.h b/drivers/fpga/xrt/include/subdev_id.h
>> new file mode 100644
>> index 000000000000..02df4b939a1b
>> --- /dev/null
>> +++ b/drivers/fpga/xrt/include/subdev_id.h
>> @@ -0,0 +1,39 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>> + *
>> + * Authors:
>> + * Cheng Zhen <[email protected]>
>> + */
>> +
>> +#ifndef _XRT_SUBDEV_ID_H_
>> +#define _XRT_SUBDEV_ID_H_
>> +
>> +/*
>> + * Every subdev driver has an ID for others to refer to it. There can be multiple number of
>> + * instances of a subdev driver. A <subdev_id, subdev_instance> tuple is a unique identification
>> + * of a specific instance of a subdev driver.
>> + */
>> +enum xrt_subdev_id {
>> + XRT_SUBDEV_INVALID = 0,
>> + XRT_SUBDEV_GRP,
>> + XRT_SUBDEV_VSEC,
>> + XRT_SUBDEV_VSEC_GOLDEN,
>> + XRT_SUBDEV_DEVCTL,
>> + XRT_SUBDEV_AXIGATE,
>> + XRT_SUBDEV_ICAP,
>> + XRT_SUBDEV_TEST,
>> + XRT_SUBDEV_MGMT_MAIN,
>> + XRT_SUBDEV_QSPI,
>> + XRT_SUBDEV_MAILBOX,
>> + XRT_SUBDEV_CMC,
>> + XRT_SUBDEV_CALIB,
>> + XRT_SUBDEV_CLKFREQ,
>> + XRT_SUBDEV_CLOCK,
>> + XRT_SUBDEV_SRSR,
>> + XRT_SUBDEV_UCS,
>> + XRT_SUBDEV_NUM, /* Total number of subdevs. */
>> + XRT_ROOT = -1, /* Special ID for root driver. */
>> +};
>> +
>> +#endif /* _XRT_SUBDEV_ID_H_ */
>> diff --git a/drivers/fpga/xrt/include/xdevice.h b/drivers/fpga/xrt/include/xdevice.h
>> new file mode 100644
>> index 000000000000..b40ebe98b54d
>> --- /dev/null
>> +++ b/drivers/fpga/xrt/include/xdevice.h
>> @@ -0,0 +1,141 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>> + *
>> + * Authors:
>> + * Lizhi Hou <[email protected]>
>> + */
>> +
>> +#ifndef _XRT_DEVICE_H_
>> +#define _XRT_DEVICE_H_
>> +
>> +#include <linux/fs.h>
>> +#include <linux/cdev.h>
>> +
>> +#define XRT_MAX_DEVICE_NODES 128
>> +#define XRT_INVALID_DEVICE_INST (XRT_MAX_DEVICE_NODES + 1)
>> +
>> +enum {
>> + XRT_DEVICE_STATE_NONE = 0,
>> + XRT_DEVICE_STATE_ADDED
>> +};
>> +
>> +/*
>> + * struct xrt_device - represent an xrt device on xrt bus
>> + *
>> + * dev: generic device interface.
>> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
>> + * name: name of the xrt device.
>> + * instance: instance of the xrt device. The xrt device with same id can have
>> + * more than 1 instances.
>> + * state: current state of the xrt device.
>> + * num_resources: The total number of resource for the xrt device.
>> + * resource: point to the xrt device resource array.
>> + * sdev_data: private data pointer.
>> + */
>> +struct xrt_device {
>> + struct device dev;
>> + u32 subdev_id;
>> + const char *name;
>> + u32 instance;
>> + u32 state;
>> + u32 num_resources;
>> + struct resource *resource;
>> + void *sdev_data;
>> +};
>> +
>> +/*
>> + * If populated by xrt device driver, infra will handle the mechanics of
>> + * char device (un)registration.
>> + */
>> +enum xrt_dev_file_mode {
>> + /* Infra create cdev, default file name */
>> + XRT_DEV_FILE_DEFAULT = 0,
>> + /* Infra create cdev, need to encode inst num in file name */
>> + XRT_DEV_FILE_MULTI_INST,
>> + /* No auto creation of cdev by infra, leaf handles it by itself */
>> + XRT_DEV_FILE_NO_AUTO,
>> +};
>> +
>> +struct xrt_dev_file_ops {
>> + const struct file_operations xsf_ops;
>> + dev_t xsf_dev_t;
>> + const char *xsf_dev_name;
>> + enum xrt_dev_file_mode xsf_mode;
>> +};
>> +
>> +/*
>> + * this struct define the endpoints belong to the same xrt device
>> + * ep_name: endpoint name
>> + * compat: compatible string
>> + */
>> +struct xrt_dev_ep_names {
>> + const char *ep_name;
>> + const char *compat;
>> +};
>> +
>> +struct xrt_dev_endpoints {
>> + struct xrt_dev_ep_names *xse_names;
>> + /* minimum number of endpoints to support the subdevice */
>> + u32 xse_min_ep;
> Why some device must have several DT nodes?
>
>> +};
>> +
>> +/*
>> + * struct xrt_driver - represent a xrt device driver
>> + *
>> + * driver: driver model structure.
>> + * subdev_id: id of the xrt device. See enum xrt_subdev_id.
>> + * file_ops: character device name and callbacks.
>> + * probe: mandatory callback for device binding.
>> + * remove: callback for device unbinding.
>> + * leaf_call: callback for servicing other leaf drivers.
>> + */
>> +struct xrt_driver {
>> + struct device_driver driver;
>> + u32 subdev_id;
>> + struct xrt_dev_file_ops file_ops;
>> + struct xrt_dev_endpoints *endpoints;
> No DOC for this field.
>
> Seems this is to list the device types the driver supports, but usually
> this is done by an id_table in driver.
>
>> +
>> + /*
>> + * Subdev driver callbacks populated by subdev driver.
>> + */
>> + int (*probe)(struct xrt_device *xrt_dev);
>> + void (*remove)(struct xrt_device *xrt_dev);
>> + /*
>> + * If leaf_call is defined, these are called by other leaf drivers.
>> + * Note that root driver may call into leaf_call of a group driver.
>> + */
>> + int (*leaf_call)(struct xrt_device *xrt_dev, u32 cmd, void *arg);
> Why this is needed? If a function of a driver module needs to be called
> by other modules, just export the function.
>
>> +};
>> +
>> +#define to_xrt_dev(d) container_of(d, struct xrt_device, dev)
>> +#define to_xrt_drv(d) container_of(d, struct xrt_driver, driver)
>> +
>> +static inline void *xrt_get_drvdata(const struct xrt_device *xdev)
>> +{
>> + return dev_get_drvdata(&xdev->dev);
>> +}
>> +
>> +static inline void xrt_set_drvdata(struct xrt_device *xdev, void *data)
>> +{
>> + dev_set_drvdata(&xdev->dev, data);
>> +}
>> +
>> +static inline void *xrt_get_xdev_data(struct device *dev)
>> +{
>> + struct xrt_device *xdev = to_xrt_dev(dev);
>> +
>> + return xdev->sdev_data;
>> +}
>> +
>> +struct xrt_device *
>> +xrt_device_register(struct device *parent, u32 id,
>> + struct resource *res, u32 res_num,
>> + void *pdata, size_t data_sz);
>> +void xrt_device_unregister(struct xrt_device *xdev);
>> +int xrt_register_driver(struct xrt_driver *drv);
>> +void xrt_unregister_driver(struct xrt_driver *drv);
>> +void *xrt_get_xdev_data(struct device *dev);
>> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32 type, u32 num);
>> +
>> +#endif /* _XRT_DEVICE_H_ */
>> diff --git a/drivers/fpga/xrt/include/xleaf.h b/drivers/fpga/xrt/include/xleaf.h
>> new file mode 100644
>> index 000000000000..f065fc766e0f
>> --- /dev/null
>> +++ b/drivers/fpga/xrt/include/xleaf.h
>> @@ -0,0 +1,205 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>> + *
>> + * Authors:
>> + * Cheng Zhen <[email protected]>
>> + * Sonal Santan <[email protected]>
>> + */
>> +
>> +#ifndef _XRT_XLEAF_H_
>> +#define _XRT_XLEAF_H_
>> +
>> +#include <linux/mod_devicetable.h>
>> +#include "xdevice.h"
>> +#include "subdev_id.h"
>> +#include "xroot.h"
>> +#include "events.h"
>> +
>> +/* All subdev drivers should use below common routines to print out msg. */
>> +#define DEV(xdev) (&(xdev)->dev)
>> +#define DEV_PDATA(xdev) \
>> + ((struct xrt_subdev_platdata *)xrt_get_xdev_data(DEV(xdev)))
>> +#define DEV_FILE_OPS(xdev) \
>> + (&(to_xrt_drv((xdev)->dev.driver))->file_ops)
>> +#define FMT_PRT(prt_fn, xdev, fmt, args...) \
>> + ({typeof(xdev) (_xdev) = (xdev); \
>> + prt_fn(DEV(_xdev), "%s %s: " fmt, \
>> + DEV_PDATA(_xdev)->xsp_root_name, __func__, ##args); })
>> +#define xrt_err(xdev, fmt, args...) FMT_PRT(dev_err, xdev, fmt, ##args)
>> +#define xrt_warn(xdev, fmt, args...) FMT_PRT(dev_warn, xdev, fmt, ##args)
>> +#define xrt_info(xdev, fmt, args...) FMT_PRT(dev_info, xdev, fmt, ##args)
>> +#define xrt_dbg(xdev, fmt, args...) FMT_PRT(dev_dbg, xdev, fmt, ##args)
>> +
>> +#define XRT_DEFINE_REGMAP_CONFIG(config_name) \
>> + static const struct regmap_config config_name = { \
>> + .reg_bits = 32, \
>> + .val_bits = 32, \
>> + .reg_stride = 4, \
>> + .max_register = 0x1000, \
>> + }
>> +
>> +enum {
>> + /* Starting cmd for common leaf cmd implemented by all leaves. */
>> + XRT_XLEAF_COMMON_BASE = 0,
>> + /* Starting cmd for leaves' specific leaf cmds. */
>> + XRT_XLEAF_CUSTOM_BASE = 64,
>> +};
>> +
>> +enum xrt_xleaf_common_leaf_cmd {
>> + XRT_XLEAF_EVENT = XRT_XLEAF_COMMON_BASE,
>> +};
>> +
>> +/*
>> + * Partially initialized by the parent driver, then, passed in as subdev driver's
>> + * platform data when creating subdev driver instance by calling platform
>> + * device register API (xrt_device_register_data() or the likes).
> Didn't find the xrt_device_register_data, should be xrt_device_register?
>
>> + *
>> + * Once device register API returns, platform driver framework makes a copy of
>> + * this buffer and maintains its life cycle. The content of the buffer is
>> + * completely owned by subdev driver.
> Generally the platform data contains some specific description of the device, so
> it should also be managered by the parent same as the device. The driver
> owned data should be in dev.driver_data
>
>> + *
>> + * Thus, parent driver should be very careful when it touches this buffer
>> + * again once it's handed over to subdev driver. And the data structure
>> + * should not contain pointers pointing to buffers that is managed by
>> + * other or parent drivers since it could have been freed before platform
>> + * data buffer is freed by platform driver framework.
>> + */
>> +struct xrt_subdev_platdata {
>> + /*
>> + * Per driver instance callback. The xdev points to the instance.
>> + * Should always be defined for subdev driver to get service from root.
>> + */
>> + xrt_subdev_root_cb_t xsp_root_cb;
>> + void *xsp_root_cb_arg;
>> +
>> + /* Something to associate w/ root for msg printing. */
>> + const char *xsp_root_name;
>> +
>> + /*
>> + * Char dev support for this subdev instance.
>> + * Initialized by subdev driver.
>> + */
>> + struct cdev xsp_cdev;
>> + struct device *xsp_sysdev;
>> + struct mutex xsp_devnode_lock; /* devnode lock */
>> + struct completion xsp_devnode_comp;
>> + int xsp_devnode_ref;
>> + bool xsp_devnode_online;
>> + bool xsp_devnode_excl;
>> +
>> + /*
>> + * Subdev driver specific init data. The buffer should be embedded
>> + * in this data structure buffer after dtb, so that it can be freed
>> + * together with platform data.
>> + */
>> + loff_t xsp_priv_off; /* Offset into this platform data buffer. */
>> + size_t xsp_priv_len;
> Didn't see the usage.
>
>> +
>> + /*
>> + * Populated by parent driver to describe the device tree for
>> + * the subdev driver to handle. Should always be last one since it's
>> + * of variable length.
>> + */
>> + bool xsp_dtb_valid;
>> + char xsp_dtb[0];
>> +};
>> +
>> +struct subdev_match_arg {
>> + enum xrt_subdev_id id;
>> + int instance;
>> +};
>> +
>> +bool xleaf_has_endpoint(struct xrt_device *xdev, const char *endpoint_name);
>> +struct xrt_device *xleaf_get_leaf(struct xrt_device *xdev,
>> + xrt_subdev_match_t cb, void *arg);
>> +
>> +static inline bool subdev_match(enum xrt_subdev_id id, struct xrt_device *xdev, void *arg)
>> +{
>> + const struct subdev_match_arg *a = (struct subdev_match_arg *)arg;
>> + int instance = a->instance;
>> +
>> + if (id != a->id)
>> + return false;
>> + if (instance != xdev->instance && instance != XRT_INVALID_DEVICE_INST)
>> + return false;
>> + return true;
>> +}
>> +
>> +static inline bool xrt_subdev_match_epname(enum xrt_subdev_id id,
>> + struct xrt_device *xdev, void *arg)
>> +{
>> + return xleaf_has_endpoint(xdev, arg);
>> +}
>> +
>> +static inline struct xrt_device *
>> +xleaf_get_leaf_by_id(struct xrt_device *xdev,
>> + enum xrt_subdev_id id, int instance)
>> +{
>> + struct subdev_match_arg arg = { id, instance };
>> +
>> + return xleaf_get_leaf(xdev, subdev_match, &arg);
>> +}
>> +
>> +static inline struct xrt_device *
>> +xleaf_get_leaf_by_epname(struct xrt_device *xdev, const char *name)
>> +{
>> + return xleaf_get_leaf(xdev, xrt_subdev_match_epname, (void *)name);
>> +}
>> +
>> +static inline int xleaf_call(struct xrt_device *tgt, u32 cmd, void *arg)
>> +{
>> + return (to_xrt_drv(tgt->dev.driver)->leaf_call)(tgt, cmd, arg);
>> +}
>> +
>> +int xleaf_broadcast_event(struct xrt_device *xdev, enum xrt_events evt, bool async);
>> +int xleaf_create_group(struct xrt_device *xdev, char *dtb);
>> +int xleaf_destroy_group(struct xrt_device *xdev, int instance);
>> +void xleaf_get_root_res(struct xrt_device *xdev, u32 region_id, struct resource **res);
>> +void xleaf_get_root_id(struct xrt_device *xdev, unsigned short *vendor, unsigned short *device,
>> + unsigned short *subvendor, unsigned short *subdevice);
>> +void xleaf_hot_reset(struct xrt_device *xdev);
>> +int xleaf_put_leaf(struct xrt_device *xdev, struct xrt_device *leaf);
>> +struct device *xleaf_register_hwmon(struct xrt_device *xdev, const char *name, void *drvdata,
>> + const struct attribute_group **grps);
>> +void xleaf_unregister_hwmon(struct xrt_device *xdev, struct device *hwmon);
>> +int xleaf_wait_for_group_bringup(struct xrt_device *xdev);
>> +
>> +/*
>> + * Character device helper APIs for use by leaf drivers
>> + */
>> +static inline bool xleaf_devnode_enabled(struct xrt_device *xdev)
>> +{
>> + return DEV_FILE_OPS(xdev)->xsf_ops.open;
>> +}
>> +
>> +int xleaf_devnode_create(struct xrt_device *xdev,
>> + const char *file_name, const char *inst_name);
>> +void xleaf_devnode_destroy(struct xrt_device *xdev);
>> +
>> +struct xrt_device *xleaf_devnode_open_excl(struct inode *inode);
>> +struct xrt_device *xleaf_devnode_open(struct inode *inode);
>> +void xleaf_devnode_close(struct inode *inode);
>> +
>> +/* Module's init/fini routines for leaf driver in xrt-lib module */
>> +#define XRT_LEAF_INIT_FINI_FUNC(name) \
>> +void name##_leaf_init_fini(bool init) \
>> +{ \
>> + if (init) \
>> + xrt_register_driver(&xrt_##name##_driver); \
>> + else \
>> + xrt_unregister_driver(&xrt_##name##_driver); \
>> +}
>> +
>> +/* Module's init/fini routines for leaf driver in xrt-lib module */
>> +void group_leaf_init_fini(bool init);
>> +void vsec_leaf_init_fini(bool init);
>> +void devctl_leaf_init_fini(bool init);
>> +void axigate_leaf_init_fini(bool init);
>> +void icap_leaf_init_fini(bool init);
>> +void calib_leaf_init_fini(bool init);
>> +void clkfreq_leaf_init_fini(bool init);
>> +void clock_leaf_init_fini(bool init);
>> +void ucs_leaf_init_fini(bool init);
>> +
>> +#endif /* _XRT_LEAF_H_ */
>> diff --git a/drivers/fpga/xrt/include/xleaf/clkfreq.h b/drivers/fpga/xrt/include/xleaf/clkfreq.h
>> new file mode 100644
>> index 000000000000..005441d5df78
>> --- /dev/null
>> +++ b/drivers/fpga/xrt/include/xleaf/clkfreq.h
>> @@ -0,0 +1,21 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>> + *
>> + * Authors:
>> + * Lizhi Hou <[email protected]>
>> + */
>> +
>> +#ifndef _XRT_CLKFREQ_H_
>> +#define _XRT_CLKFREQ_H_
>> +
>> +#include "xleaf.h"
>> +
>> +/*
>> + * CLKFREQ driver leaf calls.
>> + */
>> +enum xrt_clkfreq_leaf_cmd {
>> + XRT_CLKFREQ_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
>> +};
>> +
>> +#endif /* _XRT_CLKFREQ_H_ */
>> diff --git a/drivers/fpga/xrt/include/xleaf/clock.h b/drivers/fpga/xrt/include/xleaf/clock.h
>> new file mode 100644
>> index 000000000000..1379e24fa5d0
>> --- /dev/null
>> +++ b/drivers/fpga/xrt/include/xleaf/clock.h
>> @@ -0,0 +1,29 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>> + *
>> + * Authors:
>> + * Lizhi Hou <[email protected]>
>> + */
>> +
>> +#ifndef _XRT_CLOCK_H_
>> +#define _XRT_CLOCK_H_
>> +
>> +#include "xleaf.h"
>> +#include <linux/fpga_xrt.h>
>> +
>> +/*
>> + * CLOCK driver leaf calls.
>> + */
>> +enum xrt_clock_leaf_cmd {
>> + XRT_CLOCK_SET = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
>> + XRT_CLOCK_GET,
>> + XRT_CLOCK_VERIFY,
>> +};
>> +
>> +struct xrt_clock_get {
>> + u16 freq;
>> + u32 freq_cnter;
>> +};
>> +
>> +#endif /* _XRT_CLOCK_H_ */
>> diff --git a/drivers/fpga/xrt/include/xleaf/ddr_calibration.h b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>> new file mode 100644
>> index 000000000000..c44ae30f939a
>> --- /dev/null
>> +++ b/drivers/fpga/xrt/include/xleaf/ddr_calibration.h
>> @@ -0,0 +1,28 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>> + *
>> + * Authors:
>> + * Cheng Zhen <[email protected]>
>> + */
>> +
>> +#ifndef _XRT_DDR_CALIBRATION_H_
>> +#define _XRT_DDR_CALIBRATION_H_
>> +
>> +#include "xleaf.h"
>> +#include <linux/fpga-xrt.h>
>> +
>> +/*
>> + * Memory calibration driver leaf calls.
>> + */
>> +enum xrt_calib_results {
>> + XRT_CALIB_UNKNOWN = 0,
>> + XRT_CALIB_SUCCEEDED,
>> + XRT_CALIB_FAILED,
>> +};
>> +
>> +enum xrt_calib_leaf_cmd {
>> + XRT_CALIB_RESULT = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
>> +};
>> +
>> +#endif /* _XRT_DDR_CALIBRATION_H_ */
>> diff --git a/drivers/fpga/xrt/include/xleaf/devctl.h b/drivers/fpga/xrt/include/xleaf/devctl.h
>> new file mode 100644
>> index 000000000000..b97f3b6d9326
>> --- /dev/null
>> +++ b/drivers/fpga/xrt/include/xleaf/devctl.h
>> @@ -0,0 +1,40 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>> + *
>> + * Authors:
>> + * Lizhi Hou <[email protected]>
>> + */
>> +
>> +#ifndef _XRT_DEVCTL_H_
>> +#define _XRT_DEVCTL_H_
>> +
>> +#include "xleaf.h"
>> +
>> +/*
>> + * DEVCTL driver leaf calls.
>> + */
>> +enum xrt_devctl_leaf_cmd {
>> + XRT_DEVCTL_READ = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
>> +};
>> +
>> +enum xrt_devctl_id {
>> + XRT_DEVCTL_ROM_UUID = 0,
>> + XRT_DEVCTL_DDR_CALIB,
>> + XRT_DEVCTL_GOLDEN_VER,
>> + XRT_DEVCTL_MAX
>> +};
>> +
>> +struct xrt_devctl_rw {
>> + u32 xdr_id;
>> + void *xdr_buf;
>> + u32 xdr_len;
>> + u32 xdr_offset;
>> +};
>> +
>> +struct xrt_devctl_intf_uuid {
>> + u32 uuid_num;
>> + uuid_t *uuids;
>> +};
>> +
>> +#endif /* _XRT_DEVCTL_H_ */
>> diff --git a/drivers/fpga/xrt/lib/lib-drv.c b/drivers/fpga/xrt/lib/lib-drv.c
>> new file mode 100644
>> index 000000000000..7d21ef565651
>> --- /dev/null
>> +++ b/drivers/fpga/xrt/lib/lib-drv.c
>> @@ -0,0 +1,318 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>> + *
>> + * Authors:
>> + * Cheng Zhen <[email protected]>
>> + * Lizhi Hou <[email protected]>
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/vmalloc.h>
>> +#include <linux/slab.h>
>> +#include "xleaf.h"
>> +#include "xroot.h"
>> +#include "lib-drv.h"
>> +
>> +#define XRT_IPLIB_MODULE_NAME "xrt-lib"
>> +#define XRT_DRVNAME(drv) ((drv)->driver.name)
>> +
>> +#define XRT_SUBDEV_ID_SHIFT 16
>> +#define XRT_SUBDEV_ID_MASK ((1 << XRT_SUBDEV_ID_SHIFT) - 1)
>> +
>> +struct xrt_find_drv_data {
>> + enum xrt_subdev_id id;
>> + struct xrt_driver *xdrv;
>> +};
>> +
>> +struct class *xrt_class;
>> +static DEFINE_IDA(xrt_device_ida);
>> +
>> +static inline u32 xrt_instance_to_id(enum xrt_subdev_id id, u32 instance)
>> +{
>> + return (id << XRT_SUBDEV_ID_SHIFT) | instance;
>> +}
>> +
>> +static inline u32 xrt_id_to_instance(u32 id)
>> +{
>> + return id & XRT_SUBDEV_ID_MASK;
>> +}
>> +
>> +static int xrt_bus_match(struct device *dev, struct device_driver *drv)
>> +{
>> + struct xrt_device *xdev = to_xrt_dev(dev);
>> + struct xrt_driver *xdrv = to_xrt_drv(drv);
>> +
>> + if (xdev->subdev_id == xdrv->subdev_id)
>> + return 1;
>> +
>> + return 0;
>> +}
>> +
>> +static int xrt_bus_probe(struct device *dev)
>> +{
>> + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
>> + struct xrt_device *xdev = to_xrt_dev(dev);
>> +
>> + return xdrv->probe(xdev);
>> +}
>> +
>> +static int xrt_bus_remove(struct device *dev)
>> +{
>> + struct xrt_driver *xdrv = to_xrt_drv(dev->driver);
>> + struct xrt_device *xdev = to_xrt_dev(dev);
>> +
>> + if (xdrv->remove)
>> + xdrv->remove(xdev);
>> +
>> + return 0;
>> +}
>> +
>> +struct bus_type xrt_bus_type = {
>> + .name = "xrt",
>> + .match = xrt_bus_match,
>> + .probe = xrt_bus_probe,
>> + .remove = xrt_bus_remove,
>> +};
> Actually I'm not sure a new bus type is needed. The bus itself is a
> virtual bus and the device identifier (xdev.subdev_id) for the bus
> is SW defined, then why don't we just use the name string matching and
> create platform devices for each leaf?
>
>> +
>> +int xrt_register_driver(struct xrt_driver *drv)
>> +{
>> + const char *drvname = XRT_DRVNAME(drv);
>> + int rc = 0;
>> +
>> + /* Initialize dev_t for char dev node. */
>> + if (drv->file_ops.xsf_ops.open) {
>> + rc = alloc_chrdev_region(&drv->file_ops.xsf_dev_t, 0,
>> + XRT_MAX_DEVICE_NODES, drvname);
> I think no need to help alloc char majors for each driver here. The
> function is for driver registration, the char dev is for the actual driver
> functionality and had better be managered in its own module.
>
> I only see the xmgmt_main module needs the char dev, so maybe we only do
> it in xmgmt_main init function.
>
>> + if (rc) {
>> + pr_err("failed to alloc dev minor for %s: %d\n", drvname, rc);
>> + return rc;
>> + }
>> + } else {
>> + drv->file_ops.xsf_dev_t = (dev_t)-1;
>> + }
>> +
>> + drv->driver.owner = THIS_MODULE;
>> + drv->driver.bus = &xrt_bus_type;
>> +
>> + rc = driver_register(&drv->driver);
>> + if (rc) {
>> + pr_err("register %s xrt driver failed\n", drvname);
>> + if (drv->file_ops.xsf_dev_t != (dev_t)-1) {
>> + unregister_chrdev_region(drv->file_ops.xsf_dev_t,
>> + XRT_MAX_DEVICE_NODES);
>> + }
>> + return rc;
>> + }
>> +
>> + pr_info("%s registered successfully\n", drvname);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(xrt_register_driver);
>> +
>> +void xrt_unregister_driver(struct xrt_driver *drv)
>> +{
>> + driver_unregister(&drv->driver);
>> +
>> + if (drv->file_ops.xsf_dev_t != (dev_t)-1)
>> + unregister_chrdev_region(drv->file_ops.xsf_dev_t, XRT_MAX_DEVICE_NODES);
>> +}
>> +EXPORT_SYMBOL_GPL(xrt_unregister_driver);
>> +
>> +static int __find_driver(struct device_driver *drv, void *_data)
>> +{
>> + struct xrt_driver *xdrv = to_xrt_drv(drv);
>> + struct xrt_find_drv_data *data = _data;
>> +
>> + if (xdrv->subdev_id == data->id) {
>> + data->xdrv = xdrv;
>> + return 1;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +const char *xrt_drv_name(enum xrt_subdev_id id)
>> +{
>> + struct xrt_find_drv_data data = { 0 };
>> +
>> + data.id = id;
>> + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
>> +
>> + if (data.xdrv)
>> + return XRT_DRVNAME(data.xdrv);
>> +
>> + return NULL;
>> +}
>> +
>> +static int xrt_drv_get_instance(enum xrt_subdev_id id)
>> +{
>> + int ret;
>> +
>> + ret = ida_alloc_range(&xrt_device_ida, xrt_instance_to_id(id, 0),
>> + xrt_instance_to_id(id, XRT_MAX_DEVICE_NODES),
>> + GFP_KERNEL);
>> + if (ret < 0)
>> + return ret;
>> +
>> + return xrt_id_to_instance((u32)ret);
>> +}
>> +
>> +static void xrt_drv_put_instance(enum xrt_subdev_id id, int instance)
>> +{
>> + ida_free(&xrt_device_ida, xrt_instance_to_id(id, instance));
>> +}
>> +
>> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id id)
>> +{
>> + struct xrt_find_drv_data data = { 0 };
>> +
>> + data.id = id;
>> + bus_for_each_drv(&xrt_bus_type, NULL, &data, __find_driver);
>> +
>> + if (data.xdrv)
>> + return data.xdrv->endpoints;
>> +
>> + return NULL;
>> +}
>> +
>> +static void xrt_device_release(struct device *dev)
>> +{
>> + struct xrt_device *xdev = container_of(dev, struct xrt_device, dev);
>> +
>> + kfree(xdev);
>> +}
>> +
>> +void xrt_device_unregister(struct xrt_device *xdev)
>> +{
>> + if (xdev->state == XRT_DEVICE_STATE_ADDED)
>> + device_del(&xdev->dev);
>> +
>> + vfree(xdev->sdev_data);
>> + kfree(xdev->resource);
>> +
>> + if (xdev->instance != XRT_INVALID_DEVICE_INST)
>> + xrt_drv_put_instance(xdev->subdev_id, xdev->instance);
> I suggest all the free work be done in dev.release. The xrt_device may
> still be accessible after this function, these resources may still be
> accessed too.
>
>> +
>> + if (xdev->dev.release == xrt_device_release)
>> + put_device(&xdev->dev);
> It is OK but unnormal, I suggest you don't integrate the error handling
> of xrt_device_register here. That would make the code easier to read.
>
>> +}
>> +
>> +struct xrt_device *
>> +xrt_device_register(struct device *parent, u32 id,
>> + struct resource *res, u32 res_num,
>> + void *pdata, size_t data_sz)
>> +{
>> + struct xrt_device *xdev = NULL;
>> + int ret;
>> +
>> + xdev = kzalloc(sizeof(*xdev), GFP_KERNEL);
>> + if (!xdev)
>> + return xdev;
>> + xdev->instance = XRT_INVALID_DEVICE_INST;
>> +
>> + /* Obtain dev instance number. */
>> + ret = xrt_drv_get_instance(id);
>> + if (ret < 0) {
>> + dev_err(parent, "failed get instance, ret %d", ret);
>> + goto fail;
>> + }
>> +
>> + xdev->instance = ret;
>> + xdev->name = xrt_drv_name(id);
> What if the xrt_driver is not loaded yet?
>
> Normally the device represents the HW devices in system, it could be
> enumerated and created no matter whether its device driver exists. So I
> think some code in this patchset is not reasonable, that creates
> xrt_device when the corresponding xrt_driver exists.
>
>> + xdev->subdev_id = id;
>> + device_initialize(&xdev->dev);
>> + xdev->dev.release = xrt_device_release;
>> + xdev->dev.parent = parent;
>> +
>> + xdev->dev.bus = &xrt_bus_type;
>> + dev_set_name(&xdev->dev, "%s.%d", xdev->name, xdev->instance);
>> +
>> + xdev->num_resources = res_num;
>> + xdev->resource = kmemdup(res, sizeof(*res) * res_num, GFP_KERNEL);
>> + if (!xdev->resource)
>> + goto fail;
>> +
>> + xdev->sdev_data = vzalloc(data_sz);
> vmalloc the platform_data is quite unnormal, the concern is the
> performance. Maybe you should split the structures and only vmalloc
> for big data chunk.
>
>> + if (!xdev->sdev_data)
>> + goto fail;
>> +
>> + memcpy(xdev->sdev_data, pdata, data_sz);
> As I can see, you need to cut and copy the selected dtb data in
> platform_data for each dev. But why? Why not put the whole dtb somewhere
> and let the driver access it as needed?
>
>> +
>> + ret = device_add(&xdev->dev);
>> + if (ret) {
>> + dev_err(parent, "failed add device, ret %d", ret);
>> + goto fail;
>> + }
>> + xdev->state = XRT_DEVICE_STATE_ADDED;
>> +
>> + return xdev;
>> +
>> +fail:
>> + xrt_device_unregister(xdev);
>> + kfree(xdev);
>> +
>> + return NULL;
>> +}
>> +
>> +struct resource *xrt_get_resource(struct xrt_device *xdev, u32 type, u32 num)
>> +{
>> + u32 i;
>> +
>> + for (i = 0; i < xdev->num_resources; i++) {
>> + struct resource *r = &xdev->resource[i];
>> +
>> + if (type == resource_type(r) && num-- == 0)
>> + return r;
>> + }
>> + return NULL;
>> +}
>> +
>> +/*
>> + * Leaf driver's module init/fini callbacks. This is not a open infrastructure for dynamic
>> + * plugging in drivers. All drivers should be statically added.
> That's not the normal way, moreover, as a FPGA, users may integrate all
> kinds of IP blocks whose drivers are not belong to FPGA domain. Put them
> all in FPGA folder is not a good idea.
>
> Thanks,
> Yilun
>
>> + */
>> +static void (*leaf_init_fini_cbs[])(bool) = {
>> + group_leaf_init_fini,
>> + axigate_leaf_init_fini,
>> + icap_leaf_init_fini,
>> +};
>> +
>> +static __init int xrt_lib_init(void)
>> +{
>> + int ret;
>> + int i;
>> +
>> + ret = bus_register(&xrt_bus_type);
>> + if (ret)
>> + return ret;
>> +
>> + xrt_class = class_create(THIS_MODULE, XRT_IPLIB_MODULE_NAME);
>> + if (IS_ERR(xrt_class)) {
>> + bus_unregister(&xrt_bus_type);
>> + return PTR_ERR(xrt_class);
>> + }
>> +
>> + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
>> + leaf_init_fini_cbs[i](true);
>> + return 0;
>> +}
>> +
>> +static __exit void xrt_lib_fini(void)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
>> + leaf_init_fini_cbs[i](false);
>> +
>> + class_destroy(xrt_class);
>> + bus_unregister(&xrt_bus_type);
>> +}
>> +
>> +module_init(xrt_lib_init);
>> +module_exit(xrt_lib_fini);
>> +
>> +MODULE_AUTHOR("XRT Team <[email protected]>");
>> +MODULE_DESCRIPTION("Xilinx Alveo IP Lib driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/fpga/xrt/lib/lib-drv.h b/drivers/fpga/xrt/lib/lib-drv.h
>> new file mode 100644
>> index 000000000000..0276c28e009f
>> --- /dev/null
>> +++ b/drivers/fpga/xrt/lib/lib-drv.h
>> @@ -0,0 +1,21 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2020-2021 Xilinx, Inc.
>> + *
>> + * Authors:
>> + * Cheng Zhen <[email protected]>
>> + */
>> +
>> +#ifndef _LIB_DRV_H_
>> +#define _LIB_DRV_H_
>> +
>> +#include <linux/device/class.h>
>> +#include <linux/device/bus.h>
>> +
>> +extern struct class *xrt_class;
>> +extern struct bus_type xrt_bus_type;
>> +
>> +const char *xrt_drv_name(enum xrt_subdev_id id);
>> +struct xrt_dev_endpoints *xrt_drv_get_endpoints(enum xrt_subdev_id id);
>> +
>> +#endif /* _LIB_DRV_H_ */
>> --
>> 2.27.0