linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC 0/5] Open source FPGA image header
@ 2017-07-24 19:49 Alan Tull
  2017-07-24 19:49 ` [RFC 1/5] doc: fpga: add document for the fdt FPGA header Alan Tull
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Alan Tull @ 2017-07-24 19:49 UTC (permalink / raw)
  To: Moritz Fischer; +Cc: Alan Tull, linux-kernel, linux-fpga

This patch set adds open source FPGA image headers.  This allows FPGA
FPGA image specific information to be added to the images themselves.
The header format is a simplified form of u-boot FIT images and can be
expanded as real use cases are raised.  As all the FPGA kernel
frameworks are intended to be vendor agnostic, so also is the header.
As libfdt's licensing is permissive, this header is also intended to
be useful for OS's other than Linux.

In February, there was a conversation on the linux-fpga mailing list
where Moritz and Jason suggested headers, so with their permission
I'll be happy to add 'Suggested-by'.

An API function and a sysfs interface are added for applying FPGA
images to regions.

Patches 1-2 are documentation

Patches 3-4 are small API changes and could be rolled into my "non-DT
  support for FPGA regions" patchset.

Patch 5 is the bulk of it.

todo: I've been working on scatter gather table support for this.
It's not done yet, so I pulled it out for now.

This patch applies on top of my "non-DT support for FPGA regions" v3
patchset.

Alan Tull

Alan Tull (5):
  doc: fpga: add document for the fdt FPGA header
  doc: fpga: add sysfs document for fpga region
  fpga: add dev to fpga_image_info
  fpga-region: new function fpga_region_free
  fpga-region support for fdt headers on fpga images

 Documentation/ABI/testing/sysfs-class-fpga-region |   7 +
 Documentation/fpga/fpga-header.txt                | 103 +++++
 drivers/fpga/Kconfig                              |   7 +
 drivers/fpga/fpga-mgr.c                           |  14 +-
 drivers/fpga/fpga-region.c                        | 460 ++++++++++++++++++++++
 drivers/fpga/of-fpga-region.c                     |  16 +-
 include/linux/fpga/fpga-mgr.h                     |  13 +-
 include/linux/fpga/fpga-region.h                  |   3 +
 8 files changed, 612 insertions(+), 11 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-fpga-region
 create mode 100644 Documentation/fpga/fpga-header.txt

-- 
2.7.4

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [RFC 1/5] doc: fpga: add document for the fdt FPGA header
  2017-07-24 19:49 [RFC 0/5] Open source FPGA image header Alan Tull
@ 2017-07-24 19:49 ` Alan Tull
  2017-07-24 19:49 ` [RFC 2/5] doc: fpga: add sysfs document for fpga region Alan Tull
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Alan Tull @ 2017-07-24 19:49 UTC (permalink / raw)
  To: Moritz Fischer; +Cc: Alan Tull, linux-kernel, linux-fpga

Add a document for the open source fdt FPGA header.

Signed-off-by: Alan Tull <atull@kernel.org>
---
 Documentation/fpga/fpga-header.txt | 103 +++++++++++++++++++++++++++++++++++++
 1 file changed, 103 insertions(+)
 create mode 100644 Documentation/fpga/fpga-header.txt

diff --git a/Documentation/fpga/fpga-header.txt b/Documentation/fpga/fpga-header.txt
new file mode 100644
index 0000000..0045f63
--- /dev/null
+++ b/Documentation/fpga/fpga-header.txt
@@ -0,0 +1,103 @@
+Open source FPGA image headers
+
+Alan Tull 2017
+
+CONTENTS
+- Introduction
+- Format
+- Example
+- Using mkimage
+
+Introduction
+============
+
+Open source FPGA image headers have been added to allow FPGA image specific
+information to be included with the image.  The header format is a simplified
+form of u-boot FIT images and can be expanded as real use cases are raised.
+As all the FPGA kernel frameworks are intended to be vendor agnostic, so also
+is the header.  As libfdt's licensing is permissive, this header is also
+intended to be useful for OS's other than Linux such that the FPGA image need
+not be rebuilt for use under another OS.
+
+Format
+======
+
+The format of the header is a reduced FIT-like header, so it can be built
+using mkimage.  The header is a FDT followed by two optional binary data
+sections: a Device Tree overlay and a raw FPGA image.  When the image is
+applied to a FPGA region, first the FPGA is programmed (if the raw image is
+present) and then the DT overlay, if present, is applied.  If the DT overlay
+is eliminated, this header format may be used on systems running without
+Device Tree.
+
+Example
+=======
+
+/dts-v1/;
+
+/ {
+	description = "Program FPGA image and apply DT overlay";
+	#address-cells = <1>;
+
+	images {
+		fdt@1 {
+			description = "Flattened Device Tree blob";
+			type = "flat_dt";
+			data = /incbin/("/some/path/persona0.dtb");
+		};
+		fpga@1 {
+			description = "FPGA image";
+			type = "fpga";
+			data = /incbin/("/some/path/persona0.rbf");
+			partial-fpga-config;
+			region-unfreeze-timeout-us = <4>;
+			region-freeze-timeout-us = <4>;
+			config-complete-timeout-us = <100>;
+		};
+	};
+};
+
+This format is compatible with FIT, but leaves out a few things that were not
+needed.  The compression property is left out (assuming none).  The
+'configurations' section is left out since there is only one possible
+configuration presented here.  If we see an actual use case for
+configurations in the future, we could expand to support it seamlessly.
+
+Any FPGA image info must be included in the FPGA image section.  FPGA image
+info properties are defined in the fpga-region.txt Device Tree binding
+document at ../devicetree/bindings/fpga/fpga-region.txt.
+
+The FPGA image section has to come last.
+
+Using mkimage
+=============
+
+If the above example is named persona0.its, it can be compiled by using
+mkimage as follows.  The output file is persona0.fit.
+
+ mkimage -f persona0.its persona0.fit
+
+Applying the image
+==================
+
+A sysfs attribute 'firmware_name' is added for each FPGA region.
+
+To apply the image:
+
+  echo persona.fit > /sys/class/fpga_region/region0/firmware_name
+
+To free up the region for reprogramming:
+
+  echo > /sys/class/fpga_region/region0/firmware_name
+
+FPGA Region API Function
+========================
+
+An API for FPGA region has been added:
+
+  int fpga_region_fdt_image_apply(struct fpga_region *region)
+
+This function will parse the header of a FPGA image and do the programming
+and apply the DT overlay.  The FPGA image is presented as a scatter gather
+table, a contiguous buffer, or name of a firmware file in the region's FPGA
+image info (region->info).
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC 2/5] doc: fpga: add sysfs document for fpga region
  2017-07-24 19:49 [RFC 0/5] Open source FPGA image header Alan Tull
  2017-07-24 19:49 ` [RFC 1/5] doc: fpga: add document for the fdt FPGA header Alan Tull
@ 2017-07-24 19:49 ` Alan Tull
  2017-07-24 19:49 ` [RFC 3/5] fpga: add dev to fpga_image_info Alan Tull
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Alan Tull @ 2017-07-24 19:49 UTC (permalink / raw)
  To: Moritz Fischer; +Cc: Alan Tull, linux-kernel, linux-fpga

Document the firmware_name attribute added for each
region.

Signed-off-by: Alan Tull <atull@kernel.org>
---
 Documentation/ABI/testing/sysfs-class-fpga-region | 7 +++++++
 1 file changed, 7 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-class-fpga-region

diff --git a/Documentation/ABI/testing/sysfs-class-fpga-region b/Documentation/ABI/testing/sysfs-class-fpga-region
new file mode 100644
index 0000000..de47645
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-fpga-region
@@ -0,0 +1,7 @@
+What:		/sys/class/fpga_region/<fpga>/firmware_name
+Date:		July 2017
+KernelVersion:	4.15
+Contact:	Alan Tull <atull@kernel.org>
+Description:	Write the name of FPGA image file on the firmware path to
+		program the FPGA region.  Write nothing to free up the
+		FPGA region for reprogramming.
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC 3/5] fpga: add dev to fpga_image_info
  2017-07-24 19:49 [RFC 0/5] Open source FPGA image header Alan Tull
  2017-07-24 19:49 ` [RFC 1/5] doc: fpga: add document for the fdt FPGA header Alan Tull
  2017-07-24 19:49 ` [RFC 2/5] doc: fpga: add sysfs document for fpga region Alan Tull
@ 2017-07-24 19:49 ` Alan Tull
  2017-07-24 19:49 ` [RFC 4/5] fpga-region: new function fpga_region_free Alan Tull
  2017-07-24 19:49 ` [RFC 5/5] fpga-region support for fdt headers on fpga images Alan Tull
  4 siblings, 0 replies; 6+ messages in thread
From: Alan Tull @ 2017-07-24 19:49 UTC (permalink / raw)
  To: Moritz Fischer; +Cc: Alan Tull, linux-kernel, linux-fpga

This patch should be rolled into the next version of my
"non-dt support for FPGA regions" patchset.

Add a pointer to the device that owns the fpga_image_info to
fpga_image_info.  That way the fpga_image_info_free can
drop the 'dev' parameter and we are left with:

struct fpga_image_info *fpga_image_info_alloc(struct device *dev);

void fpga_image_info_free(struct fpga_image_info *info);

Signed-off-by: Alan Tull <atull@kernel.org>
---
 drivers/fpga/fpga-mgr.c       | 14 ++++++++++----
 drivers/fpga/of-fpga-region.c |  6 +++---
 include/linux/fpga/fpga-mgr.h |  4 +++-
 3 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
index be13cce..af33310 100644
--- a/drivers/fpga/fpga-mgr.c
+++ b/drivers/fpga/fpga-mgr.c
@@ -40,20 +40,26 @@ struct fpga_image_info *fpga_image_info_alloc(struct device *dev)
 	if (!info)
 		return ERR_PTR(-ENOMEM);
 
+	get_device(dev);
+	info->dev = dev;
+
 	return info;
 }
 EXPORT_SYMBOL_GPL(fpga_image_info_alloc);
 
-void fpga_image_info_free(struct device *dev,
-			  struct fpga_image_info *info)
+void fpga_image_info_free(struct fpga_image_info *info)
 {
+	struct device *dev;
+
 	if (!info)
 		return;
 
 	if (info->firmware_name)
-		devm_kfree(dev, info->firmware_name);
+		devm_kfree(info->dev, info->firmware_name);
 
-	devm_kfree(dev, info);
+	dev = info->dev;
+	devm_kfree(info->dev, info);
+	put_device(dev);
 }
 EXPORT_SYMBOL_GPL(fpga_image_info_free);
 
diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c
index c2ea9b6..6bb5799 100644
--- a/drivers/fpga/of-fpga-region.c
+++ b/drivers/fpga/of-fpga-region.c
@@ -273,7 +273,7 @@ static struct fpga_image_info *of_fpga_region_parse_ov(
 
 	return info;
 ret_no_info:
-	fpga_image_info_free(dev, info);
+	fpga_image_info_free(info);
 	return ERR_PTR(ret);
 }
 
@@ -314,7 +314,7 @@ static int of_fpga_region_notify_pre_apply(struct fpga_region *region,
 	ret = fpga_region_program_fpga(region);
 	if (ret) {
 		/* error; reject overlay */
-		fpga_image_info_free(dev, info);
+		fpga_image_info_free(info);
 		region->info = NULL;
 	}
 
@@ -335,7 +335,7 @@ static void of_fpga_region_notify_post_remove(struct fpga_region *region,
 {
 	fpga_bridges_disable(&region->bridge_list);
 	fpga_bridges_put(&region->bridge_list);
-	fpga_image_info_free(&region->dev, region->info);
+	fpga_image_info_free(region->info);
 	region->info = NULL;
 }
 
diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
index 50954cb..ddc56c0 100644
--- a/include/linux/fpga/fpga-mgr.h
+++ b/include/linux/fpga/fpga-mgr.h
@@ -79,6 +79,7 @@ enum fpga_mgr_states {
 
 /**
  * struct fpga_image_info - information specific to a FPGA image
+ * @dev: device that owns this info
  * @flags: boolean flags as defined above
  * @enable_timeout_us: maximum time to enable traffic through bridge (uSec)
  * @disable_timeout_us: maximum time to disable traffic through bridge (uSec)
@@ -91,6 +92,7 @@ enum fpga_mgr_states {
  * @overlay: Device Tree overlay
  */
 struct fpga_image_info {
+	struct device *dev;
 	u32 flags;
 	u32 enable_timeout_us;
 	u32 disable_timeout_us;
@@ -153,7 +155,7 @@ struct fpga_manager {
 
 struct fpga_image_info *fpga_image_info_alloc(struct device *dev);
 
-void fpga_image_info_free(struct device *dev, struct fpga_image_info *info);
+void fpga_image_info_free(struct fpga_image_info *info);
 
 int fpga_mgr_load(struct fpga_manager *mgr, struct fpga_image_info *info);
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC 4/5] fpga-region: new function fpga_region_free
  2017-07-24 19:49 [RFC 0/5] Open source FPGA image header Alan Tull
                   ` (2 preceding siblings ...)
  2017-07-24 19:49 ` [RFC 3/5] fpga: add dev to fpga_image_info Alan Tull
@ 2017-07-24 19:49 ` Alan Tull
  2017-07-24 19:49 ` [RFC 5/5] fpga-region support for fdt headers on fpga images Alan Tull
  4 siblings, 0 replies; 6+ messages in thread
From: Alan Tull @ 2017-07-24 19:49 UTC (permalink / raw)
  To: Moritz Fischer; +Cc: Alan Tull, linux-kernel, linux-fpga

Add FPGA region API function fpga_region_free() that
undoes some of what fpga_region_program_fpga() does
to free up a region to be reprogrammed.

Signed-off-by: Alan Tull <atull@kernel.org>
---
 drivers/fpga/fpga-region.c       | 20 ++++++++++++++++++++
 drivers/fpga/of-fpga-region.c    |  5 +----
 include/linux/fpga/fpga-region.h |  2 ++
 3 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c
index afc6188..944d24a 100644
--- a/drivers/fpga/fpga-region.c
+++ b/drivers/fpga/fpga-region.c
@@ -162,6 +162,26 @@ int fpga_region_program_fpga(struct fpga_region *region)
 }
 EXPORT_SYMBOL_GPL(fpga_region_program_fpga);
 
+/**
+ * fpga_region_free
+ * Undo some of what fpga_region_program_fpga() did to free up
+ * region to be reprogrammed.
+ * @region: FPGA region
+ */
+void fpga_region_free(struct fpga_region *region)
+{
+	if (!region->info)
+		return;
+
+	fpga_bridges_disable(&region->bridge_list);
+	if (region->get_bridges)
+		fpga_bridges_put(&region->bridge_list);
+
+	fpga_image_info_free(region->info);
+	region->info = NULL;
+}
+EXPORT_SYMBOL_GPL(fpga_region_free);
+
 int fpga_region_register(struct device *dev, struct fpga_region *region)
 {
 	int id, ret = 0;
diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c
index 6bb5799..019ca37 100644
--- a/drivers/fpga/of-fpga-region.c
+++ b/drivers/fpga/of-fpga-region.c
@@ -333,10 +333,7 @@ static int of_fpga_region_notify_pre_apply(struct fpga_region *region,
 static void of_fpga_region_notify_post_remove(struct fpga_region *region,
 					      struct of_overlay_notify_data *nd)
 {
-	fpga_bridges_disable(&region->bridge_list);
-	fpga_bridges_put(&region->bridge_list);
-	fpga_image_info_free(region->info);
-	region->info = NULL;
+	fpga_region_free(region);
 }
 
 /**
diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h
index f2eecdd..3b6f0b0 100644
--- a/include/linux/fpga/fpga-region.h
+++ b/include/linux/fpga/fpga-region.h
@@ -32,6 +32,8 @@ struct fpga_region *fpga_region_class_find(
 	int (*match)(struct device *, const void *));
 
 int fpga_region_program_fpga(struct fpga_region *region);
+void fpga_region_free(struct fpga_region *region);
+
 int fpga_region_register(struct device *dev, struct fpga_region *region);
 int fpga_region_unregister(struct fpga_region *region);
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC 5/5] fpga-region support for fdt headers on fpga images
  2017-07-24 19:49 [RFC 0/5] Open source FPGA image header Alan Tull
                   ` (3 preceding siblings ...)
  2017-07-24 19:49 ` [RFC 4/5] fpga-region: new function fpga_region_free Alan Tull
@ 2017-07-24 19:49 ` Alan Tull
  4 siblings, 0 replies; 6+ messages in thread
From: Alan Tull @ 2017-07-24 19:49 UTC (permalink / raw)
  To: Moritz Fischer; +Cc: Alan Tull, linux-kernel, linux-fpga

Support a FDT header for FPGA images.  Header format is a simplified
FIT containing at most one FPGA image and one DT overlay.

The FPGA image section includes FPGA image info properties needed for
programming.

If present, the DT overlay image is applied after the FPGA is
programmed.

Both images are optional.  If the DT overlay isn't present, this
interface may be used on systems running without a live Device Tree.

todo: add scatter gather table support.

Signed-off-by: Alan Tull <atull@kernel.org>
---
 drivers/fpga/Kconfig             |   7 +
 drivers/fpga/fpga-region.c       | 440 +++++++++++++++++++++++++++++++++++++++
 drivers/fpga/of-fpga-region.c    |   7 +
 include/linux/fpga/fpga-mgr.h    |   9 +
 include/linux/fpga/fpga-region.h |   1 +
 5 files changed, 464 insertions(+)

diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index c349c42..f4b595b 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -19,6 +19,13 @@ config FPGA_REGION
 	  and the FPGA Bridges associated with either a reconfigurable
 	  region of an FPGA or a whole FPGA.
 
+config FPGA_REGION_SYSFS
+       bool "FPGA Region Sysfs"
+	depends on FPGA_REGION
+	help
+	  FPGA Region sysfs interface.  This creates sysfs files under
+	  /sys/class/fpga_region/ to control programming FPGA regions.
+
 config OF_FPGA_REGION
 	tristate "FPGA Region Device Tree Overlay Support"
 	depends on OF && FPGA_REGION
diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c
index 944d24a..a9dd61e 100644
--- a/drivers/fpga/fpga-region.c
+++ b/drivers/fpga/fpga-region.c
@@ -4,6 +4,15 @@
  *  Copyright (C) 2013-2016 Altera Corporation
  *  Copyright (C) 2017 Intel Corporation
  *
+ * Dynamic DT code borrowed from Pantelis Antoniou's
+ * "OF: DT-Overlay configfs interface (v7)" patch.
+ * Copyright (C) 2013 - Pantelis Antoniou <panto@antoniou-consulting.com>
+ *
+ * FIT image parsing adapted from u-boot
+ * (C) Copyright 2008 Semihalf
+ * (C) Copyright 2000-2006
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
  * version 2, as published by the Free Software Foundation.
@@ -17,6 +26,7 @@
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/firmware.h>
 #include <linux/fpga/fpga-bridge.h>
 #include <linux/fpga/fpga-mgr.h>
 #include <linux/fpga/fpga-region.h>
@@ -24,6 +34,9 @@
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/libfdt.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 
@@ -91,6 +104,312 @@ static void fpga_region_put(struct fpga_region *region)
 	mutex_unlock(&region->mutex);
 }
 
+/*
+ * todo - of_fpga_region_overlay_apply() is adapted from create_overlay()
+ * (some changed structs) from Pantelis Antoniou's
+ * "OF: DT-Overlay configfs interface (v7)" patch.
+ * that hasn't been accepted upstream yet.
+ */
+static int of_fpga_region_overlay_apply(struct fpga_region *region)
+{
+	struct device *dev = &region->dev;
+	struct fpga_image_info *info = region->info;
+	int ret;
+
+	WARN_ON(info->overlay);
+
+	of_fdt_unflatten_tree((void *)info->fdt_blob, NULL,
+			      &info->overlay);
+	if (!info->overlay) {
+		dev_err(dev, "%s: failed to unflatten tree\n", __func__);
+		ret = -EINVAL;
+		return ret;
+	}
+	pr_debug("%s: unflattened OK\n", __func__);
+
+	/* mark it as detached */
+	of_node_set_flag(info->overlay, OF_DETACHED);
+
+	/* FDT in bitstream should not specify an external FPGA image to load */
+	if (of_find_property(info->overlay, "firmware-name", NULL))
+		dev_warn(dev, "Ignoring firmware-name property in FPGA image header\n");
+
+	/* perform resolution */
+	ret = of_resolve_phandles(info->overlay);
+	if (ret != 0) {
+		dev_err(dev, "%s: Failed to resolve tree\n", __func__);
+		return ret;
+	}
+	pr_debug("%s: resolved OK\n", __func__);
+
+	ret = of_overlay_create(info->overlay);
+	if (ret < 0) {
+		dev_err(dev, "%s: Failed to create overlay (err=%d)\n",
+			__func__, ret);
+		return ret;
+	}
+	info->ov_id = ret;
+
+	return 0;
+}
+
+static void fpga_region_overlay_rm(struct fpga_region *region)
+{
+	struct device *dev = &region->dev;
+	struct fpga_image_info *info = region->info;
+
+	if (info->ov_id >= 0) {
+		of_overlay_destroy(info->ov_id);
+		info->ov_id = -1;
+	}
+	if (info->fdt_blob) {
+		devm_kfree(dev, info->fdt_blob);
+		info->fdt_blob = NULL;
+	}
+}
+
+/**
+ * get_data_prop - get pointer, length of binary data
+ *
+ * FDT is in info->header.  Find the data property at the node offset.
+ * Give a pointer to the binary data.
+ *
+ * Returns length of data or zero
+ */
+static int get_data_prop(struct fpga_image_info *info,
+			 int noffset, const void **data)
+{
+	int len;
+
+	*data = fdt_getprop(info->header, noffset, "data", &len);
+	if (!*data)
+		return 0;
+
+	return len;
+}
+
+static int get_int_prop(struct fpga_image_info *info,
+			int root_offset,
+			const char *prop_str)
+{
+	const u32 *prop;
+
+	prop = fdt_getprop(info->header, root_offset, prop_str, NULL);
+	if (prop)
+		return fdt32_to_cpu(*prop);
+
+	return 0;
+}
+
+static void get_bool_prop_to_flags(struct fpga_image_info *info,
+				   int root_offset,
+				   const char *prop_str, int bit)
+{
+	const u32 *prop;
+
+	prop = fdt_getprop(info->header, root_offset, prop_str, NULL);
+	if (prop)
+		info->flags |= bit;
+}
+
+/**
+ * region_parse_section_fpga - parse fpga image section
+ *
+ * FPGA image header is in a contiguous buffer (region->info->header).
+ * Parse the image info.  Get pointers to the raw image.
+ *
+ * @region: FPGA region
+ * @node_offset: offset to the flat_dt node
+ *
+ * Return: 0 for success or negative error code.
+ */
+static int region_parse_section_fpga(struct fpga_region *region,
+				     int node_offset)
+{
+	struct device *dev = &region->dev;
+	struct fpga_image_info *info = region->info;
+	const void *raw_image;
+	size_t image_size;
+
+	if (fdt_getprop(info->header, node_offset, "firmware-name", NULL)) {
+		dev_warn(dev,
+			 "Ignoring firmware-name property in FPGA image header\n");
+	}
+
+	get_bool_prop_to_flags(info, node_offset, "partial-fpga-config",
+			       FPGA_MGR_PARTIAL_RECONFIG);
+
+	get_bool_prop_to_flags(info, node_offset, "external-fpga-config",
+			       FPGA_MGR_EXTERNAL_CONFIG);
+
+	get_bool_prop_to_flags(info, node_offset, "encrypted-fpga-config",
+			       FPGA_MGR_ENCRYPTED_BITSTREAM);
+
+	info->enable_timeout_us =
+		get_int_prop(info, node_offset, "region-unfreeze-timeout-us");
+
+	info->disable_timeout_us =
+		get_int_prop(info, node_offset, "region-freeze-timeout-us");
+
+	info->config_complete_timeout_us =
+		get_int_prop(info, node_offset, "config-complete-timeout-us");
+
+	image_size = get_data_prop(info, node_offset, &raw_image);
+	if (!image_size)
+		return 0;
+
+	if (info->sgt) {
+		/* todo */
+	} else {
+		info->buf = raw_image;
+		info->count = image_size;
+	}
+
+	return 0;
+}
+
+/**
+ * region_parse_section_flat_dt - Allocate memory and copy the flat_dt image
+ *
+ * FPGA image header is in a contiguous buffer (region->info->header).
+ *
+ * @region: FPGA region
+ * @node_offset: offset to the flat_dt node
+ *
+ * Return: 0 for success or negative error code.
+ */
+static int region_parse_section_flat_dt(struct fpga_region *region,
+					int node_offset)
+{
+	struct device *dev = &region->dev;
+	struct fpga_image_info *info = region->info;
+	size_t fdt_size;
+	char *fdt_blob;
+	const void *fdt_data;
+
+	fdt_size = get_data_prop(info, node_offset, &fdt_data);
+	if (!fdt_size)
+		return -EINVAL;
+
+	pr_err("fdt_size   = %d\n", fdt_size);
+
+	/* Save the DT overlay.  We will apply it after FPGA programming */
+	if (info->sgt) {
+		/* todo */
+	} else {
+		fdt_blob = devm_kmemdup(dev, fdt_data, fdt_size, GFP_KERNEL);
+		if (!fdt_blob)
+			return -ENOMEM;
+		info->fdt_blob = fdt_blob;
+	}
+
+	if (fdt_size != fdt_totalsize(info->fdt_blob)) {
+		dev_err(dev, "FDT overlay size mismatch %d vs %d\n",
+			fdt_size, fdt_totalsize(info->fdt_blob));
+		devm_kfree(dev, info->fdt_blob);
+		info->fdt_blob = NULL;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * fpga_region_parse_images - find images in the header and parse them
+ * Assume there is at maximum one fpga image and one flat_dt image.
+ *
+ * FPGA image header is in a contiguous buffer (region->info->header).
+ *
+ * @region: FPGA region
+ *
+ * Return: 0 for success or negative error code.
+ */
+static int fpga_region_parse_images(struct fpga_region *region)
+{
+	struct device *dev = &region->dev;
+	struct fpga_image_info *info = region->info;
+	const char *type_prop;
+	int images_offset;
+	int ndepth, count, noffset, ret;
+
+	if (fdt_magic(info->header) != FDT_MAGIC) {
+		dev_err(dev, "Invalid magic for FPGA image\n");
+		return -EINVAL;
+	}
+
+	if (fdt_check_header(info->header)) {
+		dev_err(dev, "Invalid device tree blob header\n");
+		return -EINVAL;
+	}
+
+	/* Find the /images node */
+	images_offset = fdt_path_offset(info->header, "/images");
+	if (images_offset < 0) {
+		dev_err(dev, "could not find /images in FPGA image header\n");
+		return -EINVAL;
+	}
+
+	/* Find the first fpga and flat_dt sections, parse them. */
+	ndepth = 0;
+	noffset = fdt_next_node(info->header, images_offset, &ndepth);
+	for (count = 0;	(noffset >= 0) && (ndepth > 0);
+	     noffset = fdt_next_node(info->header, noffset, &ndepth)) {
+		if (ndepth != 1)
+			continue;
+
+		type_prop = fdt_getprop(info->header, noffset, "type", NULL);
+
+		if (!strcmp(type_prop, "fpga")) {
+			ret = region_parse_section_fpga(region, noffset);
+		} else if (!strcmp(type_prop, "flat_dt")) {
+			ret = region_parse_section_flat_dt(region, noffset);
+		} else {
+			dev_err(dev,
+				"Unsupported FPGA image header node type (%s)\n",
+				type_prop);
+			return -EINVAL;
+		}
+
+		count++;
+	}
+
+	return 0;
+}
+
+/**
+ * fpga_region_parse_header - parse FPGA image header
+ *
+ * FPGA image is in region->info's sg list, contiguous buffer
+ * (info->header), or firmware.
+ *
+ * @region: FPGA region
+ *
+ * Parse the FPGA stream header, leave the results in the image info.
+ *
+ * Return: 0 for success or negative error code.
+ */
+static int fpga_region_parse_header(struct fpga_region *region)
+{
+	struct device *dev = &region->dev;
+	struct fpga_image_info *info = region->info;
+	int ret;
+
+	if (!info->header) {
+		if (info->sgt) {
+			/* todo */
+		} else if (info->fw) {
+			info->header = (char *)info->fw->data;
+		} else {
+			dev_err(dev, "No header\n");
+			return -EINVAL;
+		}
+	}
+
+	ret = fpga_region_parse_images(region);
+
+	return ret;
+}
+
 /**
  * fpga_region_program_fpga - program FPGA
  * @region: FPGA region
@@ -103,6 +422,9 @@ int fpga_region_program_fpga(struct fpga_region *region)
 	struct fpga_image_info *info = region->info;
 	int ret;
 
+	if (!region->info)
+		return -EINVAL;
+
 	region = fpga_region_get(region);
 	if (IS_ERR(region)) {
 		dev_err(dev, "failed to get FPGA region\n");
@@ -182,6 +504,120 @@ void fpga_region_free(struct fpga_region *region)
 }
 EXPORT_SYMBOL_GPL(fpga_region_free);
 
+/* Region's image info has sg/buf/firmware.  Parse it, apply it */
+int fpga_region_fdt_image_apply(struct fpga_region *region)
+{
+	struct device *dev = &region->dev;
+	struct fpga_image_info *info = region->info;
+	const struct firmware *fw;
+	int ret;
+
+	if (info->firmware_name) {
+		ret = request_firmware(&fw, info->firmware_name, dev);
+		if (ret)
+			return ret;
+		info->fw = fw;
+	}
+
+	ret = fpga_region_parse_header(region);
+	if (ret)
+		goto out_rel;
+
+	if (info->buf) {
+		ret = fpga_region_program_fpga(region);
+		if (ret)
+			goto out_rel;
+	}
+
+	if (info->fdt_blob) {
+		ret = of_fpga_region_overlay_apply(region);
+		if (ret) {
+			dev_err(dev, "failed to apply fdt in bitstream\n");
+			devm_kfree(dev, info->fdt_blob);
+			info->fdt_blob = NULL;
+		}
+	}
+
+out_rel:
+	if (info->fw) {
+		release_firmware(fw);
+		info->fw = NULL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(fpga_region_fdt_image_apply);
+
+#if IS_ENABLED(CONFIG_FPGA_REGION_SYSFS)
+
+static ssize_t firmware_name_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct fpga_region *region = to_fpga_region(dev);
+	struct fpga_image_info *info = region->info;
+
+	if (info && info->firmware_name)
+		return sprintf(buf, "%s\n", info->firmware_name);
+
+	return 0;
+}
+
+static ssize_t firmware_name_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	struct fpga_region *region = to_fpga_region(dev);
+	struct fpga_image_info *info;
+	char *firmware_name;
+	int ret;
+
+	firmware_name = devm_kzalloc(dev, count, GFP_KERNEL);
+	if (!firmware_name)
+		return -ENOMEM;
+
+	ret = sscanf(buf, "%s", firmware_name);
+	if (ret != 1) {
+		devm_kfree(dev, firmware_name);
+		return -EINVAL;
+	}
+
+	/* If empty string input, free up the region */
+	if (!strnlen(firmware_name, count)) {
+		fpga_region_overlay_rm(region);
+		fpga_region_free(region);
+		devm_kfree(dev, firmware_name);
+		return count;
+	}
+
+	/* Must free up region if programmed */
+	if (region->info) {
+		dev_err(dev, "region already in use\n");
+		return -EINVAL;
+	}
+
+	info = fpga_image_info_alloc(dev);
+	if (!info)
+		return -ENOMEM;
+
+	info->firmware_name = firmware_name;
+	region->info = info;
+	ret = fpga_region_fdt_image_apply(region);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static DEVICE_ATTR_RW(firmware_name);
+
+static struct attribute *fpga_region_attrs[] = {
+	&dev_attr_firmware_name.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(fpga_region);
+
+#endif /* CONFIG_FPGA_REGION_SYSFS */
+
 int fpga_region_register(struct device *dev, struct fpga_region *region)
 {
 	int id, ret = 0;
@@ -242,6 +678,10 @@ static int __init fpga_region_init(void)
 
 	fpga_region_class->dev_release = fpga_region_dev_release;
 
+#if IS_ENABLED(CONFIG_FPGA_REGION_SYSFS)
+	fpga_region_class->dev_groups = fpga_region_groups;
+#endif /* CONFIG_FPGA_REGION_SYSFS */
+
 	return 0;
 }
 
diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c
index 019ca37..034cd9d 100644
--- a/drivers/fpga/of-fpga-region.c
+++ b/drivers/fpga/of-fpga-region.c
@@ -375,6 +375,13 @@ static int of_fpga_region_notify(struct notifier_block *nb,
 	if (!region)
 		return NOTIFY_OK;
 
+	/*
+	 * If notification is because bitstream had a fdt in the header,
+	 * silently ignore notification.
+	 */
+	if (region->info && region->info->fdt_blob)
+		return NOTIFY_OK;
+
 	ret = 0;
 	switch (action) {
 	case OF_OVERLAY_PRE_APPLY:
diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
index ddc56c0..fb91154 100644
--- a/include/linux/fpga/fpga-mgr.h
+++ b/include/linux/fpga/fpga-mgr.h
@@ -16,6 +16,7 @@
  * You should have received a copy of the GNU General Public License along with
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
+#include <linux/firmware.h>
 #include <linux/mutex.h>
 #include <linux/platform_device.h>
 
@@ -86,10 +87,14 @@ 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
+ * @fw: FPGA image firmware
  * @sgt: scatter/gather table containing FPGA image
  * @buf: contiguous buffer containing FPGA image
  * @count: size of buf
+ * @fdt_blob: fdt from FPGA image header
+ * @header: header
  * @overlay: Device Tree overlay
+ * @ov_id: id of applied overlay
  */
 struct fpga_image_info {
 	struct device *dev;
@@ -98,11 +103,15 @@ struct fpga_image_info {
 	u32 disable_timeout_us;
 	u32 config_complete_timeout_us;
 	char *firmware_name;
+	const struct firmware *fw;
 	struct sg_table *sgt;
 	const char *buf;
 	size_t count;
+	char *fdt_blob;
+	char *header;
 #ifdef CONFIG_OF
 	struct device_node *overlay;
+	int ov_id;
 #endif
 };
 
diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h
index 3b6f0b0..3839430 100644
--- a/include/linux/fpga/fpga-region.h
+++ b/include/linux/fpga/fpga-region.h
@@ -33,6 +33,7 @@ struct fpga_region *fpga_region_class_find(
 
 int fpga_region_program_fpga(struct fpga_region *region);
 void fpga_region_free(struct fpga_region *region);
+int fpga_region_fdt_image_apply(struct fpga_region *region);
 
 int fpga_region_register(struct device *dev, struct fpga_region *region);
 int fpga_region_unregister(struct fpga_region *region);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2017-07-24 19:50 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-24 19:49 [RFC 0/5] Open source FPGA image header Alan Tull
2017-07-24 19:49 ` [RFC 1/5] doc: fpga: add document for the fdt FPGA header Alan Tull
2017-07-24 19:49 ` [RFC 2/5] doc: fpga: add sysfs document for fpga region Alan Tull
2017-07-24 19:49 ` [RFC 3/5] fpga: add dev to fpga_image_info Alan Tull
2017-07-24 19:49 ` [RFC 4/5] fpga-region: new function fpga_region_free Alan Tull
2017-07-24 19:49 ` [RFC 5/5] fpga-region support for fdt headers on fpga images Alan Tull

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).