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