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 Slave Serial SPI FPGA Manager
|
||||||
|
|
||||||
Xilinx Spartan-6 FPGAs support a method of loading the bitstream over
|
Xilinx Spartan-6 and 7 Series FPGAs support a method of loading the
|
||||||
what is referred to as "slave serial" interface.
|
bitstream over what is referred to as "slave serial" interface.
|
||||||
The slave serial link is not technically SPI, and might require extra
|
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.
|
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:
|
Required properties:
|
||||||
- compatible: should contain "xlnx,fpga-slave-serial"
|
- 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)
|
- 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)
|
- 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:
|
Example for full FPGA configuration:
|
||||||
|
|
||||||
fpga-region0 {
|
fpga-region0 {
|
||||||
@ -37,7 +44,8 @@ Example for full FPGA configuration:
|
|||||||
spi-max-frequency = <60000000>;
|
spi-max-frequency = <60000000>;
|
||||||
spi-cpha;
|
spi-cpha;
|
||||||
reg = <0>;
|
reg = <0>;
|
||||||
done-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
|
|
||||||
prog_b-gpios = <&gpio0 29 GPIO_ACTIVE_LOW>;
|
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)
|
- Program bitstream (DFL_FPGA_FME_PORT_PR)
|
||||||
- Assign port to PF (DFL_FPGA_FME_PORT_ASSIGN)
|
- Assign port to PF (DFL_FPGA_FME_PORT_ASSIGN)
|
||||||
- Release port from PF (DFL_FPGA_FME_PORT_RELEASE)
|
- 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
|
More functions are exposed through sysfs
|
||||||
(/sys/class/fpga_region/regionX/dfl-fme.n/):
|
(/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)
|
- Map DMA buffer (DFL_FPGA_PORT_DMA_MAP)
|
||||||
- Unmap DMA buffer (DFL_FPGA_PORT_DMA_UNMAP)
|
- Unmap DMA buffer (DFL_FPGA_PORT_DMA_UNMAP)
|
||||||
- Reset AFU (DFL_FPGA_PORT_RESET)
|
- 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:
|
DFL_FPGA_PORT_RESET:
|
||||||
reset the FPGA Port and its AFU. Userspace can do Port
|
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.
|
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
|
Add new FIUs support
|
||||||
====================
|
====================
|
||||||
It's possible that developers made some new function blocks (FIUs) under this
|
It's possible that developers made some new function blocks (FIUs) under this
|
||||||
|
@ -16,15 +16,6 @@
|
|||||||
|
|
||||||
#include "dfl-afu.h"
|
#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)
|
void afu_dma_region_init(struct dfl_feature_platform_data *pdata)
|
||||||
{
|
{
|
||||||
struct dfl_afu *afu = dfl_fpga_pdata_get_private(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;
|
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);
|
region->pages);
|
||||||
if (pinned < 0) {
|
if (pinned < 0) {
|
||||||
ret = pinned;
|
ret = pinned;
|
||||||
goto free_pages;
|
goto free_pages;
|
||||||
} else if (pinned != npages) {
|
} else if (pinned != npages) {
|
||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
goto put_pages;
|
goto unpin_pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_dbg(dev, "%d pages pinned\n", pinned);
|
dev_dbg(dev, "%d pages pinned\n", pinned);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
put_pages:
|
unpin_pages:
|
||||||
put_all_pages(region->pages, pinned);
|
unpin_user_pages(region->pages, pinned);
|
||||||
free_pages:
|
free_pages:
|
||||||
kfree(region->pages);
|
kfree(region->pages);
|
||||||
unlock_vm:
|
unlock_vm:
|
||||||
@ -94,7 +85,7 @@ static void afu_dma_unpin_pages(struct dfl_feature_platform_data *pdata,
|
|||||||
long npages = region->length >> PAGE_SHIFT;
|
long npages = region->length >> PAGE_SHIFT;
|
||||||
struct device *dev = &pdata->dev->dev;
|
struct device *dev = &pdata->dev->dev;
|
||||||
|
|
||||||
put_all_pages(region->pages, npages);
|
unpin_user_pages(region->pages, npages);
|
||||||
kfree(region->pages);
|
kfree(region->pages);
|
||||||
account_locked_vm(current->mm, npages, false);
|
account_locked_vm(current->mm, npages, false);
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* Mitchel Henry <henry.mitchel@intel.com>
|
* Mitchel Henry <henry.mitchel@intel.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/fpga-dfl.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
|
||||||
#include "dfl-afu.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);
|
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[] = {
|
const struct dfl_feature_id port_err_id_table[] = {
|
||||||
{.id = PORT_FEATURE_ID_ERROR,},
|
{.id = PORT_FEATURE_ID_ERROR,},
|
||||||
{0,}
|
{0,}
|
||||||
@ -227,4 +243,5 @@ const struct dfl_feature_id port_err_id_table[] = {
|
|||||||
const struct dfl_feature_ops port_err_ops = {
|
const struct dfl_feature_ops port_err_ops = {
|
||||||
.init = port_err_init,
|
.init = port_err_init,
|
||||||
.uinit = port_err_uinit,
|
.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,
|
.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[] = {
|
static struct dfl_feature_driver port_feature_drvs[] = {
|
||||||
{
|
{
|
||||||
.id_table = port_hdr_id_table,
|
.id_table = port_hdr_id_table,
|
||||||
@ -546,6 +570,10 @@ static struct dfl_feature_driver port_feature_drvs[] = {
|
|||||||
.id_table = port_stp_id_table,
|
.id_table = port_stp_id_table,
|
||||||
.ops = &port_stp_ops,
|
.ops = &port_stp_ops,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.id_table = port_uint_id_table,
|
||||||
|
.ops = &port_uint_ops,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.ops = NULL,
|
.ops = NULL,
|
||||||
}
|
}
|
||||||
@ -577,6 +605,7 @@ static int afu_release(struct inode *inode, struct file *filp)
|
|||||||
{
|
{
|
||||||
struct platform_device *pdev = filp->private_data;
|
struct platform_device *pdev = filp->private_data;
|
||||||
struct dfl_feature_platform_data *pdata;
|
struct dfl_feature_platform_data *pdata;
|
||||||
|
struct dfl_feature *feature;
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "Device File Release\n");
|
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);
|
dfl_feature_dev_use_end(pdata);
|
||||||
|
|
||||||
if (!dfl_feature_dev_use_count(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);
|
__port_reset(pdev);
|
||||||
afu_dma_region_destroy(pdata);
|
afu_dma_region_destroy(pdata);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
* Mitchel, Henry <henry.mitchel@intel.com>
|
* Mitchel, Henry <henry.mitchel@intel.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/fpga-dfl.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
|
||||||
#include "dfl.h"
|
#include "dfl.h"
|
||||||
@ -348,6 +349,22 @@ static void fme_global_err_uinit(struct platform_device *pdev,
|
|||||||
fme_err_mask(&pdev->dev, true);
|
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[] = {
|
const struct dfl_feature_id fme_global_err_id_table[] = {
|
||||||
{.id = FME_FEATURE_ID_GLOBAL_ERR,},
|
{.id = FME_FEATURE_ID_GLOBAL_ERR,},
|
||||||
{0,}
|
{0,}
|
||||||
@ -356,4 +373,5 @@ const struct dfl_feature_id fme_global_err_id_table[] = {
|
|||||||
const struct dfl_feature_ops fme_global_err_ops = {
|
const struct dfl_feature_ops fme_global_err_ops = {
|
||||||
.init = fme_global_err_init,
|
.init = fme_global_err_init,
|
||||||
.uinit = fme_global_err_uinit,
|
.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 dfl_feature_platform_data *pdata = filp->private_data;
|
||||||
struct platform_device *pdev = pdata->dev;
|
struct platform_device *pdev = pdata->dev;
|
||||||
|
struct dfl_feature *feature;
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "Device File Release\n");
|
dev_dbg(&pdev->dev, "Device File Release\n");
|
||||||
|
|
||||||
mutex_lock(&pdata->lock);
|
mutex_lock(&pdata->lock);
|
||||||
dfl_feature_dev_use_end(pdata);
|
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);
|
mutex_unlock(&pdata->lock);
|
||||||
|
|
||||||
return 0;
|
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];
|
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 */
|
/* PCI Device ID */
|
||||||
#define PCIE_DEVICE_ID_PF_INT_5_X 0xBCBD
|
#define PCIE_DEVICE_ID_PF_INT_5_X 0xBCBD
|
||||||
#define PCIE_DEVICE_ID_PF_INT_6_X 0xBCC0
|
#define PCIE_DEVICE_ID_PF_INT_6_X 0xBCC0
|
||||||
#define PCIE_DEVICE_ID_PF_DSC_1_X 0x09C4
|
#define PCIE_DEVICE_ID_PF_DSC_1_X 0x09C4
|
||||||
|
#define PCIE_DEVICE_ID_INTEL_PAC_N3000 0x0B30
|
||||||
/* VF Device */
|
/* VF Device */
|
||||||
#define PCIE_DEVICE_ID_VF_INT_5_X 0xBCBF
|
#define PCIE_DEVICE_ID_VF_INT_5_X 0xBCBF
|
||||||
#define PCIE_DEVICE_ID_VF_INT_6_X 0xBCC1
|
#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_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_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_VF_DSC_1_X),},
|
||||||
|
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_INTEL_PAC_N3000),},
|
||||||
{0,}
|
{0,}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(pci, cci_pcie_id_tbl);
|
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 */
|
/* remove all children feature devices */
|
||||||
dfl_fpga_feature_devs_remove(drvdata->cdev);
|
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 */
|
/* enumerate feature devices under pci device */
|
||||||
static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
|
static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
|
||||||
{
|
{
|
||||||
struct cci_drvdata *drvdata = pci_get_drvdata(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_enum_info *info;
|
||||||
struct dfl_fpga_cdev *cdev;
|
struct dfl_fpga_cdev *cdev;
|
||||||
resource_size_t start, len;
|
resource_size_t start, len;
|
||||||
int port_num, bar, i, ret = 0;
|
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
int *irq_table;
|
||||||
u32 offset;
|
u32 offset;
|
||||||
u64 v;
|
u64 v;
|
||||||
|
|
||||||
@ -97,11 +137,30 @@ static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
|
|||||||
if (!info)
|
if (!info)
|
||||||
return -ENOMEM;
|
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 */
|
/* start to find Device Feature List from Bar 0 */
|
||||||
base = cci_pci_ioremap_bar(pcidev, 0);
|
base = cci_pci_ioremap_bar(pcidev, 0);
|
||||||
if (!base) {
|
if (!base) {
|
||||||
ret = -ENOMEM;
|
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);
|
dfl_fpga_enum_info_add_dfl(info, start, len, base);
|
||||||
} else {
|
} else {
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto enum_info_free_exit;
|
goto irq_free_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* start enumeration with prepared enumeration information */
|
/* start enumeration with prepared enumeration information */
|
||||||
@ -162,11 +221,14 @@ static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
|
|||||||
if (IS_ERR(cdev)) {
|
if (IS_ERR(cdev)) {
|
||||||
dev_err(&pcidev->dev, "Enumeration failure\n");
|
dev_err(&pcidev->dev, "Enumeration failure\n");
|
||||||
ret = PTR_ERR(cdev);
|
ret = PTR_ERR(cdev);
|
||||||
goto enum_info_free_exit;
|
goto irq_free_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
drvdata->cdev = cdev;
|
drvdata->cdev = cdev;
|
||||||
|
|
||||||
|
irq_free_exit:
|
||||||
|
if (ret)
|
||||||
|
cci_pci_free_irq(pcidev);
|
||||||
enum_info_free_exit:
|
enum_info_free_exit:
|
||||||
dfl_fpga_enum_info_free(info);
|
dfl_fpga_enum_info_free(info);
|
||||||
|
|
||||||
@ -211,13 +273,11 @@ int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = cci_enumerate_feature_devs(pcidev);
|
ret = cci_enumerate_feature_devs(pcidev);
|
||||||
if (ret) {
|
if (!ret)
|
||||||
dev_err(&pcidev->dev, "enumeration failure %d.\n", ret);
|
|
||||||
goto disable_error_report_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
dev_err(&pcidev->dev, "enumeration failure %d.\n", ret);
|
||||||
|
|
||||||
disable_error_report_exit:
|
disable_error_report_exit:
|
||||||
pci_disable_pcie_error_reporting(pcidev);
|
pci_disable_pcie_error_reporting(pcidev);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -10,7 +10,9 @@
|
|||||||
* Wu Hao <hao.wu@intel.com>
|
* Wu Hao <hao.wu@intel.com>
|
||||||
* Xiao Guangrong <guangrong.xiao@linux.intel.com>
|
* Xiao Guangrong <guangrong.xiao@linux.intel.com>
|
||||||
*/
|
*/
|
||||||
|
#include <linux/fpga-dfl.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
|
||||||
#include "dfl.h"
|
#include "dfl.h"
|
||||||
|
|
||||||
@ -421,6 +423,9 @@ EXPORT_SYMBOL_GPL(dfl_fpga_dev_ops_unregister);
|
|||||||
*
|
*
|
||||||
* @dev: device to enumerate.
|
* @dev: device to enumerate.
|
||||||
* @cdev: the container device for all feature devices.
|
* @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.
|
* @feature_dev: current feature device.
|
||||||
* @ioaddr: header register region address of feature device in enumeration.
|
* @ioaddr: header register region address of feature device in enumeration.
|
||||||
* @sub_features: a sub features linked list for 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 build_feature_devs_info {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct dfl_fpga_cdev *cdev;
|
struct dfl_fpga_cdev *cdev;
|
||||||
|
unsigned int nr_irqs;
|
||||||
|
int *irq_table;
|
||||||
|
|
||||||
struct platform_device *feature_dev;
|
struct platform_device *feature_dev;
|
||||||
void __iomem *ioaddr;
|
void __iomem *ioaddr;
|
||||||
struct list_head sub_features;
|
struct list_head sub_features;
|
||||||
@ -442,12 +450,16 @@ struct build_feature_devs_info {
|
|||||||
* @mmio_res: mmio resource of this sub feature.
|
* @mmio_res: mmio resource of this sub feature.
|
||||||
* @ioaddr: mapped base address of mmio resource.
|
* @ioaddr: mapped base address of mmio resource.
|
||||||
* @node: node in sub_features linked list.
|
* @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 {
|
struct dfl_feature_info {
|
||||||
u64 fid;
|
u64 fid;
|
||||||
struct resource mmio_res;
|
struct resource mmio_res;
|
||||||
void __iomem *ioaddr;
|
void __iomem *ioaddr;
|
||||||
struct list_head node;
|
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,
|
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,
|
* it will be automatically freed by device's release() callback,
|
||||||
* platform_device_release().
|
* platform_device_release().
|
||||||
*/
|
*/
|
||||||
pdata = kzalloc(dfl_feature_platform_data_size(binfo->feature_num),
|
pdata = kzalloc(struct_size(pdata, features, binfo->feature_num), GFP_KERNEL);
|
||||||
GFP_KERNEL);
|
|
||||||
if (!pdata)
|
if (!pdata)
|
||||||
return -ENOMEM;
|
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 */
|
/* fill features and resource information for feature dev */
|
||||||
list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) {
|
list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) {
|
||||||
struct dfl_feature *feature = &pdata->features[index];
|
struct dfl_feature *feature = &pdata->features[index];
|
||||||
|
struct dfl_feature_irq_ctx *ctx;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
/* save resource information for each feature */
|
/* save resource information for each feature */
|
||||||
|
feature->dev = fdev;
|
||||||
feature->id = finfo->fid;
|
feature->id = finfo->fid;
|
||||||
feature->resource_index = index;
|
feature->resource_index = index;
|
||||||
feature->ioaddr = finfo->ioaddr;
|
feature->ioaddr = finfo->ioaddr;
|
||||||
fdev->resource[index++] = finfo->mmio_res;
|
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);
|
list_del(&finfo->node);
|
||||||
kfree(finfo);
|
kfree(finfo);
|
||||||
}
|
}
|
||||||
@ -638,6 +666,78 @@ static u64 feature_id(void __iomem *start)
|
|||||||
return 0;
|
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
|
* 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
|
* 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,
|
struct dfl_fpga_enum_dfl *dfl, resource_size_t ofst,
|
||||||
resource_size_t size, u64 fid)
|
resource_size_t size, u64 fid)
|
||||||
{
|
{
|
||||||
|
unsigned int irq_base, nr_irqs;
|
||||||
struct dfl_feature_info *finfo;
|
struct dfl_feature_info *finfo;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* read feature size and id if inputs are invalid */
|
/* read feature size and id if inputs are invalid */
|
||||||
size = size ? size : feature_size(dfl->ioaddr + ofst);
|
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)
|
if (dfl->len - ofst < size)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = parse_feature_irqs(binfo, ofst, fid, &irq_base, &nr_irqs);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
finfo = kzalloc(sizeof(*finfo), GFP_KERNEL);
|
finfo = kzalloc(sizeof(*finfo), GFP_KERNEL);
|
||||||
if (!finfo)
|
if (!finfo)
|
||||||
return -ENOMEM;
|
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.start = dfl->start + ofst;
|
||||||
finfo->mmio_res.end = finfo->mmio_res.start + size - 1;
|
finfo->mmio_res.end = finfo->mmio_res.start + size - 1;
|
||||||
finfo->mmio_res.flags = IORESOURCE_MEM;
|
finfo->mmio_res.flags = IORESOURCE_MEM;
|
||||||
|
finfo->irq_base = irq_base;
|
||||||
|
finfo->nr_irqs = nr_irqs;
|
||||||
finfo->ioaddr = dfl->ioaddr + ofst;
|
finfo->ioaddr = dfl->ioaddr + ofst;
|
||||||
|
|
||||||
list_add_tail(&finfo->node, &binfo->sub_features);
|
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);
|
devm_kfree(dev, dfl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* remove irq table */
|
||||||
|
if (info->irq_table)
|
||||||
|
devm_kfree(dev, info->irq_table);
|
||||||
|
|
||||||
devm_kfree(dev, info);
|
devm_kfree(dev, info);
|
||||||
put_device(dev);
|
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);
|
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)
|
static int remove_feature_dev(struct device *dev, void *data)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
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->dev = info->dev;
|
||||||
binfo->cdev = cdev;
|
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
|
* start enumeration for all feature devices based on Device Feature
|
||||||
* Lists.
|
* Lists.
|
||||||
@ -1241,6 +1396,160 @@ done:
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(dfl_fpga_cdev_config_ports_vf);
|
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)
|
static void __exit dfl_fpga_exit(void)
|
||||||
{
|
{
|
||||||
dfl_chardev_uinit();
|
dfl_chardev_uinit();
|
||||||
|
@ -17,7 +17,9 @@
|
|||||||
#include <linux/bitfield.h>
|
#include <linux/bitfield.h>
|
||||||
#include <linux/cdev.h>
|
#include <linux/cdev.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/eventfd.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
#include <linux/iopoll.h>
|
#include <linux/iopoll.h>
|
||||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
@ -112,6 +114,13 @@
|
|||||||
#define FME_PORT_OFST_ACC_VF 1
|
#define FME_PORT_OFST_ACC_VF 1
|
||||||
#define FME_PORT_OFST_IMP BIT_ULL(60)
|
#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 */
|
/* PORT Header Register Set */
|
||||||
#define PORT_HDR_DFH DFH
|
#define PORT_HDR_DFH DFH
|
||||||
#define PORT_HDR_GUID_L GUID_L
|
#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_AP2 2 /* 90% throttling */
|
||||||
#define PORT_STS_PWR_STATE_AP6 6 /* 100% 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
|
* struct dfl_fpga_port_ops - port ops
|
||||||
*
|
*
|
||||||
@ -188,21 +211,40 @@ struct dfl_feature_driver {
|
|||||||
const struct dfl_feature_ops *ops;
|
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
|
* 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.
|
* @id: sub feature id.
|
||||||
* @resource_index: each sub feature has one mmio resource for its registers.
|
* @resource_index: each sub feature has one mmio resource for its registers.
|
||||||
* this index is used to find its mmio resource from the
|
* this index is used to find its mmio resource from the
|
||||||
* feature dev (platform device)'s reources.
|
* feature dev (platform device)'s reources.
|
||||||
* @ioaddr: mapped mmio resource address.
|
* @ioaddr: mapped mmio resource address.
|
||||||
|
* @irq_ctx: interrupt context list.
|
||||||
|
* @nr_irqs: number of interrupt contexts.
|
||||||
* @ops: ops of this sub feature.
|
* @ops: ops of this sub feature.
|
||||||
* @priv: priv data of this feature.
|
* @priv: priv data of this feature.
|
||||||
*/
|
*/
|
||||||
struct dfl_feature {
|
struct dfl_feature {
|
||||||
|
struct platform_device *dev;
|
||||||
u64 id;
|
u64 id;
|
||||||
int resource_index;
|
int resource_index;
|
||||||
void __iomem *ioaddr;
|
void __iomem *ioaddr;
|
||||||
|
struct dfl_feature_irq_ctx *irq_ctx;
|
||||||
|
unsigned int nr_irqs;
|
||||||
const struct dfl_feature_ops *ops;
|
const struct dfl_feature_ops *ops;
|
||||||
void *priv;
|
void *priv;
|
||||||
};
|
};
|
||||||
@ -299,12 +341,6 @@ struct dfl_feature_ops {
|
|||||||
#define DFL_FPGA_FEATURE_DEV_FME "dfl-fme"
|
#define DFL_FPGA_FEATURE_DEV_FME "dfl-fme"
|
||||||
#define DFL_FPGA_FEATURE_DEV_PORT "dfl-port"
|
#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);
|
void dfl_fpga_dev_feature_uinit(struct platform_device *pdev);
|
||||||
int dfl_fpga_dev_feature_init(struct platform_device *pdev,
|
int dfl_fpga_dev_feature_init(struct platform_device *pdev,
|
||||||
struct dfl_feature_driver *feature_drvs);
|
struct dfl_feature_driver *feature_drvs);
|
||||||
@ -390,10 +426,14 @@ static inline u8 dfl_feature_revision(void __iomem *base)
|
|||||||
*
|
*
|
||||||
* @dev: parent device.
|
* @dev: parent device.
|
||||||
* @dfls: list of device feature lists.
|
* @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 dfl_fpga_enum_info {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct list_head dfls;
|
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,
|
int dfl_fpga_enum_info_add_dfl(struct dfl_fpga_enum_info *info,
|
||||||
resource_size_t start, resource_size_t len,
|
resource_size_t start, resource_size_t len,
|
||||||
void __iomem *ioaddr);
|
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);
|
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);
|
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);
|
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_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 */
|
#endif /* __FPGA_DFL_H */
|
||||||
|
@ -328,7 +328,7 @@ struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name,
|
|||||||
void *priv)
|
void *priv)
|
||||||
{
|
{
|
||||||
struct fpga_bridge *bridge;
|
struct fpga_bridge *bridge;
|
||||||
int id, ret = 0;
|
int id, ret;
|
||||||
|
|
||||||
if (!name || !strlen(name)) {
|
if (!name || !strlen(name)) {
|
||||||
dev_err(dev, "Attempt to register with no name!\n");
|
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;
|
return NULL;
|
||||||
|
|
||||||
id = ida_simple_get(&fpga_bridge_ida, 0, 0, GFP_KERNEL);
|
id = ida_simple_get(&fpga_bridge_ida, 0, 0, GFP_KERNEL);
|
||||||
if (id < 0) {
|
if (id < 0)
|
||||||
ret = id;
|
|
||||||
goto error_kfree;
|
goto error_kfree;
|
||||||
}
|
|
||||||
|
|
||||||
mutex_init(&bridge->mutex);
|
mutex_init(&bridge->mutex);
|
||||||
INIT_LIST_HEAD(&bridge->node);
|
INIT_LIST_HEAD(&bridge->node);
|
||||||
|
@ -581,10 +581,8 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
|
id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
|
||||||
if (id < 0) {
|
if (id < 0)
|
||||||
ret = id;
|
|
||||||
goto error_kfree;
|
goto error_kfree;
|
||||||
}
|
|
||||||
|
|
||||||
mutex_init(&mgr->ref_mutex);
|
mutex_init(&mgr->ref_mutex);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// 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
|
* Copyright (C) 2017 DENX Software Engineering
|
||||||
*
|
*
|
||||||
@ -23,6 +23,7 @@
|
|||||||
struct xilinx_spi_conf {
|
struct xilinx_spi_conf {
|
||||||
struct spi_device *spi;
|
struct spi_device *spi;
|
||||||
struct gpio_desc *prog_b;
|
struct gpio_desc *prog_b;
|
||||||
|
struct gpio_desc *init_b;
|
||||||
struct gpio_desc *done;
|
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;
|
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,
|
static int xilinx_spi_write_init(struct fpga_manager *mgr,
|
||||||
struct fpga_image_info *info,
|
struct fpga_image_info *info,
|
||||||
const char *buf, size_t count)
|
const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
struct xilinx_spi_conf *conf = mgr->priv;
|
struct xilinx_spi_conf *conf = mgr->priv;
|
||||||
const size_t prog_latency_7500us = 7500;
|
int err;
|
||||||
const size_t prog_pulse_1us = 1;
|
|
||||||
|
|
||||||
if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
|
if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
|
||||||
dev_err(&mgr->dev, "Partial reconfiguration not supported.\n");
|
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);
|
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);
|
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)) {
|
if (gpiod_get_value(conf->done)) {
|
||||||
dev_err(&mgr->dev, "Unexpected DONE pin state...\n");
|
dev_err(&mgr->dev, "Unexpected DONE pin state...\n");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* program latency */
|
/* program latency */
|
||||||
usleep_range(prog_latency_7500us, prog_latency_7500us + 100);
|
usleep_range(7500, 7600);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,6 +200,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
|
|||||||
return PTR_ERR(conf->prog_b);
|
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);
|
conf->done = devm_gpiod_get(&spi->dev, "done", GPIOD_IN);
|
||||||
if (IS_ERR(conf->done)) {
|
if (IS_ERR(conf->done)) {
|
||||||
dev_err(&spi->dev, "Failed to get DONE gpio: %ld\n",
|
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)
|
#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 */
|
/* 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)
|
#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 */
|
#endif /* _UAPI_LINUX_FPGA_DFL_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user