Commit cb0cec23 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'fpga-for-5.9' of...

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: default avatarMoritz 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()
parents 575ec5e5 eacfbf58
Xilinx Slave Serial SPI FPGA Manager
Xilinx Spartan-6 FPGAs support a method of loading the bitstream over
what is referred to as "slave serial" interface.
Xilinx Spartan-6 and 7 Series FPGAs support a method of loading the
bitstream over what is referred to as "slave serial" interface.
The slave serial link is not technically SPI, and might require extra
circuits in order to play nicely with other SPI slaves on the same bus.
See https://www.xilinx.com/support/documentation/user_guides/ug380.pdf
See:
- https://www.xilinx.com/support/documentation/user_guides/ug380.pdf
- https://www.xilinx.com/support/documentation/user_guides/ug470_7Series_Config.pdf
- https://www.xilinx.com/support/documentation/application_notes/xapp583-fpga-configuration.pdf
Required properties:
- compatible: should contain "xlnx,fpga-slave-serial"
......@@ -13,6 +16,10 @@ Required properties:
- prog_b-gpios: config pin (referred to as PROGRAM_B in the manual)
- done-gpios: config status pin (referred to as DONE in the manual)
Optional properties:
- init-b-gpios: initialization status and configuration error pin
(referred to as INIT_B in the manual)
Example for full FPGA configuration:
fpga-region0 {
......@@ -37,7 +44,8 @@ Example for full FPGA configuration:
spi-max-frequency = <60000000>;
spi-cpha;
reg = <0>;
done-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
prog_b-gpios = <&gpio0 29 GPIO_ACTIVE_LOW>;
init-b-gpios = <&gpio0 28 GPIO_ACTIVE_LOW>;
done-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
};
};
......@@ -89,6 +89,8 @@ The following functions are exposed through ioctls:
- Program bitstream (DFL_FPGA_FME_PORT_PR)
- Assign port to PF (DFL_FPGA_FME_PORT_ASSIGN)
- Release port from PF (DFL_FPGA_FME_PORT_RELEASE)
- Get number of irqs of FME global error (DFL_FPGA_FME_ERR_GET_IRQ_NUM)
- Set interrupt trigger for FME error (DFL_FPGA_FME_ERR_SET_IRQ)
More functions are exposed through sysfs
(/sys/class/fpga_region/regionX/dfl-fme.n/):
......@@ -149,6 +151,10 @@ The following functions are exposed through ioctls:
- Map DMA buffer (DFL_FPGA_PORT_DMA_MAP)
- Unmap DMA buffer (DFL_FPGA_PORT_DMA_UNMAP)
- Reset AFU (DFL_FPGA_PORT_RESET)
- Get number of irqs of port error (DFL_FPGA_PORT_ERR_GET_IRQ_NUM)
- Set interrupt trigger for port error (DFL_FPGA_PORT_ERR_SET_IRQ)
- Get number of irqs of UINT (DFL_FPGA_PORT_UINT_GET_IRQ_NUM)
- Set interrupt trigger for UINT (DFL_FPGA_PORT_UINT_SET_IRQ)
DFL_FPGA_PORT_RESET:
reset the FPGA Port and its AFU. Userspace can do Port
......@@ -462,6 +468,19 @@ since they are system-wide counters on FPGA device.
The current driver does not support sampling. So "perf record" is unsupported.
Interrupt support
=================
Some FME and AFU private features are able to generate interrupts. As mentioned
above, users could call ioctl (DFL_FPGA_*_GET_IRQ_NUM) to know whether or how
many interrupts are supported for this private feature. Drivers also implement
an eventfd based interrupt handling mechanism for users to get notified when
interrupt happens. Users could set eventfds to driver via
ioctl (DFL_FPGA_*_SET_IRQ), and then poll/select on these eventfds waiting for
notification.
In Current DFL, 3 sub features (Port error, FME global error and AFU interrupt)
support interrupts.
Add new FIUs support
====================
It's possible that developers made some new function blocks (FIUs) under this
......
......@@ -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);
......
......@@ -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,
};
......@@ -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);
}
......
......@@ -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,
};
......@@ -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;
......
......@@ -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);
......
This diff is collapsed.
......@@ -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 */
......@@ -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);
......
......@@ -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);
......
// 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",
......
......@@ -151,6 +151,65 @@ struct dfl_fpga_port_dma_unmap {
#define DFL_FPGA_PORT_DMA_UNMAP _IO(DFL_FPGA_MAGIC, DFL_PORT_BASE + 4)
/**
* struct dfl_fpga_irq_set - the argument for DFL_FPGA_XXX_SET_IRQ ioctl.
*
* @start: Index of the first irq.
* @count: The number of eventfd handler.
* @evtfds: Eventfd handlers.
*/
struct dfl_fpga_irq_set {
__u32 start;
__u32 count;
__s32 evtfds[];
};
/**
* DFL_FPGA_PORT_ERR_GET_IRQ_NUM - _IOR(DFL_FPGA_MAGIC, DFL_PORT_BASE + 5,
* __u32 num_irqs)
*
* Get the number of irqs supported by the fpga port error reporting private
* feature. Currently hardware supports up to 1 irq.
* Return: 0 on success, -errno on failure.
*/
#define DFL_FPGA_PORT_ERR_GET_IRQ_NUM _IOR(DFL_FPGA_MAGIC, \
DFL_PORT_BASE + 5, __u32)
/**
* DFL_FPGA_PORT_ERR_SET_IRQ - _IOW(DFL_FPGA_MAGIC, DFL_PORT_BASE + 6,
* struct dfl_fpga_irq_set)
*
* Set fpga port error reporting interrupt trigger if evtfds[n] is valid.
* Unset related interrupt trigger if evtfds[n] is a negative value.
* Return: 0 on success, -errno on failure.
*/
#define DFL_FPGA_PORT_ERR_SET_IRQ _IOW(DFL_FPGA_MAGIC, \
DFL_PORT_BASE + 6, \
struct dfl_fpga_irq_set)
/**
* DFL_FPGA_PORT_UINT_GET_IRQ_NUM - _IOR(DFL_FPGA_MAGIC, DFL_PORT_BASE + 7,
* __u32 num_irqs)
*
* Get the number of irqs supported by the fpga AFU interrupt private
* feature.
* Return: 0 on success, -errno on failure.
*/
#define DFL_FPGA_PORT_UINT_GET_IRQ_NUM _IOR(DFL_FPGA_MAGIC, \
DFL_PORT_BASE + 7, __u32)
/**
* DFL_FPGA_PORT_UINT_SET_IRQ - _IOW(DFL_FPGA_MAGIC, DFL_PORT_BASE + 8,
* struct dfl_fpga_irq_set)
*
* Set fpga AFU interrupt trigger if evtfds[n] is valid.
* Unset related interrupt trigger if evtfds[n] is a negative value.
* Return: 0 on success, -errno on failure.
*/
#define DFL_FPGA_PORT_UINT_SET_IRQ _IOW(DFL_FPGA_MAGIC, \
DFL_PORT_BASE + 8, \
struct dfl_fpga_irq_set)
/* IOCTLs for FME file descriptor */
/**
......@@ -194,4 +253,27 @@ struct dfl_fpga_fme_port_pr {
*/
#define DFL_FPGA_FME_PORT_ASSIGN _IOW(DFL_FPGA_MAGIC, DFL_FME_BASE + 2, int)
/**
* DFL_FPGA_FME_ERR_GET_IRQ_NUM - _IOR(DFL_FPGA_MAGIC, DFL_FME_BASE + 3,
* __u32 num_irqs)
*
* Get the number of irqs supported by the fpga fme error reporting private
* feature. Currently hardware supports up to 1 irq.
* Return: 0 on success, -errno on failure.
*/
#define DFL_FPGA_FME_ERR_GET_IRQ_NUM _IOR(DFL_FPGA_MAGIC, \
DFL_FME_BASE + 3, __u32)
/**
* DFL_FPGA_FME_ERR_SET_IRQ - _IOW(DFL_FPGA_MAGIC, DFL_FME_BASE + 4,
* struct dfl_fpga_irq_set)
*
* Set fpga fme error reporting interrupt trigger if evtfds[n] is valid.
* Unset related interrupt trigger if evtfds[n] is a negative value.
* Return: 0 on success, -errno on failure.
*/
#define DFL_FPGA_FME_ERR_SET_IRQ _IOW(DFL_FPGA_MAGIC, \
DFL_FME_BASE + 4, \
struct dfl_fpga_irq_set)
#endif /* _UAPI_LINUX_FPGA_DFL_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment