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; /* dafault 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 unknow 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, divisor2;
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 if (!(cmd->convert_src & (TRIG_EXT | TRIG_TIMER)))
494 tmp = cmd->scan_end_src;
495 cmd->scan_end_src &= TRIG_COUNT;
496 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
500 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
501 if (!cmd->stop_src || tmp != cmd->stop_src)
508 /* step 2: make sure trigger sources are unique and mutually compatible */
510 if (cmd->start_src != TRIG_NOW) {
511 cmd->start_src = TRIG_NOW;
515 if (cmd->scan_begin_src != TRIG_FOLLOW) {
516 cmd->scan_begin_src = TRIG_FOLLOW;
520 if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
521 cmd->convert_src = TRIG_TIMER;
525 if (cmd->scan_end_src != TRIG_COUNT) {
526 cmd->scan_end_src = TRIG_COUNT;
530 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
537 /* step 3: make sure arguments are trivially compatible */
538 if (cmd->start_arg != 0) {
543 if (cmd->scan_begin_arg != 0) {
544 cmd->scan_begin_arg = 0;
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;
552 } else { /* TRIG_EXT */
553 if (cmd->convert_arg != 0) {
554 cmd->convert_arg = 0;
559 if (!cmd->chanlist_len) {
560 cmd->chanlist_len = 1;
563 if (cmd->chanlist_len > this_board->n_aichan) {
564 cmd->chanlist_len = this_board->n_aichan;
567 if (cmd->scan_end_arg != cmd->chanlist_len) {
568 cmd->scan_end_arg = cmd->chanlist_len;
571 if (cmd->stop_src == TRIG_COUNT) {
572 if (!cmd->stop_arg) {
576 } else { /* TRIG_NONE */
577 if (cmd->stop_arg != 0) {
587 /* step 4: fix up any arguments */
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,
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)
607 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
609 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
610 struct comedi_cmd *cmd = &s->async->cmd;
612 if (cmd->start_src != TRIG_NOW)
614 if (cmd->scan_begin_src != TRIG_FOLLOW)
616 if (cmd->scan_end_src != TRIG_COUNT)
618 if (cmd->scan_end_arg != cmd->chanlist_len)
620 /* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
621 if (devpriv->irq_blocked)
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;
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) { /* PCL816 crash if any divisor is set to 1 */
641 start_pacer(dev, -1, 0, 0); /* stop pacer */
643 if (!check_and_setup_channel_list(dev, s, cmd->chanlist,
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;
654 if (cmd->stop_src == TRIG_COUNT) {
655 devpriv->ai_scans = cmd->stop_arg;
656 devpriv->ai_neverending = 0;
658 devpriv->ai_scans = 0;
659 devpriv->ai_neverending = 1;
662 if ((cmd->flags & TRIG_WAKE_EOS)) { /* don't we want wake up every scan? */
663 printk("pl816: You wankt WAKE_EOS but I dont want handle it");
664 /* devpriv->ai_eos=1; */
665 /* if (devpriv->ai_n_chan==1) */
666 /* devpriv->dma=0; // DMA is useless for this situation */
670 bytes = devpriv->hwdmasize[0];
671 if (!devpriv->ai_neverending) {
672 bytes = s->async->cmd.chanlist_len * s->async->cmd.chanlist_len * sizeof(short); /* how many */
673 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; /* how many DMA pages we must fill */
674 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; /* on last dma transfer must be moved */
675 devpriv->dma_runs_to_end--;
676 if (devpriv->dma_runs_to_end >= 0)
677 bytes = devpriv->hwdmasize[0];
679 devpriv->dma_runs_to_end = -1;
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);
691 start_pacer(dev, 1, divisor1, divisor2);
692 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
694 switch (cmd->convert_src) {
696 devpriv->int816_mode = INT_TYPE_AI1_DMA;
697 outb(0x32, dev->iobase + PCL816_CONTROL); /* Pacer+IRQ+DMA */
698 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq and DMA to card */
702 devpriv->int816_mode = INT_TYPE_AI3_DMA;
703 outb(0x34, dev->iobase + PCL816_CONTROL); /* Ext trig+IRQ+DMA */
704 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq to card */
708 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
712 static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
715 unsigned int top1, top2, i;
718 return 0; /* poll is valid only for DMA transfer */
720 spin_lock_irqsave(&dev->spinlock, flags);
722 for (i = 0; i < 20; i++) {
723 top1 = get_dma_residue(devpriv->dma); /* where is now DMA */
724 top2 = get_dma_residue(devpriv->dma);
729 spin_unlock_irqrestore(&dev->spinlock, flags);
733 top1 = devpriv->hwdmasize[0] - top1; /* where is now DMA in buffer */
734 top1 >>= 1; /* sample position */
735 top2 = top1 - devpriv->ai_poll_ptr;
736 if (top2 < 1) { /* no new samples */
737 spin_unlock_irqrestore(&dev->spinlock, flags);
741 transfer_from_dma_buf(dev, s,
742 (short *)devpriv->dmabuf[devpriv->next_dma_buf],
743 devpriv->ai_poll_ptr, top2);
745 devpriv->ai_poll_ptr = top1; /* new buffer position */
746 spin_unlock_irqrestore(&dev->spinlock, flags);
748 return s->async->buf_write_count - s->async->buf_read_count;
752 ==============================================================================
753 cancel any mode 1-4 AI
755 static int pcl816_ai_cancel(struct comedi_device *dev,
756 struct comedi_subdevice *s)
758 /* DEBUG(printk("pcl816_ai_cancel()\n");) */
760 if (devpriv->irq_blocked > 0) {
761 switch (devpriv->int816_mode) {
763 case INT_TYPE_AI1_DMA_RTC:
764 case INT_TYPE_AI3_DMA_RTC:
765 set_rtc_irq_bit(0); /* stop RTC */
766 del_timer(&devpriv->rtc_irq_timer);
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); /* Stop A/D */
775 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
776 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
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); /* clear INT request */
782 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
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;
792 DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");)
797 ==============================================================================
800 static int pcl816_check(unsigned long iobase)
802 outb(0x00, iobase + PCL816_MUX);
804 if (inb(iobase + PCL816_MUX) != 0x00)
805 return 1; /* there isn't card */
806 outb(0x55, iobase + PCL816_MUX);
808 if (inb(iobase + PCL816_MUX) != 0x55)
809 return 1; /* there isn't card */
810 outb(0x00, iobase + PCL816_MUX);
812 outb(0x18, iobase + PCL816_CONTROL);
814 if (inb(iobase + PCL816_CONTROL) != 0x18)
815 return 1; /* there isn't card */
816 return 0; /* ok, card exist */
820 ==============================================================================
821 reset whole PCL-816 cards
823 static void pcl816_reset(struct comedi_device *dev)
825 /* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */
826 /* outb (0, dev->iobase + PCL818_DA_HI); */
828 /* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */
829 /* outb (0, dev->iobase + PCL818_DO_LO); */
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); /* Stop pacer */
835 outb(0x70, dev->iobase + PCL816_CTRCTL);
836 outb(0x30, dev->iobase + PCL816_CTRCTL);
837 outb(0, dev->iobase + PCL816_RANGE);
841 ==============================================================================
842 Start/stop pacer onboard pacer
845 start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
846 unsigned int divisor2)
848 outb(0x32, dev->iobase + PCL816_CTRCTL);
849 outb(0xff, dev->iobase + PCL816_CTR0);
850 outb(0x00, dev->iobase + PCL816_CTR0);
852 outb(0xb4, dev->iobase + PCL816_CTRCTL); /* set counter 2 as mode 3 */
853 outb(0x74, dev->iobase + PCL816_CTRCTL); /* set counter 1 as mode 3 */
857 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
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);
865 /* clear pending interrupts (just in case) */
866 /* outb(0, dev->iobase + PCL816_CLRINT); */
870 ==============================================================================
871 Check if channel list from user is builded correctly
872 If it's ok, then program scan/gain logic
875 check_and_setup_channel_list(struct comedi_device *dev,
876 struct comedi_subdevice *s, unsigned int *chanlist,
879 unsigned int chansegment[16];
880 unsigned int i, nowmustbechan, seglen, segpos;
882 /* correct channel and range number check itself comedi/range.c */
884 comedi_error(dev, "range/channel list is empty!");
889 chansegment[0] = chanlist[0]; /* first channel is everytime ok */
890 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
891 /* build part of chanlist */
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; /* we detect loop, this must by finish */
897 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
898 if (nowmustbechan != CR_CHAN(chanlist[i])) {
899 /* channel list isn't continous :-( */
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]));
906 chansegment[i] = chanlist[i]; /* well, this is next correct channel in list */
909 for (i = 0, segpos = 0; i < chanlen; i++) { /* check whole chanlist */
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]) {
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; /* chan/gain list is strange */
931 devpriv->ai_act_chanlist_len = seglen;
932 devpriv->ai_act_chanlist_pos = 0;
934 for (i = 0; i < seglen; i++) { /* store range list to card */
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); /* select gain */
942 outb(devpriv->ai_act_chanlist[0] | (devpriv->ai_act_chanlist[seglen - 1] << 4), dev->iobase + PCL816_MUX); /* select channel interval to scan */
944 return 1; /* we can serve this with MUX logic */
949 ==============================================================================
950 Enable(1)/disable(0) periodic interrupts from RTC
952 static int set_rtc_irq_bit(unsigned char bit)
959 if (RTC_timer_lock > 1)
963 if (RTC_timer_lock < 0)
965 if (RTC_timer_lock > 0)
971 val = CMOS_READ(RTC_CONTROL);
977 CMOS_WRITE(val, RTC_CONTROL);
978 CMOS_READ(RTC_INTR_FLAGS);
979 restore_flags(flags);
985 ==============================================================================
986 Free any resources that we have claimed
988 static void free_resources(struct comedi_device *dev)
990 /* printk("free_resource()\n"); */
992 pcl816_ai_cancel(dev, devpriv->sub_ai);
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]);
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);
1012 free_irq(dev->irq, dev);
1014 release_region(dev->iobase, this_board->io_range);
1015 /* printk("free_resource() end\n"); */
1019 ==============================================================================
1024 static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1027 unsigned long iobase;
1028 unsigned int irq, dma;
1029 unsigned long pages;
1031 struct comedi_subdevice *s;
1033 /* claim our I/O space */
1034 iobase = it->options[0];
1035 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
1036 this_board->name, iobase);
1038 if (!request_region(iobase, this_board->io_range, "pcl816")) {
1039 printk("I/O port conflict\n");
1043 dev->iobase = iobase;
1045 if (pcl816_check(iobase)) {
1046 printk(", I cann't detect board. FAIL!\n");
1050 ret = alloc_private(dev, sizeof(struct pcl816_private));
1052 return ret; /* Can't alloc mem */
1054 /* set up some name stuff */
1055 dev->board_name = this_board->name;
1059 if (this_board->IRQbits != 0) { /* board support IRQ */
1060 irq = it->options[1];
1061 if (irq) { /* we want to use IRQ */
1062 if (((1 << irq) & this_board->IRQbits) == 0) {
1064 (", IRQ %u is out of allowed range, DISABLING IT",
1066 irq = 0; /* Bad IRQ */
1069 (irq, interrupt_pcl816, 0, "pcl816", dev)) {
1071 (", unable to allocate IRQ %u, DISABLING IT",
1073 irq = 0; /* Can't use IRQ */
1075 printk(", irq=%u", irq);
1083 devpriv->irq_free = 1;
1084 } /* 1=we have allocated irq */
1086 devpriv->irq_free = 0;
1088 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1089 devpriv->int816_mode = 0; /* mode of irq */
1092 /* grab RTC for DMA operations */
1093 devpriv->dma_rtc = 0;
1094 if (it->options[2] > 0) { /* we want to use DMA */
1095 if (RTC_lock == 0) {
1096 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
1100 devpriv->rtc_iobase = RTC_PORT(0);
1101 devpriv->rtc_iosize = RTC_IO_EXTENT;
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);
1111 if (RTC_lock == 0) {
1112 if (devpriv->rtc_iobase)
1113 release_region(devpriv->rtc_iobase,
1114 devpriv->rtc_iosize);
1116 devpriv->rtc_iobase = 0;
1117 devpriv->rtc_iosize = 0;
1120 printk("pcl816: RTC code missing");
1130 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1131 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1133 if (this_board->DMAbits != 0) { /* board support DMA */
1134 dma = it->options[2];
1136 goto no_dma; /* DMA disabled */
1138 if (((1 << dma) & this_board->DMAbits) == 0) {
1139 printk(", DMA is out of allowed range, FAIL!\n");
1140 return -EINVAL; /* Bad DMA */
1142 ret = request_dma(dma, "pcl816");
1144 printk(", unable to allocate DMA %u, FAIL!\n", dma);
1145 return -EBUSY; /* DMA isn't free */
1149 printk(", dma=%u", dma);
1150 pages = 2; /* we need 16KB */
1151 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1153 if (!devpriv->dmabuf[0]) {
1154 printk(", unable to allocate DMA buffer, FAIL!\n");
1155 /* maybe experiment with try_to_free_pages() will help .... */
1156 return -EBUSY; /* no buffer :-( */
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 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
1163 if (devpriv->dma_rtc == 0) { /* we must do duble buff :-( */
1164 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1165 if (!devpriv->dmabuf[1]) {
1167 (", unable to allocate DMA buffer, FAIL!\n");
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;
1179 /* if (this_board->n_aochan > 0)
1180 subdevs[1] = COMEDI_SUBD_AO;
1181 if (this_board->n_dichan > 0)
1182 subdevs[2] = COMEDI_SUBD_DI;
1183 if (this_board->n_dochan > 0)
1184 subdevs[3] = COMEDI_SUBD_DO;
1187 ret = alloc_subdevices(dev, 1);
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 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
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;
1209 s->type = COMEDI_SUBD_UNUSED;
1213 case 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;
1221 case COMEDI_SUBD_DI:
1222 s->subdev_flags = SDF_READABLE;
1223 s->n_chan = this_board->n_dichan;
1225 s->len_chanlist = this_board->n_dichan;
1226 s->range_table = &range_digital;
1229 case COMEDI_SUBD_DO:
1230 s->subdev_flags = SDF_WRITABLE;
1231 s->n_chan = this_board->n_dochan;
1233 s->len_chanlist = this_board->n_dochan;
1234 s->range_table = &range_digital;
1246 ==============================================================================
1249 static int pcl816_detach(struct comedi_device *dev)
1251 DEBUG(printk("comedi%d: pcl816: remove\n", dev->minor);)
1252 free_resources(dev);
1254 if (devpriv->dma_rtc)