summaryrefslogtreecommitdiff
path: root/drivers/fpga/dfl-pci.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2021-02-24 21:25:37 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2021-02-24 21:25:37 +0300
commite229b429bb4af24d9828758c0c851bb6a4169400 (patch)
tree95e49922f6c68b5f81cbf7a39349cfad42c5a0f1 /drivers/fpga/dfl-pci.c
parent7ac1161c2789be25d0d206e831b051f43028866e (diff)
parentd19db80a366576d3ffadf2508ed876b4c1faf959 (diff)
downloadlinux-e229b429bb4af24d9828758c0c851bb6a4169400.tar.xz
Merge tag 'char-misc-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc driver updates from Greg KH: "Here is the large set of char/misc/whatever driver subsystem updates for 5.12-rc1. Over time it seems like this tree is collecting more and more tiny driver subsystems in one place, making it easier for those maintainers, which is why this is getting larger. Included in here are: - coresight driver updates - habannalabs driver updates - virtual acrn driver addition (proper acks from the x86 maintainers) - broadcom misc driver addition - speakup driver updates - soundwire driver updates - fpga driver updates - amba driver updates - mei driver updates - vfio driver updates - greybus driver updates - nvmeem driver updates - phy driver updates - mhi driver updates - interconnect driver udpates - fsl-mc bus driver updates - random driver fix - some small misc driver updates (rtsx, pvpanic, etc.) All of these have been in linux-next for a while, with the only reported issue being a merge conflict due to the dfl_device_id addition from the fpga subsystem in here" * tag 'char-misc-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (311 commits) spmi: spmi-pmic-arb: Fix hw_irq overflow Documentation: coresight: Add PID tracing description coresight: etm-perf: Support PID tracing for kernel at EL2 coresight: etm-perf: Clarify comment on perf options ACRN: update MAINTAINERS: mailing list is subscribers-only regmap: sdw-mbq: use MODULE_LICENSE("GPL") regmap: sdw: use no_pm routines for SoundWire 1.2 MBQ regmap: sdw: use _no_pm functions in regmap_read/write soundwire: intel: fix possible crash when no device is detected MAINTAINERS: replace my with email with replacements mhi: Fix double dma free uapi: map_to_7segment: Update example in documentation uio: uio_pci_generic: don't fail probe if pdev->irq equals to IRQ_NOTCONNECTED drivers/misc/vmw_vmci: restrict too big queue size in qp_host_alloc_queue firewire: replace tricky statement by two simple ones vme: make remove callback return void firmware: google: make coreboot driver's remove callback return void firmware: xilinx: Use explicit values for all enum values sample/acrn: Introduce a sample of HSM ioctl interface usage virt: acrn: Introduce an interface for Service VM to control vCPU ...
Diffstat (limited to 'drivers/fpga/dfl-pci.c')
-rw-r--r--drivers/fpga/dfl-pci.c165
1 files changed, 130 insertions, 35 deletions
diff --git a/drivers/fpga/dfl-pci.c b/drivers/fpga/dfl-pci.c
index a2203d03c9e2..04e47e266f26 100644
--- a/drivers/fpga/dfl-pci.c
+++ b/drivers/fpga/dfl-pci.c
@@ -27,6 +27,14 @@
#define DRV_VERSION "0.8"
#define DRV_NAME "dfl-pci"
+#define PCI_VSEC_ID_INTEL_DFLS 0x43
+
+#define PCI_VNDR_DFLS_CNT 0x8
+#define PCI_VNDR_DFLS_RES 0xc
+
+#define PCI_VNDR_DFLS_RES_BAR_MASK GENMASK(2, 0)
+#define PCI_VNDR_DFLS_RES_OFF_MASK GENMASK(31, 3)
+
struct cci_drvdata {
struct dfl_fpga_cdev *cdev; /* container device */
};
@@ -119,49 +127,94 @@ static int *cci_pci_create_irq_table(struct pci_dev *pcidev, unsigned int nvec)
return table;
}
-/* enumerate feature devices under pci device */
-static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
+static int find_dfls_by_vsec(struct pci_dev *pcidev, struct dfl_fpga_enum_info *info)
{
- 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;
+ u32 bir, offset, vndr_hdr, dfl_cnt, dfl_res;
+ int dfl_res_off, i, bars, voff = 0;
resource_size_t start, len;
- void __iomem *base;
- int *irq_table;
- u32 offset;
- u64 v;
- /* allocate enumeration info via pci_dev */
- info = dfl_fpga_enum_info_alloc(&pcidev->dev);
- if (!info)
- return -ENOMEM;
+ while ((voff = pci_find_next_ext_capability(pcidev, voff, PCI_EXT_CAP_ID_VNDR))) {
+ vndr_hdr = 0;
+ pci_read_config_dword(pcidev, voff + PCI_VNDR_HEADER, &vndr_hdr);
- /* 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;
+ if (PCI_VNDR_HEADER_ID(vndr_hdr) == PCI_VSEC_ID_INTEL_DFLS &&
+ pcidev->vendor == PCI_VENDOR_ID_INTEL)
+ break;
+ }
+
+ if (!voff) {
+ dev_dbg(&pcidev->dev, "%s no DFL VSEC found\n", __func__);
+ return -ENODEV;
+ }
+
+ dfl_cnt = 0;
+ pci_read_config_dword(pcidev, voff + PCI_VNDR_DFLS_CNT, &dfl_cnt);
+ if (dfl_cnt > PCI_STD_NUM_BARS) {
+ dev_err(&pcidev->dev, "%s too many DFLs %d > %d\n",
+ __func__, dfl_cnt, PCI_STD_NUM_BARS);
+ return -EINVAL;
+ }
+
+ dfl_res_off = voff + PCI_VNDR_DFLS_RES;
+ if (dfl_res_off + (dfl_cnt * sizeof(u32)) > PCI_CFG_SPACE_EXP_SIZE) {
+ dev_err(&pcidev->dev, "%s DFL VSEC too big for PCIe config space\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0, bars = 0; i < dfl_cnt; i++, dfl_res_off += sizeof(u32)) {
+ dfl_res = GENMASK(31, 0);
+ pci_read_config_dword(pcidev, dfl_res_off, &dfl_res);
+
+ bir = dfl_res & PCI_VNDR_DFLS_RES_BAR_MASK;
+ if (bir >= PCI_STD_NUM_BARS) {
+ dev_err(&pcidev->dev, "%s bad bir number %d\n",
+ __func__, bir);
+ return -EINVAL;
}
- ret = dfl_fpga_enum_info_add_irq(info, nvec, irq_table);
- kfree(irq_table);
- if (ret)
- goto irq_free_exit;
+ if (bars & BIT(bir)) {
+ dev_err(&pcidev->dev, "%s DFL for BAR %d already specified\n",
+ __func__, bir);
+ return -EINVAL;
+ }
+
+ bars |= BIT(bir);
+
+ len = pci_resource_len(pcidev, bir);
+ offset = dfl_res & PCI_VNDR_DFLS_RES_OFF_MASK;
+ if (offset >= len) {
+ dev_err(&pcidev->dev, "%s bad offset %u >= %pa\n",
+ __func__, offset, &len);
+ return -EINVAL;
+ }
+
+ dev_dbg(&pcidev->dev, "%s BAR %d offset 0x%x\n", __func__, bir, offset);
+
+ len -= offset;
+
+ start = pci_resource_start(pcidev, bir) + offset;
+
+ dfl_fpga_enum_info_add_dfl(info, start, len);
}
- /* start to find Device Feature List in Bar 0 */
+ return 0;
+}
+
+/* default method of finding dfls starting at offset 0 of bar 0 */
+static int find_dfls_by_default(struct pci_dev *pcidev,
+ struct dfl_fpga_enum_info *info)
+{
+ int port_num, bar, i, ret = 0;
+ resource_size_t start, len;
+ void __iomem *base;
+ u32 offset;
+ u64 v;
+
+ /* start to find Device Feature List from Bar 0 */
base = cci_pci_ioremap_bar0(pcidev);
- if (!base) {
- ret = -ENOMEM;
- goto irq_free_exit;
- }
+ if (!base)
+ return -ENOMEM;
/*
* PF device has FME and Ports/AFUs, and VF device only has one
@@ -208,12 +261,54 @@ static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
dfl_fpga_enum_info_add_dfl(info, start, len);
} else {
ret = -ENODEV;
- goto irq_free_exit;
}
/* release I/O mappings for next step enumeration */
pcim_iounmap_regions(pcidev, BIT(0));
+ return ret;
+}
+
+/* enumerate feature devices under pci device */
+static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
+{
+ struct cci_drvdata *drvdata = pci_get_drvdata(pcidev);
+ struct dfl_fpga_enum_info *info;
+ struct dfl_fpga_cdev *cdev;
+ int nvec, ret = 0;
+ int *irq_table;
+
+ /* allocate enumeration info via pci_dev */
+ info = dfl_fpga_enum_info_alloc(&pcidev->dev);
+ 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;
+ }
+
+ ret = find_dfls_by_vsec(pcidev, info);
+ if (ret == -ENODEV)
+ ret = find_dfls_by_default(pcidev, info);
+
+ if (ret)
+ goto irq_free_exit;
+
/* start enumeration with prepared enumeration information */
cdev = dfl_fpga_feature_devs_enumerate(info);
if (IS_ERR(cdev)) {