GNU Linux-libre 4.19.207-gnu1
[releases.git] / drivers / char / xillybus / xillybus_pcie.c
1 /*
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
19 MODULE_DESCRIPTION("Xillybus driver for PCIe");
20 MODULE_AUTHOR("Eli Billauer, Xillybus Ltd.");
21 MODULE_VERSION("1.06");
22 MODULE_ALIAS("xillybus_pcie");
23 MODULE_LICENSE("GPL v2");
24
25 #define PCI_DEVICE_ID_XILLYBUS          0xebeb
26
27 #define PCI_VENDOR_ID_ACTEL             0x11aa
28 #define PCI_VENDOR_ID_LATTICE           0x1204
29
30 static const char xillyname[] = "xillybus_pcie";
31
32 static const struct pci_device_id xillyids[] = {
33         {PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILLYBUS)},
34         {PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_XILLYBUS)},
35         {PCI_DEVICE(PCI_VENDOR_ID_ACTEL, PCI_DEVICE_ID_XILLYBUS)},
36         {PCI_DEVICE(PCI_VENDOR_ID_LATTICE, PCI_DEVICE_ID_XILLYBUS)},
37         { /* End: all zeroes */ }
38 };
39
40 static int xilly_pci_direction(int direction)
41 {
42         switch (direction) {
43         case DMA_TO_DEVICE:
44                 return PCI_DMA_TODEVICE;
45         case DMA_FROM_DEVICE:
46                 return PCI_DMA_FROMDEVICE;
47         default:
48                 return PCI_DMA_BIDIRECTIONAL;
49         }
50 }
51
52 static void xilly_dma_sync_single_for_cpu_pci(struct xilly_endpoint *ep,
53                                               dma_addr_t dma_handle,
54                                               size_t size,
55                                               int direction)
56 {
57         pci_dma_sync_single_for_cpu(ep->pdev,
58                                     dma_handle,
59                                     size,
60                                     xilly_pci_direction(direction));
61 }
62
63 static void xilly_dma_sync_single_for_device_pci(struct xilly_endpoint *ep,
64                                                  dma_addr_t dma_handle,
65                                                  size_t size,
66                                                  int direction)
67 {
68         pci_dma_sync_single_for_device(ep->pdev,
69                                        dma_handle,
70                                        size,
71                                        xilly_pci_direction(direction));
72 }
73
74 static void xilly_pci_unmap(void *ptr)
75 {
76         struct xilly_mapping *data = ptr;
77
78         pci_unmap_single(data->device, data->dma_addr,
79                          data->size, data->direction);
80
81         kfree(ptr);
82 }
83
84 /*
85  * Map either through the PCI DMA mapper or the non_PCI one. Behind the
86  * scenes exactly the same functions are called with the same parameters,
87  * but that can change.
88  */
89
90 static int xilly_map_single_pci(struct xilly_endpoint *ep,
91                                 void *ptr,
92                                 size_t size,
93                                 int direction,
94                                 dma_addr_t *ret_dma_handle
95         )
96 {
97         int pci_direction;
98         dma_addr_t addr;
99         struct xilly_mapping *this;
100
101         this = kzalloc(sizeof(*this), GFP_KERNEL);
102         if (!this)
103                 return -ENOMEM;
104
105         pci_direction = xilly_pci_direction(direction);
106
107         addr = pci_map_single(ep->pdev, ptr, size, pci_direction);
108
109         if (pci_dma_mapping_error(ep->pdev, addr)) {
110                 kfree(this);
111                 return -ENODEV;
112         }
113
114         this->device = ep->pdev;
115         this->dma_addr = addr;
116         this->size = size;
117         this->direction = pci_direction;
118
119         *ret_dma_handle = addr;
120
121         return devm_add_action_or_reset(ep->dev, xilly_pci_unmap, this);
122 }
123
124 static struct xilly_endpoint_hardware pci_hw = {
125         .owner = THIS_MODULE,
126         .hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_pci,
127         .hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_pci,
128         .map_single = xilly_map_single_pci,
129 };
130
131 static int xilly_probe(struct pci_dev *pdev,
132                        const struct pci_device_id *ent)
133 {
134         struct xilly_endpoint *endpoint;
135         int rc;
136
137         endpoint = xillybus_init_endpoint(pdev, &pdev->dev, &pci_hw);
138
139         if (!endpoint)
140                 return -ENOMEM;
141
142         pci_set_drvdata(pdev, endpoint);
143
144         rc = pcim_enable_device(pdev);
145         if (rc) {
146                 dev_err(endpoint->dev,
147                         "pcim_enable_device() failed. Aborting.\n");
148                 return rc;
149         }
150
151         /* L0s has caused packet drops. No power saving, thank you. */
152
153         pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S);
154
155         if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
156                 dev_err(endpoint->dev,
157                         "Incorrect BAR configuration. Aborting.\n");
158                 return -ENODEV;
159         }
160
161         rc = pcim_iomap_regions(pdev, 0x01, xillyname);
162         if (rc) {
163                 dev_err(endpoint->dev,
164                         "pcim_iomap_regions() failed. Aborting.\n");
165                 return rc;
166         }
167
168         endpoint->registers = pcim_iomap_table(pdev)[0];
169
170         pci_set_master(pdev);
171
172         /* Set up a single MSI interrupt */
173         if (pci_enable_msi(pdev)) {
174                 dev_err(endpoint->dev,
175                         "Failed to enable MSI interrupts. Aborting.\n");
176                 return -ENODEV;
177         }
178         rc = devm_request_irq(&pdev->dev, pdev->irq, xillybus_isr, 0,
179                               xillyname, endpoint);
180         if (rc) {
181                 dev_err(endpoint->dev,
182                         "Failed to register MSI handler. Aborting.\n");
183                 return -ENODEV;
184         }
185
186         /*
187          * Some (old and buggy?) hardware drops 64-bit addressed PCIe packets,
188          * even when the PCIe driver claims that a 64-bit mask is OK. On the
189          * other hand, on some architectures, 64-bit addressing is mandatory.
190          * So go for the 64-bit mask only when failing is the other option.
191          */
192
193         if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
194                 endpoint->dma_using_dac = 0;
195         } else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
196                 endpoint->dma_using_dac = 1;
197         } else {
198                 dev_err(endpoint->dev, "Failed to set DMA mask. Aborting.\n");
199                 return -ENODEV;
200         }
201
202         return xillybus_endpoint_discovery(endpoint);
203 }
204
205 static void xilly_remove(struct pci_dev *pdev)
206 {
207         struct xilly_endpoint *endpoint = pci_get_drvdata(pdev);
208
209         xillybus_endpoint_remove(endpoint);
210 }
211
212 MODULE_DEVICE_TABLE(pci, xillyids);
213
214 static struct pci_driver xillybus_driver = {
215         .name = xillyname,
216         .id_table = xillyids,
217         .probe = xilly_probe,
218         .remove = xilly_remove,
219 };
220
221 module_pci_driver(xillybus_driver);