![Kang Luwei](/assets/img/avatar_default.png)
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>
280 lines
6.3 KiB
C
280 lines
6.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Driver for FPGA Management Engine (FME)
|
|
*
|
|
* Copyright (C) 2017-2018 Intel Corporation, Inc.
|
|
*
|
|
* Authors:
|
|
* Kang Luwei <luwei.kang@intel.com>
|
|
* Xiao Guangrong <guangrong.xiao@linux.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>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#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)
|
|
{
|
|
void __iomem *base;
|
|
u64 v;
|
|
|
|
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER);
|
|
|
|
v = readq(base + FME_HDR_CAP);
|
|
|
|
return scnprintf(buf, PAGE_SIZE, "%u\n",
|
|
(unsigned int)FIELD_GET(FME_CAP_NUM_PORTS, v));
|
|
}
|
|
static DEVICE_ATTR_RO(ports_num);
|
|
|
|
/*
|
|
* Bitstream (static FPGA region) identifier number. It contains the
|
|
* detailed version and other information of this static FPGA region.
|
|
*/
|
|
static ssize_t bitstream_id_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
void __iomem *base;
|
|
u64 v;
|
|
|
|
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER);
|
|
|
|
v = readq(base + FME_HDR_BITSTREAM_ID);
|
|
|
|
return scnprintf(buf, PAGE_SIZE, "0x%llx\n", (unsigned long long)v);
|
|
}
|
|
static DEVICE_ATTR_RO(bitstream_id);
|
|
|
|
/*
|
|
* Bitstream (static FPGA region) meta data. It contains the synthesis
|
|
* date, seed and other information of this static FPGA region.
|
|
*/
|
|
static ssize_t bitstream_metadata_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
void __iomem *base;
|
|
u64 v;
|
|
|
|
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER);
|
|
|
|
v = readq(base + FME_HDR_BITSTREAM_MD);
|
|
|
|
return scnprintf(buf, PAGE_SIZE, "0x%llx\n", (unsigned long long)v);
|
|
}
|
|
static DEVICE_ATTR_RO(bitstream_metadata);
|
|
|
|
static const struct attribute *fme_hdr_attrs[] = {
|
|
&dev_attr_ports_num.attr,
|
|
&dev_attr_bitstream_id.attr,
|
|
&dev_attr_bitstream_metadata.attr,
|
|
NULL,
|
|
};
|
|
|
|
static int fme_hdr_init(struct platform_device *pdev,
|
|
struct dfl_feature *feature)
|
|
{
|
|
void __iomem *base = feature->ioaddr;
|
|
int ret;
|
|
|
|
dev_dbg(&pdev->dev, "FME HDR Init.\n");
|
|
dev_dbg(&pdev->dev, "FME cap %llx.\n",
|
|
(unsigned long long)readq(base + FME_HDR_CAP));
|
|
|
|
ret = sysfs_create_files(&pdev->dev.kobj, fme_hdr_attrs);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void fme_hdr_uinit(struct platform_device *pdev,
|
|
struct dfl_feature *feature)
|
|
{
|
|
dev_dbg(&pdev->dev, "FME HDR UInit.\n");
|
|
sysfs_remove_files(&pdev->dev.kobj, fme_hdr_attrs);
|
|
}
|
|
|
|
static const struct dfl_feature_ops fme_hdr_ops = {
|
|
.init = fme_hdr_init,
|
|
.uinit = fme_hdr_uinit,
|
|
};
|
|
|
|
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,
|
|
},
|
|
};
|
|
|
|
static long fme_ioctl_check_extension(struct dfl_feature_platform_data *pdata,
|
|
unsigned long arg)
|
|
{
|
|
/* No extension support for now */
|
|
return 0;
|
|
}
|
|
|
|
static int fme_open(struct inode *inode, struct file *filp)
|
|
{
|
|
struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode);
|
|
struct dfl_feature_platform_data *pdata = dev_get_platdata(&fdev->dev);
|
|
int ret;
|
|
|
|
if (WARN_ON(!pdata))
|
|
return -ENODEV;
|
|
|
|
ret = dfl_feature_dev_use_begin(pdata);
|
|
if (ret)
|
|
return ret;
|
|
|
|
dev_dbg(&fdev->dev, "Device File Open\n");
|
|
filp->private_data = pdata;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fme_release(struct inode *inode, struct file *filp)
|
|
{
|
|
struct dfl_feature_platform_data *pdata = filp->private_data;
|
|
struct platform_device *pdev = pdata->dev;
|
|
|
|
dev_dbg(&pdev->dev, "Device File Release\n");
|
|
dfl_feature_dev_use_end(pdata);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct dfl_feature_platform_data *pdata = filp->private_data;
|
|
struct platform_device *pdev = pdata->dev;
|
|
struct dfl_feature *f;
|
|
long ret;
|
|
|
|
dev_dbg(&pdev->dev, "%s cmd 0x%x\n", __func__, cmd);
|
|
|
|
switch (cmd) {
|
|
case DFL_FPGA_GET_API_VERSION:
|
|
return DFL_FPGA_API_VERSION;
|
|
case DFL_FPGA_CHECK_EXTENSION:
|
|
return fme_ioctl_check_extension(pdata, arg);
|
|
default:
|
|
/*
|
|
* Let sub-feature's ioctl function to handle the cmd.
|
|
* Sub-feature's ioctl returns -ENODEV when cmd is not
|
|
* handled in this sub feature, and returns 0 or other
|
|
* error code if cmd is handled.
|
|
*/
|
|
dfl_fpga_dev_for_each_feature(pdata, f) {
|
|
if (f->ops && f->ops->ioctl) {
|
|
ret = f->ops->ioctl(pdev, f, cmd, arg);
|
|
if (ret != -ENODEV)
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
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,
|
|
.release = fme_release,
|
|
.unlocked_ioctl = fme_ioctl,
|
|
};
|
|
|
|
static int fme_probe(struct platform_device *pdev)
|
|
{
|
|
int ret;
|
|
|
|
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;
|
|
|
|
return 0;
|
|
|
|
feature_uinit:
|
|
dfl_fpga_dev_feature_uinit(pdev);
|
|
dev_destroy:
|
|
fme_dev_destroy(pdev);
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static struct platform_driver fme_driver = {
|
|
.driver = {
|
|
.name = DFL_FPGA_FEATURE_DEV_FME,
|
|
},
|
|
.probe = fme_probe,
|
|
.remove = fme_remove,
|
|
};
|
|
|
|
module_platform_driver(fme_driver);
|
|
|
|
MODULE_DESCRIPTION("FPGA Management Engine driver");
|
|
MODULE_AUTHOR("Intel Corporation");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_ALIAS("platform:dfl-fme");
|