[RFC 1/1] of-fpga-region: Add sysfs interface support for FPGA configuration
From: Nava kishore Manne
Date: Fri Jul 26 2024 - 02:38:57 EST
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(®ion->dev, "region%d", id);
+ ret = dev_set_name(®ion->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 = ®ion->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 = ®ion->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 = ®ion->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 = ®ion->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