Message ID | 1525229431-3087-18-git-send-email-hao.wu@intel.com (mailing list archive) |
---|---|
State | Superseded, archived |
Headers | show |
On Tue, May 1, 2018 at 9:50 PM, Wu Hao <hao.wu@intel.com> wrote: Hi Hao, My patches that change the API for creating managers, bridges, and regions is in Greg's char-misc-next branch now, so that will hopefully be in v4.18. You'll need to adjust for that, but that will be minor. > From: Kang Luwei <luwei.kang@intel.com> > > Partial Reconfiguration (PR) is the most important function for FME. It > allows reconfiguration for given Port/Accelerated Function Unit (AFU). > > It creates platform devices for fpga-mgr, fpga-regions and fpga-bridges, > and invokes fpga-region's interface (fpga_region_program_fpga) for PR > operation once PR request received via ioctl. Below user space interface > is exposed by this sub feature. > > Ioctl interface: > * DFL_FPGA_FME_PORT_PR > Do partial reconfiguration per information from userspace, including > target port(AFU), buffer size and address info. It returns error code > to userspace if failed. For detailed PR error information, user needs > to read fpga-mgr's status sysfs interface. > > Signed-off-by: Tim Whisonant <tim.whisonant@intel.com> > Signed-off-by: Enno Luebbers <enno.luebbers@intel.com> > Signed-off-by: Shiva Rao <shiva.rao@intel.com> > Signed-off-by: Christopher Rauer <christopher.rauer@intel.com> > Signed-off-by: Kang Luwei <luwei.kang@intel.com> > Signed-off-by: Xiao Guangrong <guangrong.xiao@linux.intel.com> > Signed-off-by: Wu Hao <hao.wu@intel.com> Acked-by: Alan Tull <atull@kernel.org> > --- > v2: moved the code to drivers/fpga folder as suggested by Alan Tull. > switched to GPLv2 license. > removed status from FPGA_FME_PORT_PR ioctl data structure. > added platform devices creation for fpga-mgr/fpga-region/fpga-bridge. > switched to fpga-region interface fpga_region_program_fpga for PR. > fixed comments from Alan Tull on FPGA_MGR_PARTIAL_RECONFIG flag usage. > fixed kbuild warnings. > v3: rename driver files to dfl-fme-*. > rebase due to fpga APIs change. > replace bitfields. > switch to fpga_cdev_find_port to find port device. > v4: rebase and correct comments for some function. > fix SPDX license issue. > remove unnecessary input parameter for destroy_bridge/region function. > add dfl-fme-pr.h for PR sub feature data structure and registers. > v5: rebase due to DFL framework API naming changes. > improve naming for IOCTL API, functions and data structure. > defer finding port platform device to fme bridge. > pass mapped ioaddr to fme manager via pdata. > remove useless devm_kfree(). > --- > drivers/fpga/Makefile | 2 +- > drivers/fpga/dfl-fme-main.c | 43 +++- > drivers/fpga/dfl-fme-pr.c | 474 ++++++++++++++++++++++++++++++++++++++++++ > drivers/fpga/dfl-fme-pr.h | 84 ++++++++ > drivers/fpga/dfl-fme.h | 38 ++++ > include/uapi/linux/fpga-dfl.h | 28 +++ > 6 files changed, 667 insertions(+), 2 deletions(-) > create mode 100644 drivers/fpga/dfl-fme-pr.c > create mode 100644 drivers/fpga/dfl-fme-pr.h > create mode 100644 drivers/fpga/dfl-fme.h > > diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile > index fbd1c85..3c44fc9 100644 > --- a/drivers/fpga/Makefile > +++ b/drivers/fpga/Makefile > @@ -32,7 +32,7 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o > obj-$(CONFIG_FPGA_DFL) += dfl.o > obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o > > -dfl-fme-objs := dfl-fme-main.o > +dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o > > # Drivers for FPGAs which implement DFL > obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o > diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c > index 6a59c07..bcb9325 100644 > --- a/drivers/fpga/dfl-fme-main.c > +++ b/drivers/fpga/dfl-fme-main.c > @@ -19,6 +19,7 @@ > #include <linux/fpga-dfl.h> > > #include "dfl.h" > +#include "dfl-fme.h" > > static ssize_t ports_num_show(struct device *dev, > struct device_attribute *attr, char *buf) > @@ -113,6 +114,10 @@ static ssize_t bitstream_metadata_show(struct device *dev, > .ops = &fme_hdr_ops, > }, > { > + .id = FME_FEATURE_ID_PR_MGMT, > + .ops = &pr_mgmt_ops, > + }, > + { > .ops = NULL, > }, > }; > @@ -187,6 +192,35 @@ static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) > return -EINVAL; > } > > +static int fme_dev_init(struct platform_device *pdev) > +{ > + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); > + struct dfl_fme *fme; > + > + fme = devm_kzalloc(&pdev->dev, sizeof(*fme), GFP_KERNEL); > + if (!fme) > + return -ENOMEM; > + > + fme->pdata = pdata; > + > + mutex_lock(&pdata->lock); > + dfl_fpga_pdata_set_private(pdata, fme); > + mutex_unlock(&pdata->lock); > + > + return 0; > +} > + > +static void fme_dev_destroy(struct platform_device *pdev) > +{ > + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); > + struct dfl_fme *fme; > + > + mutex_lock(&pdata->lock); > + fme = dfl_fpga_pdata_get_private(pdata); > + dfl_fpga_pdata_set_private(pdata, NULL); > + mutex_unlock(&pdata->lock); > +} > + > static const struct file_operations fme_fops = { > .owner = THIS_MODULE, > .open = fme_open, > @@ -198,10 +232,14 @@ static int fme_probe(struct platform_device *pdev) > { > int ret; > > - ret = dfl_fpga_dev_feature_init(pdev, fme_feature_drvs); > + ret = fme_dev_init(pdev); > if (ret) > goto exit; > > + ret = dfl_fpga_dev_feature_init(pdev, fme_feature_drvs); > + if (ret) > + goto dev_destroy; > + > ret = dfl_fpga_register_dev_ops(pdev, &fme_fops, THIS_MODULE); > if (ret) > goto feature_uinit; > @@ -210,6 +248,8 @@ static int fme_probe(struct platform_device *pdev) > > feature_uinit: > dfl_fpga_dev_feature_uinit(pdev); > +dev_destroy: > + fme_dev_destroy(pdev); > exit: > return ret; > } > @@ -218,6 +258,7 @@ static int fme_remove(struct platform_device *pdev) > { > dfl_fpga_unregister_dev_ops(pdev); > dfl_fpga_dev_feature_uinit(pdev); > + fme_dev_destroy(pdev); > > return 0; > } > diff --git a/drivers/fpga/dfl-fme-pr.c b/drivers/fpga/dfl-fme-pr.c > new file mode 100644 > index 0000000..3d5f8b9 > --- /dev/null > +++ b/drivers/fpga/dfl-fme-pr.c > @@ -0,0 +1,474 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Driver for FPGA Management Engine (FME) Partial Reconfiguration > + * > + * Copyright (C) 2017 Intel Corporation, Inc. > + * > + * Authors: > + * Kang Luwei <luwei.kang@intel.com> > + * Xiao Guangrong <guangrong.xiao@linux.intel.com> > + * Wu Hao <hao.wu@intel.com> > + * Joseph Grecco <joe.grecco@intel.com> > + * Enno Luebbers <enno.luebbers@intel.com> > + * Tim Whisonant <tim.whisonant@intel.com> > + * Ananda Ravuri <ananda.ravuri@intel.com> > + * Christopher Rauer <christopher.rauer@intel.com> > + * Henry Mitchel <henry.mitchel@intel.com> > + */ > + > +#include <linux/types.h> > +#include <linux/device.h> > +#include <linux/vmalloc.h> > +#include <linux/uaccess.h> > +#include <linux/fpga/fpga-mgr.h> > +#include <linux/fpga/fpga-bridge.h> > +#include <linux/fpga/fpga-region.h> > +#include <linux/fpga-dfl.h> > + > +#include "dfl.h" > +#include "dfl-fme.h" > +#include "dfl-fme-pr.h" > + > +static struct dfl_fme_region * > +find_fme_region_by_port_id(struct dfl_fme *fme, int port_id) How about dfl_fme_region_find_by_port_id? > +{ > + struct dfl_fme_region *fme_region; > + > + list_for_each_entry(fme_region, &fme->region_list, node) > + if (fme_region->port_id == port_id) > + return fme_region; > + > + return NULL; > +} > + > +static int dfl_fme_region_match(struct device *dev, const void *data) > +{ > + return dev->parent == data; > +} > + > +static struct fpga_region *dfl_fme_region_find(struct dfl_fme *fme, int port_id) > +{ > + struct dfl_fme_region *fme_region; > + struct fpga_region *region; > + > + fme_region = find_fme_region_by_port_id(fme, port_id); > + if (!fme_region) > + return NULL; > + > + region = fpga_region_class_find(NULL, &fme_region->region->dev, > + dfl_fme_region_match); > + if (!region) > + return NULL; > + > + return region; > +} > + > +static int fme_pr(struct platform_device *pdev, unsigned long arg) > +{ > + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); > + void __user *argp = (void __user *)arg; > + struct dfl_fpga_fme_port_pr port_pr; > + struct fpga_image_info *info; > + struct fpga_region *region; > + void __iomem *fme_hdr; > + struct dfl_fme *fme; > + unsigned long minsz; > + void *buf = NULL; > + int ret = 0; > + u64 v; > + > + minsz = offsetofend(struct dfl_fpga_fme_port_pr, buffer_address); > + > + if (copy_from_user(&port_pr, argp, minsz)) > + return -EFAULT; > + > + if (port_pr.argsz < minsz || port_pr.flags) > + return -EINVAL; > + > + if (!IS_ALIGNED(port_pr.buffer_size, 4)) > + return -EINVAL; > + > + /* get fme header region */ > + fme_hdr = dfl_get_feature_ioaddr_by_id(&pdev->dev, > + FME_FEATURE_ID_HEADER); > + > + /* check port id */ > + v = readq(fme_hdr + FME_HDR_CAP); > + if (port_pr.port_id >= FIELD_GET(FME_CAP_NUM_PORTS, v)) { > + dev_dbg(&pdev->dev, "port number more than maximum\n"); > + return -EINVAL; > + } > + > + if (!access_ok(VERIFY_READ, > + (void __user *)(unsigned long)port_pr.buffer_address, > + port_pr.buffer_size)) > + return -EFAULT; > + > + buf = vmalloc(port_pr.buffer_size); > + if (!buf) > + return -ENOMEM; > + > + if (copy_from_user(buf, > + (void __user *)(unsigned long)port_pr.buffer_address, > + port_pr.buffer_size)) { > + ret = -EFAULT; > + goto free_exit; > + } > + > + /* prepare fpga_image_info for PR */ > + info = fpga_image_info_alloc(&pdev->dev); > + if (!info) { > + ret = -ENOMEM; > + goto free_exit; > + } > + > + info->flags |= FPGA_MGR_PARTIAL_RECONFIG; > + > + mutex_lock(&pdata->lock); > + fme = dfl_fpga_pdata_get_private(pdata); > + /* fme device has been unregistered. */ > + if (!fme) { > + ret = -EINVAL; > + goto unlock_exit; > + } > + > + region = dfl_fme_region_find(fme, port_pr.port_id); > + if (!region) { > + ret = -EINVAL; > + goto unlock_exit; > + } > + > + fpga_image_info_free(region->info); > + > + info->buf = buf; > + info->count = port_pr.buffer_size; > + info->region_id = port_pr.port_id; > + region->info = info; > + > + ret = fpga_region_program_fpga(region); > + > + if (region->get_bridges) > + fpga_bridges_put(®ion->bridge_list); > + > + put_device(®ion->dev); > +unlock_exit: > + mutex_unlock(&pdata->lock); > +free_exit: > + vfree(buf); > + if (copy_to_user((void __user *)arg, &port_pr, minsz)) > + return -EFAULT; > + > + return ret; > +} > + > +/** > + * dfl_fme_create_mgr - create fpga mgr platform device as child device > + * > + * @pdata: fme platform_device's pdata > + * > + * Return: mgr platform device if successful, and error code otherwise. > + */ > +static struct platform_device * > +dfl_fme_create_mgr(struct dfl_feature_platform_data *pdata, > + struct dfl_feature *feature) > +{ > + struct platform_device *mgr, *fme = pdata->dev; > + struct dfl_fme_mgr_pdata mgr_pdata; > + int ret = -ENOMEM; > + > + if (!feature->ioaddr) > + return ERR_PTR(-ENODEV); > + > + mgr_pdata.ioaddr = feature->ioaddr; > + > + /* > + * Each FME has only one fpga-mgr, so allocate platform device using > + * the same FME platform device id. > + */ > + mgr = platform_device_alloc(DFL_FPGA_FME_MGR, fme->id); > + if (!mgr) > + return ERR_PTR(ret); > + > + mgr->dev.parent = &fme->dev; > + > + ret = platform_device_add_data(mgr, &mgr_pdata, sizeof(mgr_pdata)); > + if (ret) > + goto create_mgr_err; > + > + ret = platform_device_add(mgr); > + if (ret) > + goto create_mgr_err; > + > + return mgr; > + > +create_mgr_err: > + platform_device_put(mgr); > + return ERR_PTR(ret); > +} > + > +/** > + * dfl_fme_destroy_mgr - destroy fpga mgr platform device > + * @pdata: fme platform device's pdata > + */ > +static void dfl_fme_destroy_mgr(struct dfl_feature_platform_data *pdata) > +{ > + struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata); > + > + platform_device_unregister(priv->mgr); > +} > + > +/** > + * dfl_fme_create_bridge - create fme fpga bridge platform device as child > + * > + * @pdata: fme platform device's pdata > + * @port_id: port id for the bridge to be created. > + * > + * Return: bridge platform device if successful, and error code otherwise. > + */ > +static struct dfl_fme_bridge * > +dfl_fme_create_bridge(struct dfl_feature_platform_data *pdata, int port_id) > +{ > + struct device *dev = &pdata->dev->dev; > + struct dfl_fme_br_pdata br_pdata; > + struct dfl_fme_bridge *fme_br; > + int ret = -ENOMEM; > + > + fme_br = devm_kzalloc(dev, sizeof(*fme_br), GFP_KERNEL); > + if (!fme_br) > + return ERR_PTR(ret); > + > + br_pdata.cdev = pdata->dfl_cdev; > + br_pdata.port_id = port_id; > + > + fme_br->br = platform_device_alloc(DFL_FPGA_FME_BRIDGE, > + PLATFORM_DEVID_AUTO); > + if (!fme_br->br) > + return ERR_PTR(ret); > + > + fme_br->br->dev.parent = dev; > + > + ret = platform_device_add_data(fme_br->br, &br_pdata, sizeof(br_pdata)); > + if (ret) > + goto create_br_err; > + > + ret = platform_device_add(fme_br->br); > + if (ret) > + goto create_br_err; > + > + return fme_br; > + > +create_br_err: > + platform_device_put(fme_br->br); > + return ERR_PTR(ret); > +} > + > +/** > + * dfl_fme_destroy_bridge - destroy fpga bridge platform device > + * @fme_br: fme bridge to destroy > + */ > +static void dfl_fme_destroy_bridge(struct dfl_fme_bridge *fme_br) > +{ > + platform_device_unregister(fme_br->br); > +} > + > +/** > + * dfl_fme_destroy_bridge - destroy all fpga bridge platform device > + * @pdata: fme platform device's pdata > + */ > +static void dfl_fme_destroy_bridges(struct dfl_feature_platform_data *pdata) > +{ > + struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata); > + struct dfl_fme_bridge *fbridge, *tmp; > + > + list_for_each_entry_safe(fbridge, tmp, &priv->bridge_list, node) { > + list_del(&fbridge->node); > + dfl_fme_destroy_bridge(fbridge); > + } > +} > + > +/** > + * dfl_fme_create_region - create fpga region platform device as child > + * > + * @pdata: fme platform device's pdata > + * @mgr: mgr platform device needed for region > + * @br: br platform device needed for region > + * @port_id: port id > + * > + * Return: fme region if successful, and error code otherwise. > + */ > +static struct dfl_fme_region * > +dfl_fme_create_region(struct dfl_feature_platform_data *pdata, > + struct platform_device *mgr, > + struct platform_device *br, int port_id) > +{ > + struct dfl_fme_region_pdata region_pdata; > + struct device *dev = &pdata->dev->dev; > + struct dfl_fme_region *fme_region; > + int ret = -ENOMEM; > + > + fme_region = devm_kzalloc(dev, sizeof(*fme_region), GFP_KERNEL); > + if (!fme_region) > + return ERR_PTR(ret); > + > + region_pdata.mgr = mgr; > + region_pdata.br = br; > + > + /* > + * Each FPGA device may have more than one port, so allocate platform > + * device using the same port platform device id. > + */ > + fme_region->region = platform_device_alloc(DFL_FPGA_FME_REGION, br->id); > + if (!fme_region->region) > + return ERR_PTR(ret); > + > + fme_region->region->dev.parent = dev; > + > + ret = platform_device_add_data(fme_region->region, ®ion_pdata, > + sizeof(region_pdata)); > + if (ret) > + goto create_region_err; > + > + ret = platform_device_add(fme_region->region); > + if (ret) > + goto create_region_err; > + > + fme_region->port_id = port_id; > + > + return fme_region; > + > +create_region_err: > + platform_device_put(fme_region->region); > + return ERR_PTR(ret); > +} > + > +/** > + * dfl_fme_destroy_region - destroy fme region > + * @fme_region: fme region to destroy > + */ > +static void dfl_fme_destroy_region(struct dfl_fme_region *fme_region) > +{ > + platform_device_unregister(fme_region->region); > +} > + > +/** > + * dfl_fme_destroy_regions - destroy all fme regions > + * @pdata: fme platform device's pdata > + */ > +static void dfl_fme_destroy_regions(struct dfl_feature_platform_data *pdata) > +{ > + struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata); > + struct dfl_fme_region *fme_region, *tmp; > + > + list_for_each_entry_safe(fme_region, tmp, &priv->region_list, node) { > + list_del(&fme_region->node); > + dfl_fme_destroy_region(fme_region); > + } > +} > + > +static int > +pr_mgmt_init(struct platform_device *pdev, struct dfl_feature *feature) > +{ > + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); > + struct dfl_fme_region *fme_region; > + struct dfl_fme_bridge *fme_br; > + struct platform_device *mgr; > + struct dfl_fme *priv; > + void __iomem *fme_hdr; > + int ret = -ENODEV, i = 0; > + u64 fme_cap, port_offset; > + > + fme_hdr = dfl_get_feature_ioaddr_by_id(&pdev->dev, > + FME_FEATURE_ID_HEADER); > + > + mutex_lock(&pdata->lock); > + priv = dfl_fpga_pdata_get_private(pdata); > + > + /* Initialize the region and bridge sub device list */ > + INIT_LIST_HEAD(&priv->region_list); > + INIT_LIST_HEAD(&priv->bridge_list); > + > + /* Create fpga mgr platform device */ > + mgr = dfl_fme_create_mgr(pdata, feature); > + if (IS_ERR(mgr)) { > + dev_err(&pdev->dev, "fail to create fpga mgr pdev\n"); > + goto unlock; > + } > + > + priv->mgr = mgr; > + > + /* Read capability register to check number of regions and bridges */ > + fme_cap = readq(fme_hdr + FME_HDR_CAP); > + for (; i < FIELD_GET(FME_CAP_NUM_PORTS, fme_cap); i++) { > + port_offset = readq(fme_hdr + FME_HDR_PORT_OFST(i)); > + if (!(port_offset & FME_PORT_OFST_IMP)) > + continue; > + > + /* Create bridge for each port */ > + fme_br = dfl_fme_create_bridge(pdata, i); > + if (IS_ERR(fme_br)) { > + ret = PTR_ERR(fme_br); > + goto destroy_region; > + } > + > + list_add(&fme_br->node, &priv->bridge_list); > + > + /* Create region for each port */ > + fme_region = dfl_fme_create_region(pdata, mgr, > + fme_br->br, i); > + if (!fme_region) { > + ret = PTR_ERR(fme_region); > + goto destroy_region; > + } > + > + list_add(&fme_region->node, &priv->region_list); > + } > + mutex_unlock(&pdata->lock); > + > + return 0; > + > +destroy_region: > + dfl_fme_destroy_regions(pdata); > + dfl_fme_destroy_bridges(pdata); > + dfl_fme_destroy_mgr(pdata); > +unlock: > + mutex_unlock(&pdata->lock); > + return ret; > +} > + > +static void pr_mgmt_uinit(struct platform_device *pdev, > + struct dfl_feature *feature) > +{ > + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); > + struct dfl_fme *priv; > + > + mutex_lock(&pdata->lock); > + priv = dfl_fpga_pdata_get_private(pdata); > + > + dfl_fme_destroy_regions(pdata); > + dfl_fme_destroy_bridges(pdata); > + dfl_fme_destroy_mgr(pdata); > + mutex_unlock(&pdata->lock); > +} > + > +static long fme_pr_ioctl(struct platform_device *pdev, > + struct dfl_feature *feature, > + unsigned int cmd, unsigned long arg) > +{ > + long ret; > + > + switch (cmd) { > + case DFL_FPGA_FME_PORT_PR: > + ret = fme_pr(pdev, arg); > + break; > + default: > + ret = -ENODEV; > + } > + > + return ret; > +} > + > +const struct dfl_feature_ops pr_mgmt_ops = { > + .init = pr_mgmt_init, > + .uinit = pr_mgmt_uinit, > + .ioctl = fme_pr_ioctl, > +}; > diff --git a/drivers/fpga/dfl-fme-pr.h b/drivers/fpga/dfl-fme-pr.h > new file mode 100644 > index 0000000..2e60b53 > --- /dev/null > +++ b/drivers/fpga/dfl-fme-pr.h > @@ -0,0 +1,84 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Header file for FPGA Management Engine (FME) Partial Reconfiguration Driver > + * > + * Copyright (C) 2017 Intel Corporation, Inc. > + * > + * Authors: > + * Kang Luwei <luwei.kang@intel.com> > + * Xiao Guangrong <guangrong.xiao@linux.intel.com> > + * Wu Hao <hao.wu@intel.com> > + * Joseph Grecco <joe.grecco@intel.com> > + * Enno Luebbers <enno.luebbers@intel.com> > + * Tim Whisonant <tim.whisonant@intel.com> > + * Ananda Ravuri <ananda.ravuri@intel.com> > + * Henry Mitchel <henry.mitchel@intel.com> > + */ > + > +#ifndef __DFL_FME_PR_H > +#define __DFL_FME_PR_H > + > +#include <linux/platform_device.h> > + > +/** > + * struct dfl_fme_region - FME fpga region data structure > + * > + * @region: platform device of the FPGA region. > + * @node: used to link fme_region to a list. > + * @port_id: indicate which port this region connected to. > + */ > +struct dfl_fme_region { > + struct platform_device *region; > + struct list_head node; > + int port_id; > +}; > + > +/** > + * struct dfl_fme_region_pdata - platform data for FME region platform device. > + * > + * @mgr: platform device of the FPGA manager. > + * @br: platform device of the FPGA bridge. > + * @region_id: region id (same as port_id). > + */ > +struct dfl_fme_region_pdata { > + struct platform_device *mgr; > + struct platform_device *br; > + int region_id; > +}; > + > +/** > + * struct dfl_fme_bridge - FME fpga bridge data structure > + * > + * @br: platform device of the FPGA bridge. > + * @node: used to link fme_bridge to a list. > + */ > +struct dfl_fme_bridge { > + struct platform_device *br; > + struct list_head node; > +}; > + > +/** > + * struct dfl_fme_bridge_pdata - platform data for FME bridge platform device. > + * > + * @cdev: container device. > + * @port_id: port id. > + */ > +struct dfl_fme_br_pdata { > + struct dfl_fpga_cdev *cdev; > + int port_id; > +}; > + > +/** > + * struct dfl_fme_mgr_pdata - platform data for FME manager platform device. > + * > + * @ioaddr: mapped io address for FME manager platform device. > + */ > +struct dfl_fme_mgr_pdata { > + void __iomem *ioaddr; > +}; > + > +#define DFL_FPGA_FME_MGR "dfl-fme-mgr" > +#define DFL_FPGA_FME_BRIDGE "dfl-fme-bridge" > +#define DFL_FPGA_FME_REGION "dfl-fme-region" > + > +#endif /* __DFL_FME_PR_H */ > diff --git a/drivers/fpga/dfl-fme.h b/drivers/fpga/dfl-fme.h > new file mode 100644 > index 0000000..73b864d > --- /dev/null > +++ b/drivers/fpga/dfl-fme.h > @@ -0,0 +1,38 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Header file for FPGA Management Engine (FME) Driver > + * > + * Copyright (C) 2017 Intel Corporation, Inc. > + * > + * Authors: > + * Kang Luwei <luwei.kang@intel.com> > + * Xiao Guangrong <guangrong.xiao@linux.intel.com> > + * Wu Hao <hao.wu@intel.com> > + * Joseph Grecco <joe.grecco@intel.com> > + * Enno Luebbers <enno.luebbers@intel.com> > + * Tim Whisonant <tim.whisonant@intel.com> > + * Ananda Ravuri <ananda.ravuri@intel.com> > + * Henry Mitchel <henry.mitchel@intel.com> > + */ > + > +#ifndef __DFL_FME_H > +#define __DFL_FME_H > + > +/** > + * struct dfl_fme - dfl fme private data > + * > + * @mgr: FME's FPGA manager platform device. > + * @region_list: link list of FME's FPGA regions. > + * @bridge_list: link list of FME's FPGA bridges. > + * @pdata: fme platform device's pdata. > + */ > +struct dfl_fme { > + struct platform_device *mgr; > + struct list_head region_list; > + struct list_head bridge_list; > + struct dfl_feature_platform_data *pdata; > +}; > + > +extern const struct dfl_feature_ops pr_mgmt_ops; > + > +#endif /* __DFL_FME_H */ > diff --git a/include/uapi/linux/fpga-dfl.h b/include/uapi/linux/fpga-dfl.h > index 7585de0..1c8bb2d 100644 > --- a/include/uapi/linux/fpga-dfl.h > +++ b/include/uapi/linux/fpga-dfl.h > @@ -14,6 +14,8 @@ > #ifndef _UAPI_LINUX_FPGA_DFL_H > #define _UAPI_LINUX_FPGA_DFL_H > > +#include <linux/types.h> > + > #define DFL_FPGA_API_VERSION 0 > > /* > @@ -26,6 +28,7 @@ > #define DFL_FPGA_MAGIC 0xB6 > > #define DFL_FPGA_BASE 0 > +#define DFL_FME_BASE 0x80 > > /** > * DFL_FPGA_GET_API_VERSION - _IO(DFL_FPGA_MAGIC, DFL_FPGA_BASE + 0) > @@ -45,4 +48,29 @@ > > #define DFL_FPGA_CHECK_EXTENSION _IO(DFL_FPGA_MAGIC, DFL_FPGA_BASE + 1) > > +/* IOCTLs for FME file descriptor */ > + > +/** > + * DFL_FPGA_FME_PORT_PR - _IOW(DFL_FPGA_MAGIC, DFL_FME_BASE + 0, > + * struct dfl_fpga_fme_port_pr) > + * > + * Driver does Partial Reconfiguration based on Port ID and Buffer (Image) > + * provided by caller. > + * Return: 0 on success, -errno on failure. > + * If DFL_FPGA_FME_PORT_PR returns -EIO, that indicates the HW has detected > + * some errors during PR, under this case, the user can fetch HW error info > + * from the status of FME's fpga manager. > + */ > + > +struct dfl_fpga_fme_port_pr { > + /* Input */ > + __u32 argsz; /* Structure length */ > + __u32 flags; /* Zero for now */ > + __u32 port_id; > + __u32 buffer_size; > + __u64 buffer_address; /* Userspace address to the buffer for PR */ > +}; > + > +#define DFL_FPGA_FME_PORT_PR _IO(DFL_FPGA_MAGIC, DFL_FME_BASE + 0) > + > #endif /* _UAPI_LINUX_FPGA_DFL_H */ > -- > 1.8.3.1 > -- To unsubscribe from this list: send the line "unsubscribe linux-fpga" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index fbd1c85..3c44fc9 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -32,7 +32,7 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o obj-$(CONFIG_FPGA_DFL) += dfl.o obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o -dfl-fme-objs := dfl-fme-main.o +dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o # Drivers for FPGAs which implement DFL obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c index 6a59c07..bcb9325 100644 --- a/drivers/fpga/dfl-fme-main.c +++ b/drivers/fpga/dfl-fme-main.c @@ -19,6 +19,7 @@ #include <linux/fpga-dfl.h> #include "dfl.h" +#include "dfl-fme.h" static ssize_t ports_num_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -113,6 +114,10 @@ static ssize_t bitstream_metadata_show(struct device *dev, .ops = &fme_hdr_ops, }, { + .id = FME_FEATURE_ID_PR_MGMT, + .ops = &pr_mgmt_ops, + }, + { .ops = NULL, }, }; @@ -187,6 +192,35 @@ static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return -EINVAL; } +static int fme_dev_init(struct platform_device *pdev) +{ + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_fme *fme; + + fme = devm_kzalloc(&pdev->dev, sizeof(*fme), GFP_KERNEL); + if (!fme) + return -ENOMEM; + + fme->pdata = pdata; + + mutex_lock(&pdata->lock); + dfl_fpga_pdata_set_private(pdata, fme); + mutex_unlock(&pdata->lock); + + return 0; +} + +static void fme_dev_destroy(struct platform_device *pdev) +{ + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_fme *fme; + + mutex_lock(&pdata->lock); + fme = dfl_fpga_pdata_get_private(pdata); + dfl_fpga_pdata_set_private(pdata, NULL); + mutex_unlock(&pdata->lock); +} + static const struct file_operations fme_fops = { .owner = THIS_MODULE, .open = fme_open, @@ -198,10 +232,14 @@ static int fme_probe(struct platform_device *pdev) { int ret; - ret = dfl_fpga_dev_feature_init(pdev, fme_feature_drvs); + ret = fme_dev_init(pdev); if (ret) goto exit; + ret = dfl_fpga_dev_feature_init(pdev, fme_feature_drvs); + if (ret) + goto dev_destroy; + ret = dfl_fpga_register_dev_ops(pdev, &fme_fops, THIS_MODULE); if (ret) goto feature_uinit; @@ -210,6 +248,8 @@ static int fme_probe(struct platform_device *pdev) feature_uinit: dfl_fpga_dev_feature_uinit(pdev); +dev_destroy: + fme_dev_destroy(pdev); exit: return ret; } @@ -218,6 +258,7 @@ static int fme_remove(struct platform_device *pdev) { dfl_fpga_unregister_dev_ops(pdev); dfl_fpga_dev_feature_uinit(pdev); + fme_dev_destroy(pdev); return 0; } diff --git a/drivers/fpga/dfl-fme-pr.c b/drivers/fpga/dfl-fme-pr.c new file mode 100644 index 0000000..3d5f8b9 --- /dev/null +++ b/drivers/fpga/dfl-fme-pr.c @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for FPGA Management Engine (FME) Partial Reconfiguration + * + * Copyright (C) 2017 Intel Corporation, Inc. + * + * Authors: + * Kang Luwei <luwei.kang@intel.com> + * Xiao Guangrong <guangrong.xiao@linux.intel.com> + * Wu Hao <hao.wu@intel.com> + * Joseph Grecco <joe.grecco@intel.com> + * Enno Luebbers <enno.luebbers@intel.com> + * Tim Whisonant <tim.whisonant@intel.com> + * Ananda Ravuri <ananda.ravuri@intel.com> + * Christopher Rauer <christopher.rauer@intel.com> + * Henry Mitchel <henry.mitchel@intel.com> + */ + +#include <linux/types.h> +#include <linux/device.h> +#include <linux/vmalloc.h> +#include <linux/uaccess.h> +#include <linux/fpga/fpga-mgr.h> +#include <linux/fpga/fpga-bridge.h> +#include <linux/fpga/fpga-region.h> +#include <linux/fpga-dfl.h> + +#include "dfl.h" +#include "dfl-fme.h" +#include "dfl-fme-pr.h" + +static struct dfl_fme_region * +find_fme_region_by_port_id(struct dfl_fme *fme, int port_id) +{ + struct dfl_fme_region *fme_region; + + list_for_each_entry(fme_region, &fme->region_list, node) + if (fme_region->port_id == port_id) + return fme_region; + + return NULL; +} + +static int dfl_fme_region_match(struct device *dev, const void *data) +{ + return dev->parent == data; +} + +static struct fpga_region *dfl_fme_region_find(struct dfl_fme *fme, int port_id) +{ + struct dfl_fme_region *fme_region; + struct fpga_region *region; + + fme_region = find_fme_region_by_port_id(fme, port_id); + if (!fme_region) + return NULL; + + region = fpga_region_class_find(NULL, &fme_region->region->dev, + dfl_fme_region_match); + if (!region) + return NULL; + + return region; +} + +static int fme_pr(struct platform_device *pdev, unsigned long arg) +{ + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + void __user *argp = (void __user *)arg; + struct dfl_fpga_fme_port_pr port_pr; + struct fpga_image_info *info; + struct fpga_region *region; + void __iomem *fme_hdr; + struct dfl_fme *fme; + unsigned long minsz; + void *buf = NULL; + int ret = 0; + u64 v; + + minsz = offsetofend(struct dfl_fpga_fme_port_pr, buffer_address); + + if (copy_from_user(&port_pr, argp, minsz)) + return -EFAULT; + + if (port_pr.argsz < minsz || port_pr.flags) + return -EINVAL; + + if (!IS_ALIGNED(port_pr.buffer_size, 4)) + return -EINVAL; + + /* get fme header region */ + fme_hdr = dfl_get_feature_ioaddr_by_id(&pdev->dev, + FME_FEATURE_ID_HEADER); + + /* check port id */ + v = readq(fme_hdr + FME_HDR_CAP); + if (port_pr.port_id >= FIELD_GET(FME_CAP_NUM_PORTS, v)) { + dev_dbg(&pdev->dev, "port number more than maximum\n"); + return -EINVAL; + } + + if (!access_ok(VERIFY_READ, + (void __user *)(unsigned long)port_pr.buffer_address, + port_pr.buffer_size)) + return -EFAULT; + + buf = vmalloc(port_pr.buffer_size); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, + (void __user *)(unsigned long)port_pr.buffer_address, + port_pr.buffer_size)) { + ret = -EFAULT; + goto free_exit; + } + + /* prepare fpga_image_info for PR */ + info = fpga_image_info_alloc(&pdev->dev); + if (!info) { + ret = -ENOMEM; + goto free_exit; + } + + info->flags |= FPGA_MGR_PARTIAL_RECONFIG; + + mutex_lock(&pdata->lock); + fme = dfl_fpga_pdata_get_private(pdata); + /* fme device has been unregistered. */ + if (!fme) { + ret = -EINVAL; + goto unlock_exit; + } + + region = dfl_fme_region_find(fme, port_pr.port_id); + if (!region) { + ret = -EINVAL; + goto unlock_exit; + } + + fpga_image_info_free(region->info); + + info->buf = buf; + info->count = port_pr.buffer_size; + info->region_id = port_pr.port_id; + region->info = info; + + ret = fpga_region_program_fpga(region); + + if (region->get_bridges) + fpga_bridges_put(®ion->bridge_list); + + put_device(®ion->dev); +unlock_exit: + mutex_unlock(&pdata->lock); +free_exit: + vfree(buf); + if (copy_to_user((void __user *)arg, &port_pr, minsz)) + return -EFAULT; + + return ret; +} + +/** + * dfl_fme_create_mgr - create fpga mgr platform device as child device + * + * @pdata: fme platform_device's pdata + * + * Return: mgr platform device if successful, and error code otherwise. + */ +static struct platform_device * +dfl_fme_create_mgr(struct dfl_feature_platform_data *pdata, + struct dfl_feature *feature) +{ + struct platform_device *mgr, *fme = pdata->dev; + struct dfl_fme_mgr_pdata mgr_pdata; + int ret = -ENOMEM; + + if (!feature->ioaddr) + return ERR_PTR(-ENODEV); + + mgr_pdata.ioaddr = feature->ioaddr; + + /* + * Each FME has only one fpga-mgr, so allocate platform device using + * the same FME platform device id. + */ + mgr = platform_device_alloc(DFL_FPGA_FME_MGR, fme->id); + if (!mgr) + return ERR_PTR(ret); + + mgr->dev.parent = &fme->dev; + + ret = platform_device_add_data(mgr, &mgr_pdata, sizeof(mgr_pdata)); + if (ret) + goto create_mgr_err; + + ret = platform_device_add(mgr); + if (ret) + goto create_mgr_err; + + return mgr; + +create_mgr_err: + platform_device_put(mgr); + return ERR_PTR(ret); +} + +/** + * dfl_fme_destroy_mgr - destroy fpga mgr platform device + * @pdata: fme platform device's pdata + */ +static void dfl_fme_destroy_mgr(struct dfl_feature_platform_data *pdata) +{ + struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata); + + platform_device_unregister(priv->mgr); +} + +/** + * dfl_fme_create_bridge - create fme fpga bridge platform device as child + * + * @pdata: fme platform device's pdata + * @port_id: port id for the bridge to be created. + * + * Return: bridge platform device if successful, and error code otherwise. + */ +static struct dfl_fme_bridge * +dfl_fme_create_bridge(struct dfl_feature_platform_data *pdata, int port_id) +{ + struct device *dev = &pdata->dev->dev; + struct dfl_fme_br_pdata br_pdata; + struct dfl_fme_bridge *fme_br; + int ret = -ENOMEM; + + fme_br = devm_kzalloc(dev, sizeof(*fme_br), GFP_KERNEL); + if (!fme_br) + return ERR_PTR(ret); + + br_pdata.cdev = pdata->dfl_cdev; + br_pdata.port_id = port_id; + + fme_br->br = platform_device_alloc(DFL_FPGA_FME_BRIDGE, + PLATFORM_DEVID_AUTO); + if (!fme_br->br) + return ERR_PTR(ret); + + fme_br->br->dev.parent = dev; + + ret = platform_device_add_data(fme_br->br, &br_pdata, sizeof(br_pdata)); + if (ret) + goto create_br_err; + + ret = platform_device_add(fme_br->br); + if (ret) + goto create_br_err; + + return fme_br; + +create_br_err: + platform_device_put(fme_br->br); + return ERR_PTR(ret); +} + +/** + * dfl_fme_destroy_bridge - destroy fpga bridge platform device + * @fme_br: fme bridge to destroy + */ +static void dfl_fme_destroy_bridge(struct dfl_fme_bridge *fme_br) +{ + platform_device_unregister(fme_br->br); +} + +/** + * dfl_fme_destroy_bridge - destroy all fpga bridge platform device + * @pdata: fme platform device's pdata + */ +static void dfl_fme_destroy_bridges(struct dfl_feature_platform_data *pdata) +{ + struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata); + struct dfl_fme_bridge *fbridge, *tmp; + + list_for_each_entry_safe(fbridge, tmp, &priv->bridge_list, node) { + list_del(&fbridge->node); + dfl_fme_destroy_bridge(fbridge); + } +} + +/** + * dfl_fme_create_region - create fpga region platform device as child + * + * @pdata: fme platform device's pdata + * @mgr: mgr platform device needed for region + * @br: br platform device needed for region + * @port_id: port id + * + * Return: fme region if successful, and error code otherwise. + */ +static struct dfl_fme_region * +dfl_fme_create_region(struct dfl_feature_platform_data *pdata, + struct platform_device *mgr, + struct platform_device *br, int port_id) +{ + struct dfl_fme_region_pdata region_pdata; + struct device *dev = &pdata->dev->dev; + struct dfl_fme_region *fme_region; + int ret = -ENOMEM; + + fme_region = devm_kzalloc(dev, sizeof(*fme_region), GFP_KERNEL); + if (!fme_region) + return ERR_PTR(ret); + + region_pdata.mgr = mgr; + region_pdata.br = br; + + /* + * Each FPGA device may have more than one port, so allocate platform + * device using the same port platform device id. + */ + fme_region->region = platform_device_alloc(DFL_FPGA_FME_REGION, br->id); + if (!fme_region->region) + return ERR_PTR(ret); + + fme_region->region->dev.parent = dev; + + ret = platform_device_add_data(fme_region->region, ®ion_pdata, + sizeof(region_pdata)); + if (ret) + goto create_region_err; + + ret = platform_device_add(fme_region->region); + if (ret) + goto create_region_err; + + fme_region->port_id = port_id; + + return fme_region; + +create_region_err: + platform_device_put(fme_region->region); + return ERR_PTR(ret); +} + +/** + * dfl_fme_destroy_region - destroy fme region + * @fme_region: fme region to destroy + */ +static void dfl_fme_destroy_region(struct dfl_fme_region *fme_region) +{ + platform_device_unregister(fme_region->region); +} + +/** + * dfl_fme_destroy_regions - destroy all fme regions + * @pdata: fme platform device's pdata + */ +static void dfl_fme_destroy_regions(struct dfl_feature_platform_data *pdata) +{ + struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata); + struct dfl_fme_region *fme_region, *tmp; + + list_for_each_entry_safe(fme_region, tmp, &priv->region_list, node) { + list_del(&fme_region->node); + dfl_fme_destroy_region(fme_region); + } +} + +static int +pr_mgmt_init(struct platform_device *pdev, struct dfl_feature *feature) +{ + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_fme_region *fme_region; + struct dfl_fme_bridge *fme_br; + struct platform_device *mgr; + struct dfl_fme *priv; + void __iomem *fme_hdr; + int ret = -ENODEV, i = 0; + u64 fme_cap, port_offset; + + fme_hdr = dfl_get_feature_ioaddr_by_id(&pdev->dev, + FME_FEATURE_ID_HEADER); + + mutex_lock(&pdata->lock); + priv = dfl_fpga_pdata_get_private(pdata); + + /* Initialize the region and bridge sub device list */ + INIT_LIST_HEAD(&priv->region_list); + INIT_LIST_HEAD(&priv->bridge_list); + + /* Create fpga mgr platform device */ + mgr = dfl_fme_create_mgr(pdata, feature); + if (IS_ERR(mgr)) { + dev_err(&pdev->dev, "fail to create fpga mgr pdev\n"); + goto unlock; + } + + priv->mgr = mgr; + + /* Read capability register to check number of regions and bridges */ + fme_cap = readq(fme_hdr + FME_HDR_CAP); + for (; i < FIELD_GET(FME_CAP_NUM_PORTS, fme_cap); i++) { + port_offset = readq(fme_hdr + FME_HDR_PORT_OFST(i)); + if (!(port_offset & FME_PORT_OFST_IMP)) + continue; + + /* Create bridge for each port */ + fme_br = dfl_fme_create_bridge(pdata, i); + if (IS_ERR(fme_br)) { + ret = PTR_ERR(fme_br); + goto destroy_region; + } + + list_add(&fme_br->node, &priv->bridge_list); + + /* Create region for each port */ + fme_region = dfl_fme_create_region(pdata, mgr, + fme_br->br, i); + if (!fme_region) { + ret = PTR_ERR(fme_region); + goto destroy_region; + } + + list_add(&fme_region->node, &priv->region_list); + } + mutex_unlock(&pdata->lock); + + return 0; + +destroy_region: + dfl_fme_destroy_regions(pdata); + dfl_fme_destroy_bridges(pdata); + dfl_fme_destroy_mgr(pdata); +unlock: + mutex_unlock(&pdata->lock); + return ret; +} + +static void pr_mgmt_uinit(struct platform_device *pdev, + struct dfl_feature *feature) +{ + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_fme *priv; + + mutex_lock(&pdata->lock); + priv = dfl_fpga_pdata_get_private(pdata); + + dfl_fme_destroy_regions(pdata); + dfl_fme_destroy_bridges(pdata); + dfl_fme_destroy_mgr(pdata); + mutex_unlock(&pdata->lock); +} + +static long fme_pr_ioctl(struct platform_device *pdev, + struct dfl_feature *feature, + unsigned int cmd, unsigned long arg) +{ + long ret; + + switch (cmd) { + case DFL_FPGA_FME_PORT_PR: + ret = fme_pr(pdev, arg); + break; + default: + ret = -ENODEV; + } + + return ret; +} + +const struct dfl_feature_ops pr_mgmt_ops = { + .init = pr_mgmt_init, + .uinit = pr_mgmt_uinit, + .ioctl = fme_pr_ioctl, +}; diff --git a/drivers/fpga/dfl-fme-pr.h b/drivers/fpga/dfl-fme-pr.h new file mode 100644 index 0000000..2e60b53 --- /dev/null +++ b/drivers/fpga/dfl-fme-pr.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Header file for FPGA Management Engine (FME) Partial Reconfiguration Driver + * + * Copyright (C) 2017 Intel Corporation, Inc. + * + * Authors: + * Kang Luwei <luwei.kang@intel.com> + * Xiao Guangrong <guangrong.xiao@linux.intel.com> + * Wu Hao <hao.wu@intel.com> + * Joseph Grecco <joe.grecco@intel.com> + * Enno Luebbers <enno.luebbers@intel.com> + * Tim Whisonant <tim.whisonant@intel.com> + * Ananda Ravuri <ananda.ravuri@intel.com> + * Henry Mitchel <henry.mitchel@intel.com> + */ + +#ifndef __DFL_FME_PR_H +#define __DFL_FME_PR_H + +#include <linux/platform_device.h> + +/** + * struct dfl_fme_region - FME fpga region data structure + * + * @region: platform device of the FPGA region. + * @node: used to link fme_region to a list. + * @port_id: indicate which port this region connected to. + */ +struct dfl_fme_region { + struct platform_device *region; + struct list_head node; + int port_id; +}; + +/** + * struct dfl_fme_region_pdata - platform data for FME region platform device. + * + * @mgr: platform device of the FPGA manager. + * @br: platform device of the FPGA bridge. + * @region_id: region id (same as port_id). + */ +struct dfl_fme_region_pdata { + struct platform_device *mgr; + struct platform_device *br; + int region_id; +}; + +/** + * struct dfl_fme_bridge - FME fpga bridge data structure + * + * @br: platform device of the FPGA bridge. + * @node: used to link fme_bridge to a list. + */ +struct dfl_fme_bridge { + struct platform_device *br; + struct list_head node; +}; + +/** + * struct dfl_fme_bridge_pdata - platform data for FME bridge platform device. + * + * @cdev: container device. + * @port_id: port id. + */ +struct dfl_fme_br_pdata { + struct dfl_fpga_cdev *cdev; + int port_id; +}; + +/** + * struct dfl_fme_mgr_pdata - platform data for FME manager platform device. + * + * @ioaddr: mapped io address for FME manager platform device. + */ +struct dfl_fme_mgr_pdata { + void __iomem *ioaddr; +}; + +#define DFL_FPGA_FME_MGR "dfl-fme-mgr" +#define DFL_FPGA_FME_BRIDGE "dfl-fme-bridge" +#define DFL_FPGA_FME_REGION "dfl-fme-region" + +#endif /* __DFL_FME_PR_H */ diff --git a/drivers/fpga/dfl-fme.h b/drivers/fpga/dfl-fme.h new file mode 100644 index 0000000..73b864d --- /dev/null +++ b/drivers/fpga/dfl-fme.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Header file for FPGA Management Engine (FME) Driver + * + * Copyright (C) 2017 Intel Corporation, Inc. + * + * Authors: + * Kang Luwei <luwei.kang@intel.com> + * Xiao Guangrong <guangrong.xiao@linux.intel.com> + * Wu Hao <hao.wu@intel.com> + * Joseph Grecco <joe.grecco@intel.com> + * Enno Luebbers <enno.luebbers@intel.com> + * Tim Whisonant <tim.whisonant@intel.com> + * Ananda Ravuri <ananda.ravuri@intel.com> + * Henry Mitchel <henry.mitchel@intel.com> + */ + +#ifndef __DFL_FME_H +#define __DFL_FME_H + +/** + * struct dfl_fme - dfl fme private data + * + * @mgr: FME's FPGA manager platform device. + * @region_list: link list of FME's FPGA regions. + * @bridge_list: link list of FME's FPGA bridges. + * @pdata: fme platform device's pdata. + */ +struct dfl_fme { + struct platform_device *mgr; + struct list_head region_list; + struct list_head bridge_list; + struct dfl_feature_platform_data *pdata; +}; + +extern const struct dfl_feature_ops pr_mgmt_ops; + +#endif /* __DFL_FME_H */ diff --git a/include/uapi/linux/fpga-dfl.h b/include/uapi/linux/fpga-dfl.h index 7585de0..1c8bb2d 100644 --- a/include/uapi/linux/fpga-dfl.h +++ b/include/uapi/linux/fpga-dfl.h @@ -14,6 +14,8 @@ #ifndef _UAPI_LINUX_FPGA_DFL_H #define _UAPI_LINUX_FPGA_DFL_H +#include <linux/types.h> + #define DFL_FPGA_API_VERSION 0 /* @@ -26,6 +28,7 @@ #define DFL_FPGA_MAGIC 0xB6 #define DFL_FPGA_BASE 0 +#define DFL_FME_BASE 0x80 /** * DFL_FPGA_GET_API_VERSION - _IO(DFL_FPGA_MAGIC, DFL_FPGA_BASE + 0) @@ -45,4 +48,29 @@ #define DFL_FPGA_CHECK_EXTENSION _IO(DFL_FPGA_MAGIC, DFL_FPGA_BASE + 1) +/* IOCTLs for FME file descriptor */ + +/** + * DFL_FPGA_FME_PORT_PR - _IOW(DFL_FPGA_MAGIC, DFL_FME_BASE + 0, + * struct dfl_fpga_fme_port_pr) + * + * Driver does Partial Reconfiguration based on Port ID and Buffer (Image) + * provided by caller. + * Return: 0 on success, -errno on failure. + * If DFL_FPGA_FME_PORT_PR returns -EIO, that indicates the HW has detected + * some errors during PR, under this case, the user can fetch HW error info + * from the status of FME's fpga manager. + */ + +struct dfl_fpga_fme_port_pr { + /* Input */ + __u32 argsz; /* Structure length */ + __u32 flags; /* Zero for now */ + __u32 port_id; + __u32 buffer_size; + __u64 buffer_address; /* Userspace address to the buffer for PR */ +}; + +#define DFL_FPGA_FME_PORT_PR _IO(DFL_FPGA_MAGIC, DFL_FME_BASE + 0) + #endif /* _UAPI_LINUX_FPGA_DFL_H */