1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55#include <linux/interrupt.h>
56
57#include "../comedidev.h"
58
59#include "comedi_pci.h"
60
61#include "8255.h"
62#include "plx9052.h"
63
64#define PC236_DRIVER_NAME "amplc_pc236"
65
66
67#define PCI_VENDOR_ID_AMPLICON 0x14dc
68#define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
69#define PCI_DEVICE_ID_INVALID 0xffff
70
71
72
73#define PC236_IO_SIZE 4
74#define PC236_LCR_IO_SIZE 128
75
76
77
78
79
80#define PCI236_INTR_DISABLE (PLX9052_INTCSR_LI1ENAB_DISABLED \
81 | PLX9052_INTCSR_LI1POL_HIGH \
82 | PLX9052_INTCSR_LI2POL_HIGH \
83 | PLX9052_INTCSR_PCIENAB_DISABLED \
84 | PLX9052_INTCSR_LI1SEL_EDGE \
85 | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
86
87#define PCI236_INTR_ENABLE (PLX9052_INTCSR_LI1ENAB_ENABLED \
88 | PLX9052_INTCSR_LI1POL_HIGH \
89 | PLX9052_INTCSR_LI2POL_HIGH \
90 | PLX9052_INTCSR_PCIENAB_ENABLED \
91 | PLX9052_INTCSR_LI1SEL_EDGE \
92 | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
93
94
95
96
97
98enum pc236_bustype { isa_bustype, pci_bustype };
99enum pc236_model { pc36at_model, pci236_model, anypci_model };
100
101struct pc236_board {
102 const char *name;
103 const char *fancy_name;
104 unsigned short devid;
105 enum pc236_bustype bustype;
106 enum pc236_model model;
107};
108static const struct pc236_board pc236_boards[] = {
109 {
110 .name = "pc36at",
111 .fancy_name = "PC36AT",
112 .bustype = isa_bustype,
113 .model = pc36at_model,
114 },
115#ifdef CONFIG_COMEDI_PCI
116 {
117 .name = "pci236",
118 .fancy_name = "PCI236",
119 .devid = PCI_DEVICE_ID_AMPLICON_PCI236,
120 .bustype = pci_bustype,
121 .model = pci236_model,
122 },
123#endif
124#ifdef CONFIG_COMEDI_PCI
125 {
126 .name = PC236_DRIVER_NAME,
127 .fancy_name = PC236_DRIVER_NAME,
128 .devid = PCI_DEVICE_ID_INVALID,
129 .bustype = pci_bustype,
130 .model = anypci_model,
131 },
132#endif
133};
134
135#ifdef CONFIG_COMEDI_PCI
136static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
137 {
138 PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236,
139 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
140 0}
141};
142
143MODULE_DEVICE_TABLE(pci, pc236_pci_table);
144#endif
145
146
147
148
149#define thisboard ((const struct pc236_board *)dev->board_ptr)
150
151
152
153
154
155struct pc236_private {
156#ifdef CONFIG_COMEDI_PCI
157
158 struct pci_dev *pci_dev;
159 unsigned long lcr_iobase;
160#endif
161 int enable_irq;
162};
163
164#define devpriv ((struct pc236_private *)dev->private)
165
166
167
168
169
170
171
172static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it);
173static int pc236_detach(struct comedi_device *dev);
174static struct comedi_driver driver_amplc_pc236 = {
175 .driver_name = PC236_DRIVER_NAME,
176 .module = THIS_MODULE,
177 .attach = pc236_attach,
178 .detach = pc236_detach,
179 .board_name = &pc236_boards[0].name,
180 .offset = sizeof(struct pc236_board),
181 .num_names = ARRAY_SIZE(pc236_boards),
182};
183
184#ifdef CONFIG_COMEDI_PCI
185static int __devinit driver_amplc_pc236_pci_probe(struct pci_dev *dev,
186 const struct pci_device_id
187 *ent)
188{
189 return comedi_pci_auto_config(dev, driver_amplc_pc236.driver_name);
190}
191
192static void __devexit driver_amplc_pc236_pci_remove(struct pci_dev *dev)
193{
194 comedi_pci_auto_unconfig(dev);
195}
196
197static struct pci_driver driver_amplc_pc236_pci_driver = {
198 .id_table = pc236_pci_table,
199 .probe = &driver_amplc_pc236_pci_probe,
200 .remove = __devexit_p(&driver_amplc_pc236_pci_remove)
201};
202
203static int __init driver_amplc_pc236_init_module(void)
204{
205 int retval;
206
207 retval = comedi_driver_register(&driver_amplc_pc236);
208 if (retval < 0)
209 return retval;
210
211 driver_amplc_pc236_pci_driver.name =
212 (char *)driver_amplc_pc236.driver_name;
213 return pci_register_driver(&driver_amplc_pc236_pci_driver);
214}
215
216static void __exit driver_amplc_pc236_cleanup_module(void)
217{
218 pci_unregister_driver(&driver_amplc_pc236_pci_driver);
219 comedi_driver_unregister(&driver_amplc_pc236);
220}
221
222module_init(driver_amplc_pc236_init_module);
223module_exit(driver_amplc_pc236_cleanup_module);
224#else
225static int __init driver_amplc_pc236_init_module(void)
226{
227 return comedi_driver_register(&driver_amplc_pc236);
228}
229
230static void __exit driver_amplc_pc236_cleanup_module(void)
231{
232 comedi_driver_unregister(&driver_amplc_pc236);
233}
234
235module_init(driver_amplc_pc236_init_module);
236module_exit(driver_amplc_pc236_cleanup_module);
237#endif
238
239static int pc236_request_region(unsigned minor, unsigned long from,
240 unsigned long extent);
241static void pc236_intr_disable(struct comedi_device *dev);
242static void pc236_intr_enable(struct comedi_device *dev);
243static int pc236_intr_check(struct comedi_device *dev);
244static int pc236_intr_insn(struct comedi_device *dev,
245 struct comedi_subdevice *s, struct comedi_insn *insn,
246 unsigned int *data);
247static int pc236_intr_cmdtest(struct comedi_device *dev,
248 struct comedi_subdevice *s,
249 struct comedi_cmd *cmd);
250static int pc236_intr_cmd(struct comedi_device *dev,
251 struct comedi_subdevice *s);
252static int pc236_intr_cancel(struct comedi_device *dev,
253 struct comedi_subdevice *s);
254static irqreturn_t pc236_interrupt(int irq, void *d);
255
256
257
258
259
260#ifdef CONFIG_COMEDI_PCI
261static int
262pc236_find_pci(struct comedi_device *dev, int bus, int slot,
263 struct pci_dev **pci_dev_p)
264{
265 struct pci_dev *pci_dev = NULL;
266
267 *pci_dev_p = NULL;
268
269
270 for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
271 pci_dev != NULL;
272 pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
273 PCI_ANY_ID, pci_dev)) {
274
275 if (bus || slot) {
276 if (bus != pci_dev->bus->number
277 || slot != PCI_SLOT(pci_dev->devfn))
278 continue;
279 }
280 if (thisboard->model == anypci_model) {
281
282 int i;
283
284 for (i = 0; i < ARRAY_SIZE(pc236_boards); i++) {
285 if (pc236_boards[i].bustype != pci_bustype)
286 continue;
287 if (pci_dev->device == pc236_boards[i].devid) {
288
289 dev->board_ptr = &pc236_boards[i];
290 break;
291 }
292 }
293 if (i == ARRAY_SIZE(pc236_boards))
294 continue;
295 } else {
296
297 if (pci_dev->device != thisboard->devid)
298 continue;
299 }
300
301
302 *pci_dev_p = pci_dev;
303 return 0;
304 }
305
306 if (bus || slot) {
307 printk(KERN_ERR
308 "comedi%d: error! no %s found at pci %02x:%02x!\n",
309 dev->minor, thisboard->name, bus, slot);
310 } else {
311 printk(KERN_ERR "comedi%d: error! no %s found!\n",
312 dev->minor, thisboard->name);
313 }
314 return -EIO;
315}
316#endif
317
318
319
320
321
322
323
324static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
325{
326 struct comedi_subdevice *s;
327 unsigned long iobase = 0;
328 unsigned int irq = 0;
329#ifdef CONFIG_COMEDI_PCI
330 struct pci_dev *pci_dev = NULL;
331 int bus = 0, slot = 0;
332#endif
333 int share_irq = 0;
334 int ret;
335
336 printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
337 PC236_DRIVER_NAME);
338
339
340
341
342 ret = alloc_private(dev, sizeof(struct pc236_private));
343 if (ret < 0) {
344 printk(KERN_ERR "comedi%d: error! out of memory!\n",
345 dev->minor);
346 return ret;
347 }
348
349 switch (thisboard->bustype) {
350 case isa_bustype:
351 iobase = it->options[0];
352 irq = it->options[1];
353 share_irq = 0;
354 break;
355#ifdef CONFIG_COMEDI_PCI
356 case pci_bustype:
357 bus = it->options[0];
358 slot = it->options[1];
359 share_irq = 1;
360
361 ret = pc236_find_pci(dev, bus, slot, &pci_dev);
362 if (ret < 0)
363 return ret;
364 devpriv->pci_dev = pci_dev;
365 break;
366#endif
367 default:
368 printk(KERN_ERR
369 "comedi%d: %s: BUG! cannot determine board type!\n",
370 dev->minor, PC236_DRIVER_NAME);
371 return -EINVAL;
372 break;
373 }
374
375
376
377
378 dev->board_name = thisboard->name;
379
380
381#ifdef CONFIG_COMEDI_PCI
382 if (pci_dev) {
383
384 ret = comedi_pci_enable(pci_dev, PC236_DRIVER_NAME);
385 if (ret < 0) {
386 printk(KERN_ERR
387 "comedi%d: error! cannot enable PCI device and request regions!\n",
388 dev->minor);
389 return ret;
390 }
391 devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
392 iobase = pci_resource_start(pci_dev, 2);
393 irq = pci_dev->irq;
394 } else
395#endif
396 {
397 ret = pc236_request_region(dev->minor, iobase, PC236_IO_SIZE);
398 if (ret < 0)
399 return ret;
400 }
401 dev->iobase = iobase;
402
403
404
405
406
407 ret = alloc_subdevices(dev, 2);
408 if (ret < 0) {
409 printk(KERN_ERR "comedi%d: error! out of memory!\n",
410 dev->minor);
411 return ret;
412 }
413
414 s = dev->subdevices + 0;
415
416 ret = subdev_8255_init(dev, s, NULL, iobase);
417 if (ret < 0) {
418 printk(KERN_ERR "comedi%d: error! out of memory!\n",
419 dev->minor);
420 return ret;
421 }
422 s = dev->subdevices + 1;
423 dev->read_subdev = s;
424 s->type = COMEDI_SUBD_UNUSED;
425 pc236_intr_disable(dev);
426 if (irq) {
427 unsigned long flags = share_irq ? IRQF_SHARED : 0;
428
429 if (request_irq(irq, pc236_interrupt, flags,
430 PC236_DRIVER_NAME, dev) >= 0) {
431 dev->irq = irq;
432 s->type = COMEDI_SUBD_DI;
433 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
434 s->n_chan = 1;
435 s->maxdata = 1;
436 s->range_table = &range_digital;
437 s->insn_bits = pc236_intr_insn;
438 s->do_cmdtest = pc236_intr_cmdtest;
439 s->do_cmd = pc236_intr_cmd;
440 s->cancel = pc236_intr_cancel;
441 }
442 }
443 printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
444 if (thisboard->bustype == isa_bustype) {
445 printk("(base %#lx) ", iobase);
446 } else {
447#ifdef CONFIG_COMEDI_PCI
448 printk("(pci %s) ", pci_name(pci_dev));
449#endif
450 }
451 if (irq)
452 printk("(irq %u%s) ", irq, (dev->irq ? "" : " UNAVAILABLE"));
453 else
454 printk("(no irq) ");
455
456 printk("attached\n");
457
458 return 1;
459}
460
461
462
463
464
465
466
467
468
469static int pc236_detach(struct comedi_device *dev)
470{
471 printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
472 PC236_DRIVER_NAME);
473 if (devpriv)
474 pc236_intr_disable(dev);
475
476 if (dev->irq)
477 free_irq(dev->irq, dev);
478 if (dev->subdevices)
479 subdev_8255_cleanup(dev, dev->subdevices + 0);
480 if (devpriv) {
481#ifdef CONFIG_COMEDI_PCI
482 if (devpriv->pci_dev) {
483 if (dev->iobase)
484 comedi_pci_disable(devpriv->pci_dev);
485 pci_dev_put(devpriv->pci_dev);
486 } else
487#endif
488 {
489 if (dev->iobase)
490 release_region(dev->iobase, PC236_IO_SIZE);
491 }
492 }
493 if (dev->board_name) {
494 printk(KERN_INFO "comedi%d: %s removed\n",
495 dev->minor, dev->board_name);
496 }
497 return 0;
498}
499
500
501
502
503
504static int pc236_request_region(unsigned minor, unsigned long from,
505 unsigned long extent)
506{
507 if (!from || !request_region(from, extent, PC236_DRIVER_NAME)) {
508 printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
509 minor, from, extent);
510 return -EIO;
511 }
512 return 0;
513}
514
515
516
517
518
519
520static void pc236_intr_disable(struct comedi_device *dev)
521{
522 unsigned long flags;
523
524 spin_lock_irqsave(&dev->spinlock, flags);
525 devpriv->enable_irq = 0;
526#ifdef CONFIG_COMEDI_PCI
527 if (devpriv->lcr_iobase)
528 outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
529#endif
530 spin_unlock_irqrestore(&dev->spinlock, flags);
531}
532
533
534
535
536
537
538static void pc236_intr_enable(struct comedi_device *dev)
539{
540 unsigned long flags;
541
542 spin_lock_irqsave(&dev->spinlock, flags);
543 devpriv->enable_irq = 1;
544#ifdef CONFIG_COMEDI_PCI
545 if (devpriv->lcr_iobase)
546 outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
547#endif
548 spin_unlock_irqrestore(&dev->spinlock, flags);
549}
550
551
552
553
554
555
556
557
558static int pc236_intr_check(struct comedi_device *dev)
559{
560 int retval = 0;
561 unsigned long flags;
562
563 spin_lock_irqsave(&dev->spinlock, flags);
564 if (devpriv->enable_irq) {
565 retval = 1;
566#ifdef CONFIG_COMEDI_PCI
567 if (devpriv->lcr_iobase) {
568 if ((inl(devpriv->lcr_iobase + PLX9052_INTCSR)
569 & PLX9052_INTCSR_LI1STAT_MASK)
570 == PLX9052_INTCSR_LI1STAT_INACTIVE) {
571 retval = 0;
572 } else {
573
574 outl(PCI236_INTR_ENABLE,
575 devpriv->lcr_iobase + PLX9052_INTCSR);
576 }
577 }
578#endif
579 }
580 spin_unlock_irqrestore(&dev->spinlock, flags);
581
582 return retval;
583}
584
585
586
587
588
589static int pc236_intr_insn(struct comedi_device *dev,
590 struct comedi_subdevice *s, struct comedi_insn *insn,
591 unsigned int *data)
592{
593 data[1] = 0;
594 return 2;
595}
596
597
598
599
600
601static int pc236_intr_cmdtest(struct comedi_device *dev,
602 struct comedi_subdevice *s,
603 struct comedi_cmd *cmd)
604{
605 int err = 0;
606 int tmp;
607
608
609
610 tmp = cmd->start_src;
611 cmd->start_src &= TRIG_NOW;
612 if (!cmd->start_src || tmp != cmd->start_src)
613 err++;
614
615 tmp = cmd->scan_begin_src;
616 cmd->scan_begin_src &= TRIG_EXT;
617 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
618 err++;
619
620 tmp = cmd->convert_src;
621 cmd->convert_src &= TRIG_FOLLOW;
622 if (!cmd->convert_src || tmp != cmd->convert_src)
623 err++;
624
625 tmp = cmd->scan_end_src;
626 cmd->scan_end_src &= TRIG_COUNT;
627 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
628 err++;
629
630 tmp = cmd->stop_src;
631 cmd->stop_src &= TRIG_NONE;
632 if (!cmd->stop_src || tmp != cmd->stop_src)
633 err++;
634
635 if (err)
636 return 1;
637
638
639
640 if (err)
641 return 2;
642
643
644
645 if (cmd->start_arg != 0) {
646 cmd->start_arg = 0;
647 err++;
648 }
649 if (cmd->scan_begin_arg != 0) {
650 cmd->scan_begin_arg = 0;
651 err++;
652 }
653 if (cmd->convert_arg != 0) {
654 cmd->convert_arg = 0;
655 err++;
656 }
657 if (cmd->scan_end_arg != 1) {
658 cmd->scan_end_arg = 1;
659 err++;
660 }
661 if (cmd->stop_arg != 0) {
662 cmd->stop_arg = 0;
663 err++;
664 }
665
666 if (err)
667 return 3;
668
669
670
671 if (err)
672 return 4;
673
674 return 0;
675}
676
677
678
679
680static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
681{
682 pc236_intr_enable(dev);
683
684 return 0;
685}
686
687
688
689
690static int pc236_intr_cancel(struct comedi_device *dev,
691 struct comedi_subdevice *s)
692{
693 pc236_intr_disable(dev);
694
695 return 0;
696}
697
698
699
700
701
702static irqreturn_t pc236_interrupt(int irq, void *d)
703{
704 struct comedi_device *dev = d;
705 struct comedi_subdevice *s = dev->subdevices + 1;
706 int handled;
707
708 handled = pc236_intr_check(dev);
709 if (dev->attached && handled) {
710 comedi_buf_put(s->async, 0);
711 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
712 comedi_event(dev, s);
713 }
714 return IRQ_RETVAL(handled);
715}
716
717MODULE_AUTHOR("Comedi http://www.comedi.org");
718MODULE_DESCRIPTION("Comedi low-level driver");
719MODULE_LICENSE("GPL");
720