Hi, quick question for Alan inline below. On Tue, Jun 12, 2018 at 06:10:31PM +0800, Wu Hao wrote: > From: Kang Luwei <luwei.kang@xxxxxxxxx> > > 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@xxxxxxxxx> > Signed-off-by: Enno Luebbers <enno.luebbers@xxxxxxxxx> > Signed-off-by: Shiva Rao <shiva.rao@xxxxxxxxx> > Signed-off-by: Christopher Rauer <christopher.rauer@xxxxxxxxx> > Signed-off-by: Kang Luwei <luwei.kang@xxxxxxxxx> > Signed-off-by: Xiao Guangrong <guangrong.xiao@xxxxxxxxxxxxxxx> > Signed-off-by: Wu Hao <hao.wu@xxxxxxxxx> > Acked-by: Alan Tull <atull@xxxxxxxxxx> > --- > 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(). > v6: rebase, fix copyright time and improve function name. > add Acked-by from Alan. > --- > 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 db11f34..fd334d40 100644 > --- a/drivers/fpga/Makefile > +++ b/drivers/fpga/Makefile > @@ -33,7 +33,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 889d669..7359804 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 void fme_hdr_uinit(struct platform_device *pdev, > .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_dev_ops_register(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_dev_ops_unregister(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..5939a5c > --- /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-2018 Intel Corporation, Inc. > + * > + * Authors: > + * Kang Luwei <luwei.kang@xxxxxxxxx> > + * Xiao Guangrong <guangrong.xiao@xxxxxxxxxxxxxxx> > + * Wu Hao <hao.wu@xxxxxxxxx> > + * Joseph Grecco <joe.grecco@xxxxxxxxx> > + * Enno Luebbers <enno.luebbers@xxxxxxxxx> > + * Tim Whisonant <tim.whisonant@xxxxxxxxx> > + * Ananda Ravuri <ananda.ravuri@xxxxxxxxx> > + * Christopher Rauer <christopher.rauer@xxxxxxxxx> > + * Henry Mitchel <henry.mitchel@xxxxxxxxx> > + */ > + > +#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 * > +dfl_fme_region_find_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 = dfl_fme_region_find_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); I'm wondering if this should not be done as part of fpga_region_program_fpga() (It's not at the moment). Alan, am I missing something obvious? :) > + > + 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..096a699 > --- /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-2018 Intel Corporation, Inc. > + * > + * Authors: > + * Kang Luwei <luwei.kang@xxxxxxxxx> > + * Xiao Guangrong <guangrong.xiao@xxxxxxxxxxxxxxx> > + * Wu Hao <hao.wu@xxxxxxxxx> > + * Joseph Grecco <joe.grecco@xxxxxxxxx> > + * Enno Luebbers <enno.luebbers@xxxxxxxxx> > + * Tim Whisonant <tim.whisonant@xxxxxxxxx> > + * Ananda Ravuri <ananda.ravuri@xxxxxxxxx> > + * Henry Mitchel <henry.mitchel@xxxxxxxxx> > + */ > + > +#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..ac6d61a > --- /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-2018 Intel Corporation, Inc. > + * > + * Authors: > + * Kang Luwei <luwei.kang@xxxxxxxxx> > + * Xiao Guangrong <guangrong.xiao@xxxxxxxxxxxxxxx> > + * Wu Hao <hao.wu@xxxxxxxxx> > + * Joseph Grecco <joe.grecco@xxxxxxxxx> > + * Enno Luebbers <enno.luebbers@xxxxxxxxx> > + * Tim Whisonant <tim.whisonant@xxxxxxxxx> > + * Ananda Ravuri <ananda.ravuri@xxxxxxxxx> > + * Henry Mitchel <henry.mitchel@xxxxxxxxx> > + */ > + > +#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 9ff26cb..9abfc1d 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 > Thanks, Moritz -- To unsubscribe from this list: send the line "unsubscribe linux-fpga" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html