| From 39274ef5da8a2d0735b0dd2b3b6195503f317fca Mon Sep 17 00:00:00 2001 |
| From: Kang Luwei <luwei.kang@intel.com> |
| Date: Sat, 30 Jun 2018 08:53:24 +0800 |
| Subject: [PATCH 1718/1795] fpga: dfl: fme: add partial reconfiguration sub |
| feature support |
| |
| 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> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| (cherry picked from commit 29de76240e861d52b75405166337e94184f1875d) |
| Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> |
| --- |
| drivers/fpga/Makefile | 2 +- |
| drivers/fpga/dfl-fme-main.c | 43 ++- |
| drivers/fpga/dfl-fme-pr.c | 479 ++++++++++++++++++++++++++++++++++ |
| drivers/fpga/dfl-fme-pr.h | 84 ++++++ |
| drivers/fpga/dfl-fme.h | 38 +++ |
| include/uapi/linux/fpga-dfl.h | 27 ++ |
| 6 files changed, 671 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 db11f340ba0f..fd334d40aa1c 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 c83ff88e3bbb..086ad2420ade 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) |
| @@ -112,6 +113,10 @@ static struct dfl_feature_driver fme_feature_drvs[] = { |
| .id = FME_FEATURE_ID_HEADER, |
| .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 000000000000..fc9fd2d0482f |
| --- /dev/null |
| +++ b/drivers/fpga/dfl-fme-pr.c |
| @@ -0,0 +1,479 @@ |
| +// 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@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 * |
| +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); |
| + |
| + /* |
| + * it allows userspace to reset the PR region's logic by disabling and |
| + * reenabling the bridge to clear things out between accleration runs. |
| + * so no need to hold the bridges after partial reconfiguration. |
| + */ |
| + 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 000000000000..096a699089d3 |
| --- /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@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 000000000000..5394a216c5c0 |
| --- /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@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: linked list of FME's FPGA regions. |
| + * @bridge_list: linked 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 858e4437c31c..9666af85a8f5 100644 |
| --- a/include/uapi/linux/fpga-dfl.h |
| +++ b/include/uapi/linux/fpga-dfl.h |
| @@ -14,6 +14,7 @@ |
| #ifndef _UAPI_LINUX_FPGA_DFL_H |
| #define _UAPI_LINUX_FPGA_DFL_H |
| |
| +#include <linux/types.h> |
| #include <linux/ioctl.h> |
| |
| #define DFL_FPGA_API_VERSION 0 |
| @@ -28,6 +29,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) |
| @@ -47,4 +49,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 */ |
| -- |
| 2.19.0 |
| |