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> -----BEGIN PGP SIGNATURE----- iIUEABYIAC0WIQRORt0E5Sb/c/mZMgkXxQAtim5VSwUCXxJbZQ8cbWRmQGtlcm5l bC5vcmcACgkQF8UALYpuVUsAIgD/eMm7y2Kgw0vnteTR4diLH0T8isv24ZjgRNL1 2FpscKsBAIp7XJUF9z1I85D476GAlArbxGK8BpscbKPJVw3mP9AC =Owcl -----END PGP SIGNATURE----- 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:
commit
cb0cec23ce
@ -1,11 +1,14 @@
|
||||
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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user