1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * linux/drivers/misc/xillybus_pcie.c |
4 | * |
5 | * Copyright 2011 Xillybus Ltd, http://xillybus.com |
6 | * |
7 | * Driver for the Xillybus FPGA/host framework using PCI Express. |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/pci.h> |
12 | #include <linux/slab.h> |
13 | #include "xillybus.h" |
14 | |
15 | MODULE_DESCRIPTION("Xillybus driver for PCIe" ); |
16 | MODULE_AUTHOR("Eli Billauer, Xillybus Ltd." ); |
17 | MODULE_ALIAS("xillybus_pcie" ); |
18 | MODULE_LICENSE("GPL v2" ); |
19 | |
20 | #define PCI_DEVICE_ID_XILLYBUS 0xebeb |
21 | |
22 | #define PCI_VENDOR_ID_ACTEL 0x11aa |
23 | #define PCI_VENDOR_ID_LATTICE 0x1204 |
24 | |
25 | static const char xillyname[] = "xillybus_pcie" ; |
26 | |
27 | static const struct pci_device_id xillyids[] = { |
28 | {PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILLYBUS)}, |
29 | {PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_XILLYBUS)}, |
30 | {PCI_DEVICE(PCI_VENDOR_ID_ACTEL, PCI_DEVICE_ID_XILLYBUS)}, |
31 | {PCI_DEVICE(PCI_VENDOR_ID_LATTICE, PCI_DEVICE_ID_XILLYBUS)}, |
32 | { /* End: all zeroes */ } |
33 | }; |
34 | |
35 | static int xilly_probe(struct pci_dev *pdev, |
36 | const struct pci_device_id *ent) |
37 | { |
38 | struct xilly_endpoint *endpoint; |
39 | int rc; |
40 | |
41 | endpoint = xillybus_init_endpoint(dev: &pdev->dev); |
42 | |
43 | if (!endpoint) |
44 | return -ENOMEM; |
45 | |
46 | pci_set_drvdata(pdev, data: endpoint); |
47 | |
48 | endpoint->owner = THIS_MODULE; |
49 | |
50 | rc = pcim_enable_device(pdev); |
51 | if (rc) { |
52 | dev_err(endpoint->dev, |
53 | "pcim_enable_device() failed. Aborting.\n" ); |
54 | return rc; |
55 | } |
56 | |
57 | /* L0s has caused packet drops. No power saving, thank you. */ |
58 | |
59 | pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S); |
60 | |
61 | if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { |
62 | dev_err(endpoint->dev, |
63 | "Incorrect BAR configuration. Aborting.\n" ); |
64 | return -ENODEV; |
65 | } |
66 | |
67 | rc = pcim_iomap_regions(pdev, mask: 0x01, name: xillyname); |
68 | if (rc) { |
69 | dev_err(endpoint->dev, |
70 | "pcim_iomap_regions() failed. Aborting.\n" ); |
71 | return rc; |
72 | } |
73 | |
74 | endpoint->registers = pcim_iomap_table(pdev)[0]; |
75 | |
76 | pci_set_master(dev: pdev); |
77 | |
78 | /* Set up a single MSI interrupt */ |
79 | if (pci_enable_msi(dev: pdev)) { |
80 | dev_err(endpoint->dev, |
81 | "Failed to enable MSI interrupts. Aborting.\n" ); |
82 | return -ENODEV; |
83 | } |
84 | rc = devm_request_irq(dev: &pdev->dev, irq: pdev->irq, handler: xillybus_isr, irqflags: 0, |
85 | devname: xillyname, dev_id: endpoint); |
86 | if (rc) { |
87 | dev_err(endpoint->dev, |
88 | "Failed to register MSI handler. Aborting.\n" ); |
89 | return -ENODEV; |
90 | } |
91 | |
92 | /* |
93 | * Some (old and buggy?) hardware drops 64-bit addressed PCIe packets, |
94 | * even when the PCIe driver claims that a 64-bit mask is OK. On the |
95 | * other hand, on some architectures, 64-bit addressing is mandatory. |
96 | * So go for the 64-bit mask only when failing is the other option. |
97 | */ |
98 | |
99 | if (!dma_set_mask(dev: &pdev->dev, DMA_BIT_MASK(32))) { |
100 | endpoint->dma_using_dac = 0; |
101 | } else if (!dma_set_mask(dev: &pdev->dev, DMA_BIT_MASK(64))) { |
102 | endpoint->dma_using_dac = 1; |
103 | } else { |
104 | dev_err(endpoint->dev, "Failed to set DMA mask. Aborting.\n" ); |
105 | return -ENODEV; |
106 | } |
107 | |
108 | return xillybus_endpoint_discovery(endpoint); |
109 | } |
110 | |
111 | static void xilly_remove(struct pci_dev *pdev) |
112 | { |
113 | struct xilly_endpoint *endpoint = pci_get_drvdata(pdev); |
114 | |
115 | xillybus_endpoint_remove(endpoint); |
116 | } |
117 | |
118 | MODULE_DEVICE_TABLE(pci, xillyids); |
119 | |
120 | static struct pci_driver xillybus_driver = { |
121 | .name = xillyname, |
122 | .id_table = xillyids, |
123 | .probe = xilly_probe, |
124 | .remove = xilly_remove, |
125 | }; |
126 | |
127 | module_pci_driver(xillybus_driver); |
128 | |