1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * ddbridge.c: Digital Devices PCIe bridge driver |
4 | * |
5 | * Copyright (C) 2010-2017 Digital Devices GmbH |
6 | * Ralph Metzler <rjkm@metzlerbros.de> |
7 | * Marcus Metzler <mocm@metzlerbros.de> |
8 | */ |
9 | |
10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/init.h> |
14 | #include <linux/interrupt.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/poll.h> |
18 | #include <linux/io.h> |
19 | #include <linux/pci.h> |
20 | #include <linux/pci_ids.h> |
21 | #include <linux/timer.h> |
22 | #include <linux/i2c.h> |
23 | #include <linux/swab.h> |
24 | #include <linux/vmalloc.h> |
25 | |
26 | #include "ddbridge.h" |
27 | #include "ddbridge-i2c.h" |
28 | #include "ddbridge-regs.h" |
29 | #include "ddbridge-hw.h" |
30 | #include "ddbridge-io.h" |
31 | |
32 | /****************************************************************************/ |
33 | /* module parameters */ |
34 | |
35 | #ifdef CONFIG_PCI_MSI |
36 | #ifdef CONFIG_DVB_DDBRIDGE_MSIENABLE |
37 | static int msi = 1; |
38 | #else |
39 | static int msi; |
40 | #endif |
41 | module_param(msi, int, 0444); |
42 | #ifdef CONFIG_DVB_DDBRIDGE_MSIENABLE |
43 | MODULE_PARM_DESC(msi, "Control MSI interrupts: 0-disable, 1-enable (default)" ); |
44 | #else |
45 | MODULE_PARM_DESC(msi, "Control MSI interrupts: 0-disable (default), 1-enable" ); |
46 | #endif |
47 | #endif |
48 | |
49 | /****************************************************************************/ |
50 | /****************************************************************************/ |
51 | /****************************************************************************/ |
52 | |
53 | static void ddb_irq_disable(struct ddb *dev) |
54 | { |
55 | ddbwritel(dev, val: 0, INTERRUPT_ENABLE); |
56 | ddbwritel(dev, val: 0, MSI1_ENABLE); |
57 | } |
58 | |
59 | static void ddb_msi_exit(struct ddb *dev) |
60 | { |
61 | #ifdef CONFIG_PCI_MSI |
62 | if (dev->msi) |
63 | pci_free_irq_vectors(dev: dev->pdev); |
64 | #endif |
65 | } |
66 | |
67 | static void ddb_irq_exit(struct ddb *dev) |
68 | { |
69 | ddb_irq_disable(dev); |
70 | if (dev->msi == 2) |
71 | free_irq(pci_irq_vector(dev: dev->pdev, nr: 1), dev); |
72 | free_irq(pci_irq_vector(dev: dev->pdev, nr: 0), dev); |
73 | } |
74 | |
75 | static void ddb_remove(struct pci_dev *pdev) |
76 | { |
77 | struct ddb *dev = (struct ddb *)pci_get_drvdata(pdev); |
78 | |
79 | ddb_device_destroy(dev); |
80 | ddb_ports_detach(dev); |
81 | ddb_i2c_release(dev); |
82 | |
83 | ddb_irq_exit(dev); |
84 | ddb_msi_exit(dev); |
85 | ddb_ports_release(dev); |
86 | ddb_buffers_free(dev); |
87 | |
88 | ddb_unmap(dev); |
89 | pci_set_drvdata(pdev, NULL); |
90 | pci_disable_device(dev: pdev); |
91 | } |
92 | |
93 | #ifdef CONFIG_PCI_MSI |
94 | static void ddb_irq_msi(struct ddb *dev, int nr) |
95 | { |
96 | int stat; |
97 | |
98 | if (msi && pci_msi_enabled()) { |
99 | stat = pci_alloc_irq_vectors(dev: dev->pdev, min_vecs: 1, max_vecs: nr, |
100 | PCI_IRQ_MSI | PCI_IRQ_MSIX); |
101 | if (stat >= 1) { |
102 | dev->msi = stat; |
103 | dev_info(dev->dev, "using %d MSI interrupt(s)\n" , |
104 | dev->msi); |
105 | } else { |
106 | dev_info(dev->dev, "MSI not available.\n" ); |
107 | } |
108 | } |
109 | } |
110 | #endif |
111 | |
112 | static int ddb_irq_init(struct ddb *dev) |
113 | { |
114 | int stat; |
115 | int irq_flag = IRQF_SHARED; |
116 | |
117 | ddbwritel(dev, val: 0x00000000, INTERRUPT_ENABLE); |
118 | ddbwritel(dev, val: 0x00000000, MSI1_ENABLE); |
119 | ddbwritel(dev, val: 0x00000000, MSI2_ENABLE); |
120 | ddbwritel(dev, val: 0x00000000, MSI3_ENABLE); |
121 | ddbwritel(dev, val: 0x00000000, MSI4_ENABLE); |
122 | ddbwritel(dev, val: 0x00000000, MSI5_ENABLE); |
123 | ddbwritel(dev, val: 0x00000000, MSI6_ENABLE); |
124 | ddbwritel(dev, val: 0x00000000, MSI7_ENABLE); |
125 | |
126 | #ifdef CONFIG_PCI_MSI |
127 | ddb_irq_msi(dev, nr: 2); |
128 | |
129 | if (dev->msi) |
130 | irq_flag = 0; |
131 | if (dev->msi == 2) { |
132 | stat = request_irq(irq: pci_irq_vector(dev: dev->pdev, nr: 0), |
133 | handler: ddb_irq_handler0, flags: irq_flag, name: "ddbridge" , |
134 | dev: (void *)dev); |
135 | if (stat < 0) |
136 | return stat; |
137 | stat = request_irq(irq: pci_irq_vector(dev: dev->pdev, nr: 1), |
138 | handler: ddb_irq_handler1, flags: irq_flag, name: "ddbridge" , |
139 | dev: (void *)dev); |
140 | if (stat < 0) { |
141 | free_irq(pci_irq_vector(dev: dev->pdev, nr: 0), dev); |
142 | return stat; |
143 | } |
144 | } else |
145 | #endif |
146 | { |
147 | stat = request_irq(irq: pci_irq_vector(dev: dev->pdev, nr: 0), |
148 | handler: ddb_irq_handler, flags: irq_flag, name: "ddbridge" , |
149 | dev: (void *)dev); |
150 | if (stat < 0) |
151 | return stat; |
152 | } |
153 | if (dev->msi == 2) { |
154 | ddbwritel(dev, val: 0x0fffff00, INTERRUPT_ENABLE); |
155 | ddbwritel(dev, val: 0x0000000f, MSI1_ENABLE); |
156 | } else { |
157 | ddbwritel(dev, val: 0x0fffff0f, INTERRUPT_ENABLE); |
158 | ddbwritel(dev, val: 0x00000000, MSI1_ENABLE); |
159 | } |
160 | return stat; |
161 | } |
162 | |
163 | static int ddb_probe(struct pci_dev *pdev, |
164 | const struct pci_device_id *id) |
165 | { |
166 | struct ddb *dev; |
167 | int stat = 0; |
168 | |
169 | if (pci_enable_device(dev: pdev) < 0) |
170 | return -ENODEV; |
171 | |
172 | pci_set_master(dev: pdev); |
173 | |
174 | if (dma_set_mask(dev: &pdev->dev, DMA_BIT_MASK(64))) |
175 | if (dma_set_mask(dev: &pdev->dev, DMA_BIT_MASK(32))) |
176 | return -ENODEV; |
177 | |
178 | dev = vzalloc(size: sizeof(*dev)); |
179 | if (!dev) |
180 | return -ENOMEM; |
181 | |
182 | mutex_init(&dev->mutex); |
183 | dev->has_dma = 1; |
184 | dev->pdev = pdev; |
185 | dev->dev = &pdev->dev; |
186 | pci_set_drvdata(pdev, data: dev); |
187 | |
188 | dev->link[0].ids.vendor = id->vendor; |
189 | dev->link[0].ids.device = id->device; |
190 | dev->link[0].ids.subvendor = id->subvendor; |
191 | dev->link[0].ids.subdevice = pdev->subsystem_device; |
192 | dev->link[0].ids.devid = (id->device << 16) | id->vendor; |
193 | |
194 | dev->link[0].dev = dev; |
195 | dev->link[0].info = get_ddb_info(vendor: id->vendor, device: id->device, |
196 | subvendor: id->subvendor, subdevice: pdev->subsystem_device); |
197 | |
198 | dev_info(&pdev->dev, "detected %s\n" , dev->link[0].info->name); |
199 | |
200 | dev->regs_len = pci_resource_len(dev->pdev, 0); |
201 | dev->regs = ioremap(pci_resource_start(dev->pdev, 0), |
202 | pci_resource_len(dev->pdev, 0)); |
203 | |
204 | if (!dev->regs) { |
205 | dev_err(&pdev->dev, "not enough memory for register map\n" ); |
206 | stat = -ENOMEM; |
207 | goto fail; |
208 | } |
209 | if (ddbreadl(dev, adr: 0) == 0xffffffff) { |
210 | dev_err(&pdev->dev, "cannot read registers\n" ); |
211 | stat = -ENODEV; |
212 | goto fail; |
213 | } |
214 | |
215 | dev->link[0].ids.hwid = ddbreadl(dev, adr: 0); |
216 | dev->link[0].ids.regmapid = ddbreadl(dev, adr: 4); |
217 | |
218 | dev_info(&pdev->dev, "HW %08x REGMAP %08x\n" , |
219 | dev->link[0].ids.hwid, dev->link[0].ids.regmapid); |
220 | |
221 | ddbwritel(dev, val: 0, DMA_BASE_READ); |
222 | ddbwritel(dev, val: 0, DMA_BASE_WRITE); |
223 | |
224 | stat = ddb_irq_init(dev); |
225 | if (stat < 0) |
226 | goto fail0; |
227 | |
228 | if (ddb_init(dev) == 0) |
229 | return 0; |
230 | |
231 | ddb_irq_exit(dev); |
232 | fail0: |
233 | dev_err(&pdev->dev, "fail0\n" ); |
234 | ddb_msi_exit(dev); |
235 | fail: |
236 | dev_err(&pdev->dev, "fail\n" ); |
237 | |
238 | ddb_unmap(dev); |
239 | pci_set_drvdata(pdev, NULL); |
240 | pci_disable_device(dev: pdev); |
241 | return stat; |
242 | } |
243 | |
244 | /****************************************************************************/ |
245 | /****************************************************************************/ |
246 | /****************************************************************************/ |
247 | |
248 | #define DDB_DEVICE_ANY(_device) \ |
249 | { PCI_DEVICE_SUB(DDVID, _device, DDVID, PCI_ANY_ID) } |
250 | |
251 | static const struct pci_device_id ddb_id_table[] = { |
252 | DDB_DEVICE_ANY(0x0002), |
253 | DDB_DEVICE_ANY(0x0003), |
254 | DDB_DEVICE_ANY(0x0005), |
255 | DDB_DEVICE_ANY(0x0006), |
256 | DDB_DEVICE_ANY(0x0007), |
257 | DDB_DEVICE_ANY(0x0008), |
258 | DDB_DEVICE_ANY(0x0009), |
259 | DDB_DEVICE_ANY(0x0011), |
260 | DDB_DEVICE_ANY(0x0012), |
261 | DDB_DEVICE_ANY(0x0013), |
262 | DDB_DEVICE_ANY(0x0201), |
263 | DDB_DEVICE_ANY(0x0203), |
264 | DDB_DEVICE_ANY(0x0210), |
265 | DDB_DEVICE_ANY(0x0220), |
266 | DDB_DEVICE_ANY(0x0320), |
267 | DDB_DEVICE_ANY(0x0321), |
268 | DDB_DEVICE_ANY(0x0322), |
269 | DDB_DEVICE_ANY(0x0323), |
270 | DDB_DEVICE_ANY(0x0328), |
271 | DDB_DEVICE_ANY(0x0329), |
272 | {0} |
273 | }; |
274 | |
275 | MODULE_DEVICE_TABLE(pci, ddb_id_table); |
276 | |
277 | static struct pci_driver ddb_pci_driver = { |
278 | .name = "ddbridge" , |
279 | .id_table = ddb_id_table, |
280 | .probe = ddb_probe, |
281 | .remove = ddb_remove, |
282 | }; |
283 | |
284 | static __init int module_init_ddbridge(void) |
285 | { |
286 | int stat; |
287 | |
288 | pr_info("Digital Devices PCIE bridge driver " |
289 | DDBRIDGE_VERSION |
290 | ", Copyright (C) 2010-17 Digital Devices GmbH\n" ); |
291 | stat = ddb_init_ddbridge(); |
292 | if (stat < 0) |
293 | return stat; |
294 | stat = pci_register_driver(&ddb_pci_driver); |
295 | if (stat < 0) |
296 | ddb_exit_ddbridge(stage: 0, error: stat); |
297 | |
298 | return stat; |
299 | } |
300 | |
301 | static __exit void module_exit_ddbridge(void) |
302 | { |
303 | pci_unregister_driver(dev: &ddb_pci_driver); |
304 | ddb_exit_ddbridge(stage: 0, error: 0); |
305 | } |
306 | |
307 | module_init(module_init_ddbridge); |
308 | module_exit(module_exit_ddbridge); |
309 | |
310 | MODULE_DESCRIPTION("Digital Devices PCIe Bridge" ); |
311 | MODULE_AUTHOR("Ralph and Marcus Metzler, Metzler Brothers Systementwicklung GbR" ); |
312 | MODULE_LICENSE("GPL v2" ); |
313 | MODULE_VERSION(DDBRIDGE_VERSION); |
314 | |