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#include "../comedidev.h"
36
37#include <linux/ioport.h>
38#include <linux/mc146818rtc.h>
39#include <linux/gfp.h>
40#include <linux/delay.h>
41#include <linux/io.h>
42#include <asm/dma.h>
43
44#include "8253.h"
45
46#define DEBUG(x) x
47
48
49
50#define PCLx1x_RANGE 16
51
52
53
54
55#define PCL816_CTR0 4
56#define PCL816_CTR1 5
57#define PCL816_CTR2 6
58
59#define PCL816_CTRCTL 7
60
61
62#define PCL816_RANGE 9
63
64#define PCL816_CLRINT 10
65
66#define PCL816_MUX 11
67
68#define PCL816_CONTROL 12
69
70
71#define PCL816_STATUS 13
72#define PCL816_STATUS_DRDY_MASK 0x80
73
74
75#define PCL816_AD_LO 8
76
77#define PCL816_AD_HI 9
78
79
80#define INT_TYPE_AI1_INT 1
81#define INT_TYPE_AI1_DMA 2
82#define INT_TYPE_AI3_INT 4
83#define INT_TYPE_AI3_DMA 5
84#ifdef unused
85#define INT_TYPE_AI1_DMA_RTC 9
86#define INT_TYPE_AI3_DMA_RTC 10
87
88
89#define RTC_IRQ 8
90#define RTC_IO_EXTENT 0x10
91#endif
92
93#define MAGIC_DMA_WORD 0x5a5a
94
95static const struct comedi_lrange range_pcl816 = { 8, {
96 BIP_RANGE(10),
97 BIP_RANGE(5),
98 BIP_RANGE(2.5),
99 BIP_RANGE(1.25),
100 UNI_RANGE(10),
101 UNI_RANGE(5),
102 UNI_RANGE(2.5),
103 UNI_RANGE(1.25),
104 }
105};
106
107struct pcl816_board {
108
109 const char *name;
110 int n_ranges;
111 int n_aichan;
112 unsigned int ai_ns_min;
113 int n_aochan;
114 int n_dichan;
115 int n_dochan;
116 const struct comedi_lrange *ai_range_type;
117 const struct comedi_lrange *ao_range_type;
118 unsigned int io_range;
119 unsigned int IRQbits;
120 unsigned int DMAbits;
121 int ai_maxdata;
122 int ao_maxdata;
123 int ai_chanlist;
124 int ao_chanlist;
125 int i8254_osc_base;
126};
127
128static const struct pcl816_board boardtypes[] = {
129 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
130 &range_pcl816, PCLx1x_RANGE,
131 0x00fc,
132 0x0a,
133 0xffff,
134 0xffff,
135 1024,
136 1,
137 100},
138 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
139 &range_pcl816, PCLx1x_RANGE,
140 0x00fc,
141 0x0a,
142 0x3fff,
143 0x3fff,
144 1024,
145 1,
146 100},
147};
148
149#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl816_board))
150#define devpriv ((struct pcl816_private *)dev->private)
151#define this_board ((const struct pcl816_board *)dev->board_ptr)
152
153static int pcl816_attach(struct comedi_device *dev,
154 struct comedi_devconfig *it);
155static int pcl816_detach(struct comedi_device *dev);
156
157#ifdef unused
158static int RTC_lock = 0;
159static int RTC_timer_lock = 0;
160#endif
161
162static struct comedi_driver driver_pcl816 = {
163 .driver_name = "pcl816",
164 .module = THIS_MODULE,
165 .attach = pcl816_attach,
166 .detach = pcl816_detach,
167 .board_name = &boardtypes[0].name,
168 .num_names = n_boardtypes,
169 .offset = sizeof(struct pcl816_board),
170};
171
172static int __init driver_pcl816_init_module(void)
173{
174 return comedi_driver_register(&driver_pcl816);
175}
176
177static void __exit driver_pcl816_cleanup_module(void)
178{
179 comedi_driver_unregister(&driver_pcl816);
180}
181
182module_init(driver_pcl816_init_module);
183module_exit(driver_pcl816_cleanup_module);
184
185struct pcl816_private {
186
187 unsigned int dma;
188 int dma_rtc;
189#ifdef unused
190 unsigned long rtc_iobase;
191 unsigned int rtc_iosize;
192 unsigned int rtc_irq;
193#endif
194 unsigned long dmabuf[2];
195 unsigned int dmapages[2];
196 unsigned int hwdmaptr[2];
197 unsigned int hwdmasize[2];
198 unsigned int dmasamplsize;
199 unsigned int last_top_dma;
200 int next_dma_buf;
201 long dma_runs_to_end;
202 unsigned long last_dma_run;
203
204 unsigned int ai_scans;
205 unsigned char ai_neverending;
206 int irq_free;
207 int irq_blocked;
208#ifdef unused
209 int rtc_irq_blocked;
210#endif
211 int irq_was_now_closed;
212 int int816_mode;
213 struct comedi_subdevice *last_int_sub;
214 int ai_act_scan;
215 unsigned int ai_act_chanlist[16];
216 unsigned int ai_act_chanlist_len;
217 unsigned int ai_act_chanlist_pos;
218 unsigned int ai_n_chan;
219 unsigned int ai_poll_ptr;
220 struct comedi_subdevice *sub_ai;
221#ifdef unused
222 struct timer_list rtc_irq_timer;
223 unsigned long rtc_freq;
224#endif
225};
226
227
228
229
230static int check_channel_list(struct comedi_device *dev,
231 struct comedi_subdevice *s,
232 unsigned int *chanlist, unsigned int chanlen);
233static void setup_channel_list(struct comedi_device *dev,
234 struct comedi_subdevice *s,
235 unsigned int *chanlist, unsigned int seglen);
236static int pcl816_ai_cancel(struct comedi_device *dev,
237 struct comedi_subdevice *s);
238static void start_pacer(struct comedi_device *dev, int mode,
239 unsigned int divisor1, unsigned int divisor2);
240#ifdef unused
241static int set_rtc_irq_bit(unsigned char bit);
242#endif
243
244static int pcl816_ai_cmdtest(struct comedi_device *dev,
245 struct comedi_subdevice *s,
246 struct comedi_cmd *cmd);
247static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
248
249
250
251
252
253static int pcl816_ai_insn_read(struct comedi_device *dev,
254 struct comedi_subdevice *s,
255 struct comedi_insn *insn, unsigned int *data)
256{
257 int n;
258 int timeout;
259
260 DPRINTK("mode 0 analog input\n");
261
262 outb(0, dev->iobase + PCL816_CONTROL);
263
264 outb(0, dev->iobase + PCL816_CLRINT);
265
266
267 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
268
269 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE);
270
271 for (n = 0; n < insn->n; n++) {
272
273 outb(0, dev->iobase + PCL816_AD_LO);
274
275 timeout = 100;
276 while (timeout--) {
277 if (!(inb(dev->iobase + PCL816_STATUS) &
278 PCL816_STATUS_DRDY_MASK)) {
279
280 data[n] =
281 ((inb(dev->iobase +
282 PCL816_AD_HI) << 8) |
283 (inb(dev->iobase + PCL816_AD_LO)));
284
285 outb(0, dev->iobase + PCL816_CLRINT);
286 break;
287 }
288 udelay(1);
289 }
290
291 if (!timeout) {
292 comedi_error(dev, "A/D insn timeout\n");
293 data[0] = 0;
294
295 outb(0, dev->iobase + PCL816_CLRINT);
296 return -EIO;
297 }
298
299 }
300 return n;
301}
302
303
304
305
306
307
308static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
309{
310 struct comedi_device *dev = d;
311 struct comedi_subdevice *s = dev->subdevices + 0;
312 int low, hi;
313 int timeout = 50;
314
315 while (timeout--) {
316 if (!(inb(dev->iobase + PCL816_STATUS) &
317 PCL816_STATUS_DRDY_MASK))
318 break;
319 udelay(1);
320 }
321 if (!timeout) {
322 outb(0, dev->iobase + PCL816_CLRINT);
323 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
324 pcl816_ai_cancel(dev, s);
325 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
326 comedi_event(dev, s);
327 return IRQ_HANDLED;
328
329 }
330
331
332 low = inb(dev->iobase + PCL816_AD_LO);
333 hi = inb(dev->iobase + PCL816_AD_HI);
334
335 comedi_buf_put(s->async, (hi << 8) | low);
336
337 outb(0, dev->iobase + PCL816_CLRINT);
338
339 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
340 devpriv->ai_act_chanlist_pos = 0;
341
342 s->async->cur_chan++;
343 if (s->async->cur_chan >= devpriv->ai_n_chan) {
344 s->async->cur_chan = 0;
345 devpriv->ai_act_scan++;
346 }
347
348 if (!devpriv->ai_neverending)
349
350 if (devpriv->ai_act_scan >= devpriv->ai_scans) {
351
352 pcl816_ai_cancel(dev, s);
353 s->async->events |= COMEDI_CB_EOA;
354 }
355 comedi_event(dev, s);
356 return IRQ_HANDLED;
357}
358
359
360
361
362
363static void transfer_from_dma_buf(struct comedi_device *dev,
364 struct comedi_subdevice *s, short *ptr,
365 unsigned int bufptr, unsigned int len)
366{
367 int i;
368
369 s->async->events = 0;
370
371 for (i = 0; i < len; i++) {
372
373 comedi_buf_put(s->async, ptr[bufptr++]);
374
375 if (++devpriv->ai_act_chanlist_pos >=
376 devpriv->ai_act_chanlist_len) {
377 devpriv->ai_act_chanlist_pos = 0;
378 }
379
380 s->async->cur_chan++;
381 if (s->async->cur_chan >= devpriv->ai_n_chan) {
382 s->async->cur_chan = 0;
383 devpriv->ai_act_scan++;
384 }
385
386 if (!devpriv->ai_neverending)
387
388 if (devpriv->ai_act_scan >= devpriv->ai_scans) {
389 pcl816_ai_cancel(dev, s);
390 s->async->events |= COMEDI_CB_EOA;
391 s->async->events |= COMEDI_CB_BLOCK;
392 break;
393 }
394 }
395
396 comedi_event(dev, s);
397}
398
399static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
400{
401 struct comedi_device *dev = d;
402 struct comedi_subdevice *s = dev->subdevices + 0;
403 int len, bufptr, this_dma_buf;
404 unsigned long dma_flags;
405 short *ptr;
406
407 disable_dma(devpriv->dma);
408 this_dma_buf = devpriv->next_dma_buf;
409
410
411 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) {
412
413 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
414 set_dma_mode(devpriv->dma, DMA_MODE_READ);
415 dma_flags = claim_dma_lock();
416
417 set_dma_addr(devpriv->dma,
418 devpriv->hwdmaptr[devpriv->next_dma_buf]);
419 if (devpriv->dma_runs_to_end) {
420 set_dma_count(devpriv->dma,
421 devpriv->hwdmasize[devpriv->
422 next_dma_buf]);
423 } else {
424 set_dma_count(devpriv->dma, devpriv->last_dma_run);
425 }
426 release_dma_lock(dma_flags);
427 enable_dma(devpriv->dma);
428 }
429
430 devpriv->dma_runs_to_end--;
431 outb(0, dev->iobase + PCL816_CLRINT);
432
433 ptr = (short *)devpriv->dmabuf[this_dma_buf];
434
435 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
436 bufptr = devpriv->ai_poll_ptr;
437 devpriv->ai_poll_ptr = 0;
438
439 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
440 return IRQ_HANDLED;
441}
442
443
444
445
446
447static irqreturn_t interrupt_pcl816(int irq, void *d)
448{
449 struct comedi_device *dev = d;
450 DPRINTK("<I>");
451
452 if (!dev->attached) {
453 comedi_error(dev, "premature interrupt");
454 return IRQ_HANDLED;
455 }
456
457 switch (devpriv->int816_mode) {
458 case INT_TYPE_AI1_DMA:
459 case INT_TYPE_AI3_DMA:
460 return interrupt_pcl816_ai_mode13_dma(irq, d);
461 case INT_TYPE_AI1_INT:
462 case INT_TYPE_AI3_INT:
463 return interrupt_pcl816_ai_mode13_int(irq, d);
464 }
465
466 outb(0, dev->iobase + PCL816_CLRINT);
467 if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) |
468 (!devpriv->int816_mode)) {
469 if (devpriv->irq_was_now_closed) {
470 devpriv->irq_was_now_closed = 0;
471
472 return IRQ_HANDLED;
473 }
474 comedi_error(dev, "bad IRQ!");
475 return IRQ_NONE;
476 }
477 comedi_error(dev, "IRQ from unknown source!");
478 return IRQ_NONE;
479}
480
481
482
483
484
485static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd)
486{
487 printk(KERN_INFO "pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
488 cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
489 printk(KERN_INFO "pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
490 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
491 printk(KERN_INFO "pcl816 e=%d stopsrc=%x scanend=%x\n", e,
492 cmd->stop_src, cmd->scan_end_src);
493 printk(KERN_INFO "pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n",
494 e, cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
495}
496
497
498
499
500static int pcl816_ai_cmdtest(struct comedi_device *dev,
501 struct comedi_subdevice *s, struct comedi_cmd *cmd)
502{
503 int err = 0;
504 int tmp, divisor1 = 0, divisor2 = 0;
505
506 DEBUG(printk(KERN_INFO "pcl816 pcl812_ai_cmdtest\n");
507 pcl816_cmdtest_out(-1, cmd);
508 );
509
510
511 tmp = cmd->start_src;
512 cmd->start_src &= TRIG_NOW;
513 if (!cmd->start_src || tmp != cmd->start_src)
514 err++;
515
516 tmp = cmd->scan_begin_src;
517 cmd->scan_begin_src &= TRIG_FOLLOW;
518 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
519 err++;
520
521 tmp = cmd->convert_src;
522 cmd->convert_src &= TRIG_EXT | TRIG_TIMER;
523 if (!cmd->convert_src || tmp != cmd->convert_src)
524 err++;
525
526 tmp = cmd->scan_end_src;
527 cmd->scan_end_src &= TRIG_COUNT;
528 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
529 err++;
530
531 tmp = cmd->stop_src;
532 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
533 if (!cmd->stop_src || tmp != cmd->stop_src)
534 err++;
535
536 if (err)
537 return 1;
538
539
540
541
542
543
544
545 if (cmd->start_src != TRIG_NOW) {
546 cmd->start_src = TRIG_NOW;
547 err++;
548 }
549
550 if (cmd->scan_begin_src != TRIG_FOLLOW) {
551 cmd->scan_begin_src = TRIG_FOLLOW;
552 err++;
553 }
554
555 if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
556 cmd->convert_src = TRIG_TIMER;
557 err++;
558 }
559
560 if (cmd->scan_end_src != TRIG_COUNT) {
561 cmd->scan_end_src = TRIG_COUNT;
562 err++;
563 }
564
565 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
566 err++;
567
568 if (err)
569 return 2;
570
571
572
573 if (cmd->start_arg != 0) {
574 cmd->start_arg = 0;
575 err++;
576 }
577
578 if (cmd->scan_begin_arg != 0) {
579 cmd->scan_begin_arg = 0;
580 err++;
581 }
582 if (cmd->convert_src == TRIG_TIMER) {
583 if (cmd->convert_arg < this_board->ai_ns_min) {
584 cmd->convert_arg = this_board->ai_ns_min;
585 err++;
586 }
587 } else {
588 if (cmd->convert_arg != 0) {
589 cmd->convert_arg = 0;
590 err++;
591 }
592 }
593
594 if (cmd->scan_end_arg != cmd->chanlist_len) {
595 cmd->scan_end_arg = cmd->chanlist_len;
596 err++;
597 }
598 if (cmd->stop_src == TRIG_COUNT) {
599 if (!cmd->stop_arg) {
600 cmd->stop_arg = 1;
601 err++;
602 }
603 } else {
604 if (cmd->stop_arg != 0) {
605 cmd->stop_arg = 0;
606 err++;
607 }
608 }
609
610 if (err)
611 return 3;
612
613
614
615 if (cmd->convert_src == TRIG_TIMER) {
616 tmp = cmd->convert_arg;
617 i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
618 &divisor1, &divisor2,
619 &cmd->convert_arg,
620 cmd->flags & TRIG_ROUND_MASK);
621 if (cmd->convert_arg < this_board->ai_ns_min)
622 cmd->convert_arg = this_board->ai_ns_min;
623 if (tmp != cmd->convert_arg)
624 err++;
625 }
626
627 if (err)
628 return 4;
629
630
631
632
633 if (cmd->chanlist) {
634 if (!check_channel_list(dev, s, cmd->chanlist,
635 cmd->chanlist_len))
636 return 5;
637 }
638
639 return 0;
640}
641
642static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
643{
644 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
645 struct comedi_cmd *cmd = &s->async->cmd;
646 unsigned int seglen;
647
648 if (cmd->start_src != TRIG_NOW)
649 return -EINVAL;
650 if (cmd->scan_begin_src != TRIG_FOLLOW)
651 return -EINVAL;
652 if (cmd->scan_end_src != TRIG_COUNT)
653 return -EINVAL;
654 if (cmd->scan_end_arg != cmd->chanlist_len)
655 return -EINVAL;
656
657 if (devpriv->irq_blocked)
658 return -EBUSY;
659
660 if (cmd->convert_src == TRIG_TIMER) {
661 if (cmd->convert_arg < this_board->ai_ns_min)
662 cmd->convert_arg = this_board->ai_ns_min;
663
664 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
665 &divisor2, &cmd->convert_arg,
666 cmd->flags & TRIG_ROUND_MASK);
667
668
669 if (divisor1 == 1) {
670 divisor1 = 2;
671 divisor2 /= 2;
672 }
673 if (divisor2 == 1) {
674 divisor2 = 2;
675 divisor1 /= 2;
676 }
677 }
678
679 start_pacer(dev, -1, 0, 0);
680
681 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
682 if (seglen < 1)
683 return -EINVAL;
684 setup_channel_list(dev, s, cmd->chanlist, seglen);
685 udelay(1);
686
687 devpriv->ai_n_chan = cmd->chanlist_len;
688 devpriv->ai_act_scan = 0;
689 s->async->cur_chan = 0;
690 devpriv->irq_blocked = 1;
691 devpriv->ai_poll_ptr = 0;
692 devpriv->irq_was_now_closed = 0;
693
694 if (cmd->stop_src == TRIG_COUNT) {
695 devpriv->ai_scans = cmd->stop_arg;
696 devpriv->ai_neverending = 0;
697 } else {
698 devpriv->ai_scans = 0;
699 devpriv->ai_neverending = 1;
700 }
701
702
703 if ((cmd->flags & TRIG_WAKE_EOS)) {
704 printk(KERN_INFO
705 "pl816: You wankt WAKE_EOS but I dont want handle it");
706
707
708
709 }
710
711 if (devpriv->dma) {
712 bytes = devpriv->hwdmasize[0];
713 if (!devpriv->ai_neverending) {
714
715 bytes = s->async->cmd.chanlist_len *
716 s->async->cmd.chanlist_len *
717 sizeof(short);
718
719
720 devpriv->dma_runs_to_end = bytes /
721 devpriv->hwdmasize[0];
722
723
724 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0];
725 devpriv->dma_runs_to_end--;
726 if (devpriv->dma_runs_to_end >= 0)
727 bytes = devpriv->hwdmasize[0];
728 } else
729 devpriv->dma_runs_to_end = -1;
730
731 devpriv->next_dma_buf = 0;
732 set_dma_mode(devpriv->dma, DMA_MODE_READ);
733 dma_flags = claim_dma_lock();
734 clear_dma_ff(devpriv->dma);
735 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
736 set_dma_count(devpriv->dma, bytes);
737 release_dma_lock(dma_flags);
738 enable_dma(devpriv->dma);
739 }
740
741 start_pacer(dev, 1, divisor1, divisor2);
742 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
743
744 switch (cmd->convert_src) {
745 case TRIG_TIMER:
746 devpriv->int816_mode = INT_TYPE_AI1_DMA;
747
748
749 outb(0x32, dev->iobase + PCL816_CONTROL);
750
751
752 outb(dmairq, dev->iobase + PCL816_STATUS);
753 break;
754
755 default:
756 devpriv->int816_mode = INT_TYPE_AI3_DMA;
757
758
759 outb(0x34, dev->iobase + PCL816_CONTROL);
760
761
762 outb(dmairq, dev->iobase + PCL816_STATUS);
763 break;
764 }
765
766 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
767 return 0;
768}
769
770static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
771{
772 unsigned long flags;
773 unsigned int top1, top2, i;
774
775 if (!devpriv->dma)
776 return 0;
777
778 spin_lock_irqsave(&dev->spinlock, flags);
779
780 for (i = 0; i < 20; i++) {
781 top1 = get_dma_residue(devpriv->dma);
782 top2 = get_dma_residue(devpriv->dma);
783 if (top1 == top2)
784 break;
785 }
786 if (top1 != top2) {
787 spin_unlock_irqrestore(&dev->spinlock, flags);
788 return 0;
789 }
790
791
792 top1 = devpriv->hwdmasize[0] - top1;
793 top1 >>= 1;
794 top2 = top1 - devpriv->ai_poll_ptr;
795 if (top2 < 1) {
796 spin_unlock_irqrestore(&dev->spinlock, flags);
797 return 0;
798 }
799
800 transfer_from_dma_buf(dev, s,
801 (short *)devpriv->dmabuf[devpriv->next_dma_buf],
802 devpriv->ai_poll_ptr, top2);
803
804 devpriv->ai_poll_ptr = top1;
805 spin_unlock_irqrestore(&dev->spinlock, flags);
806
807 return s->async->buf_write_count - s->async->buf_read_count;
808}
809
810
811
812
813
814static int pcl816_ai_cancel(struct comedi_device *dev,
815 struct comedi_subdevice *s)
816{
817
818
819 if (devpriv->irq_blocked > 0) {
820 switch (devpriv->int816_mode) {
821#ifdef unused
822 case INT_TYPE_AI1_DMA_RTC:
823 case INT_TYPE_AI3_DMA_RTC:
824 set_rtc_irq_bit(0);
825 del_timer(&devpriv->rtc_irq_timer);
826#endif
827 case INT_TYPE_AI1_DMA:
828 case INT_TYPE_AI3_DMA:
829 disable_dma(devpriv->dma);
830 case INT_TYPE_AI1_INT:
831 case INT_TYPE_AI3_INT:
832 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73,
833 dev->iobase + PCL816_CONTROL);
834 udelay(1);
835 outb(0, dev->iobase + PCL816_CONTROL);
836
837
838 outb(0xb0, dev->iobase + PCL816_CTRCTL);
839 outb(0x70, dev->iobase + PCL816_CTRCTL);
840 outb(0, dev->iobase + PCL816_AD_LO);
841 inb(dev->iobase + PCL816_AD_LO);
842 inb(dev->iobase + PCL816_AD_HI);
843
844
845 outb(0, dev->iobase + PCL816_CLRINT);
846
847
848 outb(0, dev->iobase + PCL816_CONTROL);
849 devpriv->irq_blocked = 0;
850 devpriv->irq_was_now_closed = devpriv->int816_mode;
851 devpriv->int816_mode = 0;
852 devpriv->last_int_sub = s;
853
854 break;
855 }
856 }
857
858 DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");)
859 return 0;
860}
861
862
863
864
865
866static int pcl816_check(unsigned long iobase)
867{
868 outb(0x00, iobase + PCL816_MUX);
869 udelay(1);
870 if (inb(iobase + PCL816_MUX) != 0x00)
871 return 1;
872 outb(0x55, iobase + PCL816_MUX);
873 udelay(1);
874 if (inb(iobase + PCL816_MUX) != 0x55)
875 return 1;
876 outb(0x00, iobase + PCL816_MUX);
877 udelay(1);
878 outb(0x18, iobase + PCL816_CONTROL);
879 udelay(1);
880 if (inb(iobase + PCL816_CONTROL) != 0x18)
881 return 1;
882 return 0;
883}
884
885
886
887
888
889static void pcl816_reset(struct comedi_device *dev)
890{
891
892
893
894
895
896
897 outb(0, dev->iobase + PCL816_CONTROL);
898 outb(0, dev->iobase + PCL816_MUX);
899 outb(0, dev->iobase + PCL816_CLRINT);
900 outb(0xb0, dev->iobase + PCL816_CTRCTL);
901 outb(0x70, dev->iobase + PCL816_CTRCTL);
902 outb(0x30, dev->iobase + PCL816_CTRCTL);
903 outb(0, dev->iobase + PCL816_RANGE);
904}
905
906
907
908
909
910static void
911start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
912 unsigned int divisor2)
913{
914 outb(0x32, dev->iobase + PCL816_CTRCTL);
915 outb(0xff, dev->iobase + PCL816_CTR0);
916 outb(0x00, dev->iobase + PCL816_CTR0);
917 udelay(1);
918
919
920 outb(0xb4, dev->iobase + PCL816_CTRCTL);
921
922 outb(0x74, dev->iobase + PCL816_CTRCTL);
923 udelay(1);
924
925 if (mode == 1) {
926 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
927 divisor2);
928 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
929 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
930 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
931 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
932 }
933
934
935
936}
937
938
939
940
941
942
943static int
944check_channel_list(struct comedi_device *dev,
945 struct comedi_subdevice *s, unsigned int *chanlist,
946 unsigned int chanlen)
947{
948 unsigned int chansegment[16];
949 unsigned int i, nowmustbechan, seglen, segpos;
950
951
952 if (chanlen < 1) {
953 comedi_error(dev, "range/channel list is empty!");
954 return 0;
955 }
956
957 if (chanlen > 1) {
958
959 chansegment[0] = chanlist[0];
960 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
961
962 DEBUG(printk(KERN_INFO "%d. %d %d\n", i,
963 CR_CHAN(chanlist[i]),
964 CR_RANGE(chanlist[i]));)
965
966
967 if (chanlist[0] == chanlist[i])
968 break;
969 nowmustbechan =
970 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
971 if (nowmustbechan != CR_CHAN(chanlist[i])) {
972
973 printk(KERN_WARNING
974 "comedi%d: pcl816: channel list must "
975 "be continuous! chanlist[%i]=%d but "
976 "must be %d or %d!\n", dev->minor,
977 i, CR_CHAN(chanlist[i]), nowmustbechan,
978 CR_CHAN(chanlist[0]));
979 return 0;
980 }
981
982 chansegment[i] = chanlist[i];
983 }
984
985
986 for (i = 0, segpos = 0; i < chanlen; i++) {
987 DEBUG(printk("%d %d=%d %d\n",
988 CR_CHAN(chansegment[i % seglen]),
989 CR_RANGE(chansegment[i % seglen]),
990 CR_CHAN(chanlist[i]),
991 CR_RANGE(chanlist[i]));)
992 if (chanlist[i] != chansegment[i % seglen]) {
993 printk(KERN_WARNING
994 "comedi%d: pcl816: bad channel or range"
995 " number! chanlist[%i]=%d,%d,%d and not"
996 " %d,%d,%d!\n", dev->minor, i,
997 CR_CHAN(chansegment[i]),
998 CR_RANGE(chansegment[i]),
999 CR_AREF(chansegment[i]),
1000 CR_CHAN(chanlist[i % seglen]),
1001 CR_RANGE(chanlist[i % seglen]),
1002 CR_AREF(chansegment[i % seglen]));
1003 return 0;
1004 }
1005 }
1006 } else {
1007 seglen = 1;
1008 }
1009
1010 return seglen;
1011}
1012
1013
1014
1015
1016
1017static void
1018setup_channel_list(struct comedi_device *dev,
1019 struct comedi_subdevice *s, unsigned int *chanlist,
1020 unsigned int seglen)
1021{
1022 unsigned int i;
1023
1024 devpriv->ai_act_chanlist_len = seglen;
1025 devpriv->ai_act_chanlist_pos = 0;
1026
1027 for (i = 0; i < seglen; i++) {
1028 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
1029 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
1030
1031 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE);
1032 }
1033
1034 udelay(1);
1035
1036 outb(devpriv->ai_act_chanlist[0] |
1037 (devpriv->ai_act_chanlist[seglen - 1] << 4),
1038 dev->iobase + PCL816_MUX);
1039}
1040
1041#ifdef unused
1042
1043
1044
1045
1046static int set_rtc_irq_bit(unsigned char bit)
1047{
1048 unsigned char val;
1049 unsigned long flags;
1050
1051 if (bit == 1) {
1052 RTC_timer_lock++;
1053 if (RTC_timer_lock > 1)
1054 return 0;
1055 } else {
1056 RTC_timer_lock--;
1057 if (RTC_timer_lock < 0)
1058 RTC_timer_lock = 0;
1059 if (RTC_timer_lock > 0)
1060 return 0;
1061 }
1062
1063 save_flags(flags);
1064 cli();
1065 val = CMOS_READ(RTC_CONTROL);
1066 if (bit)
1067 val |= RTC_PIE;
1068 else
1069 val &= ~RTC_PIE;
1070
1071 CMOS_WRITE(val, RTC_CONTROL);
1072 CMOS_READ(RTC_INTR_FLAGS);
1073 restore_flags(flags);
1074 return 0;
1075}
1076#endif
1077
1078
1079
1080
1081
1082static void free_resources(struct comedi_device *dev)
1083{
1084
1085 if (dev->private) {
1086 pcl816_ai_cancel(dev, devpriv->sub_ai);
1087 pcl816_reset(dev);
1088 if (devpriv->dma)
1089 free_dma(devpriv->dma);
1090 if (devpriv->dmabuf[0])
1091 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1092 if (devpriv->dmabuf[1])
1093 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1094#ifdef unused
1095 if (devpriv->rtc_irq)
1096 free_irq(devpriv->rtc_irq, dev);
1097 if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
1098 if (devpriv->rtc_iobase)
1099 release_region(devpriv->rtc_iobase,
1100 devpriv->rtc_iosize);
1101 }
1102#endif
1103 }
1104
1105 if (dev->irq)
1106 free_irq(dev->irq, dev);
1107 if (dev->iobase)
1108 release_region(dev->iobase, this_board->io_range);
1109
1110}
1111
1112
1113
1114
1115
1116
1117
1118static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1119{
1120 int ret;
1121 unsigned long iobase;
1122 unsigned int irq, dma;
1123 unsigned long pages;
1124
1125 struct comedi_subdevice *s;
1126
1127
1128 iobase = it->options[0];
1129 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
1130 this_board->name, iobase);
1131
1132 if (!request_region(iobase, this_board->io_range, "pcl816")) {
1133 printk("I/O port conflict\n");
1134 return -EIO;
1135 }
1136
1137 dev->iobase = iobase;
1138
1139 if (pcl816_check(iobase)) {
1140 printk(KERN_ERR ", I cann't detect board. FAIL!\n");
1141 return -EIO;
1142 }
1143
1144 ret = alloc_private(dev, sizeof(struct pcl816_private));
1145 if (ret < 0)
1146 return ret;
1147
1148
1149 dev->board_name = this_board->name;
1150
1151
1152 irq = 0;
1153 if (this_board->IRQbits != 0) {
1154 irq = it->options[1];
1155 if (irq) {
1156 if (((1 << irq) & this_board->IRQbits) == 0) {
1157 printk
1158 (", IRQ %u is out of allowed range, "
1159 "DISABLING IT", irq);
1160 irq = 0;
1161 } else {
1162 if (request_irq
1163 (irq, interrupt_pcl816, 0, "pcl816", dev)) {
1164 printk
1165 (", unable to allocate IRQ %u, "
1166 "DISABLING IT", irq);
1167 irq = 0;
1168 } else {
1169 printk(KERN_INFO ", irq=%u", irq);
1170 }
1171 }
1172 }
1173 }
1174
1175 dev->irq = irq;
1176 if (irq)
1177 devpriv->irq_free = 1;
1178 else
1179 devpriv->irq_free = 0;
1180
1181 devpriv->irq_blocked = 0;
1182 devpriv->int816_mode = 0;
1183
1184#ifdef unused
1185
1186 devpriv->dma_rtc = 0;
1187 if (it->options[2] > 0) {
1188 if (RTC_lock == 0) {
1189 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
1190 "pcl816 (RTC)"))
1191 goto no_rtc;
1192 }
1193 devpriv->rtc_iobase = RTC_PORT(0);
1194 devpriv->rtc_iosize = RTC_IO_EXTENT;
1195 RTC_lock++;
1196#ifdef UNTESTED_CODE
1197 if (!request_irq(RTC_IRQ, interrupt_pcl816_ai_mode13_dma_rtc, 0,
1198 "pcl816 DMA (RTC)", dev)) {
1199 devpriv->dma_rtc = 1;
1200 devpriv->rtc_irq = RTC_IRQ;
1201 printk(", dma_irq=%u", devpriv->rtc_irq);
1202 } else {
1203 RTC_lock--;
1204 if (RTC_lock == 0) {
1205 if (devpriv->rtc_iobase)
1206 release_region(devpriv->rtc_iobase,
1207 devpriv->rtc_iosize);
1208 }
1209 devpriv->rtc_iobase = 0;
1210 devpriv->rtc_iosize = 0;
1211 }
1212#else
1213 printk("pcl816: RTC code missing");
1214#endif
1215
1216 }
1217
1218no_rtc:
1219#endif
1220
1221 dma = 0;
1222 devpriv->dma = dma;
1223 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1224 goto no_dma;
1225
1226 if (this_board->DMAbits != 0) {
1227 dma = it->options[2];
1228 if (dma < 1)
1229 goto no_dma;
1230
1231 if (((1 << dma) & this_board->DMAbits) == 0) {
1232 printk(", DMA is out of allowed range, FAIL!\n");
1233 return -EINVAL;
1234 }
1235 ret = request_dma(dma, "pcl816");
1236 if (ret) {
1237 printk(KERN_ERR
1238 ", unable to allocate DMA %u, FAIL!\n", dma);
1239 return -EBUSY;
1240 }
1241
1242 devpriv->dma = dma;
1243 printk(KERN_INFO ", dma=%u", dma);
1244 pages = 2;
1245 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1246
1247 if (!devpriv->dmabuf[0]) {
1248 printk(", unable to allocate DMA buffer, FAIL!\n");
1249
1250
1251
1252
1253 return -EBUSY;
1254 }
1255 devpriv->dmapages[0] = pages;
1256 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1257 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
1258
1259
1260 if (devpriv->dma_rtc == 0) {
1261 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1262 if (!devpriv->dmabuf[1]) {
1263 printk(KERN_ERR
1264 ", unable to allocate DMA buffer, "
1265 "FAIL!\n");
1266 return -EBUSY;
1267 }
1268 devpriv->dmapages[1] = pages;
1269 devpriv->hwdmaptr[1] =
1270 virt_to_bus((void *)devpriv->dmabuf[1]);
1271 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1272 }
1273 }
1274
1275no_dma:
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285 ret = alloc_subdevices(dev, 1);
1286 if (ret < 0)
1287 return ret;
1288
1289 s = dev->subdevices + 0;
1290 if (this_board->n_aichan > 0) {
1291 s->type = COMEDI_SUBD_AI;
1292 devpriv->sub_ai = s;
1293 dev->read_subdev = s;
1294 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1295 s->n_chan = this_board->n_aichan;
1296 s->subdev_flags |= SDF_DIFF;
1297
1298 s->maxdata = this_board->ai_maxdata;
1299 s->len_chanlist = this_board->ai_chanlist;
1300 s->range_table = this_board->ai_range_type;
1301 s->cancel = pcl816_ai_cancel;
1302 s->do_cmdtest = pcl816_ai_cmdtest;
1303 s->do_cmd = pcl816_ai_cmd;
1304 s->poll = pcl816_ai_poll;
1305 s->insn_read = pcl816_ai_insn_read;
1306 } else {
1307 s->type = COMEDI_SUBD_UNUSED;
1308 }
1309
1310#if 0
1311case COMEDI_SUBD_AO:
1312 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1313 s->n_chan = this_board->n_aochan;
1314 s->maxdata = this_board->ao_maxdata;
1315 s->len_chanlist = this_board->ao_chanlist;
1316 s->range_table = this_board->ao_range_type;
1317 break;
1318
1319case COMEDI_SUBD_DI:
1320 s->subdev_flags = SDF_READABLE;
1321 s->n_chan = this_board->n_dichan;
1322 s->maxdata = 1;
1323 s->len_chanlist = this_board->n_dichan;
1324 s->range_table = &range_digital;
1325 break;
1326
1327case COMEDI_SUBD_DO:
1328 s->subdev_flags = SDF_WRITABLE;
1329 s->n_chan = this_board->n_dochan;
1330 s->maxdata = 1;
1331 s->len_chanlist = this_board->n_dochan;
1332 s->range_table = &range_digital;
1333 break;
1334#endif
1335
1336 pcl816_reset(dev);
1337
1338 printk("\n");
1339
1340 return 0;
1341}
1342
1343
1344
1345
1346
1347static int pcl816_detach(struct comedi_device *dev)
1348{
1349 DEBUG(printk(KERN_INFO "comedi%d: pcl816: remove\n", dev->minor);)
1350 free_resources(dev);
1351#ifdef unused
1352 if (devpriv->dma_rtc)
1353 RTC_lock--;
1354#endif
1355 return 0;
1356}
1357
1358MODULE_AUTHOR("Comedi http://www.comedi.org");
1359MODULE_DESCRIPTION("Comedi low-level driver");
1360MODULE_LICENSE("GPL");
1361