2 comedi/drivers/pcl816.c
4 Author: Juan Grigera <juan@grigera.com.ar>
5 based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812
7 hardware driver for Advantech cards:
13 Description: Advantech PCL-816 cards, PCL-814
14 Author: Juan Grigera <juan@grigera.com.ar>
15 Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
17 Updated: Tue, 2 Apr 2002 23:15:21 -0800
19 PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
20 Differences are at resolution (16 vs 12 bits).
22 The driver support AI command mode, other subdevices not written.
24 Analog output and digital input and output are not supported.
26 Configuration Options:
28 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
29 [2] - DMA (0=disable, 1, 3)
30 [3] - 0, 10=10MHz clock for 8254
31 1= 1MHz clock for 8254
35 #include "../comedidev.h"
37 #include <linux/ioport.h>
38 #include <linux/mc146818rtc.h>
39 #include <linux/delay.h>
46 /* boards constants */
48 #define PCLx1x_RANGE 16
50 /* #define outb(x,y) printk("OUTB(%x, 200+%d)\n", x,y-0x200); outb(x,y) */
52 /* INTEL 8254 counters */
56 /* R: counter read-back register W: counter control */
57 #define PCL816_CTRCTL 7
59 /* R: A/D high byte W: A/D range control */
60 #define PCL816_RANGE 9
61 /* W: clear INT request */
62 #define PCL816_CLRINT 10
63 /* R: next mux scan channel W: mux scan channel & range control pointer */
65 /* R/W: operation control register */
66 #define PCL816_CONTROL 12
68 /* R: return status byte W: set DMA/IRQ */
69 #define PCL816_STATUS 13
70 #define PCL816_STATUS_DRDY_MASK 0x80
72 /* R: low byte of A/D W: soft A/D trigger */
73 #define PCL816_AD_LO 8
74 /* R: high byte of A/D W: A/D range control */
75 #define PCL816_AD_HI 9
77 /* type of interrupt handler */
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
83 #define INT_TYPE_AI1_DMA_RTC 9
84 #define INT_TYPE_AI3_DMA_RTC 10
88 #define RTC_IO_EXTENT 0x10
91 #define MAGIC_DMA_WORD 0x5a5a
93 static const struct comedi_lrange range_pcl816 = { 8, {
105 struct pcl816_board {
107 const char *name; /* board name */
108 int n_ranges; /* len of range list */
109 int n_aichan; /* num of A/D chans in diferencial mode */
110 unsigned int ai_ns_min; /* minimal alllowed delay between samples (in ns) */
111 int n_aochan; /* num of D/A chans */
112 int n_dichan; /* num of DI chans */
113 int n_dochan; /* num of DO chans */
114 const struct comedi_lrange *ai_range_type; /* default A/D rangelist */
115 const struct comedi_lrange *ao_range_type; /* default D/A rangelist */
116 unsigned int io_range; /* len of IO space */
117 unsigned int IRQbits; /* allowed interrupts */
118 unsigned int DMAbits; /* allowed DMA chans */
119 int ai_maxdata; /* maxdata for A/D */
120 int ao_maxdata; /* maxdata for D/A */
121 int ai_chanlist; /* allowed len of channel list A/D */
122 int ao_chanlist; /* allowed len of channel list D/A */
123 int i8254_osc_base; /* 1/frequency of on board oscilator in ns */
126 static const struct pcl816_board boardtypes[] = {
127 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
128 &range_pcl816, PCLx1x_RANGE,
129 0x00fc, /* IRQ mask */
131 0xffff, /* 16-bit card */
132 0xffff, /* D/A maxdata */
134 1, /* ao chan list */
136 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
137 &range_pcl816, PCLx1x_RANGE,
140 0x3fff, /* 14 bit card */
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)
151 static int pcl816_attach(struct comedi_device *dev,
152 struct comedi_devconfig *it);
153 static int pcl816_detach(struct comedi_device *dev);
156 static int RTC_lock = 0; /* RTC lock */
157 static int RTC_timer_lock = 0; /* RTC int lock */
160 static 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),
170 COMEDI_INITCLEANUP(driver_pcl816);
172 struct pcl816_private {
174 unsigned int dma; /* used DMA, 0=don't use DMA */
175 int dma_rtc; /* 1=RTC used with DMA, 0=no RTC alloc */
177 unsigned long rtc_iobase; /* RTC port region */
178 unsigned int rtc_iosize;
179 unsigned int rtc_irq;
181 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
182 unsigned int dmapages[2]; /* len of DMA buffers in PAGE_SIZEs */
183 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
184 unsigned int hwdmasize[2]; /* len of DMA buffers in Bytes */
185 unsigned int dmasamplsize; /* size in samples hwdmasize[0]/2 */
186 unsigned int last_top_dma; /* DMA pointer in last RTC int */
187 int next_dma_buf; /* which DMA buffer will be used next round */
188 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
189 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
191 unsigned int ai_scans; /* len of scanlist */
192 unsigned char ai_neverending; /* if=1, then we do neverending record (you must use cancel()) */
193 int irq_free; /* 1=have allocated IRQ */
194 int irq_blocked; /* 1=IRQ now uses any subdev */
196 int rtc_irq_blocked; /* 1=we now do AI with DMA&RTC */
198 int irq_was_now_closed; /* when IRQ finish, there's stored int816_mode for last interrupt */
199 int int816_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
200 struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */
201 int ai_act_scan; /* how many scans we finished */
202 unsigned int ai_act_chanlist[16]; /* MUX setting for actual AI operations */
203 unsigned int ai_act_chanlist_len; /* how long is actual MUX list */
204 unsigned int ai_act_chanlist_pos; /* actual position in MUX list */
205 unsigned int ai_poll_ptr; /* how many sampes transfer poll */
206 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
208 struct timer_list rtc_irq_timer; /* timer for RTC sanity check */
209 unsigned long rtc_freq; /* RTC int freq */
214 ==============================================================================
216 static int check_and_setup_channel_list(struct comedi_device *dev,
217 struct comedi_subdevice *s,
218 unsigned int *chanlist, int chanlen);
219 static int pcl816_ai_cancel(struct comedi_device *dev,
220 struct comedi_subdevice *s);
221 static void start_pacer(struct comedi_device *dev, int mode,
222 unsigned int divisor1, unsigned int divisor2);
224 static int set_rtc_irq_bit(unsigned char bit);
227 static int pcl816_ai_cmdtest(struct comedi_device *dev,
228 struct comedi_subdevice *s,
229 struct comedi_cmd *cmd);
230 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
233 ==============================================================================
234 ANALOG INPUT MODE0, 816 cards, slow version
236 static int pcl816_ai_insn_read(struct comedi_device *dev,
237 struct comedi_subdevice *s,
238 struct comedi_insn *insn, unsigned int *data)
243 DPRINTK("mode 0 analog input\n");
244 /* software trigger, DMA and INT off */
245 outb(0, dev->iobase + PCL816_CONTROL);
246 /* clear INT (conversion end) flag */
247 outb(0, dev->iobase + PCL816_CLRINT);
249 /* Set the input channel */
250 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
251 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE); /* select gain */
253 for (n = 0; n < insn->n; n++) {
255 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
259 if (!(inb(dev->iobase + PCL816_STATUS) &
260 PCL816_STATUS_DRDY_MASK)) {
261 /* return read value */
264 PCL816_AD_HI) << 8) |
265 (inb(dev->iobase + PCL816_AD_LO)));
267 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
272 /* Return timeout error */
274 comedi_error(dev, "A/D insn timeout\n");
276 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
285 ==============================================================================
286 analog input interrupt mode 1 & 3, 818 cards
287 one sample per interrupt version
289 static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
291 struct comedi_device *dev = d;
292 struct comedi_subdevice *s = dev->subdevices + 0;
294 int timeout = 50; /* wait max 50us */
297 if (!(inb(dev->iobase + PCL816_STATUS) &
298 PCL816_STATUS_DRDY_MASK))
302 if (!timeout) { /* timeout, bail error */
303 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
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);
313 low = inb(dev->iobase + PCL816_AD_LO);
314 hi = inb(dev->iobase + PCL816_AD_HI);
316 comedi_buf_put(s->async, (hi << 8) | low);
318 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
320 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
321 devpriv->ai_act_chanlist_pos = 0;
323 if (s->async->cur_chan == 0) {
324 devpriv->ai_act_scan++;
327 if (!devpriv->ai_neverending)
328 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
329 /* all data sampled */
330 pcl816_ai_cancel(dev, s);
331 s->async->events |= COMEDI_CB_EOA;
333 comedi_event(dev, s);
338 ==============================================================================
339 analog input dma mode 1 & 3, 816 cards
341 static void transfer_from_dma_buf(struct comedi_device *dev,
342 struct comedi_subdevice *s, short *ptr,
343 unsigned int bufptr, unsigned int len)
347 s->async->events = 0;
349 for (i = 0; i < len; i++) {
351 comedi_buf_put(s->async, ptr[bufptr++]);
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++;
359 if (!devpriv->ai_neverending)
360 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
361 pcl816_ai_cancel(dev, s);
362 s->async->events |= COMEDI_CB_EOA;
363 s->async->events |= COMEDI_CB_BLOCK;
368 comedi_event(dev, s);
371 static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
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;
379 disable_dma(devpriv->dma);
380 this_dma_buf = devpriv->next_dma_buf;
382 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) { /* switch dma bufs */
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 /* clear_dma_ff (devpriv->dma); */
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->
395 set_dma_count(devpriv->dma, devpriv->last_dma_run);
397 release_dma_lock(dma_flags);
398 enable_dma(devpriv->dma);
401 devpriv->dma_runs_to_end--;
402 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
404 ptr = (short *)devpriv->dmabuf[this_dma_buf];
406 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
407 bufptr = devpriv->ai_poll_ptr;
408 devpriv->ai_poll_ptr = 0;
410 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
415 ==============================================================================
418 static irqreturn_t interrupt_pcl816(int irq, void *d)
420 struct comedi_device *dev = d;
423 if (!dev->attached) {
424 comedi_error(dev, "premature interrupt");
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);
437 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
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 /* comedi_error(dev,"last IRQ.."); */
445 comedi_error(dev, "bad IRQ!");
448 comedi_error(dev, "IRQ from unknown source!");
453 ==============================================================================
456 static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd)
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,
464 printk("pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e,
465 cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
469 ==============================================================================
471 static int pcl816_ai_cmdtest(struct comedi_device *dev,
472 struct comedi_subdevice *s, struct comedi_cmd *cmd)
475 int tmp, divisor1 = 0, divisor2 = 0;
477 DEBUG(printk("pcl816 pcl812_ai_cmdtest\n"); pcl816_cmdtest_out(-1, cmd);
480 /* step 1: make sure trigger sources are trivially valid */
481 tmp = cmd->start_src;
482 cmd->start_src &= TRIG_NOW;
483 if (!cmd->start_src || tmp != cmd->start_src)
486 tmp = cmd->scan_begin_src;
487 cmd->scan_begin_src &= TRIG_FOLLOW;
488 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
491 tmp = cmd->convert_src;
492 cmd->convert_src &= TRIG_EXT | TRIG_TIMER;
493 if (!cmd->convert_src || tmp != cmd->convert_src)
496 tmp = cmd->scan_end_src;
497 cmd->scan_end_src &= TRIG_COUNT;
498 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
502 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
503 if (!cmd->stop_src || tmp != cmd->stop_src)
510 /* step 2: make sure trigger sources are unique and mutually compatible */
512 if (cmd->start_src != TRIG_NOW) {
513 cmd->start_src = TRIG_NOW;
517 if (cmd->scan_begin_src != TRIG_FOLLOW) {
518 cmd->scan_begin_src = TRIG_FOLLOW;
522 if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
523 cmd->convert_src = TRIG_TIMER;
527 if (cmd->scan_end_src != TRIG_COUNT) {
528 cmd->scan_end_src = TRIG_COUNT;
532 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
539 /* step 3: make sure arguments are trivially compatible */
540 if (cmd->start_arg != 0) {
545 if (cmd->scan_begin_arg != 0) {
546 cmd->scan_begin_arg = 0;
549 if (cmd->convert_src == TRIG_TIMER) {
550 if (cmd->convert_arg < this_board->ai_ns_min) {
551 cmd->convert_arg = this_board->ai_ns_min;
554 } else { /* TRIG_EXT */
555 if (cmd->convert_arg != 0) {
556 cmd->convert_arg = 0;
561 if (!cmd->chanlist_len) {
562 cmd->chanlist_len = 1;
565 if (cmd->chanlist_len > this_board->n_aichan) {
566 cmd->chanlist_len = this_board->n_aichan;
569 if (cmd->scan_end_arg != cmd->chanlist_len) {
570 cmd->scan_end_arg = cmd->chanlist_len;
573 if (cmd->stop_src == TRIG_COUNT) {
574 if (!cmd->stop_arg) {
578 } else { /* TRIG_NONE */
579 if (cmd->stop_arg != 0) {
589 /* step 4: fix up any arguments */
590 if (cmd->convert_src == TRIG_TIMER) {
591 tmp = cmd->convert_arg;
592 i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
593 &divisor1, &divisor2,
595 cmd->flags & TRIG_ROUND_MASK);
596 if (cmd->convert_arg < this_board->ai_ns_min)
597 cmd->convert_arg = this_board->ai_ns_min;
598 if (tmp != cmd->convert_arg)
609 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
611 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
612 struct comedi_cmd *cmd = &s->async->cmd;
614 if (cmd->start_src != TRIG_NOW)
616 if (cmd->scan_begin_src != TRIG_FOLLOW)
618 if (cmd->scan_end_src != TRIG_COUNT)
620 if (cmd->scan_end_arg != cmd->chanlist_len)
622 /* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
623 if (devpriv->irq_blocked)
626 if (cmd->convert_src == TRIG_TIMER) {
627 if (cmd->convert_arg < this_board->ai_ns_min)
628 cmd->convert_arg = this_board->ai_ns_min;
630 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
631 &divisor2, &cmd->convert_arg,
632 cmd->flags & TRIG_ROUND_MASK);
633 if (divisor1 == 1) { /* PCL816 crash if any divisor is set to 1 */
643 start_pacer(dev, -1, 0, 0); /* stop pacer */
645 if (!check_and_setup_channel_list(dev, s, cmd->chanlist,
650 devpriv->ai_act_scan = 0;
651 s->async->cur_chan = 0;
652 devpriv->irq_blocked = 1;
653 devpriv->ai_poll_ptr = 0;
654 devpriv->irq_was_now_closed = 0;
656 if (cmd->stop_src == TRIG_COUNT) {
657 devpriv->ai_scans = cmd->stop_arg;
658 devpriv->ai_neverending = 0;
660 devpriv->ai_scans = 0;
661 devpriv->ai_neverending = 1;
664 if ((cmd->flags & TRIG_WAKE_EOS)) { /* don't we want wake up every scan? */
665 printk("pl816: You wankt WAKE_EOS but I dont want handle it");
666 /* devpriv->ai_eos=1; */
667 /* if (devpriv->ai_n_chan==1) */
668 /* devpriv->dma=0; // DMA is useless for this situation */
672 bytes = devpriv->hwdmasize[0];
673 if (!devpriv->ai_neverending) {
674 bytes = s->async->cmd.chanlist_len * s->async->cmd.chanlist_len * sizeof(short); /* how many */
675 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; /* how many DMA pages we must fill */
676 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; /* on last dma transfer must be moved */
677 devpriv->dma_runs_to_end--;
678 if (devpriv->dma_runs_to_end >= 0)
679 bytes = devpriv->hwdmasize[0];
681 devpriv->dma_runs_to_end = -1;
683 devpriv->next_dma_buf = 0;
684 set_dma_mode(devpriv->dma, DMA_MODE_READ);
685 dma_flags = claim_dma_lock();
686 clear_dma_ff(devpriv->dma);
687 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
688 set_dma_count(devpriv->dma, bytes);
689 release_dma_lock(dma_flags);
690 enable_dma(devpriv->dma);
693 start_pacer(dev, 1, divisor1, divisor2);
694 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
696 switch (cmd->convert_src) {
698 devpriv->int816_mode = INT_TYPE_AI1_DMA;
699 outb(0x32, dev->iobase + PCL816_CONTROL); /* Pacer+IRQ+DMA */
700 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq and DMA to card */
704 devpriv->int816_mode = INT_TYPE_AI3_DMA;
705 outb(0x34, dev->iobase + PCL816_CONTROL); /* Ext trig+IRQ+DMA */
706 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq to card */
710 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
714 static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
717 unsigned int top1, top2, i;
720 return 0; /* poll is valid only for DMA transfer */
722 spin_lock_irqsave(&dev->spinlock, flags);
724 for (i = 0; i < 20; i++) {
725 top1 = get_dma_residue(devpriv->dma); /* where is now DMA */
726 top2 = get_dma_residue(devpriv->dma);
731 spin_unlock_irqrestore(&dev->spinlock, flags);
735 top1 = devpriv->hwdmasize[0] - top1; /* where is now DMA in buffer */
736 top1 >>= 1; /* sample position */
737 top2 = top1 - devpriv->ai_poll_ptr;
738 if (top2 < 1) { /* no new samples */
739 spin_unlock_irqrestore(&dev->spinlock, flags);
743 transfer_from_dma_buf(dev, s,
744 (short *)devpriv->dmabuf[devpriv->next_dma_buf],
745 devpriv->ai_poll_ptr, top2);
747 devpriv->ai_poll_ptr = top1; /* new buffer position */
748 spin_unlock_irqrestore(&dev->spinlock, flags);
750 return s->async->buf_write_count - s->async->buf_read_count;
754 ==============================================================================
755 cancel any mode 1-4 AI
757 static int pcl816_ai_cancel(struct comedi_device *dev,
758 struct comedi_subdevice *s)
760 /* DEBUG(printk("pcl816_ai_cancel()\n");) */
762 if (devpriv->irq_blocked > 0) {
763 switch (devpriv->int816_mode) {
765 case INT_TYPE_AI1_DMA_RTC:
766 case INT_TYPE_AI3_DMA_RTC:
767 set_rtc_irq_bit(0); /* stop RTC */
768 del_timer(&devpriv->rtc_irq_timer);
770 case INT_TYPE_AI1_DMA:
771 case INT_TYPE_AI3_DMA:
772 disable_dma(devpriv->dma);
773 case INT_TYPE_AI1_INT:
774 case INT_TYPE_AI3_INT:
775 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73, dev->iobase + PCL816_CONTROL); /* Stop A/D */
777 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
778 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
779 outb(0x70, dev->iobase + PCL816_CTRCTL);
780 outb(0, dev->iobase + PCL816_AD_LO);
781 inb(dev->iobase + PCL816_AD_LO);
782 inb(dev->iobase + PCL816_AD_HI);
783 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
784 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
785 devpriv->irq_blocked = 0;
786 devpriv->irq_was_now_closed = devpriv->int816_mode;
787 devpriv->int816_mode = 0;
788 devpriv->last_int_sub = s;
794 DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");)
799 ==============================================================================
802 static int pcl816_check(unsigned long iobase)
804 outb(0x00, iobase + PCL816_MUX);
806 if (inb(iobase + PCL816_MUX) != 0x00)
807 return 1; /* there isn't card */
808 outb(0x55, iobase + PCL816_MUX);
810 if (inb(iobase + PCL816_MUX) != 0x55)
811 return 1; /* there isn't card */
812 outb(0x00, iobase + PCL816_MUX);
814 outb(0x18, iobase + PCL816_CONTROL);
816 if (inb(iobase + PCL816_CONTROL) != 0x18)
817 return 1; /* there isn't card */
818 return 0; /* ok, card exist */
822 ==============================================================================
823 reset whole PCL-816 cards
825 static void pcl816_reset(struct comedi_device *dev)
827 /* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */
828 /* outb (0, dev->iobase + PCL818_DA_HI); */
830 /* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */
831 /* outb (0, dev->iobase + PCL818_DO_LO); */
833 outb(0, dev->iobase + PCL816_CONTROL);
834 outb(0, dev->iobase + PCL816_MUX);
835 outb(0, dev->iobase + PCL816_CLRINT);
836 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
837 outb(0x70, dev->iobase + PCL816_CTRCTL);
838 outb(0x30, dev->iobase + PCL816_CTRCTL);
839 outb(0, dev->iobase + PCL816_RANGE);
843 ==============================================================================
844 Start/stop pacer onboard pacer
847 start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
848 unsigned int divisor2)
850 outb(0x32, dev->iobase + PCL816_CTRCTL);
851 outb(0xff, dev->iobase + PCL816_CTR0);
852 outb(0x00, dev->iobase + PCL816_CTR0);
854 outb(0xb4, dev->iobase + PCL816_CTRCTL); /* set counter 2 as mode 3 */
855 outb(0x74, dev->iobase + PCL816_CTRCTL); /* set counter 1 as mode 3 */
859 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
861 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
862 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
863 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
864 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
867 /* clear pending interrupts (just in case) */
868 /* outb(0, dev->iobase + PCL816_CLRINT); */
872 ==============================================================================
873 Check if channel list from user is builded correctly
874 If it's ok, then program scan/gain logic
877 check_and_setup_channel_list(struct comedi_device *dev,
878 struct comedi_subdevice *s, unsigned int *chanlist,
881 unsigned int chansegment[16];
882 unsigned int i, nowmustbechan, seglen, segpos;
884 /* correct channel and range number check itself comedi/range.c */
886 comedi_error(dev, "range/channel list is empty!");
891 chansegment[0] = chanlist[0]; /* first channel is everytime ok */
892 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
893 /* build part of chanlist */
894 DEBUG(printk("%d. %d %d\n", i, CR_CHAN(chanlist[i]),
895 CR_RANGE(chanlist[i]));)
896 if (chanlist[0] == chanlist[i])
897 break; /* we detect loop, this must by finish */
899 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
900 if (nowmustbechan != CR_CHAN(chanlist[i])) {
901 /* channel list isn't continous :-( */
903 ("comedi%d: pcl816: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
904 dev->minor, i, CR_CHAN(chanlist[i]),
905 nowmustbechan, CR_CHAN(chanlist[0]));
908 chansegment[i] = chanlist[i]; /* well, this is next correct channel in list */
911 for (i = 0, segpos = 0; i < chanlen; i++) { /* check whole chanlist */
912 DEBUG(printk("%d %d=%d %d\n",
913 CR_CHAN(chansegment[i % seglen]),
914 CR_RANGE(chansegment[i % seglen]),
915 CR_CHAN(chanlist[i]),
916 CR_RANGE(chanlist[i]));)
917 if (chanlist[i] != chansegment[i % seglen]) {
919 ("comedi%d: pcl816: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
920 dev->minor, i, CR_CHAN(chansegment[i]),
921 CR_RANGE(chansegment[i]),
922 CR_AREF(chansegment[i]),
923 CR_CHAN(chanlist[i % seglen]),
924 CR_RANGE(chanlist[i % seglen]),
925 CR_AREF(chansegment[i % seglen]));
926 return 0; /* chan/gain list is strange */
933 devpriv->ai_act_chanlist_len = seglen;
934 devpriv->ai_act_chanlist_pos = 0;
936 for (i = 0; i < seglen; i++) { /* store range list to card */
937 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
938 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
939 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE); /* select gain */
944 outb(devpriv->ai_act_chanlist[0] | (devpriv->ai_act_chanlist[seglen - 1] << 4), dev->iobase + PCL816_MUX); /* select channel interval to scan */
946 return 1; /* we can serve this with MUX logic */
951 ==============================================================================
952 Enable(1)/disable(0) periodic interrupts from RTC
954 static int set_rtc_irq_bit(unsigned char bit)
961 if (RTC_timer_lock > 1)
965 if (RTC_timer_lock < 0)
967 if (RTC_timer_lock > 0)
973 val = CMOS_READ(RTC_CONTROL);
979 CMOS_WRITE(val, RTC_CONTROL);
980 CMOS_READ(RTC_INTR_FLAGS);
981 restore_flags(flags);
987 ==============================================================================
988 Free any resources that we have claimed
990 static void free_resources(struct comedi_device *dev)
992 /* printk("free_resource()\n"); */
994 pcl816_ai_cancel(dev, devpriv->sub_ai);
997 free_dma(devpriv->dma);
998 if (devpriv->dmabuf[0])
999 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1000 if (devpriv->dmabuf[1])
1001 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1003 if (devpriv->rtc_irq)
1004 free_irq(devpriv->rtc_irq, dev);
1005 if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
1006 if (devpriv->rtc_iobase)
1007 release_region(devpriv->rtc_iobase,
1008 devpriv->rtc_iosize);
1014 free_irq(dev->irq, dev);
1016 release_region(dev->iobase, this_board->io_range);
1017 /* printk("free_resource() end\n"); */
1021 ==============================================================================
1026 static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1029 unsigned long iobase;
1030 unsigned int irq, dma;
1031 unsigned long pages;
1033 struct comedi_subdevice *s;
1035 /* claim our I/O space */
1036 iobase = it->options[0];
1037 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
1038 this_board->name, iobase);
1040 if (!request_region(iobase, this_board->io_range, "pcl816")) {
1041 printk("I/O port conflict\n");
1045 dev->iobase = iobase;
1047 if (pcl816_check(iobase)) {
1048 printk(", I cann't detect board. FAIL!\n");
1052 ret = alloc_private(dev, sizeof(struct pcl816_private));
1054 return ret; /* Can't alloc mem */
1056 /* set up some name stuff */
1057 dev->board_name = this_board->name;
1061 if (this_board->IRQbits != 0) { /* board support IRQ */
1062 irq = it->options[1];
1063 if (irq) { /* we want to use IRQ */
1064 if (((1 << irq) & this_board->IRQbits) == 0) {
1066 (", IRQ %u is out of allowed range, DISABLING IT",
1068 irq = 0; /* Bad IRQ */
1071 (irq, interrupt_pcl816, 0, "pcl816", dev)) {
1073 (", unable to allocate IRQ %u, DISABLING IT",
1075 irq = 0; /* Can't use IRQ */
1077 printk(", irq=%u", irq);
1085 devpriv->irq_free = 1;
1086 } /* 1=we have allocated irq */
1088 devpriv->irq_free = 0;
1090 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1091 devpriv->int816_mode = 0; /* mode of irq */
1094 /* grab RTC for DMA operations */
1095 devpriv->dma_rtc = 0;
1096 if (it->options[2] > 0) { /* we want to use DMA */
1097 if (RTC_lock == 0) {
1098 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
1102 devpriv->rtc_iobase = RTC_PORT(0);
1103 devpriv->rtc_iosize = RTC_IO_EXTENT;
1105 #ifdef UNTESTED_CODE
1106 if (!request_irq(RTC_IRQ, interrupt_pcl816_ai_mode13_dma_rtc, 0,
1107 "pcl816 DMA (RTC)", dev)) {
1108 devpriv->dma_rtc = 1;
1109 devpriv->rtc_irq = RTC_IRQ;
1110 printk(", dma_irq=%u", devpriv->rtc_irq);
1113 if (RTC_lock == 0) {
1114 if (devpriv->rtc_iobase)
1115 release_region(devpriv->rtc_iobase,
1116 devpriv->rtc_iosize);
1118 devpriv->rtc_iobase = 0;
1119 devpriv->rtc_iosize = 0;
1122 printk("pcl816: RTC code missing");
1132 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1133 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1135 if (this_board->DMAbits != 0) { /* board support DMA */
1136 dma = it->options[2];
1138 goto no_dma; /* DMA disabled */
1140 if (((1 << dma) & this_board->DMAbits) == 0) {
1141 printk(", DMA is out of allowed range, FAIL!\n");
1142 return -EINVAL; /* Bad DMA */
1144 ret = request_dma(dma, "pcl816");
1146 printk(", unable to allocate DMA %u, FAIL!\n", dma);
1147 return -EBUSY; /* DMA isn't free */
1151 printk(", dma=%u", dma);
1152 pages = 2; /* we need 16KB */
1153 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1155 if (!devpriv->dmabuf[0]) {
1156 printk(", unable to allocate DMA buffer, FAIL!\n");
1157 /* maybe experiment with try_to_free_pages() will help .... */
1158 return -EBUSY; /* no buffer :-( */
1160 devpriv->dmapages[0] = pages;
1161 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1162 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
1163 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
1165 if (devpriv->dma_rtc == 0) { /* we must do duble buff :-( */
1166 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1167 if (!devpriv->dmabuf[1]) {
1169 (", unable to allocate DMA buffer, FAIL!\n");
1172 devpriv->dmapages[1] = pages;
1173 devpriv->hwdmaptr[1] =
1174 virt_to_bus((void *)devpriv->dmabuf[1]);
1175 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1181 /* if (this_board->n_aochan > 0)
1182 subdevs[1] = COMEDI_SUBD_AO;
1183 if (this_board->n_dichan > 0)
1184 subdevs[2] = COMEDI_SUBD_DI;
1185 if (this_board->n_dochan > 0)
1186 subdevs[3] = COMEDI_SUBD_DO;
1189 ret = alloc_subdevices(dev, 1);
1193 s = dev->subdevices + 0;
1194 if (this_board->n_aichan > 0) {
1195 s->type = COMEDI_SUBD_AI;
1196 devpriv->sub_ai = s;
1197 dev->read_subdev = s;
1198 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1199 s->n_chan = this_board->n_aichan;
1200 s->subdev_flags |= SDF_DIFF;
1201 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
1202 s->maxdata = this_board->ai_maxdata;
1203 s->len_chanlist = this_board->ai_chanlist;
1204 s->range_table = this_board->ai_range_type;
1205 s->cancel = pcl816_ai_cancel;
1206 s->do_cmdtest = pcl816_ai_cmdtest;
1207 s->do_cmd = pcl816_ai_cmd;
1208 s->poll = pcl816_ai_poll;
1209 s->insn_read = pcl816_ai_insn_read;
1211 s->type = COMEDI_SUBD_UNUSED;
1215 case COMEDI_SUBD_AO:
1216 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1217 s->n_chan = this_board->n_aochan;
1218 s->maxdata = this_board->ao_maxdata;
1219 s->len_chanlist = this_board->ao_chanlist;
1220 s->range_table = this_board->ao_range_type;
1223 case COMEDI_SUBD_DI:
1224 s->subdev_flags = SDF_READABLE;
1225 s->n_chan = this_board->n_dichan;
1227 s->len_chanlist = this_board->n_dichan;
1228 s->range_table = &range_digital;
1231 case COMEDI_SUBD_DO:
1232 s->subdev_flags = SDF_WRITABLE;
1233 s->n_chan = this_board->n_dochan;
1235 s->len_chanlist = this_board->n_dochan;
1236 s->range_table = &range_digital;
1248 ==============================================================================
1251 static int pcl816_detach(struct comedi_device *dev)
1253 DEBUG(printk("comedi%d: pcl816: remove\n", dev->minor);)
1254 free_resources(dev);
1256 if (devpriv->dma_rtc)