1 // SPDX-License-Identifier: GPL-2.0
3 * Driver for FPGA Device Feature List (DFL) PCIe device
5 * Copyright (C) 2017-2018 Intel Corporation, Inc.
8 * Zhang Yi <Yi.Z.Zhang@intel.com>
9 * Xiao Guangrong <guangrong.xiao@linux.intel.com>
10 * Joseph Grecco <joe.grecco@intel.com>
11 * Enno Luebbers <enno.luebbers@intel.com>
12 * Tim Whisonant <tim.whisonant@intel.com>
13 * Ananda Ravuri <ananda.ravuri@intel.com>
14 * Henry Mitchel <henry.mitchel@intel.com>
17 #include <linux/pci.h>
18 #include <linux/types.h>
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/stddef.h>
22 #include <linux/errno.h>
23 #include <linux/aer.h>
27 #define DRV_VERSION "0.8"
28 #define DRV_NAME "dfl-pci"
30 #define PCI_VSEC_ID_INTEL_DFLS 0x43
32 #define PCI_VNDR_DFLS_CNT 0x8
33 #define PCI_VNDR_DFLS_RES 0xc
35 #define PCI_VNDR_DFLS_RES_BAR_MASK GENMASK(2, 0)
36 #define PCI_VNDR_DFLS_RES_OFF_MASK GENMASK(31, 3)
39 struct dfl_fpga_cdev
*cdev
; /* container device */
42 static void __iomem
*cci_pci_ioremap_bar0(struct pci_dev
*pcidev
)
44 if (pcim_iomap_regions(pcidev
, BIT(0), DRV_NAME
))
47 return pcim_iomap_table(pcidev
)[0];
50 static int cci_pci_alloc_irq(struct pci_dev
*pcidev
)
52 int ret
, nvec
= pci_msix_vec_count(pcidev
);
55 dev_dbg(&pcidev
->dev
, "fpga interrupt not supported\n");
59 ret
= pci_alloc_irq_vectors(pcidev
, nvec
, nvec
, PCI_IRQ_MSIX
);
66 static void cci_pci_free_irq(struct pci_dev
*pcidev
)
68 pci_free_irq_vectors(pcidev
);
72 #define PCIE_DEVICE_ID_PF_INT_5_X 0xBCBD
73 #define PCIE_DEVICE_ID_PF_INT_6_X 0xBCC0
74 #define PCIE_DEVICE_ID_PF_DSC_1_X 0x09C4
75 #define PCIE_DEVICE_ID_INTEL_PAC_N3000 0x0B30
76 #define PCIE_DEVICE_ID_INTEL_PAC_D5005 0x0B2B
77 #define PCIE_DEVICE_ID_SILICOM_PAC_N5010 0x1000
78 #define PCIE_DEVICE_ID_SILICOM_PAC_N5011 0x1001
81 #define PCIE_DEVICE_ID_VF_INT_5_X 0xBCBF
82 #define PCIE_DEVICE_ID_VF_INT_6_X 0xBCC1
83 #define PCIE_DEVICE_ID_VF_DSC_1_X 0x09C5
84 #define PCIE_DEVICE_ID_INTEL_PAC_D5005_VF 0x0B2C
86 static struct pci_device_id cci_pcie_id_tbl
[] = {
87 {PCI_DEVICE(PCI_VENDOR_ID_INTEL
, PCIE_DEVICE_ID_PF_INT_5_X
),},
88 {PCI_DEVICE(PCI_VENDOR_ID_INTEL
, PCIE_DEVICE_ID_VF_INT_5_X
),},
89 {PCI_DEVICE(PCI_VENDOR_ID_INTEL
, PCIE_DEVICE_ID_PF_INT_6_X
),},
90 {PCI_DEVICE(PCI_VENDOR_ID_INTEL
, PCIE_DEVICE_ID_VF_INT_6_X
),},
91 {PCI_DEVICE(PCI_VENDOR_ID_INTEL
, PCIE_DEVICE_ID_PF_DSC_1_X
),},
92 {PCI_DEVICE(PCI_VENDOR_ID_INTEL
, PCIE_DEVICE_ID_VF_DSC_1_X
),},
93 {PCI_DEVICE(PCI_VENDOR_ID_INTEL
, PCIE_DEVICE_ID_INTEL_PAC_N3000
),},
94 {PCI_DEVICE(PCI_VENDOR_ID_INTEL
, PCIE_DEVICE_ID_INTEL_PAC_D5005
),},
95 {PCI_DEVICE(PCI_VENDOR_ID_INTEL
, PCIE_DEVICE_ID_INTEL_PAC_D5005_VF
),},
96 {PCI_DEVICE(PCI_VENDOR_ID_SILICOM_DENMARK
, PCIE_DEVICE_ID_SILICOM_PAC_N5010
),},
97 {PCI_DEVICE(PCI_VENDOR_ID_SILICOM_DENMARK
, PCIE_DEVICE_ID_SILICOM_PAC_N5011
),},
100 MODULE_DEVICE_TABLE(pci
, cci_pcie_id_tbl
);
102 static int cci_init_drvdata(struct pci_dev
*pcidev
)
104 struct cci_drvdata
*drvdata
;
106 drvdata
= devm_kzalloc(&pcidev
->dev
, sizeof(*drvdata
), GFP_KERNEL
);
110 pci_set_drvdata(pcidev
, drvdata
);
115 static void cci_remove_feature_devs(struct pci_dev
*pcidev
)
117 struct cci_drvdata
*drvdata
= pci_get_drvdata(pcidev
);
119 /* remove all children feature devices */
120 dfl_fpga_feature_devs_remove(drvdata
->cdev
);
121 cci_pci_free_irq(pcidev
);
124 static int *cci_pci_create_irq_table(struct pci_dev
*pcidev
, unsigned int nvec
)
129 table
= kcalloc(nvec
, sizeof(int), GFP_KERNEL
);
133 for (i
= 0; i
< nvec
; i
++)
134 table
[i
] = pci_irq_vector(pcidev
, i
);
139 static int find_dfls_by_vsec(struct pci_dev
*pcidev
, struct dfl_fpga_enum_info
*info
)
141 u32 bir
, offset
, vndr_hdr
, dfl_cnt
, dfl_res
;
142 int dfl_res_off
, i
, bars
, voff
= 0;
143 resource_size_t start
, len
;
145 while ((voff
= pci_find_next_ext_capability(pcidev
, voff
, PCI_EXT_CAP_ID_VNDR
))) {
147 pci_read_config_dword(pcidev
, voff
+ PCI_VNDR_HEADER
, &vndr_hdr
);
149 if (PCI_VNDR_HEADER_ID(vndr_hdr
) == PCI_VSEC_ID_INTEL_DFLS
&&
150 pcidev
->vendor
== PCI_VENDOR_ID_INTEL
)
155 dev_dbg(&pcidev
->dev
, "%s no DFL VSEC found\n", __func__
);
160 pci_read_config_dword(pcidev
, voff
+ PCI_VNDR_DFLS_CNT
, &dfl_cnt
);
161 if (dfl_cnt
> PCI_STD_NUM_BARS
) {
162 dev_err(&pcidev
->dev
, "%s too many DFLs %d > %d\n",
163 __func__
, dfl_cnt
, PCI_STD_NUM_BARS
);
167 dfl_res_off
= voff
+ PCI_VNDR_DFLS_RES
;
168 if (dfl_res_off
+ (dfl_cnt
* sizeof(u32
)) > PCI_CFG_SPACE_EXP_SIZE
) {
169 dev_err(&pcidev
->dev
, "%s DFL VSEC too big for PCIe config space\n",
174 for (i
= 0, bars
= 0; i
< dfl_cnt
; i
++, dfl_res_off
+= sizeof(u32
)) {
175 dfl_res
= GENMASK(31, 0);
176 pci_read_config_dword(pcidev
, dfl_res_off
, &dfl_res
);
178 bir
= dfl_res
& PCI_VNDR_DFLS_RES_BAR_MASK
;
179 if (bir
>= PCI_STD_NUM_BARS
) {
180 dev_err(&pcidev
->dev
, "%s bad bir number %d\n",
185 if (bars
& BIT(bir
)) {
186 dev_err(&pcidev
->dev
, "%s DFL for BAR %d already specified\n",
193 len
= pci_resource_len(pcidev
, bir
);
194 offset
= dfl_res
& PCI_VNDR_DFLS_RES_OFF_MASK
;
196 dev_err(&pcidev
->dev
, "%s bad offset %u >= %pa\n",
197 __func__
, offset
, &len
);
201 dev_dbg(&pcidev
->dev
, "%s BAR %d offset 0x%x\n", __func__
, bir
, offset
);
205 start
= pci_resource_start(pcidev
, bir
) + offset
;
207 dfl_fpga_enum_info_add_dfl(info
, start
, len
);
213 /* default method of finding dfls starting at offset 0 of bar 0 */
214 static int find_dfls_by_default(struct pci_dev
*pcidev
,
215 struct dfl_fpga_enum_info
*info
)
217 int port_num
, bar
, i
, ret
= 0;
218 resource_size_t start
, len
;
223 /* start to find Device Feature List from Bar 0 */
224 base
= cci_pci_ioremap_bar0(pcidev
);
229 * PF device has FME and Ports/AFUs, and VF device only has one
230 * Port/AFU. Check them and add related "Device Feature List" info
231 * for the next step enumeration.
233 if (dfl_feature_is_fme(base
)) {
234 start
= pci_resource_start(pcidev
, 0);
235 len
= pci_resource_len(pcidev
, 0);
237 dfl_fpga_enum_info_add_dfl(info
, start
, len
);
240 * find more Device Feature Lists (e.g. Ports) per information
241 * indicated by FME module.
243 v
= readq(base
+ FME_HDR_CAP
);
244 port_num
= FIELD_GET(FME_CAP_NUM_PORTS
, v
);
246 WARN_ON(port_num
> MAX_DFL_FPGA_PORT_NUM
);
248 for (i
= 0; i
< port_num
; i
++) {
249 v
= readq(base
+ FME_HDR_PORT_OFST(i
));
251 /* skip ports which are not implemented. */
252 if (!(v
& FME_PORT_OFST_IMP
))
256 * add Port's Device Feature List information for next
259 bar
= FIELD_GET(FME_PORT_OFST_BAR_ID
, v
);
260 offset
= FIELD_GET(FME_PORT_OFST_DFH_OFST
, v
);
261 start
= pci_resource_start(pcidev
, bar
) + offset
;
262 len
= pci_resource_len(pcidev
, bar
) - offset
;
264 dfl_fpga_enum_info_add_dfl(info
, start
, len
);
266 } else if (dfl_feature_is_port(base
)) {
267 start
= pci_resource_start(pcidev
, 0);
268 len
= pci_resource_len(pcidev
, 0);
270 dfl_fpga_enum_info_add_dfl(info
, start
, len
);
275 /* release I/O mappings for next step enumeration */
276 pcim_iounmap_regions(pcidev
, BIT(0));
281 /* enumerate feature devices under pci device */
282 static int cci_enumerate_feature_devs(struct pci_dev
*pcidev
)
284 struct cci_drvdata
*drvdata
= pci_get_drvdata(pcidev
);
285 struct dfl_fpga_enum_info
*info
;
286 struct dfl_fpga_cdev
*cdev
;
290 /* allocate enumeration info via pci_dev */
291 info
= dfl_fpga_enum_info_alloc(&pcidev
->dev
);
295 /* add irq info for enumeration if the device support irq */
296 nvec
= cci_pci_alloc_irq(pcidev
);
298 dev_err(&pcidev
->dev
, "Fail to alloc irq %d.\n", nvec
);
300 goto enum_info_free_exit
;
302 irq_table
= cci_pci_create_irq_table(pcidev
, nvec
);
308 ret
= dfl_fpga_enum_info_add_irq(info
, nvec
, irq_table
);
314 ret
= find_dfls_by_vsec(pcidev
, info
);
316 ret
= find_dfls_by_default(pcidev
, info
);
321 /* start enumeration with prepared enumeration information */
322 cdev
= dfl_fpga_feature_devs_enumerate(info
);
324 dev_err(&pcidev
->dev
, "Enumeration failure\n");
329 drvdata
->cdev
= cdev
;
333 cci_pci_free_irq(pcidev
);
335 dfl_fpga_enum_info_free(info
);
341 int cci_pci_probe(struct pci_dev
*pcidev
, const struct pci_device_id
*pcidevid
)
345 ret
= pcim_enable_device(pcidev
);
347 dev_err(&pcidev
->dev
, "Failed to enable device %d.\n", ret
);
351 ret
= pci_enable_pcie_error_reporting(pcidev
);
352 if (ret
&& ret
!= -EINVAL
)
353 dev_info(&pcidev
->dev
, "PCIE AER unavailable %d.\n", ret
);
355 pci_set_master(pcidev
);
357 if (!pci_set_dma_mask(pcidev
, DMA_BIT_MASK(64))) {
358 ret
= pci_set_consistent_dma_mask(pcidev
, DMA_BIT_MASK(64));
360 goto disable_error_report_exit
;
361 } else if (!pci_set_dma_mask(pcidev
, DMA_BIT_MASK(32))) {
362 ret
= pci_set_consistent_dma_mask(pcidev
, DMA_BIT_MASK(32));
364 goto disable_error_report_exit
;
367 dev_err(&pcidev
->dev
, "No suitable DMA support available.\n");
368 goto disable_error_report_exit
;
371 ret
= cci_init_drvdata(pcidev
);
373 dev_err(&pcidev
->dev
, "Fail to init drvdata %d.\n", ret
);
374 goto disable_error_report_exit
;
377 ret
= cci_enumerate_feature_devs(pcidev
);
381 dev_err(&pcidev
->dev
, "enumeration failure %d.\n", ret
);
383 disable_error_report_exit
:
384 pci_disable_pcie_error_reporting(pcidev
);
388 static int cci_pci_sriov_configure(struct pci_dev
*pcidev
, int num_vfs
)
390 struct cci_drvdata
*drvdata
= pci_get_drvdata(pcidev
);
391 struct dfl_fpga_cdev
*cdev
= drvdata
->cdev
;
395 * disable SRIOV and then put released ports back to default
398 pci_disable_sriov(pcidev
);
400 dfl_fpga_cdev_config_ports_pf(cdev
);
406 * before enable SRIOV, put released ports into VF access mode
409 ret
= dfl_fpga_cdev_config_ports_vf(cdev
, num_vfs
);
413 ret
= pci_enable_sriov(pcidev
, num_vfs
);
415 dfl_fpga_cdev_config_ports_pf(cdev
);
423 static void cci_pci_remove(struct pci_dev
*pcidev
)
425 if (dev_is_pf(&pcidev
->dev
))
426 cci_pci_sriov_configure(pcidev
, 0);
428 cci_remove_feature_devs(pcidev
);
429 pci_disable_pcie_error_reporting(pcidev
);
432 static struct pci_driver cci_pci_driver
= {
434 .id_table
= cci_pcie_id_tbl
,
435 .probe
= cci_pci_probe
,
436 .remove
= cci_pci_remove
,
437 .sriov_configure
= cci_pci_sriov_configure
,
440 module_pci_driver(cci_pci_driver
);
442 MODULE_DESCRIPTION("FPGA DFL PCIe Device Driver");
443 MODULE_AUTHOR("Intel Corporation");
444 MODULE_LICENSE("GPL v2");