[RFC 7/8] fpga-region: add sysfs interface
From: Alan Tull
Date: Wed Feb 15 2017 - 11:15:47 EST
Add a sysfs interface to control programming FPGA.
Each fpga-region will get the following files which set values
in the fpga_image_info struct for that region. More files will
need to be added as fpga_image_info expands.
firmware_name
* writing a name of a FPGA image file to firmware_name causes the
FPGA region to write the FPGA
partial_config
* 0 : full reconfiguration
* 1 : partial reconfiguration
unfreeze_timeout
* Timeout for waiting for a freeze bridge to enable traffic
freeze_timeout
* Timeout for waiting for a freeze bridge to disable traffic
Signed-off-by: Alan Tull <atull@xxxxxxxxxx>
---
drivers/fpga/Kconfig | 8 ++
drivers/fpga/fpga-region.c | 241 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 249 insertions(+)
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index be9c23d..6455e02 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -21,6 +21,14 @@ 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 file for each
+ FPGA Region under /sys/class/fpga_region/ to show status and
+ control programming FPGA regions.
+
config OF_FPGA_REGION
tristate "FPGA Region Device Tree Overlay Support"
depends on FPGA_REGION
diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c
index 5690237..a63bc6c 100644
--- a/drivers/fpga/fpga-region.c
+++ b/drivers/fpga/fpga-region.c
@@ -105,6 +105,243 @@ EXPORT_SYMBOL_GPL(fpga_region_ovl_image_info);
#endif /* CONFIG_OF_FPGA_REGION */
+#if IS_ENABLED(CONFIG_FPGA_REGION_SYSFS)
+
+struct fpga_image_info *image_info_from_region(struct fpga_region *region)
+{
+ struct fpga_image_info *image_info;
+
+ /* If region has an overlay, display image_info from overlay. */
+ image_info = fpga_region_ovl_image_info(region);
+ if (!image_info)
+ image_info = region->image_info;
+
+ return image_info;
+}
+
+/*
+ * Controlling a region both by sysfs and by device tree overlays is
+ * not supported.
+ */
+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 *image_info;
+
+ image_info = image_info_from_region(region);
+
+ if (image_info && image_info->firmware_name)
+ return sprintf(buf, "%s\n", image_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);
+ char *firmware_name;
+ size_t len;
+ int ret;
+
+ /*
+ * Controlling a region both by sysfs and by device tree overlays is
+ * not supported.
+ */
+ if (fpga_region_ovl_image_info(region))
+ return -EINVAL;
+
+ if (!region->image_info) {
+ region->image_info = fpga_region_alloc_image_info(region);
+ if (!region->image_info)
+ return -ENOMEM;
+ }
+
+ firmware_name = devm_kzalloc(dev, count, GFP_KERNEL);
+ if (!firmware_name)
+ return -ENOMEM;
+ pr_err("count = %d\n", count);
+ /* lose terminating \n */
+ strcpy(firmware_name, buf);
+ len = strlen(firmware_name);
+ if (firmware_name[len - 1] == '\n')
+ firmware_name[len - 1] = 0;
+ if (firmware_name[0] == 0) {
+ devm_kfree(dev, firmware_name);
+ firmware_name = NULL;
+ }
+
+ /* Release previous firmware name (if any). Save current one. */
+ if (region->image_info->firmware_name)
+ devm_kfree(dev, region->image_info->firmware_name);
+ region->image_info->firmware_name = firmware_name;
+
+ if (firmware_name) {
+ ret = fpga_region_program_fpga(region, region->image_info);
+ if (ret)
+ dev_err(dev,
+ "FPGA programming failed with value %d\n", ret);
+ } else {
+ /*
+ * Writing null string to firmware_name will disable and put
+ * the bridges (if there were any bridges in the bridge list).
+ */
+ fpga_bridges_disable(®ion->bridge_list);
+ if (region->get_bridges)
+ fpga_bridges_put(®ion->bridge_list);
+ fpga_region_free_image_info(region, region->image_info);
+ region->image_info = NULL;
+ }
+
+ return count;
+}
+
+static ssize_t partial_config_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fpga_region *region = to_fpga_region(dev);
+ struct fpga_image_info *image_info;
+ int partial;
+
+ image_info = image_info_from_region(region);
+ if (!image_info)
+ return 0;
+
+ partial = !!(image_info->flags & FPGA_MGR_PARTIAL_RECONFIG);
+
+ return sprintf(buf, "%d\n", partial);
+}
+
+static ssize_t partial_config_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fpga_region *region = to_fpga_region(dev);
+ unsigned long val;
+ int ret;
+
+ if (fpga_region_ovl_image_info(region))
+ return -EINVAL;
+
+ if (!region->image_info) {
+ region->image_info = fpga_region_alloc_image_info(region);
+ if (!region->image_info)
+ return -ENOMEM;
+ }
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ if (val == 1)
+ region->image_info->flags |= FPGA_MGR_PARTIAL_RECONFIG;
+ else if (val == 0)
+ region->image_info->flags &= ~FPGA_MGR_PARTIAL_RECONFIG;
+ else
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t unfreeze_timeout_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fpga_region *region = to_fpga_region(dev);
+ struct fpga_image_info *image_info;
+
+ image_info = image_info_from_region(region);
+ if (!image_info)
+ return 0;
+
+ return sprintf(buf, "%d\n", image_info->enable_timeout_us);
+}
+
+static ssize_t unfreeze_timeout_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct fpga_region *region = to_fpga_region(dev);
+ unsigned long val;
+ int ret;
+
+ if (fpga_region_ovl_image_info(region))
+ return -EINVAL;
+
+ if (!region->image_info) {
+ region->image_info = fpga_region_alloc_image_info(region);
+ if (!region->image_info)
+ return -ENOMEM;
+ }
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ region->image_info->enable_timeout_us = val;
+
+ return count;
+}
+
+static ssize_t freeze_timeout_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fpga_region *region = to_fpga_region(dev);
+ struct fpga_image_info *image_info;
+
+ image_info = image_info_from_region(region);
+ if (!image_info)
+ return 0;
+
+ return sprintf(buf, "%d\n", image_info->disable_timeout_us);
+}
+
+static ssize_t freeze_timeout_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct fpga_region *region = to_fpga_region(dev);
+ unsigned long val;
+ int ret;
+
+ if (fpga_region_ovl_image_info(region))
+ return -EINVAL;
+
+ if (!region->image_info) {
+ region->image_info = fpga_region_alloc_image_info(region);
+ if (!region->image_info)
+ return -ENOMEM;
+ }
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ region->image_info->disable_timeout_us = val;
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(firmware_name);
+static DEVICE_ATTR_RW(partial_config);
+static DEVICE_ATTR_RW(unfreeze_timeout);
+static DEVICE_ATTR_RW(freeze_timeout);
+
+static struct attribute *fpga_region_attrs[] = {
+ &dev_attr_firmware_name.attr,
+ &dev_attr_partial_config.attr,
+ &dev_attr_unfreeze_timeout.attr,
+ &dev_attr_freeze_timeout.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(fpga_region);
+
+#endif /* CONFIG_FPGA_REGION_SYSFS */
+
/**
* fpga_region_get - get an exclusive reference to a fpga region
* @region: FPGA Region struct
@@ -288,6 +525,10 @@ static int __init fpga_region_init(void)
if (IS_ERR(fpga_region_class))
return PTR_ERR(fpga_region_class);
+#if IS_ENABLED(CONFIG_FPGA_REGION_SYSFS)
+ fpga_region_class->dev_groups = fpga_region_groups;
+#endif /* CONFIG_FPGA_REGION_SYSFS */
+
fpga_region_class->dev_release = fpga_region_dev_release;
return 0;
--
2.7.4