1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * nosy - Snoop mode driver for TI PCILynx 1394 controllers |
4 | * Copyright (C) 2002-2007 Kristian Høgsberg |
5 | */ |
6 | |
7 | #include <linux/device.h> |
8 | #include <linux/errno.h> |
9 | #include <linux/fs.h> |
10 | #include <linux/init.h> |
11 | #include <linux/interrupt.h> |
12 | #include <linux/io.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/kref.h> |
15 | #include <linux/miscdevice.h> |
16 | #include <linux/module.h> |
17 | #include <linux/mutex.h> |
18 | #include <linux/pci.h> |
19 | #include <linux/poll.h> |
20 | #include <linux/sched.h> /* required for linux/wait.h */ |
21 | #include <linux/slab.h> |
22 | #include <linux/spinlock.h> |
23 | #include <linux/time64.h> |
24 | #include <linux/timex.h> |
25 | #include <linux/uaccess.h> |
26 | #include <linux/wait.h> |
27 | #include <linux/dma-mapping.h> |
28 | #include <linux/atomic.h> |
29 | #include <asm/byteorder.h> |
30 | |
31 | #include "nosy.h" |
32 | #include "nosy-user.h" |
33 | |
34 | #define TCODE_PHY_PACKET 0x10 |
35 | #define PCI_DEVICE_ID_TI_PCILYNX 0x8000 |
36 | |
37 | static char driver_name[] = KBUILD_MODNAME; |
38 | |
39 | /* this is the physical layout of a PCL, its size is 128 bytes */ |
40 | struct pcl { |
41 | __le32 next; |
42 | __le32 async_error_next; |
43 | u32 user_data; |
44 | __le32 pcl_status; |
45 | __le32 remaining_transfer_count; |
46 | __le32 next_data_buffer; |
47 | struct { |
48 | __le32 control; |
49 | __le32 pointer; |
50 | } buffer[13]; |
51 | }; |
52 | |
53 | struct packet { |
54 | unsigned int length; |
55 | char data[]; |
56 | }; |
57 | |
58 | struct packet_buffer { |
59 | char *data; |
60 | size_t capacity; |
61 | long total_packet_count, lost_packet_count; |
62 | atomic_t size; |
63 | struct packet *head, *tail; |
64 | wait_queue_head_t wait; |
65 | }; |
66 | |
67 | struct pcilynx { |
68 | struct pci_dev *pci_device; |
69 | __iomem char *registers; |
70 | |
71 | struct pcl *rcv_start_pcl, *rcv_pcl; |
72 | __le32 *rcv_buffer; |
73 | |
74 | dma_addr_t rcv_start_pcl_bus, rcv_pcl_bus, rcv_buffer_bus; |
75 | |
76 | spinlock_t client_list_lock; |
77 | struct list_head client_list; |
78 | |
79 | struct miscdevice misc; |
80 | struct list_head link; |
81 | struct kref kref; |
82 | }; |
83 | |
84 | static inline struct pcilynx * |
85 | lynx_get(struct pcilynx *lynx) |
86 | { |
87 | kref_get(kref: &lynx->kref); |
88 | |
89 | return lynx; |
90 | } |
91 | |
92 | static void |
93 | lynx_release(struct kref *kref) |
94 | { |
95 | kfree(container_of(kref, struct pcilynx, kref)); |
96 | } |
97 | |
98 | static inline void |
99 | lynx_put(struct pcilynx *lynx) |
100 | { |
101 | kref_put(kref: &lynx->kref, release: lynx_release); |
102 | } |
103 | |
104 | struct client { |
105 | struct pcilynx *lynx; |
106 | u32 tcode_mask; |
107 | struct packet_buffer buffer; |
108 | struct list_head link; |
109 | }; |
110 | |
111 | static DEFINE_MUTEX(card_mutex); |
112 | static LIST_HEAD(card_list); |
113 | |
114 | static int |
115 | packet_buffer_init(struct packet_buffer *buffer, size_t capacity) |
116 | { |
117 | buffer->data = kmalloc(size: capacity, GFP_KERNEL); |
118 | if (buffer->data == NULL) |
119 | return -ENOMEM; |
120 | buffer->head = (struct packet *) buffer->data; |
121 | buffer->tail = (struct packet *) buffer->data; |
122 | buffer->capacity = capacity; |
123 | buffer->lost_packet_count = 0; |
124 | atomic_set(v: &buffer->size, i: 0); |
125 | init_waitqueue_head(&buffer->wait); |
126 | |
127 | return 0; |
128 | } |
129 | |
130 | static void |
131 | packet_buffer_destroy(struct packet_buffer *buffer) |
132 | { |
133 | kfree(objp: buffer->data); |
134 | } |
135 | |
136 | static int |
137 | packet_buffer_get(struct client *client, char __user *data, size_t user_length) |
138 | { |
139 | struct packet_buffer *buffer = &client->buffer; |
140 | size_t length; |
141 | char *end; |
142 | |
143 | if (wait_event_interruptible(buffer->wait, |
144 | atomic_read(&buffer->size) > 0) || |
145 | list_empty(head: &client->lynx->link)) |
146 | return -ERESTARTSYS; |
147 | |
148 | if (atomic_read(v: &buffer->size) == 0) |
149 | return -ENODEV; |
150 | |
151 | /* FIXME: Check length <= user_length. */ |
152 | |
153 | end = buffer->data + buffer->capacity; |
154 | length = buffer->head->length; |
155 | |
156 | if (&buffer->head->data[length] < end) { |
157 | if (copy_to_user(to: data, from: buffer->head->data, n: length)) |
158 | return -EFAULT; |
159 | buffer->head = (struct packet *) &buffer->head->data[length]; |
160 | } else { |
161 | size_t split = end - buffer->head->data; |
162 | |
163 | if (copy_to_user(to: data, from: buffer->head->data, n: split)) |
164 | return -EFAULT; |
165 | if (copy_to_user(to: data + split, from: buffer->data, n: length - split)) |
166 | return -EFAULT; |
167 | buffer->head = (struct packet *) &buffer->data[length - split]; |
168 | } |
169 | |
170 | /* |
171 | * Decrease buffer->size as the last thing, since this is what |
172 | * keeps the interrupt from overwriting the packet we are |
173 | * retrieving from the buffer. |
174 | */ |
175 | atomic_sub(i: sizeof(struct packet) + length, v: &buffer->size); |
176 | |
177 | return length; |
178 | } |
179 | |
180 | static void |
181 | packet_buffer_put(struct packet_buffer *buffer, void *data, size_t length) |
182 | { |
183 | char *end; |
184 | |
185 | buffer->total_packet_count++; |
186 | |
187 | if (buffer->capacity < |
188 | atomic_read(v: &buffer->size) + sizeof(struct packet) + length) { |
189 | buffer->lost_packet_count++; |
190 | return; |
191 | } |
192 | |
193 | end = buffer->data + buffer->capacity; |
194 | buffer->tail->length = length; |
195 | |
196 | if (&buffer->tail->data[length] < end) { |
197 | memcpy(buffer->tail->data, data, length); |
198 | buffer->tail = (struct packet *) &buffer->tail->data[length]; |
199 | } else { |
200 | size_t split = end - buffer->tail->data; |
201 | |
202 | memcpy(buffer->tail->data, data, split); |
203 | memcpy(buffer->data, data + split, length - split); |
204 | buffer->tail = (struct packet *) &buffer->data[length - split]; |
205 | } |
206 | |
207 | /* Finally, adjust buffer size and wake up userspace reader. */ |
208 | |
209 | atomic_add(i: sizeof(struct packet) + length, v: &buffer->size); |
210 | wake_up_interruptible(&buffer->wait); |
211 | } |
212 | |
213 | static inline void |
214 | reg_write(struct pcilynx *lynx, int offset, u32 data) |
215 | { |
216 | writel(val: data, addr: lynx->registers + offset); |
217 | } |
218 | |
219 | static inline u32 |
220 | reg_read(struct pcilynx *lynx, int offset) |
221 | { |
222 | return readl(addr: lynx->registers + offset); |
223 | } |
224 | |
225 | static inline void |
226 | reg_set_bits(struct pcilynx *lynx, int offset, u32 mask) |
227 | { |
228 | reg_write(lynx, offset, data: (reg_read(lynx, offset) | mask)); |
229 | } |
230 | |
231 | /* |
232 | * Maybe the pcl programs could be set up to just append data instead |
233 | * of using a whole packet. |
234 | */ |
235 | static inline void |
236 | run_pcl(struct pcilynx *lynx, dma_addr_t pcl_bus, |
237 | int dmachan) |
238 | { |
239 | reg_write(lynx, DMA0_CURRENT_PCL + dmachan * 0x20, data: pcl_bus); |
240 | reg_write(lynx, DMA0_CHAN_CTRL + dmachan * 0x20, |
241 | DMA_CHAN_CTRL_ENABLE | DMA_CHAN_CTRL_LINK); |
242 | } |
243 | |
244 | static int |
245 | set_phy_reg(struct pcilynx *lynx, int addr, int val) |
246 | { |
247 | if (addr > 15) { |
248 | dev_err(&lynx->pci_device->dev, |
249 | "PHY register address %d out of range\n" , addr); |
250 | return -1; |
251 | } |
252 | if (val > 0xff) { |
253 | dev_err(&lynx->pci_device->dev, |
254 | "PHY register value %d out of range\n" , val); |
255 | return -1; |
256 | } |
257 | reg_write(lynx, LINK_PHY, LINK_PHY_WRITE | |
258 | LINK_PHY_ADDR(addr) | LINK_PHY_WDATA(val)); |
259 | |
260 | return 0; |
261 | } |
262 | |
263 | static int |
264 | nosy_open(struct inode *inode, struct file *file) |
265 | { |
266 | int minor = iminor(inode); |
267 | struct client *client; |
268 | struct pcilynx *tmp, *lynx = NULL; |
269 | |
270 | mutex_lock(&card_mutex); |
271 | list_for_each_entry(tmp, &card_list, link) |
272 | if (tmp->misc.minor == minor) { |
273 | lynx = lynx_get(lynx: tmp); |
274 | break; |
275 | } |
276 | mutex_unlock(lock: &card_mutex); |
277 | if (lynx == NULL) |
278 | return -ENODEV; |
279 | |
280 | client = kmalloc(size: sizeof *client, GFP_KERNEL); |
281 | if (client == NULL) |
282 | goto fail; |
283 | |
284 | client->tcode_mask = ~0; |
285 | client->lynx = lynx; |
286 | INIT_LIST_HEAD(list: &client->link); |
287 | |
288 | if (packet_buffer_init(buffer: &client->buffer, capacity: 128 * 1024) < 0) |
289 | goto fail; |
290 | |
291 | file->private_data = client; |
292 | |
293 | return stream_open(inode, filp: file); |
294 | fail: |
295 | kfree(objp: client); |
296 | lynx_put(lynx); |
297 | |
298 | return -ENOMEM; |
299 | } |
300 | |
301 | static int |
302 | nosy_release(struct inode *inode, struct file *file) |
303 | { |
304 | struct client *client = file->private_data; |
305 | struct pcilynx *lynx = client->lynx; |
306 | |
307 | spin_lock_irq(lock: &lynx->client_list_lock); |
308 | list_del_init(entry: &client->link); |
309 | spin_unlock_irq(lock: &lynx->client_list_lock); |
310 | |
311 | packet_buffer_destroy(buffer: &client->buffer); |
312 | kfree(objp: client); |
313 | lynx_put(lynx); |
314 | |
315 | return 0; |
316 | } |
317 | |
318 | static __poll_t |
319 | nosy_poll(struct file *file, poll_table *pt) |
320 | { |
321 | struct client *client = file->private_data; |
322 | __poll_t ret = 0; |
323 | |
324 | poll_wait(filp: file, wait_address: &client->buffer.wait, p: pt); |
325 | |
326 | if (atomic_read(v: &client->buffer.size) > 0) |
327 | ret = EPOLLIN | EPOLLRDNORM; |
328 | |
329 | if (list_empty(head: &client->lynx->link)) |
330 | ret |= EPOLLHUP; |
331 | |
332 | return ret; |
333 | } |
334 | |
335 | static ssize_t |
336 | nosy_read(struct file *file, char __user *buffer, size_t count, loff_t *offset) |
337 | { |
338 | struct client *client = file->private_data; |
339 | |
340 | return packet_buffer_get(client, data: buffer, user_length: count); |
341 | } |
342 | |
343 | static long |
344 | nosy_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
345 | { |
346 | struct client *client = file->private_data; |
347 | spinlock_t *client_list_lock = &client->lynx->client_list_lock; |
348 | struct nosy_stats stats; |
349 | int ret; |
350 | |
351 | switch (cmd) { |
352 | case NOSY_IOC_GET_STATS: |
353 | spin_lock_irq(lock: client_list_lock); |
354 | stats.total_packet_count = client->buffer.total_packet_count; |
355 | stats.lost_packet_count = client->buffer.lost_packet_count; |
356 | spin_unlock_irq(lock: client_list_lock); |
357 | |
358 | if (copy_to_user(to: (void __user *) arg, from: &stats, n: sizeof stats)) |
359 | return -EFAULT; |
360 | else |
361 | return 0; |
362 | |
363 | case NOSY_IOC_START: |
364 | ret = -EBUSY; |
365 | spin_lock_irq(lock: client_list_lock); |
366 | if (list_empty(head: &client->link)) { |
367 | list_add_tail(new: &client->link, head: &client->lynx->client_list); |
368 | ret = 0; |
369 | } |
370 | spin_unlock_irq(lock: client_list_lock); |
371 | |
372 | return ret; |
373 | |
374 | case NOSY_IOC_STOP: |
375 | spin_lock_irq(lock: client_list_lock); |
376 | list_del_init(entry: &client->link); |
377 | spin_unlock_irq(lock: client_list_lock); |
378 | |
379 | return 0; |
380 | |
381 | case NOSY_IOC_FILTER: |
382 | spin_lock_irq(lock: client_list_lock); |
383 | client->tcode_mask = arg; |
384 | spin_unlock_irq(lock: client_list_lock); |
385 | |
386 | return 0; |
387 | |
388 | default: |
389 | return -EINVAL; |
390 | /* Flush buffer, configure filter. */ |
391 | } |
392 | } |
393 | |
394 | static const struct file_operations nosy_ops = { |
395 | .owner = THIS_MODULE, |
396 | .read = nosy_read, |
397 | .unlocked_ioctl = nosy_ioctl, |
398 | .poll = nosy_poll, |
399 | .open = nosy_open, |
400 | .release = nosy_release, |
401 | }; |
402 | |
403 | #define PHY_PACKET_SIZE 12 /* 1 payload, 1 inverse, 1 ack = 3 quadlets */ |
404 | |
405 | static void |
406 | packet_irq_handler(struct pcilynx *lynx) |
407 | { |
408 | struct client *client; |
409 | u32 tcode_mask, tcode, timestamp; |
410 | size_t length; |
411 | struct timespec64 ts64; |
412 | |
413 | /* FIXME: Also report rcv_speed. */ |
414 | |
415 | length = __le32_to_cpu(lynx->rcv_pcl->pcl_status) & 0x00001fff; |
416 | tcode = __le32_to_cpu(lynx->rcv_buffer[1]) >> 4 & 0xf; |
417 | |
418 | ktime_get_real_ts64(tv: &ts64); |
419 | timestamp = ts64.tv_nsec / NSEC_PER_USEC; |
420 | lynx->rcv_buffer[0] = (__force __le32)timestamp; |
421 | |
422 | if (length == PHY_PACKET_SIZE) |
423 | tcode_mask = 1 << TCODE_PHY_PACKET; |
424 | else |
425 | tcode_mask = 1 << tcode; |
426 | |
427 | spin_lock(lock: &lynx->client_list_lock); |
428 | |
429 | list_for_each_entry(client, &lynx->client_list, link) |
430 | if (client->tcode_mask & tcode_mask) |
431 | packet_buffer_put(buffer: &client->buffer, |
432 | data: lynx->rcv_buffer, length: length + 4); |
433 | |
434 | spin_unlock(lock: &lynx->client_list_lock); |
435 | } |
436 | |
437 | static void |
438 | bus_reset_irq_handler(struct pcilynx *lynx) |
439 | { |
440 | struct client *client; |
441 | struct timespec64 ts64; |
442 | u32 timestamp; |
443 | |
444 | ktime_get_real_ts64(tv: &ts64); |
445 | timestamp = ts64.tv_nsec / NSEC_PER_USEC; |
446 | |
447 | spin_lock(lock: &lynx->client_list_lock); |
448 | |
449 | list_for_each_entry(client, &lynx->client_list, link) |
450 | packet_buffer_put(buffer: &client->buffer, data: ×tamp, length: 4); |
451 | |
452 | spin_unlock(lock: &lynx->client_list_lock); |
453 | } |
454 | |
455 | static irqreturn_t |
456 | irq_handler(int irq, void *device) |
457 | { |
458 | struct pcilynx *lynx = device; |
459 | u32 pci_int_status; |
460 | |
461 | pci_int_status = reg_read(lynx, PCI_INT_STATUS); |
462 | |
463 | if (pci_int_status == ~0) |
464 | /* Card was ejected. */ |
465 | return IRQ_NONE; |
466 | |
467 | if ((pci_int_status & PCI_INT_INT_PEND) == 0) |
468 | /* Not our interrupt, bail out quickly. */ |
469 | return IRQ_NONE; |
470 | |
471 | if ((pci_int_status & PCI_INT_P1394_INT) != 0) { |
472 | u32 link_int_status; |
473 | |
474 | link_int_status = reg_read(lynx, LINK_INT_STATUS); |
475 | reg_write(lynx, LINK_INT_STATUS, data: link_int_status); |
476 | |
477 | if ((link_int_status & LINK_INT_PHY_BUSRESET) > 0) |
478 | bus_reset_irq_handler(lynx); |
479 | } |
480 | |
481 | /* Clear the PCI_INT_STATUS register only after clearing the |
482 | * LINK_INT_STATUS register; otherwise the PCI_INT_P1394 will |
483 | * be set again immediately. */ |
484 | |
485 | reg_write(lynx, PCI_INT_STATUS, data: pci_int_status); |
486 | |
487 | if ((pci_int_status & PCI_INT_DMA0_HLT) > 0) { |
488 | packet_irq_handler(lynx); |
489 | run_pcl(lynx, pcl_bus: lynx->rcv_start_pcl_bus, dmachan: 0); |
490 | } |
491 | |
492 | return IRQ_HANDLED; |
493 | } |
494 | |
495 | static void |
496 | remove_card(struct pci_dev *dev) |
497 | { |
498 | struct pcilynx *lynx = pci_get_drvdata(pdev: dev); |
499 | struct client *client; |
500 | |
501 | mutex_lock(&card_mutex); |
502 | list_del_init(entry: &lynx->link); |
503 | misc_deregister(misc: &lynx->misc); |
504 | mutex_unlock(lock: &card_mutex); |
505 | |
506 | reg_write(lynx, PCI_INT_ENABLE, data: 0); |
507 | free_irq(lynx->pci_device->irq, lynx); |
508 | |
509 | spin_lock_irq(lock: &lynx->client_list_lock); |
510 | list_for_each_entry(client, &lynx->client_list, link) |
511 | wake_up_interruptible(&client->buffer.wait); |
512 | spin_unlock_irq(lock: &lynx->client_list_lock); |
513 | |
514 | dma_free_coherent(dev: &lynx->pci_device->dev, size: sizeof(struct pcl), |
515 | cpu_addr: lynx->rcv_start_pcl, dma_handle: lynx->rcv_start_pcl_bus); |
516 | dma_free_coherent(dev: &lynx->pci_device->dev, size: sizeof(struct pcl), |
517 | cpu_addr: lynx->rcv_pcl, dma_handle: lynx->rcv_pcl_bus); |
518 | dma_free_coherent(dev: &lynx->pci_device->dev, PAGE_SIZE, cpu_addr: lynx->rcv_buffer, |
519 | dma_handle: lynx->rcv_buffer_bus); |
520 | |
521 | iounmap(addr: lynx->registers); |
522 | pci_disable_device(dev); |
523 | lynx_put(lynx); |
524 | } |
525 | |
526 | #define RCV_BUFFER_SIZE (16 * 1024) |
527 | |
528 | static int |
529 | add_card(struct pci_dev *dev, const struct pci_device_id *unused) |
530 | { |
531 | struct pcilynx *lynx; |
532 | u32 p, end; |
533 | int ret, i; |
534 | |
535 | if (dma_set_mask(dev: &dev->dev, DMA_BIT_MASK(32))) { |
536 | dev_err(&dev->dev, |
537 | "DMA address limits not supported for PCILynx hardware\n" ); |
538 | return -ENXIO; |
539 | } |
540 | if (pci_enable_device(dev)) { |
541 | dev_err(&dev->dev, "Failed to enable PCILynx hardware\n" ); |
542 | return -ENXIO; |
543 | } |
544 | pci_set_master(dev); |
545 | |
546 | lynx = kzalloc(size: sizeof *lynx, GFP_KERNEL); |
547 | if (lynx == NULL) { |
548 | dev_err(&dev->dev, "Failed to allocate control structure\n" ); |
549 | ret = -ENOMEM; |
550 | goto fail_disable; |
551 | } |
552 | lynx->pci_device = dev; |
553 | pci_set_drvdata(pdev: dev, data: lynx); |
554 | |
555 | spin_lock_init(&lynx->client_list_lock); |
556 | INIT_LIST_HEAD(list: &lynx->client_list); |
557 | kref_init(kref: &lynx->kref); |
558 | |
559 | lynx->registers = ioremap(pci_resource_start(dev, 0), |
560 | PCILYNX_MAX_REGISTER); |
561 | if (lynx->registers == NULL) { |
562 | dev_err(&dev->dev, "Failed to map registers\n" ); |
563 | ret = -ENOMEM; |
564 | goto fail_deallocate_lynx; |
565 | } |
566 | |
567 | lynx->rcv_start_pcl = dma_alloc_coherent(dev: &lynx->pci_device->dev, |
568 | size: sizeof(struct pcl), |
569 | dma_handle: &lynx->rcv_start_pcl_bus, |
570 | GFP_KERNEL); |
571 | lynx->rcv_pcl = dma_alloc_coherent(dev: &lynx->pci_device->dev, |
572 | size: sizeof(struct pcl), |
573 | dma_handle: &lynx->rcv_pcl_bus, GFP_KERNEL); |
574 | lynx->rcv_buffer = dma_alloc_coherent(dev: &lynx->pci_device->dev, |
575 | RCV_BUFFER_SIZE, |
576 | dma_handle: &lynx->rcv_buffer_bus, GFP_KERNEL); |
577 | if (lynx->rcv_start_pcl == NULL || |
578 | lynx->rcv_pcl == NULL || |
579 | lynx->rcv_buffer == NULL) { |
580 | dev_err(&dev->dev, "Failed to allocate receive buffer\n" ); |
581 | ret = -ENOMEM; |
582 | goto fail_deallocate_buffers; |
583 | } |
584 | lynx->rcv_start_pcl->next = cpu_to_le32(lynx->rcv_pcl_bus); |
585 | lynx->rcv_pcl->next = cpu_to_le32(PCL_NEXT_INVALID); |
586 | lynx->rcv_pcl->async_error_next = cpu_to_le32(PCL_NEXT_INVALID); |
587 | |
588 | lynx->rcv_pcl->buffer[0].control = |
589 | cpu_to_le32(PCL_CMD_RCV | PCL_BIGENDIAN | 2044); |
590 | lynx->rcv_pcl->buffer[0].pointer = |
591 | cpu_to_le32(lynx->rcv_buffer_bus + 4); |
592 | p = lynx->rcv_buffer_bus + 2048; |
593 | end = lynx->rcv_buffer_bus + RCV_BUFFER_SIZE; |
594 | for (i = 1; p < end; i++, p += 2048) { |
595 | lynx->rcv_pcl->buffer[i].control = |
596 | cpu_to_le32(PCL_CMD_RCV | PCL_BIGENDIAN | 2048); |
597 | lynx->rcv_pcl->buffer[i].pointer = cpu_to_le32(p); |
598 | } |
599 | lynx->rcv_pcl->buffer[i - 1].control |= cpu_to_le32(PCL_LAST_BUFF); |
600 | |
601 | reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); |
602 | /* Fix buggy cards with autoboot pin not tied low: */ |
603 | reg_write(lynx, DMA0_CHAN_CTRL, data: 0); |
604 | reg_write(lynx, DMA_GLOBAL_REGISTER, data: 0x00 << 24); |
605 | |
606 | #if 0 |
607 | /* now, looking for PHY register set */ |
608 | if ((get_phy_reg(lynx, 2) & 0xe0) == 0xe0) { |
609 | lynx->phyic.reg_1394a = 1; |
610 | PRINT(KERN_INFO, lynx->id, |
611 | "found 1394a conform PHY (using extended register set)" ); |
612 | lynx->phyic.vendor = get_phy_vendorid(lynx); |
613 | lynx->phyic.product = get_phy_productid(lynx); |
614 | } else { |
615 | lynx->phyic.reg_1394a = 0; |
616 | PRINT(KERN_INFO, lynx->id, "found old 1394 PHY" ); |
617 | } |
618 | #endif |
619 | |
620 | /* Setup the general receive FIFO max size. */ |
621 | reg_write(lynx, FIFO_SIZES, data: 255); |
622 | |
623 | reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_DMA_ALL); |
624 | |
625 | reg_write(lynx, LINK_INT_ENABLE, |
626 | LINK_INT_PHY_TIME_OUT | LINK_INT_PHY_REG_RCVD | |
627 | LINK_INT_PHY_BUSRESET | LINK_INT_IT_STUCK | |
628 | LINK_INT_AT_STUCK | LINK_INT_SNTRJ | |
629 | LINK_INT_TC_ERR | LINK_INT_GRF_OVER_FLOW | |
630 | LINK_INT_ITF_UNDER_FLOW | LINK_INT_ATF_UNDER_FLOW); |
631 | |
632 | /* Disable the L flag in self ID packets. */ |
633 | set_phy_reg(lynx, addr: 4, val: 0); |
634 | |
635 | /* Put this baby into snoop mode */ |
636 | reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_SNOOP_ENABLE); |
637 | |
638 | run_pcl(lynx, pcl_bus: lynx->rcv_start_pcl_bus, dmachan: 0); |
639 | |
640 | if (request_irq(irq: dev->irq, handler: irq_handler, IRQF_SHARED, |
641 | name: driver_name, dev: lynx)) { |
642 | dev_err(&dev->dev, |
643 | "Failed to allocate shared interrupt %d\n" , dev->irq); |
644 | ret = -EIO; |
645 | goto fail_deallocate_buffers; |
646 | } |
647 | |
648 | lynx->misc.parent = &dev->dev; |
649 | lynx->misc.minor = MISC_DYNAMIC_MINOR; |
650 | lynx->misc.name = "nosy" ; |
651 | lynx->misc.fops = &nosy_ops; |
652 | |
653 | mutex_lock(&card_mutex); |
654 | ret = misc_register(misc: &lynx->misc); |
655 | if (ret) { |
656 | dev_err(&dev->dev, "Failed to register misc char device\n" ); |
657 | mutex_unlock(lock: &card_mutex); |
658 | goto fail_free_irq; |
659 | } |
660 | list_add_tail(new: &lynx->link, head: &card_list); |
661 | mutex_unlock(lock: &card_mutex); |
662 | |
663 | dev_info(&dev->dev, |
664 | "Initialized PCILynx IEEE1394 card, irq=%d\n" , dev->irq); |
665 | |
666 | return 0; |
667 | |
668 | fail_free_irq: |
669 | reg_write(lynx, PCI_INT_ENABLE, data: 0); |
670 | free_irq(lynx->pci_device->irq, lynx); |
671 | |
672 | fail_deallocate_buffers: |
673 | if (lynx->rcv_start_pcl) |
674 | dma_free_coherent(dev: &lynx->pci_device->dev, size: sizeof(struct pcl), |
675 | cpu_addr: lynx->rcv_start_pcl, |
676 | dma_handle: lynx->rcv_start_pcl_bus); |
677 | if (lynx->rcv_pcl) |
678 | dma_free_coherent(dev: &lynx->pci_device->dev, size: sizeof(struct pcl), |
679 | cpu_addr: lynx->rcv_pcl, dma_handle: lynx->rcv_pcl_bus); |
680 | if (lynx->rcv_buffer) |
681 | dma_free_coherent(dev: &lynx->pci_device->dev, PAGE_SIZE, |
682 | cpu_addr: lynx->rcv_buffer, dma_handle: lynx->rcv_buffer_bus); |
683 | iounmap(addr: lynx->registers); |
684 | |
685 | fail_deallocate_lynx: |
686 | kfree(objp: lynx); |
687 | |
688 | fail_disable: |
689 | pci_disable_device(dev); |
690 | |
691 | return ret; |
692 | } |
693 | |
694 | static struct pci_device_id pci_table[] = { |
695 | { |
696 | .vendor = PCI_VENDOR_ID_TI, |
697 | .device = PCI_DEVICE_ID_TI_PCILYNX, |
698 | .subvendor = PCI_ANY_ID, |
699 | .subdevice = PCI_ANY_ID, |
700 | }, |
701 | { } /* Terminating entry */ |
702 | }; |
703 | |
704 | MODULE_DEVICE_TABLE(pci, pci_table); |
705 | |
706 | static struct pci_driver lynx_pci_driver = { |
707 | .name = driver_name, |
708 | .id_table = pci_table, |
709 | .probe = add_card, |
710 | .remove = remove_card, |
711 | }; |
712 | |
713 | module_pci_driver(lynx_pci_driver); |
714 | |
715 | MODULE_AUTHOR("Kristian Hoegsberg" ); |
716 | MODULE_DESCRIPTION("Snoop mode driver for TI pcilynx 1394 controllers" ); |
717 | MODULE_LICENSE("GPL" ); |
718 | |