1106 lines
32 KiB
Diff
1106 lines
32 KiB
Diff
diff --git a/xilinx-configfs-overlays.patch b/xilinx-configfs-overlays.patch
|
|
new file mode 100644
|
|
index 0000000..53c5349
|
|
--- /dev/null
|
|
+++ b/xilinx-configfs-overlays.patch
|
|
@@ -0,0 +1,387 @@
|
|
+# Add the Device Tree Overlay ConfigFS interface for PL programming
|
|
+# diff from Xilinx/linux-xilinx/tree/xlnx_rebase_v6.6_LTS commit ID: 7e42c87
|
|
+# See: https://github.com/Xilinx/linux-xlnx/commit/7e42c87c489b702795794e480fb5483897ff9e91
|
|
+
|
|
+diff --git a/Documentation/devicetree/configfs-overlays.txt b/Documentation/devicetree/configfs-overlays.txt
|
|
+new file mode 100644
|
|
+index 00000000000000..5fa43e0643072c
|
|
+--- /dev/null
|
|
++++ b/Documentation/devicetree/configfs-overlays.txt
|
|
+@@ -0,0 +1,31 @@
|
|
++Howto use the configfs overlay interface.
|
|
++
|
|
++A device-tree configfs entry is created in /config/device-tree/overlays
|
|
++and and it is manipulated using standard file system I/O.
|
|
++Note that this is a debug level interface, for use by developers and
|
|
++not necessarily something accessed by normal users due to the
|
|
++security implications of having direct access to the kernel's device tree.
|
|
++
|
|
++* To create an overlay you mkdir the directory:
|
|
++
|
|
++ # mkdir /config/device-tree/overlays/foo
|
|
++
|
|
++* Either you echo the overlay firmware file to the path property file.
|
|
++
|
|
++ # echo foo.dtbo >/config/device-tree/overlays/foo/path
|
|
++
|
|
++* Or you cat the contents of the overlay to the dtbo file
|
|
++
|
|
++ # cat foo.dtbo >/config/device-tree/overlays/foo/dtbo
|
|
++
|
|
++The overlay file will be applied, and devices will be created/destroyed
|
|
++as required.
|
|
++
|
|
++To remove it simply rmdir the directory.
|
|
++
|
|
++ # rmdir /config/device-tree/overlays/foo
|
|
++
|
|
++The rationalle of the dual interface (firmware & direct copy) is that each is
|
|
++better suited to different use patterns. The firmware interface is what's
|
|
++intended to be used by hardware managers in the kernel, while the copy interface
|
|
++make sense for developers (since it avoids problems with namespaces).
|
|
+diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
|
|
+index da9826accb1b5d..4c7af532589d0c 100644
|
|
+--- a/drivers/of/Kconfig
|
|
++++ b/drivers/of/Kconfig
|
|
+@@ -102,4 +102,15 @@ config OF_OVERLAY
|
|
+ config OF_NUMA
|
|
+ bool
|
|
+
|
|
++config OF_CONFIGFS
|
|
++ bool "Device Tree Overlay ConfigFS interface"
|
|
++ select CONFIGFS_FS
|
|
++ depends on OF_OVERLAY
|
|
++ help
|
|
++ Select this option to enable simple user-space driven DT overlay
|
|
++ interface to support device tree manipulated at runtime.
|
|
++ Say Y here to include this support.
|
|
++
|
|
++ If unsure, say N.
|
|
++
|
|
+ endif # OF
|
|
+diff --git a/drivers/of/Makefile b/drivers/of/Makefile
|
|
+index eff624854575c5..61bd05f08ca1b8 100644
|
|
+--- a/drivers/of/Makefile
|
|
++++ b/drivers/of/Makefile
|
|
+@@ -1,6 +1,7 @@
|
|
+ # SPDX-License-Identifier: GPL-2.0
|
|
+ obj-y = base.o cpu.o device.o module.o platform.o property.o
|
|
+ obj-$(CONFIG_OF_KOBJ) += kobj.o
|
|
++obj-$(CONFIG_OF_CONFIGFS) += configfs.o
|
|
+ obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
|
|
+ obj-$(CONFIG_OF_FLATTREE) += fdt.o
|
|
+ obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
|
|
+diff --git a/drivers/of/configfs.c b/drivers/of/configfs.c
|
|
+new file mode 100644
|
|
+index 00000000000000..3839f14dc8938e
|
|
+--- /dev/null
|
|
++++ b/drivers/of/configfs.c
|
|
+@@ -0,0 +1,294 @@
|
|
++// SPDX-License-Identifier: GPL-2.0
|
|
++/*
|
|
++ * Configfs entries for device-tree
|
|
++ *
|
|
++ * Copyright (C) 2013 - Pantelis Antoniou <panto@antoniou-consulting.com>
|
|
++ *
|
|
++ * This program is free software; you can redistribute it and/or
|
|
++ * modify it under the terms of the GNU General Public License
|
|
++ * as published by the Free Software Foundation; either version
|
|
++ * 2 of the License, or (at your option) any later version.
|
|
++ */
|
|
++#include <linux/ctype.h>
|
|
++#include <linux/cpu.h>
|
|
++#include <linux/module.h>
|
|
++#include <linux/of.h>
|
|
++#include <linux/of_fdt.h>
|
|
++#include <linux/spinlock.h>
|
|
++#include <linux/sizes.h>
|
|
++#include <linux/slab.h>
|
|
++#include <linux/proc_fs.h>
|
|
++#include <linux/configfs.h>
|
|
++#include <linux/types.h>
|
|
++#include <linux/stat.h>
|
|
++#include <linux/limits.h>
|
|
++#include <linux/file.h>
|
|
++#include <linux/vmalloc.h>
|
|
++#include <linux/firmware.h>
|
|
++
|
|
++#include "of_private.h"
|
|
++
|
|
++struct cfs_overlay_item {
|
|
++ struct config_item item;
|
|
++
|
|
++ char path[PATH_MAX];
|
|
++
|
|
++ const struct firmware *fw;
|
|
++ struct device_node *overlay;
|
|
++ int ov_id;
|
|
++
|
|
++ void *dtbo;
|
|
++ int dtbo_size;
|
|
++
|
|
++ void *mem;
|
|
++};
|
|
++
|
|
++static DEFINE_MUTEX(overlay_lock);
|
|
++
|
|
++static int create_overlay(struct cfs_overlay_item *overlay, void *blob)
|
|
++{
|
|
++ int err;
|
|
++
|
|
++ /* FIXME */
|
|
++ err = of_overlay_fdt_apply(blob, overlay->dtbo_size, &overlay->ov_id, NULL);
|
|
++ if (err < 0) {
|
|
++ pr_err("%s: Failed to create overlay (err=%d)\n",
|
|
++ __func__, err);
|
|
++ return err;
|
|
++ }
|
|
++
|
|
++ return err;
|
|
++}
|
|
++
|
|
++static inline struct cfs_overlay_item
|
|
++ *to_cfs_overlay_item(struct config_item *item)
|
|
++{
|
|
++ return item ? container_of(item, struct cfs_overlay_item, item) : NULL;
|
|
++}
|
|
++
|
|
++static ssize_t cfs_overlay_item_path_show(struct config_item *item, char *page)
|
|
++{
|
|
++ return sprintf(page, "%s\n", to_cfs_overlay_item(item)->path);
|
|
++}
|
|
++
|
|
++static ssize_t cfs_overlay_item_path_store(struct config_item *item,
|
|
++ const char *page, size_t count)
|
|
++{
|
|
++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
|
|
++ const char *p = page;
|
|
++ char *s;
|
|
++ int err;
|
|
++
|
|
++ /* if it's set do not allow changes */
|
|
++ if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
|
|
++ return -EPERM;
|
|
++
|
|
++ /* copy to path buffer (and make sure it's always zero terminated */
|
|
++ count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p);
|
|
++ overlay->path[sizeof(overlay->path) - 1] = '\0';
|
|
++
|
|
++ /* strip trailing newlines */
|
|
++ s = overlay->path + strlen(overlay->path);
|
|
++ while (s > overlay->path && *--s == '\n')
|
|
++ *s = '\0';
|
|
++
|
|
++ pr_debug("%s: path is '%s'\n", __func__, overlay->path);
|
|
++
|
|
++ err = request_firmware(&overlay->fw, overlay->path, NULL);
|
|
++ if (err != 0)
|
|
++ goto out_err;
|
|
++
|
|
++ overlay->dtbo_size = overlay->fw->size;
|
|
++ err = create_overlay(overlay, (void *)overlay->fw->data);
|
|
++ if (err < 0)
|
|
++ goto out_err;
|
|
++
|
|
++ return count;
|
|
++
|
|
++out_err:
|
|
++
|
|
++ release_firmware(overlay->fw);
|
|
++ overlay->fw = NULL;
|
|
++
|
|
++ overlay->path[0] = '\0';
|
|
++
|
|
++ return count;
|
|
++}
|
|
++
|
|
++static ssize_t cfs_overlay_item_status_show(struct config_item *item,
|
|
++ char *page)
|
|
++{
|
|
++ return sprintf(page, "%s\n", to_cfs_overlay_item(item)->ov_id >= 0 ?
|
|
++ "applied" : "unapplied");
|
|
++}
|
|
++
|
|
++CONFIGFS_ATTR(cfs_overlay_item_, path);
|
|
++CONFIGFS_ATTR_RO(cfs_overlay_item_, status);
|
|
++
|
|
++static struct configfs_attribute *cfs_overlay_attrs[] = {
|
|
++ &cfs_overlay_item_attr_path,
|
|
++ &cfs_overlay_item_attr_status,
|
|
++ NULL,
|
|
++};
|
|
++
|
|
++ssize_t cfs_overlay_item_dtbo_read(struct config_item *item,
|
|
++ void *buf, size_t max_count)
|
|
++{
|
|
++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
|
|
++
|
|
++ pr_debug("%s: buf=%p max_count=%zu\n", __func__, buf, max_count);
|
|
++
|
|
++ if (!overlay->dtbo)
|
|
++ return 0;
|
|
++
|
|
++ /* copy if buffer provided */
|
|
++ if (buf) {
|
|
++ /* the buffer must be large enough */
|
|
++ if (overlay->dtbo_size > max_count)
|
|
++ return -ENOSPC;
|
|
++
|
|
++ memcpy(buf, overlay->dtbo, overlay->dtbo_size);
|
|
++ }
|
|
++
|
|
++ return overlay->dtbo_size;
|
|
++}
|
|
++
|
|
++ssize_t cfs_overlay_item_dtbo_write(struct config_item *item,
|
|
++ const void *buf, size_t count)
|
|
++{
|
|
++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
|
|
++ int err;
|
|
++
|
|
++ /* if it's set do not allow changes */
|
|
++ if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
|
|
++ return -EPERM;
|
|
++
|
|
++ /* copy the contents */
|
|
++ overlay->dtbo = kmemdup(buf, count, GFP_KERNEL);
|
|
++ if (!overlay->dtbo)
|
|
++ return -ENOMEM;
|
|
++
|
|
++ overlay->dtbo_size = count;
|
|
++
|
|
++ err = create_overlay(overlay, overlay->dtbo);
|
|
++ if (err < 0)
|
|
++ goto out_err;
|
|
++
|
|
++ return count;
|
|
++
|
|
++out_err:
|
|
++ kfree(overlay->dtbo);
|
|
++ overlay->dtbo = NULL;
|
|
++ overlay->dtbo_size = 0;
|
|
++
|
|
++ return err;
|
|
++}
|
|
++
|
|
++CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M);
|
|
++
|
|
++static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = {
|
|
++ &cfs_overlay_item_attr_dtbo,
|
|
++ NULL,
|
|
++};
|
|
++
|
|
++static void cfs_overlay_release(struct config_item *item)
|
|
++{
|
|
++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
|
|
++
|
|
++ if (overlay->ov_id >= 0)
|
|
++ of_overlay_remove(&overlay->ov_id);
|
|
++ if (overlay->fw)
|
|
++ release_firmware(overlay->fw);
|
|
++ /* kfree with NULL is safe */
|
|
++ kfree(overlay->dtbo);
|
|
++ kfree(overlay->mem);
|
|
++ kfree(overlay);
|
|
++}
|
|
++
|
|
++static struct configfs_item_operations cfs_overlay_item_ops = {
|
|
++ .release = cfs_overlay_release,
|
|
++};
|
|
++
|
|
++static struct config_item_type cfs_overlay_type = {
|
|
++ .ct_item_ops = &cfs_overlay_item_ops,
|
|
++ .ct_attrs = cfs_overlay_attrs,
|
|
++ .ct_bin_attrs = cfs_overlay_bin_attrs,
|
|
++ .ct_owner = THIS_MODULE,
|
|
++};
|
|
++
|
|
++static struct config_item
|
|
++ *cfs_overlay_group_make_item(struct config_group *group,
|
|
++ const char *name)
|
|
++{
|
|
++ struct cfs_overlay_item *overlay;
|
|
++
|
|
++ overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
|
|
++ if (!overlay)
|
|
++ return ERR_PTR(-ENOMEM);
|
|
++ overlay->ov_id = -1;
|
|
++ config_item_init_type_name(&overlay->item, name, &cfs_overlay_type);
|
|
++
|
|
++ return &overlay->item;
|
|
++}
|
|
++
|
|
++static void cfs_overlay_group_drop_item(struct config_group *group,
|
|
++ struct config_item *item)
|
|
++{
|
|
++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
|
|
++
|
|
++ config_item_put(&overlay->item);
|
|
++}
|
|
++
|
|
++static struct configfs_group_operations overlays_ops = {
|
|
++ .make_item = cfs_overlay_group_make_item,
|
|
++ .drop_item = cfs_overlay_group_drop_item,
|
|
++};
|
|
++
|
|
++static struct config_item_type overlays_type = {
|
|
++ .ct_group_ops = &overlays_ops,
|
|
++ .ct_owner = THIS_MODULE,
|
|
++};
|
|
++
|
|
++static struct configfs_group_operations of_cfs_ops = {
|
|
++ /* empty - we don't allow anything to be created */
|
|
++};
|
|
++
|
|
++static struct config_item_type of_cfs_type = {
|
|
++ .ct_group_ops = &of_cfs_ops,
|
|
++ .ct_owner = THIS_MODULE,
|
|
++};
|
|
++
|
|
++struct config_group of_cfs_overlay_group;
|
|
++
|
|
++static struct configfs_subsystem of_cfs_subsys = {
|
|
++ .su_group = {
|
|
++ .cg_item = {
|
|
++ .ci_namebuf = "device-tree",
|
|
++ .ci_type = &of_cfs_type,
|
|
++ },
|
|
++ },
|
|
++ .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex),
|
|
++};
|
|
++
|
|
++static int __init of_cfs_init(void)
|
|
++{
|
|
++ int ret;
|
|
++
|
|
++ pr_info("%s\n", __func__);
|
|
++
|
|
++ config_group_init(&of_cfs_subsys.su_group);
|
|
++ config_group_init_type_name(&of_cfs_overlay_group, "overlays",
|
|
++ &overlays_type);
|
|
++ configfs_add_default_group(&of_cfs_overlay_group,
|
|
++ &of_cfs_subsys.su_group);
|
|
++
|
|
++ ret = configfs_register_subsystem(&of_cfs_subsys);
|
|
++ if (ret != 0) {
|
|
++ pr_err("%s: failed to register subsys\n", __func__);
|
|
++ goto out;
|
|
++ }
|
|
++ pr_info("%s: OK\n", __func__);
|
|
++out:
|
|
++ return ret;
|
|
++}
|
|
++late_initcall(of_cfs_init);
|
|
+diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
|
|
+index a9a292d6d59b26..aee4c4edd5ee9d 100644
|
|
+--- a/drivers/of/overlay.c
|
|
++++ b/drivers/of/overlay.c
|
|
+@@ -1245,6 +1245,9 @@ int of_overlay_remove(int *ovcs_id)
|
|
+ if (!ret)
|
|
+ ret = ret_tmp;
|
|
+
|
|
++ /* Wait for completion of call_rcu()'s */
|
|
++ rcu_barrier();
|
|
++
|
|
+ free_overlay_changeset(ovcs);
|
|
+
|
|
+ err_unlock:
|
|
\ No newline at end of file
|
|
diff --git a/xilinx-fpga-manager.patch b/xilinx-fpga-manager.patch
|
|
new file mode 100644
|
|
index 0000000..33daffe
|
|
--- /dev/null
|
|
+++ b/xilinx-fpga-manager.patch
|
|
@@ -0,0 +1,676 @@
|
|
+# Enable user-space interface for PL programming via Linux FPGA manager
|
|
+# diff cherry-picked from Xilinx/linux-xilinx/tree/xlnx_rebase_v6.6_LTS
|
|
+# commit IDs: e61c0a9, 0a38712, dc67651, 89a24e3, 8d224b1, 2a9c05f, 4e94580
|
|
+# https://github.com/Xilinx/linux-xlnx/commits/xlnx_rebase_v6.6_LTS/drivers/fpga/fpga-mgr.c
|
|
+
|
|
+diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
|
|
+index 2f689ac4ba3a..67eb50451af2 100644
|
|
+--- a/drivers/fpga/Kconfig
|
|
++++ b/drivers/fpga/Kconfig
|
|
+@@ -12,6 +12,16 @@ menuconfig FPGA
|
|
+
|
|
+ if FPGA
|
|
+
|
|
++config FPGA_MGR_DEBUG_FS
|
|
++ tristate "FPGA debug fs"
|
|
++ select DEBUG_FS
|
|
++ help
|
|
++ Say Y here if you want to expose a DebugFS interface for the
|
|
++ FPGA Manager Framework. FPGA manager DebugFS provides a user
|
|
++ interface to read the fpga specific configuration information.
|
|
++
|
|
++ If unsure, say N.
|
|
++
|
|
+ config FPGA_MGR_SOCFPGA
|
|
+ tristate "Altera SOCFPGA FPGA Manager"
|
|
+ depends on ARCH_INTEL_SOCFPGA || COMPILE_TEST
|
|
+diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
|
|
+index 0f4035b089a2..3aa9f5f041f6 100644
|
|
+--- a/drivers/fpga/fpga-mgr.c
|
|
++++ b/drivers/fpga/fpga-mgr.c
|
|
+@@ -8,6 +8,9 @@
|
|
+ * With code from the mailing list:
|
|
+ * Copyright (C) 2013 Xilinx, Inc.
|
|
+ */
|
|
++#include <linux/dma-buf.h>
|
|
++#include <linux/dma-map-ops.h>
|
|
++#include <linux/kernel.h>
|
|
+ #include <linux/firmware.h>
|
|
+ #include <linux/fpga/fpga-mgr.h>
|
|
+ #include <linux/idr.h>
|
|
+@@ -61,12 +64,14 @@ static inline int fpga_mgr_write_complete(struct fpga_manager *mgr,
|
|
+ {
|
|
+ int ret = 0;
|
|
+
|
|
++ mgr->err = 0;
|
|
+ mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE;
|
|
+ if (mgr->mops->write_complete)
|
|
+ ret = mgr->mops->write_complete(mgr, info);
|
|
+ if (ret) {
|
|
+ dev_err(&mgr->dev, "Error after writing image data to FPGA\n");
|
|
+ mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR;
|
|
++ mgr->err = ret;
|
|
+ return ret;
|
|
+ }
|
|
+ mgr->state = FPGA_MGR_STATE_OPERATING;
|
|
+@@ -272,6 +277,7 @@ static int fpga_mgr_write_init_buf(struct fpga_manager *mgr,
|
|
+ size_t header_size = info->header_size;
|
|
+ int ret;
|
|
+
|
|
++ mgr->err = 0;
|
|
+ mgr->state = FPGA_MGR_STATE_WRITE_INIT;
|
|
+
|
|
+ if (header_size > count)
|
|
+@@ -284,6 +290,7 @@ static int fpga_mgr_write_init_buf(struct fpga_manager *mgr,
|
|
+ if (ret) {
|
|
+ dev_err(&mgr->dev, "Error preparing FPGA for writing\n");
|
|
+ mgr->state = FPGA_MGR_STATE_WRITE_INIT_ERR;
|
|
++ mgr->err = ret;
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+@@ -364,11 +371,15 @@ static int fpga_mgr_buf_load_sg(struct fpga_manager *mgr,
|
|
+ {
|
|
+ int ret;
|
|
+
|
|
++ if (info->flags & FPGA_MGR_USERKEY_ENCRYPTED_BITSTREAM)
|
|
++ memcpy(info->key, mgr->key, ENCRYPTED_KEY_LEN);
|
|
++
|
|
+ ret = fpga_mgr_prepare_sg(mgr, info, sgt);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Write the FPGA image to the FPGA. */
|
|
++ mgr->err = 0;
|
|
+ mgr->state = FPGA_MGR_STATE_WRITE;
|
|
+ if (mgr->mops->write_sg) {
|
|
+ ret = fpga_mgr_write_sg(mgr, sgt);
|
|
+@@ -405,6 +416,7 @@ static int fpga_mgr_buf_load_sg(struct fpga_manager *mgr,
|
|
+ if (ret) {
|
|
+ dev_err(&mgr->dev, "Error while writing image data to FPGA\n");
|
|
+ mgr->state = FPGA_MGR_STATE_WRITE_ERR;
|
|
++ mgr->err = ret;
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+@@ -436,11 +448,13 @@ static int fpga_mgr_buf_load_mapped(struct fpga_manager *mgr,
|
|
+ /*
|
|
+ * Write the FPGA image to the FPGA.
|
|
+ */
|
|
++ mgr->err = 0;
|
|
+ mgr->state = FPGA_MGR_STATE_WRITE;
|
|
+ ret = fpga_mgr_write(mgr, buf, count);
|
|
+ if (ret) {
|
|
+ dev_err(&mgr->dev, "Error while writing image data to FPGA\n");
|
|
+ mgr->state = FPGA_MGR_STATE_WRITE_ERR;
|
|
++ mgr->err = ret;
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+@@ -519,6 +533,39 @@ static int fpga_mgr_buf_load(struct fpga_manager *mgr,
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
++static int fpga_dmabuf_load(struct fpga_manager *mgr,
|
|
++ struct fpga_image_info *info)
|
|
++{
|
|
++ struct dma_buf_attachment *attach;
|
|
++ struct sg_table *sgt;
|
|
++ int ret;
|
|
++
|
|
++ /* create attachment for dmabuf with the user device */
|
|
++ attach = dma_buf_attach(mgr->dmabuf, &mgr->dev);
|
|
++ if (IS_ERR(attach)) {
|
|
++ pr_err("failed to attach dmabuf\n");
|
|
++ ret = PTR_ERR(attach);
|
|
++ goto fail_put;
|
|
++ }
|
|
++
|
|
++ sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
|
|
++ if (IS_ERR(sgt)) {
|
|
++ ret = PTR_ERR(sgt);
|
|
++ goto fail_detach;
|
|
++ }
|
|
++
|
|
++ info->sgt = sgt;
|
|
++ ret = fpga_mgr_buf_load_sg(mgr, info, info->sgt);
|
|
++ dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
|
|
++
|
|
++fail_detach:
|
|
++ dma_buf_detach(mgr->dmabuf, attach);
|
|
++fail_put:
|
|
++ dma_buf_put(mgr->dmabuf);
|
|
++
|
|
++ return ret;
|
|
++}
|
|
++
|
|
+ /**
|
|
+ * fpga_mgr_firmware_load - request firmware and load to fpga
|
|
+ * @mgr: fpga manager
|
|
+@@ -543,11 +590,17 @@ static int fpga_mgr_firmware_load(struct fpga_manager *mgr,
|
|
+
|
|
+ dev_info(dev, "writing %s to %s\n", image_name, mgr->name);
|
|
+
|
|
++ mgr->err = 0;
|
|
+ mgr->state = FPGA_MGR_STATE_FIRMWARE_REQ;
|
|
+
|
|
++ /* flags indicates whether to do full or partial reconfiguration */
|
|
++ info->flags = mgr->flags;
|
|
++ memcpy(info->key, mgr->key, ENCRYPTED_KEY_LEN);
|
|
++
|
|
+ ret = request_firmware(&fw, image_name, dev);
|
|
+ if (ret) {
|
|
+ mgr->state = FPGA_MGR_STATE_FIRMWARE_REQ_ERR;
|
|
++ mgr->err = ret;
|
|
+ dev_err(dev, "Error requesting firmware %s\n", image_name);
|
|
+ return ret;
|
|
+ }
|
|
+@@ -573,6 +626,8 @@ int fpga_mgr_load(struct fpga_manager *mgr, struct fpga_image_info *info)
|
|
+ {
|
|
+ info->header_size = mgr->mops->initial_header_size;
|
|
+
|
|
++ if (mgr->flags & FPGA_MGR_CONFIG_DMA_BUF)
|
|
++ return fpga_dmabuf_load(mgr, info);
|
|
+ if (info->sgt)
|
|
+ return fpga_mgr_buf_load_sg(mgr, info, info->sgt);
|
|
+ if (info->buf && info->count)
|
|
+@@ -626,6 +681,9 @@ static ssize_t state_show(struct device *dev,
|
|
+ {
|
|
+ struct fpga_manager *mgr = to_fpga_manager(dev);
|
|
+
|
|
++ if (mgr->err)
|
|
++ return sprintf(buf, "%s: 0x%x\n", state_str[mgr->state], mgr->err);
|
|
++
|
|
+ return sprintf(buf, "%s\n", state_str[mgr->state]);
|
|
+ }
|
|
+
|
|
+@@ -652,14 +710,88 @@ static ssize_t status_show(struct device *dev,
|
|
+ return len;
|
|
+ }
|
|
+
|
|
++static ssize_t firmware_store(struct device *dev,
|
|
++ struct device_attribute *attr,
|
|
++ const char *buf, size_t count)
|
|
++{
|
|
++ struct fpga_manager *mgr = to_fpga_manager(dev);
|
|
++ unsigned int len;
|
|
++ char image_name[NAME_MAX];
|
|
++ int ret;
|
|
++
|
|
++ /* struct with information about the FPGA image to program. */
|
|
++ struct fpga_image_info info = {0};
|
|
++
|
|
++ info.header_size = mgr->mops->initial_header_size;
|
|
++
|
|
++ /* lose terminating \n */
|
|
++ strcpy(image_name, buf);
|
|
++ len = strlen(image_name);
|
|
++ if (image_name[len - 1] == '\n')
|
|
++ image_name[len - 1] = 0;
|
|
++
|
|
++ ret = fpga_mgr_firmware_load(mgr, &info, image_name);
|
|
++ if (ret)
|
|
++ return ret;
|
|
++
|
|
++ return count;
|
|
++}
|
|
++
|
|
++static ssize_t key_show(struct device *dev,
|
|
++ struct device_attribute *attr, char *buf)
|
|
++{
|
|
++ struct fpga_manager *mgr = to_fpga_manager(dev);
|
|
++
|
|
++ return snprintf(buf, ENCRYPTED_KEY_LEN + 1, "%s\n", mgr->key);
|
|
++}
|
|
++
|
|
++static ssize_t key_store(struct device *dev,
|
|
++ struct device_attribute *attr,
|
|
++ const char *buf, size_t count)
|
|
++{
|
|
++ struct fpga_manager *mgr = to_fpga_manager(dev);
|
|
++
|
|
++ memcpy(mgr->key, buf, count);
|
|
++
|
|
++ return count;
|
|
++}
|
|
++
|
|
++static ssize_t flags_show(struct device *dev,
|
|
++ struct device_attribute *attr, char *buf)
|
|
++{
|
|
++ struct fpga_manager *mgr = to_fpga_manager(dev);
|
|
++
|
|
++ return sprintf(buf, "%lx\n", mgr->flags);
|
|
++}
|
|
++
|
|
++static ssize_t flags_store(struct device *dev,
|
|
++ struct device_attribute *attr,
|
|
++ const char *buf, size_t count)
|
|
++{
|
|
++ struct fpga_manager *mgr = to_fpga_manager(dev);
|
|
++ int ret;
|
|
++
|
|
++ ret = kstrtol(buf, 16, &mgr->flags);
|
|
++ if (ret)
|
|
++ return ret;
|
|
++
|
|
++ return count;
|
|
++}
|
|
++
|
|
+ static DEVICE_ATTR_RO(name);
|
|
+ static DEVICE_ATTR_RO(state);
|
|
+ static DEVICE_ATTR_RO(status);
|
|
++static DEVICE_ATTR_WO(firmware);
|
|
++static DEVICE_ATTR_RW(flags);
|
|
++static DEVICE_ATTR_RW(key);
|
|
+
|
|
+ static struct attribute *fpga_mgr_attrs[] = {
|
|
+ &dev_attr_name.attr,
|
|
+ &dev_attr_state.attr,
|
|
+ &dev_attr_status.attr,
|
|
++ &dev_attr_firmware.attr,
|
|
++ &dev_attr_flags.attr,
|
|
++ &dev_attr_key.attr,
|
|
+ NULL,
|
|
+ };
|
|
+ ATTRIBUTE_GROUPS(fpga_mgr);
|
|
+@@ -739,6 +871,106 @@ void fpga_mgr_put(struct fpga_manager *mgr)
|
|
+ }
|
|
+ EXPORT_SYMBOL_GPL(fpga_mgr_put);
|
|
+
|
|
++#ifdef CONFIG_FPGA_MGR_DEBUG_FS
|
|
++#include <linux/debugfs.h>
|
|
++
|
|
++static int fpga_mgr_read(struct seq_file *s, void *data)
|
|
++{
|
|
++ struct fpga_manager *mgr = (struct fpga_manager *)s->private;
|
|
++ int ret = 0;
|
|
++
|
|
++ if (!mgr->mops->read)
|
|
++ return -ENOENT;
|
|
++
|
|
++ if (!mutex_trylock(&mgr->ref_mutex))
|
|
++ return -EBUSY;
|
|
++
|
|
++ if (mgr->state != FPGA_MGR_STATE_OPERATING) {
|
|
++ ret = -EPERM;
|
|
++ goto err_unlock;
|
|
++ }
|
|
++
|
|
++ /* Read the FPGA configuration data from the fabric */
|
|
++ ret = mgr->mops->read(mgr, s);
|
|
++ if (ret)
|
|
++ dev_err(&mgr->dev, "Error while reading configuration data from FPGA\n");
|
|
++
|
|
++err_unlock:
|
|
++ mutex_unlock(&mgr->ref_mutex);
|
|
++
|
|
++ return ret;
|
|
++}
|
|
++
|
|
++static int fpga_mgr_read_open(struct inode *inode, struct file *file)
|
|
++{
|
|
++ return single_open(file, fpga_mgr_read, inode->i_private);
|
|
++}
|
|
++
|
|
++static const struct file_operations fpga_mgr_ops_image = {
|
|
++ .owner = THIS_MODULE,
|
|
++ .open = fpga_mgr_read_open,
|
|
++ .read = seq_read,
|
|
++};
|
|
++#endif
|
|
++
|
|
++static int fpga_dmabuf_fd_get(struct file *file, char __user *argp)
|
|
++{
|
|
++ struct fpga_manager *mgr = (struct fpga_manager *)(file->private_data);
|
|
++ int buffd;
|
|
++
|
|
++ if (copy_from_user(&buffd, argp, sizeof(buffd)))
|
|
++ return -EFAULT;
|
|
++
|
|
++ mgr->dmabuf = dma_buf_get(buffd);
|
|
++ if (IS_ERR_OR_NULL(mgr->dmabuf))
|
|
++ return -EINVAL;
|
|
++
|
|
++ mgr->flags = FPGA_MGR_CONFIG_DMA_BUF;
|
|
++
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++static int fpga_device_open(struct inode *inode, struct file *file)
|
|
++{
|
|
++ struct miscdevice *miscdev = file->private_data;
|
|
++ struct fpga_manager *mgr = container_of(miscdev,
|
|
++ struct fpga_manager, miscdev);
|
|
++
|
|
++ file->private_data = mgr;
|
|
++
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++static int fpga_device_release(struct inode *inode, struct file *file)
|
|
++{
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++static long fpga_device_ioctl(struct file *file, unsigned int cmd,
|
|
++ unsigned long arg)
|
|
++{
|
|
++ char __user *argp = (char __user *)arg;
|
|
++ int err;
|
|
++
|
|
++ switch (cmd) {
|
|
++ case FPGA_IOCTL_LOAD_DMA_BUFF:
|
|
++ err = fpga_dmabuf_fd_get(file, argp);
|
|
++ break;
|
|
++ default:
|
|
++ err = -ENOTTY;
|
|
++ }
|
|
++
|
|
++ return err;
|
|
++}
|
|
++
|
|
++static const struct file_operations fpga_fops = {
|
|
++ .owner = THIS_MODULE,
|
|
++ .open = fpga_device_open,
|
|
++ .release = fpga_device_release,
|
|
++ .unlocked_ioctl = fpga_device_ioctl,
|
|
++ .compat_ioctl = fpga_device_ioctl,
|
|
++};
|
|
++
|
|
+ /**
|
|
+ * fpga_mgr_lock - Lock FPGA manager for exclusive use
|
|
+ * @mgr: fpga manager
|
|
+@@ -788,6 +1020,9 @@ __fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *
|
|
+ struct module *owner)
|
|
+ {
|
|
+ const struct fpga_manager_ops *mops = info->mops;
|
|
++#ifdef CONFIG_FPGA_MGR_DEBUG_FS
|
|
++ struct dentry *d, *parent_dir;
|
|
++#endif
|
|
+ struct fpga_manager *mgr;
|
|
+ int id, ret;
|
|
+
|
|
+@@ -826,10 +1061,28 @@ __fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *
|
|
+ mgr->dev.of_node = parent->of_node;
|
|
+ mgr->dev.id = id;
|
|
+
|
|
++ /* Make device dma capable by inheriting from parent's */
|
|
++ set_dma_ops(&mgr->dev, get_dma_ops(parent));
|
|
++ ret = dma_coerce_mask_and_coherent(&mgr->dev, dma_get_mask(parent));
|
|
++ if (ret) {
|
|
++ dev_warn(parent,
|
|
++ "Failed to set DMA mask %llx. Trying to continue... %x\n",
|
|
++ dma_get_mask(parent), ret);
|
|
++ }
|
|
++
|
|
+ ret = dev_set_name(&mgr->dev, "fpga%d", id);
|
|
+ if (ret)
|
|
+ goto error_device;
|
|
+
|
|
++ mgr->miscdev.minor = MISC_DYNAMIC_MINOR;
|
|
++ mgr->miscdev.name = kobject_name(&mgr->dev.kobj);
|
|
++ mgr->miscdev.fops = &fpga_fops;
|
|
++ ret = misc_register(&mgr->miscdev);
|
|
++ if (ret) {
|
|
++ pr_err("fpga: failed to register misc device.\n");
|
|
++ goto error_device;
|
|
++ }
|
|
++
|
|
+ /*
|
|
+ * Initialize framework state by requesting low level driver read state
|
|
+ * from device. FPGA may be in reset mode or may have been programmed
|
|
+@@ -843,6 +1096,28 @@ __fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *
|
|
+ return ERR_PTR(ret);
|
|
+ }
|
|
+
|
|
++#ifdef CONFIG_FPGA_MGR_DEBUG_FS
|
|
++ mgr->dir = debugfs_create_dir("fpga", NULL);
|
|
++ if (!mgr->dir)
|
|
++ goto error_device;
|
|
++
|
|
++ parent_dir = mgr->dir;
|
|
++ d = debugfs_create_dir(mgr->dev.kobj.name, parent_dir);
|
|
++ if (!d) {
|
|
++ debugfs_remove_recursive(parent_dir);
|
|
++ goto error_device;
|
|
++ }
|
|
++
|
|
++ parent_dir = d;
|
|
++ d = debugfs_create_file("image", 0644, parent_dir, mgr,
|
|
++ &fpga_mgr_ops_image);
|
|
++ if (!d) {
|
|
++ debugfs_remove_recursive(mgr->dir);
|
|
++ goto error_device;
|
|
++ }
|
|
++#endif
|
|
++ dev_info(&mgr->dev, "%s registered\n", mgr->name);
|
|
++
|
|
+ return mgr;
|
|
+
|
|
+ error_device:
|
|
+@@ -894,6 +1169,10 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
|
|
+ {
|
|
+ dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name);
|
|
+
|
|
++#ifdef CONFIG_FPGA_MGR_DEBUG_FS
|
|
++ debugfs_remove_recursive(mgr->dir);
|
|
++#endif
|
|
++
|
|
+ /*
|
|
+ * If the low level driver provides a method for putting fpga into
|
|
+ * a desired state upon unregister, do it.
|
|
+diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c
|
|
+index a6affd83f275..48ac7617296e 100644
|
|
+--- a/drivers/fpga/of-fpga-region.c
|
|
++++ b/drivers/fpga/of-fpga-region.c
|
|
+@@ -230,6 +230,16 @@ of_fpga_region_parse_ov(struct fpga_region *region,
|
|
+ if (of_property_read_bool(overlay, "encrypted-fpga-config"))
|
|
+ info->flags |= FPGA_MGR_ENCRYPTED_BITSTREAM;
|
|
+
|
|
++ if (of_property_read_bool(overlay, "userkey-encrypted-fpga-config"))
|
|
++ info->flags |= FPGA_MGR_USERKEY_ENCRYPTED_BITSTREAM;
|
|
++
|
|
++ if (of_property_read_bool(overlay, "ddrmem-authenticated-fpga-config"))
|
|
++ info->flags |= FPGA_MGR_DDR_MEM_AUTH_BITSTREAM;
|
|
++
|
|
++ if (of_property_read_bool(overlay,
|
|
++ "securemem-authenticated-fpga-config"))
|
|
++ info->flags |= FPGA_MGR_SECURE_MEM_AUTH_BITSTREAM;
|
|
++
|
|
+ if (!of_property_read_string(overlay, "firmware-name",
|
|
+ &firmware_name)) {
|
|
+ info->firmware_name = devm_kstrdup(dev, firmware_name,
|
|
+diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
|
|
+index f3434e2c487b..d2434ed85eff 100644
|
|
+--- a/drivers/fpga/zynqmp-fpga.c
|
|
++++ b/drivers/fpga/zynqmp-fpga.c
|
|
+@@ -43,25 +43,47 @@ static int zynqmp_fpga_ops_write(struct fpga_manager *mgr,
|
|
+ struct zynqmp_fpga_priv *priv;
|
|
+ dma_addr_t dma_addr;
|
|
+ u32 eemi_flags = 0;
|
|
++ size_t dma_size;
|
|
+ char *kbuf;
|
|
+ int ret;
|
|
+
|
|
+ priv = mgr->priv;
|
|
+
|
|
+- kbuf = dma_alloc_coherent(priv->dev, size, &dma_addr, GFP_KERNEL);
|
|
++ if (priv->flags & FPGA_MGR_USERKEY_ENCRYPTED_BITSTREAM)
|
|
++ dma_size = size + ENCRYPTED_KEY_LEN;
|
|
++ else
|
|
++ dma_size = size;
|
|
++
|
|
++ kbuf = dma_alloc_coherent(priv->dev, dma_size, &dma_addr, GFP_KERNEL);
|
|
+ if (!kbuf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ memcpy(kbuf, buf, size);
|
|
+
|
|
++ if (priv->flags & FPGA_MGR_USERKEY_ENCRYPTED_BITSTREAM) {
|
|
++ eemi_flags |= XILINX_ZYNQMP_PM_FPGA_ENCRYPTION_USERKEY;
|
|
++ memcpy(kbuf + size, mgr->key, ENCRYPTED_KEY_LEN);
|
|
++ } else if (priv->flags & FPGA_MGR_ENCRYPTED_BITSTREAM) {
|
|
++ eemi_flags |= XILINX_ZYNQMP_PM_FPGA_ENCRYPTION_DEVKEY;
|
|
++ }
|
|
++
|
|
+ wmb(); /* ensure all writes are done before initiate FW call */
|
|
+
|
|
++ if (priv->flags & FPGA_MGR_DDR_MEM_AUTH_BITSTREAM)
|
|
++ eemi_flags |= XILINX_ZYNQMP_PM_FPGA_AUTHENTICATION_DDR;
|
|
++ else if (priv->flags & FPGA_MGR_SECURE_MEM_AUTH_BITSTREAM)
|
|
++ eemi_flags |= XILINX_ZYNQMP_PM_FPGA_AUTHENTICATION_OCM;
|
|
++
|
|
+ if (priv->flags & FPGA_MGR_PARTIAL_RECONFIG)
|
|
+ eemi_flags |= XILINX_ZYNQMP_PM_FPGA_PARTIAL;
|
|
+
|
|
+- ret = zynqmp_pm_fpga_load(dma_addr, size, eemi_flags);
|
|
++ if (priv->flags & FPGA_MGR_USERKEY_ENCRYPTED_BITSTREAM)
|
|
++ ret = zynqmp_pm_fpga_load(dma_addr, dma_addr + size,
|
|
++ eemi_flags);
|
|
++ else
|
|
++ ret = zynqmp_pm_fpga_load(dma_addr, size, eemi_flags);
|
|
+
|
|
+- dma_free_coherent(priv->dev, size, kbuf, dma_addr);
|
|
++ dma_free_coherent(priv->dev, dma_size, kbuf, dma_addr);
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h
|
|
+index e8b12ec8b060..ffd2dfdb6abd 100644
|
|
+--- a/include/linux/firmware/xlnx-zynqmp.h
|
|
++++ b/include/linux/firmware/xlnx-zynqmp.h
|
|
+@@ -83,6 +83,10 @@
|
|
+ */
|
|
+ #define XILINX_ZYNQMP_PM_FPGA_FULL 0x0U
|
|
+ #define XILINX_ZYNQMP_PM_FPGA_PARTIAL BIT(0)
|
|
++#define XILINX_ZYNQMP_PM_FPGA_AUTHENTICATION_DDR BIT(1)
|
|
++#define XILINX_ZYNQMP_PM_FPGA_AUTHENTICATION_OCM BIT(2)
|
|
++#define XILINX_ZYNQMP_PM_FPGA_ENCRYPTION_USERKEY BIT(3)
|
|
++#define XILINX_ZYNQMP_PM_FPGA_ENCRYPTION_DEVKEY BIT(4)
|
|
+
|
|
+ /* FPGA Status Reg */
|
|
+ #define XILINX_ZYNQMP_PM_FPGA_CONFIG_STAT_OFFSET 7U
|
|
+diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
|
|
+index 0d4fe068f3d8..f884d268c974 100644
|
|
+--- a/include/linux/fpga/fpga-mgr.h
|
|
++++ b/include/linux/fpga/fpga-mgr.h
|
|
+@@ -9,8 +9,11 @@
|
|
+ #define _LINUX_FPGA_MGR_H
|
|
+
|
|
+ #include <linux/mutex.h>
|
|
++#include <linux/miscdevice.h>
|
|
+ #include <linux/platform_device.h>
|
|
+
|
|
++#define ENCRYPTED_KEY_LEN 64 /* Bytes */
|
|
++
|
|
+ struct fpga_manager;
|
|
+ struct sg_table;
|
|
+
|
|
+@@ -66,17 +69,29 @@ enum fpga_mgr_states {
|
|
+ *
|
|
+ * %FPGA_MGR_EXTERNAL_CONFIG: FPGA has been configured prior to Linux booting
|
|
+ *
|
|
+- * %FPGA_MGR_ENCRYPTED_BITSTREAM: indicates bitstream is encrypted
|
|
++ * %FPGA_MGR_ENCRYPTED_BITSTREAM: indicates bitstream is encrypted with
|
|
++ * device key
|
|
+ *
|
|
+ * %FPGA_MGR_BITSTREAM_LSB_FIRST: SPI bitstream bit order is LSB first
|
|
+ *
|
|
+ * %FPGA_MGR_COMPRESSED_BITSTREAM: FPGA bitstream is compressed
|
|
++ *
|
|
++ * %FPGA_MGR_USERKEY_ENCRYPTED_BITSTREAM: indicates bitstream is encrypted with
|
|
++ * user key
|
|
++ * %FPGA_MGR_DDR_MEM_AUTH_BITSTREAM: do bitstream authentication using DDR
|
|
++ * memory if supported
|
|
++ * %FPGA_MGR_SECURE_MEM_AUTH_BITSTREAM: do bitstream authentication using secure
|
|
++ * memory if supported
|
|
+ */
|
|
+ #define FPGA_MGR_PARTIAL_RECONFIG BIT(0)
|
|
+ #define FPGA_MGR_EXTERNAL_CONFIG BIT(1)
|
|
+ #define FPGA_MGR_ENCRYPTED_BITSTREAM BIT(2)
|
|
+ #define FPGA_MGR_BITSTREAM_LSB_FIRST BIT(3)
|
|
+ #define FPGA_MGR_COMPRESSED_BITSTREAM BIT(4)
|
|
++#define FPGA_MGR_USERKEY_ENCRYPTED_BITSTREAM BIT(5)
|
|
++#define FPGA_MGR_DDR_MEM_AUTH_BITSTREAM BIT(6)
|
|
++#define FPGA_MGR_SECURE_MEM_AUTH_BITSTREAM BIT(7)
|
|
++#define FPGA_MGR_CONFIG_DMA_BUF BIT(5)
|
|
+
|
|
+ /**
|
|
+ * struct fpga_image_info - information specific to an FPGA image
|
|
+@@ -86,6 +101,7 @@ enum fpga_mgr_states {
|
|
+ * @config_complete_timeout_us: maximum time for FPGA to switch to operating
|
|
+ * status in the write_complete op.
|
|
+ * @firmware_name: name of FPGA image firmware file
|
|
++ * @key: key value useful for Encrypted Bitstream loading to read the userkey
|
|
+ * @sgt: scatter/gather table containing FPGA image
|
|
+ * @buf: contiguous buffer containing FPGA image
|
|
+ * @count: size of buf
|
|
+@@ -102,6 +118,7 @@ struct fpga_image_info {
|
|
+ u32 disable_timeout_us;
|
|
+ u32 config_complete_timeout_us;
|
|
+ char *firmware_name;
|
|
++ char key[ENCRYPTED_KEY_LEN];
|
|
+ struct sg_table *sgt;
|
|
+ const char *buf;
|
|
+ size_t count;
|
|
+@@ -160,6 +177,7 @@ struct fpga_manager_info {
|
|
+ * @write: write count bytes of configuration data to the FPGA
|
|
+ * @write_sg: write the scatter list of configuration data to the FPGA
|
|
+ * @write_complete: set FPGA to operating state after writing is done
|
|
++ * @read: optional: read FPGA configuration information
|
|
+ * @fpga_remove: optional: Set FPGA into a specific state during driver remove
|
|
+ * @groups: optional attribute groups.
|
|
+ *
|
|
+@@ -182,6 +200,7 @@ struct fpga_manager_ops {
|
|
+ int (*write_sg)(struct fpga_manager *mgr, struct sg_table *sgt);
|
|
+ int (*write_complete)(struct fpga_manager *mgr,
|
|
+ struct fpga_image_info *info);
|
|
++ int (*read)(struct fpga_manager *mgr, struct seq_file *s);
|
|
+ void (*fpga_remove)(struct fpga_manager *mgr);
|
|
+ const struct attribute_group **groups;
|
|
+ };
|
|
+@@ -196,23 +215,37 @@ struct fpga_manager_ops {
|
|
+ /**
|
|
+ * struct fpga_manager - fpga manager structure
|
|
+ * @name: name of low level fpga manager
|
|
++ * @flags: flags determines the type of Bitstream
|
|
++ * @key: key value useful for Encrypted Bitstream loading to read the userkey
|
|
+ * @dev: fpga manager device
|
|
++ * @dmabuf: shared dma buffer
|
|
+ * @ref_mutex: only allows one reference to fpga manager
|
|
++ * @miscdev: information about character device node
|
|
+ * @state: state of fpga manager
|
|
+ * @compat_id: FPGA manager id for compatibility check.
|
|
+ * @mops: pointer to struct of fpga manager ops
|
|
+ * @mops_owner: module containing the mops
|
|
+ * @priv: low level driver private date
|
|
++ * @err: low level driver error code
|
|
++ * @dir: debugfs image directory
|
|
+ */
|
|
+ struct fpga_manager {
|
|
+ const char *name;
|
|
++ unsigned long flags;
|
|
++ char key[ENCRYPTED_KEY_LEN + 1];
|
|
+ struct device dev;
|
|
++ struct dma_buf *dmabuf;
|
|
+ struct mutex ref_mutex;
|
|
++ struct miscdevice miscdev;
|
|
+ enum fpga_mgr_states state;
|
|
+ struct fpga_compat_id *compat_id;
|
|
+ const struct fpga_manager_ops *mops;
|
|
+ struct module *mops_owner;
|
|
+ void *priv;
|
|
++ int err;
|
|
++#ifdef CONFIG_FPGA_MGR_DEBUG_FS
|
|
++ struct dentry *dir;
|
|
++#endif
|
|
+ };
|
|
+
|
|
+ #define to_fpga_manager(d) container_of(d, struct fpga_manager, dev)
|
|
+@@ -258,4 +291,6 @@ __devm_fpga_mgr_register(struct device *parent, const char *name,
|
|
+ const struct fpga_manager_ops *mops, void *priv,
|
|
+ struct module *owner);
|
|
+
|
|
++#define FPGA_IOCTL_LOAD_DMA_BUFF _IOWR('R', 1, __u32)
|
|
++
|
|
+ #endif /*_LINUX_FPGA_MGR_H */
|
|
diff --git a/zynq_image.nix b/zynq_image.nix
|
|
index d5c5eda..7ede584 100644
|
|
--- a/zynq_image.nix
|
|
+++ b/zynq_image.nix
|
|
@@ -3,6 +3,16 @@
|
|
with lib;
|
|
let
|
|
customKernel = (pkgs.linux_6_6.override {
|
|
+ kernelPatches = [
|
|
+ ({
|
|
+ name = "xilinx-configfs-overlays";
|
|
+ patch = ./xilinx-configfs-overlays.patch;
|
|
+ })
|
|
+ ({
|
|
+ name = "xilinx-fpga-manager";
|
|
+ patch = ./xilinx-fpga-manager.patch;
|
|
+ })
|
|
+ ];
|
|
extraConfig = ''
|
|
OVERLAY_FS y
|
|
MEDIA_SUPPORT n
|
|
@@ -17,6 +27,7 @@ let
|
|
OF_FPGA_REGION y
|
|
FPGA_MGR_ZYNQ_FPGA y
|
|
OF_OVERLAY y
|
|
+ OF_CONFIGFS y
|
|
'';
|
|
}).overrideAttrs (oa: {
|
|
postInstall = ''
|