[RFC 1/1] of-fpga-region: Add sysfs interface support for FPGA configuration

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Adds sysfs interface as part of the of-fpga-region. This newly added
sysfs interface uses Device Tree Overlay (DTO) files to configure/reprogram
an FPGA while an operating system is running.This solution will not change
the existing sequence When a DT overlay that targets an FPGA Region is
applied.
	- Disable appropriate FPGA bridges.
	- Program the FPGA using the FPGA manager.
	- Enable the FPGA bridges.
	- The Device Tree overlay is accepted into the live tree.
	- Child devices are populated.

When the overlay is removed, the child nodes will be removed, and the FPGA
Region will disable the bridges.

Usage:
To configure/reprogram an FPGA region:
echo "fpga.dtbo" > /sys/class/fpga_region/<region>/device/load

To remove an FPGA region:
echo "fpga.dtbo" > /sys/class/fpga_region/<region>/device/remove

To get an FPGA region status:
cat /sys/class/fpga_region/<region>/device/status

Signed-off-by: Nava kishore Manne <nava.kishore.manne@xxxxxxx>
---
 .../ABI/testing/sysfs-class-of-fpga-region    | 30 ++++++
 MAINTAINERS                                   |  1 +
 drivers/fpga/fpga-region.c                    |  4 +-
 drivers/fpga/of-fpga-region.c                 | 92 +++++++++++++++++++
 include/linux/fpga/fpga-region.h              | 15 +++
 5 files changed, 141 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-of-fpga-region

diff --git a/Documentation/ABI/testing/sysfs-class-of-fpga-region b/Documentation/ABI/testing/sysfs-class-of-fpga-region
new file mode 100644
index 000000000000..aeb4e3be4ff3
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-of-fpga-region
@@ -0,0 +1,30 @@
+What:		/sys/class/fpga_region/<region>/device/load
+Date:		July 2024
+KernelVersion:	6.10
+Contact:	Nava kishore Manne <nava.kishore.manne@xxxxxxx>
+Description:	(WO) Configure/Reprogram an FPGA region.
+		It uses Device Tree Overlay (DTO) files to configurer (or)
+		reprogram an FPGA. While an operating system is running.
+		The bitstream and the relevant DTO file has to be located
+		on the appropriate firmware path, typically, /lib/firmware.
+		For example, when user pass the option "echo fpga.dtbo"	the
+		file /lib/firmware/fpga.dtbo must be present.
+
+What:		/sys/class/fpga_region/<region>/device/remove
+Date:		July 2024
+KernelVersion:	6.10
+Contact:	Nava kishore Manne <nava.kishore.manne@xxxxxxx>
+Description:	(WO) Revert the changes added by the load interface
+		It revert and free an overlay changeset added by the load
+		interface.
+
+What:		/sys/class/fpga_region/<region>/device/status
+Date:		July 2024
+KernelVersion:	6.10
+Contact:	Nava kishore Manne <nava.kishore.manne@xxxxxxx>
+Description:	(RO) Status of the FPGA region
+		This file is used to check the status of the FPGA region.
+		This is a list of strings for the supported status.
+
+		* applied	= FPGA is programmed and operating
+		* unapplied	= Error while programing the FPGA
diff --git a/MAINTAINERS b/MAINTAINERS
index c0a3d9e93689..384f1d6f3af9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8795,6 +8795,7 @@ L:	linux-fpga@xxxxxxxxxxxxxxx
 S:	Maintained
 Q:	http://patchwork.kernel.org/project/linux-fpga/list/
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/fpga/linux-fpga.git
+F:	Documentation/ABI/testing/sysfs-class-of-fpga-region
 F:	Documentation/devicetree/bindings/fpga/
 F:	Documentation/driver-api/fpga/
 F:	Documentation/fpga/
diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c
index 753cd142503e..0733db1347ea 100644
--- a/drivers/fpga/fpga-region.c
+++ b/drivers/fpga/fpga-region.c
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
+#include <linux/of.h>
 
 static DEFINE_IDA(fpga_region_ida);
 static const struct class fpga_region_class;
@@ -192,6 +193,7 @@ struct fpga_region *
 __fpga_region_register_full(struct device *parent, const struct fpga_region_info *info,
 			    struct module *owner)
 {
+	struct device_node *np = parent->of_node;
 	struct fpga_region *region;
 	int id, ret = 0;
 
@@ -225,7 +227,7 @@ __fpga_region_register_full(struct device *parent, const struct fpga_region_info
 	region->dev.of_node = parent->of_node;
 	region->dev.id = id;
 
-	ret = dev_set_name(&region->dev, "region%d", id);
+	ret = dev_set_name(&region->dev, "%s", of_node_full_name(np));
 	if (ret)
 		goto err_remove;
 
diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c
index 8526a5a86f0c..edcc4c23a4b4 100644
--- a/drivers/fpga/of-fpga-region.c
+++ b/drivers/fpga/of-fpga-region.c
@@ -5,6 +5,7 @@
  *  Copyright (C) 2013-2016 Altera Corporation
  *  Copyright (C) 2017 Intel Corporation
  */
+#include <linux/firmware.h>
 #include <linux/fpga/fpga-bridge.h>
 #include <linux/fpga/fpga-mgr.h>
 #include <linux/fpga/fpga-region.h>
@@ -347,6 +348,7 @@ static int of_fpga_region_notify(struct notifier_block *nb,
 				 unsigned long action, void *arg)
 {
 	struct of_overlay_notify_data *nd = arg;
+	struct fpga_overlay_image_info *ovcs;
 	struct fpga_region *region;
 	int ret;
 
@@ -371,6 +373,10 @@ static int of_fpga_region_notify(struct notifier_block *nb,
 	if (!region)
 		return NOTIFY_OK;
 
+	ovcs = &region->ovcs;
+	if (!ovcs->fw)
+		return NOTIFY_STOP;
+
 	ret = 0;
 	switch (action) {
 	case OF_OVERLAY_PRE_APPLY:
@@ -394,6 +400,91 @@ static struct notifier_block fpga_region_of_nb = {
 	.notifier_call = of_fpga_region_notify,
 };
 
+static ssize_t load_store(struct device *dev, struct device_attribute *attr,
+			  const char *buf, size_t count)
+{
+	struct fpga_region *region = to_fpga_region(dev);
+	struct fpga_overlay_image_info *ovcs = &region->ovcs;
+	char *s;
+	int err;
+
+	/* if it's set do not allow changes */
+	if (ovcs->ovcs_id)
+		return -EPERM;
+
+	/* copy to path buffer (and make sure it's always zero terminated */
+	count = snprintf(ovcs->path, sizeof(ovcs->path) - 1, "%s", buf);
+	ovcs->path[sizeof(ovcs->path) - 1] = '\0';
+
+	/* strip trailing newlines */
+	s = ovcs->path + strlen(ovcs->path);
+	while (s > ovcs->path && *--s == '\n')
+		*s = '\0';
+
+	err = request_firmware(&ovcs->fw, ovcs->path, NULL);
+	if (err != 0)
+		goto out_err;
+
+	err = of_overlay_fdt_apply((void *)ovcs->fw->data, ovcs->fw->size,
+				   &ovcs->ovcs_id, NULL);
+	if (err < 0) {
+		pr_err("%s: Failed to create overlay (err=%d)\n",
+		       __func__, err);
+		release_firmware(ovcs->fw);
+		goto out_err;
+	}
+
+	return count;
+out_err:
+	ovcs->path[0] = '\0';
+	ovcs->ovcs_id = 0;
+	ovcs->fw = NULL;
+
+	return err;
+}
+
+static ssize_t remove_store(struct device *dev, struct device_attribute *attr,
+			    const char *buf, size_t count)
+{
+	struct fpga_region *region = to_fpga_region(dev);
+	struct fpga_overlay_image_info *ovcs = &region->ovcs;
+
+	if (!ovcs->ovcs_id)
+		return -EPERM;
+
+	of_overlay_remove(&ovcs->ovcs_id);
+	release_firmware(ovcs->fw);
+
+	ovcs->path[0] = '\0';
+	ovcs->ovcs_id = 0;
+	ovcs->fw = NULL;
+
+	return count;
+}
+
+static ssize_t status_show(struct device *dev,
+			   struct device_attribute *attr, char *buf)
+{
+	struct fpga_region *region = to_fpga_region(dev);
+	struct fpga_overlay_image_info *ovcs = &region->ovcs;
+
+	return sprintf(buf, "%s\n", ovcs->ovcs_id > 0 ?
+		       "applied" : "unapplied");
+}
+
+static DEVICE_ATTR_WO(load);
+static DEVICE_ATTR_WO(remove);
+static DEVICE_ATTR_RO(status);
+
+static struct attribute *of_fpga_region_attrs[] = {
+	&dev_attr_load.attr,
+	&dev_attr_remove.attr,
+	&dev_attr_status.attr,
+	NULL,
+};
+
+ATTRIBUTE_GROUPS(of_fpga_region);
+
 static int of_fpga_region_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -440,6 +531,7 @@ static struct platform_driver of_fpga_region_driver = {
 	.driver = {
 		.name	= "of-fpga-region",
 		.of_match_table = of_match_ptr(fpga_region_of_match),
+		.dev_groups = of_fpga_region_groups,
 	},
 };
 
diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h
index 5fbc05fe70a6..36143301b49b 100644
--- a/include/linux/fpga/fpga-region.h
+++ b/include/linux/fpga/fpga-region.h
@@ -9,6 +9,19 @@
 
 struct fpga_region;
 
+/**
+ * struct fpga_overlay_image_info - information specific to an FPGA Overlay
+ * image.
+ * @fw: firmware of coeff table.
+ * @path: path of FPGA overlay image firmware file.
+ * @ovcs_id: overlay changeset id.
+ */
+struct fpga_overlay_image_info {
+	const struct firmware *fw;
+	char path[PATH_MAX];
+	int ovcs_id;
+};
+
 /**
  * struct fpga_region_info - collection of parameters an FPGA Region
  * @mgr: fpga region manager
@@ -37,6 +50,7 @@ struct fpga_region_info {
  * @info: FPGA image info
  * @compat_id: FPGA region id for compatibility check.
  * @ops_owner: module containing the get_bridges function
+ * @ovcs: FPGA overlay image info
  * @priv: private data
  * @get_bridges: optional function to get bridges to a list
  */
@@ -48,6 +62,7 @@ struct fpga_region {
 	struct fpga_image_info *info;
 	struct fpga_compat_id *compat_id;
 	struct module *ops_owner;
+	struct fpga_overlay_image_info ovcs;
 	void *priv;
 	int (*get_bridges)(struct fpga_region *region);
 };
-- 
2.34.1





[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux