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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79#include <linux/interrupt.h>
80#include <linux/slab.h>
81#include "../comedidev.h"
82#include "pcm_common.h"
83#include <linux/pci.h>
84
85
86#define CHANS_PER_PORT 8
87#define PORTS_PER_ASIC 6
88#define INTR_PORTS_PER_ASIC 3
89#define MAX_CHANS_PER_SUBDEV 24
90#define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
91#define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
92#define INTR_CHANS_PER_ASIC 24
93#define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
94#define MAX_DIO_CHANS (PORTS_PER_ASIC*1*CHANS_PER_PORT)
95#define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC)
96#define SDEV_NO ((int)(s - dev->subdevices))
97#define CALC_N_DIO_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) )
98
99#define ASIC_IOSIZE (0x0B)
100#define PCMMIO48_IOSIZE ASIC_IOSIZE
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115#define REG_PORT0 0x0
116#define REG_PORT1 0x1
117#define REG_PORT2 0x2
118#define REG_PORT3 0x3
119#define REG_PORT4 0x4
120#define REG_PORT5 0x5
121#define REG_INT_PENDING 0x6
122#define REG_PAGELOCK 0x7
123
124
125
126
127#define REG_POL0 0x8
128#define REG_POL1 0x9
129#define REG_POL2 0xA
130#define REG_ENAB0 0x8
131#define REG_ENAB1 0x9
132#define REG_ENAB2 0xA
133#define REG_INT_ID0 0x8
134#define REG_INT_ID1 0x9
135#define REG_INT_ID2 0xA
136
137#define NUM_PAGED_REGS 3
138#define NUM_PAGES 4
139#define FIRST_PAGED_REG 0x8
140#define REG_PAGE_BITOFFSET 6
141#define REG_LOCK_BITOFFSET 0
142#define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
143#define REG_LOCK_MASK (~(REG_PAGE_MASK))
144#define PAGE_POL 1
145#define PAGE_ENAB 2
146#define PAGE_INT_ID 3
147
148static int ai_rinsn(struct comedi_device *, struct comedi_subdevice *,
149 struct comedi_insn *, unsigned int *);
150static int ao_rinsn(struct comedi_device *, struct comedi_subdevice *,
151 struct comedi_insn *, unsigned int *);
152static int ao_winsn(struct comedi_device *, struct comedi_subdevice *,
153 struct comedi_insn *, unsigned int *);
154
155
156
157
158
159
160struct pcmmio_board {
161 const char *name;
162 const int dio_num_asics;
163 const int dio_num_ports;
164 const int total_iosize;
165 const int ai_bits;
166 const int ao_bits;
167 const int n_ai_chans;
168 const int n_ao_chans;
169 const struct comedi_lrange *ai_range_table, *ao_range_table;
170 int (*ai_rinsn) (struct comedi_device *dev,
171 struct comedi_subdevice *s,
172 struct comedi_insn *insn,
173 unsigned int *data);
174 int (*ao_rinsn) (struct comedi_device *dev,
175 struct comedi_subdevice *s,
176 struct comedi_insn *insn,
177 unsigned int *data);
178 int (*ao_winsn) (struct comedi_device *dev,
179 struct comedi_subdevice *s,
180 struct comedi_insn *insn,
181 unsigned int *data);
182};
183
184static const struct comedi_lrange ranges_ai = {
185 4, {RANGE(-5., 5.), RANGE(-10., 10.), RANGE(0., 5.), RANGE(0., 10.)}
186};
187
188static const struct comedi_lrange ranges_ao = {
189 6, {RANGE(0., 5.), RANGE(0., 10.), RANGE(-5., 5.), RANGE(-10., 10.),
190 RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
191};
192
193static const struct pcmmio_board pcmmio_boards[] = {
194 {
195 .name = "pcmmio",
196 .dio_num_asics = 1,
197 .dio_num_ports = 6,
198 .total_iosize = 32,
199 .ai_bits = 16,
200 .ao_bits = 16,
201 .n_ai_chans = 16,
202 .n_ao_chans = 8,
203 .ai_range_table = &ranges_ai,
204 .ao_range_table = &ranges_ao,
205 .ai_rinsn = ai_rinsn,
206 .ao_rinsn = ao_rinsn,
207 .ao_winsn = ao_winsn},
208};
209
210
211
212
213#define thisboard ((const struct pcmmio_board *)dev->board_ptr)
214
215
216struct pcmmio_subdev_private {
217
218 union {
219
220
221 unsigned long iobases[PORTS_PER_SUBDEV];
222
223
224 unsigned long iobase;
225 };
226 union {
227 struct {
228
229
230 struct {
231
232
233
234
235 int asic;
236
237
238
239
240 int first_chan;
241
242
243
244
245 int num_asic_chans;
246
247
248
249
250 int asic_chan;
251
252
253
254
255 int enabled_mask;
256 int active;
257 int stop_count;
258 int continuous;
259 spinlock_t spinlock;
260 } intr;
261 } dio;
262 struct {
263
264 unsigned int shadow_samples[8];
265 } ao;
266 };
267};
268
269
270
271
272
273
274struct pcmmio_private {
275
276 struct {
277 unsigned char pagelock;
278
279 unsigned char pol[NUM_PAGED_REGS];
280
281 unsigned char enab[NUM_PAGED_REGS];
282 int num;
283 unsigned long iobase;
284 unsigned int irq;
285 spinlock_t spinlock;
286 } asics[MAX_ASICS];
287 struct pcmmio_subdev_private *sprivs;
288};
289
290
291
292
293
294#define devpriv ((struct pcmmio_private *)dev->private)
295#define subpriv ((struct pcmmio_subdev_private *)s->private)
296
297
298
299
300
301
302static int pcmmio_attach(struct comedi_device *dev,
303 struct comedi_devconfig *it);
304static int pcmmio_detach(struct comedi_device *dev);
305
306static struct comedi_driver driver = {
307 .driver_name = "pcmmio",
308 .module = THIS_MODULE,
309 .attach = pcmmio_attach,
310 .detach = pcmmio_detach,
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329 .board_name = &pcmmio_boards[0].name,
330 .offset = sizeof(struct pcmmio_board),
331 .num_names = ARRAY_SIZE(pcmmio_boards),
332};
333
334static int pcmmio_dio_insn_bits(struct comedi_device *dev,
335 struct comedi_subdevice *s,
336 struct comedi_insn *insn, unsigned int *data);
337static int pcmmio_dio_insn_config(struct comedi_device *dev,
338 struct comedi_subdevice *s,
339 struct comedi_insn *insn, unsigned int *data);
340
341static irqreturn_t interrupt_pcmmio(int irq, void *d);
342static void pcmmio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
343static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
344static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
345static int pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
346 struct comedi_cmd *cmd);
347
348
349
350static void init_asics(struct comedi_device *dev);
351static void switch_page(struct comedi_device *dev, int asic, int page);
352#ifdef notused
353static void lock_port(struct comedi_device *dev, int asic, int port);
354static void unlock_port(struct comedi_device *dev, int asic, int port);
355#endif
356
357
358
359
360
361
362
363static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
364{
365 struct comedi_subdevice *s;
366 int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
367 thisasic_chanct = 0;
368 unsigned long iobase;
369 unsigned int irq[MAX_ASICS];
370
371 iobase = it->options[0];
372 irq[0] = it->options[1];
373
374 printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
375 iobase);
376
377 dev->iobase = iobase;
378
379 if (!iobase || !request_region(iobase,
380 thisboard->total_iosize,
381 driver.driver_name)) {
382 printk("I/O port conflict\n");
383 return -EIO;
384 }
385
386
387
388
389
390 dev->board_name = thisboard->name;
391
392
393
394
395
396 if (alloc_private(dev, sizeof(struct pcmmio_private)) < 0) {
397 printk("cannot allocate private data structure\n");
398 return -ENOMEM;
399 }
400
401 for (asic = 0; asic < MAX_ASICS; ++asic) {
402 devpriv->asics[asic].num = asic;
403 devpriv->asics[asic].iobase =
404 dev->iobase + 16 + asic * ASIC_IOSIZE;
405
406
407
408
409 devpriv->asics[asic].irq = 0;
410 spin_lock_init(&devpriv->asics[asic].spinlock);
411 }
412
413 chans_left = CHANS_PER_ASIC * thisboard->dio_num_asics;
414 n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
415 n_subdevs = n_dio_subdevs + 2;
416 devpriv->sprivs =
417 kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private),
418 GFP_KERNEL);
419 if (!devpriv->sprivs) {
420 printk("cannot allocate subdevice private data structures\n");
421 return -ENOMEM;
422 }
423
424
425
426
427
428
429 if (alloc_subdevices(dev, n_subdevs) < 0) {
430 printk("cannot allocate subdevice data structures\n");
431 return -ENOMEM;
432 }
433
434
435 sdev_no = 0;
436 s = dev->subdevices + sdev_no;
437 s->private = devpriv->sprivs + sdev_no;
438 s->maxdata = (1 << thisboard->ai_bits) - 1;
439 s->range_table = thisboard->ai_range_table;
440 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
441 s->type = COMEDI_SUBD_AI;
442 s->n_chan = thisboard->n_ai_chans;
443 s->len_chanlist = s->n_chan;
444 s->insn_read = thisboard->ai_rinsn;
445 subpriv->iobase = dev->iobase + 0;
446
447 outb(0, subpriv->iobase + 3);
448 outb(0, subpriv->iobase + 4 + 3);
449
450
451 ++sdev_no;
452 s = dev->subdevices + sdev_no;
453 s->private = devpriv->sprivs + sdev_no;
454 s->maxdata = (1 << thisboard->ao_bits) - 1;
455 s->range_table = thisboard->ao_range_table;
456 s->subdev_flags = SDF_READABLE;
457 s->type = COMEDI_SUBD_AO;
458 s->n_chan = thisboard->n_ao_chans;
459 s->len_chanlist = s->n_chan;
460 s->insn_read = thisboard->ao_rinsn;
461 s->insn_write = thisboard->ao_winsn;
462 subpriv->iobase = dev->iobase + 8;
463
464 outb(0, subpriv->iobase + 3);
465 outb(0, subpriv->iobase + 4 + 3);
466
467 ++sdev_no;
468 port = 0;
469 asic = 0;
470 for (; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
471 int byte_no;
472
473 s = dev->subdevices + sdev_no;
474 s->private = devpriv->sprivs + sdev_no;
475 s->maxdata = 1;
476 s->range_table = &range_digital;
477 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
478 s->type = COMEDI_SUBD_DIO;
479 s->insn_bits = pcmmio_dio_insn_bits;
480 s->insn_config = pcmmio_dio_insn_config;
481 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
482 subpriv->dio.intr.asic = -1;
483 subpriv->dio.intr.first_chan = -1;
484 subpriv->dio.intr.asic_chan = -1;
485 subpriv->dio.intr.num_asic_chans = -1;
486 subpriv->dio.intr.active = 0;
487 s->len_chanlist = 1;
488
489
490
491 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
492 if (port >= PORTS_PER_ASIC) {
493 port = 0;
494 ++asic;
495 thisasic_chanct = 0;
496 }
497 subpriv->iobases[byte_no] =
498 devpriv->asics[asic].iobase + port;
499
500 if (thisasic_chanct <
501 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
502 && subpriv->dio.intr.asic < 0) {
503
504
505
506
507 subpriv->dio.intr.asic = asic;
508 subpriv->dio.intr.active = 0;
509 subpriv->dio.intr.stop_count = 0;
510 subpriv->dio.intr.first_chan = byte_no * 8;
511 subpriv->dio.intr.asic_chan = thisasic_chanct;
512 subpriv->dio.intr.num_asic_chans =
513 s->n_chan - subpriv->dio.intr.first_chan;
514 s->cancel = pcmmio_cancel;
515 s->do_cmd = pcmmio_cmd;
516 s->do_cmdtest = pcmmio_cmdtest;
517 s->len_chanlist =
518 subpriv->dio.intr.num_asic_chans;
519 }
520 thisasic_chanct += CHANS_PER_PORT;
521 }
522 spin_lock_init(&subpriv->dio.intr.spinlock);
523
524 chans_left -= s->n_chan;
525
526 if (!chans_left) {
527
528
529
530
531 asic = 0;
532 port = 0;
533 }
534
535 }
536
537 init_asics(dev);
538
539 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
540 if (irq[asic]
541 && request_irq(irq[asic], interrupt_pcmmio,
542 IRQF_SHARED, thisboard->name, dev)) {
543 int i;
544
545 for (i = asic - 1; i >= 0; --i) {
546 free_irq(irq[i], dev);
547 devpriv->asics[i].irq = irq[i] = 0;
548 }
549 irq[asic] = 0;
550 }
551 devpriv->asics[asic].irq = irq[asic];
552 }
553
554 dev->irq = irq[0];
555
556
557
558
559 if (irq[0]) {
560 printk("irq: %u ", irq[0]);
561 if (thisboard->dio_num_asics == 2 && irq[1])
562 printk("second ASIC irq: %u ", irq[1]);
563 } else {
564 printk("(IRQ mode disabled) ");
565 }
566
567 printk("attached\n");
568
569 return 1;
570}
571
572
573
574
575
576
577
578
579
580static int pcmmio_detach(struct comedi_device *dev)
581{
582 int i;
583
584 printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
585 if (dev->iobase)
586 release_region(dev->iobase, thisboard->total_iosize);
587
588 for (i = 0; i < MAX_ASICS; ++i) {
589 if (devpriv && devpriv->asics[i].irq)
590 free_irq(devpriv->asics[i].irq, dev);
591 }
592
593 if (devpriv && devpriv->sprivs)
594 kfree(devpriv->sprivs);
595
596 return 0;
597}
598
599
600
601
602
603
604static int pcmmio_dio_insn_bits(struct comedi_device *dev,
605 struct comedi_subdevice *s,
606 struct comedi_insn *insn, unsigned int *data)
607{
608 int byte_no;
609 if (insn->n != 2)
610 return -EINVAL;
611
612
613
614
615
616
617
618
619
620
621
622
623#ifdef DAMMIT_ITS_BROKEN
624
625 printk("write mask: %08x data: %08x\n", data[0], data[1]);
626#endif
627
628 s->state = 0;
629
630 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
631
632 unsigned long ioaddr = subpriv->iobases[byte_no],
633
634 offset = byte_no * 8;
635
636 unsigned char byte = 0,
637
638 write_mask_byte = (data[0] >> offset) & 0xff,
639
640 data_byte = (data[1] >> offset) & 0xff;
641
642 byte = inb(ioaddr);
643
644#ifdef DAMMIT_ITS_BROKEN
645
646 printk
647 ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
648 byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
649 offset, ioaddr, (unsigned)byte);
650#endif
651
652 if (write_mask_byte) {
653
654
655
656
657
658 byte &= ~write_mask_byte;
659
660 byte |= ~data_byte & write_mask_byte;
661
662 outb(byte, ioaddr);
663 }
664#ifdef DAMMIT_ITS_BROKEN
665
666 printk("data_out_byte %02x\n", (unsigned)byte);
667#endif
668
669 s->state |= ((unsigned int)byte) << offset;
670 }
671
672
673 data[1] = ~s->state;
674
675#ifdef DAMMIT_ITS_BROKEN
676
677 printk("s->state %08x data_out %08x\n", s->state, data[1]);
678#endif
679
680 return 2;
681}
682
683
684
685
686
687static int pcmmio_dio_insn_config(struct comedi_device *dev,
688 struct comedi_subdevice *s,
689 struct comedi_insn *insn, unsigned int *data)
690{
691 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
692 chan % 8;
693 unsigned long ioaddr;
694 unsigned char byte;
695
696
697 ioaddr = subpriv->iobases[byte_no];
698
699
700
701
702
703
704
705
706
707
708
709
710
711 switch (data[0]) {
712 case INSN_CONFIG_DIO_OUTPUT:
713
714
715 s->io_bits |= 1 << chan;
716 break;
717 case INSN_CONFIG_DIO_INPUT:
718
719
720 byte = inb(ioaddr);
721 byte &= ~(1 << bit_no);
722
723
724
725
726
727
728
729 outb(byte, ioaddr);
730
731
732 s->io_bits &= ~(1 << chan);
733 break;
734
735 case INSN_CONFIG_DIO_QUERY:
736
737 data[1] =
738 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
739 return insn->n;
740 break;
741
742 default:
743 return -EINVAL;
744 break;
745 }
746
747 return insn->n;
748}
749
750static void init_asics(struct comedi_device *dev)
751{
752
753 int asic;
754
755 for (asic = 0; asic < thisboard->dio_num_asics; ++asic) {
756 int port, page;
757 unsigned long baseaddr = devpriv->asics[asic].iobase;
758
759 switch_page(dev, asic, 0);
760
761
762 for (port = 0; port < PORTS_PER_ASIC; ++port)
763 outb(0, baseaddr + REG_PORT0 + port);
764
765
766 for (page = 1; page < NUM_PAGES; ++page) {
767 int reg;
768
769 switch_page(dev, asic, page);
770 for (reg = FIRST_PAGED_REG;
771 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
772 outb(0, baseaddr + reg);
773 }
774
775
776
777
778
779
780
781
782
783 switch_page(dev, asic, 0);
784 }
785}
786
787static void switch_page(struct comedi_device *dev, int asic, int page)
788{
789 if (asic < 0 || asic >= thisboard->dio_num_asics)
790 return;
791 if (page < 0 || page >= NUM_PAGES)
792 return;
793
794 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
795 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
796
797
798 outb(devpriv->asics[asic].pagelock,
799 devpriv->asics[asic].iobase + REG_PAGELOCK);
800}
801
802#ifdef notused
803static void lock_port(struct comedi_device *dev, int asic, int port)
804{
805 if (asic < 0 || asic >= thisboard->dio_num_asics)
806 return;
807 if (port < 0 || port >= PORTS_PER_ASIC)
808 return;
809
810 devpriv->asics[asic].pagelock |= 0x1 << port;
811
812 outb(devpriv->asics[asic].pagelock,
813 devpriv->asics[asic].iobase + REG_PAGELOCK);
814 return;
815}
816
817static void unlock_port(struct comedi_device *dev, int asic, int port)
818{
819 if (asic < 0 || asic >= thisboard->dio_num_asics)
820 return;
821 if (port < 0 || port >= PORTS_PER_ASIC)
822 return;
823 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
824
825 outb(devpriv->asics[asic].pagelock,
826 devpriv->asics[asic].iobase + REG_PAGELOCK);
827}
828#endif
829
830static irqreturn_t interrupt_pcmmio(int irq, void *d)
831{
832 int asic, got1 = 0;
833 struct comedi_device *dev = (struct comedi_device *)d;
834
835 for (asic = 0; asic < MAX_ASICS; ++asic) {
836 if (irq == devpriv->asics[asic].irq) {
837 unsigned long flags;
838 unsigned triggered = 0;
839 unsigned long iobase = devpriv->asics[asic].iobase;
840
841 unsigned char int_pend;
842
843 spin_lock_irqsave(&devpriv->asics[asic].spinlock,
844 flags);
845
846 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
847
848 if (int_pend) {
849 int port;
850 for (port = 0; port < INTR_PORTS_PER_ASIC;
851 ++port) {
852 if (int_pend & (0x1 << port)) {
853 unsigned char
854 io_lines_with_edges = 0;
855 switch_page(dev, asic,
856 PAGE_INT_ID);
857 io_lines_with_edges =
858 inb(iobase +
859 REG_INT_ID0 + port);
860
861 if (io_lines_with_edges)
862
863
864
865
866 outb(0, iobase +
867 REG_INT_ID0 +
868 port);
869
870 triggered |=
871 io_lines_with_edges <<
872 port * 8;
873 }
874 }
875
876 ++got1;
877 }
878
879 spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
880 flags);
881
882 if (triggered) {
883 struct comedi_subdevice *s;
884
885
886
887
888 printk
889 ("PCMMIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n",
890 irq, asic, triggered);
891 for (s = dev->subdevices + 2;
892 s < dev->subdevices + dev->n_subdevices;
893 ++s) {
894
895
896
897
898 if (subpriv->dio.intr.asic == asic) {
899 unsigned long flags;
900 unsigned oldevents;
901
902 spin_lock_irqsave(&subpriv->dio.
903 intr.spinlock,
904 flags);
905
906 oldevents = s->async->events;
907
908 if (subpriv->dio.intr.active) {
909 unsigned mytrig =
910 ((triggered >>
911 subpriv->dio.intr.asic_chan)
912 &
913 ((0x1 << subpriv->
914 dio.intr.
915 num_asic_chans) -
916 1)) << subpriv->
917 dio.intr.first_chan;
918 if (mytrig &
919 subpriv->dio.
920 intr.enabled_mask) {
921 unsigned int val
922 = 0;
923 unsigned int n,
924 ch, len;
925
926 len =
927 s->
928 async->cmd.chanlist_len;
929 for (n = 0;
930 n < len;
931 n++) {
932 ch = CR_CHAN(s->async->cmd.chanlist[n]);
933 if (mytrig & (1U << ch))
934 val |= (1U << n);
935 }
936
937 if (comedi_buf_put(s->async, ((short *)&val)[0])
938 &&
939 comedi_buf_put
940 (s->async,
941 ((short *)
942 &val)[1])) {
943 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
944 } else {
945
946
947 pcmmio_stop_intr
948 (dev,
949 s);
950 }
951
952
953 if (!subpriv->dio.intr.continuous) {
954
955 if (subpriv->dio.intr.stop_count > 0) {
956 subpriv->dio.intr.stop_count--;
957 if (subpriv->dio.intr.stop_count == 0) {
958 s->async->events |= COMEDI_CB_EOA;
959
960 pcmmio_stop_intr
961 (dev,
962 s);
963 }
964 }
965 }
966 }
967 }
968
969 spin_unlock_irqrestore
970 (&subpriv->dio.intr.
971 spinlock, flags);
972
973 if (oldevents !=
974 s->async->events) {
975 comedi_event(dev, s);
976 }
977
978 }
979
980 }
981 }
982
983 }
984 }
985 if (!got1)
986 return IRQ_NONE;
987 return IRQ_HANDLED;
988}
989
990static void pcmmio_stop_intr(struct comedi_device *dev,
991 struct comedi_subdevice *s)
992{
993 int nports, firstport, asic, port;
994
995 asic = subpriv->dio.intr.asic;
996 if (asic < 0)
997 return;
998
999 subpriv->dio.intr.enabled_mask = 0;
1000 subpriv->dio.intr.active = 0;
1001 s->async->inttrig = 0;
1002 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
1003 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
1004 switch_page(dev, asic, PAGE_ENAB);
1005 for (port = firstport; port < firstport + nports; ++port) {
1006
1007 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
1008 }
1009}
1010
1011static int pcmmio_start_intr(struct comedi_device *dev,
1012 struct comedi_subdevice *s)
1013{
1014 if (!subpriv->dio.intr.continuous && subpriv->dio.intr.stop_count == 0) {
1015
1016 s->async->events |= COMEDI_CB_EOA;
1017 subpriv->dio.intr.active = 0;
1018 return 1;
1019 } else {
1020 unsigned bits = 0, pol_bits = 0, n;
1021 int nports, firstport, asic, port;
1022 struct comedi_cmd *cmd = &s->async->cmd;
1023
1024 asic = subpriv->dio.intr.asic;
1025 if (asic < 0)
1026 return 1;
1027
1028 subpriv->dio.intr.enabled_mask = 0;
1029 subpriv->dio.intr.active = 1;
1030 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
1031 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
1032 if (cmd->chanlist) {
1033 for (n = 0; n < cmd->chanlist_len; n++) {
1034 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
1035 pol_bits |= (CR_AREF(cmd->chanlist[n])
1036 || CR_RANGE(cmd->
1037 chanlist[n]) ? 1U : 0U)
1038 << CR_CHAN(cmd->chanlist[n]);
1039 }
1040 }
1041 bits &= ((0x1 << subpriv->dio.intr.num_asic_chans) -
1042 1) << subpriv->dio.intr.first_chan;
1043 subpriv->dio.intr.enabled_mask = bits;
1044
1045 {
1046
1047
1048
1049
1050 unsigned char b;
1051
1052
1053
1054
1055 outb(1 << 4, dev->iobase + 3);
1056
1057 b = dev->irq & ((1 << 4) - 1);
1058 outb(b, dev->iobase + 2);
1059
1060 }
1061
1062 switch_page(dev, asic, PAGE_ENAB);
1063 for (port = firstport; port < firstport + nports; ++port) {
1064 unsigned enab =
1065 bits >> (subpriv->dio.intr.first_chan + (port -
1066 firstport)
1067 * 8) & 0xff, pol =
1068 pol_bits >> (subpriv->dio.intr.first_chan +
1069 (port - firstport) * 8) & 0xff;
1070
1071 outb(enab,
1072 devpriv->asics[asic].iobase + REG_ENAB0 + port);
1073 switch_page(dev, asic, PAGE_POL);
1074 outb(pol,
1075 devpriv->asics[asic].iobase + REG_ENAB0 + port);
1076 }
1077 }
1078 return 0;
1079}
1080
1081static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
1082{
1083 unsigned long flags;
1084
1085 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1086 if (subpriv->dio.intr.active)
1087 pcmmio_stop_intr(dev, s);
1088 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1089
1090 return 0;
1091}
1092
1093
1094
1095
1096static int
1097pcmmio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
1098 unsigned int trignum)
1099{
1100 unsigned long flags;
1101 int event = 0;
1102
1103 if (trignum != 0)
1104 return -EINVAL;
1105
1106 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1107 s->async->inttrig = 0;
1108 if (subpriv->dio.intr.active)
1109 event = pcmmio_start_intr(dev, s);
1110 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1111
1112 if (event)
1113 comedi_event(dev, s);
1114
1115 return 1;
1116}
1117
1118
1119
1120
1121static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
1122{
1123 struct comedi_cmd *cmd = &s->async->cmd;
1124 unsigned long flags;
1125 int event = 0;
1126
1127 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1128 subpriv->dio.intr.active = 1;
1129
1130
1131 switch (cmd->stop_src) {
1132 case TRIG_COUNT:
1133 subpriv->dio.intr.continuous = 0;
1134 subpriv->dio.intr.stop_count = cmd->stop_arg;
1135 break;
1136 default:
1137
1138 subpriv->dio.intr.continuous = 1;
1139 subpriv->dio.intr.stop_count = 0;
1140 break;
1141 }
1142
1143
1144 switch (cmd->start_src) {
1145 case TRIG_INT:
1146 s->async->inttrig = pcmmio_inttrig_start_intr;
1147 break;
1148 default:
1149
1150 event = pcmmio_start_intr(dev, s);
1151 break;
1152 }
1153 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1154
1155 if (event)
1156 comedi_event(dev, s);
1157
1158 return 0;
1159}
1160
1161static int
1162pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
1163 struct comedi_cmd *cmd)
1164{
1165 return comedi_pcm_cmdtest(dev, s, cmd);
1166}
1167
1168static int adc_wait_ready(unsigned long iobase)
1169{
1170 unsigned long retry = 100000;
1171 while (retry--)
1172 if (inb(iobase + 3) & 0x80)
1173 return 0;
1174 return 1;
1175}
1176
1177
1178static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
1179 struct comedi_insn *insn, unsigned int *data)
1180{
1181 int n;
1182 unsigned long iobase = subpriv->iobase;
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196 for (n = 0; n < insn->n; n++) {
1197 unsigned chan = CR_CHAN(insn->chanspec), range =
1198 CR_RANGE(insn->chanspec), aref = CR_AREF(insn->chanspec);
1199 unsigned char command_byte = 0;
1200 unsigned iooffset = 0;
1201 short sample, adc_adjust = 0;
1202
1203 if (chan > 7)
1204 chan -= 8, iooffset = 4;
1205
1206
1207
1208
1209 if (aref != AREF_DIFF) {
1210 aref = AREF_GROUND;
1211 command_byte |= 1 << 7;
1212
1213
1214
1215 }
1216 if (range < 2)
1217 adc_adjust = 0x8000;
1218
1219
1220
1221
1222
1223
1224
1225 if (chan % 2) {
1226 command_byte |= 1 << 6;
1227
1228
1229
1230 }
1231
1232
1233 command_byte |= ((chan / 2) & 0x3) << 4;
1234
1235
1236 command_byte |= (range & 0x3) << 2;
1237
1238
1239
1240 outb(command_byte, iobase + iooffset + 2);
1241
1242
1243 adc_wait_ready(iobase + iooffset);
1244
1245
1246 outb(command_byte, iobase + iooffset + 2);
1247
1248 adc_wait_ready(iobase + iooffset);
1249
1250
1251 sample = inb(iobase + iooffset + 0);
1252
1253
1254 sample |= inb(iobase + iooffset + 1) << 8;
1255 sample += adc_adjust;
1256 data[n] = sample;
1257 }
1258
1259 return n;
1260}
1261
1262static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
1263 struct comedi_insn *insn, unsigned int *data)
1264{
1265 int n;
1266 for (n = 0; n < insn->n; n++) {
1267 unsigned chan = CR_CHAN(insn->chanspec);
1268 if (chan < s->n_chan)
1269 data[n] = subpriv->ao.shadow_samples[chan];
1270 }
1271 return n;
1272}
1273
1274static int wait_dac_ready(unsigned long iobase)
1275{
1276 unsigned long retry = 100000L;
1277
1278
1279
1280
1281
1282
1283
1284 while (retry--) {
1285 if (inb(iobase + 3) & 0x80)
1286 return 0;
1287
1288 }
1289 return 1;
1290}
1291
1292static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
1293 struct comedi_insn *insn, unsigned int *data)
1294{
1295 int n;
1296 unsigned iobase = subpriv->iobase, iooffset = 0;
1297
1298 for (n = 0; n < insn->n; n++) {
1299 unsigned chan = CR_CHAN(insn->chanspec), range =
1300 CR_RANGE(insn->chanspec);
1301 if (chan < s->n_chan) {
1302 unsigned char command_byte = 0, range_byte =
1303 range & ((1 << 4) - 1);
1304 if (chan >= 4)
1305 chan -= 4, iooffset += 4;
1306
1307 outb(range_byte, iobase + iooffset + 0);
1308 outb(0, iobase + iooffset + 1);
1309
1310
1311 command_byte = (chan << 1) | 0x60;
1312 outb(command_byte, iobase + iooffset + 2);
1313
1314 wait_dac_ready(iobase + iooffset);
1315
1316
1317 outb(data[n] & 0xff, iobase + iooffset + 0);
1318
1319
1320 outb((data[n] >> 8) & 0xff, iobase + iooffset + 1);
1321
1322
1323
1324
1325
1326 command_byte = 0x70 | (chan << 1);
1327
1328 outb(command_byte, iobase + iooffset + 2);
1329
1330 wait_dac_ready(iobase + iooffset);
1331
1332
1333 subpriv->ao.shadow_samples[chan] = data[n];
1334 }
1335 }
1336 return n;
1337}
1338
1339
1340
1341
1342
1343static int __init driver_init_module(void)
1344{
1345 return comedi_driver_register(&driver);
1346}
1347
1348static void __exit driver_cleanup_module(void)
1349{
1350 comedi_driver_unregister(&driver);
1351}
1352
1353module_init(driver_init_module);
1354module_exit(driver_cleanup_module);
1355
1356MODULE_AUTHOR("Comedi http://www.comedi.org");
1357MODULE_DESCRIPTION("Comedi low-level driver");
1358MODULE_LICENSE("GPL");
1359