Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753041Ab3IRTPq (ORCPT ); Wed, 18 Sep 2013 15:15:46 -0400 Received: from mho-02-ewr.mailhop.org ([204.13.248.72]:61523 "EHLO mho-02-ewr.mailhop.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751047Ab3IRTPp (ORCPT ); Wed, 18 Sep 2013 15:15:45 -0400 X-Mail-Handler: Dyn Standard SMTP by Dyn X-Originating-IP: 108.39.110.144 X-Report-Abuse-To: abuse@dyndns.com (see http://www.dyndns.com/services/sendlabs/outbound_abuse.html for abuse reporting information) X-MHO-User: U2FsdGVkX1/HxhzOlvnawZJoRvNlG0v4ERyf6T4W8Us= Date: Wed, 18 Sep 2013 15:15:17 -0400 From: Jason Cooper To: Michal Simek Cc: linux-kernel@vger.kernel.org, monstr@monstr.eu, Alan Tull , Pavel Machek , Greg Kroah-Hartman , Dinh Nguyen , Philip Balister , Alessandro Rubini , Mauro Carvalho Chehab , Andrew Morton , Cesar Eduardo Barros , Joe Perches , "David S. Miller" , Stephen Warren , Arnd Bergmann , David Brown , Dom Cobley , Jason Gunthorpe Subject: Re: [RFC PATCH] fpga: Introduce new fpga subsystem Message-ID: <20130918191517.GQ19937@titan.lakedaemon.net> References: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.20 (2009-06-14) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 20073 Lines: 660 + Jason Gunthorpe On Wed, Sep 18, 2013 at 05:56:39PM +0200, Michal Simek wrote: > This new subsystem should unify all fpga drivers which > do the same things. Load configuration data to fpga > or another programmable logic through common interface. > It doesn't matter if it is MMIO device, gpio bitbanging, > etc. connection. The point is to have the same > inteface for these drivers. > > Signed-off-by: Michal Simek > > --- > MAINTAINERS | 7 + > drivers/Kconfig | 2 + > drivers/Makefile | 1 + > drivers/fpga/Kconfig | 18 ++ > drivers/fpga/Makefile | 5 + > drivers/fpga/fpga-mgr.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++++ > include/linux/fpga.h | 105 ++++++++++++ > 7 files changed, 571 insertions(+) > create mode 100644 drivers/fpga/Kconfig > create mode 100644 drivers/fpga/Makefile > create mode 100644 drivers/fpga/fpga-mgr.c > create mode 100644 include/linux/fpga.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index e61c2e8..5c7597b 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -3427,6 +3427,13 @@ F: include/linux/fmc*.h > F: include/linux/ipmi-fru.h > K: fmc_d.*register > > +FPGA SUBSYSTEM > +M: Michal Simek > +T: git git://git.xilinx.com/linux-xlnx.git > +S: Supported > +F: drivers/fpga/ > +F: include/linux/fpga.h > + > FPU EMULATOR > M: Bill Metzenthen > W: http://floatingpoint.sourceforge.net/emulator/index.html > diff --git a/drivers/Kconfig b/drivers/Kconfig > index aa43b91..17f3caa 100644 > --- a/drivers/Kconfig > +++ b/drivers/Kconfig > @@ -166,4 +166,6 @@ source "drivers/reset/Kconfig" > > source "drivers/fmc/Kconfig" > > +source "drivers/fpga/Kconfig" > + > endmenu > diff --git a/drivers/Makefile b/drivers/Makefile > index ab93de8..2b5e73b 100644 > --- a/drivers/Makefile > +++ b/drivers/Makefile > @@ -152,3 +152,4 @@ obj-$(CONFIG_VME_BUS) += vme/ > obj-$(CONFIG_IPACK_BUS) += ipack/ > obj-$(CONFIG_NTB) += ntb/ > obj-$(CONFIG_FMC) += fmc/ > +obj-$(CONFIG_FPGA) += fpga/ > diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig > new file mode 100644 > index 0000000..5357645 > --- /dev/null > +++ b/drivers/fpga/Kconfig > @@ -0,0 +1,18 @@ > +# > +# FPGA framework configuration > +# > + > +menu "FPGA devices" > + > +config FPGA > + tristate "FPGA Framework" > + help > + Say Y here if you want support for configuring FPGAs from the > + kernel. The FPGA framework adds a FPGA manager class and FPGA > + manager drivers. > + > +if FPGA > + > +endif > + > +endmenu > diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile > new file mode 100644 > index 0000000..3c7f29b > --- /dev/null > +++ b/drivers/fpga/Makefile > @@ -0,0 +1,5 @@ > +# > +# Makefile for the fpga framework and fpga manager drivers. > +# > + > +obj-$(CONFIG_FPGA) += fpga-mgr.o > diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c > new file mode 100644 > index 0000000..7312efd > --- /dev/null > +++ b/drivers/fpga/fpga-mgr.c > @@ -0,0 +1,433 @@ > +/* > + * FPGA Framework > + * > + * Copyright (C) 2013 Xilinx, Inc. > + * > + * based on origin code from > + * Copyright (C) 2013 Altera Corporation > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program. If not, see . > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +static struct idr fpga_mgr_idr; > +static spinlock_t fpga_mgr_idr_lock; > +static struct class *fpga_mgr_class; > + > +/** > + * fpga_mgr_name_show - Show fpga manager name > + * @dev: Pointer to the device structure > + * @attr: Pointer to the device attribute structure > + * @buf: Pointer to the buffer location > + * > + * Returns the number of bytes copied to @buf, a negative error number otherwise > + */ > +static ssize_t fpga_mgr_name_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct fpga_manager *mgr = dev_get_drvdata(dev); > + > + if (!mgr) > + return -ENODEV; > + > + return snprintf(buf, sizeof(mgr->name), "%s\n", mgr->name); > +} > + > +/** > + * fpga_mgr_status_show - Show fpga manager status > + * @dev: Pointer to the device structure > + * @attr: Pointer to the device attribute structure > + * @buf: Pointer to the buffer location > + * > + * Returns the number of bytes copied to @buf, a negative error number otherwise > + */ > +static ssize_t fpga_mgr_status_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct fpga_manager *mgr = dev_get_drvdata(dev); > + > + if (!mgr || !mgr->mops || !mgr->mops->status) > + return -ENODEV; > + > + return mgr->mops->status(mgr, buf); > +} > + > +/** > + * fpga_mgr_read - Read data from fpga > + * @mgr: Pointer to the fpga manager structure > + * @buf: Pointer to the buffer location > + * @count: Pointer to the number of copied bytes > + * > + * Function reads fpga bitstream and copy them to output buffer. > + * > + * Returns the number of bytes copied to @buf, a negative error number otherwise > + */ > +static int fpga_mgr_read(struct fpga_manager *mgr, char *buf, ssize_t *count) > +{ > + int ret; > + > + if (test_and_set_bit_lock(FPGA_MGR_DEV_BUSY, &mgr->flags)) > + return -EBUSY; > + > + if (!mgr->mops || !mgr->mops->read) { > + dev_err(mgr->dev, > + "Controller doesn't support read operations!\n"); > + return -EPERM; > + } > + > + if (mgr->mops->read_init) { > + ret = mgr->mops->read_init(mgr); > + if (ret) { > + dev_err(mgr->dev, "Failed in read-init!\n"); > + return ret; > + } > + } > + > + if (mgr->mops->read) { > + ret = mgr->mops->read(mgr, buf, count); > + if (ret) { > + dev_err(mgr->dev, "Failed to read firmware!\n"); > + return ret; > + } > + } > + > + if (mgr->mops->read_complete) { > + ret = mgr->mops->read_complete(mgr); > + if (ret) { > + dev_err(mgr->dev, "Failed in read-complete!\n"); > + return ret; > + } > + } > + > + clear_bit_unlock(FPGA_MGR_DEV_BUSY, &mgr->flags); > + > + return 0; > +} > + > +/** > + * fpga_mgr_attr_read - Read data from fpga > + * @dev: Pointer to the device structure > + * @attr: Pointer to the device attribute structure > + * @buf: Pointer to the buffer location > + * > + * Function reads fpga bitstream and copy them to output buffer > + * > + * Returns the number of bytes copied to @buf, a negative error number otherwise > + */ > +static ssize_t fpga_mgr_attr_read(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct fpga_manager *mgr = dev_get_drvdata(dev); > + ssize_t count; > + int ret; > + > + if (mgr && mgr->fpga_read) > + ret = mgr->fpga_read(mgr, buf, &count); > + > + return ret == 0 ? count : -EPERM; > +} > + > +/** > + * fpga_mgr_write - Write data to fpga > + * @mgr: Pointer to the fpga manager structure > + * @fw_name: Pointer to the buffer location with bistream firmware filename > + * > + * @buf contains firmware filename which is loading through firmware > + * interface and passed to the fpga driver. > + * > + * Returns string lenght added to @fw_name, a negative error number otherwise > + */ > +static int fpga_mgr_write(struct fpga_manager *mgr, const char *fw_name) > +{ > + int ret = 0; > + const struct firmware *fw; > + > + if (!fw_name || !strlen(fw_name)) { > + dev_err(mgr->dev, "Firmware name is not specified!\n"); > + return -EINVAL; > + } > + > + if (!mgr->mops || !mgr->mops->write) { > + dev_err(mgr->dev, > + "Controller doesn't support write operations!\n"); > + return -EPERM; > + } > + > + ret = request_firmware(&fw, fw_name, mgr->dev); > + if (ret) { > + dev_err(mgr->dev, "Failed to load firmware %s!\n", fw_name); > + return ret; > + } > + > + if (test_and_set_bit_lock(FPGA_MGR_DEV_BUSY, &mgr->flags)) > + return -EBUSY; > + > + /* Init controller */ > + if (mgr->mops->write_init) { > + ret = mgr->mops->write_init(mgr); > + if (ret) { > + dev_err(mgr->dev, "Failed to write_init!\n"); > + return ret; > + } > + } > + > + /* Do write */ > + ret = mgr->mops->write(mgr, (void *)fw->data, fw->size); > + if (ret) { > + dev_err(mgr->dev, "Failed to write data!\n"); > + return ret; > + } > + > + if (mgr->mops->write_complete) { > + ret = mgr->mops->write_complete(mgr); > + if (ret) { > + dev_err(mgr->dev, "Failed to write_init!\n"); > + return ret; > + } > + } > + > + release_firmware(fw); > + > + clear_bit_unlock(FPGA_MGR_DEV_BUSY, &mgr->flags); > + > + return 0; > +} > + > +/** > + * fpga_mgr_attr_write - Write data to fpga > + * @dev: Pointer to the device structure > + * @attr: Pointer to the device attribute structure > + * @buf: Pointer to the buffer location with bistream firmware filename > + * @count: Number of characters in @buf > + * > + * @buf contains firmware filename which is loading through firmware > + * interface and passed to the fpga driver. > + * > + * Returns string lenght added to @buf, a negative error number otherwise > + */ > +static ssize_t fpga_mgr_attr_write(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct fpga_manager *mgr = dev_get_drvdata(dev); > + int ret; > + > + if (mgr && mgr->fpga_write) > + ret = mgr->fpga_write(mgr, buf); > + > + return ret == 0 ? strlen(buf) : -EPERM; > +} > + > +static struct device_attribute fpga_mgr_attrs[] = { > + __ATTR(name, S_IRUGO, fpga_mgr_name_show, NULL), > + __ATTR(status, S_IRUGO, fpga_mgr_status_show, NULL), > + __ATTR(fpga, S_IRUGO | S_IWUGO, fpga_mgr_attr_read, > + fpga_mgr_attr_write), > + __ATTR_NULL > +}; > + > +/** > + * fpga_mgr_register: Register new fpga manager > + * @pdev: Pointer to the platform device of fpga manager > + * @mops: Pointer to the fpga manager operations > + * @name: Name of fpga manager > + * @priv: Pointer to the fpga manager private data > + * > + * Function register fpga manager with uniq ID and create device > + * for accessing the device. > + * > + * Returns 0 on success, a negative error number otherwise > + */ > +int fpga_mgr_register(struct platform_device *pdev, > + struct fpga_manager_ops *mops, char *name, void *priv) > +{ > + struct fpga_manager *mgr; > + int ret; > + > + if (!mops) { > + dev_err(&pdev->dev, "Register failed: NO fpga_manager_ops!\n"); > + return -EINVAL; > + } > + > + if (!name || !strlen(name)) { > + dev_err(&pdev->dev, "Register failed: NO name specific!\n"); > + return -EINVAL; > + } > + > + mgr = devm_kzalloc(&pdev->dev, sizeof(*mgr), GFP_KERNEL); > + if (!mgr) { > + dev_err(&pdev->dev, "Failed to alloc mgr struct!\n"); > + return -ENOMEM; > + } > + > + /* Get unique number */ > + idr_preload(GFP_KERNEL); > + spin_lock(&fpga_mgr_idr_lock); > + ret = idr_alloc(&fpga_mgr_idr, mgr, 0, 0, GFP_NOWAIT); > + if (ret >= 0) > + mgr->nr = ret; > + spin_unlock(&fpga_mgr_idr_lock); > + idr_preload_end(); > + if (ret < 0) > + return ret; > + > + /* Setup all necessary information */ > + mgr->dev = &pdev->dev; > + mgr->mops = mops; > + mgr->priv = priv; > + mgr->fpga_read = fpga_mgr_read; > + mgr->fpga_write = fpga_mgr_write; > + strlcpy(mgr->name, name, sizeof(mgr->name)); > + > + mgr->dev = device_create(fpga_mgr_class, get_device(&pdev->dev), > + MKDEV(0, 0), mgr, "fpga%d", mgr->nr); > + if (IS_ERR(mgr->dev)) { > + spin_lock(&fpga_mgr_idr_lock); > + idr_remove(&fpga_mgr_idr, mgr->nr); > + spin_unlock(&fpga_mgr_idr_lock); > + > + dev_err(&pdev->dev, "Failed to create device!\n"); > + return PTR_ERR(mgr->dev); > + } > + > + platform_set_drvdata(pdev, mgr); > + > + dev_info(&pdev->dev, "Fpga manager [%s] registered as minor %d\n", > + mgr->name, mgr->nr); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(fpga_mgr_register); > + > +/** > + * fpga_mgr_unregister: Remove fpga manager > + * @pdev: Pointer to the platform device of fpga manager > + * > + * Function unregister fpga manager and release all temporary structures > + * > + * Returns 0 for all cases > + */ > +int fpga_mgr_unregister(struct platform_device *pdev) > +{ > + struct fpga_manager *mgr = platform_get_drvdata(pdev); > + > + if (mgr && mgr->mops && mgr->mops->fpga_remove) > + mgr->mops->fpga_remove(mgr); > + > + device_unregister(mgr->dev); > + > + spin_lock(&fpga_mgr_idr_lock); > + idr_remove(&fpga_mgr_idr, mgr->nr); > + spin_unlock(&fpga_mgr_idr_lock); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(fpga_mgr_unregister); > + > +/** > + * of_dev_node_match: Compare associated device tree node with > + * @dev: Pointer to the device > + * @data: Pointer to the device tree node > + * > + * Returns 1 on success > + */ > +static int of_dev_node_match(struct device *dev, void *data) > +{ > + return dev->of_node == data; > +} > + > +/** > + * of_find_fpga_mgr_by_node - Find the fpga_manager associated with a node > + * @node: Pointer to the device tree node > + * > + * Returns fpga_manager pointer, or NULL if not found > + */ > +struct fpga_manager *of_find_fpga_mgr_by_node(struct device_node *node) > +{ > + struct device *dev; > + struct fpga_manager *mgr; > + > + dev = bus_find_device(&platform_bus_type, NULL, node, > + of_dev_node_match); > + if (!dev) > + return NULL; > + > + mgr = dev_get_drvdata(dev); > + > + return mgr ? mgr : NULL; > +} > +EXPORT_SYMBOL(of_find_fpga_mgr_by_node); > + > +/** > + * of_find_fpga_mgr_by_phandle - Find the fpga_manager associated with a node > + * @node: Pointer to the device tree node > + * @phandle_name: Pointer to the phandle_name > + * > + * Returns fpga_manager pointer, or NULL if not found > + */ > +struct fpga_manager *of_find_fpga_mgr_by_phandle(struct device_node *node, > + const char *phandle_name) > +{ > + struct device_node *fpga_node; > + > + fpga_node = of_parse_phandle(node, phandle_name, 0); > + if (!fpga_node) { > + pr_err("Phandle not found\n"); > + return ERR_PTR(-ENODEV); > + } > + > + return of_find_fpga_mgr_by_node(fpga_node); > +} > +EXPORT_SYMBOL(of_find_fpga_mgr_by_phandle); > + > +/** > + * fpga_mgr_init: Create fpga class > + */ > +static int __init fpga_mgr_init(void) > +{ > + pr_info("FPGA Manager framework driver\n"); > + > + fpga_mgr_class = class_create(THIS_MODULE, "fpga"); > + if (IS_ERR(fpga_mgr_class)) > + return PTR_ERR(fpga_mgr_class); > + > + idr_init(&fpga_mgr_idr); > + spin_lock_init(&fpga_mgr_idr_lock); > + > + fpga_mgr_class->dev_attrs = fpga_mgr_attrs; > + > + return 0; > +} > +subsys_initcall(fpga_mgr_init); > + > +/** > + * fpga_mgr_exit: Destroy fpga class > + */ > +static void __exit fpga_mgr_exit(void) > +{ > + class_destroy(fpga_mgr_class); > + idr_destroy(&fpga_mgr_idr); > +} > +module_exit(fpga_mgr_exit); > + > +MODULE_AUTHOR("Xilinx, Inc."); > +MODULE_DESCRIPTION("FPGA Manager framework driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/include/linux/fpga.h b/include/linux/fpga.h > new file mode 100644 > index 0000000..970c42e > --- /dev/null > +++ b/include/linux/fpga.h > @@ -0,0 +1,105 @@ > +/* > + * FPGA Framework > + * > + * Copyright (C) 2013 Xilinx, Inc. > + * > + * based on origin code from > + * Copyright (C) 2013 Altera Corporation > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program. If not, see . > + */ > + > +#include > +#include > +#include > + > +#ifndef _LINUX_FPGA_H > +#define _LINUX_FPGA_H > + > +struct fpga_manager; > + > +/** > + * struct fpga_manager_ops - FPGA manager driver operations > + * > + * @status: The function to call for getting fpga manager status. > + * Returns number of characters written to the @buf and > + * a string of the FPGA's status in @bug. > + * @read_init: The function to call for preparing the FPGA for reading > + * its confuration data. Returns 0 on success, a negative error > + * number otherwise. > + * @read: Read configuration data from the FPGA. Return 0 on success, > + * a negative error number otherwise. > + * @read_complete: Return FPGA to a default state after reading is done. > + * Return 0 on success, a negative error number otherwise. > + * @write_init: Prepare the FPGA to receive configuration data. > + * Return 0 on success, a negative error number otherwise. > + * @write: Write count bytes of configuration data to the FPGA placed in @buf. > + * Return 0 on success, a negative error number otherwise. > + * @write_complete: Return FPGA to default state after writing is done. > + * Return 0 on success, a negative error number otherwise. > + * @fpga_remove: Set FPGA into a specific state during driver remove. > + * > + * fpga_manager_ops are the low level functions implemented by a specific > + * fpga manager driver. Leaving any of these out that aren't needed is fine > + * as they are all tested for NULL before being called. > + */ > +struct fpga_manager_ops { > + int (*status)(struct fpga_manager *mgr, char *buf); > + int (*read_init)(struct fpga_manager *mgr); > + int (*read)(struct fpga_manager *mgr, char *buf, ssize_t *count); > + int (*read_complete)(struct fpga_manager *mgr); > + int (*write_init)(struct fpga_manager *mgr); > + int (*write)(struct fpga_manager *mgr, char *buf, ssize_t count); > + int (*write_complete)(struct fpga_manager *mgr); > + void (*fpga_remove)(struct fpga_manager *mgr); > +}; > + > +/* flag bits */ > +#define FPGA_MGR_DEV_BUSY 0 > + > +/** > + * struct fpga_manager - FPGA manager driver structure > + * > + * @name: Name of fpga manager > + * @dev: Pointer to the device structure > + * @mops: Pointer to the fpga manager operations > + * @priv: Pointer to fpga manager private data > + * @nr: Unique manager number in the system > + * @flags: For saving temporary > + * @fpga_read: The function to call for reading configuration data from > + * the FPGA. > + * @fpga_write: The function to call for writing configuration data to the FPGA. > + */ > +struct fpga_manager { > + char name[48]; > + struct device *dev; > + struct fpga_manager_ops *mops; > + void *priv; > + unsigned int nr; > + unsigned long flags; > + int (*fpga_read)(struct fpga_manager *mgr, char *buf, > + ssize_t *count); > + int (*fpga_write)(struct fpga_manager *mgr, const char *fw_name); > +}; > + > +int fpga_mgr_register(struct platform_device *pdev, > + struct fpga_manager_ops *mops, char *name, void *priv); > + > +int fpga_mgr_unregister(struct platform_device *pdev); > + > +struct fpga_manager *of_find_fpga_mgr_by_node(struct device_node *node); > +struct fpga_manager *of_find_fpga_mgr_by_phandle(struct device_node *node, > + const char *phandle_name); > + > +#endif /*_LINUX_FPGA_H */ > -- > 1.8.2.3 > -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/