xref: /kernel/linux/linux-5.10/drivers/fpga/dfl-pci.c (revision 8c2ecf20)
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Driver for FPGA Device Feature List (DFL) PCIe device
4 *
5 * Copyright (C) 2017-2018 Intel Corporation, Inc.
6 *
7 * Authors:
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>
15 */
16
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>
24
25#include "dfl.h"
26
27#define DRV_VERSION	"0.8"
28#define DRV_NAME	"dfl-pci"
29
30struct cci_drvdata {
31	struct dfl_fpga_cdev *cdev;	/* container device */
32};
33
34static void __iomem *cci_pci_ioremap_bar0(struct pci_dev *pcidev)
35{
36	if (pcim_iomap_regions(pcidev, BIT(0), DRV_NAME))
37		return NULL;
38
39	return pcim_iomap_table(pcidev)[0];
40}
41
42static int cci_pci_alloc_irq(struct pci_dev *pcidev)
43{
44	int ret, nvec = pci_msix_vec_count(pcidev);
45
46	if (nvec <= 0) {
47		dev_dbg(&pcidev->dev, "fpga interrupt not supported\n");
48		return 0;
49	}
50
51	ret = pci_alloc_irq_vectors(pcidev, nvec, nvec, PCI_IRQ_MSIX);
52	if (ret < 0)
53		return ret;
54
55	return nvec;
56}
57
58static void cci_pci_free_irq(struct pci_dev *pcidev)
59{
60	pci_free_irq_vectors(pcidev);
61}
62
63/* PCI Device ID */
64#define PCIE_DEVICE_ID_PF_INT_5_X		0xBCBD
65#define PCIE_DEVICE_ID_PF_INT_6_X		0xBCC0
66#define PCIE_DEVICE_ID_PF_DSC_1_X		0x09C4
67#define PCIE_DEVICE_ID_INTEL_PAC_N3000		0x0B30
68#define PCIE_DEVICE_ID_INTEL_PAC_D5005		0x0B2B
69/* VF Device */
70#define PCIE_DEVICE_ID_VF_INT_5_X		0xBCBF
71#define PCIE_DEVICE_ID_VF_INT_6_X		0xBCC1
72#define PCIE_DEVICE_ID_VF_DSC_1_X		0x09C5
73#define PCIE_DEVICE_ID_INTEL_PAC_D5005_VF	0x0B2C
74
75static struct pci_device_id cci_pcie_id_tbl[] = {
76	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_PF_INT_5_X),},
77	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_VF_INT_5_X),},
78	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_PF_INT_6_X),},
79	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_VF_INT_6_X),},
80	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_PF_DSC_1_X),},
81	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_VF_DSC_1_X),},
82	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_INTEL_PAC_N3000),},
83	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_INTEL_PAC_D5005),},
84	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_INTEL_PAC_D5005_VF),},
85	{0,}
86};
87MODULE_DEVICE_TABLE(pci, cci_pcie_id_tbl);
88
89static int cci_init_drvdata(struct pci_dev *pcidev)
90{
91	struct cci_drvdata *drvdata;
92
93	drvdata = devm_kzalloc(&pcidev->dev, sizeof(*drvdata), GFP_KERNEL);
94	if (!drvdata)
95		return -ENOMEM;
96
97	pci_set_drvdata(pcidev, drvdata);
98
99	return 0;
100}
101
102static void cci_remove_feature_devs(struct pci_dev *pcidev)
103{
104	struct cci_drvdata *drvdata = pci_get_drvdata(pcidev);
105
106	/* remove all children feature devices */
107	dfl_fpga_feature_devs_remove(drvdata->cdev);
108	cci_pci_free_irq(pcidev);
109}
110
111static int *cci_pci_create_irq_table(struct pci_dev *pcidev, unsigned int nvec)
112{
113	unsigned int i;
114	int *table;
115
116	table = kcalloc(nvec, sizeof(int), GFP_KERNEL);
117	if (!table)
118		return table;
119
120	for (i = 0; i < nvec; i++)
121		table[i] = pci_irq_vector(pcidev, i);
122
123	return table;
124}
125
126/* enumerate feature devices under pci device */
127static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
128{
129	struct cci_drvdata *drvdata = pci_get_drvdata(pcidev);
130	int port_num, bar, i, nvec, ret = 0;
131	struct dfl_fpga_enum_info *info;
132	struct dfl_fpga_cdev *cdev;
133	resource_size_t start, len;
134	void __iomem *base;
135	int *irq_table;
136	u32 offset;
137	u64 v;
138
139	/* allocate enumeration info via pci_dev */
140	info = dfl_fpga_enum_info_alloc(&pcidev->dev);
141	if (!info)
142		return -ENOMEM;
143
144	/* add irq info for enumeration if the device support irq */
145	nvec = cci_pci_alloc_irq(pcidev);
146	if (nvec < 0) {
147		dev_err(&pcidev->dev, "Fail to alloc irq %d.\n", nvec);
148		ret = nvec;
149		goto enum_info_free_exit;
150	} else if (nvec) {
151		irq_table = cci_pci_create_irq_table(pcidev, nvec);
152		if (!irq_table) {
153			ret = -ENOMEM;
154			goto irq_free_exit;
155		}
156
157		ret = dfl_fpga_enum_info_add_irq(info, nvec, irq_table);
158		kfree(irq_table);
159		if (ret)
160			goto irq_free_exit;
161	}
162
163	/* start to find Device Feature List in Bar 0 */
164	base = cci_pci_ioremap_bar0(pcidev);
165	if (!base) {
166		ret = -ENOMEM;
167		goto irq_free_exit;
168	}
169
170	/*
171	 * PF device has FME and Ports/AFUs, and VF device only has one
172	 * Port/AFU. Check them and add related "Device Feature List" info
173	 * for the next step enumeration.
174	 */
175	if (dfl_feature_is_fme(base)) {
176		start = pci_resource_start(pcidev, 0);
177		len = pci_resource_len(pcidev, 0);
178
179		dfl_fpga_enum_info_add_dfl(info, start, len);
180
181		/*
182		 * find more Device Feature Lists (e.g. Ports) per information
183		 * indicated by FME module.
184		 */
185		v = readq(base + FME_HDR_CAP);
186		port_num = FIELD_GET(FME_CAP_NUM_PORTS, v);
187
188		WARN_ON(port_num > MAX_DFL_FPGA_PORT_NUM);
189
190		for (i = 0; i < port_num; i++) {
191			v = readq(base + FME_HDR_PORT_OFST(i));
192
193			/* skip ports which are not implemented. */
194			if (!(v & FME_PORT_OFST_IMP))
195				continue;
196
197			/*
198			 * add Port's Device Feature List information for next
199			 * step enumeration.
200			 */
201			bar = FIELD_GET(FME_PORT_OFST_BAR_ID, v);
202			offset = FIELD_GET(FME_PORT_OFST_DFH_OFST, v);
203			start = pci_resource_start(pcidev, bar) + offset;
204			len = pci_resource_len(pcidev, bar) - offset;
205
206			dfl_fpga_enum_info_add_dfl(info, start, len);
207		}
208	} else if (dfl_feature_is_port(base)) {
209		start = pci_resource_start(pcidev, 0);
210		len = pci_resource_len(pcidev, 0);
211
212		dfl_fpga_enum_info_add_dfl(info, start, len);
213	} else {
214		ret = -ENODEV;
215		goto irq_free_exit;
216	}
217
218	/* release I/O mappings for next step enumeration */
219	pcim_iounmap_regions(pcidev, BIT(0));
220
221	/* start enumeration with prepared enumeration information */
222	cdev = dfl_fpga_feature_devs_enumerate(info);
223	if (IS_ERR(cdev)) {
224		dev_err(&pcidev->dev, "Enumeration failure\n");
225		ret = PTR_ERR(cdev);
226		goto irq_free_exit;
227	}
228
229	drvdata->cdev = cdev;
230
231irq_free_exit:
232	if (ret)
233		cci_pci_free_irq(pcidev);
234enum_info_free_exit:
235	dfl_fpga_enum_info_free(info);
236
237	return ret;
238}
239
240static
241int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid)
242{
243	int ret;
244
245	ret = pcim_enable_device(pcidev);
246	if (ret < 0) {
247		dev_err(&pcidev->dev, "Failed to enable device %d.\n", ret);
248		return ret;
249	}
250
251	ret = pci_enable_pcie_error_reporting(pcidev);
252	if (ret && ret != -EINVAL)
253		dev_info(&pcidev->dev, "PCIE AER unavailable %d.\n", ret);
254
255	pci_set_master(pcidev);
256
257	if (!pci_set_dma_mask(pcidev, DMA_BIT_MASK(64))) {
258		ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64));
259		if (ret)
260			goto disable_error_report_exit;
261	} else if (!pci_set_dma_mask(pcidev, DMA_BIT_MASK(32))) {
262		ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(32));
263		if (ret)
264			goto disable_error_report_exit;
265	} else {
266		ret = -EIO;
267		dev_err(&pcidev->dev, "No suitable DMA support available.\n");
268		goto disable_error_report_exit;
269	}
270
271	ret = cci_init_drvdata(pcidev);
272	if (ret) {
273		dev_err(&pcidev->dev, "Fail to init drvdata %d.\n", ret);
274		goto disable_error_report_exit;
275	}
276
277	ret = cci_enumerate_feature_devs(pcidev);
278	if (!ret)
279		return ret;
280
281	dev_err(&pcidev->dev, "enumeration failure %d.\n", ret);
282
283disable_error_report_exit:
284	pci_disable_pcie_error_reporting(pcidev);
285	return ret;
286}
287
288static int cci_pci_sriov_configure(struct pci_dev *pcidev, int num_vfs)
289{
290	struct cci_drvdata *drvdata = pci_get_drvdata(pcidev);
291	struct dfl_fpga_cdev *cdev = drvdata->cdev;
292
293	if (!num_vfs) {
294		/*
295		 * disable SRIOV and then put released ports back to default
296		 * PF access mode.
297		 */
298		pci_disable_sriov(pcidev);
299
300		dfl_fpga_cdev_config_ports_pf(cdev);
301
302	} else {
303		int ret;
304
305		/*
306		 * before enable SRIOV, put released ports into VF access mode
307		 * first of all.
308		 */
309		ret = dfl_fpga_cdev_config_ports_vf(cdev, num_vfs);
310		if (ret)
311			return ret;
312
313		ret = pci_enable_sriov(pcidev, num_vfs);
314		if (ret) {
315			dfl_fpga_cdev_config_ports_pf(cdev);
316			return ret;
317		}
318	}
319
320	return num_vfs;
321}
322
323static void cci_pci_remove(struct pci_dev *pcidev)
324{
325	if (dev_is_pf(&pcidev->dev))
326		cci_pci_sriov_configure(pcidev, 0);
327
328	cci_remove_feature_devs(pcidev);
329	pci_disable_pcie_error_reporting(pcidev);
330}
331
332static struct pci_driver cci_pci_driver = {
333	.name = DRV_NAME,
334	.id_table = cci_pcie_id_tbl,
335	.probe = cci_pci_probe,
336	.remove = cci_pci_remove,
337	.sriov_configure = cci_pci_sriov_configure,
338};
339
340module_pci_driver(cci_pci_driver);
341
342MODULE_DESCRIPTION("FPGA DFL PCIe Device Driver");
343MODULE_AUTHOR("Intel Corporation");
344MODULE_LICENSE("GPL v2");
345