Commit | Line | Data |
---|---|---|
7dc7967f | 1 | // SPDX-License-Identifier: GPL-2.0+ |
bdd4a571 | 2 | #include <linux/kernel.h> |
f7315408 | 3 | #include <linux/idr.h> |
7dc7967f GKH |
4 | #include <linux/init.h> |
5 | #include <linux/module.h> | |
6 | #include <linux/pci.h> | |
7 | #include <linux/types.h> | |
8 | #include <linux/export.h> | |
9 | #include <linux/slab.h> | |
bdd4a571 GKH |
10 | #include <linux/fs.h> |
11 | #include <linux/errno.h> | |
12 | #include <linux/cdev.h> | |
13 | #include <linux/rwsem.h> | |
14 | #include <linux/uaccess.h> | |
7dc7967f GKH |
15 | #include <linux/io.h> |
16 | #include <linux/mfd/core.h> | |
17 | #include <linux/platform_device.h> | |
18 | #include <linux/ioport.h> | |
91b6cb72 | 19 | #include <linux/io-64-nonatomic-lo-hi.h> |
7dc7967f GKH |
20 | #include <linux/interrupt.h> |
21 | #include <linux/workqueue.h> | |
22 | #include <linux/device.h> | |
23 | #include <linux/sched.h> | |
24 | #include <linux/jiffies.h> | |
25 | #include "pcie.h" | |
bdd4a571 | 26 | #include "uapi.h" |
7dc7967f | 27 | |
f7315408 JS |
28 | static DEFINE_IDA(card_num_ida); |
29 | ||
7dc7967f | 30 | /******************************************************* |
d8ac3593 JS |
31 | * SysFS Attributes |
32 | ******************************************************/ | |
33 | ||
80bcd6cc | 34 | static ssize_t ssid_show(struct device *dev, struct device_attribute *attr, |
a986d796 JS |
35 | char *buf) |
36 | { | |
e416dad1 | 37 | struct kp2000_device *pcard = dev_get_drvdata(dev); |
a986d796 | 38 | |
80bcd6cc JS |
39 | return sprintf(buf, "%016llx\n", pcard->ssid); |
40 | } | |
41 | static DEVICE_ATTR_RO(ssid); | |
d8ac3593 | 42 | |
80bcd6cc JS |
43 | static ssize_t ddna_show(struct device *dev, struct device_attribute *attr, |
44 | char *buf) | |
45 | { | |
e416dad1 | 46 | struct kp2000_device *pcard = dev_get_drvdata(dev); |
d8ac3593 | 47 | |
80bcd6cc JS |
48 | return sprintf(buf, "%016llx\n", pcard->ddna); |
49 | } | |
50 | static DEVICE_ATTR_RO(ddna); | |
51 | ||
52 | static ssize_t card_id_show(struct device *dev, struct device_attribute *attr, | |
53 | char *buf) | |
54 | { | |
e416dad1 | 55 | struct kp2000_device *pcard = dev_get_drvdata(dev); |
d8ac3593 | 56 | |
80bcd6cc JS |
57 | return sprintf(buf, "%08x\n", pcard->card_id); |
58 | } | |
59 | static DEVICE_ATTR_RO(card_id); | |
d8ac3593 | 60 | |
80bcd6cc JS |
61 | static ssize_t hw_rev_show(struct device *dev, struct device_attribute *attr, |
62 | char *buf) | |
63 | { | |
e416dad1 | 64 | struct kp2000_device *pcard = dev_get_drvdata(dev); |
d8ac3593 | 65 | |
80bcd6cc | 66 | return sprintf(buf, "%08x\n", pcard->hardware_revision); |
7dc7967f | 67 | } |
80bcd6cc | 68 | static DEVICE_ATTR_RO(hw_rev); |
7dc7967f | 69 | |
80bcd6cc JS |
70 | static ssize_t build_show(struct device *dev, struct device_attribute *attr, |
71 | char *buf) | |
72 | { | |
e416dad1 | 73 | struct kp2000_device *pcard = dev_get_drvdata(dev); |
80bcd6cc | 74 | |
80bcd6cc JS |
75 | return sprintf(buf, "%08x\n", pcard->build_version); |
76 | } | |
77 | static DEVICE_ATTR_RO(build); | |
78 | ||
79 | static ssize_t build_date_show(struct device *dev, | |
80 | struct device_attribute *attr, char *buf) | |
81 | { | |
e416dad1 | 82 | struct kp2000_device *pcard = dev_get_drvdata(dev); |
80bcd6cc | 83 | |
80bcd6cc JS |
84 | return sprintf(buf, "%08x\n", pcard->build_datestamp); |
85 | } | |
86 | static DEVICE_ATTR_RO(build_date); | |
87 | ||
88 | static ssize_t build_time_show(struct device *dev, | |
89 | struct device_attribute *attr, char *buf) | |
90 | { | |
e416dad1 | 91 | struct kp2000_device *pcard = dev_get_drvdata(dev); |
80bcd6cc | 92 | |
80bcd6cc JS |
93 | return sprintf(buf, "%08x\n", pcard->build_timestamp); |
94 | } | |
95 | static DEVICE_ATTR_RO(build_time); | |
96 | ||
97 | static ssize_t cpld_reg_show(struct device *dev, struct device_attribute *attr, | |
98 | char *buf) | |
7dc7967f | 99 | { |
e416dad1 | 100 | struct kp2000_device *pcard = dev_get_drvdata(dev); |
7dc7967f GKH |
101 | u64 val; |
102 | ||
7dc7967f | 103 | val = readq(pcard->sysinfo_regs_base + REG_CPLD_CONFIG); |
80bcd6cc | 104 | return sprintf(buf, "%016llx\n", val); |
7dc7967f | 105 | } |
80bcd6cc | 106 | static DEVICE_ATTR_RO(cpld_reg); |
d8ac3593 JS |
107 | |
108 | static ssize_t cpld_reconfigure(struct device *dev, | |
109 | struct device_attribute *attr, | |
110 | const char *buf, size_t count) | |
7dc7967f | 111 | { |
e416dad1 | 112 | struct kp2000_device *pcard = dev_get_drvdata(dev); |
72db61d7 | 113 | unsigned long wr_val; |
d8ac3593 JS |
114 | int rv; |
115 | ||
72db61d7 | 116 | rv = kstrtoul(buf, 0, &wr_val); |
d8ac3593 JS |
117 | if (rv < 0) |
118 | return rv; | |
119 | if (wr_val > 7) | |
120 | return -EINVAL; | |
7dc7967f | 121 | |
d8ac3593 JS |
122 | wr_val = wr_val << 8; |
123 | wr_val |= 0x1; // Set the "Configure Go" bit | |
124 | writeq(wr_val, pcard->sysinfo_regs_base + REG_CPLD_CONFIG); | |
125 | return count; | |
126 | } | |
80bcd6cc | 127 | static DEVICE_ATTR(cpld_reconfigure, 0220, NULL, cpld_reconfigure); |
7dc7967f | 128 | |
eb1a5c64 JS |
129 | static ssize_t irq_mask_reg_show(struct device *dev, |
130 | struct device_attribute *attr, char *buf) | |
131 | { | |
132 | struct kp2000_device *pcard = dev_get_drvdata(dev); | |
133 | u64 val; | |
134 | ||
135 | val = readq(pcard->sysinfo_regs_base + REG_INTERRUPT_MASK); | |
136 | return sprintf(buf, "%016llx\n", val); | |
137 | } | |
138 | static DEVICE_ATTR_RO(irq_mask_reg); | |
139 | ||
140 | static ssize_t irq_active_reg_show(struct device *dev, | |
141 | struct device_attribute *attr, char *buf) | |
142 | { | |
143 | struct kp2000_device *pcard = dev_get_drvdata(dev); | |
144 | u64 val; | |
145 | ||
146 | val = readq(pcard->sysinfo_regs_base + REG_INTERRUPT_ACTIVE); | |
147 | return sprintf(buf, "%016llx\n", val); | |
148 | } | |
149 | static DEVICE_ATTR_RO(irq_active_reg); | |
150 | ||
151 | static ssize_t pcie_error_count_reg_show(struct device *dev, | |
152 | struct device_attribute *attr, | |
153 | char *buf) | |
154 | { | |
155 | struct kp2000_device *pcard = dev_get_drvdata(dev); | |
156 | u64 val; | |
157 | ||
158 | val = readq(pcard->sysinfo_regs_base + REG_PCIE_ERROR_COUNT); | |
159 | return sprintf(buf, "%016llx\n", val); | |
160 | } | |
161 | static DEVICE_ATTR_RO(pcie_error_count_reg); | |
162 | ||
163 | static ssize_t core_table_offset_show(struct device *dev, | |
164 | struct device_attribute *attr, char *buf) | |
165 | { | |
166 | struct kp2000_device *pcard = dev_get_drvdata(dev); | |
167 | ||
168 | return sprintf(buf, "%08x\n", pcard->core_table_offset); | |
169 | } | |
170 | static DEVICE_ATTR_RO(core_table_offset); | |
171 | ||
172 | static ssize_t core_table_length_show(struct device *dev, | |
173 | struct device_attribute *attr, char *buf) | |
174 | { | |
175 | struct kp2000_device *pcard = dev_get_drvdata(dev); | |
176 | ||
177 | return sprintf(buf, "%08x\n", pcard->core_table_length); | |
178 | } | |
179 | static DEVICE_ATTR_RO(core_table_length); | |
180 | ||
d8ac3593 JS |
181 | static const struct attribute *kp_attr_list[] = { |
182 | &dev_attr_ssid.attr, | |
183 | &dev_attr_ddna.attr, | |
184 | &dev_attr_card_id.attr, | |
185 | &dev_attr_hw_rev.attr, | |
186 | &dev_attr_build.attr, | |
187 | &dev_attr_build_date.attr, | |
188 | &dev_attr_build_time.attr, | |
189 | &dev_attr_cpld_reg.attr, | |
190 | &dev_attr_cpld_reconfigure.attr, | |
eb1a5c64 JS |
191 | &dev_attr_irq_mask_reg.attr, |
192 | &dev_attr_irq_active_reg.attr, | |
193 | &dev_attr_pcie_error_count_reg.attr, | |
194 | &dev_attr_core_table_offset.attr, | |
195 | &dev_attr_core_table_length.attr, | |
d8ac3593 | 196 | NULL, |
7dc7967f GKH |
197 | }; |
198 | ||
7dc7967f | 199 | /******************************************************* |
d8ac3593 JS |
200 | * Functions |
201 | ******************************************************/ | |
7dc7967f GKH |
202 | |
203 | static void wait_and_read_ssid(struct kp2000_device *pcard) | |
204 | { | |
d8ac3593 JS |
205 | u64 read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_SSID); |
206 | unsigned long timeout; | |
207 | ||
f08ab0ab | 208 | if (read_val & 0x8000000000000000UL) { |
d8ac3593 JS |
209 | pcard->ssid = read_val; |
210 | return; | |
211 | } | |
212 | ||
213 | timeout = jiffies + (HZ * 2); | |
214 | do { | |
215 | read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_SSID); | |
f08ab0ab | 216 | if (read_val & 0x8000000000000000UL) { |
d8ac3593 JS |
217 | pcard->ssid = read_val; |
218 | return; | |
219 | } | |
220 | cpu_relax(); | |
221 | //schedule(); | |
222 | } while (time_before(jiffies, timeout)); | |
223 | ||
224 | dev_notice(&pcard->pdev->dev, "SSID didn't show up!\n"); | |
225 | ||
d8ac3593 JS |
226 | // Timed out waiting for the SSID to show up, stick all zeros in the |
227 | // value | |
228 | pcard->ssid = 0; | |
7dc7967f GKH |
229 | } |
230 | ||
231 | static int read_system_regs(struct kp2000_device *pcard) | |
232 | { | |
d8ac3593 JS |
233 | u64 read_val; |
234 | ||
235 | read_val = readq(pcard->sysinfo_regs_base + REG_MAGIC_NUMBER); | |
236 | if (read_val != KP2000_MAGIC_VALUE) { | |
237 | dev_err(&pcard->pdev->dev, | |
89a237aa | 238 | "Invalid magic! Got: 0x%016llx Want: 0x%016llx\n", |
d8ac3593 JS |
239 | read_val, KP2000_MAGIC_VALUE); |
240 | return -EILSEQ; | |
241 | } | |
242 | ||
243 | read_val = readq(pcard->sysinfo_regs_base + REG_CARD_ID_AND_BUILD); | |
f08ab0ab HJ |
244 | pcard->card_id = (read_val & 0xFFFFFFFF00000000UL) >> 32; |
245 | pcard->build_version = (read_val & 0x00000000FFFFFFFFUL) >> 0; | |
d8ac3593 JS |
246 | |
247 | read_val = readq(pcard->sysinfo_regs_base + REG_DATE_AND_TIME_STAMPS); | |
f08ab0ab HJ |
248 | pcard->build_datestamp = (read_val & 0xFFFFFFFF00000000UL) >> 32; |
249 | pcard->build_timestamp = (read_val & 0x00000000FFFFFFFFUL) >> 0; | |
d8ac3593 JS |
250 | |
251 | read_val = readq(pcard->sysinfo_regs_base + REG_CORE_TABLE_OFFSET); | |
f08ab0ab HJ |
252 | pcard->core_table_length = (read_val & 0xFFFFFFFF00000000UL) >> 32; |
253 | pcard->core_table_offset = (read_val & 0x00000000FFFFFFFFUL) >> 0; | |
d8ac3593 JS |
254 | |
255 | wait_and_read_ssid(pcard); | |
256 | ||
257 | read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_HW_ID); | |
258 | pcard->core_table_rev = (read_val & 0x0000000000000F00) >> 8; | |
259 | pcard->hardware_revision = (read_val & 0x000000000000001F); | |
260 | ||
261 | read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_DDNA); | |
262 | pcard->ddna = read_val; | |
263 | ||
264 | dev_info(&pcard->pdev->dev, | |
265 | "system_regs: %08x %08x %08x %08x %02x %d %d %016llx %016llx\n", | |
266 | pcard->card_id, | |
267 | pcard->build_version, | |
268 | pcard->build_datestamp, | |
269 | pcard->build_timestamp, | |
270 | pcard->hardware_revision, | |
271 | pcard->core_table_rev, | |
272 | pcard->core_table_length, | |
273 | pcard->ssid, | |
274 | pcard->ddna); | |
275 | ||
276 | if (pcard->core_table_rev > 1) { | |
277 | dev_err(&pcard->pdev->dev, | |
278 | "core table entry revision is higher than we can deal with, cannot continue with this card!\n"); | |
279 | return 1; | |
280 | } | |
281 | ||
282 | return 0; | |
7dc7967f GKH |
283 | } |
284 | ||
6bcd56b7 | 285 | static irqreturn_t kp2000_irq_handler(int irq, void *dev_id) |
7dc7967f | 286 | { |
6bcd56b7 GKH |
287 | struct kp2000_device *pcard = dev_id; |
288 | ||
289 | writel(KPC_DMA_CARD_IRQ_ENABLE | | |
290 | KPC_DMA_CARD_USER_INTERRUPT_MODE | | |
291 | KPC_DMA_CARD_USER_INTERRUPT_ACTIVE, | |
292 | pcard->dma_common_regs); | |
293 | return IRQ_HANDLED; | |
7dc7967f GKH |
294 | } |
295 | ||
92642f38 GKH |
296 | static int kp2000_pcie_probe(struct pci_dev *pdev, |
297 | const struct pci_device_id *id) | |
7dc7967f | 298 | { |
d8ac3593 JS |
299 | int err = 0; |
300 | struct kp2000_device *pcard; | |
d8ac3593 JS |
301 | int rv; |
302 | unsigned long reg_bar_phys_addr; | |
303 | unsigned long reg_bar_phys_len; | |
304 | unsigned long dma_bar_phys_addr; | |
305 | unsigned long dma_bar_phys_len; | |
306 | u16 regval; | |
307 | ||
a1ceab8b | 308 | pcard = kzalloc(sizeof(*pcard), GFP_KERNEL); |
5298be48 | 309 | if (!pcard) |
d8ac3593 | 310 | return -ENOMEM; |
d8ac3593 JS |
311 | dev_dbg(&pdev->dev, "probe: allocated struct kp2000_device @ %p\n", |
312 | pcard); | |
313 | ||
f7315408 JS |
314 | err = ida_simple_get(&card_num_ida, 1, INT_MAX, GFP_KERNEL); |
315 | if (err < 0) { | |
316 | dev_err(&pdev->dev, "probe: failed to get card number (%d)\n", | |
317 | err); | |
fd5a82f4 | 318 | goto err_free_pcard; |
f7315408 JS |
319 | } |
320 | pcard->card_num = err; | |
321 | scnprintf(pcard->name, 16, "kpcard%u", pcard->card_num); | |
d8ac3593 JS |
322 | |
323 | mutex_init(&pcard->sem); | |
276accf2 | 324 | mutex_lock(&pcard->sem); |
2f9dcc46 | 325 | |
d8ac3593 JS |
326 | pcard->pdev = pdev; |
327 | pci_set_drvdata(pdev, pcard); | |
328 | ||
d8ac3593 JS |
329 | err = pci_enable_device(pcard->pdev); |
330 | if (err) { | |
331 | dev_err(&pcard->pdev->dev, | |
332 | "probe: failed to enable PCIE2000 PCIe device (%d)\n", | |
333 | err); | |
fd5a82f4 | 334 | goto err_remove_ida; |
d8ac3593 JS |
335 | } |
336 | ||
eafae15f | 337 | /* Setup the Register BAR */ |
d8ac3593 JS |
338 | reg_bar_phys_addr = pci_resource_start(pcard->pdev, REG_BAR); |
339 | reg_bar_phys_len = pci_resource_len(pcard->pdev, REG_BAR); | |
340 | ||
4bdc0d67 | 341 | pcard->regs_bar_base = ioremap(reg_bar_phys_addr, PAGE_SIZE); |
636928fc | 342 | if (!pcard->regs_bar_base) { |
d8ac3593 JS |
343 | dev_err(&pcard->pdev->dev, |
344 | "probe: REG_BAR could not remap memory to virtual space\n"); | |
345 | err = -ENODEV; | |
fd5a82f4 | 346 | goto err_disable_device; |
d8ac3593 JS |
347 | } |
348 | dev_dbg(&pcard->pdev->dev, | |
349 | "probe: REG_BAR virt hardware address start [%p]\n", | |
350 | pcard->regs_bar_base); | |
351 | ||
352 | err = pci_request_region(pcard->pdev, REG_BAR, KP_DRIVER_NAME_KP2000); | |
353 | if (err) { | |
d8ac3593 JS |
354 | dev_err(&pcard->pdev->dev, |
355 | "probe: failed to acquire PCI region (%d)\n", | |
356 | err); | |
357 | err = -ENODEV; | |
a50185cb | 358 | goto err_unmap_regs; |
d8ac3593 JS |
359 | } |
360 | ||
361 | pcard->regs_base_resource.start = reg_bar_phys_addr; | |
362 | pcard->regs_base_resource.end = reg_bar_phys_addr + | |
363 | reg_bar_phys_len - 1; | |
364 | pcard->regs_base_resource.flags = IORESOURCE_MEM; | |
365 | ||
eafae15f | 366 | /* Setup the DMA BAR */ |
d8ac3593 JS |
367 | dma_bar_phys_addr = pci_resource_start(pcard->pdev, DMA_BAR); |
368 | dma_bar_phys_len = pci_resource_len(pcard->pdev, DMA_BAR); | |
369 | ||
4bdc0d67 | 370 | pcard->dma_bar_base = ioremap(dma_bar_phys_addr, |
d8ac3593 | 371 | dma_bar_phys_len); |
636928fc | 372 | if (!pcard->dma_bar_base) { |
d8ac3593 JS |
373 | dev_err(&pcard->pdev->dev, |
374 | "probe: DMA_BAR could not remap memory to virtual space\n"); | |
375 | err = -ENODEV; | |
a50185cb | 376 | goto err_release_regs; |
d8ac3593 JS |
377 | } |
378 | dev_dbg(&pcard->pdev->dev, | |
379 | "probe: DMA_BAR virt hardware address start [%p]\n", | |
380 | pcard->dma_bar_base); | |
381 | ||
382 | pcard->dma_common_regs = pcard->dma_bar_base + KPC_DMA_COMMON_OFFSET; | |
383 | ||
384 | err = pci_request_region(pcard->pdev, DMA_BAR, "kp2000_pcie"); | |
385 | if (err) { | |
d8ac3593 JS |
386 | dev_err(&pcard->pdev->dev, |
387 | "probe: failed to acquire PCI region (%d)\n", err); | |
388 | err = -ENODEV; | |
a50185cb | 389 | goto err_unmap_dma; |
d8ac3593 JS |
390 | } |
391 | ||
392 | pcard->dma_base_resource.start = dma_bar_phys_addr; | |
393 | pcard->dma_base_resource.end = dma_bar_phys_addr + | |
394 | dma_bar_phys_len - 1; | |
395 | pcard->dma_base_resource.flags = IORESOURCE_MEM; | |
396 | ||
eafae15f | 397 | /* Read System Regs */ |
d8ac3593 JS |
398 | pcard->sysinfo_regs_base = pcard->regs_bar_base; |
399 | err = read_system_regs(pcard); | |
400 | if (err) | |
a50185cb | 401 | goto err_release_dma; |
d8ac3593 JS |
402 | |
403 | // Disable all "user" interrupts because they're not used yet. | |
f08ab0ab | 404 | writeq(0xFFFFFFFFFFFFFFFFUL, |
d8ac3593 JS |
405 | pcard->sysinfo_regs_base + REG_INTERRUPT_MASK); |
406 | ||
d8ac3593 JS |
407 | // let the card master PCIe |
408 | pci_set_master(pcard->pdev); | |
eafae15f | 409 | |
d8ac3593 JS |
410 | // enable IO and mem if not already done |
411 | pci_read_config_word(pcard->pdev, PCI_COMMAND, ®val); | |
412 | regval |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY); | |
413 | pci_write_config_word(pcard->pdev, PCI_COMMAND, regval); | |
414 | ||
415 | // Clear relaxed ordering bit | |
416 | pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, | |
417 | PCI_EXP_DEVCTL_RELAX_EN, 0); | |
418 | ||
419 | // Set Max_Payload_Size and Max_Read_Request_Size | |
420 | regval = (0x0) << 5; // Max_Payload_Size = 128 B | |
421 | pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, | |
422 | PCI_EXP_DEVCTL_PAYLOAD, regval); | |
423 | regval = (0x0) << 12; // Max_Read_Request_Size = 128 B | |
424 | pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, | |
425 | PCI_EXP_DEVCTL_READRQ, regval); | |
426 | ||
427 | // Enable error reporting for: Correctable Errors, Non-Fatal Errors, | |
428 | // Fatal Errors, Unsupported Requests | |
429 | pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, 0, | |
430 | PCI_EXP_DEVCTL_CERE | | |
431 | PCI_EXP_DEVCTL_NFERE | | |
432 | PCI_EXP_DEVCTL_FERE | | |
433 | PCI_EXP_DEVCTL_URRE); | |
434 | ||
435 | err = dma_set_mask(PCARD_TO_DEV(pcard), DMA_BIT_MASK(64)); | |
436 | if (err) { | |
437 | dev_err(&pcard->pdev->dev, | |
438 | "CANNOT use DMA mask %0llx\n", DMA_BIT_MASK(64)); | |
a50185cb | 439 | goto err_release_dma; |
d8ac3593 JS |
440 | } |
441 | dev_dbg(&pcard->pdev->dev, | |
442 | "Using DMA mask %0llx\n", dma_get_mask(PCARD_TO_DEV(pcard))); | |
443 | ||
d8ac3593 JS |
444 | err = pci_enable_msi(pcard->pdev); |
445 | if (err < 0) | |
a50185cb | 446 | goto err_release_dma; |
d8ac3593 JS |
447 | |
448 | rv = request_irq(pcard->pdev->irq, kp2000_irq_handler, IRQF_SHARED, | |
449 | pcard->name, pcard); | |
450 | if (rv) { | |
451 | dev_err(&pcard->pdev->dev, | |
745cde4e | 452 | "%s: failed to request_irq: %d\n", __func__, rv); |
fd5a82f4 | 453 | goto err_disable_msi; |
d8ac3593 JS |
454 | } |
455 | ||
1c10f069 | 456 | err = sysfs_create_files(&pdev->dev.kobj, kp_attr_list); |
d8ac3593 JS |
457 | if (err) { |
458 | dev_err(&pdev->dev, "Failed to add sysfs files: %d\n", err); | |
fd5a82f4 | 459 | goto err_free_irq; |
d8ac3593 JS |
460 | } |
461 | ||
d8ac3593 JS |
462 | err = kp2000_probe_cores(pcard); |
463 | if (err) | |
fd5a82f4 | 464 | goto err_remove_sysfs; |
d8ac3593 | 465 | |
eafae15f | 466 | /* Enable IRQs in HW */ |
6bcd56b7 GKH |
467 | writel(KPC_DMA_CARD_IRQ_ENABLE | KPC_DMA_CARD_USER_INTERRUPT_MODE, |
468 | pcard->dma_common_regs); | |
2f9dcc46 | 469 | |
276accf2 | 470 | mutex_unlock(&pcard->sem); |
d8ac3593 JS |
471 | return 0; |
472 | ||
fd5a82f4 | 473 | err_remove_sysfs: |
1c10f069 | 474 | sysfs_remove_files(&pdev->dev.kobj, kp_attr_list); |
fd5a82f4 | 475 | err_free_irq: |
d8ac3593 | 476 | free_irq(pcard->pdev->irq, pcard); |
fd5a82f4 | 477 | err_disable_msi: |
d8ac3593 | 478 | pci_disable_msi(pcard->pdev); |
a50185cb SS |
479 | err_release_dma: |
480 | pci_release_region(pdev, DMA_BAR); | |
fd5a82f4 | 481 | err_unmap_dma: |
d8ac3593 | 482 | iounmap(pcard->dma_bar_base); |
a50185cb SS |
483 | err_release_regs: |
484 | pci_release_region(pdev, REG_BAR); | |
fd5a82f4 | 485 | err_unmap_regs: |
d8ac3593 | 486 | iounmap(pcard->regs_bar_base); |
fd5a82f4 | 487 | err_disable_device: |
d8ac3593 | 488 | pci_disable_device(pcard->pdev); |
fd5a82f4 | 489 | err_remove_ida: |
276accf2 | 490 | mutex_unlock(&pcard->sem); |
f7315408 | 491 | ida_simple_remove(&card_num_ida, pcard->card_num); |
fd5a82f4 | 492 | err_free_pcard: |
d8ac3593 JS |
493 | kfree(pcard); |
494 | return err; | |
7dc7967f GKH |
495 | } |
496 | ||
92642f38 | 497 | static void kp2000_pcie_remove(struct pci_dev *pdev) |
7dc7967f | 498 | { |
d8ac3593 | 499 | struct kp2000_device *pcard = pci_get_drvdata(pdev); |
7dc7967f | 500 | |
636928fc | 501 | if (!pcard) |
d8ac3593 | 502 | return; |
2f9dcc46 | 503 | |
276accf2 | 504 | mutex_lock(&pcard->sem); |
d8ac3593 JS |
505 | kp2000_remove_cores(pcard); |
506 | mfd_remove_devices(PCARD_TO_DEV(pcard)); | |
1c10f069 | 507 | sysfs_remove_files(&pdev->dev.kobj, kp_attr_list); |
d8ac3593 JS |
508 | free_irq(pcard->pdev->irq, pcard); |
509 | pci_disable_msi(pcard->pdev); | |
636928fc | 510 | if (pcard->dma_bar_base) { |
d8ac3593 JS |
511 | iounmap(pcard->dma_bar_base); |
512 | pci_release_region(pdev, DMA_BAR); | |
513 | pcard->dma_bar_base = NULL; | |
514 | } | |
636928fc | 515 | if (pcard->regs_bar_base) { |
d8ac3593 JS |
516 | iounmap(pcard->regs_bar_base); |
517 | pci_release_region(pdev, REG_BAR); | |
518 | pcard->regs_bar_base = NULL; | |
519 | } | |
520 | pci_disable_device(pcard->pdev); | |
521 | pci_set_drvdata(pdev, NULL); | |
276accf2 | 522 | mutex_unlock(&pcard->sem); |
f7315408 | 523 | ida_simple_remove(&card_num_ida, pcard->card_num); |
d8ac3593 | 524 | kfree(pcard); |
7dc7967f | 525 | } |
92642f38 GKH |
526 | |
527 | struct class *kpc_uio_class; | |
528 | ATTRIBUTE_GROUPS(kpc_uio_class); | |
529 | ||
530 | static const struct pci_device_id kp2000_pci_device_ids[] = { | |
531 | { PCI_DEVICE(PCI_VENDOR_ID_DAKTRONICS, PCI_DEVICE_ID_DAKTRONICS) }, | |
532 | { PCI_DEVICE(PCI_VENDOR_ID_DAKTRONICS, PCI_DEVICE_ID_DAKTRONICS_KADOKA_P2KR0) }, | |
533 | { 0, } | |
534 | }; | |
535 | MODULE_DEVICE_TABLE(pci, kp2000_pci_device_ids); | |
536 | ||
537 | static struct pci_driver kp2000_driver_inst = { | |
538 | .name = "kp2000_pcie", | |
539 | .id_table = kp2000_pci_device_ids, | |
540 | .probe = kp2000_pcie_probe, | |
541 | .remove = kp2000_pcie_remove, | |
542 | }; | |
543 | ||
8dd3355a | 544 | static int __init kp2000_pcie_init(void) |
92642f38 GKH |
545 | { |
546 | kpc_uio_class = class_create(THIS_MODULE, "kpc_uio"); | |
547 | if (IS_ERR(kpc_uio_class)) | |
548 | return PTR_ERR(kpc_uio_class); | |
549 | ||
550 | kpc_uio_class->dev_groups = kpc_uio_class_groups; | |
551 | return pci_register_driver(&kp2000_driver_inst); | |
552 | } | |
553 | module_init(kp2000_pcie_init); | |
554 | ||
8dd3355a | 555 | static void __exit kp2000_pcie_exit(void) |
92642f38 GKH |
556 | { |
557 | pci_unregister_driver(&kp2000_driver_inst); | |
558 | class_destroy(kpc_uio_class); | |
f7315408 | 559 | ida_destroy(&card_num_ida); |
92642f38 GKH |
560 | } |
561 | module_exit(kp2000_pcie_exit); | |
562 | ||
563 | MODULE_LICENSE("GPL"); | |
564 | MODULE_AUTHOR("Lee.Brooke@Daktronics.com, Matt.Sickler@Daktronics.com"); | |
565 | MODULE_SOFTDEP("pre: uio post: kpc_nwl_dma kpc_i2c kpc_spi"); |