From patchwork Wed Feb 15 16:14:20 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alan Tull X-Patchwork-Id: 9574399 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id BED2B60493 for ; Wed, 15 Feb 2017 16:15:56 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AD4502846B for ; Wed, 15 Feb 2017 16:15:56 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A24FD284B4; Wed, 15 Feb 2017 16:15:56 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1DC1F2846B for ; Wed, 15 Feb 2017 16:15:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751478AbdBOQO5 (ORCPT ); Wed, 15 Feb 2017 11:14:57 -0500 Received: from mail.kernel.org ([198.145.29.136]:51880 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751966AbdBOQOx (ORCPT ); Wed, 15 Feb 2017 11:14:53 -0500 Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 0012E20320; Wed, 15 Feb 2017 16:14:46 +0000 (UTC) Received: from localhost.localdomain (user-0ccsrjt.cable.mindspring.com [24.206.110.125]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 2DEF020306; Wed, 15 Feb 2017 16:14:45 +0000 (UTC) From: Alan Tull To: Moritz Fischer , Jason Gunthorpe Cc: Alan Tull , linux-kernel@vger.kernel.org, linux-fpga@vger.kernel.org Subject: [RFC 7/8] fpga-region: add sysfs interface Date: Wed, 15 Feb 2017 10:14:20 -0600 Message-Id: <1487175261-7051-8-git-send-email-atull@kernel.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1487175261-7051-1-git-send-email-atull@kernel.org> References: <1487175261-7051-1-git-send-email-atull@kernel.org> X-Virus-Scanned: ClamAV using ClamSMTP Sender: linux-fpga-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fpga@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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;