Merge tag 'fpga-for-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/mdf/linux-fpga into char-misc-next

Moritz writes:

FPGA Manager changes for 5.9-rc1

Here is the (slightly larger than usual) patch set for the 5.9-rc1 merge
window.

DFL:
- Xu's changes add support for AFU interrupt handling and puts them to
  use for error handling.
- Xu's other change also adds another device-id for the Intel FPGA PAC N3000.
- John's change converts from using get_user_pages() to
  pin_user_pages().
- Gustavo's patch cleans up some of the allocation by using
  struct_size().

Xilinx:
- Luca's changes clean up the xilinx-spi and xilinx-slave-serial drivers
  and updates the comments and dt-bindings to reflect the fact it also
  supports 7 series devices.

Core:
- Tom cleaned up the fpga-bridge / fpga-mgr core by removing some
  dead-stores.

All patches have been reviewed on the mailing list, and have been in the
last few linux-next releases (as part of my for-next branch) without issues.

Signed-off-by: Moritz Fischer <mdf@kernel.org>

* tag 'fpga-for-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/mdf/linux-fpga:
  fpga: dfl: pci: add device id for Intel FPGA PAC N3000
  Documentation: fpga: dfl: add descriptions for interrupt related interfaces.
  fpga: dfl: afu: add AFU interrupt support
  fpga: dfl: fme: add interrupt support for global error reporting
  fpga: dfl: afu: add interrupt support for port error reporting
  fpga: dfl: introduce interrupt trigger setting API
  fpga: dfl: pci: add irq info for feature devices enumeration
  fpga: dfl: parse interrupt info for feature devices on enumeration
  fpga manager: xilinx-spi: check INIT_B pin during write_init
  dt-bindings: fpga: xilinx-slave-serial: add optional INIT_B GPIO
  fpga: Fix dead store in fpga-bridge.c
  fpga: Fix dead store fpga-mgr.c
  fpga: dfl: Use struct_size() in kzalloc()
  fpga manager: xilinx-spi: remove unneeded, mistyped variables
  fpga manager: xilinx-spi: valid for the 7 Series too
  dt-bindings: fpga: xilinx-slave-serial: valid for the 7 Series too
  fpga: dfl: afu: convert get_user_pages() --> pin_user_pages()
This commit is contained in:
Greg Kroah-Hartman
2020-07-23 09:24:26 +02:00
14 changed files with 687 additions and 47 deletions

View File

@@ -16,15 +16,6 @@
#include "dfl-afu.h"
static void put_all_pages(struct page **pages, int npages)
{
int i;
for (i = 0; i < npages; i++)
if (pages[i])
put_page(pages[i]);
}
void afu_dma_region_init(struct dfl_feature_platform_data *pdata)
{
struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
@@ -57,22 +48,22 @@ static int afu_dma_pin_pages(struct dfl_feature_platform_data *pdata,
goto unlock_vm;
}
pinned = get_user_pages_fast(region->user_addr, npages, FOLL_WRITE,
pinned = pin_user_pages_fast(region->user_addr, npages, FOLL_WRITE,
region->pages);
if (pinned < 0) {
ret = pinned;
goto free_pages;
} else if (pinned != npages) {
ret = -EFAULT;
goto put_pages;
goto unpin_pages;
}
dev_dbg(dev, "%d pages pinned\n", pinned);
return 0;
put_pages:
put_all_pages(region->pages, pinned);
unpin_pages:
unpin_user_pages(region->pages, pinned);
free_pages:
kfree(region->pages);
unlock_vm:
@@ -94,7 +85,7 @@ static void afu_dma_unpin_pages(struct dfl_feature_platform_data *pdata,
long npages = region->length >> PAGE_SHIFT;
struct device *dev = &pdata->dev->dev;
put_all_pages(region->pages, npages);
unpin_user_pages(region->pages, npages);
kfree(region->pages);
account_locked_vm(current->mm, npages, false);

View File

@@ -14,6 +14,7 @@
* Mitchel Henry <henry.mitchel@intel.com>
*/
#include <linux/fpga-dfl.h>
#include <linux/uaccess.h>
#include "dfl-afu.h"
@@ -219,6 +220,21 @@ static void port_err_uinit(struct platform_device *pdev,
afu_port_err_mask(&pdev->dev, true);
}
static long
port_err_ioctl(struct platform_device *pdev, struct dfl_feature *feature,
unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case DFL_FPGA_PORT_ERR_GET_IRQ_NUM:
return dfl_feature_ioctl_get_num_irqs(pdev, feature, arg);
case DFL_FPGA_PORT_ERR_SET_IRQ:
return dfl_feature_ioctl_set_irq(pdev, feature, arg);
default:
dev_dbg(&pdev->dev, "%x cmd not handled", cmd);
return -ENODEV;
}
}
const struct dfl_feature_id port_err_id_table[] = {
{.id = PORT_FEATURE_ID_ERROR,},
{0,}
@@ -227,4 +243,5 @@ const struct dfl_feature_id port_err_id_table[] = {
const struct dfl_feature_ops port_err_ops = {
.init = port_err_init,
.uinit = port_err_uinit,
.ioctl = port_err_ioctl,
};

View File

@@ -529,6 +529,30 @@ static const struct dfl_feature_ops port_stp_ops = {
.init = port_stp_init,
};
static long
port_uint_ioctl(struct platform_device *pdev, struct dfl_feature *feature,
unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case DFL_FPGA_PORT_UINT_GET_IRQ_NUM:
return dfl_feature_ioctl_get_num_irqs(pdev, feature, arg);
case DFL_FPGA_PORT_UINT_SET_IRQ:
return dfl_feature_ioctl_set_irq(pdev, feature, arg);
default:
dev_dbg(&pdev->dev, "%x cmd not handled", cmd);
return -ENODEV;
}
}
static const struct dfl_feature_id port_uint_id_table[] = {
{.id = PORT_FEATURE_ID_UINT,},
{0,}
};
static const struct dfl_feature_ops port_uint_ops = {
.ioctl = port_uint_ioctl,
};
static struct dfl_feature_driver port_feature_drvs[] = {
{
.id_table = port_hdr_id_table,
@@ -546,6 +570,10 @@ static struct dfl_feature_driver port_feature_drvs[] = {
.id_table = port_stp_id_table,
.ops = &port_stp_ops,
},
{
.id_table = port_uint_id_table,
.ops = &port_uint_ops,
},
{
.ops = NULL,
}
@@ -577,6 +605,7 @@ static int afu_release(struct inode *inode, struct file *filp)
{
struct platform_device *pdev = filp->private_data;
struct dfl_feature_platform_data *pdata;
struct dfl_feature *feature;
dev_dbg(&pdev->dev, "Device File Release\n");
@@ -586,6 +615,9 @@ static int afu_release(struct inode *inode, struct file *filp)
dfl_feature_dev_use_end(pdata);
if (!dfl_feature_dev_use_count(pdata)) {
dfl_fpga_dev_for_each_feature(pdata, feature)
dfl_fpga_set_irq_triggers(feature, 0,
feature->nr_irqs, NULL);
__port_reset(pdev);
afu_dma_region_destroy(pdata);
}

View File

@@ -15,6 +15,7 @@
* Mitchel, Henry <henry.mitchel@intel.com>
*/
#include <linux/fpga-dfl.h>
#include <linux/uaccess.h>
#include "dfl.h"
@@ -348,6 +349,22 @@ static void fme_global_err_uinit(struct platform_device *pdev,
fme_err_mask(&pdev->dev, true);
}
static long
fme_global_error_ioctl(struct platform_device *pdev,
struct dfl_feature *feature,
unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case DFL_FPGA_FME_ERR_GET_IRQ_NUM:
return dfl_feature_ioctl_get_num_irqs(pdev, feature, arg);
case DFL_FPGA_FME_ERR_SET_IRQ:
return dfl_feature_ioctl_set_irq(pdev, feature, arg);
default:
dev_dbg(&pdev->dev, "%x cmd not handled", cmd);
return -ENODEV;
}
}
const struct dfl_feature_id fme_global_err_id_table[] = {
{.id = FME_FEATURE_ID_GLOBAL_ERR,},
{0,}
@@ -356,4 +373,5 @@ const struct dfl_feature_id fme_global_err_id_table[] = {
const struct dfl_feature_ops fme_global_err_ops = {
.init = fme_global_err_init,
.uinit = fme_global_err_uinit,
.ioctl = fme_global_error_ioctl,
};

View File

@@ -620,11 +620,17 @@ 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;
struct dfl_feature *feature;
dev_dbg(&pdev->dev, "Device File Release\n");
mutex_lock(&pdata->lock);
dfl_feature_dev_use_end(pdata);
if (!dfl_feature_dev_use_count(pdata))
dfl_fpga_dev_for_each_feature(pdata, feature)
dfl_fpga_set_irq_triggers(feature, 0,
feature->nr_irqs, NULL);
mutex_unlock(&pdata->lock);
return 0;

View File

@@ -39,10 +39,32 @@ static void __iomem *cci_pci_ioremap_bar(struct pci_dev *pcidev, int bar)
return pcim_iomap_table(pcidev)[bar];
}
static int cci_pci_alloc_irq(struct pci_dev *pcidev)
{
int ret, nvec = pci_msix_vec_count(pcidev);
if (nvec <= 0) {
dev_dbg(&pcidev->dev, "fpga interrupt not supported\n");
return 0;
}
ret = pci_alloc_irq_vectors(pcidev, nvec, nvec, PCI_IRQ_MSIX);
if (ret < 0)
return ret;
return nvec;
}
static void cci_pci_free_irq(struct pci_dev *pcidev)
{
pci_free_irq_vectors(pcidev);
}
/* PCI Device ID */
#define PCIE_DEVICE_ID_PF_INT_5_X 0xBCBD
#define PCIE_DEVICE_ID_PF_INT_6_X 0xBCC0
#define PCIE_DEVICE_ID_PF_DSC_1_X 0x09C4
#define PCIE_DEVICE_ID_INTEL_PAC_N3000 0x0B30
/* VF Device */
#define PCIE_DEVICE_ID_VF_INT_5_X 0xBCBF
#define PCIE_DEVICE_ID_VF_INT_6_X 0xBCC1
@@ -55,6 +77,7 @@ static struct pci_device_id cci_pcie_id_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_VF_INT_6_X),},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_PF_DSC_1_X),},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_VF_DSC_1_X),},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_INTEL_PAC_N3000),},
{0,}
};
MODULE_DEVICE_TABLE(pci, cci_pcie_id_tbl);
@@ -78,17 +101,34 @@ static void cci_remove_feature_devs(struct pci_dev *pcidev)
/* remove all children feature devices */
dfl_fpga_feature_devs_remove(drvdata->cdev);
cci_pci_free_irq(pcidev);
}
static int *cci_pci_create_irq_table(struct pci_dev *pcidev, unsigned int nvec)
{
unsigned int i;
int *table;
table = kcalloc(nvec, sizeof(int), GFP_KERNEL);
if (!table)
return table;
for (i = 0; i < nvec; i++)
table[i] = pci_irq_vector(pcidev, i);
return table;
}
/* enumerate feature devices under pci device */
static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
{
struct cci_drvdata *drvdata = pci_get_drvdata(pcidev);
int port_num, bar, i, nvec, ret = 0;
struct dfl_fpga_enum_info *info;
struct dfl_fpga_cdev *cdev;
resource_size_t start, len;
int port_num, bar, i, ret = 0;
void __iomem *base;
int *irq_table;
u32 offset;
u64 v;
@@ -97,11 +137,30 @@ static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
if (!info)
return -ENOMEM;
/* add irq info for enumeration if the device support irq */
nvec = cci_pci_alloc_irq(pcidev);
if (nvec < 0) {
dev_err(&pcidev->dev, "Fail to alloc irq %d.\n", nvec);
ret = nvec;
goto enum_info_free_exit;
} else if (nvec) {
irq_table = cci_pci_create_irq_table(pcidev, nvec);
if (!irq_table) {
ret = -ENOMEM;
goto irq_free_exit;
}
ret = dfl_fpga_enum_info_add_irq(info, nvec, irq_table);
kfree(irq_table);
if (ret)
goto irq_free_exit;
}
/* start to find Device Feature List from Bar 0 */
base = cci_pci_ioremap_bar(pcidev, 0);
if (!base) {
ret = -ENOMEM;
goto enum_info_free_exit;
goto irq_free_exit;
}
/*
@@ -154,7 +213,7 @@ static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
dfl_fpga_enum_info_add_dfl(info, start, len, base);
} else {
ret = -ENODEV;
goto enum_info_free_exit;
goto irq_free_exit;
}
/* start enumeration with prepared enumeration information */
@@ -162,11 +221,14 @@ static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
if (IS_ERR(cdev)) {
dev_err(&pcidev->dev, "Enumeration failure\n");
ret = PTR_ERR(cdev);
goto enum_info_free_exit;
goto irq_free_exit;
}
drvdata->cdev = cdev;
irq_free_exit:
if (ret)
cci_pci_free_irq(pcidev);
enum_info_free_exit:
dfl_fpga_enum_info_free(info);
@@ -211,12 +273,10 @@ int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid)
}
ret = cci_enumerate_feature_devs(pcidev);
if (ret) {
dev_err(&pcidev->dev, "enumeration failure %d.\n", ret);
goto disable_error_report_exit;
}
if (!ret)
return ret;
return ret;
dev_err(&pcidev->dev, "enumeration failure %d.\n", ret);
disable_error_report_exit:
pci_disable_pcie_error_reporting(pcidev);

View File

@@ -10,7 +10,9 @@
* Wu Hao <hao.wu@intel.com>
* Xiao Guangrong <guangrong.xiao@linux.intel.com>
*/
#include <linux/fpga-dfl.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include "dfl.h"
@@ -421,6 +423,9 @@ EXPORT_SYMBOL_GPL(dfl_fpga_dev_ops_unregister);
*
* @dev: device to enumerate.
* @cdev: the container device for all feature devices.
* @nr_irqs: number of irqs for all feature devices.
* @irq_table: Linux IRQ numbers for all irqs, indexed by local irq index of
* this device.
* @feature_dev: current feature device.
* @ioaddr: header register region address of feature device in enumeration.
* @sub_features: a sub features linked list for feature device in enumeration.
@@ -429,6 +434,9 @@ EXPORT_SYMBOL_GPL(dfl_fpga_dev_ops_unregister);
struct build_feature_devs_info {
struct device *dev;
struct dfl_fpga_cdev *cdev;
unsigned int nr_irqs;
int *irq_table;
struct platform_device *feature_dev;
void __iomem *ioaddr;
struct list_head sub_features;
@@ -442,12 +450,16 @@ struct build_feature_devs_info {
* @mmio_res: mmio resource of this sub feature.
* @ioaddr: mapped base address of mmio resource.
* @node: node in sub_features linked list.
* @irq_base: start of irq index in this sub feature.
* @nr_irqs: number of irqs of this sub feature.
*/
struct dfl_feature_info {
u64 fid;
struct resource mmio_res;
void __iomem *ioaddr;
struct list_head node;
unsigned int irq_base;
unsigned int nr_irqs;
};
static void dfl_fpga_cdev_add_port_dev(struct dfl_fpga_cdev *cdev,
@@ -487,8 +499,7 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
* it will be automatically freed by device's release() callback,
* platform_device_release().
*/
pdata = kzalloc(dfl_feature_platform_data_size(binfo->feature_num),
GFP_KERNEL);
pdata = kzalloc(struct_size(pdata, features, binfo->feature_num), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
@@ -520,13 +531,30 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
/* fill features and resource information for feature dev */
list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) {
struct dfl_feature *feature = &pdata->features[index];
struct dfl_feature_irq_ctx *ctx;
unsigned int i;
/* save resource information for each feature */
feature->dev = fdev;
feature->id = finfo->fid;
feature->resource_index = index;
feature->ioaddr = finfo->ioaddr;
fdev->resource[index++] = finfo->mmio_res;
if (finfo->nr_irqs) {
ctx = devm_kcalloc(binfo->dev, finfo->nr_irqs,
sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
for (i = 0; i < finfo->nr_irqs; i++)
ctx[i].irq =
binfo->irq_table[finfo->irq_base + i];
feature->irq_ctx = ctx;
feature->nr_irqs = finfo->nr_irqs;
}
list_del(&finfo->node);
kfree(finfo);
}
@@ -638,6 +666,78 @@ static u64 feature_id(void __iomem *start)
return 0;
}
static int parse_feature_irqs(struct build_feature_devs_info *binfo,
resource_size_t ofst, u64 fid,
unsigned int *irq_base, unsigned int *nr_irqs)
{
void __iomem *base = binfo->ioaddr + ofst;
unsigned int i, ibase, inr = 0;
int virq;
u64 v;
/*
* Ideally DFL framework should only read info from DFL header, but
* current version DFL only provides mmio resources information for
* each feature in DFL Header, no field for interrupt resources.
* Interrupt resource information is provided by specific mmio
* registers of each private feature which supports interrupt. So in
* order to parse and assign irq resources, DFL framework has to look
* into specific capability registers of these private features.
*
* Once future DFL version supports generic interrupt resource
* information in common DFL headers, the generic interrupt parsing
* code will be added. But in order to be compatible to old version
* DFL, the driver may still fall back to these quirks.
*/
switch (fid) {
case PORT_FEATURE_ID_UINT:
v = readq(base + PORT_UINT_CAP);
ibase = FIELD_GET(PORT_UINT_CAP_FST_VECT, v);
inr = FIELD_GET(PORT_UINT_CAP_INT_NUM, v);
break;
case PORT_FEATURE_ID_ERROR:
v = readq(base + PORT_ERROR_CAP);
ibase = FIELD_GET(PORT_ERROR_CAP_INT_VECT, v);
inr = FIELD_GET(PORT_ERROR_CAP_SUPP_INT, v);
break;
case FME_FEATURE_ID_GLOBAL_ERR:
v = readq(base + FME_ERROR_CAP);
ibase = FIELD_GET(FME_ERROR_CAP_INT_VECT, v);
inr = FIELD_GET(FME_ERROR_CAP_SUPP_INT, v);
break;
}
if (!inr) {
*irq_base = 0;
*nr_irqs = 0;
return 0;
}
dev_dbg(binfo->dev, "feature: 0x%llx, irq_base: %u, nr_irqs: %u\n",
fid, ibase, inr);
if (ibase + inr > binfo->nr_irqs) {
dev_err(binfo->dev,
"Invalid interrupt number in feature 0x%llx\n", fid);
return -EINVAL;
}
for (i = 0; i < inr; i++) {
virq = binfo->irq_table[ibase + i];
if (virq < 0 || virq > NR_IRQS) {
dev_err(binfo->dev,
"Invalid irq table entry for feature 0x%llx\n",
fid);
return -EINVAL;
}
}
*irq_base = ibase;
*nr_irqs = inr;
return 0;
}
/*
* when create sub feature instances, for private features, it doesn't need
* to provide resource size and feature id as they could be read from DFH
@@ -650,7 +750,9 @@ create_feature_instance(struct build_feature_devs_info *binfo,
struct dfl_fpga_enum_dfl *dfl, resource_size_t ofst,
resource_size_t size, u64 fid)
{
unsigned int irq_base, nr_irqs;
struct dfl_feature_info *finfo;
int ret;
/* read feature size and id if inputs are invalid */
size = size ? size : feature_size(dfl->ioaddr + ofst);
@@ -659,6 +761,10 @@ create_feature_instance(struct build_feature_devs_info *binfo,
if (dfl->len - ofst < size)
return -EINVAL;
ret = parse_feature_irqs(binfo, ofst, fid, &irq_base, &nr_irqs);
if (ret)
return ret;
finfo = kzalloc(sizeof(*finfo), GFP_KERNEL);
if (!finfo)
return -ENOMEM;
@@ -667,6 +773,8 @@ create_feature_instance(struct build_feature_devs_info *binfo,
finfo->mmio_res.start = dfl->start + ofst;
finfo->mmio_res.end = finfo->mmio_res.start + size - 1;
finfo->mmio_res.flags = IORESOURCE_MEM;
finfo->irq_base = irq_base;
finfo->nr_irqs = nr_irqs;
finfo->ioaddr = dfl->ioaddr + ofst;
list_add_tail(&finfo->node, &binfo->sub_features);
@@ -853,6 +961,10 @@ void dfl_fpga_enum_info_free(struct dfl_fpga_enum_info *info)
devm_kfree(dev, dfl);
}
/* remove irq table */
if (info->irq_table)
devm_kfree(dev, info->irq_table);
devm_kfree(dev, info);
put_device(dev);
}
@@ -892,6 +1004,45 @@ int dfl_fpga_enum_info_add_dfl(struct dfl_fpga_enum_info *info,
}
EXPORT_SYMBOL_GPL(dfl_fpga_enum_info_add_dfl);
/**
* dfl_fpga_enum_info_add_irq - add irq table to enum info
*
* @info: ptr to dfl_fpga_enum_info
* @nr_irqs: number of irqs of the DFL fpga device to be enumerated.
* @irq_table: Linux IRQ numbers for all irqs, indexed by local irq index of
* this device.
*
* One FPGA device may have several interrupts. This function adds irq
* information of the DFL fpga device to enum info for next step enumeration.
* This function should be called before dfl_fpga_feature_devs_enumerate().
* As we only support one irq domain for all DFLs in the same enum info, adding
* irq table a second time for the same enum info will return error.
*
* If we need to enumerate DFLs which belong to different irq domains, we
* should fill more enum info and enumerate them one by one.
*
* Return: 0 on success, negative error code otherwise.
*/
int dfl_fpga_enum_info_add_irq(struct dfl_fpga_enum_info *info,
unsigned int nr_irqs, int *irq_table)
{
if (!nr_irqs || !irq_table)
return -EINVAL;
if (info->irq_table)
return -EEXIST;
info->irq_table = devm_kmemdup(info->dev, irq_table,
sizeof(int) * nr_irqs, GFP_KERNEL);
if (!info->irq_table)
return -ENOMEM;
info->nr_irqs = nr_irqs;
return 0;
}
EXPORT_SYMBOL_GPL(dfl_fpga_enum_info_add_irq);
static int remove_feature_dev(struct device *dev, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -959,6 +1110,10 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info)
binfo->dev = info->dev;
binfo->cdev = cdev;
binfo->nr_irqs = info->nr_irqs;
if (info->nr_irqs)
binfo->irq_table = info->irq_table;
/*
* start enumeration for all feature devices based on Device Feature
* Lists.
@@ -1241,6 +1396,160 @@ done:
}
EXPORT_SYMBOL_GPL(dfl_fpga_cdev_config_ports_vf);
static irqreturn_t dfl_irq_handler(int irq, void *arg)
{
struct eventfd_ctx *trigger = arg;
eventfd_signal(trigger, 1);
return IRQ_HANDLED;
}
static int do_set_irq_trigger(struct dfl_feature *feature, unsigned int idx,
int fd)
{
struct platform_device *pdev = feature->dev;
struct eventfd_ctx *trigger;
int irq, ret;
irq = feature->irq_ctx[idx].irq;
if (feature->irq_ctx[idx].trigger) {
free_irq(irq, feature->irq_ctx[idx].trigger);
kfree(feature->irq_ctx[idx].name);
eventfd_ctx_put(feature->irq_ctx[idx].trigger);
feature->irq_ctx[idx].trigger = NULL;
}
if (fd < 0)
return 0;
feature->irq_ctx[idx].name =
kasprintf(GFP_KERNEL, "fpga-irq[%u](%s-%llx)", idx,
dev_name(&pdev->dev), feature->id);
if (!feature->irq_ctx[idx].name)
return -ENOMEM;
trigger = eventfd_ctx_fdget(fd);
if (IS_ERR(trigger)) {
ret = PTR_ERR(trigger);
goto free_name;
}
ret = request_irq(irq, dfl_irq_handler, 0,
feature->irq_ctx[idx].name, trigger);
if (!ret) {
feature->irq_ctx[idx].trigger = trigger;
return ret;
}
eventfd_ctx_put(trigger);
free_name:
kfree(feature->irq_ctx[idx].name);
return ret;
}
/**
* dfl_fpga_set_irq_triggers - set eventfd triggers for dfl feature interrupts
*
* @feature: dfl sub feature.
* @start: start of irq index in this dfl sub feature.
* @count: number of irqs.
* @fds: eventfds to bind with irqs. unbind related irq if fds[n] is negative.
* unbind "count" specified number of irqs if fds ptr is NULL.
*
* Bind given eventfds with irqs in this dfl sub feature. Unbind related irq if
* fds[n] is negative. Unbind "count" specified number of irqs if fds ptr is
* NULL.
*
* Return: 0 on success, negative error code otherwise.
*/
int dfl_fpga_set_irq_triggers(struct dfl_feature *feature, unsigned int start,
unsigned int count, int32_t *fds)
{
unsigned int i;
int ret = 0;
/* overflow */
if (unlikely(start + count < start))
return -EINVAL;
/* exceeds nr_irqs */
if (start + count > feature->nr_irqs)
return -EINVAL;
for (i = 0; i < count; i++) {
int fd = fds ? fds[i] : -1;
ret = do_set_irq_trigger(feature, start + i, fd);
if (ret) {
while (i--)
do_set_irq_trigger(feature, start + i, -1);
break;
}
}
return ret;
}
EXPORT_SYMBOL_GPL(dfl_fpga_set_irq_triggers);
/**
* dfl_feature_ioctl_get_num_irqs - dfl feature _GET_IRQ_NUM ioctl interface.
* @pdev: the feature device which has the sub feature
* @feature: the dfl sub feature
* @arg: ioctl argument
*
* Return: 0 on success, negative error code otherwise.
*/
long dfl_feature_ioctl_get_num_irqs(struct platform_device *pdev,
struct dfl_feature *feature,
unsigned long arg)
{
return put_user(feature->nr_irqs, (__u32 __user *)arg);
}
EXPORT_SYMBOL_GPL(dfl_feature_ioctl_get_num_irqs);
/**
* dfl_feature_ioctl_set_irq - dfl feature _SET_IRQ ioctl interface.
* @pdev: the feature device which has the sub feature
* @feature: the dfl sub feature
* @arg: ioctl argument
*
* Return: 0 on success, negative error code otherwise.
*/
long dfl_feature_ioctl_set_irq(struct platform_device *pdev,
struct dfl_feature *feature,
unsigned long arg)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_fpga_irq_set hdr;
s32 *fds;
long ret;
if (!feature->nr_irqs)
return -ENOENT;
if (copy_from_user(&hdr, (void __user *)arg, sizeof(hdr)))
return -EFAULT;
if (!hdr.count || (hdr.start + hdr.count > feature->nr_irqs) ||
(hdr.start + hdr.count < hdr.start))
return -EINVAL;
fds = memdup_user((void __user *)(arg + sizeof(hdr)),
hdr.count * sizeof(s32));
if (IS_ERR(fds))
return PTR_ERR(fds);
mutex_lock(&pdata->lock);
ret = dfl_fpga_set_irq_triggers(feature, hdr.start, hdr.count, fds);
mutex_unlock(&pdata->lock);
kfree(fds);
return ret;
}
EXPORT_SYMBOL_GPL(dfl_feature_ioctl_set_irq);
static void __exit dfl_fpga_exit(void)
{
dfl_chardev_uinit();

View File

@@ -17,7 +17,9 @@
#include <linux/bitfield.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/eventfd.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/platform_device.h>
@@ -112,6 +114,13 @@
#define FME_PORT_OFST_ACC_VF 1
#define FME_PORT_OFST_IMP BIT_ULL(60)
/* FME Error Capability Register */
#define FME_ERROR_CAP 0x70
/* FME Error Capability Register Bitfield */
#define FME_ERROR_CAP_SUPP_INT BIT_ULL(0) /* Interrupt Support */
#define FME_ERROR_CAP_INT_VECT GENMASK_ULL(12, 1) /* Interrupt vector */
/* PORT Header Register Set */
#define PORT_HDR_DFH DFH
#define PORT_HDR_GUID_L GUID_L
@@ -145,6 +154,20 @@
#define PORT_STS_PWR_STATE_AP2 2 /* 90% throttling */
#define PORT_STS_PWR_STATE_AP6 6 /* 100% throttling */
/* Port Error Capability Register */
#define PORT_ERROR_CAP 0x38
/* Port Error Capability Register Bitfield */
#define PORT_ERROR_CAP_SUPP_INT BIT_ULL(0) /* Interrupt Support */
#define PORT_ERROR_CAP_INT_VECT GENMASK_ULL(12, 1) /* Interrupt vector */
/* Port Uint Capability Register */
#define PORT_UINT_CAP 0x8
/* Port Uint Capability Register Bitfield */
#define PORT_UINT_CAP_INT_NUM GENMASK_ULL(11, 0) /* Interrupts num */
#define PORT_UINT_CAP_FST_VECT GENMASK_ULL(23, 12) /* First Vector */
/**
* struct dfl_fpga_port_ops - port ops
*
@@ -188,21 +211,40 @@ struct dfl_feature_driver {
const struct dfl_feature_ops *ops;
};
/**
* struct dfl_feature_irq_ctx - dfl private feature interrupt context
*
* @irq: Linux IRQ number of this interrupt.
* @trigger: eventfd context to signal when interrupt happens.
* @name: irq name needed when requesting irq.
*/
struct dfl_feature_irq_ctx {
int irq;
struct eventfd_ctx *trigger;
char *name;
};
/**
* struct dfl_feature - sub feature of the feature devices
*
* @dev: ptr to pdev of the feature device which has the sub feature.
* @id: sub feature id.
* @resource_index: each sub feature has one mmio resource for its registers.
* this index is used to find its mmio resource from the
* feature dev (platform device)'s reources.
* @ioaddr: mapped mmio resource address.
* @irq_ctx: interrupt context list.
* @nr_irqs: number of interrupt contexts.
* @ops: ops of this sub feature.
* @priv: priv data of this feature.
*/
struct dfl_feature {
struct platform_device *dev;
u64 id;
int resource_index;
void __iomem *ioaddr;
struct dfl_feature_irq_ctx *irq_ctx;
unsigned int nr_irqs;
const struct dfl_feature_ops *ops;
void *priv;
};
@@ -299,12 +341,6 @@ struct dfl_feature_ops {
#define DFL_FPGA_FEATURE_DEV_FME "dfl-fme"
#define DFL_FPGA_FEATURE_DEV_PORT "dfl-port"
static inline int dfl_feature_platform_data_size(const int num)
{
return sizeof(struct dfl_feature_platform_data) +
num * sizeof(struct dfl_feature);
}
void dfl_fpga_dev_feature_uinit(struct platform_device *pdev);
int dfl_fpga_dev_feature_init(struct platform_device *pdev,
struct dfl_feature_driver *feature_drvs);
@@ -390,10 +426,14 @@ static inline u8 dfl_feature_revision(void __iomem *base)
*
* @dev: parent device.
* @dfls: list of device feature lists.
* @nr_irqs: number of irqs for all feature devices.
* @irq_table: Linux IRQ numbers for all irqs, indexed by hw irq numbers.
*/
struct dfl_fpga_enum_info {
struct device *dev;
struct list_head dfls;
unsigned int nr_irqs;
int *irq_table;
};
/**
@@ -417,6 +457,8 @@ struct dfl_fpga_enum_info *dfl_fpga_enum_info_alloc(struct device *dev);
int dfl_fpga_enum_info_add_dfl(struct dfl_fpga_enum_info *info,
resource_size_t start, resource_size_t len,
void __iomem *ioaddr);
int dfl_fpga_enum_info_add_irq(struct dfl_fpga_enum_info *info,
unsigned int nr_irqs, int *irq_table);
void dfl_fpga_enum_info_free(struct dfl_fpga_enum_info *info);
/**
@@ -468,4 +510,13 @@ int dfl_fpga_cdev_release_port(struct dfl_fpga_cdev *cdev, int port_id);
int dfl_fpga_cdev_assign_port(struct dfl_fpga_cdev *cdev, int port_id);
void dfl_fpga_cdev_config_ports_pf(struct dfl_fpga_cdev *cdev);
int dfl_fpga_cdev_config_ports_vf(struct dfl_fpga_cdev *cdev, int num_vf);
int dfl_fpga_set_irq_triggers(struct dfl_feature *feature, unsigned int start,
unsigned int count, int32_t *fds);
long dfl_feature_ioctl_get_num_irqs(struct platform_device *pdev,
struct dfl_feature *feature,
unsigned long arg);
long dfl_feature_ioctl_set_irq(struct platform_device *pdev,
struct dfl_feature *feature,
unsigned long arg);
#endif /* __FPGA_DFL_H */

View File

@@ -328,7 +328,7 @@ struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name,
void *priv)
{
struct fpga_bridge *bridge;
int id, ret = 0;
int id, ret;
if (!name || !strlen(name)) {
dev_err(dev, "Attempt to register with no name!\n");
@@ -340,10 +340,8 @@ struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name,
return NULL;
id = ida_simple_get(&fpga_bridge_ida, 0, 0, GFP_KERNEL);
if (id < 0) {
ret = id;
if (id < 0)
goto error_kfree;
}
mutex_init(&bridge->mutex);
INIT_LIST_HEAD(&bridge->node);

View File

@@ -581,10 +581,8 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
return NULL;
id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
if (id < 0) {
ret = id;
if (id < 0)
goto error_kfree;
}
mutex_init(&mgr->ref_mutex);

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Xilinx Spartan6 Slave Serial SPI Driver
* Xilinx Spartan6 and 7 Series Slave Serial SPI Driver
*
* Copyright (C) 2017 DENX Software Engineering
*
@@ -23,6 +23,7 @@
struct xilinx_spi_conf {
struct spi_device *spi;
struct gpio_desc *prog_b;
struct gpio_desc *init_b;
struct gpio_desc *done;
};
@@ -36,13 +37,45 @@ static enum fpga_mgr_states xilinx_spi_state(struct fpga_manager *mgr)
return FPGA_MGR_STATE_UNKNOWN;
}
/**
* wait_for_init_b - wait for the INIT_B pin to have a given state, or wait
* a given delay if the pin is unavailable
*
* @mgr: The FPGA manager object
* @value: Value INIT_B to wait for (1 = asserted = low)
* @alt_udelay: Delay to wait if the INIT_B GPIO is not available
*
* Returns 0 when the INIT_B GPIO reached the given state or -ETIMEDOUT if
* too much time passed waiting for that. If no INIT_B GPIO is available
* then always return 0.
*/
static int wait_for_init_b(struct fpga_manager *mgr, int value,
unsigned long alt_udelay)
{
struct xilinx_spi_conf *conf = mgr->priv;
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
if (conf->init_b) {
while (time_before(jiffies, timeout)) {
/* dump_state(conf, "wait for init_d .."); */
if (gpiod_get_value(conf->init_b) == value)
return 0;
usleep_range(100, 400);
}
return -ETIMEDOUT;
}
udelay(alt_udelay);
return 0;
}
static int xilinx_spi_write_init(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count)
{
struct xilinx_spi_conf *conf = mgr->priv;
const size_t prog_latency_7500us = 7500;
const size_t prog_pulse_1us = 1;
int err;
if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
dev_err(&mgr->dev, "Partial reconfiguration not supported.\n");
@@ -51,17 +84,28 @@ static int xilinx_spi_write_init(struct fpga_manager *mgr,
gpiod_set_value(conf->prog_b, 1);
udelay(prog_pulse_1us); /* min is 500 ns */
err = wait_for_init_b(mgr, 1, 1); /* min is 500 ns */
if (err) {
dev_err(&mgr->dev, "INIT_B pin did not go low\n");
gpiod_set_value(conf->prog_b, 0);
return err;
}
gpiod_set_value(conf->prog_b, 0);
err = wait_for_init_b(mgr, 0, 0);
if (err) {
dev_err(&mgr->dev, "INIT_B pin did not go high\n");
return err;
}
if (gpiod_get_value(conf->done)) {
dev_err(&mgr->dev, "Unexpected DONE pin state...\n");
return -EIO;
}
/* program latency */
usleep_range(prog_latency_7500us, prog_latency_7500us + 100);
usleep_range(7500, 7600);
return 0;
}
@@ -156,6 +200,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
return PTR_ERR(conf->prog_b);
}
conf->init_b = devm_gpiod_get_optional(&spi->dev, "init-b", GPIOD_IN);
if (IS_ERR(conf->init_b)) {
dev_err(&spi->dev, "Failed to get INIT_B gpio: %ld\n",
PTR_ERR(conf->init_b));
return PTR_ERR(conf->init_b);
}
conf->done = devm_gpiod_get(&spi->dev, "done", GPIOD_IN);
if (IS_ERR(conf->done)) {
dev_err(&spi->dev, "Failed to get DONE gpio: %ld\n",