blob: 67013652358b905039bbfae37791cb721baf7352 [file] [log] [blame]
Eli Billauer48bae052013-06-24 18:55:47 +03001/*
2 * linux/drivers/misc/xillybus_pcie.c
3 *
4 * Copyright 2011 Xillybus Ltd, http://xillybus.com
5 *
6 * Driver for the Xillybus FPGA/host framework using PCI Express.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the smems of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 */
12
13#include <linux/module.h>
14#include <linux/pci.h>
15#include <linux/pci-aspm.h>
16#include <linux/slab.h>
17#include "xillybus.h"
18
19MODULE_DESCRIPTION("Xillybus driver for PCIe");
20MODULE_AUTHOR("Eli Billauer, Xillybus Ltd.");
21MODULE_VERSION("1.06");
22MODULE_ALIAS("xillybus_pcie");
23MODULE_LICENSE("GPL v2");
24
25#define PCI_DEVICE_ID_XILLYBUS 0xebeb
26
27#define PCI_VENDOR_ID_ALTERA 0x1172
28#define PCI_VENDOR_ID_ACTEL 0x11aa
29#define PCI_VENDOR_ID_LATTICE 0x1204
30
Eli Billauere71042f2013-07-27 00:24:00 +030031static const char xillyname[] = "xillybus_pcie";
32
Eli Billauer48bae052013-06-24 18:55:47 +030033static DEFINE_PCI_DEVICE_TABLE(xillyids) = {
34 {PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILLYBUS)},
35 {PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_XILLYBUS)},
36 {PCI_DEVICE(PCI_VENDOR_ID_ACTEL, PCI_DEVICE_ID_XILLYBUS)},
37 {PCI_DEVICE(PCI_VENDOR_ID_LATTICE, PCI_DEVICE_ID_XILLYBUS)},
38 { /* End: all zeroes */ }
39};
40
41static int xilly_pci_direction(int direction)
42{
43 switch (direction) {
44 case DMA_TO_DEVICE:
45 return PCI_DMA_TODEVICE;
46 case DMA_FROM_DEVICE:
47 return PCI_DMA_FROMDEVICE;
48 default:
49 return PCI_DMA_BIDIRECTIONAL;
50 }
51}
52
53static void xilly_dma_sync_single_for_cpu_pci(struct xilly_endpoint *ep,
54 dma_addr_t dma_handle,
55 size_t size,
56 int direction)
57{
58 pci_dma_sync_single_for_cpu(ep->pdev,
59 dma_handle,
60 size,
61 xilly_pci_direction(direction));
62}
63
64static void xilly_dma_sync_single_for_device_pci(struct xilly_endpoint *ep,
65 dma_addr_t dma_handle,
66 size_t size,
67 int direction)
68{
69 pci_dma_sync_single_for_device(ep->pdev,
70 dma_handle,
71 size,
72 xilly_pci_direction(direction));
73}
74
75/*
76 * Map either through the PCI DMA mapper or the non_PCI one. Behind the
77 * scenes exactly the same functions are called with the same parameters,
78 * but that can change.
79 */
80
81static dma_addr_t xilly_map_single_pci(struct xilly_cleanup *mem,
82 struct xilly_endpoint *ep,
83 void *ptr,
84 size_t size,
85 int direction
86 )
87{
88
89 dma_addr_t addr = 0;
90 struct xilly_dma *this;
91 int pci_direction;
92
93 this = kmalloc(sizeof(struct xilly_dma), GFP_KERNEL);
94 if (!this)
95 return 0;
96
97 pci_direction = xilly_pci_direction(direction);
98 addr = pci_map_single(ep->pdev, ptr, size, pci_direction);
99 this->direction = pci_direction;
100
101 if (pci_dma_mapping_error(ep->pdev, addr)) {
102 kfree(this);
103 return 0;
104 }
105
106 this->dma_addr = addr;
107 this->pdev = ep->pdev;
108 this->size = size;
109
110 list_add_tail(&this->node, &mem->to_unmap);
111
112 return addr;
113}
114
Eli Billauer7ee9ded2013-07-31 11:22:43 +0300115static void xilly_unmap_single_pci(struct xilly_dma *entry)
Eli Billauer48bae052013-06-24 18:55:47 +0300116{
117 pci_unmap_single(entry->pdev,
118 entry->dma_addr,
119 entry->size,
120 entry->direction);
121}
122
123static struct xilly_endpoint_hardware pci_hw = {
124 .owner = THIS_MODULE,
Eli Billauer7ee9ded2013-07-31 11:22:43 +0300125 .hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_pci,
126 .hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_pci,
Eli Billauer48bae052013-06-24 18:55:47 +0300127 .map_single = xilly_map_single_pci,
128 .unmap_single = xilly_unmap_single_pci
129};
130
131static int xilly_probe(struct pci_dev *pdev,
132 const struct pci_device_id *ent)
133{
134 struct xilly_endpoint *endpoint;
135 int rc = 0;
136
137 endpoint = xillybus_init_endpoint(pdev, NULL, &pci_hw);
138
139 if (!endpoint)
140 return -ENOMEM;
141
142 pci_set_drvdata(pdev, endpoint);
143
144 rc = pci_enable_device(pdev);
145
146 /* L0s has caused packet drops. No power saving, thank you. */
147
148 pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S);
149
150 if (rc) {
151 pr_err("xillybus: pci_enable_device() failed. "
152 "Aborting.\n");
153 goto no_enable;
154 }
155
156 if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
157 pr_err("xillybus: Incorrect BAR configuration. "
158 "Aborting.\n");
159 rc = -ENODEV;
160 goto bad_bar;
161 }
162
163 rc = pci_request_regions(pdev, xillyname);
164 if (rc) {
165 pr_err("xillybus: pci_request_regions() failed. "
166 "Aborting.\n");
167 goto failed_request_regions;
168 }
169
170 endpoint->registers = pci_iomap(pdev, 0, 128);
171
172 if (!endpoint->registers) {
173 pr_err("xillybus: Failed to map BAR 0. Aborting.\n");
174 goto failed_iomap0;
175 }
176
177 pci_set_master(pdev);
178
179 /* Set up a single MSI interrupt */
180 if (pci_enable_msi(pdev)) {
181 pr_err("xillybus: Failed to enable MSI interrupts. "
182 "Aborting.\n");
183 rc = -ENODEV;
184 goto failed_enable_msi;
185 }
186 rc = request_irq(pdev->irq, xillybus_isr, 0, xillyname, endpoint);
187
188 if (rc) {
189 pr_err("xillybus: Failed to register MSI handler. "
190 "Aborting.\n");
191 rc = -ENODEV;
192 goto failed_register_msi;
193 }
194
195 /*
196 * In theory, an attempt to set the DMA mask to 64 and dma_using_dac=1
197 * is the right thing. But some unclever PCIe drivers report it's OK
198 * when the hardware drops those 64-bit PCIe packets. So trust
199 * nobody and use 32 bits DMA addressing in any case.
200 */
201
202 if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32)))
203 endpoint->dma_using_dac = 0;
204 else {
205 pr_err("xillybus: Failed to set DMA mask. "
206 "Aborting.\n");
207 rc = -ENODEV;
208 goto failed_dmamask;
209 }
210
211 rc = xillybus_endpoint_discovery(endpoint);
212
213 if (!rc)
214 return 0;
215
216failed_dmamask:
217 free_irq(pdev->irq, endpoint);
218failed_register_msi:
219 pci_disable_msi(pdev);
220failed_enable_msi:
221 /* pci_clear_master(pdev); Nobody else seems to do this */
222 pci_iounmap(pdev, endpoint->registers);
223failed_iomap0:
224 pci_release_regions(pdev);
225failed_request_regions:
226bad_bar:
227 pci_disable_device(pdev);
228no_enable:
229 xillybus_do_cleanup(&endpoint->cleanup, endpoint);
230
231 kfree(endpoint);
232 return rc;
233}
234
235static void xilly_remove(struct pci_dev *pdev)
236{
237 struct xilly_endpoint *endpoint = pci_get_drvdata(pdev);
238
239 xillybus_endpoint_remove(endpoint);
240
241 free_irq(pdev->irq, endpoint);
242
243 pci_disable_msi(pdev);
244 pci_iounmap(pdev, endpoint->registers);
245 pci_release_regions(pdev);
246 pci_disable_device(pdev);
247
248 xillybus_do_cleanup(&endpoint->cleanup, endpoint);
249
250 kfree(endpoint);
251}
252
253MODULE_DEVICE_TABLE(pci, xillyids);
254
255static struct pci_driver xillybus_driver = {
256 .name = xillyname,
257 .id_table = xillyids,
258 .probe = xilly_probe,
259 .remove = xilly_remove,
260};
261
262module_pci_driver(xillybus_driver);