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